Add production features: slog adapter, scan helpers, slow query logging, pool stats, tracer passthrough, test tx isolation
Some checks failed
CI / test (push) Failing after 13s
Some checks failed
CI / test (push) Failing after 13s
- slog.go: SlogLogger adapts *slog.Logger to dbx.Logger interface - scan.go: Collect[T] and CollectOne[T] generic helpers using pgx.RowToStructByName - cluster.go: slow query logging via Config.SlowQueryThreshold (Warn level in queryEnd) - stats.go: PoolStats with Cluster.Stats() aggregating pool stats across all nodes - config.go/node.go: NodeConfig.Tracer passthrough for pgx.QueryTracer (OpenTelemetry) - options.go: WithSlowQueryThreshold and WithTracer functional options - dbxtest/tx.go: RunInTx runs callback in always-rolled-back transaction for test isolation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
81
scan_test.go
Normal file
81
scan_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package dbx_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"git.codelab.vc/pkg/dbx"
|
||||
"git.codelab.vc/pkg/dbx/dbxtest"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type scanRow struct {
|
||||
ID int `db:"id"`
|
||||
Name string `db:"name"`
|
||||
}
|
||||
|
||||
func TestCollect(t *testing.T) {
|
||||
c := dbxtest.NewTestCluster(t)
|
||||
ctx := context.Background()
|
||||
|
||||
_, err := c.Exec(ctx, `CREATE TEMPORARY TABLE test_collect (id int, name text)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = c.Exec(ctx, `INSERT INTO test_collect (id, name) VALUES (1, 'alice'), (2, 'bob')`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rows, err := dbx.Collect[scanRow](ctx, c, `SELECT id, name FROM test_collect ORDER BY id`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rows) != 2 {
|
||||
t.Fatalf("expected 2 rows, got %d", len(rows))
|
||||
}
|
||||
if rows[0].ID != 1 || rows[0].Name != "alice" {
|
||||
t.Errorf("row 0: got %+v", rows[0])
|
||||
}
|
||||
if rows[1].ID != 2 || rows[1].Name != "bob" {
|
||||
t.Errorf("row 1: got %+v", rows[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectOne(t *testing.T) {
|
||||
c := dbxtest.NewTestCluster(t)
|
||||
ctx := context.Background()
|
||||
|
||||
_, err := c.Exec(ctx, `CREATE TEMPORARY TABLE test_collect_one (id int, name text)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = c.Exec(ctx, `INSERT INTO test_collect_one (id, name) VALUES (1, 'alice')`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
row, err := dbx.CollectOne[scanRow](ctx, c, `SELECT id, name FROM test_collect_one WHERE id = 1`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if row.ID != 1 || row.Name != "alice" {
|
||||
t.Errorf("got %+v", row)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectOneNoRows(t *testing.T) {
|
||||
c := dbxtest.NewTestCluster(t)
|
||||
ctx := context.Background()
|
||||
|
||||
_, err := c.Exec(ctx, `CREATE TEMPORARY TABLE test_collect_norows (id int, name text)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = dbx.CollectOne[scanRow](ctx, c, `SELECT id, name FROM test_collect_norows`)
|
||||
if !errors.Is(err, pgx.ErrNoRows) {
|
||||
t.Errorf("expected pgx.ErrNoRows, got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user