package db import ( "database/sql" "path/filepath" "testing" ) // TestAddAllowBlockJunkEntryWorksAfterMigration reproduces the same class of bug as // the filter-rules CHECK migrations: a DB created before 'junk' was added to // esrv_mailbox_allowblock's list_type CHECK constraint (the self-service webmail // Blocklist feature) kept the old, narrower constraint forever, since SQLite can't // ALTER a CHECK on an existing table. func TestAddAllowBlockJunkEntryWorksAfterMigration(t *testing.T) { path := filepath.Join(t.TempDir(), "test.db") raw, err := sql.Open("sqlite", path) if err != nil { t.Fatal(err) } if _, err := raw.Exec(`CREATE TABLE esrv_mailboxes (id INTEGER PRIMARY KEY AUTOINCREMENT)`); err != nil { t.Fatal(err) } if _, err := raw.Exec(` CREATE TABLE esrv_mailbox_allowblock ( id INTEGER PRIMARY KEY AUTOINCREMENT, mailbox_id INTEGER NOT NULL REFERENCES esrv_mailboxes(id), list_type TEXT NOT NULL CHECK(list_type IN ('allow','block')), pattern TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE(mailbox_id, list_type, pattern) ) `); err != nil { t.Fatal(err) } if _, err := raw.Exec(`INSERT INTO esrv_mailboxes (id) VALUES (1)`); err != nil { t.Fatal(err) } if _, err := raw.Exec(` INSERT INTO esrv_mailbox_allowblock (mailbox_id, list_type, pattern) VALUES (1, 'allow', 'trusted@example.com') `); err != nil { t.Fatal(err) } if err := raw.Close(); err != nil { t.Fatal(err) } database, err := Open(path) if err != nil { t.Fatal(err) } t.Cleanup(func() { database.Close() }) if _, err := database.AddAllowBlockEntry(1, "junk", "spammer@example.com"); err != nil { t.Fatalf("AddAllowBlockEntry with 'junk' after migrating a legacy DB: %v", err) } entries, err := database.ListAllowBlock(1) if err != nil { t.Fatal(err) } if len(entries) != 2 { t.Fatalf("expected the pre-existing allow entry to survive the table rebuild alongside the new junk one, got %d entries", len(entries)) } found := false for _, e := range entries { if e.ListType == "allow" && e.Pattern == "trusted@example.com" { found = true } } if !found { t.Fatalf("pre-existing allow entry's data was not preserved across the migration: %+v", entries) } junked, err := database.IsJunked(1, "spammer@example.com") if err != nil || !junked { t.Fatalf("expected spammer@example.com to be junked, got junked=%v err=%v", junked, err) } }