package link import ( "errors" "testing" "time" ) func window() StoreWindow { return StoreWindow{GiveUpAfter: 2 * time.Minute} } // The guarantee itself (novox/hq ADR 0083): a push the store cannot record is held, not dropped // and not falsely acknowledged. func TestAMessageTheStoreCannotTakeYetIsHeld(t *testing.T) { if got := window().Decide(ErrTryAgain, "", "", 0); got != Hold { t.Fatalf("got %v; a push the store could not record was not held", got) } } // A refusal is an answer. Holding it would turn a message the mesh understood into one it // retries forever. func TestARefusalIsNotHeld(t *testing.T) { if got := window().Decide(errors.New("that node does not exist"), "", "", 0); got != Take { t.Fatalf("got %v; a refusal was mistaken for the store being away", got) } } // Held has a limit, and past it the message is settled rather than held for ever. func TestAStoreThatNeverComesBackEndsTheHold(t *testing.T) { if got := window().Decide(ErrTryAgain, "", "", 3*time.Minute); got != GiveUp { t.Fatalf("got %v; the window has no end", got) } if got := window().Decide(ErrTryAgain, "", "", time.Minute); got != Hold { t.Fatalf("got %v; the window ended early", got) } } // **The problem that holding in the server introduces.** A naked message is redelivered whatever // else happened meanwhile, so a report about a superseded declaration comes back after the newer // one was applied — and applying it would undo the newer. func TestAReportAboutASupersededDeclarationIsNotApplied(t *testing.T) { got := window().Decide(nil, "digest-of-the-old-one", "digest-of-the-current-one", 0) if got != Stale { t.Fatalf("got %v; a redelivery that lost its race would have undone what came after", got) } } func TestAReportAboutTheOutstandingDeclarationIsApplied(t *testing.T) { if got := window().Decide(nil, "same", "same", 0); got != Take { t.Fatalf("got %v; a current report was discarded", got) } } // A message that is about no declaration — an enrolment, a build result — is never stale: there // is nothing for it to be out of date with. func TestAMessageAboutNoDeclarationIsNeverStale(t *testing.T) { if got := window().Decide(nil, "", "whatever-is-outstanding", 0); got != Take { t.Fatalf("got %v; an enrolment was treated as a stale report", got) } if got := window().Decide(nil, "a-digest", "", 0); got != Take { t.Fatalf("got %v; a report was called stale against a node that was sent nothing", got) } } // Staleness is decided before the store is waited on: a redelivery that lost its race must not // hold a slot in the window that a current message needs. func TestAStaleMessageIsNotHeldForTheStore(t *testing.T) { if got := window().Decide(ErrTryAgain, "old", "current", 0); got != Stale { t.Fatalf("got %v; a superseded message was held for a store it would never be applied to", got) } } // The delay backs off: a store that is gone is not helped by being asked every second, and the // delay is what keeps a window of held messages from becoming a spin. func TestRedeliveryBacksOff(t *testing.T) { first := RedeliverAfter(0) later := RedeliverAfter(10 * time.Second) last := RedeliverAfter(time.Minute) if !(first < later && later < last) { t.Fatalf("delays do not back off: %v %v %v", first, later, last) } if last > 30*time.Second { t.Fatalf("a held message waits %v between attempts, which is longer than a store restart", last) } }