6 "github.com/mjl-/mox/imapclient"
7 "github.com/mjl-/mox/store"
10func TestListBasic(t *testing.T) {
11 testListBasic(t, false)
14func TestListBasicUIDOnly(t *testing.T) {
15 testListBasic(t, true)
18func testListBasic(t *testing.T, uidonly bool) {
19 tc := start(t, uidonly)
22 tc.login("mjl@mox.example", password0)
24 ulist := func(name string, flags ...string) imapclient.UntaggedList {
28 return imapclient.UntaggedList{Flags: flags, Separator: '/', Mailbox: name}
31 tc.last(tc.client.List("INBOX"))
32 tc.xuntagged(ulist("Inbox"))
34 tc.last(tc.client.List("Inbox"))
35 tc.xuntagged(ulist("Inbox"))
37 tc.last(tc.client.List("expungebox"))
40 tc.last(tc.client.List("%"))
41 tc.xuntagged(ulist("Archive", `\Archive`), ulist("Drafts", `\Drafts`), ulist("Inbox"), ulist("Junk", `\Junk`), ulist("Sent", `\Sent`), ulist("Trash", `\Trash`))
43 tc.last(tc.client.List("*"))
44 tc.xuntagged(ulist("Archive", `\Archive`), ulist("Drafts", `\Drafts`), ulist("Inbox"), ulist("Junk", `\Junk`), ulist("Sent", `\Sent`), ulist("Trash", `\Trash`))
46 tc.last(tc.client.List("A*"))
47 tc.xuntagged(ulist("Archive", `\Archive`))
49 tc.client.Create("Inbox/todo", nil)
51 tc.last(tc.client.List("Inbox*"))
52 tc.xuntagged(ulist("Inbox"), ulist("Inbox/todo"))
54 tc.last(tc.client.List("Inbox/%"))
55 tc.xuntagged(ulist("Inbox/todo"))
57 tc.last(tc.client.List("Inbox/*"))
58 tc.xuntagged(ulist("Inbox/todo"))
60 // Leading full INBOX is turned into Inbox, so mailbox matches.
61 tc.last(tc.client.List("INBOX/*"))
62 tc.xuntagged(ulist("Inbox/todo"))
64 // No match because we are only touching various casings of the full "INBOX".
65 tc.last(tc.client.List("INBO*"))
69func TestListExtended(t *testing.T) {
70 testListExtended(t, false)
73func TestListExtendedUIDOnly(t *testing.T) {
74 testListExtended(t, true)
77func testListExtended(t *testing.T, uidonly bool) {
78 defer mockUIDValidity()()
80 tc := start(t, uidonly)
83 tc.login("mjl@mox.example", password0)
85 ulist := func(name string, flags ...string) imapclient.UntaggedList {
89 return imapclient.UntaggedList{Flags: flags, Separator: '/', Mailbox: name}
92 uidvals := map[string]uint32{}
93 use := store.DefaultInitialMailboxes.SpecialUse
94 for _, name := range []string{"Inbox", use.Archive, use.Draft, use.Junk, use.Sent, use.Trash} {
97 for _, name := range store.DefaultInitialMailboxes.Regular {
100 var uidvalnext uint32 = 3
101 uidval := func(name string) uint32 {
102 v, ok := uidvals[name]
111 ustatus := func(name string) imapclient.UntaggedStatus {
112 attrs := map[imapclient.StatusAttr]int64{
113 imapclient.StatusMessages: 0,
114 imapclient.StatusUIDNext: 1,
115 imapclient.StatusUIDValidity: int64(uidval(name)),
116 imapclient.StatusUnseen: 0,
117 imapclient.StatusDeleted: 0,
118 imapclient.StatusSize: 0,
119 imapclient.StatusRecent: 0,
120 imapclient.StatusAppendLimit: 0,
122 return imapclient.UntaggedStatus{Mailbox: name, Attrs: attrs}
126 Fsubscribed = `\Subscribed`
127 Fhaschildren = `\HasChildren`
128 Fhasnochildren = `\HasNoChildren`
129 Fnonexistent = `\NonExistent`
130 Farchive = `\Archive`
137 // untaggedlist with flags subscribed and hasnochildren
138 xlist := func(name string, flags ...string) imapclient.UntaggedList {
139 flags = append([]string{Fhasnochildren, Fsubscribed}, flags...)
140 return ulist(name, flags...)
143 xchildlist := func(name string, flags ...string) imapclient.UntaggedList {
144 u := ulist(name, flags...)
145 comp := imapclient.TaggedExtComp{String: "SUBSCRIBED"}
146 u.Extended = []imapclient.MboxListExtendedItem{{Tag: "CHILDINFO", Val: imapclient.TaggedExtVal{Comp: &comp}}}
150 tc.last(tc.client.ListFull(false, "INBOX"))
151 tc.xuntagged(xlist("Inbox"), ustatus("Inbox"))
153 tc.last(tc.client.ListFull(false, "Inbox"))
154 tc.xuntagged(xlist("Inbox"), ustatus("Inbox"))
156 tc.last(tc.client.ListFull(false, "%"))
157 tc.xuntagged(xlist("Archive", Farchive), ustatus("Archive"), xlist("Drafts", Fdraft), ustatus("Drafts"), xlist("Inbox"), ustatus("Inbox"), xlist("Junk", Fjunk), ustatus("Junk"), xlist("Sent", Fsent), ustatus("Sent"), xlist("Trash", Ftrash), ustatus("Trash"))
159 tc.last(tc.client.ListFull(false, "*"))
160 tc.xuntagged(xlist("Archive", Farchive), ustatus("Archive"), xlist("Drafts", Fdraft), ustatus("Drafts"), xlist("Inbox"), ustatus("Inbox"), xlist("Junk", Fjunk), ustatus("Junk"), xlist("Sent", Fsent), ustatus("Sent"), xlist("Trash", Ftrash), ustatus("Trash"))
162 tc.last(tc.client.ListFull(false, "A*"))
163 tc.xuntagged(xlist("Archive", Farchive), ustatus("Archive"))
165 tc.last(tc.client.ListFull(false, "A*", "Junk"))
166 tc.xuntagged(xlist("Archive", Farchive), ustatus("Archive"), xlist("Junk", Fjunk), ustatus("Junk"))
168 tc.client.Create("Inbox/todo", nil)
170 tc.last(tc.client.ListFull(false, "Inbox*"))
171 tc.xuntagged(ulist("Inbox", Fhaschildren, Fsubscribed), ustatus("Inbox"), xlist("Inbox/todo"), ustatus("Inbox/todo"))
173 tc.last(tc.client.ListFull(false, "Inbox/%"))
174 tc.xuntagged(xlist("Inbox/todo"), ustatus("Inbox/todo"))
176 tc.last(tc.client.ListFull(false, "Inbox/*"))
177 tc.xuntagged(xlist("Inbox/todo"), ustatus("Inbox/todo"))
179 // Leading full INBOX is turned into Inbox, so mailbox matches.
180 tc.last(tc.client.ListFull(false, "INBOX/*"))
181 tc.xuntagged(xlist("Inbox/todo"), ustatus("Inbox/todo"))
183 // No match because we are only touching various casings of the full "INBOX".
184 tc.last(tc.client.ListFull(false, "INBO*"))
187 tc.last(tc.client.ListFull(true, "Inbox"))
188 tc.xuntagged(xchildlist("Inbox", Fsubscribed, Fhaschildren), ustatus("Inbox"))
190 tc.client.Unsubscribe("Inbox")
191 tc.last(tc.client.ListFull(true, "Inbox"))
192 tc.xuntagged(xchildlist("Inbox", Fhaschildren), ustatus("Inbox"))
194 tc.client.Delete("Inbox/todo") // Still subscribed.
195 tc.last(tc.client.ListFull(true, "Inbox"))
196 tc.xuntagged(xchildlist("Inbox", Fhasnochildren), ustatus("Inbox"))
198 // Simple extended list without RETURN options.
199 tc.transactf("ok", `list "" ("inbox")`)
200 tc.xuntagged(ulist("Inbox"))
202 tc.transactf("ok", `list () "" ("inbox") return ()`)
203 tc.xuntagged(ulist("Inbox"))
205 tc.transactf("ok", `list "" ("inbox") return ()`)
206 tc.xuntagged(ulist("Inbox"))
208 tc.transactf("ok", `list () "" ("inbox")`)
209 tc.xuntagged(ulist("Inbox"))
211 tc.transactf("ok", `list (remote) "" ("inbox")`)
212 tc.xuntagged(ulist("Inbox"))
214 tc.transactf("ok", `list (remote) "" "/inbox"`)
217 tc.transactf("ok", `list (remote) "/inbox" ""`)
220 tc.transactf("ok", `list (remote) "inbox" ""`)
223 tc.transactf("ok", `list (remote) "inbox" "a"`)
226 tc.client.Create("inbox/a", nil)
227 tc.transactf("ok", `list (remote) "inbox" "a"`)
228 tc.xuntagged(ulist("Inbox/a"))
230 tc.client.Subscribe("x")
231 tc.transactf("ok", `list (subscribed) "" x return (subscribed)`)
232 tc.xuntagged(imapclient.UntaggedList{Flags: []string{`\Subscribed`, `\NonExistent`}, Separator: '/', Mailbox: "x"})
234 tc.transactf("bad", `list (recursivematch) "" "*"`) // Cannot have recursivematch without a base selection option like subscribed.
235 tc.transactf("bad", `list (recursivematch remote) "" "*"`) // "remote" is not a base selection option.
236 tc.transactf("bad", `list (unknown) "" "*"`) // Unknown selection options must result in BAD.
237 tc.transactf("bad", `list () "" "*" return (unknown)`) // Unknown return options must result in BAD.
240 tc.transactf("ok", `setmetadata inbox (/private/comment "y")`)
241 tc.transactf("ok", `list () "" ("inbox") return (metadata (/private/comment /shared/comment))`)
244 imapclient.UntaggedMetadataAnnotations{
246 Annotations: []imapclient.Annotation{
247 {Key: "/private/comment", IsString: true, Value: []byte("y")},
248 {Key: "/shared/comment"},
253 tc.transactf("bad", `list () "" ("inbox") return (metadata ())`) // Metadata list must be non-empty.
254 tc.transactf("bad", `list () "" ("inbox") return (metadata (/shared/comment "/private/comment" ))`) // Extra space.