package build import ( "context" "errors" "strings" "testing" "time" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshbuilds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-sdk/go/catalogue" ) // The runner's build step ask is the builder's one entry point for a candidate (design 47 G A-97): each entry is read by its // fingerprint, built through the one claim, and the step's end says passed only for a build that is done. func TestABuildStepAnswersItsEnd(t *testing.T) { r := newRig(t, catalogue.Artifact{Name: "files", Kind: "archive"}) entryOf := func(fp, commit string) meshcatalog.CatalogEntry { return meshcatalog.CatalogEntry{Module: "m", Repository: "novox/m", Commit: commit, Fp: fp, ManifestDigest: mdigest('a'), Path: "."} } r.b.StepEntry = func(_ context.Context, fp string) (meshcatalog.CatalogEntry, error) { if fp == fp1 { return entryOf(fp1, "c1"), nil } return meshcatalog.CatalogEntry{}, errors.New("not found") } ask := func(entries ...meshbuilds.BuildEntry) meshbuilds.StepEnded { return r.b.BuildStep(t.Context(), meshbuilds.StepRun{Pipeline: "main.1.m", Step: meshbuilds.StepRunStepBuild, Entries: entries}) } if end := ask(); end.Outcome != meshbuilds.StepEndedOutcomeNothingToRun { t.Errorf("an ask naming no entry: %+v", end) } if end := ask(meshbuilds.BuildEntry{Module: "m", Fingerprint: "src2:" + strings.Repeat("2", 64)}); end.Outcome != meshbuilds.StepEndedOutcomeCouldNotRun || !strings.Contains(end.Said, "cannot be read") { t.Errorf("an entry the seat does not hold: %+v", end) } if end := ask(meshbuilds.BuildEntry{Module: "m", Fingerprint: fp1, Commit: "other"}); end.Outcome != meshbuilds.StepEndedOutcomeCouldNotRun { t.Errorf("an entry at another commit: %+v", end) } // A build already done is passed at once, naming each entry as it was asked. done, _ := builds.EncodeBuilderBuild(builds.BuilderBuild{State: builds.BuildDone, Manifest: mdigest('a')}) if _, err := r.st.Create(t.Context(), r.key, done); err != nil { t.Fatal(err) } entries := []meshbuilds.BuildEntry{{Module: "m", Fingerprint: fp1, Commit: "c1"}} if end := ask(entries...); end.Outcome != meshbuilds.StepEndedOutcomePassed || end.Step != meshbuilds.StepEndedStepBuild || len(end.Entries) != 1 || end.Entries[0].Commit != "c1" || end.Pipeline != "main.1.m" { t.Errorf("a done build: %+v", end) } } // A step ask waits for the end of its build without a timer: the log entry of that end wakes it (design 47 G A-97). func TestAStepAskIsWokenByItsBuildsEnd(t *testing.T) { r := newRig(t) key := r.key ch := r.b.awaitEnd(key) r.b.stepEnded(builds.BuildLogEntry{Event: builds.BuildStartedEvent, Module: "m", FP: fp1}) // a start wakes nothing select { case <-ch: t.Fatal("a build's start woke the wait") default: } r.b.stepEnded(builds.BuildLogEntry{Event: builds.BuildEndedEvent, Module: "m", FP: fp1, Outcome: builds.BuildDone}) select { case e := <-ch: if e.Outcome != builds.BuildDone { t.Errorf("end = %+v", e) } case <-time.After(time.Second): t.Fatal("the end of the build did not wake the wait") } r.b.dropEnd(key, ch) if len(r.b.stepWaits) != 0 { t.Errorf("waits left: %v", r.b.stepWaits) } }