test: add comprehensive test suite (44 → 169 tests) and v1.1 plan
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>
This commit is contained in:
parent
9c3ca14578
commit
a8f3aa6f7e
12 changed files with 3820 additions and 2 deletions
140
internal/database/database_test.go
Normal file
140
internal/database/database_test.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// 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")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue