package main import ( "encoding/json" "strings" "testing" "github.com/novox/mesh-host/internal/link" "github.com/novox/mesh-host/internal/store" ) // A declaration carries no order, so a host cannot tell an older one from a newer (novox/hq // 04-ISSUES/107). Its only identity was the digest of its bytes: "not the last" could be said, // "older" could not. func keptWith(t *testing.T, sequence int64) store.Declared { t.Helper() return keptAt(t, link.Order{Sequence: sequence}) } func keptAt(t *testing.T, order link.Order) store.Declared { t.Helper() body, err := json.Marshal(map[string]any{ "declaration": 1, "resources": []any{}, "owns_nothing": true, "sequence": order.Sequence, "epoch": order.Epoch, }) if err != nil { t.Fatal(err) } return store.Declared{Declaration: body, Signature: []byte("x")} } func TestAnOlderDeclarationFromTheMeshIsRefused(t *testing.T) { err := refuseOlder(keptWith(t, 7), nil, link.Order{Sequence: 5}) if err == nil { t.Fatal("sequence 5 was accepted over a kept 7") } if !strings.Contains(err.Error(), "older") || !strings.Contains(err.Error(), "nothing was applied") { t.Fatalf("the refusal does not say what it is: %v", err) } } func TestANewerOrEqualDeclarationIsNot(t *testing.T) { if err := refuseOlder(keptWith(t, 7), nil, link.Order{Sequence: 8}); err != nil { t.Fatalf("sequence 8 was refused over a kept 7: %v", err) } // Equal is the same declaration again, which reconciling is for. if err := refuseOlder(keptWith(t, 7), nil, link.Order{Sequence: 7}); err != nil { t.Fatalf("the same sequence was refused: %v", err) } } func TestNoOrderClaimedMeansNoOrderCompared(t *testing.T) { // An older controller sends none; a host that received before it understood them kept none. // Refusing on a guess would strand a node the moment the controller is older than the host. if err := refuseOlder(keptWith(t, 7), nil, link.Order{Sequence: 0}); err != nil { t.Fatalf("a declaration claiming no order was refused: %v", err) } if err := refuseOlder(keptWith(t, 0), nil, link.Order{Sequence: 3}); err != nil { t.Fatalf("a declaration was refused against a kept one that claimed no order: %v", err) } if err := refuseOlder(store.Declared{}, store.ErrNothingDeclared, link.Order{Sequence: 3}); err != nil { t.Fatalf("a first declaration was refused: %v", err) } } // **The lease epoch decides first** (novox/hq to-be 45 §6): a declaration from a controller that lost // its lease is refused whatever its sequence, and a new lease holder's is taken whatever its sequence. func TestAnOlderEpochIsRefusedAndANewerTaken(t *testing.T) { kept := keptAt(t, link.Order{Epoch: 57, Sequence: 3}) err := refuseOlder(kept, nil, link.Order{Epoch: 41, Sequence: 12}) if err == nil || !strings.Contains(err.Error(), "epoch 41") || !strings.Contains(err.Error(), "epoch 57") { t.Fatalf("an older epoch was not refused by name: %v", err) } if err := refuseOlder(kept, nil, link.Order{Epoch: 58, Sequence: 1}); err != nil { t.Fatalf("a new lease holder's first declaration was refused for its sequence: %v", err) } if err := refuseOlder(kept, nil, link.Order{Epoch: 57, Sequence: 2}); err == nil { t.Fatal("a lower sequence under the same epoch was accepted") } // A controller with no lease — older, or rolled back to a build from before it — is today's // behaviour: no epoch compared. if err := refuseOlder(kept, nil, link.Order{Sequence: 4}); err != nil { t.Fatalf("a declaration with no epoch was refused for one: %v", err) } if held, older := olderThanKept(kept, nil, link.Order{Epoch: 41, Sequence: 12}); !older || held != (link.Order{Epoch: 57, Sequence: 3}) { t.Fatalf("olderThanKept said %v, holding %+v", older, held) } // What is kept cannot be read: no order to compare, so nothing is refused on a guess. if _, older := olderThanKept(store.Declared{Declaration: []byte("garbled")}, nil, link.Order{Epoch: 41, Sequence: 1}); older { t.Fatal("refused against a kept declaration nobody can read") } }