1 /*
2    SPDX-FileCopyrightText: 2009 Andras Mantia <amantia@kde.org>
3 
4    SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5    SPDX-FileContributor: Kevin Ottens <kevin@kdab.com>
6 
7    SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include <QTest>
11 
12 #include "kimap/loginjob.h"
13 #include "kimap/selectjob.h"
14 #include "kimap/session.h"
15 #include "kimaptest/fakeserver.h"
16 
17 #include <QSignalSpy>
18 #include <QTest>
19 
20 using Messages = QMap<qint64, KIMAP::Message>;
21 
22 Q_DECLARE_METATYPE(KIMAP::Message)
23 Q_DECLARE_METATYPE(Messages)
24 
25 class SelectJobTest : public QObject
26 {
27     Q_OBJECT
28 
29 public:
SelectJobTest()30     explicit SelectJobTest()
31     {
32         qRegisterMetaType<KIMAP::ImapSet>();
33         qRegisterMetaType<KIMAP::Message>();
34         qRegisterMetaType<QMap<qint64, KIMAP::Message>>("QMap<qint64,KIMAP::Message>");
35     }
36 
37 private Q_SLOTS:
38 
testSingleSelect_data()39     void testSingleSelect_data()
40     {
41         QTest::addColumn<QList<QByteArray>>("scenario");
42         QTest::addColumn<QList<QByteArray>>("flags");
43         QTest::addColumn<QList<QByteArray>>("permanentflags");
44         QTest::addColumn<int>("messagecount");
45         QTest::addColumn<int>("recentcount");
46         QTest::addColumn<int>("firstUnseenIndex");
47         QTest::addColumn<qint64>("uidValidity");
48         QTest::addColumn<qint64>("nextUid");
49         QTest::addColumn<quint64>("highestmodseq");
50         QTest::addColumn<bool>("condstoreEnabled");
51         QTest::addColumn<bool>("readonly");
52         QTest::addColumn<qint64>("lastUidvalidity");
53         QTest::addColumn<quint64>("lastModseq");
54         QTest::addColumn<KIMAP::ImapSet>("vanished");
55         QTest::addColumn<Messages>("modified");
56 
57         QList<QByteArray> scenario;
58         QList<QByteArray> flags;
59         QList<QByteArray> permanentflags;
60         scenario << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\" (CONDSTORE)"
61                  << "S: * 172 EXISTS"
62                  << "S: * 1 RECENT"
63                  << "S: * OK [UNSEEN 12] Message 12 is first unseen"
64                  << "S: * OK [UIDVALIDITY 3857529045] UIDs valid"
65                  << "S: * OK [UIDNEXT 4392] Predicted next UID"
66                  << "S: * OK [HIGHESTMODSEQ 123456789]"
67                  << R"(S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft))"
68                  << R"(S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited)"
69                  << "S: A000001 OK [READ-WRITE] SELECT completed";
70 
71         flags << "\\Answered"
72               << "\\Flagged"
73               << "\\Deleted"
74               << "\\Seen"
75               << "\\Draft";
76         permanentflags << "\\Deleted"
77                        << "\\Seen"
78                        << "\\*";
79         QTest::newRow("good") << scenario << flags << permanentflags << 172 << 1 << 12 << 3857529045LL << 4392LL << 123456789ULL << true << false << -1LL
80                               << 0ULL << KIMAP::ImapSet{} << Messages{};
81 
82         scenario.clear();
83         flags.clear();
84         permanentflags.clear();
85         scenario << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\""
86                  << "S: A000001 BAD command unknown or arguments invalid";
87         QTest::newRow("bad") << scenario << flags << permanentflags << 0 << 0 << 0 << 0LL << 0LL << 0ULL << false << false << -1LL << 0ULL << KIMAP::ImapSet{}
88                              << Messages{};
89 
90         scenario.clear();
91         flags.clear();
92         permanentflags.clear();
93         scenario << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\""
94                  << "S: A000001 NO select failure";
95         QTest::newRow("no") << scenario << flags << permanentflags << 0 << 0 << 0 << 0LL << 0LL << 0ULL << false << false << -1LL << 0ULL << KIMAP::ImapSet{}
96                             << Messages{};
97 
98         scenario.clear();
99         scenario << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\" (QRESYNC (67890007 90060115194045000))"
100                  << "S: * 314 EXISTS"
101                  << "S: * 15 RECENT"
102                  << "S: * OK [UIDVALIDITY 67890007] UIDVALIDITY"
103                  << "S: * OK [UIDNEXT 567] Predicted next UID"
104                  << "S: * OK [HIGHESTMODSEQ 90060115205545359]"
105                  << "S: * OK [UNSEEN 7] There are some unseen messages in the mailbox"
106                  << R"(S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen))"
107                  << R"(S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)] Permanent flags)"
108                  << "S: * VANISHED (EARLIER) 41,43:116,118,120:211,214:540"
109                  << "S: * 49 FETCH (UID 117 FLAGS (\\Seen \\Answered) MODSEQ (90060115194045001))"
110                  << "S: * 50 FETCH (UID 119 FLAGS (\\Draft $MDNSent) MODSEQ (90060115194045308))"
111                  << "S: * 100 FETCH (UID 541 FLAGS (\\Seen $Forwarded) MODSEQ (90060115194045001))"
112                  << "S: A000001 OK [READ-WRITE] mailbox selected";
113         permanentflags = {"\\Answered", "\\Flagged", "\\Draft", "\\Deleted", "\\Seen", "\\*"};
114         flags = {"\\Answered", "\\Flagged", "\\Draft", "\\Deleted", "\\Seen"};
115         KIMAP::ImapSet vanished;
116         vanished.add(41);
117         vanished.add(KIMAP::ImapInterval{43, 116});
118         vanished.add(118);
119         vanished.add(KIMAP::ImapInterval{120, 211});
120         vanished.add(KIMAP::ImapInterval{214, 540});
121         Messages modified = {{49, KIMAP::Message{117, 0, {"\\Seen", "\\Answered"}, {{"MODSEQ", QVariant{90060115194045001ULL}}}, {}, {}}},
122                              {50, KIMAP::Message{119, 0, {"\\Draft", "$MDNSent"}, {{"MODSEQ", QVariant{90060115194045308ULL}}}, {}, {}}},
123                              {100, KIMAP::Message{541, 0, {"\\Seen", "$Forwarded"}, {{"MODSEQ", QVariant{90060115194045001ULL}}}, {}, {}}}};
124         QTest::newRow("QResync") << scenario << flags << permanentflags << 314 << 15 << 7 << 67890007LL << 567LL << 90060115205545359ULL << false << false
125                                  << 67890007LL << 90060115194045000ULL << vanished << modified;
126     }
127 
testSingleSelect()128     void testSingleSelect()
129     {
130         QFETCH(QList<QByteArray>, scenario);
131         QFETCH(QList<QByteArray>, flags);
132         QFETCH(QList<QByteArray>, permanentflags);
133         QFETCH(int, messagecount);
134         QFETCH(int, recentcount);
135         QFETCH(int, firstUnseenIndex);
136         QFETCH(qint64, uidValidity);
137         QFETCH(qint64, nextUid);
138         QFETCH(quint64, highestmodseq);
139         QFETCH(bool, condstoreEnabled);
140         QFETCH(qint64, lastUidvalidity);
141         QFETCH(quint64, lastModseq);
142         QFETCH(KIMAP::ImapSet, vanished);
143         QFETCH(Messages, modified);
144 
145         FakeServer fakeServer;
146         fakeServer.setScenario(scenario);
147         fakeServer.startAndWait();
148 
149         KIMAP::Session session(QStringLiteral("127.0.0.1"), 5989);
150 
151         auto job = new KIMAP::SelectJob(&session);
152         job->setCondstoreEnabled(condstoreEnabled);
153         job->setMailBox(QStringLiteral("INBOX"));
154         if (lastUidvalidity > -1 && lastModseq > 0) {
155             job->setQResync(lastUidvalidity, lastModseq);
156         }
157 
158         QSignalSpy vanishedSpy(job, &KIMAP::SelectJob::vanished);
159         QVERIFY(vanishedSpy.isValid());
160 
161         QSignalSpy modifiedSpy(job, &KIMAP::SelectJob::modified);
162         QVERIFY(modifiedSpy.isValid());
163 
164         bool result = job->exec();
165         QEXPECT_FAIL("bad", "Expected failure on BAD scenario", Continue);
166         QEXPECT_FAIL("no", "Expected failure on NO scenario", Continue);
167         QVERIFY(result);
168         if (result) {
169             QCOMPARE(job->flags(), flags);
170             QCOMPARE(job->permanentFlags(), permanentflags);
171             QCOMPARE(job->messageCount(), messagecount);
172             QCOMPARE(job->recentCount(), recentcount);
173             QCOMPARE(job->firstUnseenIndex(), firstUnseenIndex);
174             QCOMPARE(job->uidValidity(), uidValidity);
175             QCOMPARE(job->nextUid(), nextUid);
176             QCOMPARE(job->highestModSequence(), highestmodseq);
177 
178             if (!vanished.isEmpty()) {
179                 QCOMPARE(vanishedSpy.size(), 1);
180                 QCOMPARE(vanishedSpy.at(0).at(0).value<KIMAP::ImapSet>(), vanished);
181             }
182 
183             if (!modified.isEmpty()) {
184                 Messages collectedModified;
185                 for (const auto &modifiedSpyCatch : modifiedSpy) {
186                     const auto msgs = modifiedSpyCatch.at(0).value<Messages>();
187                     for (auto it = msgs.begin(); it != msgs.end(); ++it) {
188                         collectedModified.insert(it.key(), it.value());
189                     }
190                 }
191 
192                 QCOMPARE(collectedModified, modified);
193             }
194         }
195 
196         fakeServer.quit();
197     }
198 
testSeveralSelect()199     void testSeveralSelect()
200     {
201         FakeServer fakeServer;
202         fakeServer.setScenario(QList<QByteArray>() << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\""
203                                                    << "S: A000001 OK [READ-WRITE] SELECT completed"
204                                                    << "C: A000002 SELECT \"INBOX/Foo\""
205                                                    << "S: A000002 OK [READ-WRITE] SELECT completed");
206         fakeServer.startAndWait();
207 
208         KIMAP::Session session(QStringLiteral("127.0.0.1"), 5989);
209 
210         auto job = new KIMAP::SelectJob(&session);
211         job->setMailBox(QStringLiteral("INBOX"));
212         QVERIFY(job->exec());
213 
214         job = new KIMAP::SelectJob(&session);
215         job->setMailBox(QStringLiteral("INBOX/Foo"));
216         QVERIFY(job->exec());
217     }
218 
testReadOnlySelect_data()219     void testReadOnlySelect_data()
220     {
221         QTest::addColumn<QList<QByteArray>>("scenario");
222         QTest::addColumn<bool>("examine");
223         QTest::addColumn<bool>("isReadOnly");
224 
225         {
226             QList<QByteArray> scenario;
227             scenario << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\""
228                      << "S: A000001 OK [READ-WRITE] SELECT ok";
229             QTest::newRow("SELECT rw") << scenario << false << false;
230         }
231 
232         {
233             QList<QByteArray> scenario;
234             scenario << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\""
235                      << "S: A000001 OK SELECT ok";
236             QTest::newRow("SELECT rw (without code)") << scenario << false << false;
237         }
238 
239         {
240             QList<QByteArray> scenario;
241             scenario << FakeServer::preauth() << "C: A000001 SELECT \"INBOX\""
242                      << "S: A000001 OK [READ-ONLY] SELECT ok";
243             QTest::newRow("SELECT ro") << scenario << false << true;
244         }
245 
246         {
247             QList<QByteArray> scenario;
248             scenario << FakeServer::preauth() << "C: A000001 EXAMINE \"INBOX\""
249                      << "S: A000001 OK [READ-ONLY] EXAMINE ok";
250             QTest::newRow("EXAMINE ro") << scenario << true << true;
251         }
252     }
253 
testReadOnlySelect()254     void testReadOnlySelect()
255     {
256         QFETCH(QList<QByteArray>, scenario);
257         QFETCH(bool, examine);
258         QFETCH(bool, isReadOnly);
259 
260         FakeServer fakeServer;
261         fakeServer.setScenario(scenario);
262         fakeServer.startAndWait();
263 
264         KIMAP::Session session(QStringLiteral("127.0.0.1"), 5989);
265 
266         KIMAP::SelectJob select(&session);
267         select.setOpenReadOnly(examine);
268         select.setMailBox(QStringLiteral("INBOX"));
269         QVERIFY(select.exec());
270 
271         QCOMPARE(select.isOpenReadOnly(), isReadOnly);
272     }
273 };
274 
275 QTEST_GUILESS_MAIN(SelectJobTest)
276 
277 #include "selectjobtest.moc"
278