107 lines
5.2 KiB
Go
107 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
"github.com/novox/mesh-controller/internal/conditions"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// 1146/AC6: the first merge into a repository no registered module is built from asks the first build of every module
|
|
// the merge commit holds when it was made on the operator's Approve of that head, and asks nothing without it.
|
|
func TestAFirstMergeIntoANewRepositoryAsksItsModulesBuildsOnlyOnTheApprove(t *testing.T) {
|
|
const head = "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"
|
|
now := time.Date(2026, 10, 19, 0, 6, 0, 0, time.UTC)
|
|
mergedAt := now.Add(-time.Hour).Format(time.RFC3339Nano)
|
|
heard := link.SourceMoved{Owner: "novox", Repo: "mesh-delivery", Base: "main", HeadSHA: head, Commit: "0177ea7a",
|
|
Number: 1, MergedAt: mergedAt, ModuleDirsSaid: true, ModuleDirs: []string{"watchman", "view"},
|
|
Paths: []string{"view/module.json", "watchman/module.json", "README.md"}}
|
|
built := inventory.Entry{Source: inventory.Source{Repository: "novox/mesh-catalog", Seat: "git"}}
|
|
for _, c := range []struct {
|
|
what string
|
|
change func(*anApprovedMerge, *link.SourceMoved, *[]inventory.Entry)
|
|
unkept bool
|
|
dirs []string
|
|
refused bool
|
|
waits bool
|
|
}{
|
|
{what: "the Approve of the merged head", dirs: []string{"view", "watchman"}},
|
|
{what: "a Decline", refused: true, change: func(a *anApprovedMerge, _ *link.SourceMoved, _ *[]inventory.Entry) {
|
|
a.warrant.Option, a.warrant.Label, a.merge.Option = "decline", "Decline", "decline"
|
|
}},
|
|
{what: "no word of mesh-delivery yet, however long ago the merge was made", unkept: true, waits: true},
|
|
{what: "a module already built from the repository", change: func(_ *anApprovedMerge, _ *link.SourceMoved, e *[]inventory.Entry) {
|
|
*e = append(*e, inventory.Entry{Source: inventory.Source{Repository: "novox/mesh-delivery", Seat: "git"}})
|
|
}},
|
|
{what: "a merge into another branch", change: func(_ *anApprovedMerge, m *link.SourceMoved, _ *[]inventory.Entry) { m.Base = "feat" }},
|
|
{what: "a merge that holds no module.json", change: func(_ *anApprovedMerge, m *link.SourceMoved, _ *[]inventory.Entry) {
|
|
m.ModuleDirs, m.Paths = nil, []string{"README.md"}
|
|
}},
|
|
} {
|
|
t.Run(c.what, func(t *testing.T) {
|
|
a := aDeliveryMergeOf(t, head, map[string]string{"novox/mesh-delivery@a1a1a1a1a1a1": head})
|
|
a.merge.Number = 1
|
|
m, entries := heard, []inventory.Entry{built}
|
|
if c.change != nil {
|
|
c.change(&a, &m, &entries)
|
|
}
|
|
wasKept, wasAsk := keptDeliveryMerge, routerAskOf
|
|
t.Cleanup(func() { keptDeliveryMerge, routerAskOf = wasKept, wasAsk })
|
|
keptDeliveryMerge = func(context.Context, string, string, int, string) (deliveryMerge, bool, error) {
|
|
return a.merge, !c.unkept, nil
|
|
}
|
|
routerAskOf = func(context.Context, catalogueReader, string) (*asks.Ask, *asks.Warrant, error) {
|
|
return &a.ask, &a.warrant, nil
|
|
}
|
|
dirs, _, err := firstBuildsOf(t.Context(), nil, m, entries, nil)
|
|
switch {
|
|
case c.waits && !errors.Is(err, errNotAnnounced):
|
|
t.Fatalf("1146/AC6: %s: want a wait for the word, got %v", c.what, err)
|
|
case c.refused && !errors.Is(err, errFirstNotApproved):
|
|
t.Fatalf("1146/AC6: %s: want the Approve missing, got %v (dirs %v)", c.what, err, dirs)
|
|
case !c.refused && !c.waits && err != nil:
|
|
t.Fatalf("1146/AC6: %s: %v", c.what, err)
|
|
case !slices.Equal(dirs, c.dirs):
|
|
t.Fatalf("1146/AC6: %s: first builds asked of %v, want %v", c.what, dirs, c.dirs)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 1146/AC8: a first merge waiting past the bound for mesh-delivery's word is said, naming the repository, the commit and
|
|
// the missing word; one inside the bound, one that is no first merge, and one answered (no longer waiting) are not.
|
|
func TestAFirstMergeWaitingPastItsBoundIsSaidAndClearsWhenAnswered(t *testing.T) {
|
|
now := time.Date(2026, 10, 19, 1, 0, 0, 0, time.UTC)
|
|
waiting := func(commit string, first bool, heard time.Time) inventory.BatchedMerge {
|
|
event, _ := json.Marshal(keptMerge{SourceMoved: link.SourceMoved{Owner: "novox", Repo: "mesh-delivery", Base: "main",
|
|
Commit: commit, Number: 1, HeadSHA: "a1a1a1a1a1a1"}, FirstBuild: first})
|
|
return inventory.BatchedMerge{Repository: "novox/mesh-delivery", Branch: "main", Commit: commit, Heard: heard, Event: event}
|
|
}
|
|
got := firstBuildObservations([]inventory.BatchedMerge{
|
|
waiting("0177ea7a00", true, now.Add(-firstBuildWaitBound-time.Minute)),
|
|
waiting("1111111100", true, now.Add(-time.Minute)),
|
|
waiting("2222222200", false, now.Add(-time.Hour)),
|
|
}, now)
|
|
if len(got) != 1 {
|
|
t.Fatalf("1146/AC8: %d conditions raised, want the one merge past the bound: %+v", len(got), got)
|
|
}
|
|
o := got[0]
|
|
if o.Kind != kindFirstBuildUnasked || o.Scope != conditions.ScopeMerge || o.Severity != conditions.Warning ||
|
|
!strings.Contains(o.Summary, "novox/mesh-delivery") || !strings.Contains(o.Summary, "0177ea7a") ||
|
|
!strings.Contains(o.Summary, "announcement of the operator's Approve") {
|
|
t.Fatalf("1146/AC8: the condition is %+v", o)
|
|
}
|
|
// Answered, the merge is no longer listed as waiting: nothing is observed, and the condition clears.
|
|
if cleared := firstBuildObservations(nil, now); len(cleared) != 0 {
|
|
t.Fatalf("1146/AC8: an answered merge still raises %+v", cleared)
|
|
}
|
|
}
|