The seat mesh-builds' accepted kind step (cherry-picked contract 0dc8c723 of feat/47-G-recover-runner) names an entry by fingerprint; the builder reads it through the catalog half or the seat mesh-catalog's entry, builds it through its one claim, plan and lanes (a build per fingerprint, no pull request number) and says the end as step-ended.<node> under the ask's id, naming each entry as asked. A build already done is passed at once; the end of a build wakes its waiting step through the log entry. Builds: design 47 G §230, §194, §473, A-84, A-91, A-97, A-99 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
77 lines
3.2 KiB
Go
77 lines
3.2 KiB
Go
package build
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshbuilds"
|
|
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
|
"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)
|
|
}
|
|
}
|