forgejo-mcp-broker/internal/store/store_test.go

149 lines
3.6 KiB
Go
Raw Normal View History

package store_test
import (
"context"
"errors"
"path/filepath"
"testing"
"kode.naiv.no/olemd/forgejo-mcp-broker/internal/store"
)
func tempStorePath(t *testing.T) string {
t.Helper()
return filepath.Join(t.TempDir(), "broker.db")
}
func TestOpen_FreshDB_AppliesMigrations(t *testing.T) {
ctx := t.Context()
s, err := store.Open(ctx, tempStorePath(t))
if err != nil {
t.Fatalf("Open: %v", err)
}
defer s.Close()
// broker_meta should exist and carry the schema_version row.
var version string
row := s.DB().QueryRowContext(ctx,
`SELECT value FROM broker_meta WHERE key = ?`, "schema_version")
if err := row.Scan(&version); err != nil {
t.Fatalf("reading schema_version: %v", err)
}
if version != "1" {
t.Errorf("schema_version = %q, want %q", version, "1")
}
// schema_migrations should have recorded the initial migration.
var count int
row = s.DB().QueryRowContext(ctx, `SELECT COUNT(*) FROM schema_migrations`)
if err := row.Scan(&count); err != nil {
t.Fatalf("counting schema_migrations: %v", err)
}
if count != 1 {
t.Errorf("schema_migrations row count = %d, want 1", count)
}
}
func TestOpen_ReopenIsIdempotent(t *testing.T) {
ctx := t.Context()
path := tempStorePath(t)
s1, err := store.Open(ctx, path)
if err != nil {
t.Fatalf("first Open: %v", err)
}
if err := s1.Close(); err != nil {
t.Fatalf("first Close: %v", err)
}
s2, err := store.Open(ctx, path)
if err != nil {
t.Fatalf("second Open: %v", err)
}
defer s2.Close()
// Still exactly one applied migration.
var count int
if err := s2.DB().QueryRowContext(ctx,
`SELECT COUNT(*) FROM schema_migrations`).Scan(&count); err != nil {
t.Fatalf("counting schema_migrations: %v", err)
}
if count != 1 {
t.Errorf("after reopen, schema_migrations count = %d, want 1", count)
}
}
func TestOpen_NonexistentParent(t *testing.T) {
// Config.Validate normally catches this, but the store must still fail
// clearly when called with an unreachable path.
ctx := t.Context()
path := filepath.Join(t.TempDir(), "does", "not", "exist", "broker.db")
s, err := store.Open(ctx, path)
if err == nil {
_ = s.Close()
t.Fatal("Open should fail for nonexistent parent directory")
}
}
func TestPing_AfterOpen(t *testing.T) {
ctx := t.Context()
s, err := store.Open(ctx, tempStorePath(t))
if err != nil {
t.Fatalf("Open: %v", err)
}
defer s.Close()
if err := s.Ping(ctx); err != nil {
t.Errorf("Ping after Open failed: %v", err)
}
}
func TestPing_AfterClose(t *testing.T) {
ctx := t.Context()
s, err := store.Open(ctx, tempStorePath(t))
if err != nil {
t.Fatalf("Open: %v", err)
}
if err := s.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
if err := s.Ping(ctx); err == nil {
t.Error("Ping after Close should fail")
}
}
func TestPing_CanceledContext(t *testing.T) {
ctx := t.Context()
s, err := store.Open(ctx, tempStorePath(t))
if err != nil {
t.Fatalf("Open: %v", err)
}
defer s.Close()
cctx, cancel := context.WithCancel(ctx)
cancel()
if err := s.Ping(cctx); err == nil {
t.Error("Ping with canceled context should fail")
} else if !errors.Is(err, context.Canceled) {
// PingContext should surface the cancellation — not a hard requirement
// since driver behavior varies slightly, so log rather than fail.
t.Logf("Ping error not context.Canceled (ok): %v", err)
}
}
func TestPath_ReturnsAbsolute(t *testing.T) {
ctx := t.Context()
// Use a relative path to confirm Store.Path returns an absolute one.
dir := t.TempDir()
t.Chdir(dir)
s, err := store.Open(ctx, "broker.db")
if err != nil {
t.Fatalf("Open: %v", err)
}
defer s.Close()
if !filepath.IsAbs(s.Path()) {
t.Errorf("Path = %q, want absolute", s.Path())
}
}