Name no node in cups's, nats's, mesh-watcher's, mesh-files's, dunst's and desk-channel's fixtures (issue 1838)

This commit is contained in:
jochen
2026-10-22 21:31:08 +02:00
parent 39d724fd77
commit 7ad9e531d4
8 changed files with 33 additions and 33 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ Research 027 asked for "`cups` with the printer's driver". Measured on 2026-10-0
| workstation | queue | device | prints through | driver package installed |
|---|---|---|---|---|
| laptop | `Brother` (MFC-L8690CDW) | `ipp://` on the LAN | **IPP Everywhere, driverless** | `brother-mfc-l8690cdw` (AUR), unused |
| desktop | `Brother_MFC_Novox` (default) | `ipp://` on the LAN | **IPP Everywhere, driverless** | `brother-mfc-l8390cdw` and its `-debug` (AUR), unused |
| desktop | `Brother_MFC_NodeC` (default) | `ipp://` on the LAN | **IPP Everywhere, driverless** | `brother-mfc-l8390cdw` and its `-debug` (AUR), unused |
| desktop | `Kanjuro` (Canon PIXMA MG4200) | `cnijnet:` | the vendor's PPD and filter | `cnijfilter-mg4200` 3.80 (AUR) |
**Both Brother queues already print without a vendor driver.** CUPS's own IPP Everywhere support,
+7 -7
View File
@@ -36,13 +36,13 @@ func theDesktop(t *testing.T, more func(line string, c Cmd) (Result, bool)) *fak
case "lpstat -r":
return ok("scheduler is running\n")
case "lpstat -d":
return ok("system default destination: Brother_MFC_Novox\n")
return ok("system default destination: Brother_MFC_NodeC\n")
case "lpstat -p":
return ok("printer Brother_MFC_Novox is idle. enabled since Mon Jun 29 21:34:30 2026\n" +
return ok("printer Brother_MFC_NodeC is idle. enabled since Mon Jun 29 21:34:30 2026\n" +
"printer Kanjuro disabled since Sun Jun 9 11:16:03 2024 -\n\tPaused\n")
case "lpstat -a":
return ok("Brother_MFC_Novox accepting requests since Mon Jun 29 21:34:30 2026\nKanjuro not accepting requests since Sun Jun 9 11:16:03 2024 -\n\tRejecting Jobs\n")
case "lpoptions -p Brother_MFC_Novox":
return ok("Brother_MFC_NodeC accepting requests since Mon Jun 29 21:34:30 2026\nKanjuro not accepting requests since Sun Jun 9 11:16:03 2024 -\n\tRejecting Jobs\n")
case "lpoptions -p Brother_MFC_NodeC":
return ok(`copies=1 device-uri=ipp://192.0.2.171/ipp/port1 finishings=3 marker-levels=70,100,8,100 marker-low-levels=10,10,10,10 marker-names='Black\ Toner\ Cartridge,Cyan\ Toner\ Cartridge,Magenta\ Toner\ Cartridge,Yellow\ Toner\ Cartridge' marker-types=toner,toner,toner,toner printer-is-shared=false printer-make-and-model='Printer - IPP Everywhere' printer-state-reasons=none`)
case "lpoptions -p Kanjuro":
return ok(`device-uri=cnijnet:/18-0C-AC-B0-62-83 printer-is-shared=false printer-make-and-model='Canon MG4200 series Ver.3.80' printer-state-reasons=paused`)
@@ -54,7 +54,7 @@ func theDesktop(t *testing.T, more func(line string, c Cmd) (Result, bool)) *fak
func TestPrintersReadsStateDeviceModelAndSupplies(t *testing.T) {
theDesktop(t, nil)
got, err := Printers()
if err != nil || len(got.Printers) != 2 || got.Default != "Brother_MFC_Novox" || got.Scheduler != "scheduler is running" {
if err != nil || len(got.Printers) != 2 || got.Default != "Brother_MFC_NodeC" || got.Scheduler != "scheduler is running" {
t.Fatalf("%+v %v", got, err)
}
b, k := got.Printers[0], got.Printers[1]
@@ -186,11 +186,11 @@ func TestDefaultReadsAndSetsThroughSudo(t *testing.T) {
return Result{}, false
})
got, err := Default("")
if err != nil || got["default"] != "Brother_MFC_Novox" {
if err != nil || got["default"] != "Brother_MFC_NodeC" {
t.Fatalf("%v %v", got, err)
}
got, err = Default("Kanjuro")
if err != nil || got["default"] != "Kanjuro" || got["was"] != "Brother_MFC_Novox" {
if err != nil || got["default"] != "Kanjuro" || got["was"] != "Brother_MFC_NodeC" {
t.Fatalf("%v %v", got, err)
}
if l := f.lines(); l[len(l)-1] != "sudo -n lpadmin -d Kanjuro" {
@@ -133,7 +133,7 @@ func TestANotifierThatStartedAgainIsShownNothingFromMemory(t *testing.T) {
d, shown, said, store := newDesk(t, "laptop")
d.Paused("laptop", false)
d.SayStanding()
take(t, d, asks.Show, asks.Message{Handle: "open", Title: "2 failed services on shanks",
take(t, d, asks.Show, asks.Message{Handle: "open", Title: "2 failed services on node-d",
Buttons: []asks.Button{{Label: "Silence for a week", Ticket: "t-open"}}})
take(t, d, asks.Show, asks.Message{Handle: "silenced", Title: "Agent-home shrank",
Buttons: []asks.Button{{Label: "Silence for a week", Ticket: "t-silenced"}}})
@@ -162,7 +162,7 @@ func TestANotifierThatStartedAgainIsShownNothingFromMemory(t *testing.T) {
t.Fatalf("the standing after the start: %d said, last %s", len(got)-standings, got[len(got)-1])
}
// The router says the open one again under its handle: in place of its notification, with its answers.
take(t, d, asks.Show, asks.Message{Handle: "open", Title: "2 failed services on shanks",
take(t, d, asks.Show, asks.Message{Handle: "open", Title: "2 failed services on node-d",
Buttons: []asks.Button{{Label: "Silence for a week", Ticket: "t-open"}}})
last := (*shown)[len(*shown)-1]
if len(*shown) != before+1 || last.args["replace_id"] != openID || last.args["actions"] == nil {
@@ -329,7 +329,7 @@ func TestItRunsAsItsOwnAccount(t *testing.T) {
// was not shown; a message under a new handle replaces nothing.
func TestAMessageSaidAgainUnderItsHandleReplacesItsNotification(t *testing.T) {
d, shown, _, store := newDesk(t, "laptop", "desktop")
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "2 failed services on shanks"})
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "2 failed services on node-d"})
take(t, d, asks.Send, asks.Message{Handle: "m2", Title: "another problem"})
if len(*shown) != 4 {
t.Fatalf("shown %d", len(*shown))
@@ -342,7 +342,7 @@ func TestAMessageSaidAgainUnderItsHandleReplacesItsNotification(t *testing.T) {
first := store["m1"].On
// The desk now names a third machine as well.
d.Machines = func() []string { return []string{"laptop", "desktop", "third"} }
take(t, d, asks.Show, asks.Message{Handle: "m1", Title: "Still open: 2 failed services on shanks", Urgent: true,
take(t, d, asks.Show, asks.Message{Handle: "m1", Title: "Still open: 2 failed services on node-d", Urgent: true,
Buttons: []asks.Button{{Label: "Silence for a week", Ticket: "t1"}}})
again := (*shown)[4:]
if len(again) != 3 {
@@ -367,7 +367,7 @@ func TestAMessageSaidAgainUnderItsHandleReplacesItsNotification(t *testing.T) {
// later edit or clearing still reaches it; failing everywhere, it is said to the router as failed.
func TestAFailedReplaceKeepsTheNotificationItWasToReplace(t *testing.T) {
d, shown, said, store := newDesk(t, "laptop", "desktop")
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "2 failed services on shanks"})
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "2 failed services on node-d"})
first := store["m1"].On
notify := d.Notify
d.Notify = func(machine string, args map[string]any) (json.RawMessage, error) {
@@ -376,12 +376,12 @@ func TestAFailedReplaceKeepsTheNotificationItWasToReplace(t *testing.T) {
}
return notify(machine, args)
}
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "Still open: 2 failed services on shanks"})
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "Still open: 2 failed services on node-d"})
if got := store["m1"].On["desktop"]; got != first["desktop"] {
t.Fatalf("the desktop's notification %d was forgotten: %v", first["desktop"], store["m1"].On)
}
d.Notify = func(string, map[string]any) (json.RawMessage, error) { return nil, errors.New("away") }
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "Now urgent: 2 failed services on shanks"})
take(t, d, asks.Send, asks.Message{Handle: "m1", Title: "Now urgent: 2 failed services on node-d"})
if len(said[asks.IntakeSubject(asks.FailedWhat, Kind)]) != 1 {
t.Fatalf("shown nowhere, and not said as failed")
}
@@ -17,7 +17,7 @@ func TestANotificationsChosenAnswerIsSaidAsAnEvent(t *testing.T) {
withBus(t)
bin := fakeBinaries(t, map[string]string{"notify-send": `for a in "$@"; do printf '[%s]' "$a"; done > "$LOG"; echo 42; sleep 0.2; echo "$CHOSEN"`})
t.Setenv("LOG", filepath.Join(bin, "log"))
t.Setenv("MESH_NODE", "g14")
t.Setenv("MESH_NODE", "node-b")
var mu sync.Mutex
var said []map[string]any
before := emit
@@ -58,7 +58,7 @@ func TestANotificationsChosenAnswerIsSaidAsAnEvent(t *testing.T) {
time.Sleep(20 * time.Millisecond)
}
mu.Lock()
if len(said) != 1 || said[0]["token"] != "c3d4" || said[0]["id"] != 42 || said[0]["machine"] != "g14" {
if len(said) != 1 || said[0]["token"] != "c3d4" || said[0]["id"] != 42 || said[0]["machine"] != "node-b" {
t.Fatalf("said %v", said)
}
said = nil
@@ -96,7 +96,7 @@ func TestActionsAreRefusedForWhatTheyCannotBe(t *testing.T) {
// **A bundle that started says so**, so the operator channel shows again the answers lost with the one
// before.
func TestABundleThatStartedSaysSo(t *testing.T) {
t.Setenv("MESH_NODE", "g14")
t.Setenv("MESH_NODE", "node-b")
var said []string
before := emit
emit = func(event string, body any) error {
@@ -105,7 +105,7 @@ func TestABundleThatStartedSaysSo(t *testing.T) {
}
t.Cleanup(func() { emit = before })
sayStarted()
if len(said) != 1 || said[0] != "started map[machine:g14]" {
if len(said) != 1 || said[0] != "started map[machine:node-b]" {
t.Fatalf("%v", said)
}
}
@@ -97,7 +97,7 @@ func testFiles(t *testing.T) *rig {
r.Root = g.root
r.Now = func() time.Time { return time.Date(2026, 10, 16, 12, 0, 0, 0, time.UTC) }
seq := 0
g.Files = &Files{Reader: r, State: filepath.Join(dir, "state"), Owned: map[string]string{}, Node: "shanks",
g.Files = &Files{Reader: r, State: filepath.Join(dir, "state"), Owned: map[string]string{}, Node: "node-d",
OwnerOf: func(p string) (Ident, error) {
if id, ok := g.owners[p]; ok {
return id, nil
@@ -344,7 +344,7 @@ func TestAnOwnedPathIsRefusedNamingItsOwnerAndPassesWithAnOverride(t *testing.T)
if len(lines) != 1 || lines[0]["override"] != ov.Override {
t.Errorf("log: %v", lines)
}
if len(g.told) != 1 || !strings.HasPrefix(g.told[0], "mesh-files on shanks | ") ||
if len(g.told) != 1 || !strings.HasPrefix(g.told[0], "mesh-files on node-d | ") ||
!strings.Contains(g.told[0], ov.Override) || !strings.Contains(g.told[0], "mesh-hosts (node-hostname)") ||
!strings.Contains(g.told[0], "person.jochen") {
t.Errorf("told: %v", g.told)
@@ -384,7 +384,7 @@ func TestACredentialFileIsRefusedToReadButMayBeCopied(t *testing.T) {
func TestATargetOnAnotherNodeIsRefused(t *testing.T) {
g := testFiles(t)
write(t, g.path("a"), "x")
for _, to := range []string{"ace:/tmp/a", "novox:/etc/hosts"} {
for _, to := range []string{"node-a:/tmp/a", "node-c:/etc/hosts"} {
for _, run := range []func() error{
func() error { _, err := g.Copy(by, g.path("a"), to); return err },
func() error { _, err := g.Move(by, g.path("a"), to); return err },
@@ -80,7 +80,7 @@ func TestUploadSendsTheFileUnderItsHash(t *testing.T) {
g, st, lv := transferRig(t)
body := bytes.Repeat([]byte("a document\n"), 1000)
write(t, g.path("home/ops/doc.pdf"), string(body))
ans, err := g.Upload(g.path("home/ops/doc.pdf"), "mesh-shanks-brother", "")
ans, err := g.Upload(g.path("home/ops/doc.pdf"), "mesh-node-d-brother", "")
if err != nil {
t.Fatal(err)
}
@@ -88,10 +88,10 @@ func TestUploadSendsTheFileUnderItsHash(t *testing.T) {
if ans.Key != want || ans.SHA256 != want || ans.Size != int64(len(body)) {
t.Fatalf("answer %+v", ans)
}
if !bytes.Equal(st.objects["/mesh-shanks-brother/"+want], body) {
if !bytes.Equal(st.objects["/mesh-node-d-brother/"+want], body) {
t.Fatal("the store did not receive exactly the file's bytes")
}
if len(lv.asks) != 1 || lv.asks[0] != "put mesh-shanks-brother/"+want {
if len(lv.asks) != 1 || lv.asks[0] != "put mesh-node-d-brother/"+want {
t.Fatalf("link asks %v", lv.asks)
}
}
@@ -31,7 +31,7 @@ func (c *clock) pass(d time.Duration) { c.t = c.t.Add(d) }
func watcher(t *testing.T) (*Watcher, *fakeTelegram, *clock) {
c := &clock{t: time.Date(2026, 10, 6, 12, 0, 0, 0, time.UTC)}
tg := &fakeTelegram{}
return NewWatcher(tg, c.now, t.Logf, "ace"), tg, c
return NewWatcher(tg, c.now, t.Logf, "node-a"), tg, c
}
// beatEvery has the controller's doctor run, and the bus answer, every five minutes up to d.
@@ -204,11 +204,11 @@ func TestNotGivenATokenIsBlind(t *testing.T) {
func TestOnTheControlNodeItSaysItIsMisplaced(t *testing.T) {
w, _, _ := watcher(t)
w.Placed([]string{"novox"})
if st := w.Status("listening"); st.Verdict != "ok: watching" || st.Control != "novox" {
w.Placed([]string{"node-c"})
if st := w.Status("listening"); st.Verdict != "ok: watching" || st.Control != "node-c" {
t.Fatalf("%+v", st)
}
w.Placed([]string{"ace"})
w.Placed([]string{"node-a"})
if st := w.Status("listening"); !strings.HasPrefix(st.Verdict, "MISPLACED") {
t.Fatalf("%+v", st)
}
+4 -4
View File
@@ -135,8 +135,8 @@ func TestClosedConnectionsArePerUserAndNameAndSayWhichWereDropped(t *testing.T)
const closed = `{"connections":[
{"cid":10,"name":"mesh-controller verb conditions","authorized_user":"controller","start":"2026-10-08T14:12:00Z","stop":"2026-10-08T14:12:01Z","reason":"Client Closed"},
{"cid":11,"name":"mesh-controller verb conditions","authorized_user":"controller","start":"2026-10-08T14:13:00Z","stop":"2026-10-08T14:13:03Z","reason":"Client Closed"},
{"cid":12,"name":"ace.node-tools","authorized_user":"ace.node-tools","start":"2026-10-08T13:00:00Z","stop":"2026-10-08T14:14:00Z","reason":"Stale Connection"},
{"cid":13,"name":"ace.node-tools","authorized_user":"ace.node-tools","start":"2026-10-08T14:14:05Z","stop":"2026-10-08T14:15:00Z","reason":"Read Error"},
{"cid":12,"name":"node-a.node-tools","authorized_user":"node-a.node-tools","start":"2026-10-08T13:00:00Z","stop":"2026-10-08T14:14:00Z","reason":"Stale Connection"},
{"cid":13,"name":"node-a.node-tools","authorized_user":"node-a.node-tools","start":"2026-10-08T14:14:05Z","stop":"2026-10-08T14:15:00Z","reason":"Read Error"},
{"cid":5,"name":"old","authorized_user":"controller","start":"2026-10-08T10:00:00Z","stop":"2026-10-08T10:00:01Z","reason":"Client Closed"},
{"cid":14,"name":"","start":"2026-10-08T14:16:00Z","stop":"2026-10-08T14:16:00Z","reason":"Authentication Failure"}]}`
now, _ := time.Parse(time.RFC3339, "2026-10-08T14:20:00Z")
@@ -157,7 +157,7 @@ func TestClosedConnectionsArePerUserAndNameAndSayWhichWereDropped(t *testing.T)
c.Names[0].Name != "mesh-controller verb conditions" || c.Names[0].Lived != 3 {
t.Errorf("the controller's: %+v", c)
}
tools := byUser["ace.node-tools"]
tools := byUser["node-a.node-tools"]
if tools.Closed != 2 || tools.Dropped != 2 || tools.Names[0].Reasons["Stale Connection"] != 1 {
t.Errorf("the runtime's: %+v", tools)
}
@@ -168,7 +168,7 @@ func TestClosedConnectionsArePerUserAndNameAndSayWhichWereDropped(t *testing.T)
t.Error("a record older than the window does not reach back the whole window")
}
narrowed, _ := ClosedConnections([]byte(closed), now, 1, "node-tools")
if got := narrowed["users"].([]ClosedUser); len(got) != 1 || got[0].User != "ace.node-tools" {
if got := narrowed["users"].([]ClosedUser); len(got) != 1 || got[0].User != "node-a.node-tools" {
t.Errorf("narrowed: %+v", got)
}
}