package catalogue import ( "encoding/json" "strings" "testing" ) // A module declares the state it keeps and the state it reads (novox/hq ADR 0202), a bucket by its // bare name or with the owner's options. func TestAManifestMaySayWhatStateItKeepsAndReads(t *testing.T) { m, err := ParseManifest([]byte(`{"module":"claude-code","version":"1",` + `"state":["servers",{"name":"seen","history":5,"ttl-seconds":3600}],` + `"reads":["licence-manager.bindings"]}`)) if err != nil { t.Fatal(err) } if len(m.State) != 2 || m.State[0].Name != "servers" || m.State[1].History != 5 || m.State[1].TTLSeconds != 3600 { t.Fatalf("state not read: %+v", m.State) } if len(m.Reads) != 1 || m.Reads[0] != "licence-manager.bindings" { t.Fatalf("reads not read: %v", m.Reads) } // Written back as it came in: the short form where nothing else is said. out, _ := json.Marshal(m.State) if string(out) != `["servers",{"name":"seen","history":5,"ttl-seconds":3600}]` { t.Fatalf("written back as %s", out) } } // A name the bus could not hold, or that would let two modules derive one bucket, is refused at // registration in the manifest's words. func TestAStateNameIsLocalAndOneToken(t *testing.T) { for _, c := range []struct{ manifest, says string }{ {`{"module":"a","version":"1","state":["mesh.servers"]}`, `keeps state "mesh.servers": a state is named locally`}, {`{"module":"a","version":"1","state":["my_servers"]}`, `keeps state "my_servers"`}, {`{"module":"a","version":"1","state":["s","s"]}`, `keeps state "s" twice`}, {`{"module":"a","version":"1","state":[{"name":"s","history":65}]}`, `a key keeps between 1 and 64`}, {`{"module":"a.b","version":"1","state":["s"]}`, `no dot`}, {`{"module":"a","version":"1","reads":["bindings"]}`, `a read is .`}, {`{"module":"a","version":"1","reads":["a.s"]}`, `which is its own state`}, {`{"module":"a","version":"1","state":[{"name":"s","shared":true}]}`, `{name, history, ttl-seconds}`}, } { _, err := ParseManifest([]byte(c.manifest)) if err == nil { t.Errorf("%s was accepted", c.manifest) continue } if !strings.Contains(err.Error(), c.says) { t.Errorf("%s refused for the wrong reason: %v", c.manifest, err) } } } // A read whose owner is present must name a state that owner keeps; an absent owner says nothing, // because a module may be installed before the one whose state it reads. func TestAReadNamesStateItsOwnerKeeps(t *testing.T) { owner := Manifest{Module: "licence-manager", State: []StateDeclaration{{Name: "bindings"}}} good := Manifest{Module: "claude-code", Reads: []string{"licence-manager.bindings", "absent.anything"}} bad := Manifest{Module: "other", Reads: []string{"licence-manager.tokens"}} if p := StateReadsNothingDeclares([]Manifest{owner, good}); len(p) != 0 { t.Fatalf("a read of declared state was refused: %v", p) } p := StateReadsNothingDeclares([]Manifest{owner, bad}) if len(p) != 1 || !strings.Contains(p[0], `licence-manager keeps no state called "tokens"`) { t.Fatalf("a read of state nobody keeps was not named: %v", p) } } // **Across the whole catalogue**: every state name is local, and every read whose owner is present // names state that owner keeps. func TestEveryManifestsStateIsLocalAndEveryReadIsKept(t *testing.T) { manifests := theCatalogue(t) var problems []string for _, m := range manifests { problems = append(problems, StateProblems(m)...) } problems = append(problems, StateReadsNothingDeclares(manifests)...) if len(problems) > 0 { t.Fatalf("the catalogue's state is not what ADR 0202 says:\n %s", strings.Join(problems, "\n ")) } } // `module check` says it too: the cross-catalogue pass names a read nothing on the shelf keeps. func TestTheCataloguePassNamesAReadItsOwnerDoesNotKeep(t *testing.T) { shelf := Shelf{ "licence-manager": {Module: "licence-manager", State: []StateDeclaration{{Name: "bindings"}}}, "claude-code": {Module: "claude-code", Reads: []string{"licence-manager.tokens"}}, } problems := CatalogueProblems(shelf) if len(problems) != 1 || !strings.Contains(problems[0], `keeps no state called "tokens"`) { t.Fatalf("the catalogue pass said %v", problems) } }