147 lines
3.6 KiB
Go
147 lines
3.6 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// testLogger returns a logger that writes nowhere, keeping test output clean.
|
|
func testLogger() *slog.Logger {
|
|
return slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
}
|
|
|
|
func TestOpen_InMemory_正常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
|
|
db, err := Open(ctx, ":memory:", testLogger())
|
|
if err != nil {
|
|
t.Fatalf("Open(:memory:) error: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if err := db.PingContext(ctx); err != nil {
|
|
t.Fatalf("ping in-memory db: %v", err)
|
|
}
|
|
|
|
var n int
|
|
if err := db.QueryRowContext(ctx, "SELECT count(*) FROM sqlite_master WHERE type='table'").Scan(&n); err != nil {
|
|
t.Fatalf("query sqlite_master: %v", err)
|
|
}
|
|
if n != 0 {
|
|
t.Fatalf("expected no tables before migrations, got %d", n)
|
|
}
|
|
}
|
|
|
|
func TestOpen_InMemory_NilLogger_正常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
|
|
// A nil logger must not panic and must produce a usable connection.
|
|
db, err := Open(ctx, ":memory:", nil)
|
|
if err != nil {
|
|
t.Fatalf("Open with nil logger error: %v", err)
|
|
}
|
|
defer db.Close()
|
|
if err := db.PingContext(ctx); err != nil {
|
|
t.Fatalf("ping with nil logger: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestOpen_InMemory_Isolation_并发(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
|
|
db1, err := Open(ctx, ":memory:", testLogger())
|
|
if err != nil {
|
|
t.Fatalf("open db1: %v", err)
|
|
}
|
|
defer db1.Close()
|
|
db2, err := Open(ctx, ":memory:", testLogger())
|
|
if err != nil {
|
|
t.Fatalf("open db2: %v", err)
|
|
}
|
|
defer db2.Close()
|
|
|
|
if _, err := db1.ExecContext(ctx, "CREATE TABLE t1(x)"); err != nil {
|
|
t.Fatalf("create table in db1: %v", err)
|
|
}
|
|
|
|
var n int
|
|
if err := db2.QueryRowContext(ctx, "SELECT count(*) FROM sqlite_master WHERE name='t1'").Scan(&n); err != nil {
|
|
t.Fatalf("query db2: %v", err)
|
|
}
|
|
if n != 0 {
|
|
t.Fatalf("db2 must not see db1's table (isolation broken), count=%d", n)
|
|
}
|
|
}
|
|
|
|
func TestOpen_File_正常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
|
|
path := filepath.Join(t.TempDir(), "cyrene.db")
|
|
db, err := Open(ctx, path, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("Open(file) error: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if err := db.PingContext(ctx); err != nil {
|
|
t.Fatalf("ping file db: %v", err)
|
|
}
|
|
|
|
// Persistence across a reopen with the same DSN.
|
|
db.ExecContext(ctx, "CREATE TABLE p(x)")
|
|
db.Close()
|
|
|
|
db2, err := Open(ctx, path, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("reopen(file) error: %v", err)
|
|
}
|
|
defer db2.Close()
|
|
var n int
|
|
if err := db2.QueryRowContext(ctx, "SELECT count(*) FROM sqlite_master WHERE name='p'").Scan(&n); err != nil {
|
|
t.Fatalf("query reopened db: %v", err)
|
|
}
|
|
if n != 1 {
|
|
t.Fatalf("expected table p to persist across reopen, count=%d", n)
|
|
}
|
|
}
|
|
|
|
func TestOpen_BlankPath_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
|
|
for _, path := range []string{"", " "} {
|
|
if _, err := Open(ctx, path, testLogger()); !errors.Is(err, ErrInvalidPath) {
|
|
t.Fatalf("Open(%q) err = %v, want wrap of ErrInvalidPath", path, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOpen_CreatesParentDir_正常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
|
|
// Parent directory does not exist yet: Open must create it so a custom
|
|
// DATA_DIR works on first run.
|
|
dir := filepath.Join(t.TempDir(), "does-not-exist")
|
|
path := filepath.Join(dir, "cyrene.db")
|
|
db, err := Open(ctx, path, testLogger())
|
|
if err != nil {
|
|
t.Fatalf("Open(%q) error creating parent dir: %v", path, err)
|
|
}
|
|
defer db.Close()
|
|
if fi, err := os.Stat(dir); err != nil {
|
|
t.Fatalf("parent dir not created: %v", err)
|
|
} else if !fi.IsDir() {
|
|
t.Fatalf("parent path is not a dir: %v", fi)
|
|
}
|
|
}
|