103 lines
4.1 KiB
Go
103 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// Every state, log and read the manifest declares has a tool that reads it, and every act the module takes by itself
|
|
// a tool that starts it (novox/hq ADR 0565 §15): the verb config's actions, and the switch's tools.
|
|
func TestEveryStateAndActHasItsTool(t *testing.T) {
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var m struct {
|
|
State []string `json:"state"`
|
|
Packages map[string]string `json:"packages"`
|
|
Logs []string `json:"logs"`
|
|
Reads []string `json:"reads"`
|
|
Tools []string `json:"tools"`
|
|
Claims []struct {
|
|
Name string `json:"name"`
|
|
Serves []string `json:"serves"`
|
|
} `json:"claims"`
|
|
}
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The builder's keys are read through its published package, taken by its series (ADR 0634 §1).
|
|
if m.Packages["builds"] != "0.1" {
|
|
t.Errorf("packages %v: want builds of series 0.1", m.Packages)
|
|
}
|
|
via := map[string]string{
|
|
ValuesState: "config list", ResolvedState: "config list", PredefinedState: "config list",
|
|
ProposalsState: "config proposals", PullsState: "config list", KeptState: "config list", HistoryLog: "config history",
|
|
DeclaredState: DeclaredTool,
|
|
PlacementRead: "config list", WantsRead: "config list", BuildsRead: "config list", ModulesRead: DeclaredTool,
|
|
ManifestsRead: DeclaredTool, IndexerPredefinedRead: "config list", CandidatesRead: ResyncTool,
|
|
"a resolution": ResyncTool, "the indexer's candidates taken": ResyncTool,
|
|
// design 32: each new state with the tool that reads it, each act with the tool that starts it.
|
|
RenamesState: RenamesTool, RetiringState: RetiringTool, FindingsState: "config_renames and config_retiring",
|
|
"a move refused": RenameAgainTool, "a window dropped": RenameCloseTool, "an ask again": RetiringTool,
|
|
"the owners' first views": FirstViewsTool, "a first view asked again": FirstViewAgainTool,
|
|
}
|
|
served := map[string]bool{}
|
|
for _, tool := range Tools(&Config{}) {
|
|
served[tool.Name] = true
|
|
}
|
|
if len(m.Claims) != 1 || m.Claims[0].Name != Seat || strings.Join(m.Claims[0].Serves, ",") != strings.Join(SeatVerbs, ",") {
|
|
t.Errorf("claims %+v, want %s serving %v", m.Claims, Seat, SeatVerbs)
|
|
}
|
|
// The stream and its events are declared as the module emits them, and the manifest decodes whole.
|
|
if dm, err := catalogue.Decode(raw); err != nil {
|
|
t.Errorf("the manifest does not decode: %v", err)
|
|
} else {
|
|
// An event goes to one place (novox/hq ADR 0363 §2): config-changed to EVENTS, the rename window's to the stream.
|
|
if !slices.Equal(dm.Emits, []string{ConfigChanged}) {
|
|
t.Errorf("emits %v, want only %s: the stream carries the others", dm.Emits, ConfigChanged)
|
|
}
|
|
if len(dm.Streams) != 1 || dm.Streams[0].Name != StreamName || dm.Streams[0].Days() != 7 || !dm.Streams[0].Carries(EventRenamed) ||
|
|
!dm.Streams[0].Carries(EventRetiring) || !dm.Streams[0].Carries(EventRetired) || dm.Streams[0].Carries(ConfigChanged) {
|
|
t.Errorf("streams %+v: want %s, seven days, carrying renamed, retiring and retired but not config-changed", dm.Streams, StreamName)
|
|
}
|
|
}
|
|
for _, verb := range SeatVerbs {
|
|
if !served[Seat+"."+verb] {
|
|
t.Errorf("the seat's verb %s is not served", verb)
|
|
}
|
|
}
|
|
for _, name := range m.Tools {
|
|
if !served[name] {
|
|
t.Errorf("the manifest lists the tool %s, which is not served", name)
|
|
}
|
|
}
|
|
// The delivery owners' state is read through their tools and views (ADR 0615 §1): only the builder's builds, which has
|
|
// no tool yet, is still a read.
|
|
if len(m.Reads) != 1 || m.Reads[0] != BuildsRead {
|
|
t.Errorf("reads %v, want the builder's builds alone", m.Reads)
|
|
}
|
|
for _, list := range [][]string{m.State, m.Logs, m.Reads} {
|
|
for _, s := range list {
|
|
if _, ok := via[s]; !ok {
|
|
t.Errorf("%s has no tool that reads it", s)
|
|
}
|
|
}
|
|
}
|
|
for what, tool := range via {
|
|
name, _, _ := strings.Cut(tool, " ")
|
|
if name == "config" {
|
|
name = Seat + "." + Verb
|
|
}
|
|
if !served[name] {
|
|
t.Errorf("%s is read or started by %s, which is not served", what, tool)
|
|
}
|
|
}
|
|
}
|