krz/gitbay
A CLI-first git forge.
clone: git clone https://gitbay.org/krz/gitbay.git
main: internal/store/store_test.go · raw
1package store
2
3import (
4 "path/filepath"
5 "strings"
6 "testing"
7)
8
9func open(t *testing.T) *Store {
10 t.Helper()
11 s, err := Open(filepath.Join(t.TempDir(), "gitbay.db"))
12 if err != nil {
13 t.Fatal(err)
14 }
15 t.Cleanup(func() { s.Close() })
16 return s
17}
18
19func TestMigrateUpDown(t *testing.T) {
20 s := open(t)
21
22 if err := s.MigrateUp(); err != nil {
23 t.Fatal(err)
24 }
25 v, err := s.Version()
26 if err != nil {
27 t.Fatal(err)
28 }
29 if v < 1 {
30 t.Fatalf("version %d after MigrateUp", v)
31 }
32
33 // Seeded settings row exists.
34 var epoch string
35 if err := s.DB.QueryRow("SELECT value FROM settings WHERE key = 'key_epoch'").Scan(&epoch); err != nil {
36 t.Fatal(err)
37 }
38 if epoch != "1" {
39 t.Fatalf("key_epoch = %q, want 1", epoch)
40 }
41
42 // Down to empty, then back up.
43 if err := s.MigrateTo(0); err != nil {
44 t.Fatal(err)
45 }
46 var n int
47 if err := s.DB.QueryRow("SELECT count(*) FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").Scan(&n); err != nil {
48 t.Fatal(err)
49 }
50 if n != 0 {
51 t.Fatalf("%d tables remain after down-migration to 0", n)
52 }
53 if err := s.MigrateUp(); err != nil {
54 t.Fatal(err)
55 }
56 // Idempotent at latest.
57 if err := s.MigrateUp(); err != nil {
58 t.Fatal(err)
59 }
60}
61
62func TestKeyFingerprintGloballyUnique(t *testing.T) {
63 s := open(t)
64 if err := s.MigrateUp(); err != nil {
65 t.Fatal(err)
66 }
67 mustExec := func(q string, args ...any) {
68 t.Helper()
69 if _, err := s.DB.Exec(q, args...); err != nil {
70 t.Fatal(err)
71 }
72 }
73 mustExec("INSERT INTO users (username) VALUES ('alice'), ('bob')")
74 mustExec("INSERT INTO ssh_keys (user_id, fingerprint, algo, blob) VALUES (1, 'SHA256:aaa', 'ed25519', x'00')")
75
76 // Same fingerprint on a different account must be rejected.
77 _, err := s.DB.Exec("INSERT INTO ssh_keys (user_id, fingerprint, algo, blob) VALUES (2, 'SHA256:aaa', 'ed25519', x'00')")
78 if err == nil || !strings.Contains(err.Error(), "UNIQUE") {
79 t.Fatalf("duplicate ssh fingerprint across accounts: err = %v, want UNIQUE violation", err)
80 }
81
82 mustExec("INSERT INTO pgp_keys (user_id, fingerprint, armored) VALUES (1, 'FPR1', '-----')")
83 _, err = s.DB.Exec("INSERT INTO pgp_keys (user_id, fingerprint, armored) VALUES (2, 'FPR1', '-----')")
84 if err == nil || !strings.Contains(err.Error(), "UNIQUE") {
85 t.Fatalf("duplicate pgp fingerprint across accounts: err = %v, want UNIQUE violation", err)
86 }
87}
88
89func TestForeignKeysEnforced(t *testing.T) {
90 s := open(t)
91 if err := s.MigrateUp(); err != nil {
92 t.Fatal(err)
93 }
94 _, err := s.DB.Exec("INSERT INTO ssh_keys (user_id, fingerprint, algo, blob) VALUES (999, 'SHA256:zzz', 'ed25519', x'00')")
95 if err == nil {
96 t.Fatal("insert with dangling user_id succeeded; foreign keys are off")
97 }
98}