Files
LambdaMail/services/protocols/internal/infrastructure/postgres/flag_repository_test.go
T

121 lines
4.6 KiB
Go

package postgres
import (
"context"
"sort"
"testing"
"github.com/google/uuid"
"lambdamail/protocols/internal/application/port"
)
func TestFlagRepository_SetFlags_AddThenDelThenSet(t *testing.T) {
pool := testPool(t)
defer pool.Close()
ctx := context.Background()
domainID := uuid.New()
domainName := "flagrepo-test-" + domainID.String() + ".invalid"
_, err := pool.Exec(ctx, `INSERT INTO domains (id, name, punycode_name) VALUES ($1, $2, $3)`,
domainID, domainName, domainName)
if err != nil {
t.Fatalf("seed domain: %v", err)
}
defer pool.Exec(ctx, `DELETE FROM domains WHERE id = $1`, domainID)
mailboxID := uuid.New()
_, err = pool.Exec(ctx, `INSERT INTO mailboxes (id, domain_id, local_part, email_address, password_hash) VALUES ($1, $2, 'user', $3, '$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG')`,
mailboxID, domainID, "user@"+domainName)
if err != nil {
t.Fatalf("seed mailbox: %v", err)
}
folderID := uuid.New()
_, err = pool.Exec(ctx, `INSERT INTO folders (id, mailbox_id, name, special_use) VALUES ($1, $2, 'INBOX', 'inbox')`,
folderID, mailboxID)
if err != nil {
t.Fatalf("seed folder: %v", err)
}
blobID := uuid.New()
_, err = pool.Exec(ctx, `INSERT INTO message_blobs (id, content_sha256, storage_driver, storage_path, size_bytes) VALUES ($1, $2, 'local', '/tmp/x', 10)`,
blobID, "1111110000000000000000000000000000000000000000000000000000000000"[:64])
if err != nil {
t.Fatalf("seed blob: %v", err)
}
messageID := uuid.New()
_, err = pool.Exec(ctx, `
INSERT INTO email_messages (id, mailbox_id, folder_id, uid, blob_id, sender_address, recipient_addresses, size_bytes)
VALUES ($1, $2, $3, 7, $4, 'sender@example.test', $5, 10)
`, messageID, mailboxID, folderID, blobID, []string{"user@" + domainName})
if err != nil {
t.Fatalf("seed message: %v", err)
}
readFlags := func() []string {
t.Helper()
rows, err := pool.Query(ctx, `SELECT flag FROM message_flags WHERE message_id = $1 ORDER BY flag`, messageID)
if err != nil {
t.Fatalf("query flags: %v", err)
}
defer rows.Close()
var flags []string
for rows.Next() {
var f string
if err := rows.Scan(&f); err != nil {
t.Fatalf("scan flag: %v", err)
}
flags = append(flags, f)
}
return flags
}
repo := NewFlagRepository(pool)
if updated, err := repo.SetFlags(ctx, folderID.String(), 7, port.FlagOpAdd, []string{"\\Seen", "\\Flagged"}, 0); err != nil || !updated {
t.Fatalf("add: updated=%v, err=%v", updated, err)
}
got := readFlags()
sort.Strings(got)
if len(got) != 2 || got[0] != "\\Flagged" || got[1] != "\\Seen" {
t.Fatalf("after add: flags = %v, want [\\Flagged \\Seen]", got)
}
if updated, err := repo.SetFlags(ctx, folderID.String(), 7, port.FlagOpDel, []string{"\\Flagged"}, 0); err != nil || !updated {
t.Fatalf("del: updated=%v, err=%v", updated, err)
}
got = readFlags()
if len(got) != 1 || got[0] != "\\Seen" {
t.Fatalf("after del: flags = %v, want [\\Seen]", got)
}
if updated, err := repo.SetFlags(ctx, folderID.String(), 7, port.FlagOpSet, []string{"\\Answered"}, 0); err != nil || !updated {
t.Fatalf("set: updated=%v, err=%v", updated, err)
}
got = readFlags()
if len(got) != 1 || got[0] != "\\Answered" {
t.Fatalf("after set: flags = %v, want [\\Answered] (Set replaces the full flag set)", got)
}
// A client sending "STORE 1 FLAGS (\Seen \Seen)" - a duplicate flag in a
// single command - must not fail the whole STORE with a primary key
// violation on the second INSERT for the same (message_id, received_at,
// flag).
if updated, err := repo.SetFlags(ctx, folderID.String(), 7, port.FlagOpSet, []string{"\\Seen", "\\Seen"}, 0); err != nil || !updated {
t.Fatalf("set with duplicate flags in input: updated=%v, err=%v", updated, err)
}
got = readFlags()
if len(got) != 1 || got[0] != "\\Seen" {
t.Fatalf("after set with duplicate input flags: flags = %v, want [\\Seen]", got)
}
// Test UNCHANGEDSINCE conditional store:
// Message's modseq was updated during the previous SetFlags call.
// Providing a lower unchangedSince value must fail (return updated=false).
if updated, err := repo.SetFlags(ctx, folderID.String(), 7, port.FlagOpSet, []string{"\\Flagged"}, 1); err != nil || updated {
t.Fatalf("conditional set with lower modseq: expected updated=false, got updated=%v, err=%v", updated, err)
}
// Providing a matching or higher unchangedSince value must succeed (return updated=true).
if updated, err := repo.SetFlags(ctx, folderID.String(), 7, port.FlagOpSet, []string{"\\Flagged"}, 100); err != nil || !updated {
t.Fatalf("conditional set with higher modseq: expected updated=true, got updated=%v, err=%v", updated, err)
}
}