View: read no build record and no composition of the controller's: fingerprints against the builder's done keys alone, a move's judgement from the node agent's have (issue 1955, ADR 0571 §1, §6)
This commit is contained in:
@@ -48,7 +48,7 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
kvs := map[string]jetstream.KeyValue{}
|
||||
for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, view.BuildsBucket, delivery.PresenceBucket, delivery.StuckBucket} {
|
||||
for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket} {
|
||||
kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -82,8 +82,6 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8
|
||||
}
|
||||
put(delivery.PlacementBucket, "placement.a", placeValue("a", "x", "y"))
|
||||
put(delivery.PlacementBucket, "placement.b", placeValue("b"))
|
||||
put(view.BuildsBucket, "x.build.b1", []byte(`{"id":"b1","module":"x"}`))
|
||||
put(view.BuildsBucket, "x.newest", []byte("b1"))
|
||||
put(delivery.HaveBucket, "a.x", haveValue("b1", delivery.PhaseChecking, delivery.HealthWaits))
|
||||
put(delivery.HaveBucket, "b.x", haveValue("b1", delivery.PhaseRunning, delivery.HealthOK))
|
||||
|
||||
@@ -161,7 +159,7 @@ func TestProcessWholeShowsWhatTheWatchmanSays(t *testing.T) { // 1168/AC5
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
kvs := map[string]jetstream.KeyValue{}
|
||||
for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, view.BuildsBucket, delivery.PresenceBucket, delivery.StuckBucket} {
|
||||
for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket} {
|
||||
kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -247,7 +245,7 @@ func TestProcessWholeShowsTheIndexersBucketsOnceTheyExist(t *testing.T) { // 124
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, view.BuildsBucket, delivery.PresenceBucket, delivery.StuckBucket} {
|
||||
for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket} {
|
||||
if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
return err
|
||||
})
|
||||
model.SetBacklog(view.BacklogOver(js, 5*time.Second))
|
||||
for _, bucket := range []string{delivery.HaveBucket, delivery.PlacementBucket, view.BuildsBucket, delivery.PresenceBucket, delivery.StuckBucket, delivery.CandidatesBucket, delivery.ModulesBucket, delivery.AsksBucket, delivery.AnswersBucket, delivery.BuilderBuildsBucket, delivery.AssignerWantsBucket, delivery.SourceBucket, delivery.ControllerComposedBucket, delivery.AssignerAsksBucket, delivery.ViewAnswersBucket, delivery.TakenBucket} {
|
||||
for _, bucket := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket, delivery.CandidatesBucket, delivery.ModulesBucket, delivery.AsksBucket, delivery.AnswersBucket, delivery.BuilderBuildsBucket, delivery.AssignerWantsBucket, delivery.SourceBucket, delivery.AssignerAsksBucket, delivery.ViewAnswersBucket, delivery.TakenBucket} {
|
||||
go follow(ctx, js, bucket, false, model, logger)
|
||||
}
|
||||
// The modules' and the source's history give each fingerprint's first commit and each head's source revision (ADR 0556).
|
||||
|
||||
@@ -50,30 +50,6 @@ func modEntry(t *testing.T, name string, m delivery.Module) Entry {
|
||||
return Entry{Bucket: delivery.ModulesBucket, Key: name, Value: b, At: t0}
|
||||
}
|
||||
|
||||
// trunkBuild is a build record as the controller writes it (mesh-sdk go/delivery/testdata/build-record-build-agent.json is
|
||||
// the captured shape: id, state, repository, ref, module, commit, path, source-fingerprint, source-inputs, at).
|
||||
func trunkBuild(module, id, ref, state, fp, at string) []Entry {
|
||||
// an outcome has no state key; a failed one has "failed" (mesh-controller internal/chain/records.go:57-59, :109)
|
||||
rec := `{"id":"` + id + `",`
|
||||
switch state {
|
||||
case "":
|
||||
case "failed":
|
||||
rec += `"failed":"the build failed",`
|
||||
default:
|
||||
rec += `"state":"` + state + `",`
|
||||
}
|
||||
rec += `"repository":"novox/mesh-delivery","ref":"` + ref + `","module":"` + module + `","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","path":"modules/` + module + `","at":"` + at + `"`
|
||||
if fp != "" {
|
||||
// the controller writes the inputs whenever it writes the fingerprint (mesh-sdk testdata/build-record-build-agent.json)
|
||||
rec += `,"source-fingerprint":"` + fp + `","source-inputs":{"Module":"` + module + `","Tree":"t","Bases":null,"Contexts":null,"Toolchains":null,"Unpinned":""}`
|
||||
}
|
||||
rec += `}`
|
||||
return []Entry{
|
||||
{Bucket: BuildsBucket, Key: module + ".build." + id, Value: []byte(rec), At: t0},
|
||||
{Bucket: BuildsBucket, Key: module + ".newest", Value: []byte(id), At: t0},
|
||||
}
|
||||
}
|
||||
|
||||
func fpRow(t *testing.T, s Snapshot, module string) FingerprintRow {
|
||||
t.Helper()
|
||||
for _, f := range s.Fingerprints {
|
||||
@@ -111,62 +87,27 @@ func TestSnapshotListsTheCandidateGroups(t *testing.T) { // 1241/AC1
|
||||
}
|
||||
}
|
||||
|
||||
func TestFingerprintAgainstTheNewestTrunkBuild(t *testing.T) { // 1241/AC2
|
||||
// 1955/L7 (ADR 0571 §1): the view reads no build record of the controller's: a module with no fingerprint of the new
|
||||
// form is said so, never compared with the controller's builds.
|
||||
func TestAModuleOfTheOldFormIsComparedWithNoBuild(t *testing.T) {
|
||||
m := New()
|
||||
fill(m, trunkBuild("a", "build-1", "main", "", fF, "2026-10-19T10:00:00Z")...)
|
||||
fill(m, trunkBuild("d", "build-2", "main", "", fF, "2026-10-19T10:00:00Z")...)
|
||||
fill(m,
|
||||
modEntry(t, "a", delivery.Module{FP: fF, Repository: "novox/mesh-delivery", Commit: hA}),
|
||||
modEntry(t, "d", delivery.Module{FP: fG, Repository: "novox/mesh-delivery", Commit: hA}),
|
||||
modEntry(t, "b", delivery.Module{None: "an artifact compiles a Go program from a build source that git trees do not pin: no fingerprint before release 4", Repository: "novox/mesh-delivery", Commit: hA}),
|
||||
modEntry(t, "e", delivery.Module{FP: fG, Repository: "novox/mesh-delivery", Commit: hA}))
|
||||
modEntry(t, "b", delivery.Module{None: "an artifact compiles a Go program from a build source that git trees do not pin: no fingerprint before release 4", Repository: "novox/mesh-delivery", Commit: hA}))
|
||||
s := m.Snapshot()
|
||||
if r := fpRow(t, s, "a"); r.State != FPSame || r.Indexer != fF || r.Build != fF {
|
||||
t.Errorf("a = %+v, want same", r)
|
||||
}
|
||||
if r := fpRow(t, s, "d"); r.State != FPDiffers || r.Indexer != fG || r.Build != fF {
|
||||
t.Errorf("d = %+v, want differs with both", r)
|
||||
}
|
||||
if r := fpRow(t, s, "b"); r.State != FPNone || !strings.Contains(r.Reason, "release 4") || r.Indexer != "" {
|
||||
t.Errorf("b = %+v, want none with the reason", r)
|
||||
}
|
||||
if r := fpRow(t, s, "e"); r.State != FPNoBuild || r.Indexer != fG || r.Build != "" {
|
||||
t.Errorf("e = %+v, want no build with the indexer's alone", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnlyTheNewestSuccessfulTrunkBuildCounts(t *testing.T) { // 1241/AC2 (the newest key is only a hint)
|
||||
m := New()
|
||||
fill(m, trunkBuild("a", "build-1", "main", "", fG, "2026-10-19T09:00:00Z")...)
|
||||
fill(m, trunkBuild("a", "build-2", "main", "", fF, "2026-10-19T10:00:00Z")...)
|
||||
fill(m, trunkBuild("a", "build-3", "main", "failed", fG, "2026-10-19T11:00:00Z")...) // newer, not built
|
||||
fill(m, trunkBuild("a", "build-4", "feat/x", "", fG, "2026-10-19T12:00:00Z")...) // newer, not trunk; also the newest key
|
||||
fill(m, modEntry(t, "a", delivery.Module{FP: fF, Repository: "novox/mesh-delivery", Commit: hA}))
|
||||
if r := fpRow(t, m.Snapshot(), "a"); r.State != FPSame || r.Build != fF {
|
||||
t.Errorf("a = %+v, want same with the 10:00 trunk build's fingerprint", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFingerprintIsReadWithTheSDKRecord(t *testing.T) { // 1241/AC3
|
||||
// The record as the controller's encoder wrote it (mesh-sdk go/delivery/testdata/build-record-build-agent.json), whole,
|
||||
// with its source-inputs under their Go field names: the view reads source-fingerprint from it.
|
||||
const rec = `{"id":"build-1792395447088936520","repository":"novox/mesh-controller","ref":"main","module":"build-agent","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","path":"modules/build-agent","manifest-digest":"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","source-fingerprint":"src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7","source-inputs":{"Module":"build-agent","Tree":"11c0b4f90cae0d6e4ef9cc48cbfdf9f6dbe52bca","Bases":["sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"],"Contexts":{"server":"narrow:d51ca744fd11af59d616c858f76c27b3e58e16e11fbdc0c720eb9d7d19d55fac"},"Toolchains":null,"Unpinned":""},"made":[{"name":"server","kind":"image","reference":"registry.example/build-agent@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"}],"at":"2026-10-19T09:38:00Z"}`
|
||||
const fp = "src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7"
|
||||
m := New()
|
||||
fill(m, Entry{Bucket: BuildsBucket, Key: delivery.BuildKey("build-agent", "build-1792395447088936520"), Value: []byte(rec), At: t0},
|
||||
Entry{Bucket: BuildsBucket, Key: delivery.NewestKey("build-agent"), Value: []byte("build-1792395447088936520"), At: t0},
|
||||
modEntry(t, "build-agent", delivery.Module{FP: fp, Repository: "novox/mesh-controller", Commit: hA}))
|
||||
if r := fpRow(t, m.Snapshot(), "build-agent"); r.State != FPSame || r.Build != fp {
|
||||
t.Errorf("build-agent = %+v, want same with %s", r, fp)
|
||||
for _, name := range []string{"a", "b"} {
|
||||
if r := fpRow(t, s, name); r.State != FPNone || !(strings.Contains(r.Reason, "new form") || strings.Contains(r.Reason, "release 4")) || r.Build != "" {
|
||||
t.Errorf("%s = %+v, want none: no build of the builder's is compared", name, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shown(t *testing.T) *Model {
|
||||
m := New()
|
||||
fill(m, trunkBuild("a", "build-1", "main", "", fF, "2026-10-19T10:00:00Z")...)
|
||||
fill(m,
|
||||
candidate(t, "feat/x", pullOf("novox/mesh-delivery", 12, hA, "a"), pullOf("novox/mesh-catalog", 300, hB, "c")),
|
||||
modEntry(t, "a", delivery.Module{FP: fG, Repository: "novox/mesh-delivery", Commit: hA}),
|
||||
modEntry(t, "a", delivery.Module{FP2: fp2A, Repository: "novox/mesh-delivery", Commit: hA}),
|
||||
builderKey(t, "a", fp2A, delivery.BuilderBuild{State: delivery.BuildFailed, Error: "the compile failed"}),
|
||||
modEntry(t, "b", delivery.Module{None: "no tree", Repository: "novox/mesh-delivery", Commit: hA}))
|
||||
return m
|
||||
}
|
||||
@@ -175,14 +116,14 @@ func TestTableAndPagePrintTheGroupsAndTheComparison(t *testing.T) { // 1241/AC6
|
||||
m := shown(t)
|
||||
var out bytes.Buffer
|
||||
Table(&out, m.Snapshot())
|
||||
for _, want := range []string{"CANDIDATES", "feat/x", "novox/mesh-delivery#12", hA[:12], "novox/mesh-catalog#300", "FINGERPRINT", "differs", "indexer " + fG, "build " + fF, "no tree"} {
|
||||
for _, want := range []string{"CANDIDATES", "feat/x", "novox/mesh-delivery#12", hA[:12], "novox/mesh-catalog#300", "FINGERPRINT", "the compile failed", fp2A, "no tree"} {
|
||||
if !strings.Contains(out.String(), want) {
|
||||
t.Errorf("the table lacks %q:\n%s", want, out.String())
|
||||
}
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
Web(m).ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||
for _, want := range []string{"CANDIDATES", "feat/x", "novox/mesh-delivery#12", hA[:12], "FINGERPRINTS", "differs", fG, fF, "no tree"} {
|
||||
for _, want := range []string{"CANDIDATES", "feat/x", "novox/mesh-delivery#12", hA[:12], "FINGERPRINTS", "the compile failed", fp2A, "no tree"} {
|
||||
if !strings.Contains(rec.Body.String(), want) {
|
||||
t.Errorf("the page lacks %q:\n%s", want, rec.Body.String())
|
||||
}
|
||||
@@ -198,23 +139,3 @@ func TestUnreadableIndexerValuesAreNamed(t *testing.T) { // never dropped silent
|
||||
t.Errorf("snapshot = %+v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPlanAskedBuildWithNoRefIsATrunkBuild(t *testing.T) { // 1281/AC5
|
||||
// The build record of module mesh-delivery from mesh-catalog, build-1792435539606203626, as `mesh-controller.command
|
||||
// builds` showed it on 2026-10-19 at about 21:20 local: built on ace at 20:45 local, the repository the clone URL, the
|
||||
// source fingerprint below, and no ref (a delivery plan asked it after a merge; the listing shows no "at main").
|
||||
// No reading command shows the whole record: its path is the module's directory in the catalogue, and its
|
||||
// source-inputs are mailu's live ones (mesh-sdk go/delivery/fingerprint_test.go) with the module's name put in.
|
||||
const live = "src1:f79e2eedc2689cf6b3591ebfdb81ec65899131122fb2fe3dd9e384091d8a2a6d"
|
||||
const rec = `{"id":"build-1792435539606203626","repository":"http://novox.internal:20000/novox/mesh-catalog.git",` +
|
||||
`"module":"mesh-delivery","commit":"9efdd87a","path":"modules/mesh-delivery","at":"2026-10-19T18:45:00Z","source-fingerprint":"` + live + `",` +
|
||||
`"source-inputs":{"Module":"mesh-delivery","Tree":"cfc6fa8ff5fc1e220132e51ade653b9d3f387b3e","Bases":["sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"],"Contexts":null,"Toolchains":null,"Unpinned":""}}`
|
||||
m := New()
|
||||
fill(m,
|
||||
Entry{Bucket: BuildsBucket, Key: "mesh-delivery.build.build-1792435539606203626", Value: []byte(rec), At: t0},
|
||||
Entry{Bucket: BuildsBucket, Key: "mesh-delivery.newest", Value: []byte("build-1792435539606203626"), At: t0},
|
||||
modEntry(t, "mesh-delivery", delivery.Module{FP: live, Repository: "novox/mesh-catalog", Commit: hA}))
|
||||
if r := fpRow(t, m.Snapshot(), "mesh-delivery"); r.State != FPSame || r.Build != live {
|
||||
t.Errorf("mesh-delivery = %+v, want same as the no-ref build's fingerprint", r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,8 +103,8 @@ type WantRow struct {
|
||||
Newest string `json:"newest,omitempty"`
|
||||
Rule int `json:"rule,omitempty"`
|
||||
Words string `json:"words,omitempty"`
|
||||
// Judgement is the controller's judgement of the move to the composition the want names (recreates, moves-data, neither or
|
||||
// cannot-judge), and Judged its words; empty when the controller wrote none (ADR 0539 §14).
|
||||
// Judgement is the node agent's judgement of the move that waits for a person's yes (its have in waiting-yes, ADR 0547
|
||||
// §3), and Judged its words; empty otherwise. The controller's composed copy is no longer read (ADR 0571 §6).
|
||||
Judgement string `json:"judgement,omitempty"`
|
||||
Judged string `json:"judged,omitempty"`
|
||||
}
|
||||
@@ -589,13 +589,6 @@ func (m *Model) apply(e Entry) {
|
||||
}
|
||||
w, err := delivery.DecodeWant(e.Value)
|
||||
m.wants[key] = wantEntry{value: w, err: errText(err), at: e.At}
|
||||
case delivery.ControllerComposedBucket:
|
||||
if e.Deleted {
|
||||
delete(m.composed, e.Key)
|
||||
return
|
||||
}
|
||||
d, err := delivery.DecodeComposedDigest(e.Value)
|
||||
m.composed[e.Key] = composedEntry{value: d, err: errText(err)}
|
||||
case delivery.TakenBucket:
|
||||
if e.Deleted {
|
||||
delete(m.taken, e.Key)
|
||||
@@ -643,34 +636,6 @@ func (m *Model) apply(e Entry) {
|
||||
return
|
||||
}
|
||||
m.keys[e.Key] = keyEntry{value: b}
|
||||
case BuildsBucket:
|
||||
module, rest, ok := strings.Cut(e.Key, ".")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case rest == "newest":
|
||||
if e.Deleted {
|
||||
delete(m.newest, module)
|
||||
return
|
||||
}
|
||||
m.newest[module] = strings.TrimSpace(string(e.Value))
|
||||
case strings.HasPrefix(rest, "build."):
|
||||
if e.Deleted {
|
||||
delete(m.builds, e.Key)
|
||||
return
|
||||
}
|
||||
var b build
|
||||
if json.Unmarshal(e.Value, &b) != nil {
|
||||
return
|
||||
}
|
||||
// The source fingerprint is read with the SDK's record, not with the view's own struct.
|
||||
var rec delivery.BuildRecord
|
||||
if json.Unmarshal(e.Value, &rec) != nil {
|
||||
return
|
||||
}
|
||||
m.builds[e.Key] = buildEntry{build: b, rec: rec}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,38 +834,22 @@ func (m *Model) groupRows() []GroupRow {
|
||||
// successful trunk build (delivery.BuildRecord.IsTrunkBuild: an outcome, not failed, on main or with no ref) that recorded one. The newest key is any state and ref, so it is not
|
||||
// used: the records are.
|
||||
func (m *Model) fingerprintRows() []FingerprintRow {
|
||||
newest := map[string]buildEntry{}
|
||||
for _, b := range m.builds {
|
||||
if b.rec.Module == "" || !b.rec.IsTrunkBuild() || b.rec.SourceFingerprint == "" {
|
||||
continue
|
||||
}
|
||||
if old, ok := newest[b.rec.Module]; !ok || b.rec.At.After(old.rec.At) || (b.rec.At.Equal(old.rec.At) && b.rec.ID > old.rec.ID) {
|
||||
newest[b.rec.Module] = b
|
||||
}
|
||||
}
|
||||
out := []FingerprintRow{}
|
||||
for name, mod := range m.modules {
|
||||
if mod.err != "" {
|
||||
continue
|
||||
}
|
||||
row := FingerprintRow{Module: name, Repository: mod.value.Repository, Commit: mod.value.Commit}
|
||||
if mod.value.FP2 != "" || mod.value.None2 != "" {
|
||||
// The new form (R4e): the indexer's fp2 against the builder's key of it.
|
||||
out = append(out, m.newFormRow(name, row, mod.value))
|
||||
if mod.value.FP2 == "" && mod.value.None2 == "" {
|
||||
// The builder builds the new form alone, and its done keys are the build history the view reads (ADR 0571 §1).
|
||||
row.State, row.Reason = FPNone, mod.value.None
|
||||
if row.Reason == "" {
|
||||
row.Reason = "the indexer holds no fingerprint of the new form for it, so no build of the builder's is compared"
|
||||
}
|
||||
out = append(out, row)
|
||||
continue
|
||||
}
|
||||
b, hasBuild := newest[name]
|
||||
switch {
|
||||
case mod.value.None != "":
|
||||
row.State, row.Reason = FPNone, mod.value.None
|
||||
case !hasBuild:
|
||||
row.State, row.Indexer = FPNoBuild, mod.value.FP
|
||||
case b.rec.SourceFingerprint == mod.value.FP:
|
||||
row.State, row.Indexer, row.Build = FPSame, mod.value.FP, b.rec.SourceFingerprint
|
||||
default:
|
||||
row.State, row.Indexer, row.Build = FPDiffers, mod.value.FP, b.rec.SourceFingerprint
|
||||
}
|
||||
out = append(out, row)
|
||||
out = append(out, m.newFormRow(name, row, mod.value))
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Module < out[j].Module })
|
||||
return out
|
||||
@@ -959,11 +908,6 @@ func (s Snapshot) Counted() int {
|
||||
return n
|
||||
}
|
||||
|
||||
// The buckets the view reads besides the have bucket (which mesh-sdk's delivery package names).
|
||||
const (
|
||||
BuildsBucket = "mesh-controller_builds"
|
||||
)
|
||||
|
||||
// The statuses of a row.
|
||||
const (
|
||||
StatusAssigned = "assigned"
|
||||
@@ -1089,10 +1033,8 @@ func (m *Model) wantRows() []WantRow {
|
||||
if has && h.err == "" {
|
||||
row.Have, row.Phase, row.Health = h.have.FP, string(h.have.Phase), string(h.have.Health)
|
||||
}
|
||||
if ckey, err := delivery.ComposedKey(node, module, v.FP); err == nil && !v.Removed {
|
||||
if c, ok := m.composed[ckey]; ok && c.err == "" {
|
||||
row.Judgement, row.Judged = string(c.value.Judgement), c.value.Words
|
||||
}
|
||||
if has && h.err == "" && h.have.Phase == delivery.PhaseWaitingYes && !v.Removed {
|
||||
row.Judgement, row.Judged = string(h.have.Judgement), h.have.Detail
|
||||
}
|
||||
switch {
|
||||
case v.Removed:
|
||||
|
||||
@@ -45,14 +45,6 @@ func place(t *testing.T, node string, modules ...string) Entry {
|
||||
return Entry{Bucket: delivery.PlacementBucket, Key: key, Value: v, At: t0}
|
||||
}
|
||||
|
||||
func buildRec(module, id, extra string) []Entry {
|
||||
rec := `{"id":"` + id + `","module":"` + module + `","commit":"c"` + extra + `}`
|
||||
return []Entry{
|
||||
{Bucket: BuildsBucket, Key: module + ".build." + id, Value: []byte(rec), At: t0},
|
||||
{Bucket: BuildsBucket, Key: module + ".newest", Value: []byte(id), At: t0},
|
||||
}
|
||||
}
|
||||
|
||||
func find(t *testing.T, s Snapshot, node, module string) Row {
|
||||
t.Helper()
|
||||
for _, r := range s.Rows {
|
||||
@@ -78,9 +70,6 @@ func base(t *testing.T) []Entry {
|
||||
haveE(t, "b", "x", have(t, "b1", delivery.PhaseChecking, delivery.HealthWaits, "starting"), t0.Add(2*time.Minute)),
|
||||
haveE(t, "b", "z", have(t, "b3", delivery.PhaseRunning, delivery.HealthOK, ""), t0.Add(3*time.Minute)),
|
||||
}
|
||||
es = append(es, buildRec("x", "b1", "")...)
|
||||
es = append(es, buildRec("y", "b2", "")...)
|
||||
es = append(es, buildRec("z", "b3", "")...)
|
||||
return es
|
||||
}
|
||||
|
||||
|
||||
@@ -143,16 +143,15 @@ func TestTheWantsPageSaysWantsAgainstHaves(t *testing.T) {
|
||||
|
||||
// 1906/D7 (ADR 0539 §14): the controller's judgement of the move a want names is shown beside it, with its words.
|
||||
func TestTheJudgementOfTheMoveIsShownBesideTheWant(t *testing.T) {
|
||||
// 1955/L7 (ADR 0571 §6): the judgement is the node agent's, in its have waiting for a yes; the controller's composed copy
|
||||
// is not read.
|
||||
m := New()
|
||||
key, err := delivery.ComposedKey("ace", "web", fpNew)
|
||||
h, err := delivery.EncodeHave(delivery.Have{FP: fpOld, Phase: delivery.PhaseWaitingYes, Health: delivery.HealthOK,
|
||||
Judgement: delivery.JudgedCannot, Waits: "sha256:" + strings.Repeat("c", 64), Detail: "the composition it runs is gone"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v, err := delivery.EncodeComposedDigest(delivery.ComposedDigest{Digest: "sha256:" + strings.Repeat("c", 64), Judgement: delivery.JudgedCannot, Words: "the composition it runs is gone"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fill(m, wantE(t, "ace", "web", delivery.Want{FP: fpNew}), Entry{Bucket: delivery.ControllerComposedBucket, Key: key, Value: v, At: t0})
|
||||
fill(m, wantE(t, "ace", "web", delivery.Want{FP: fpNew}), Entry{Bucket: delivery.HaveBucket, Key: "ace.web", Value: h, At: t0})
|
||||
rows := m.Snapshot().Wants
|
||||
if len(rows) != 1 || rows[0].Judgement != string(delivery.JudgedCannot) || !strings.Contains(rows[0].Judged, "gone") {
|
||||
t.Fatalf("rows = %+v", rows)
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
|
||||
if m.Module != "mesh-delivery-view" {
|
||||
t.Errorf("module = %q", m.Module)
|
||||
}
|
||||
want := []string{"mesh-build-go.pauses", "mesh-build-go.steps", "mesh-build-image.pauses", "mesh-build-image.steps", "mesh-build-node.pauses", "mesh-build-node.steps", "mesh-conditions.conditions", "mesh-config.resolved", "mesh-controller.builds", "mesh-controller.composed", "mesh-delivery-assigner.asks", "mesh-delivery-assigner.differences", "mesh-delivery-assigner.placement", "mesh-delivery-assigner.wants", "mesh-delivery-builder.builds", "mesh-delivery-checker.tries", "mesh-delivery-gate.asks", "mesh-delivery-gate.source", "mesh-delivery-indexer.candidates", "mesh-delivery-indexer.config-candidates", "mesh-delivery-indexer.heard", "mesh-delivery-indexer.modules", "mesh-delivery-indexer.predefined", "mesh-delivery-indexer.taken", "mesh-delivery-teller.answers", "mesh-delivery-watchman.presence", "mesh-delivery-watchman.stuck", "mesh-healers.attempts", "mesh-vault.signers", "node-engine.have"} // view_status reads the keeper's conditions and the healers' attempts (1952/V1); build_usage reads the build modules' steps and pauses, mesh-config's capacities and the checker's tries; 1168/AC1, 1241/AC4, 1345/AC3, 1297/B8, B9
|
||||
want := []string{"mesh-build-go.pauses", "mesh-build-go.steps", "mesh-build-image.pauses", "mesh-build-image.steps", "mesh-build-node.pauses", "mesh-build-node.steps", "mesh-conditions.conditions", "mesh-config.resolved", "mesh-delivery-assigner.asks", "mesh-delivery-assigner.differences", "mesh-delivery-assigner.placement", "mesh-delivery-assigner.wants", "mesh-delivery-builder.builds", "mesh-delivery-checker.tries", "mesh-delivery-gate.asks", "mesh-delivery-gate.source", "mesh-delivery-indexer.candidates", "mesh-delivery-indexer.config-candidates", "mesh-delivery-indexer.heard", "mesh-delivery-indexer.modules", "mesh-delivery-indexer.predefined", "mesh-delivery-indexer.taken", "mesh-delivery-teller.answers", "mesh-delivery-watchman.presence", "mesh-delivery-watchman.stuck", "mesh-healers.attempts", "mesh-vault.signers", "node-engine.have"} // view_status reads the keeper's conditions and the healers' attempts (1952/V1); build_usage reads the build modules' steps and pauses, mesh-config's capacities and the checker's tries; 1168/AC1, 1241/AC4, 1345/AC3, 1297/B8, B9
|
||||
got := slices.Clone(m.Reads)
|
||||
slices.Sort(got)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
|
||||
@@ -41,8 +41,6 @@
|
||||
"mesh-build-node.pauses",
|
||||
"mesh-build-node.steps",
|
||||
"mesh-config.resolved",
|
||||
"mesh-controller.builds",
|
||||
"mesh-controller.composed",
|
||||
"mesh-delivery-assigner.asks",
|
||||
"mesh-delivery-assigner.differences",
|
||||
"mesh-delivery-assigner.placement",
|
||||
|
||||
Reference in New Issue
Block a user