package store import ( "context" "testing" ) func TestMigratedIdempotency(t *testing.T) { s := testStore(t) ctx := context.Background() epSrc, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "src", Host: "a", Port: 993, TLSMode: "ssl"}) epDst, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "dst", Host: "b", Port: 993, TLSMode: "ssl"}) taskID, _ := s.CreateTask(ctx, Task{Name: "t", SrcEndpointID: epSrc, DstEndpointID: epDst}) accID, _ := s.CreateAccount(ctx, Account{TaskID: taskID, SrcLogin: "u", SrcPassEnc: "x", DstLogin: "u2", DstPassEnc: "y"}) if err := s.MarkMigrated(ctx, accID, "INBOX", ""); err != nil { t.Fatalf("mark: %v", err) } if err := s.MarkMigrated(ctx, accID, "INBOX", ""); err != nil { t.Fatalf("second mark must not error (ON CONFLICT): %v", err) } ok, err := s.IsMigrated(ctx, accID, "") if err != nil || !ok { t.Fatalf("IsMigrated = %v,%v want true,nil", ok, err) } absent, _ := s.IsMigrated(ctx, accID, "") if absent { t.Fatal("unknown key must be false") } } // A re-run must start from zero counters, not accumulate on top of the previous // run's totals — otherwise a run that skips everything still shows the prior // run's Copied count. ResetAccountCounters is called at the start of runAccount. func TestResetAccountCounters(t *testing.T) { s := testStore(t) ctx := context.Background() epSrc, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "src", Host: "a", Port: 993, TLSMode: "ssl"}) epDst, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "dst", Host: "b", Port: 993, TLSMode: "ssl"}) taskID, _ := s.CreateTask(ctx, Task{Name: "t", SrcEndpointID: epSrc, DstEndpointID: epDst}) accID, _ := s.CreateAccount(ctx, Account{TaskID: taskID, SrcLogin: "u", SrcPassEnc: "x", DstLogin: "u2", DstPassEnc: "y"}) // First run: everything copied. if err := s.IncAccountCounters(ctx, accID, 100, 0, 1); err != nil { t.Fatalf("inc: %v", err) } // Second run starts: counters reset to zero. if err := s.ResetAccountCounters(ctx, accID); err != nil { t.Fatalf("reset: %v", err) } // Second run skips everything. if err := s.IncAccountCounters(ctx, accID, 0, 100, 0); err != nil { t.Fatalf("inc2: %v", err) } accs, err := s.ListAccountsByTask(ctx, taskID) if err != nil || len(accs) != 1 { t.Fatalf("list: %v len=%d", err, len(accs)) } a := accs[0] if a.Copied != 0 || a.Skipped != 100 || a.Errors != 0 { t.Fatalf("after reset+rerun: copied=%d skipped=%d errors=%d want 0/100/0", a.Copied, a.Skipped, a.Errors) } } // Fixing an imported account's credentials must replace only what the operator // supplied: a nil password keeps the stored ciphertext, and both test verdicts // go back to unknown because they described the old credentials. func TestUpdateAccountCredentials(t *testing.T) { s := testStore(t) ctx := context.Background() epSrc, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "src", Host: "a", Port: 993, TLSMode: "ssl"}) epDst, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "dst", Host: "b", Port: 993, TLSMode: "ssl"}) taskID, _ := s.CreateTask(ctx, Task{Name: "t", SrcEndpointID: epSrc, DstEndpointID: epDst}) accID, _ := s.CreateAccount(ctx, Account{TaskID: taskID, SrcLogin: "u", SrcPassEnc: "oldsrc", DstLogin: "u2", DstPassEnc: "olddst"}) _ = s.SetAccountTestStatus(ctx, accID, "src", "fail") _ = s.SetAccountTestStatus(ctx, accID, "dst", "ok") _ = s.SetAccountError(ctx, accID, "authentication failed") newSrc := "newsrc" if err := s.UpdateAccountCredentials(ctx, accID, "u@src.example", "u@dst.example", &newSrc, nil); err != nil { t.Fatalf("update: %v", err) } accs, _ := s.ListAccountsByTask(ctx, taskID) if len(accs) != 1 { t.Fatalf("len=%d want 1", len(accs)) } a := accs[0] if a.SrcLogin != "u@src.example" || a.DstLogin != "u@dst.example" { t.Fatalf("logins not updated: %q / %q", a.SrcLogin, a.DstLogin) } if a.SrcPassEnc != "newsrc" { t.Fatalf("src password not updated: %q", a.SrcPassEnc) } if a.DstPassEnc != "olddst" { t.Fatalf("nil password must keep the stored one, got %q", a.DstPassEnc) } if a.TestSrcStatus != "unknown" || a.TestDstStatus != "unknown" { t.Fatalf("test statuses not reset: %q / %q", a.TestSrcStatus, a.TestDstStatus) } if a.LastError != "" { t.Fatalf("last_error not cleared: %q", a.LastError) } } func TestSetAccountFolderMapping(t *testing.T) { s := testStore(t) ctx := context.Background() epSrc, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "src", Host: "a", Port: 993, TLSMode: "ssl"}) epDst, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "dst", Host: "b", Port: 993, TLSMode: "ssl"}) taskID, _ := s.CreateTask(ctx, Task{Name: "t", SrcEndpointID: epSrc, DstEndpointID: epDst}) accID, _ := s.CreateAccount(ctx, Account{TaskID: taskID, SrcLogin: "u", SrcPassEnc: "x", DstLogin: "u2", DstPassEnc: "y"}) // Fresh account defaults: empty map, empty exclusions (not nil after scan). accs, _ := s.ListAccountsByTask(ctx, taskID) if len(accs) != 1 || len(accs[0].FolderMapping) != 0 || len(accs[0].ExcludedFolders) != 0 { t.Fatalf("defaults: map=%v excl=%v", accs[0].FolderMapping, accs[0].ExcludedFolders) } if err := s.SetAccountFolderMapping(ctx, accID, map[string]string{"Спам": "Spam"}, []string{"Trash"}); err != nil { t.Fatalf("set: %v", err) } accs, _ = s.ListAccountsByTask(ctx, taskID) a := accs[0] if a.FolderMapping["Спам"] != "Spam" { t.Fatalf("mapping not persisted: %v", a.FolderMapping) } if len(a.ExcludedFolders) != 1 || a.ExcludedFolders[0] != "Trash" { t.Fatalf("excluded not persisted: %v", a.ExcludedFolders) } }