1b7b70426c
- config: koanf-based loading (defaults → YAML → KINDEXR_ env vars) - db: embedded SQLite migrations with BEGIN/END-aware statement splitter - server: chi router, GET /health returns JSON stats - cmd/kindexr: graceful SIGTERM shutdown - cmd/kindexr-cli: stub - deploy: systemd unit, example config, nginx snippet - all packages covered by race-clean tests
123 lines
2.7 KiB
Go
123 lines
2.7 KiB
Go
package db_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"git.utn.lol/enki/kindexr/internal/db"
|
|
)
|
|
|
|
func tempDB(t *testing.T) *db.DB {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "test.db")
|
|
d, err := db.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("Open failed: %v", err)
|
|
}
|
|
t.Cleanup(func() { d.Close() })
|
|
return d
|
|
}
|
|
|
|
func TestOpen(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "test.db")
|
|
d, err := db.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("Open returned error: %v", err)
|
|
}
|
|
d.Close()
|
|
}
|
|
|
|
func TestMigrationsApplied(t *testing.T) {
|
|
d := tempDB(t)
|
|
ctx := context.Background()
|
|
version, err := d.MigrationVersion(ctx)
|
|
if err != nil {
|
|
t.Fatalf("MigrationVersion error: %v", err)
|
|
}
|
|
if version < 1 {
|
|
t.Errorf("expected at least 1 migration applied, got version %d", version)
|
|
}
|
|
}
|
|
|
|
func TestMigrationsIdempotent(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "test.db")
|
|
|
|
d1, err := db.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("first Open failed: %v", err)
|
|
}
|
|
d1.Close()
|
|
|
|
d2, err := db.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("second Open failed (idempotency): %v", err)
|
|
}
|
|
d2.Close()
|
|
}
|
|
|
|
func TestSchema(t *testing.T) {
|
|
d := tempDB(t)
|
|
|
|
expectedTables := []string{
|
|
"torrents", "files", "trackers", "tags",
|
|
"publishers", "relays", "api_keys", "health", "comments",
|
|
}
|
|
|
|
for _, table := range expectedTables {
|
|
var name string
|
|
err := d.QueryRow(
|
|
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, table,
|
|
).Scan(&name)
|
|
if err == sql.ErrNoRows {
|
|
t.Errorf("expected table %q to exist, but it does not", table)
|
|
} else if err != nil {
|
|
t.Errorf("querying for table %q: %v", table, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFTS5(t *testing.T) {
|
|
d := tempDB(t)
|
|
|
|
// Insert a torrent row.
|
|
_, err := d.Exec(`
|
|
INSERT INTO torrents
|
|
(event_id, info_hash, pubkey, created_at, ingested_at, title, description, raw_event)
|
|
VALUES
|
|
('evt1', 'aabbcc', 'pubkey1', 1715000000, 1715000001, 'test torrent title', 'test description', '{}')
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("insert torrent: %v", err)
|
|
}
|
|
|
|
// FTS5 search.
|
|
var rowid int64
|
|
err = d.QueryRow(`SELECT rowid FROM torrents_fts WHERE torrents_fts MATCH 'test'`).Scan(&rowid)
|
|
if err == sql.ErrNoRows {
|
|
t.Error("FTS5 search for 'test' returned no rows; trigger may not be working")
|
|
} else if err != nil {
|
|
t.Fatalf("FTS5 search error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGetStats(t *testing.T) {
|
|
d := tempDB(t)
|
|
ctx := context.Background()
|
|
|
|
stats, err := d.GetStats(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetStats error: %v", err)
|
|
}
|
|
if stats.EventsTotal != 0 {
|
|
t.Errorf("expected EventsTotal=0, got %d", stats.EventsTotal)
|
|
}
|
|
if stats.LastEventAt != nil {
|
|
t.Errorf("expected LastEventAt=nil, got %v", stats.LastEventAt)
|
|
}
|
|
}
|