feat(store): add account from_address field + v2 migration
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
@@ -28,7 +29,7 @@ func TestOpenCreatesSchemaAndIsIdempotent(t *testing.T) {
|
||||
t.Fatalf("first Open: %v", err)
|
||||
}
|
||||
v, err := s.GetSetting("schema_version")
|
||||
if err != nil || v != "1" {
|
||||
if err != nil || v != "2" {
|
||||
t.Fatalf("schema_version: %q err=%v", v, err)
|
||||
}
|
||||
s.Close()
|
||||
@@ -39,7 +40,7 @@ func TestOpenCreatesSchemaAndIsIdempotent(t *testing.T) {
|
||||
t.Fatalf("second Open: %v", err)
|
||||
}
|
||||
defer s2.Close()
|
||||
if v, _ := s2.GetSetting("schema_version"); v != "1" {
|
||||
if v, _ := s2.GetSetting("schema_version"); v != "2" {
|
||||
t.Fatalf("schema_version after reopen: %q", v)
|
||||
}
|
||||
}
|
||||
@@ -104,3 +105,63 @@ func TestForeignKeyCascade(t *testing.T) {
|
||||
t.Fatalf("whitelist_in row not cascade-deleted: count=%d err=%v", count, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenMigratesV1AddsFromAddress(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "emcli.db")
|
||||
|
||||
// Hand-build a v1 database: accounts table WITHOUT from_address, a settings
|
||||
// table pinned at schema_version=1, and one pre-existing account row.
|
||||
raw, err := sql.Open("sqlite", p)
|
||||
if err != nil {
|
||||
t.Fatalf("sql.Open: %v", err)
|
||||
}
|
||||
const v1Schema = `
|
||||
CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
||||
CREATE TABLE accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
mode TEXT NOT NULL,
|
||||
imap_host TEXT NOT NULL,
|
||||
imap_port INTEGER NOT NULL,
|
||||
imap_security TEXT NOT NULL,
|
||||
smtp_host TEXT, smtp_port INTEGER, smtp_security TEXT,
|
||||
auth_type TEXT NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
enc_password BLOB,
|
||||
enc_oauth_client_id BLOB, enc_oauth_client_secret BLOB, enc_oauth_refresh_token BLOB,
|
||||
whitelist_in_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
whitelist_out_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
subject_regex TEXT,
|
||||
process_backlog INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
INSERT INTO settings(key,value) VALUES ('schema_version','1');
|
||||
INSERT INTO accounts(name,mode,imap_host,imap_port,imap_security,auth_type,username)
|
||||
VALUES ('legacy','RO','imap.example.com',993,'tls','password','login@example.com');
|
||||
`
|
||||
if _, err := raw.Exec(v1Schema); err != nil {
|
||||
t.Fatalf("seed v1 schema: %v", err)
|
||||
}
|
||||
raw.Close()
|
||||
|
||||
// Open via the store: the migration must add from_address and bump to v2.
|
||||
s, err := Open(p)
|
||||
if err != nil {
|
||||
t.Fatalf("Open (migrate): %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
if v, _ := s.GetSetting("schema_version"); v != "2" {
|
||||
t.Fatalf("schema_version after migrate: %q, want 2", v)
|
||||
}
|
||||
// ListAccounts SELECTs from_address; it would error if the column were missing.
|
||||
accs, err := s.ListAccounts()
|
||||
if err != nil {
|
||||
t.Fatalf("ListAccounts after migrate: %v", err)
|
||||
}
|
||||
if len(accs) != 1 || accs[0].FromAddress != "" {
|
||||
t.Fatalf("legacy account wrong after migrate: %+v", accs)
|
||||
}
|
||||
if got := accs[0].SendFrom(); got != "login@example.com" {
|
||||
t.Fatalf("legacy account should send from username, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user