package catalogue import ( "encoding/json" "reflect" "strings" "testing" ) // Defends novox/hq ADR 0208: the graphical session is one module per piece, on the mesh's seats. // §2: the eleven roles are the mesh's own node seats, each with the verbs it starts with. func TestTheGraphicalSessionsSeatsAreTheMeshsOwnWithTheirVerbs(t *testing.T) { want := map[string][]string{ LoginManagerSeat: {"sessions"}, DisplayServerSeat: {"displays", "layout"}, DisplaySessionSeat: {"reload", "workspaces", "windows"}, TerminalEmulatorSeat: {"open"}, LauncherSeat: {"menu", "secret"}, NotifierSeat: {"send", "history"}, LockScreenSeat: {"lock"}, ClipboardSeat: {"history", "copy"}, BarSeat: nil, CompositorSeat: nil, SecretServiceSeat: nil, } for name, verbs := range want { s, ok := SeatNamed(name) if !ok { t.Errorf("%s is not in the mesh's set", name) continue } if s.Scope != ScopeNode || s.Decision != "novox/hq ADR 0208" { t.Errorf("%s is %s-scoped under %q", name, s.Scope, s.Decision) } var got []string for _, v := range s.Serves { got = append(got, v.Name) if v.Description == "" || v.Input["type"] != "object" { t.Errorf("%s.%s has no description or no object schema", name, v.Name) } } if !reflect.DeepEqual(got, verbs) { t.Errorf("%s serves %v, want %v", name, got, verbs) } } // The launcher's menu takes a list, and the layout verb says its actions. menu, _ := SeatNamed(LauncherSeat) choices := menu.Serves[0].Input["properties"].(map[string]any)["choices"].(map[string]any) if choices["type"] != "array" { t.Errorf("menu's choices are %v, not a list", choices["type"]) } display, _ := SeatNamed(DisplayServerSeat) action := display.Serves[1].Input["properties"].(map[string]any)["action"].(map[string]any) if !reflect.DeepEqual(action["enum"], []string{"list", "save", "apply"}) { t.Errorf("layout's actions are %v", action["enum"]) } // And they survive the store's JSON, which is where the live set comes from. if _, err := json.Marshal(graphicalSessionSeats()); err != nil { t.Fatal(err) } } func displayServer(name, display string, claims ...string) Manifest { m := Manifest{Module: name, Provides: []Offer{{Name: display, Reach: ReachMachine}}} for _, c := range claims { m.Claims = append(m.Claims, Claim{Name: c}) } return m } func windowManager() Manifest { return Manifest{Module: "i3", Requires: []string{"x11-display"}} } // §3: a display is resolved on the requiring module's own node. func TestAMachineReachRequirementResolvesToTheProviderOnItsOwnNode(t *testing.T) { cat := shelf(windowManager(), displayServer("xorg", "x11-display", DisplayServerSeat)) got, err := Resolve(cat, []string{"xorg", "i3"}, workstation(), World{}) if err != nil { t.Fatalf("i3 beside xorg did not resolve: %v", err) } if !reflect.DeepEqual(names(got), []string{"xorg", "i3"}) && !reflect.DeepEqual(names(got), []string{"i3", "xorg"}) { t.Errorf("resolved %v", names(got)) } } // §3: never answered by installing a provider, and never by another machine's. func TestAMachineReachRequirementIsNotPulledInNorAnsweredFromAnotherNode(t *testing.T) { cat := shelf(windowManager(), displayServer("xorg", "x11-display", DisplayServerSeat), displayServer("xwayland", "x11-display")) // Another machine runs xorg and says so to the world; it does not count. world := World{Offered: map[string][]Provider{ "x11-display": {{Node: "laptop", At: "laptop.mesh", Module: "xorg"}}}} _, err := Resolve(cat, []string{"i3"}, workstation(), world) if err == nil { t.Fatal("i3 resolved on a machine with no display of its own") } for _, want := range []string{ `"x11-display" is wanted by i3`, "usable only on the machine that provides it", "assign one to workstation", "xorg (holds node-display-server)", "xwayland", } { if !strings.Contains(err.Error(), want) { t.Errorf("the refusal does not say %q:\n%v", want, err) } } // With a single provider in the catalogue too: one candidate is still not a choice to make // for somebody, unlike a node-scoped provision without the machine's reach. _, err = Resolve(shelf(windowManager(), displayServer("xorg", "x11-display", DisplayServerSeat)), []string{"i3"}, workstation(), World{}) if err == nil { t.Fatal("xorg was pulled in for i3") } // Not in the first pass, whose refusals take the machine off the network. if _, err := Resolve(cat, []string{"i3"}, workstation(), World{Unchecked: true}); err != nil { t.Errorf("the first pass refused: %v", err) } } func TestACatalogueDisagreeingAboutAProvisionsReachIsRefused(t *testing.T) { cat := shelf(windowManager(), displayServer("xorg", "x11-display"), Manifest{Module: "fake-x", Provides: Offers("x11-display")}) _, err := Resolve(cat, []string{"xorg", "i3"}, workstation(), World{}) if err == nil || !strings.Contains(err.Error(), `the catalogue disagrees about "x11-display"`) { t.Fatalf("a provision with and without the machine's reach gave %v", err) } }