4151b6f8c5
- FolderState: GetFolderState, UpsertFolderState, ListFolderStates, DecideResync - syncFolder nutzt per-folder UID-Tracking statt globalem highest_uid - UIDVALIDITY-Check loest automatisch Full-Resync aus - imap_folder_state Tabelle in initSchema (CREATE TABLE IF NOT EXISTS) - SetAuditLogger in main.go verdrahtet Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
83 lines
2.1 KiB
Go
83 lines
2.1 KiB
Go
package imap
|
|
|
|
import "testing"
|
|
|
|
// TestDecideResync covers the per-folder sync gate introduced by PROJ-45.
|
|
// The branches under test correspond directly to the acceptance criteria:
|
|
// - first-ever sync (no stored state)
|
|
// - normal incremental sync (UIDVALIDITY unchanged)
|
|
// - UIDVALIDITY mismatch → full resync + audit event
|
|
// - server returns UIDVALIDITY=0 → defensive fallback, keep stored cursor
|
|
func TestDecideResync(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
storedUID uint32
|
|
storedValid uint32
|
|
serverValid uint32
|
|
wantFull bool
|
|
wantReset bool
|
|
wantStart uint32
|
|
}{
|
|
{
|
|
name: "first sync, no stored state",
|
|
storedUID: 0,
|
|
storedValid: 0,
|
|
serverValid: 12345,
|
|
wantFull: true,
|
|
wantReset: false,
|
|
wantStart: 0,
|
|
},
|
|
{
|
|
name: "incremental, validity unchanged",
|
|
storedUID: 1500,
|
|
storedValid: 12345,
|
|
serverValid: 12345,
|
|
wantFull: false,
|
|
wantReset: false,
|
|
wantStart: 1500,
|
|
},
|
|
{
|
|
name: "uidvalidity mismatch triggers full resync + audit",
|
|
storedUID: 1500,
|
|
storedValid: 12345,
|
|
serverValid: 99999,
|
|
wantFull: true,
|
|
wantReset: true,
|
|
wantStart: 0,
|
|
},
|
|
{
|
|
name: "server uidvalidity 0 defensive: keep cursor, no reset",
|
|
storedUID: 800,
|
|
storedValid: 12345,
|
|
serverValid: 0,
|
|
wantFull: false,
|
|
wantReset: false,
|
|
wantStart: 800,
|
|
},
|
|
{
|
|
name: "stored validity 0 (legacy row) does not trigger reset",
|
|
storedUID: 0,
|
|
storedValid: 0,
|
|
serverValid: 7777,
|
|
wantFull: true,
|
|
wantReset: false,
|
|
wantStart: 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := DecideResync(tc.storedUID, tc.storedValid, tc.serverValid)
|
|
if got.FullResync != tc.wantFull {
|
|
t.Errorf("FullResync: got %v want %v", got.FullResync, tc.wantFull)
|
|
}
|
|
if got.UIDValidReset != tc.wantReset {
|
|
t.Errorf("UIDValidReset: got %v want %v", got.UIDValidReset, tc.wantReset)
|
|
}
|
|
if got.EffectiveStart != tc.wantStart {
|
|
t.Errorf("EffectiveStart: got %d want %d", got.EffectiveStart, tc.wantStart)
|
|
}
|
|
})
|
|
}
|
|
}
|