oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package home
4
5import (
6 "amadan.net/oficina/home/internal/operator"
7 "amadan.net/rastrillo/rastrillo/migrate"
8 "testing"
9)
10
11func TestOperatorSchemaMatchesSelectedRoleAndRejectsDrift(t *testing.T) {
12 for _, role := range []string{"credential", "directory"} {
13 for _, mode := range []string{"valid", "other-role", "missing-migration", "changed-checksum", "extra-trigger", "extra-table"} {
14 t.Run(role+"/"+mode, func(t *testing.T) {
15 check, err := operatorSchemaCheck(role)
16 if err != nil {
17 t.Fatal(err)
18 }
19 databaseRole := role
20 if mode == "other-role" {
21 if role == "credential" {
22 databaseRole = "directory"
23 } else {
24 databaseRole = "credential"
25 }
26 }
27 db := compatibilityFixture(t, databaseRole)
28 defer db.Close()
29 var sql string
30 switch mode {
31 case "missing-migration":
32 sql = "DELETE FROM rastrillo_migrations WHERE id=(SELECT id FROM rastrillo_migrations LIMIT 1)"
33 case "changed-checksum":
34 sql = "UPDATE rastrillo_migrations SET checksum='wrong' WHERE id=(SELECT id FROM rastrillo_migrations LIMIT 1)"
35 case "extra-trigger":
36 sql = "CREATE TRIGGER unexpected AFTER DELETE ON rastrillo_migrations BEGIN SELECT 1; END"
37 case "extra-table":
38 sql = "CREATE TABLE unexpected(value TEXT)"
39 }
40 if sql != "" {
41 if _, err := db.Exec(sql); err != nil {
42 t.Fatal(err)
43 }
44 }
45 conn, err := db.Conn(t.Context())
46 if err != nil {
47 t.Fatal(err)
48 }
49 defer conn.Close()
50 if _, err := conn.ExecContext(t.Context(), "BEGIN IMMEDIATE"); err != nil {
51 t.Fatal(err)
52 }
53 err = check(t.Context(), conn)
54 if mode == "valid" && err != nil {
55 t.Fatal(err)
56 }
57 if mode != "valid" && err != operator.ErrRefused {
58 t.Fatal("schema drift accepted", err)
59 }
60 if _, err := conn.ExecContext(t.Context(), "ROLLBACK"); err != nil {
61 t.Fatal(err)
62 }
63 })
64 }
65 }
66 if _, err := operatorSchemaCheck(""); err != operator.ErrRefused {
67 t.Fatal("missing role accepted", err)
68 }
69}
70
71func TestRuntimeOperatorSchemasAreAdditiveAndRoleSpecific(t *testing.T) {
72 for _, role := range []string{"", "credential", "directory"} {
73 t.Run(role, func(t *testing.T) {
74 cfg := Runtime{role: role}
75 db, err := migrate.Replay(t.Context(), cfg.Schema().All())
76 if err != nil {
77 t.Fatal(err)
78 }
79 defer db.Close()
80 tables := map[string]bool{}
81 rows, err := db.Query("SELECT name FROM sqlite_schema WHERE type='table'")
82 if err != nil {
83 t.Fatal(err)
84 }
85 for rows.Next() {
86 var name string
87 if rows.Scan(&name) != nil {
88 t.Fatal("schema read")
89 }
90 tables[name] = true
91 }
92 if rows.Err() != nil || rows.Close() != nil {
93 t.Fatal("schema scan")
94 }
95 if tables["credential_operator_results"] != (role == "credential") || tables["directory_operator_results"] != (role == "directory") || tables["people"] == (role == "directory") || tables["teams"] != (role == "directory") {
96 t.Fatal("role schema crossed boundary")
97 }
98 })
99 }
100}
101
102func TestServingRetainsAdditiveLegacyLedgerButOperatorRefusesIt(t *testing.T) {
103 pair := newExchangePair(t)
104 db := pair.directory.db
105 if _, err := db.Writer().Exec("CREATE TABLE legacy_additive_state(value INTEGER); INSERT INTO rastrillo_migrations(id,applied_at,checksum) VALUES('legacy_additive/0001','2026-01-01T00:00:00Z','historical-checksum')"); err != nil {
106 t.Fatal(err)
107 }
108 if _, err := NewRuntime(*pair.directory.runtime, db, quietLogger()); err != nil {
109 t.Fatal("ordinary serving rejected historical additive migration", err)
110 }
111 conn, err := db.Writer().Conn(t.Context())
112 if err != nil {
113 t.Fatal(err)
114 }
115 defer conn.Close()
116 if err := operator.VerifyLedger(t.Context(), conn, pair.directory.runtime.Schema()); err != operator.ErrRefused {
117 t.Fatal("operator accepted foreign ledger", err)
118 }
119}
120