tenseleyflow/shithub / 75713ce

Browse files

Add sqlc config + meta queries + generated metadb package + integration test

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
75713ced0f171f728179c225ef7e179810a03318
Parents
92b65dd
Tree
90282ef

8 changed files

StatusFile+-
A internal/meta/meta_test.go 85 0
A internal/meta/queries/queries.sql 20 0
A internal/meta/sqlc/db.go 25 0
A internal/meta/sqlc/models.go 15 0
A internal/meta/sqlc/querier.go 19 0
A internal/meta/sqlc/queries.sql.go 77 0
A seeds/dev/.gitkeep 3 0
A sqlc.yaml 19 0
internal/meta/meta_test.goadded
@@ -0,0 +1,85 @@
1
+// SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+package meta_test
4
+
5
+import (
6
+	"context"
7
+	"testing"
8
+
9
+	metadb "github.com/tenseleyFlow/shithub/internal/meta/sqlc"
10
+	"github.com/tenseleyFlow/shithub/internal/testing/dbtest"
11
+)
12
+
13
+// TestMetaRoundtrip exercises the dbtest harness end-to-end: clone-from-template,
14
+// migrations applied, sqlc-generated queries against the meta table.
15
+func TestMetaRoundtrip(t *testing.T) {
16
+	t.Parallel()
17
+	pool := dbtest.NewTestDB(t)
18
+	q := metadb.New()
19
+	ctx := context.Background()
20
+
21
+	// Migrations populate the schema_version + app rows.
22
+	got, err := q.GetMeta(ctx, pool, "schema_version")
23
+	if err != nil {
24
+		t.Fatalf("GetMeta(schema_version): %v", err)
25
+	}
26
+	if string(got.Value) != `"0001"` {
27
+		t.Errorf("schema_version: got %s, want %q", got.Value, `"0001"`)
28
+	}
29
+
30
+	// Set a new key and read it back.
31
+	if err := q.SetMeta(ctx, pool, metadb.SetMetaParams{Key: "test_key", Value: []byte(`"hello"`)}); err != nil {
32
+		t.Fatalf("SetMeta: %v", err)
33
+	}
34
+	got2, err := q.GetMeta(ctx, pool, "test_key")
35
+	if err != nil {
36
+		t.Fatalf("GetMeta(test_key): %v", err)
37
+	}
38
+	if string(got2.Value) != `"hello"` {
39
+		t.Errorf("test_key: got %s, want %q", got2.Value, `"hello"`)
40
+	}
41
+
42
+	// updated_at trigger: re-set with a new value, updated_at advances.
43
+	first := got2.UpdatedAt
44
+	if err := q.SetMeta(ctx, pool, metadb.SetMetaParams{Key: "test_key", Value: []byte(`"world"`)}); err != nil {
45
+		t.Fatalf("SetMeta upsert: %v", err)
46
+	}
47
+	got3, err := q.GetMeta(ctx, pool, "test_key")
48
+	if err != nil {
49
+		t.Fatalf("GetMeta upsert: %v", err)
50
+	}
51
+	if !got3.UpdatedAt.Time.After(first.Time) {
52
+		t.Errorf("updated_at trigger did not advance: first=%v, after=%v", first.Time, got3.UpdatedAt.Time)
53
+	}
54
+
55
+	// List + delete.
56
+	rows, err := q.ListMeta(ctx, pool)
57
+	if err != nil {
58
+		t.Fatalf("ListMeta: %v", err)
59
+	}
60
+	if len(rows) < 3 { // schema_version, app, test_key
61
+		t.Errorf("ListMeta: got %d rows, want >= 3", len(rows))
62
+	}
63
+	if err := q.DeleteMeta(ctx, pool, "test_key"); err != nil {
64
+		t.Fatalf("DeleteMeta: %v", err)
65
+	}
66
+}
67
+
68
+// TestMetaParallelism asserts dbtest gives each parallel test its own DB.
69
+// If two parallel tests shared a database, the writes here would race.
70
+func TestMetaParallelism(t *testing.T) {
71
+	t.Parallel()
72
+	for i := 0; i < 4; i++ {
73
+		i := i
74
+		t.Run("worker", func(t *testing.T) {
75
+			t.Parallel()
76
+			pool := dbtest.NewTestDB(t)
77
+			q := metadb.New()
78
+			key := "parallel_key"
79
+			val := []byte(`"worker"`)
80
+			if err := q.SetMeta(context.Background(), pool, metadb.SetMetaParams{Key: key, Value: val}); err != nil {
81
+				t.Fatalf("SetMeta worker %d: %v", i, err)
82
+			}
83
+		})
84
+	}
85
+}
internal/meta/queries/queries.sqladded
@@ -0,0 +1,20 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+-- name: GetMeta :one
4
+SELECT key, value, updated_at
5
+FROM meta
6
+WHERE key = $1;
7
+
8
+-- name: SetMeta :exec
9
+INSERT INTO meta (key, value)
10
+VALUES ($1, $2)
11
+ON CONFLICT (key) DO UPDATE
12
+    SET value = EXCLUDED.value;
13
+
14
+-- name: ListMeta :many
15
+SELECT key, value, updated_at
16
+FROM meta
17
+ORDER BY key;
18
+
19
+-- name: DeleteMeta :exec
20
+DELETE FROM meta WHERE key = $1;
internal/meta/sqlc/db.goadded
@@ -0,0 +1,25 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package metadb
6
+
7
+import (
8
+	"context"
9
+
10
+	"github.com/jackc/pgx/v5"
11
+	"github.com/jackc/pgx/v5/pgconn"
12
+)
13
+
14
+type DBTX interface {
15
+	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
16
+	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
17
+	QueryRow(context.Context, string, ...interface{}) pgx.Row
18
+}
19
+
20
+func New() *Queries {
21
+	return &Queries{}
22
+}
23
+
24
+type Queries struct {
25
+}
internal/meta/sqlc/models.goadded
@@ -0,0 +1,15 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package metadb
6
+
7
+import (
8
+	"github.com/jackc/pgx/v5/pgtype"
9
+)
10
+
11
+type Meta struct {
12
+	Key       string
13
+	Value     []byte
14
+	UpdatedAt pgtype.Timestamptz
15
+}
internal/meta/sqlc/querier.goadded
@@ -0,0 +1,19 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package metadb
6
+
7
+import (
8
+	"context"
9
+)
10
+
11
+type Querier interface {
12
+	DeleteMeta(ctx context.Context, db DBTX, key string) error
13
+	// SPDX-License-Identifier: AGPL-3.0-or-later
14
+	GetMeta(ctx context.Context, db DBTX, key string) (Meta, error)
15
+	ListMeta(ctx context.Context, db DBTX) ([]Meta, error)
16
+	SetMeta(ctx context.Context, db DBTX, arg SetMetaParams) error
17
+}
18
+
19
+var _ Querier = (*Queries)(nil)
internal/meta/sqlc/queries.sql.goadded
@@ -0,0 +1,77 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+// source: queries.sql
5
+
6
+package metadb
7
+
8
+import (
9
+	"context"
10
+)
11
+
12
+const deleteMeta = `-- name: DeleteMeta :exec
13
+DELETE FROM meta WHERE key = $1
14
+`
15
+
16
+func (q *Queries) DeleteMeta(ctx context.Context, db DBTX, key string) error {
17
+	_, err := db.Exec(ctx, deleteMeta, key)
18
+	return err
19
+}
20
+
21
+const getMeta = `-- name: GetMeta :one
22
+
23
+SELECT key, value, updated_at
24
+FROM meta
25
+WHERE key = $1
26
+`
27
+
28
+// SPDX-License-Identifier: AGPL-3.0-or-later
29
+func (q *Queries) GetMeta(ctx context.Context, db DBTX, key string) (Meta, error) {
30
+	row := db.QueryRow(ctx, getMeta, key)
31
+	var i Meta
32
+	err := row.Scan(&i.Key, &i.Value, &i.UpdatedAt)
33
+	return i, err
34
+}
35
+
36
+const listMeta = `-- name: ListMeta :many
37
+SELECT key, value, updated_at
38
+FROM meta
39
+ORDER BY key
40
+`
41
+
42
+func (q *Queries) ListMeta(ctx context.Context, db DBTX) ([]Meta, error) {
43
+	rows, err := db.Query(ctx, listMeta)
44
+	if err != nil {
45
+		return nil, err
46
+	}
47
+	defer rows.Close()
48
+	items := []Meta{}
49
+	for rows.Next() {
50
+		var i Meta
51
+		if err := rows.Scan(&i.Key, &i.Value, &i.UpdatedAt); err != nil {
52
+			return nil, err
53
+		}
54
+		items = append(items, i)
55
+	}
56
+	if err := rows.Err(); err != nil {
57
+		return nil, err
58
+	}
59
+	return items, nil
60
+}
61
+
62
+const setMeta = `-- name: SetMeta :exec
63
+INSERT INTO meta (key, value)
64
+VALUES ($1, $2)
65
+ON CONFLICT (key) DO UPDATE
66
+    SET value = EXCLUDED.value
67
+`
68
+
69
+type SetMetaParams struct {
70
+	Key   string
71
+	Value []byte
72
+}
73
+
74
+func (q *Queries) SetMeta(ctx context.Context, db DBTX, arg SetMetaParams) error {
75
+	_, err := db.Exec(ctx, setMeta, arg.Key, arg.Value)
76
+	return err
77
+}
seeds/dev/.gitkeepadded
@@ -0,0 +1,3 @@
1
+# Seed SQL files dropped here are loaded in lex order by `shithubd seed`.
2
+# Numbered prefixes recommended (e.g. 010_users.sql, 020_repos.sql).
3
+# Domain seeds will land in their owning sprints (S05+).
sqlc.yamladded
@@ -0,0 +1,19 @@
1
+version: "2"
2
+sql:
3
+  - engine: postgresql
4
+    schema: internal/migrationsfs/migrations
5
+    queries: internal/meta/queries
6
+    gen:
7
+      go:
8
+        package: metadb
9
+        out: internal/meta/sqlc
10
+        sql_package: pgx/v5
11
+        emit_json_tags: false
12
+        emit_pointers_for_null_types: false
13
+        emit_prepared_queries: false
14
+        emit_interface: true
15
+        emit_exact_table_names: false
16
+        emit_empty_slices: true
17
+        emit_methods_with_db_argument: true
18
+        rename:
19
+          tg_set_updated_at: TgSetUpdatedAt