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) } }) } }