The controller asks no build of anybody (ADR 0501 §5, ADR 0525 clause 6, ADR 0503 §3). Every module of a walk is taken from the builder's done key; a key not done yet waits, named in the plan, and never fails it. - C2b/C2c: internal/builder and cmd/mesh-controller's asks go (askABuild, onlyTheBuildersOwn, buildOneAsked, takeIn from an outcome, buildAgentsOwn, the builder's say and its fallback, the daemon hearing an outcome). The server follows the retired seats' built subjects only to let go of them, because the installer's carried user list names them. - C2d: replay, queue, cancel, clear, kill, pause, resume, delivery-check, check-here and merge-gate are refused by name. - C3: the check queues (CHECKS_READY, CHECKS_OTHER) and their workers, the checks-running and plans-building buckets and their grants, the build queue and its hold (plans_building.go, queue.go, link/holds.go, builds_nats.go, checks_nats.go). - C4: merge_gate*.go, the pull request's repo-check path, the Checked event and its layers; a pull request asks the controller nothing but to tell mesh-config what its head declares. - C5: the builds bucket is written only by key_takein. - C10 (help and refusals): the terminal's help lists only what is served. Integration tests of the plan's tiers, sends, stops and retries now run on a stand-in builder with a done key for every module (walk_keys_scaffold_integration_test.go); the tests of the asks, the queue, the merge gate and the replays are deleted.
100 lines
3.2 KiB
Go
100 lines
3.2 KiB
Go
package link
|
|
|
|
import (
|
|
"go/parser"
|
|
"go/token"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
)
|
|
|
|
// Every subject the controller consumes is a kind with a contract, and every test a contract names
|
|
// exists (novox/hq to-be 45 Phase 2, ADR 0227 rule 2).
|
|
func TestEveryConsumedKindHasAContract(t *testing.T) {
|
|
// What the controller can be handed, from the subjects it is granted and the ones it derives.
|
|
subjects := []string{EnrolSubject, ReportSubject("anchor"),
|
|
ToolsAliveSubject("anchor"), HealthSubject("anchor"), "mesh.mod.postgres.event.provisioner.failing"}
|
|
subjects = append(subjects, broker.ControllerFollows...)
|
|
subjects = append(subjects, broker.ControllerHears...)
|
|
kinds := map[string]bool{}
|
|
for _, s := range subjects {
|
|
kind, known := kindOfSubject(s)
|
|
if !known {
|
|
if strings.Contains(s, "*") {
|
|
continue // a pattern among the follows; its concrete subject is listed above
|
|
}
|
|
t.Errorf("the controller follows %s and has no kind for it", s)
|
|
continue
|
|
}
|
|
kinds[kind] = true
|
|
}
|
|
for kind := range kinds {
|
|
c, ok := Contracts[kind]
|
|
switch {
|
|
case !ok:
|
|
t.Errorf("the controller consumes %s and no contract says how an older one is told from a newer", kind)
|
|
case (c.Ordered == "") == (c.Unordered == ""):
|
|
t.Errorf("%s's contract says neither, or both, how it is ordered and why it need not be: %+v", kind, c)
|
|
case c.Ordered != "" && len(c.Tests) == 0:
|
|
t.Errorf("%s is ordered and no test delivers the newer and then the older", kind)
|
|
}
|
|
}
|
|
for kind := range Contracts {
|
|
if !kinds[kind] {
|
|
t.Errorf("a contract for %s, which the controller does not consume", kind)
|
|
}
|
|
}
|
|
|
|
// Every test named exists in this repository.
|
|
exists := map[string]bool{}
|
|
fset := token.NewFileSet()
|
|
err := filepath.WalkDir("../..", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if d.IsDir() && (d.Name() == "vendor" || d.Name() == ".git") {
|
|
return filepath.SkipDir
|
|
}
|
|
if d.IsDir() || !strings.HasSuffix(path, "_test.go") {
|
|
return nil
|
|
}
|
|
f, err := parser.ParseFile(fset, path, nil, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for name, obj := range f.Scope.Objects {
|
|
if obj.Kind.String() == "func" && strings.HasPrefix(name, "Test") {
|
|
exists[name] = true
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for kind, c := range Contracts {
|
|
for _, name := range c.Tests {
|
|
if !exists[name] {
|
|
t.Errorf("%s's contract names %s, which no test in this repository is", kind, name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1183/AC3: the controller no longer takes the node-engine's alive message: its subject is no kind, so nothing is
|
|
// subscribed to it and nothing hears it. The tool runner's tools-alive stays.
|
|
func TestTheAliveMessageIsNoKindTheControllerHears(t *testing.T) { // 1183/AC3
|
|
if kind, known := kindOfSubject("mesh.control.anchor.alive"); known {
|
|
t.Fatalf("the controller still takes the alive message as %s", kind)
|
|
}
|
|
if kind, known := kindOfSubject(ToolsAliveSubject("anchor")); !known || kind != KindToolsHeartbeat {
|
|
t.Fatalf("the tool runner's tools-alive is %q, %v", kind, known)
|
|
}
|
|
if _, ok := Contracts["heartbeat"]; ok {
|
|
t.Fatal("a contract still names the alive message")
|
|
}
|
|
}
|