Files
cloud/sqlpool/sqlpool_test.go
hanzo-dev 6b5262349c cek opens the file; the driver sets the pragmas; cloud says neither twice
basedb existed for exactly one reason: cek.Open chose a path and did not create
its parent, and on the pure-Go codec that does not fail the open — the database
is written back at CLOSE, so a missing directory loses every write of the session
after the caller has been told it had a store. cek v0.2.1 does the MkdirAll
itself, pinned by a round-trip test, so the wrapper is now a synonym. It is
deleted and its 70 call sites name cek directly.

The larger duplication was underneath it. Sixty stores each set
SetMaxOpenConns(1) and re-applied busy_timeout, journal_mode=WAL and
foreign_keys=ON by hand. hanzoai/sqlite already applies those, on EVERY
connection — which the hand-rolled db.Exec did not: a one-shot Exec lands on
whichever connection happens to serve it and is gone the moment that connection
is recycled. So the fifty-two copies were not merely a fact restated fifty-two
times, they were the weaker of the two mechanisms shadowing the stronger one.
They say nothing now, and sqlpool_test.go asserts the driver still delivers each
default, so the deletion goes red in one place instead of rotting in fifty.

What is NOT already universal is the pool cap. sqlite sets it on the envelope
path and not on the live-libsqlcipher path, which is the one the shipped image
builds — so the cap is real, and it is stated once, in sqlpool.Single. The
package imports nothing but database/sql, so every store can reach it.

Two databases were outside all of this and are not any more. team keeps its
pragmas, because a dynamic journal_mode and foreign_keys=OFF are an override
rather than a restatement. git's ssh-key registry was a bare sql.Open on a
hand-joined path — no namespace, no key, and consequently the only store in the
binary written to disk in plaintext. It opens through cek like everything else.

hanzoai/sqlite stays at v0.4.0. v0.5.0 deletes the DEK/principal API
(PrincipalType, NewDEK, WrapDEK, UnwrapDEK, PrincipalAAD, DeriveKey) that
hanzoai/commerce and hanzoai/tasks still compile against, and no published
version of either has migrated, so taking it breaks the build. cek v0.2.1 does
not want it either — it requires v0.4.0.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-01 22:05:54 -07:00

75 lines
2.5 KiB
Go

package sqlpool
import (
"database/sql"
"testing"
"github.com/hanzoai/cek"
_ "github.com/hanzoai/cloud/internal/devmaster"
"github.com/hanzoai/namespace"
_ "github.com/hanzoai/sqlite"
)
func TestSinglePinsThePool(t *testing.T) {
db, err := sql.Open("sqlite", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
if got := db.Stats().MaxOpenConnections; got != 0 {
t.Fatalf("precondition: pool already capped at %d", got)
}
Single(db)
if got := db.Stats().MaxOpenConnections; got != 1 {
t.Fatalf("MaxOpenConnections = %d, want 1", got)
}
}
// This is the test that lets ~50 stores delete their PRAGMA blocks.
//
// Every one of them opened a database and then re-applied busy_timeout,
// journal_mode=WAL and foreign_keys=ON by hand. hanzoai/sqlite already applies
// those — per CONNECTION, which the hand-rolled db.Exec did not: a one-shot Exec
// lands on whichever connection serves it and is lost the moment that connection
// is recycled. So the blocks were not just duplicated, they were the weaker of
// the two mechanisms.
//
// Deleting them is only safe while this holds, so assert it here rather than
// trusting a changelog. If a future hanzoai/sqlite stops applying a default, this
// goes red in ONE place instead of corrupting fifty stores in silence.
func TestCekOpenAlreadyCarriesTheDefaultsStoresUsedToSetByHand(t *testing.T) {
db, err := cek.Open(namespace.MustOrgProject("acme", ""), "widget", t.TempDir())
if err != nil {
t.Fatalf("open: %v", err)
}
defer db.Close()
// PRAGMA readback is numeric for the boolean/enum pragmas: foreign_keys 1 = ON,
// synchronous 1 = NORMAL. journal_mode answers with its name.
for _, want := range []struct{ pragma, value string }{
{"journal_mode", "wal"},
{"foreign_keys", "1"},
{"synchronous", "1"},
} {
var got string
if err := db.QueryRow("PRAGMA " + want.pragma).Scan(&got); err != nil {
t.Errorf("PRAGMA %s: %v", want.pragma, err)
continue
}
if got != want.value {
t.Errorf("PRAGMA %s = %s, want %s — a store somewhere is now relying on a default the driver stopped setting", want.pragma, got, want.value)
}
}
// The stores set busy_timeout=5000. The driver sets its own; require only that
// there IS a wait, because the number is the driver's to choose and a longer
// one is strictly safer than the value the stores hard-coded.
var busy int
if err := db.QueryRow(`PRAGMA busy_timeout`).Scan(&busy); err != nil {
t.Fatalf("PRAGMA busy_timeout: %v", err)
}
if busy < 5000 {
t.Errorf("busy_timeout = %d, want at least the 5000 the stores used to set", busy)
}
}