159 lines
8.3 KiB
Go
159 lines
8.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// novox/hq issue 459, ADR 0299: since the merge window one walk carries every merge of its batch, so a stop of one
|
|
// group member through `delivery-stop` ended the walk of every other delivery in it. The stop now names the merges
|
|
// to leave out: the walk is ended, and every other merge it answered is walked again in a fresh batch without them
|
|
// — the stopped merge, and each later merge of its repository's branch, which contains it and ends with it.
|
|
|
|
// endsItsLine ends, as the test ends, a line the controller's output left open (a walk's ask prints " tier N: " and
|
|
// the asked build the rest, which the tests' fake ask never prints): `go test -v` would put the test's "--- PASS"
|
|
// after it, and the store-tests step of merge-check.sh, which reads that marker at a line's start, would say the
|
|
// test never ran.
|
|
func endsItsLine(t *testing.T) {
|
|
t.Cleanup(func() { fmt.Println() })
|
|
}
|
|
|
|
// leaved is a kept merge of a repository, made at a moment.
|
|
func leaved(repository, commit string, made time.Time) inventory.BatchedMerge {
|
|
return inventory.BatchedMerge{Repository: repository, Branch: "main", Commit: commit, Merged: made, Heard: made}
|
|
}
|
|
|
|
// The stopped merge is left out, and so is each later merge of its repository's branch, naming the merge it
|
|
// contains; an earlier merge of that repository and every other repository's merge are walked again.
|
|
func TestLeftOutAreTheStoppedMergeAndTheMergesThatContainIt(t *testing.T) {
|
|
c1 := leaved("novox/mesh-catalog", "c1aaaaaaaaaa", t0)
|
|
c2 := leaved("novox/mesh-catalog", "c2bbbbbbbbbb", t0.Add(10*time.Second))
|
|
c3 := leaved("novox/mesh-catalog", "c3cccccccccc", t0.Add(20*time.Second))
|
|
o1 := leaved("novox/one", "o1dddddddddd", t0.Add(5*time.Second))
|
|
again, out, err := leaveOut([]inventory.BatchedMerge{c1, o1, c2, c3}, []string{"novox/Mesh-Catalog@c2bbbbbbbbbb"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var walked []string
|
|
for _, m := range again {
|
|
walked = append(walked, m.Commit)
|
|
}
|
|
if !reflect.DeepEqual(walked, []string{c1.Commit, o1.Commit}) {
|
|
t.Fatalf("walked again: %v; want the earlier catalogue merge and the other repository's", walked)
|
|
}
|
|
want := []inventory.PlanLeftOut{{Repository: "novox/mesh-catalog", Commit: c2.Commit},
|
|
{Repository: "novox/mesh-catalog", Commit: c3.Commit, Contains: c2.Commit}}
|
|
if !reflect.DeepEqual(out, want) {
|
|
t.Fatalf("left out: %+v; want %+v", out, want)
|
|
}
|
|
// A merge the walk does not answer is refused, naming it: nothing ends.
|
|
if _, _, err := leaveOut([]inventory.BatchedMerge{c1, o1}, []string{"novox/mesh-catalog@ffffffffffff"}); err == nil ||
|
|
!strings.Contains(err.Error(), "ffffffffffff") {
|
|
t.Fatalf("a merge the walk does not answer was left out: %v", err)
|
|
}
|
|
if _, _, err := leaveOut([]inventory.BatchedMerge{c1}, []string{"no-commit"}); err == nil {
|
|
t.Fatal("a merge without its commit was taken")
|
|
}
|
|
}
|
|
|
|
// moving is a kept merge that moved modules when it was heard.
|
|
func moving(m inventory.BatchedMerge, modules ...string) inventory.BatchedMerge {
|
|
m.Event, _ = json.Marshal(keptMerge{Moves: modules})
|
|
return m
|
|
}
|
|
|
|
// A merge of another repository that moves a module the stopped merge moves is left out too, naming the modules
|
|
// it shares: the module is built at its branch, which holds the stopped merge (review of #232). And so on, until
|
|
// none more is: a merge sharing a module with that one is left out as well.
|
|
func TestAMergeSharingAStoppedMergesModuleIsLeftOut(t *testing.T) {
|
|
stopped := moving(leaved("novox/mesh-catalog", "c1aaaaaaaaaa", t0), "app")
|
|
reader := moving(leaved("novox/one", "o1bbbbbbbbbb", t0.Add(time.Second)), "app", "one")
|
|
next := moving(leaved("novox/two", "t1cccccccccc", t0.Add(2*time.Second)), "one")
|
|
free := moving(leaved("novox/three", "h1dddddddddd", t0.Add(3*time.Second)), "three")
|
|
again, out, err := leaveOut([]inventory.BatchedMerge{stopped, reader, next, free}, []string{"novox/mesh-catalog@c1aaaaaaa"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(again) != 1 || again[0].Commit != free.Commit {
|
|
t.Fatalf("walked again: %+v; want the merge that shares nothing alone", again)
|
|
}
|
|
want := []inventory.PlanLeftOut{{Repository: "novox/mesh-catalog", Commit: stopped.Commit},
|
|
{Repository: "novox/one", Commit: reader.Commit, Contains: stopped.Commit, Shares: []string{"app"}},
|
|
{Repository: "novox/two", Commit: next.Commit, Contains: stopped.Commit, Shares: []string{"one"}}}
|
|
if !reflect.DeepEqual(out, want) {
|
|
t.Fatalf("left out %+v; want %+v", out, want)
|
|
}
|
|
}
|
|
|
|
// A walk withholds a module a stopped merge moved where it would build it only as a dependent; one a merge of its
|
|
// own batch moves is built, and empty tiers go.
|
|
func TestAWalkWithholdsAStoppedMergesDependents(t *testing.T) {
|
|
plan := inventory.Plan{Tiers: [][]string{{"one"}, {"app"}, {"notes", "app2"}},
|
|
Modules: map[string]*inventory.PlanModule{"one": {}, "app": {}, "notes": {}, "app2": {}}}
|
|
got := withhold(&plan, []string{"app", "notes", "absent"}, []string{"notes"})
|
|
if !reflect.DeepEqual(got, []string{"app"}) || !reflect.DeepEqual(plan.Tiers, [][]string{{"one"}, {"notes", "app2"}}) {
|
|
t.Fatalf("withheld %v, tiers %v", got, plan.Tiers)
|
|
}
|
|
if _, in := plan.Modules["app"]; in {
|
|
t.Fatal("a withheld module is still in the plan")
|
|
}
|
|
}
|
|
|
|
// The seat verb passes the merges to leave out to the command.
|
|
func TestDeliveryStopTakesTheMergesToLeaveOut(t *testing.T) {
|
|
got, err := argvFor("delivery-stop", map[string]any{"plan": "plan-1", "why": "w", "by": "jochen",
|
|
"without": "novox/mesh-lab@b1,novox/mesh-lab@b2"})
|
|
want := []string{"delivery", "stop", "plan-1", "--why", "w", "--by", "mesh-delivery for jochen",
|
|
"--without", "novox/mesh-lab@b1,novox/mesh-lab@b2"}
|
|
if err != nil || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("delivery-stop with without → %v %v, wanted %v", got, err, want)
|
|
}
|
|
}
|
|
|
|
// A walk of the catalogue's merge and another repository's is stopped without the catalogue's: it ends stopped,
|
|
// names the batch that walks the other merge again and the merge it left out, and that batch is cut into a walk
|
|
// that builds the other repository's module alone.
|
|
|
|
// A later merge of the stopped merge's repository contains it: it is left out too, named with the merge it
|
|
// contains, and only the other repository's merge is walked again. With nothing left to walk, the walk is only
|
|
// ended. A merge the walk does not answer is refused, and nothing ends.
|
|
|
|
// A merge heard while the walk ran waits in the open batch, at a later commit of the same repository. The merge
|
|
// walked again joins that batch, never a batch of its own that would walk the older commit after the newer one
|
|
// (review of #232): its window closes, and its walk is at the newer commit, answering both.
|
|
|
|
// A stop names what the stopped walk already sent and nothing walks again, on which machines; and a later merge of
|
|
// the stopped merge's repository that waits in a batch, which will deliver the stopped change. A stopped walk
|
|
// that walked its merges again is refused a retry, saying why.
|
|
|
|
// A batch not yet cut is stopped without a merge: the merge goes to a stopped record of its own, the batch goes on
|
|
// with the others and withholds the stopped merge's modules; with every merge left out, the batch is stopped.
|
|
|
|
// The stopped walk, the batch and the merges moved are one act (ADR 0299 decision 7): a plan written by somebody
|
|
// else since it was read refuses all of it, and no merge moves, no plan is written.
|
|
|
|
// What a stopped walk leaves on the machines names every machine its sends reached (review of #232): a module sent
|
|
// with no first machine reached every machine running it; one sent first reached those, and the rest it was sent
|
|
// to; a module a merge walked again moves is sent again, and is not named.
|
|
func TestKeptNamesEveryMachineASendReached(t *testing.T) {
|
|
at := t0
|
|
p := inventory.Plan{Modules: map[string]*inventory.PlanModule{
|
|
"together": {SentAt: &at},
|
|
"first": {First: []string{"anchor"}, FirstAt: &at, Rest: map[string]inventory.SentDeclaration{"laptop": {}}},
|
|
"again": {SentAt: &at},
|
|
"unsent": {},
|
|
}}
|
|
got := keptOf(p, []string{"again"}, map[string][]string{"together": {"laptop", "anchor"}, "again": {"anchor"}})
|
|
want := []inventory.PlanKept{{Module: "first", Machines: []string{"anchor", "laptop"}},
|
|
{Module: "together", Machines: []string{"anchor", "laptop"}}}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("kept %+v; want %+v", got, want)
|
|
}
|
|
}
|