Files
patrickandClaude Sonnet 5.5 4eb300c252 feat(PROJ-94): Postfach-Zuordnung pro IMAP-Konto, imap-link
Tabelle imap_account_mails, Importer schreibt die Zuordnung auch bei Duplikaten,
Zugriffsregel und Index-Tokens nutzen sie, archivmail imap-link befüllt den
Bestand (Trockenlauf Standard, --apply schreibt nur die Zuordnung).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-29 14:38:27 +01:00

102 lines
3.2 KiB
Go

package storage
import (
"context"
"regexp"
"slices"
"testing"
)
func TestImapAccountToken(t *testing.T) {
re := regexp.MustCompile(`^imapacct[1-9][0-9]*$`)
for _, id := range []int64{1, 7, 42, 1234567890} {
tok := ImapAccountToken(id)
if !re.MatchString(tok) {
t.Errorf("%d: token %q does not satisfy the index format", id, tok)
}
}
if ImapAccountToken(42) != "imapacct42" {
t.Errorf("got %q", ImapAccountToken(42))
}
for _, id := range []int64{0, -1} {
if tok := ImapAccountToken(id); tok != "" {
t.Errorf("%d: invalid id must yield no token, got %q", id, tok)
}
}
}
func TestMailSourceAccountToken(t *testing.T) {
id := func(v int64) *int64 { return &v }
tests := []struct {
src MailSource
want string
}{
{MailSource{Type: "imap", ID: id(3)}, "imapacct3"},
{MailSource{Type: "imap"}, ""},
{MailSource{Type: "pop3", ID: id(3)}, ""},
{MailSource{Type: "smtp"}, ""},
{MailSource{Type: "import", ID: id(3)}, ""},
{MailSource{}, ""},
{MailSource{Type: "imap", ID: id(0)}, ""},
}
for _, tt := range tests {
if got := tt.src.AccountToken(); got != tt.want {
t.Errorf("%+v: got %q, want %q", tt.src, got, tt.want)
}
}
}
func TestOwnerIdentities(t *testing.T) {
if got := ownerIdentities(" Alice ", "ALICE@Example.com"); !slices.Equal(got, []string{"alice", "alice@example.com"}) {
t.Errorf("got %v", got)
}
if got := ownerIdentities("bob@example.com", "Bob@example.com"); !slices.Equal(got, []string{"bob@example.com"}) {
t.Errorf("duplicate identity: got %v", got)
}
if got := ownerIdentities(" ", ""); got != nil {
t.Errorf("blank identities must yield nothing, got %v", got)
}
}
func TestImapAccountIDsForOwnerWithoutDB(t *testing.T) {
s := &Store{}
ids, err := s.ImapAccountIDsForOwner(context.Background(), "alice", "alice@example.com", nil)
if err != nil || ids != nil {
t.Errorf("got (%v, %v)", ids, err)
}
if rcpt := s.DeliveryRcpt(context.Background(), "x", nil, nil); rcpt != "" {
t.Errorf("no DB must yield no rcpt, got %q", rcpt)
}
}
func TestMailSourceAccountTokensWithLinks(t *testing.T) {
id := func(v int64) *int64 { return &v }
tests := []struct {
name string
src MailSource
want []string
}{
{"source only", MailSource{Type: "imap", ID: id(3)}, []string{"imapacct3"}},
{"links without source", MailSource{Linked: []int64{12, 4}}, []string{"imapacct4", "imapacct12"}},
{"source plus links, deduplicated", MailSource{Type: "imap", ID: id(4), Linked: []int64{12, 4, 4}}, []string{"imapacct4", "imapacct12"}},
{"pop3 source is no account", MailSource{Type: "pop3", ID: id(4), Linked: []int64{7}}, []string{"imapacct7"}},
{"invalid link ids", MailSource{Linked: []int64{0, -1}}, []string{}},
{"nothing", MailSource{}, []string{}},
}
for _, tt := range tests {
if got := tt.src.AccountTokens(); !slices.Equal(got, tt.want) {
t.Errorf("%s: got %v, want %v", tt.name, got, tt.want)
}
}
}
func TestLinkedImapAccountsWithoutDB(t *testing.T) {
s := &Store{}
if ids, err := s.LinkedImapAccounts(context.Background(), "x"); err != nil || ids != nil {
t.Errorf("got (%v, %v)", ids, err)
}
if m, err := s.LinkedImapAccountsBatch(context.Background(), []string{"x"}); err != nil || len(m) != 0 {
t.Errorf("got (%v, %v)", m, err)
}
}