| 1 | package registry |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func claimFingerprints(t *testing.T, r *Registry) (string, string) { |
| 6 | t.Helper() |
| 7 | f, err := r.Fingerprint() |
| 8 | if err != nil { |
| 9 | t.Fatal(err) |
| 10 | } |
| 11 | s, err := r.SnapshotFingerprint() |
| 12 | if err != nil { |
| 13 | t.Fatal(err) |
| 14 | } |
| 15 | return f, s |
| 16 | } |
| 17 | |
| 18 | // Each ownership column moves both fingerprints on its own, partial |
| 19 | // ownership included, and clearing it returns the exact default digests. |
| 20 | func TestFingerprintsSeeEachClaimOwnershipField(t *testing.T) { |
| 21 | reg := openTestRegistry(t) |
| 22 | if err := reg.Upsert(Route{Host: "c", Kind: "instance", Socket: "/s", App: "a", CreatedAt: 1700000000}); err != nil { |
| 23 | t.Fatal(err) |
| 24 | } |
| 25 | defF, defS := claimFingerprints(t, reg) |
| 26 | |
| 27 | set := func(assign string, args ...any) { |
| 28 | t.Helper() |
| 29 | if _, err := reg.sql.Exec(`UPDATE routes SET `+assign+` WHERE host = 'c'`, args...); err != nil { |
| 30 | t.Fatal(err) |
| 31 | } |
| 32 | } |
| 33 | const clear = `claim_incarnation = '', claim_stamp = 0, claim_owner = '', claim_row_id = ''` |
| 34 | |
| 35 | cases := []struct { |
| 36 | name string |
| 37 | assign string |
| 38 | args []any |
| 39 | }{ |
| 40 | {"incarnation", `claim_incarnation = ?`, []any{"i1"}}, |
| 41 | {"zero-incarnation", `claim_incarnation = ?`, []any{"0"}}, |
| 42 | {"zero-owner", `claim_owner = ?`, []any{"0"}}, |
| 43 | {"zero-row_id", `claim_row_id = ?`, []any{"0"}}, |
| 44 | {"stamp", `claim_stamp = ?`, []any{7}}, |
| 45 | {"owner", `claim_owner = ?`, []any{"o1"}}, |
| 46 | {"row_id", `claim_row_id = ?`, []any{"r1"}}, |
| 47 | {"incarnation+owner", `claim_incarnation = ?, claim_owner = ?`, []any{"i1", "o1"}}, |
| 48 | {"stamp+row_id", `claim_stamp = ?, claim_row_id = ?`, []any{7, "r1"}}, |
| 49 | {"full", `claim_incarnation = ?, claim_stamp = ?, claim_owner = ?, claim_row_id = ?`, []any{"i1", 7, "o1", "r1"}}, |
| 50 | } |
| 51 | seenF := map[string]string{} |
| 52 | seenS := map[string]string{} |
| 53 | for _, c := range cases { |
| 54 | set(c.assign, c.args...) |
| 55 | f, s := claimFingerprints(t, reg) |
| 56 | if f == defF { |
| 57 | t.Errorf("%s: Fingerprint did not change", c.name) |
| 58 | } |
| 59 | if s == defS { |
| 60 | t.Errorf("%s: SnapshotFingerprint did not change", c.name) |
| 61 | } |
| 62 | if prev, dup := seenF[f]; dup { |
| 63 | t.Errorf("%s: Fingerprint collides with %s", c.name, prev) |
| 64 | } |
| 65 | if prev, dup := seenS[s]; dup { |
| 66 | t.Errorf("%s: SnapshotFingerprint collides with %s", c.name, prev) |
| 67 | } |
| 68 | seenF[f], seenS[s] = c.name, c.name |
| 69 | |
| 70 | set(clear) |
| 71 | if f, s := claimFingerprints(t, reg); f != defF || s != defS { |
| 72 | t.Errorf("%s: cleared ownership did not restore default digests", c.name) |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | // The same value in different ownership fields must not hash alike. |
| 78 | func TestFingerprintsDistinguishClaimFieldsHoldingSameValue(t *testing.T) { |
| 79 | reg := openTestRegistry(t) |
| 80 | if err := reg.Upsert(Route{Host: "c", Kind: "instance", Socket: "/s", CreatedAt: 1700000000}); err != nil { |
| 81 | t.Fatal(err) |
| 82 | } |
| 83 | digests := func(assign string) (string, string) { |
| 84 | t.Helper() |
| 85 | if _, err := reg.sql.Exec(`UPDATE routes SET claim_incarnation = '', claim_owner = '', claim_row_id = '', ` + assign + ` WHERE host = 'c'`); err != nil { |
| 86 | t.Fatal(err) |
| 87 | } |
| 88 | return claimFingerprints(t, reg) |
| 89 | } |
| 90 | fa, sa := digests(`claim_owner = 'x'`) |
| 91 | fb, sb := digests(`claim_row_id = 'x'`) |
| 92 | fc, sc := digests(`claim_incarnation = 'x'`) |
| 93 | if fa == fb || fa == fc || fb == fc { |
| 94 | t.Error("Fingerprint aliases ownership fields") |
| 95 | } |
| 96 | if sa == sb || sa == sc || sb == sc { |
| 97 | t.Error("SnapshotFingerprint aliases ownership fields") |
| 98 | } |
| 99 | } |
| 100 | |