package main import ( "bytes" "reflect" "strings" "testing" "time" sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // A build's outcome is taken in the same way whoever hears it (novox/hq issue 176): recorded, and // the module registered with its source as the seat and path when the request said so — never the // URL. A definition naming an installation is recorded and not registered; a failure is recorded // and said. // novox/hq 04-ISSUES/215: a build asked at a commit is recorded as built from that commit, and the // module keeps following the branch it followed — a new one, the default branch. // novox/hq 04-ISSUES/219: an older request heard after a newer one is recorded and not registered, // so a push sends what the newer request built. func TestABuildIDSaysWhenItWasAsked(t *testing.T) { at := time.Date(2026, 10, 3, 21, 51, 57, 392539762, time.UTC) if got, ok := link.BuildAskedAt(link.NewBuildID(at)); !ok || !got.Equal(at) { t.Errorf("read back %v %v; want %v", got, ok, at) } if got, ok := link.BuildAskedAt("build-1791064317392539762"); !ok || got.Format(time.TimeOnly) != "21:51:57" { t.Errorf("the incident's id reads as %v %v", got, ok) } for _, id := range []string{"b-1", "build-2", "build-", "build-x", ""} { if _, ok := link.BuildAskedAt(id); ok { t.Errorf("%q read as a request time", id) } } } // novox/hq issue 1204: builds says the source fingerprint the builder recorded, the one tool that reads it. func TestBuildsSaysTheSourceFingerprint(t *testing.T) { at := time.Date(2026, 10, 19, 10, 40, 0, 0, time.UTC) built := inventory.Build{ID: "build-1", Module: "mailu", Commit: "b4527d9091e972f28cb365623d9280045e7741a3", On: "ace", Repository: "novox/mesh-catalog", At: at, ReuseKey: "r1:abc", SourceFingerprint: "src1:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"} // 1204/AC1 var out bytes.Buffer printBuild(&out, built) lines := strings.Split(strings.TrimRight(out.String(), "\n"), "\n") if last := lines[len(lines)-1]; last != " source "+built.SourceFingerprint { t.Errorf("last line %q; want the source line after the reuse key:\n%s", last, out.String()) } if !strings.Contains(lines[len(lines)-2], "reuse key r1:abc") { t.Errorf("the source line does not follow the reuse key:\n%s", out.String()) } // 1204/AC2 without := built without.SourceFingerprint = "" var plain bytes.Buffer printBuild(&plain, without) if strings.Contains(plain.String(), "source") { t.Errorf("a build with no fingerprint says a source line:\n%s", plain.String()) } if want := strings.Join(lines[:len(lines)-1], "\n") + "\n"; plain.String() != want { t.Errorf("the other lines changed:\n%s\nwant\n%s", plain.String(), want) } } // 1215/AC4: what the builder said of the inputs is what the controller keeps of the build. func TestTheControllerKeepsTheInputsABuilderSaid(t *testing.T) { in := &sdkdelivery.SourceInputs{Module: "mailu", Tree: "t", Toolchains: map[string]string{"code": "ts sha256:aa 01"}} kept := buildFrom(link.BuildResult{ID: "build-1", Repository: "r", Commit: "c", SourceFingerprint: "src1:x", SourceInputs: in}) if !reflect.DeepEqual(kept.SourceInputs, in) { t.Errorf("the controller keeps %+v, the builder said %+v", kept.SourceInputs, in) } }