Monorepo for Tangled
tangled.org
1package db
2
3import (
4 "database/sql"
5 "errors"
6 "testing"
7)
8
9func seedProfile(t *testing.T, d *DB, did string) {
10 t.Helper()
11 if _, err := d.Exec(
12 `insert into profile (did, description, include_bluesky, location, preferred_handle)
13 values (?, ?, ?, ?, ?)`,
14 did, "hi", 0, "", "boltless.bsky.social",
15 ); err != nil {
16 t.Fatalf("seed profile: %v", err)
17 }
18 if _, err := d.Exec(
19 `insert into profile_links (did, link) values (?, ?)`,
20 did, "https://boltless.example/blog",
21 ); err != nil {
22 t.Fatalf("seed profile_links: %v", err)
23 }
24 if _, err := d.Exec(
25 `insert into profile_stats (did, kind) values (?, ?)`,
26 did, "open-pull-request-count",
27 ); err != nil {
28 t.Fatalf("seed profile_stats: %v", err)
29 }
30 if _, err := d.Exec(
31 `insert into profile_pinned_repositories (did, pin) values (?, ?)`,
32 did, "did:plc:limpet",
33 ); err != nil {
34 t.Fatalf("seed profile_pinned_repositories: %v", err)
35 }
36}
37
38func countRows(t *testing.T, d *DB, query string, args ...any) int {
39 t.Helper()
40 var n int
41 if err := d.QueryRow(query, args...).Scan(&n); err != nil {
42 t.Fatalf("count: %v", err)
43 }
44 return n
45}
46
47func TestDeleteProfile_CascadesAllChildTables(t *testing.T) {
48 d := newTestDB(t)
49 const did = "did:plc:boltless"
50 seedProfile(t, d, did)
51
52 if got := countRows(t, d, `select count(*) from profile where did = ?`, did); got != 1 {
53 t.Fatalf("pre: profile rows = %d, want 1", got)
54 }
55 if got := countRows(t, d, `select count(*) from profile_links where did = ?`, did); got != 1 {
56 t.Fatalf("pre: profile_links rows = %d, want 1", got)
57 }
58 if got := countRows(t, d, `select count(*) from profile_stats where did = ?`, did); got != 1 {
59 t.Fatalf("pre: profile_stats rows = %d, want 1", got)
60 }
61 if got := countRows(t, d, `select count(*) from profile_pinned_repositories where did = ?`, did); got != 1 {
62 t.Fatalf("pre: profile_pinned_repositories rows = %d, want 1", got)
63 }
64
65 tx, err := d.Begin()
66 if err != nil {
67 t.Fatalf("Begin: %v", err)
68 }
69 if err := DeleteProfile(tx, did); err != nil {
70 t.Fatalf("DeleteProfile: %v", err)
71 }
72
73 if got := countRows(t, d, `select count(*) from profile where did = ?`, did); got != 0 {
74 t.Errorf("post: profile rows = %d, want 0", got)
75 }
76 if got := countRows(t, d, `select count(*) from profile_links where did = ?`, did); got != 0 {
77 t.Errorf("post: profile_links rows = %d, want 0 (cascade)", got)
78 }
79 if got := countRows(t, d, `select count(*) from profile_stats where did = ?`, did); got != 0 {
80 t.Errorf("post: profile_stats rows = %d, want 0 (cascade)", got)
81 }
82 if got := countRows(t, d, `select count(*) from profile_pinned_repositories where did = ?`, did); got != 0 {
83 t.Errorf("post: profile_pinned_repositories rows = %d, want 0 (cascade)", got)
84 }
85}
86
87func TestDeleteProfile_NoRowsIsNoop(t *testing.T) {
88 d := newTestDB(t)
89
90 tx, err := d.Begin()
91 if err != nil {
92 t.Fatalf("Begin: %v", err)
93 }
94 if err := DeleteProfile(tx, "did:plc:akshay"); err != nil {
95 t.Errorf("DeleteProfile on missing did: %v, want nil", err)
96 }
97}
98
99func TestDeleteProfile_LeavesOtherDidsAlone(t *testing.T) {
100 d := newTestDB(t)
101 seedProfile(t, d, "did:plc:boltless")
102 seedProfile(t, d, "did:plc:akshay")
103
104 tx, err := d.Begin()
105 if err != nil {
106 t.Fatalf("Begin: %v", err)
107 }
108 if err := DeleteProfile(tx, "did:plc:boltless"); err != nil {
109 t.Fatalf("DeleteProfile: %v", err)
110 }
111
112 if got := countRows(t, d, `select count(*) from profile where did = ?`, "did:plc:akshay"); got != 1 {
113 t.Errorf("other profile should survive: rows = %d, want 1", got)
114 }
115 if got := countRows(t, d, `select count(*) from profile_links where did = ?`, "did:plc:akshay"); got != 1 {
116 t.Errorf("other profile_links should survive: rows = %d, want 1", got)
117 }
118 if got := countRows(t, d, `select count(*) from profile_stats where did = ?`, "did:plc:akshay"); got != 1 {
119 t.Errorf("other profile_stats should survive: rows = %d, want 1", got)
120 }
121 if got := countRows(t, d, `select count(*) from profile_pinned_repositories where did = ?`, "did:plc:akshay"); got != 1 {
122 t.Errorf("other profile_pinned_repositories should survive: rows = %d, want 1", got)
123 }
124}
125
126func TestGetPreferredHandle_AfterDeleteReturnsNoRows(t *testing.T) {
127 d := newTestDB(t)
128 const did = "did:plc:boltless"
129 seedProfile(t, d, did)
130
131 h, err := GetPreferredHandle(d, did)
132 if err != nil {
133 t.Fatalf("GetPreferredHandle pre-delete: %v", err)
134 }
135 if string(h) != "boltless.bsky.social" {
136 t.Fatalf("handle = %q, want %q", h, "boltless.bsky.social")
137 }
138
139 tx, err := d.Begin()
140 if err != nil {
141 t.Fatalf("Begin: %v", err)
142 }
143 if err := DeleteProfile(tx, did); err != nil {
144 t.Fatalf("DeleteProfile: %v", err)
145 }
146
147 _, err = GetPreferredHandle(d, did)
148 if !errors.Is(err, sql.ErrNoRows) {
149 t.Errorf("GetPreferredHandle post-delete: err = %v, want sql.ErrNoRows", err)
150 }
151}