package store import "testing" func backlogAccount(t *testing.T, s *Store, backlog bool) { t.Helper() a := sampleAccount() a.ProcessBacklog = backlog if _, err := s.AddAccount(a); err != nil { t.Fatalf("AddAccount: %v", err) } } func TestBaselineIgnoresHistoryByDefault(t *testing.T) { s := openTemp(t) backlogAccount(t, s, false) if err := s.EnsureFolderBaseline("work", "INBOX", 1, 100); err != nil { t.Fatalf("baseline: %v", err) } // Existing mail (uid <= 100) is not new; 101 is. for _, tc := range []struct { uid uint32 want bool }{{50, false}, {100, false}, {101, true}} { got, _ := s.IsNew("work", "INBOX", tc.uid) if got != tc.want { t.Fatalf("IsNew(%d)=%v want %v", tc.uid, got, tc.want) } } } func TestBaselineBacklogProcessesAll(t *testing.T) { s := openTemp(t) backlogAccount(t, s, true) _ = s.EnsureFolderBaseline("work", "INBOX", 1, 100) if n, _ := s.IsNew("work", "INBOX", 1); !n { t.Fatal("with backlog, uid 1 must be new") } } func TestAckRemovesFromNewAndIsOutOfOrderSafe(t *testing.T) { s := openTemp(t) backlogAccount(t, s, false) _ = s.EnsureFolderBaseline("work", "INBOX", 1, 100) // New mail arrives: 101..105. Ack out of order: 103 then 101. _ = s.Ack("work", "INBOX", 1, 103) if n, _ := s.IsNew("work", "INBOX", 103); n { t.Fatal("103 acked, must not be new") } if n, _ := s.IsNew("work", "INBOX", 101); !n { t.Fatal("101 not acked yet, must still be new") } _ = s.Ack("work", "INBOX", 1, 101, 102) got, _ := s.FilterNew("work", "INBOX", []uint32{101, 102, 103, 104, 105}) want := []uint32{104, 105} if len(got) != 2 || got[0] != want[0] || got[1] != want[1] { t.Fatalf("FilterNew got %v want %v", got, want) } } func TestCompactionAdvancesFloor(t *testing.T) { s := openTemp(t) backlogAccount(t, s, false) _ = s.EnsureFolderBaseline("work", "INBOX", 1, 100) // Ack a contiguous run just above the floor: 101,102,103. _ = s.Ack("work", "INBOX", 1, 102, 101, 103) f, _ := s.floor("work", "INBOX") if f != 103 { t.Fatalf("floor should advance to 103, got %d", f) } // The acked rows for the collapsed run are gone. var n int _ = s.db.QueryRow("SELECT COUNT(*) FROM acked").Scan(&n) if n != 0 { t.Fatalf("acked rows should be compacted away, got %d", n) } // A hole remains uncompacted: ack 105 (gap at 104). _ = s.Ack("work", "INBOX", 1, 105) f, _ = s.floor("work", "INBOX") if f != 103 { t.Fatalf("floor must stay at 103 while 104 is a hole, got %d", f) } _ = s.db.QueryRow("SELECT COUNT(*) FROM acked").Scan(&n) if n != 1 { t.Fatalf("105 should remain in acked, got %d rows", n) } } func TestUIDValidityChangeResets(t *testing.T) { s := openTemp(t) backlogAccount(t, s, false) _ = s.EnsureFolderBaseline("work", "INBOX", 1, 100) _ = s.Ack("work", "INBOX", 1, 105) // Server reports a new UIDVALIDITY: state resets, re-baselines at new max. if err := s.EnsureFolderBaseline("work", "INBOX", 2, 10); err != nil { t.Fatalf("rebaseline: %v", err) } f, _ := s.floor("work", "INBOX") if f != 10 { t.Fatalf("floor should re-baseline to 10, got %d", f) } var n int _ = s.db.QueryRow("SELECT COUNT(*) FROM acked").Scan(&n) if n != 0 { t.Fatalf("acked must reset on uidvalidity change, got %d", n) } }