1package config
2
3import (
4 "reflect"
5 "strings"
6 "testing"
7 "time"
8)
9
10func TestSaveEmailBodyEvictsLeastRecentlyAccessedAcrossFolders(t *testing.T) {
11 folderCacheTestSetup(t)
12
13 oldTime := time.Now().Add(-2 * time.Hour)
14 recentTime := time.Now().Add(-1 * time.Hour)
15
16 if err := saveEmailBodyCache(&EmailBodyCache{
17 FolderName: "INBOX",
18 Bodies: []CachedEmailBody{
19 {
20 UID: 1,
21 AccountID: "acct",
22 Body: strings.Repeat("a", 10),
23 SizeBytes: 10,
24 CachedAt: oldTime,
25 LastAccessedAt: oldTime,
26 },
27 },
28 }); err != nil {
29 t.Fatalf("save old cache: %v", err)
30 }
31
32 if err := saveEmailBodyCache(&EmailBodyCache{
33 FolderName: "Archive",
34 Bodies: []CachedEmailBody{
35 {
36 UID: 2,
37 AccountID: "acct",
38 Body: strings.Repeat("b", 10),
39 SizeBytes: 10,
40 CachedAt: recentTime,
41 LastAccessedAt: recentTime,
42 },
43 },
44 }); err != nil {
45 t.Fatalf("save recent cache: %v", err)
46 }
47
48 if err := SaveEmailBody("Sent", CachedEmailBody{
49 UID: 3,
50 AccountID: "acct",
51 Body: strings.Repeat("c", 10),
52 }, 20); err != nil {
53 t.Fatalf("SaveEmailBody: %v", err)
54 }
55
56 inbox, err := LoadEmailBodyCache("INBOX")
57 if err != nil {
58 t.Fatalf("LoadEmailBodyCache(INBOX): %v", err)
59 }
60 if len(inbox.Bodies) != 0 {
61 t.Fatalf("oldest INBOX body should be evicted, got %d bodies", len(inbox.Bodies))
62 }
63
64 archive, err := LoadEmailBodyCache("Archive")
65 if err != nil {
66 t.Fatalf("LoadEmailBodyCache(Archive): %v", err)
67 }
68 if len(archive.Bodies) != 1 || archive.Bodies[0].UID != 2 {
69 t.Fatalf("recent Archive body should remain, got %+v", archive.Bodies)
70 }
71
72 sent, err := LoadEmailBodyCache("Sent")
73 if err != nil {
74 t.Fatalf("LoadEmailBodyCache(Sent): %v", err)
75 }
76 if len(sent.Bodies) != 1 || sent.Bodies[0].UID != 3 {
77 t.Fatalf("new Sent body should remain, got %+v", sent.Bodies)
78 }
79}
80
81func TestSaveEmailBodyEvictsMultipleEntriesUntilUnderLimit(t *testing.T) {
82 folderCacheTestSetup(t)
83
84 now := time.Now()
85 bodies := make([]CachedEmailBody, 0, 4)
86 for i := 1; i <= 4; i++ {
87 accessedAt := now.Add(-time.Duration(5-i) * time.Minute)
88 bodies = append(bodies, CachedEmailBody{
89 UID: uint32(i),
90 AccountID: "acct",
91 Body: strings.Repeat(string(rune('a'+i-1)), 10),
92 SizeBytes: 10,
93 CachedAt: accessedAt,
94 LastAccessedAt: accessedAt,
95 })
96 }
97
98 if err := saveEmailBodyCache(&EmailBodyCache{
99 FolderName: "INBOX",
100 Bodies: bodies,
101 }); err != nil {
102 t.Fatalf("save cache: %v", err)
103 }
104
105 if err := SaveEmailBody("Archive", CachedEmailBody{
106 UID: 5,
107 AccountID: "acct",
108 Body: strings.Repeat("e", 30),
109 }, 50); err != nil {
110 t.Fatalf("SaveEmailBody: %v", err)
111 }
112
113 inbox, err := LoadEmailBodyCache("INBOX")
114 if err != nil {
115 t.Fatalf("LoadEmailBodyCache(INBOX): %v", err)
116 }
117
118 gotUIDs := make([]uint32, 0, len(inbox.Bodies))
119 for _, body := range inbox.Bodies {
120 gotUIDs = append(gotUIDs, body.UID)
121 }
122 wantUIDs := []uint32{3, 4}
123 if !reflect.DeepEqual(gotUIDs, wantUIDs) {
124 t.Fatalf("remaining INBOX UIDs = %v, want %v", gotUIDs, wantUIDs)
125 }
126
127 archive, err := LoadEmailBodyCache("Archive")
128 if err != nil {
129 t.Fatalf("LoadEmailBodyCache(Archive): %v", err)
130 }
131 if len(archive.Bodies) != 1 || archive.Bodies[0].UID != 5 {
132 t.Fatalf("new Archive body should remain, got %+v", archive.Bodies)
133 }
134}
135
136func TestSaveEmailBodyDropsOversizedReplacement(t *testing.T) {
137 folderCacheTestSetup(t)
138
139 if err := SaveEmailBody("INBOX", CachedEmailBody{
140 UID: 1,
141 AccountID: "acct",
142 Body: strings.Repeat("a", 10),
143 }, 20); err != nil {
144 t.Fatalf("initial SaveEmailBody: %v", err)
145 }
146
147 if err := SaveEmailBody("INBOX", CachedEmailBody{
148 UID: 1,
149 AccountID: "acct",
150 Body: strings.Repeat("b", 25),
151 }, 20); err != nil {
152 t.Fatalf("oversized SaveEmailBody: %v", err)
153 }
154
155 cache, err := LoadEmailBodyCache("INBOX")
156 if err != nil {
157 t.Fatalf("LoadEmailBodyCache: %v", err)
158 }
159 if len(cache.Bodies) != 0 {
160 t.Fatalf("oversized replacement should not remain cached, got %+v", cache.Bodies)
161 }
162}