Monorepo for Tangled
tangled.org
1package db
2
3import (
4 "testing"
5
6 "github.com/bluesky-social/indigo/atproto/syntax"
7 "tangled.org/core/api/tangled"
8)
9
10const (
11 didBoltless = "did:plc:boltless"
12 didAkshay = "did:plc:akshay"
13 keyShared = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAISharedSharedSharedSharedSharedSharedShar01"
14 keyRotated = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIRotatedRotatedRotatedRotatedRotatedRot02"
15 keyOther = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtherOtherOtherOtherOtherOtherOtherOth03"
16)
17
18func TestUpsertPublicKey_GlobalUniqueness(t *testing.T) {
19 d := newTestDB(t)
20 addDid(t, d, didBoltless)
21 addDid(t, d, didAkshay)
22
23 if err := d.UpsertPublicKey(pubKey(didBoltless, "r-first", keyShared)); err != nil {
24 t.Fatalf("first upsert: %v", err)
25 }
26 if err := d.UpsertPublicKey(pubKey(didAkshay, "r-second", keyShared)); err != nil {
27 t.Fatalf("second upsert: %v", err)
28 }
29
30 owners := ownersByKey(t, d)
31 if got := len(owners); got != 1 {
32 t.Fatalf("stored %d copies of the key, want 1, owners=%v", got, owners)
33 }
34 if owners[keyShared] != didBoltless {
35 t.Errorf("key registered to %q, want %q (first writer keeps it)", owners[keyShared], didBoltless)
36 }
37}
38
39func TestUpsertPublicKey_RotatesAtSameRkey(t *testing.T) {
40 d := newTestDB(t)
41 addDid(t, d, didBoltless)
42
43 if err := d.UpsertPublicKey(pubKey(didBoltless, "rotate", keyShared)); err != nil {
44 t.Fatalf("upsert old: %v", err)
45 }
46 if err := d.UpsertPublicKey(pubKey(didBoltless, "rotate", keyRotated)); err != nil {
47 t.Fatalf("upsert new: %v", err)
48 }
49
50 owners := ownersByKey(t, d)
51 if _, ok := owners[keyShared]; ok {
52 t.Errorf("old key %q survived rotation at the same rkey", keyShared)
53 }
54 if _, ok := owners[keyRotated]; !ok {
55 t.Errorf("rotated key %q not stored", keyRotated)
56 }
57 if got := len(owners); got != 1 {
58 t.Errorf("stored %d keys, want 1", got)
59 }
60}
61
62func TestDeletePublicKeyByRkey(t *testing.T) {
63 d := newTestDB(t)
64 addDid(t, d, didBoltless)
65
66 if err := d.UpsertPublicKey(pubKey(didBoltless, "keep", keyShared)); err != nil {
67 t.Fatalf("upsert keep: %v", err)
68 }
69 if err := d.UpsertPublicKey(pubKey(didBoltless, "drop", keyOther)); err != nil {
70 t.Fatalf("upsert drop: %v", err)
71 }
72
73 if err := d.DeletePublicKeyByRkey(didBoltless, ""); err != nil {
74 t.Fatalf("delete with empty rkey: %v", err)
75 }
76 if got := len(ownersByKey(t, d)); got != 2 {
77 t.Fatalf("empty rkey deleted %d rows, want a no-op leaving 2", 2-got)
78 }
79
80 if err := d.DeletePublicKeyByRkey(didBoltless, "drop"); err != nil {
81 t.Fatalf("delete drop: %v", err)
82 }
83
84 owners := ownersByKey(t, d)
85 if _, ok := owners[keyOther]; ok {
86 t.Errorf("key at rkey %q was not deleted", "drop")
87 }
88 if _, ok := owners[keyShared]; !ok {
89 t.Errorf("delete removed the wrong key, %q is gone", keyShared)
90 }
91}
92
93func TestInsertPublicKey_SkipsEmptyKey(t *testing.T) {
94 d := newTestDB(t)
95 addDid(t, d, didBoltless)
96
97 if err := d.UpsertPublicKey(pubKey(didBoltless, "empty", "")); err != nil {
98 t.Fatalf("upsert empty key: %v", err)
99 }
100
101 if got := len(ownersByKey(t, d)); got != 0 {
102 t.Errorf("stored %d rows for an empty key, want 0", got)
103 }
104}
105
106func addDid(t *testing.T, d *DB, did string) {
107 t.Helper()
108 if err := AddDid(d, did); err != nil {
109 t.Fatalf("AddDid(%q): %v", did, err)
110 }
111}
112
113func pubKey(did syntax.DID, rkey syntax.RecordKey, key string) PublicKey {
114 return PublicKey{
115 Did: did,
116 Rkey: rkey,
117 PublicKey: tangled.PublicKey{Key: key, CreatedAt: "2026-06-20T00:00:00Z"},
118 }
119}
120
121func ownersByKey(t *testing.T, d *DB) map[string]string {
122 t.Helper()
123 rows, err := d.GetAllPublicKeys()
124 if err != nil {
125 t.Fatalf("GetAllPublicKeys: %v", err)
126 }
127 return foldOwners(rows, map[string]string{})
128}
129
130func foldOwners(rows []PublicKey, acc map[string]string) map[string]string {
131 if len(rows) == 0 {
132 return acc
133 }
134 acc[rows[0].Key] = rows[0].Did.String()
135 return foldOwners(rows[1:], acc)
136}