Split the unit level from the integration level in cmd, builder, link, catalogue, inventory; cheap fixtures for the archive-entry bound, the registry waits and the catalogue copy (issue 895)
This commit is contained in:
@@ -7,10 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/broker"
|
||||
"github.com/novox/mesh-controller/internal/inventory"
|
||||
"github.com/novox/mesh-controller/internal/link"
|
||||
"github.com/novox/mesh-controller/internal/testbus"
|
||||
)
|
||||
|
||||
func rolloutOf(sentAgo time.Duration, reported bool) (inventory.Plan, []inventory.Reported) {
|
||||
@@ -112,49 +109,9 @@ func eventually(t *testing.T, what string, ok func() bool) {
|
||||
}
|
||||
|
||||
// Asks made while a look runs are not lost and are one look after it, not one each.
|
||||
func TestAsksMadeWhileALookRunsAreOneLookAfterIt(t *testing.T) {
|
||||
var failures, looks atomic.Int32
|
||||
block := make(chan struct{})
|
||||
l := lookerCounting(&failures, &looks, block)
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
go l.run(ctx)
|
||||
l.ask()
|
||||
eventually(t, "the first look starts", func() bool { return looks.Load() == 1 })
|
||||
for i := 0; i < 20; i++ {
|
||||
l.ask()
|
||||
}
|
||||
block <- struct{}{} // the first look ends
|
||||
eventually(t, "the asks made meanwhile are looked at", func() bool { return looks.Load() == 2 })
|
||||
block <- struct{}{}
|
||||
time.Sleep(60 * time.Millisecond)
|
||||
if n := looks.Load(); n != 2 {
|
||||
t.Errorf("%d looks for 21 asks; want 2", n)
|
||||
}
|
||||
}
|
||||
|
||||
// A look that failed a step is looked at again, one at a time with a longer wait each time, and a look that fails
|
||||
// nothing ends the retries (novox/hq ADR 0406 §5; the one named retry).
|
||||
func TestAFailedLookIsLookedAtAgainAndAGoodOneEndsTheRetries(t *testing.T) {
|
||||
var failures, looks atomic.Int32
|
||||
failures.Store(3) // three looks fail, then one is good
|
||||
l := lookerCounting(&failures, &looks, nil)
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
go l.run(ctx)
|
||||
l.ask()
|
||||
eventually(t, "four looks", func() bool { return looks.Load() == 4 })
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
if n := looks.Load(); n != 4 {
|
||||
t.Errorf("%d looks; the good one should have ended the retries at 4", n)
|
||||
}
|
||||
l.mu.Lock()
|
||||
armed, backoff := l.retry != nil, l.backoff
|
||||
l.mu.Unlock()
|
||||
if armed || backoff != 0 {
|
||||
t.Errorf("a retry is armed (%v) or the wait kept (%v) after a good look", armed, backoff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheRetryWaitDoublesToItsMost(t *testing.T) {
|
||||
l := newLooker(func(context.Context) bool { return true })
|
||||
@@ -175,35 +132,6 @@ func TestTheRetryWaitDoublesToItsMost(t *testing.T) {
|
||||
}
|
||||
|
||||
// A plan saved asks for a look, and a walk saved done is armed to be recorded once its end can be known.
|
||||
func TestAPlanSavedAsksForALookAndADoneWalkIsArmedToBeRecorded(t *testing.T) {
|
||||
var failures, looks atomic.Int32
|
||||
l := lookerCounting(&failures, &looks, nil)
|
||||
lookerMu.Lock()
|
||||
theLooker = l
|
||||
lookerMu.Unlock()
|
||||
t.Cleanup(func() { lookerMu.Lock(); theLooker = nil; lookerMu.Unlock() })
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
go l.run(ctx)
|
||||
|
||||
planKept(inventory.Plan{ID: "walk-1", State: inventory.PlanDone})
|
||||
t.Cleanup(func() {
|
||||
walkEnds.mu.Lock()
|
||||
defer walkEnds.mu.Unlock()
|
||||
if t := walkEnds.timers["walk-1"]; t != nil {
|
||||
t.Stop()
|
||||
}
|
||||
delete(walkEnds.timers, "walk-1")
|
||||
})
|
||||
eventually(t, "a look", func() bool { return looks.Load() == 1 })
|
||||
walkEnds.mu.Lock()
|
||||
_, armed := walkEnds.timers["walk-1"]
|
||||
walkEnds.mu.Unlock()
|
||||
if !armed {
|
||||
t.Error("a walk saved done is not armed to be recorded")
|
||||
}
|
||||
askToLook() // no panic, no looker, once it is taken away
|
||||
}
|
||||
|
||||
// The tier-sends a step made are said once each, per machine, with the modules it was sent.
|
||||
func TestTheSendsAStepMadeAreSaidPerMachine(t *testing.T) {
|
||||
@@ -221,77 +149,9 @@ func TestTheSendsAStepMadeAreSaidPerMachine(t *testing.T) {
|
||||
}
|
||||
|
||||
// A silent key that expires asks for a look; a key a report deleted does not (issue 974, novox/hq ADR 0406 §5).
|
||||
func TestAnExpiredSilentKeyAsksForALookAndAReportsDeleteDoesNot(t *testing.T) {
|
||||
js, err := broker.Dial(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(js.Close)
|
||||
if err := js.EnsureControllerBuckets(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var failures, looks atomic.Int32
|
||||
l := lookerCounting(&failures, &looks, nil)
|
||||
lookerMu.Lock()
|
||||
theLooker = l
|
||||
lookerMu.Unlock()
|
||||
t.Cleanup(func() { lookerMu.Lock(); theLooker = nil; lookerMu.Unlock(); setSilentClock(nil) })
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
go l.run(ctx)
|
||||
if err := startRolloutSilent(ctx, js); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c := silentClockOf()
|
||||
w := silentWait{Plan: "plan-5", Tier: 0, Node: "ace"}
|
||||
if err := c.Arm(ctx, w, time.Second, time.Now()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := c.Reported(ctx, w); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(1800 * time.Millisecond)
|
||||
if n := looks.Load(); n != 0 {
|
||||
t.Fatalf("a key a report deleted asked for %d looks", n)
|
||||
}
|
||||
if err := c.Arm(ctx, w, time.Second, time.Now()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eventually(t, "the expiry asks for a look", func() bool { return looks.Load() == 1 })
|
||||
}
|
||||
|
||||
// A provisioner client's word on a module's files asks for a look, as a send that handed it modules is applied only
|
||||
// once it has said so (issue 974, review 1): no walk waits for its silent key for what the client already said.
|
||||
func TestAProvisionerClientsWordAsksForALook(t *testing.T) {
|
||||
js, err := broker.Dial(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(js.Close)
|
||||
if err := broker.AssertMeshStreams(js); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var failures, looks atomic.Int32
|
||||
l := lookerCounting(&failures, &looks, nil)
|
||||
lookerMu.Lock()
|
||||
theLooker = l
|
||||
lookerMu.Unlock()
|
||||
t.Cleanup(func() { lookerMu.Lock(); theLooker = nil; lookerMu.Unlock() })
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
go l.run(ctx)
|
||||
if err := startProvAppliedLooks(ctx, js); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
legacy, err := js.Conn().JetStream()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := legacy.Publish(broker.ProvisionerAppliedSubject("ace", "plex"), []byte(`{"status":"applied"}`)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eventually(t, "a look on the client's word", func() bool { return looks.Load() >= 1 })
|
||||
}
|
||||
|
||||
type brokenClock struct{ silentClock }
|
||||
|
||||
@@ -319,33 +179,3 @@ func TestKeysThatCannotBeReadAreWatchedByTimersAndTheLookFails(t *testing.T) {
|
||||
|
||||
// A plan moved by a process the serving controller did not spawn — a verb run at the control node's terminal — is
|
||||
// looked at: the controller follows its own plan-moved (issue 974, round 1).
|
||||
func TestAPlanMovedByAnotherProcessAsksForALook(t *testing.T) {
|
||||
js, err := broker.Dial(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(js.Close)
|
||||
if err := broker.AssertMeshStreams(js); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var failures, looks atomic.Int32
|
||||
l := lookerCounting(&failures, &looks, nil)
|
||||
lookerMu.Lock()
|
||||
theLooker = l
|
||||
lookerMu.Unlock()
|
||||
t.Cleanup(func() { lookerMu.Lock(); theLooker = nil; lookerMu.Unlock() })
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
go l.run(ctx)
|
||||
if err := startPlanMovedLooks(ctx, js); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
legacy, err := js.Conn().JetStream()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := legacy.Publish(link.SeatEventSubject(link.MeshControllerSeat, link.KeyPlanMoved), []byte(`{}`)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eventually(t, "a look on the plan moved", func() bool { return looks.Load() >= 1 })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user