package builds import ( "testing" "time" ) func TestTheBuildersLogKeepsAStartAndAnEndAndTheirDuration(t *testing.T) { // ADR 0571 clause 1: the builder's log, a start and an end, and the duration between them. at := time.Date(2026, 10, 29, 22, 0, 0, 0, time.UTC) start := BuildLogEntry{Event: BuildStartedEvent, Module: "letta", FP: "src2:a", Source: "abc", Repository: "novox/mesh-catalog", Directory: "modules/letta", Commit: "abc123", Node: "ace", At: at} end := start end.Event, end.At, end.Outcome, end.Manifest, end.Against = BuildEndedEvent, at.Add(3*time.Minute), "done", "sha256:m", []string{"artifact-store://base/image@sha256:b"} end.Made = []BuiltArtifact{{Name: "image", Kind: "image", Digest: "sha256:1", Reference: "artifact-store://letta/image@sha256:1"}} for _, e := range []BuildLogEntry{start, end} { raw, err := EncodeBuildLogEntry(e) back, derr := DecodeBuildLogEntry(raw) if err != nil || derr != nil || back.Event != e.Event || back.Outcome != e.Outcome || back.Directory != "modules/letta" || back.Repository != "novox/mesh-catalog" || back.Commit != "abc123" { t.Errorf("%s read back as %+v, %v %v", e.Event, back, err, derr) } } if d, ok := BuildDuration(start, end); !ok || d != 3*time.Minute { t.Errorf("the build took %s, %v", d, ok) } older := end older.At, older.FP = at.Add(-time.Hour), "src2:b" other := end other.Module = "mesh-catalog" if n := NewestPerModule([]BuildLogEntry{older, end, other}); n["letta"].FP != "src2:a" || len(n) != 2 { t.Errorf("each module's newest is %v", n) } if e := EndedSince([]BuildLogEntry{end, start, older}, at.Add(-2*time.Hour)); len(e) != 2 || e[0].FP != "src2:b" { t.Errorf("the ended builds since are %v", e) } if k, err := BuildLogKey("letta"); k != "letta" || err != nil || BuildLogRead != "mesh-delivery-builder.build-log" { t.Errorf("the key is %q, %v", k, err) } if _, err := BuildLogKey("a.b"); err == nil { t.Error("a module with a dot was taken as a key") } for _, edit := range []func(*BuildLogEntry){func(e *BuildLogEntry) { e.Outcome = "" }, func(e *BuildLogEntry) { e.Commit = "" }, func(e *BuildLogEntry) { e.Event, e.Outcome, e.Against = BuildStartedEvent, "", nil }} { bad := end edit(&bad) if _, err := EncodeBuildLogEntry(bad); err == nil { t.Errorf("%+v was encoded", bad) } } } func TestOnlyABuildsStartOrEndIsABuildEntry(t *testing.T) { for entry, want := range map[string]bool{`{"event":"build.started"}`: true, `{"event":"build.ended"}`: true, `{"event":"build.said"}`: false, `not json`: true, `{}`: true} { if got := IsBuildEntry([]byte(entry)); got != want { t.Errorf("IsBuildEntry(%s) = %v", entry, got) } } }