package oltp
import (
"path/filepath"
"testing"
"time"
)
func chatOf(t *testing.T, d *DB, name string) bool {
t.Helper()
var on bool
if err := d.db.QueryRow(`SELECT chat FROM repo WHERE name = ?`, name).Scan(&on); err != nil {
t.Fatalf("chat of %s: %v", name, err)
}
return on
}
// A store that becomes a tracking mirror starts with chat off. An admin's
// choice survives a change of interval, and re-attaching after a detach
// starts over, the way accept_writes does.
func TestBecomingAMirrorTurnsChatOff(t *testing.T) {
d := openTest(t)
if err := d.AddRepo("m", "/tmp/m", "", "public"); err != nil {
t.Fatal(err)
}
if !chatOf(t, d, "m") {
t.Fatal("a native repository does not start with chat on")
}
if err := d.SetMirror("m", "https://up.example/m", time.Hour); err != nil {
t.Fatal(err)
}
if chatOf(t, d, "m") {
t.Fatal("becoming a mirror left chat on")
}
if err := d.SetRepoChat("m", true); err != nil {
t.Fatal(err)
}
if err := d.SetMirror("m", "https://up.example/m", 2*time.Hour); err != nil {
t.Fatal(err)
}
if !chatOf(t, d, "m") {
t.Fatal("changing the interval undid the admin's chat setting")
}
if err := d.DetachMirror("m"); err != nil {
t.Fatal(err)
}
if !chatOf(t, d, "m") {
t.Fatal("detaching changed the chat setting")
}
if err := d.SetMirror("m", "https://up.example/m", time.Hour); err != nil {
t.Fatal(err)
}
if chatOf(t, d, "m") {
t.Fatal("re-attaching kept chat on from the previous posture")
}
}
// Setting a chat bridge is syndicating the upstream's chat, which only
// means anything with the channel on.
func TestAChatBridgeTurnsChatOn(t *testing.T) {
d := openTest(t)
if err := d.AddRepo("z", "/tmp/z", "", "public"); err != nil {
t.Fatal(err)
}
if err := d.SetMirror("z", "https://up.example/z", time.Hour); err != nil {
t.Fatal(err)
}
if chatOf(t, d, "z") {
t.Fatal("precondition: a fresh mirror has chat off")
}
if err := d.SetChatBridge("z", "https://up.example/z", "bot", "secret"); err != nil {
t.Fatal(err)
}
if !chatOf(t, d, "z") {
t.Fatal("a bridge did not turn chat on")
}
}
// Mirrors registered before this default existed have chat on because
// the column said so. One open turns them off, unless a bridge says the
// channel is in use; a later open does not undo what an admin chose.
func TestExistingMirrorsLoseChatOnceUnlessBridged(t *testing.T) {
path := filepath.Join(t.TempDir(), "oltp.db")
d, err := Open(path)
if err != nil {
t.Fatal(err)
}
for _, name := range []string{"plain", "bridged", "native"} {
if err := d.AddRepo(name, "/tmp/"+name, "", "public"); err != nil {
t.Fatal(err)
}
}
for _, name := range []string{"plain", "bridged"} {
if err := d.SetMirror(name, "https://up.example/"+name, time.Hour); err != nil {
t.Fatal(err)
}
// The world before the default: every mirror had chat on.
if _, err := d.db.Exec(`UPDATE repo SET chat = 1 WHERE name = ?`, name); err != nil {
t.Fatal(err)
}
}
if err := d.SetChatBridge("bridged", "https://up.example/bridged", "bot", "s"); err != nil {
t.Fatal(err)
}
// This database was opened fresh above, so the migration already
// ran (over nothing) and recorded itself; forget that, as a database
// from before the migration existed would have nothing recorded.
if _, err := d.db.Exec(`DELETE FROM applied_migration WHERE name = 'mirror-chat-off'`); err != nil {
t.Fatal(err)
}
d.Close()
d, err = Open(path)
if err != nil {
t.Fatal(err)
}
if chatOf(t, d, "plain") {
t.Error("an unbridged mirror kept chat on across the migration")
}
if !chatOf(t, d, "bridged") {
t.Error("a bridged mirror lost its chat")
}
if !chatOf(t, d, "native") {
t.Error("a native repository lost its chat")
}
// The admin turns it back on; the next open must not run again.
if err := d.SetRepoChat("plain", true); err != nil {
t.Fatal(err)
}
d.Close()
d, err = Open(path)
if err != nil {
t.Fatal(err)
}
defer d.Close()
if !chatOf(t, d, "plain") {
t.Error("the migration ran a second time and undid the admin's choice")
}
}