78 lines
2.3 KiB
Go
78 lines
2.3 KiB
Go
package db
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestOpenMigratesAndSeeds(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "test.db")
|
|
|
|
d, err := Open(path)
|
|
if err != nil {
|
|
t.Fatalf("first open: %v", err)
|
|
}
|
|
|
|
// Local user is seeded.
|
|
var email string
|
|
if err := d.QueryRow(`SELECT email FROM users WHERE id = ?`, LocalUserID).Scan(&email); err != nil {
|
|
t.Fatalf("local user not seeded: %v", err)
|
|
}
|
|
|
|
// All expected tables exist.
|
|
for _, table := range []string{"users", "documents", "suggestions", "plagiarism_reports", "schema_migrations"} {
|
|
var name string
|
|
err := d.QueryRow(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, table).Scan(&name)
|
|
if err != nil {
|
|
t.Errorf("table %q missing: %v", table, err)
|
|
}
|
|
}
|
|
|
|
// The voice suggestion type is permitted by the CHECK constraint.
|
|
if _, err := d.Exec(`INSERT INTO documents (id, user_id) VALUES ('d1', ?)`, LocalUserID); err != nil {
|
|
t.Fatalf("insert document: %v", err)
|
|
}
|
|
if _, err := d.Exec(
|
|
`INSERT INTO suggestions (doc_id, from_pos, to_pos, original, replacement, explanation, type)
|
|
VALUES ('d1', 0, 4, 'teh', '', 'voice flag', 'voice')`,
|
|
); err != nil {
|
|
t.Fatalf("insert voice suggestion: %v", err)
|
|
}
|
|
|
|
// An invalid type is rejected.
|
|
if _, err := d.Exec(
|
|
`INSERT INTO suggestions (doc_id, from_pos, to_pos, original, replacement, explanation, type)
|
|
VALUES ('d1', 0, 4, 'teh', 'the', 'x', 'nonsense')`,
|
|
); err == nil {
|
|
t.Error("expected CHECK constraint to reject invalid suggestion type")
|
|
}
|
|
|
|
// Cascade delete removes child suggestions (foreign keys enabled).
|
|
if _, err := d.Exec(`DELETE FROM documents WHERE id = 'd1'`); err != nil {
|
|
t.Fatalf("delete document: %v", err)
|
|
}
|
|
var n int
|
|
if err := d.QueryRow(`SELECT COUNT(*) FROM suggestions WHERE doc_id = 'd1'`).Scan(&n); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 0 {
|
|
t.Errorf("expected cascade delete, got %d orphan suggestions", n)
|
|
}
|
|
d.Close()
|
|
|
|
// Re-opening is idempotent: migrations and seed don't double-apply or error.
|
|
d2, err := Open(path)
|
|
if err != nil {
|
|
t.Fatalf("second open: %v", err)
|
|
}
|
|
defer d2.Close()
|
|
|
|
var users int
|
|
if err := d2.QueryRow(`SELECT COUNT(*) FROM users WHERE id = ?`, LocalUserID).Scan(&users); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if users != 1 {
|
|
t.Errorf("expected exactly 1 local user after reopen, got %d", users)
|
|
}
|
|
}
|