Feature 02 task 1: SQLite connection and embedded migration runner (#10)
CI / ci (push) Successful in 49s
CI / ci (push) Successful in 49s
Co-authored-by: dev_agent <dev_agent@cubecraftlabs.com> Co-committed-by: dev_agent <dev_agent@cubecraftlabs.com>
This commit was merged in pull request #10.
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func openTestStore(t *testing.T) *Store {
|
||||
t.Helper()
|
||||
s, err := Open(context.Background(), t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := s.Close(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
func TestOpenCreatesDatabaseFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
s, err := Open(context.Background(), dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
if _, err := os.Stat(filepath.Join(dir, FileName)); err != nil {
|
||||
t.Fatalf("database file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenMissingDirFails(t *testing.T) {
|
||||
_, err := Open(context.Background(), filepath.Join(t.TempDir(), "missing"))
|
||||
if err == nil {
|
||||
t.Fatal("Open succeeded on a missing data directory")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolSizes(t *testing.T) {
|
||||
s := openTestStore(t)
|
||||
if got := s.Writer.Stats().MaxOpenConnections; got != 1 {
|
||||
t.Errorf("writer max open connections = %d, want 1", got)
|
||||
}
|
||||
if got := s.Reader.Stats().MaxOpenConnections; got != readPoolSize {
|
||||
t.Errorf("reader max open connections = %d, want %d", got, readPoolSize)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPragmasOnEveryConnection holds every reader connection open at once so
|
||||
// each one is a distinct connection, then checks the pragmas on each and on
|
||||
// the writer.
|
||||
func TestPragmasOnEveryConnection(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
s := openTestStore(t)
|
||||
|
||||
conns := []*sql.Conn{}
|
||||
for range readPoolSize {
|
||||
c, err := s.Reader.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
conns = append(conns, c)
|
||||
}
|
||||
w, err := s.Writer.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer w.Close()
|
||||
conns = append(conns, w)
|
||||
|
||||
for i, c := range conns {
|
||||
var fk, busy int
|
||||
var journal string
|
||||
if err := c.QueryRowContext(ctx, "PRAGMA foreign_keys").Scan(&fk); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := c.QueryRowContext(ctx, "PRAGMA busy_timeout").Scan(&busy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := c.QueryRowContext(ctx, "PRAGMA journal_mode").Scan(&journal); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fk != 1 || busy != 5000 || journal != "wal" {
|
||||
t.Errorf("connection %d: foreign_keys=%d busy_timeout=%d journal_mode=%q, want 1, 5000, wal", i, fk, busy, journal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderIsReadOnly(t *testing.T) {
|
||||
s := openTestStore(t)
|
||||
if _, err := s.Reader.Exec("CREATE TABLE t (id INTEGER)"); err == nil {
|
||||
t.Fatal("write through the reader succeeded")
|
||||
}
|
||||
if _, err := s.Writer.Exec("CREATE TABLE t (id INTEGER)"); err != nil {
|
||||
t.Fatalf("write through the writer: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForeignKeysEnforced(t *testing.T) {
|
||||
s := openTestStore(t)
|
||||
if _, err := s.Writer.Exec(`
|
||||
CREATE TABLE parent (id INTEGER PRIMARY KEY);
|
||||
CREATE TABLE child (parent_id INTEGER NOT NULL REFERENCES parent(id));
|
||||
`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := s.Writer.Exec("INSERT INTO child (parent_id) VALUES (42)"); err == nil {
|
||||
t.Fatal("insert violating a foreign key succeeded")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user