Add intake worker

Subsystem to monitor IMAP mailbox for new messages.

Introduces:
- intake: worker that uses IDLE or polling to detect new emails.
- imap: client wrapper for connection management and IMAP commands.
- filter: logic for IMAP search and sender allow-list.
- tracker: concurrency control to prevent processing the same UID twice.
- backoff: for handling connection retries with jitter.
This commit is contained in:
dwrz
2026-01-04 20:59:26 +00:00
parent ce3943cc1d
commit c53ee5f6ad
10 changed files with 1666 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
package tracker
import (
"sync"
"testing"
"github.com/emersion/go-imap/v2"
)
func TestNew(t *testing.T) {
tr := New()
if tr == nil {
t.Fatal("New() returned nil")
}
if tr.ids == nil {
t.Fatal("New() initialized with nil map")
}
}
func TestLifecycle(t *testing.T) {
tr := New()
uid := imap.UID(123)
// 1. First acquire should succeed.
if !tr.TryAcquire(uid) {
t.Fatalf(
"expected TryAcquire(%d) to return true, got false",
uid,
)
}
// 2. Second acquire on same UID should fail.
if tr.TryAcquire(uid) {
t.Fatalf(
"expected TryAcquire(%d) to return false when already held, got true",
uid,
)
}
// 3. Different UID should succeed.
otherUID := imap.UID(456)
if !tr.TryAcquire(otherUID) {
t.Fatalf(
"expected TryAcquire(%d) to return true, got false",
otherUID,
)
}
// 4. Release first UID.
tr.Release(uid)
// 5. First UID should be acquirable again.
if !tr.TryAcquire(uid) {
t.Fatalf(
"expected TryAcquire(%d) to return true after Release, got false",
uid,
)
}
}
func TestConcurrency(t *testing.T) {
tr := New()
uid := imap.UID(1)
routines := 16
var wg sync.WaitGroup
// Counter for successful acquisitions.
successCount := 0
var mu sync.Mutex
for range routines {
wg.Go(func() {
if tr.TryAcquire(uid) {
mu.Lock()
successCount++
mu.Unlock()
}
})
}
wg.Wait()
if successCount != 1 {
t.Errorf(
"expected exactly 1 successful acquisition for concurrent access to same UID, got %d",
successCount,
)
}
// Verify we can release safely after concurrent hammer.
tr.Release(uid)
if !tr.TryAcquire(uid) {
t.Error("failed to acquire UID after concurrent test release")
}
}