Add 125 new test functions across 10 new test files, covering: - CSRF middleware (8 tests): double-submit cookie validation - Auth middleware (12 tests): SessionLoader, RequireAdmin, context helpers - API handlers (28 tests): auth, faves CRUD, tags, users, export/import - Web handlers (41 tests): signup, login, password reset, fave CRUD, admin panel, feeds, import/export, profiles, settings - Config (8 tests): env parsing, defaults, trusted proxies, normalization - Database (6 tests): migrations, PRAGMAs, idempotency, seeding - Image processing (10 tests): JPEG/PNG, resize, EXIF strip, path traversal - Render (6 tests): page/error/partial rendering, template functions - Settings store (3 tests): CRUD operations - Regression tests for display name fallback and CSP-safe autocomplete Also adds CSRF middleware to testServer chain for end-to-end CSRF verification, TESTPLAN.md documenting coverage, and PLANS-v1.1.md with implementation plans for notes+OG, PWA, editing UX, and admin. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
140 lines
3.1 KiB
Go
140 lines
3.1 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package database
|
|
|
|
import (
|
|
"testing"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func TestOpenInMemory(t *testing.T) {
|
|
db, err := Open(":memory:")
|
|
if err != nil {
|
|
t.Fatalf("Open(:memory:): %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Verify the connection is usable.
|
|
var result int
|
|
if err := db.QueryRow("SELECT 1").Scan(&result); err != nil {
|
|
t.Fatalf("query: %v", err)
|
|
}
|
|
if result != 1 {
|
|
t.Errorf("SELECT 1 = %d", result)
|
|
}
|
|
}
|
|
|
|
func TestMigrateCreatesAllTables(t *testing.T) {
|
|
db, err := Open(":memory:")
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if err := Migrate(db); err != nil {
|
|
t.Fatalf("migrate: %v", err)
|
|
}
|
|
|
|
// Check that core tables exist.
|
|
tables := []string{"users", "faves", "tags", "fave_tags", "sessions", "site_settings", "schema_migrations", "signup_requests"}
|
|
for _, table := range tables {
|
|
var count int
|
|
err := db.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?", table).Scan(&count)
|
|
if err != nil {
|
|
t.Errorf("check table %s: %v", table, err)
|
|
}
|
|
if count != 1 {
|
|
t.Errorf("table %s does not exist", table)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMigrateIdempotent(t *testing.T) {
|
|
db, err := Open(":memory:")
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// First migration.
|
|
if err := Migrate(db); err != nil {
|
|
t.Fatalf("first migrate: %v", err)
|
|
}
|
|
|
|
// Second migration should be a no-op.
|
|
if err := Migrate(db); err != nil {
|
|
t.Fatalf("second migrate: %v", err)
|
|
}
|
|
|
|
// Verify schema_migrations has entries (no duplicates).
|
|
var count int
|
|
db.QueryRow("SELECT COUNT(*) FROM schema_migrations").Scan(&count)
|
|
if count < 1 {
|
|
t.Error("expected at least one migration record")
|
|
}
|
|
}
|
|
|
|
func TestPRAGMAs(t *testing.T) {
|
|
db, err := Open(":memory:")
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// WAL mode.
|
|
var journalMode string
|
|
db.QueryRow("PRAGMA journal_mode").Scan(&journalMode)
|
|
// In-memory databases use "memory" journal mode, not "wal".
|
|
// WAL is only meaningful for file-based databases.
|
|
// We just verify the pragma was accepted without error.
|
|
|
|
// Foreign keys should be ON.
|
|
var fk int
|
|
db.QueryRow("PRAGMA foreign_keys").Scan(&fk)
|
|
if fk != 1 {
|
|
t.Errorf("foreign_keys = %d, want 1", fk)
|
|
}
|
|
|
|
// Busy timeout.
|
|
var timeout int
|
|
db.QueryRow("PRAGMA busy_timeout").Scan(&timeout)
|
|
if timeout != 5000 {
|
|
t.Errorf("busy_timeout = %d, want 5000", timeout)
|
|
}
|
|
}
|
|
|
|
func TestSingleConnection(t *testing.T) {
|
|
db, err := Open(":memory:")
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
stats := db.Stats()
|
|
if stats.MaxOpenConnections != 1 {
|
|
t.Errorf("MaxOpenConnections = %d, want 1", stats.MaxOpenConnections)
|
|
}
|
|
}
|
|
|
|
func TestSiteSettingsSeeded(t *testing.T) {
|
|
db, err := Open(":memory:")
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
if err := Migrate(db); err != nil {
|
|
t.Fatalf("migrate: %v", err)
|
|
}
|
|
|
|
// Migrations should seed a default site_settings row.
|
|
var siteName string
|
|
err = db.QueryRow("SELECT site_name FROM site_settings WHERE id = 1").Scan(&siteName)
|
|
if err != nil {
|
|
t.Fatalf("query site_settings: %v", err)
|
|
}
|
|
if siteName == "" {
|
|
t.Error("expected non-empty default site_name")
|
|
}
|
|
}
|