Nothing the control queue carries is lost while the store restarts: an enrolment claims its token and spends it last, and is asked to try again; build results, upgrades and catch-ups are handed back, bounded (novox/hq issue 083)

This commit is contained in:
2026-09-22 14:06:28 +02:00
parent b9cdb96a90
commit 1a41b88ed3
8 changed files with 433 additions and 59 deletions
+64
View File
@@ -383,3 +383,67 @@ func TestBeingHeardFromDoesNotChangeWhatANodeOwns(t *testing.T) {
t.Errorf("after a bare word that the node is here, the mesh believes it owns %v", owned)
}
}
// **A token is claimed, then spent** (novox/hq issue 083). A presenter may claim it again — an
// enrolment interrupted by a restarting store asks again with the same key — while another is held
// off until the lease lapses; and it is spent only by the one holding the claim.
func TestATokenIsClaimedByOnePresenterAndSpentOnlyByIt(t *testing.T) {
inv := fresh(t)
ctx := t.Context()
if _, err := inv.AddNode(ctx, "laptop"); err != nil {
t.Fatal(err)
}
issued, err := inv.IssueToken(ctx, "laptop", time.Hour)
if err != nil {
t.Fatal(err)
}
node, err := inv.Claim(ctx, issued.Secret, "key-a")
if err != nil || node.Name != "laptop" {
t.Fatalf("a fresh token was not claimed for its node: %v %q", err, node.Name)
}
if _, err := inv.Claim(ctx, issued.Secret, "key-a"); err != nil {
t.Fatalf("the presenter holding the claim could not claim again after an interruption: %v", err)
}
if _, err := inv.Claim(ctx, issued.Secret, "key-b"); !errors.Is(err, ErrTokenInUse) {
t.Fatalf("a second presenter was not held off while the claim is live: %v", err)
}
if err := inv.Spend(ctx, issued.Secret, "key-b"); !errors.Is(err, ErrTokenRefused) {
t.Fatalf("a presenter not holding the claim spent the token: %v", err)
}
if err := inv.Spend(ctx, issued.Secret, "key-a"); err != nil {
t.Fatalf("the presenter holding the claim could not spend it: %v", err)
}
for _, by := range []string{"key-a", "key-b"} {
if _, err := inv.Claim(ctx, issued.Secret, by); !errors.Is(err, ErrTokenRefused) {
t.Fatalf("a spent token was claimed again by %s: %v", by, err)
}
}
}
// A claim lapses: a host that gave up and was started over, with keys of its own, is not held off
// for longer than the lease.
func TestAClaimThatLapsedCanBeTakenByAnotherPresenter(t *testing.T) {
inv := fresh(t)
ctx := t.Context()
if _, err := inv.AddNode(ctx, "laptop"); err != nil {
t.Fatal(err)
}
issued, err := inv.IssueToken(ctx, "laptop", time.Hour)
if err != nil {
t.Fatal(err)
}
if _, err := inv.Claim(ctx, issued.Secret, "key-a"); err != nil {
t.Fatal(err)
}
if _, err := inv.store.Pool().Exec(ctx,
`update enrolment_token set claimed_until = now() - interval '1 second' where secret = $1`,
hashSecret(issued.Secret)); err != nil {
t.Fatal(err)
}
if _, err := inv.Claim(ctx, issued.Secret, "key-b"); err != nil {
t.Fatalf("a lapsed claim held off a new presenter: %v", err)
}
if err := inv.Spend(ctx, issued.Secret, "key-a"); !errors.Is(err, ErrTokenRefused) {
t.Fatalf("the presenter whose claim lapsed could still spend the token: %v", err)
}
}