eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "database/sql"
5 "os"
6 "path/filepath"
7 "testing"
8)
9
10// A fleet sweep (`deploy --all --version`) must leave pinned instances alone —
11// the pin used to gate only the AUTO-updater, and a manual promote stomped a
12// pinned canary while its pin sat intact. sweepSkipsPinned is the decision.
13func TestSweepSkipsPinned(t *testing.T) {
14 dir := t.TempDir()
15
16 // A hibernating host reads prefs straight from its parked DB — build one
17 // with a pin, exactly as the instance's own settings write would.
18 prev := instanceDataDir
19 instanceDataDir = dir
20 defer func() { instanceDataDir = prev }()
21
22 mkDB := func(host, pin string) {
23 db, err := sql.Open("sqlite", filepath.Join(dir, host+".db"))
24 if err != nil {
25 t.Fatal(err)
26 }
27 defer db.Close()
28 if _, err := db.Exec("CREATE TABLE config (key TEXT PRIMARY KEY, value TEXT)"); err != nil {
29 t.Fatal(err)
30 }
31 if pin != "" {
32 if _, err := db.Exec("INSERT INTO config VALUES('update_pin', ?)", pin); err != nil {
33 t.Fatal(err)
34 }
35 }
36 }
37
38 // Hibernating + pinned to something else → skipped.
39 mkDB("pinned.example", "aaaa111")
40 rt := &Route{Host: "pinned.example", Hibernate: true}
41 if skip, why := sweepSkipsPinned(rt, "bbbb222"); !skip || why == "" {
42 t.Fatalf("pinned canary must be skipped by the sweep, got skip=%v why=%q", skip, why)
43 }
44 // Pinned to the sweep's own target → deploy (harmless repoint/no-op).
45 if skip, _ := sweepSkipsPinned(rt, "aaaa111"); skip {
46 t.Fatal("a host pinned to the sweep's target version should deploy")
47 }
48
49 // Hibernating, no pin → deploys.
50 mkDB("plain.example", "")
51 rt2 := &Route{Host: "plain.example", Hibernate: true}
52 if skip, _ := sweepSkipsPinned(rt2, "bbbb222"); skip {
53 t.Fatal("an unpinned host must not be skipped")
54 }
55
56 // Hibernating with an UNREADABLE parked DB fails closed (the updater's
57 // rule): the pin may live only in the copy we can't read.
58 rt3 := &Route{Host: "missing.example", Hibernate: true}
59 if skip, why := sweepSkipsPinned(rt3, "bbbb222"); !skip || why == "" {
60 t.Fatalf("unreadable hibernating prefs must fail closed, got skip=%v why=%q", skip, why)
61 }
62
63 // A RUNNING host whose socket is dead is probably crashed — the sweep is
64 // its rescue, so it deploys anyway (with a note, not silence).
65 rt4 := &Route{Host: "down.example", Socket: filepath.Join(dir, "no-such.sock")}
66 if skip, why := sweepSkipsPinned(rt4, "bbbb222"); skip || why == "" {
67 t.Fatalf("a dead-socket running host should deploy with a note, got skip=%v why=%q", skip, why)
68 }
69
70 _ = os.Remove(filepath.Join(dir, "pinned.example.db"))
71}
72