package catalogue import ( "reflect" "strings" "testing" ) // Defends novox/hq ADR 0212: a seat says what it receives, its holder places it, and a contribution // depends on the seat. func hotkeysHolder() Manifest { return Manifest{Module: "triggerhappy", Claims: []Claim{{Name: HotkeysSeat}}, Resources: []map[string]any{ {"id": "triggers", "type": "file", "path": "/etc/triggerhappy/triggers.d/mesh.conf", "content": "# the mesh's triggers\n${contribution:node-hotkeys:trigger}"}, }} } func TestAContributionReachesTheHoldersFileInModuleOrderNamedByModule(t *testing.T) { laptop := Manifest{Module: "laptop", Contributions: []SeatContribution{ {Seat: HotkeysSeat, Kind: "trigger", Content: "KEY_PROG1 1 play ${machine:account-home}"}, {Seat: HotkeysSeat, Kind: "trigger", Content: "KEY_F21 1 touchpad\n"}, }} another := Manifest{Module: "another", Contributions: []SeatContribution{ {Seat: HotkeysSeat, Kind: "trigger", Content: "KEY_MUTE 1 mute ${shell:zsh:first}"}, }} unrelated := Manifest{Module: "bar", Contributions: []SeatContribution{ {Seat: DisplaySessionSeat, Kind: "config", Content: "bar { }"}, }} r := Resolution{Node: "laptop", Account: "op", Modules: []Manifest{hotkeysHolder(), laptop, another, unrelated}} out, err := r.Declaration(Rendering{}) if err != nil { t.Fatal(err) } var file map[string]any for _, res := range out { if res["id"] == "triggerhappy.triggers" { file = res } } if file == nil { t.Fatalf("the holder's file was not composed: %v", out) } // Module order; each module named once; text never read, so neither ${machine:…} nor ${shell:…} // inside a contribution is filled. want := "# the mesh's triggers\n" + "# another\nKEY_MUTE 1 mute ${shell:zsh:first}\n" + "# laptop\nKEY_PROG1 1 play ${machine:account-home}\nKEY_F21 1 touchpad\n" if got := file["content"]; got != want { t.Fatalf("the holder's file is\n%s\nnot\n%s", got, want) } } func TestNoContributionPlacesNothing(t *testing.T) { r := Resolution{Node: "laptop", Account: "op", Modules: []Manifest{hotkeysHolder()}} out, err := r.Declaration(Rendering{}) if err != nil { t.Fatal(err) } for _, res := range out { if res["id"] == "triggerhappy.triggers" && res["content"] != "# the mesh's triggers\n" { t.Fatalf("an empty kind left %q", res["content"]) } } } func TestAContributionToASeatThatDoesNotReceiveItIsRefused(t *testing.T) { cases := map[string]string{ `{"seat":"node-hotkeys","kind":"config","content":"x"}`: "it receives trigger", `{"seat":"node-nothing","kind":"trigger","content":"x"}`: "the mesh does not define", `{"seat":"node-hotkeys","kind":"trigger","content":" "}`: "has no content", `{"seat":"node-display-session","kind":"trigger","content":"x"}`: "it receives config", } for c, want := range cases { raw := `{"module":"laptop","contributions":[` + c + `]}` if _, err := ParseManifest([]byte(raw)); err == nil || !strings.Contains(err.Error(), want) { t.Errorf("%s: accepted, or refused without %q: %v", c, want, err) } } } func TestAContributionPlaceholderOutsideTheHolderIsRefused(t *testing.T) { raw := `{"module":"laptop","resources":[{"id":"t","type":"file","path":"/etc/t","content":"${contribution:node-hotkeys:trigger}"}]}` if _, err := ParseManifest([]byte(raw)); err == nil || !strings.Contains(err.Error(), "laptop does not claim node-hotkeys") { t.Errorf("a placeholder outside the holder was accepted: %v", err) } raw = `{"module":"triggerhappy","claims":[{"name":"node-hotkeys"}],"resources":[{"id":"t","type":"file","path":"/etc/t","content":"${contribution:node-hotkeys:keys}"}]}` if _, err := ParseManifest([]byte(raw)); err == nil || !strings.Contains(err.Error(), "it receives trigger") { t.Errorf("a placeholder for a kind the seat does not receive was accepted: %v", err) } } func TestAContributionDependsOnItsSeat(t *testing.T) { m := Manifest{Module: "laptop", Contributions: []SeatContribution{ {Seat: HotkeysSeat, Kind: "trigger", Content: "x"}, {Seat: DisplaySessionSeat, Kind: "config", Content: "y"}, }} if got, want := DependsOn(m), []string{DisplaySessionSeat, HotkeysSeat}; !reflect.DeepEqual(got, want) { t.Errorf("depends on %v, want %v", got, want) } catalogue := map[string]Manifest{"laptop": m, "triggerhappy": hotkeysHolder()} if _, err := AssignRefusal(catalogue, "laptop", nil, []string{"laptop"}); err == nil || !strings.Contains(err.Error(), HotkeysSeat) { t.Errorf("a contributor without the holder was accepted: %v", err) } } func TestTheHotkeysSeatIsTheMeshsAndReceivesTriggers(t *testing.T) { s, ok := SeatNamed(HotkeysSeat) if !ok || s.Scope != ScopeNode || len(s.Receives) != 1 || s.Receives[0].Kind != "trigger" { t.Fatalf("%+v %v", s, ok) } d, _ := SeatNamed(DisplaySessionSeat) if len(d.Receives) != 1 || d.Receives[0].Kind != "config" { t.Fatalf("the display session receives %+v", d.Receives) } }