1 /*
2  * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org>
3  SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5 
6 
7 #include <testlib/qtest_zanshin.h>
8 
9 #include "utils/mockobject.h"
10 
11 #include "testlib/akonadifakejobs.h"
12 #include "testlib/akonadifakemonitor.h"
13 
14 #include "akonadi/akonadiprojectrepository.h"
15 #include "akonadi/akonadiserializerinterface.h"
16 #include "akonadi/akonadistorageinterface.h"
17 
18 using namespace mockitopp;
19 
20 Q_DECLARE_METATYPE(Testlib::AkonadiFakeItemFetchJob*)
21 
22 class AkonadiProjectRepositoryTest : public QObject
23 {
24     Q_OBJECT
25 private slots:
shouldCreateProjectInDataSource()26     void shouldCreateProjectInDataSource()
27     {
28         // GIVEN
29 
30         // A project and its corresponding item already not existing in storage
31         Akonadi::Item item;
32         auto project = Domain::Project::Ptr::create();
33 
34         // A data source and its corresponding collection existing in storage
35         Akonadi::Collection collection(42);
36         auto source = Domain::DataSource::Ptr::create();
37 
38         // A mock create job
39         auto itemCreateJob = new FakeJob(this);
40 
41         // Storage mock returning the create job
42         Utils::MockObject<Akonadi::StorageInterface> storageMock;
43         storageMock(&Akonadi::StorageInterface::createItem).when(item, collection)
44                                                            .thenReturn(itemCreateJob);
45 
46         // Serializer mock
47         Utils::MockObject<Akonadi::SerializerInterface> serializerMock;
48         serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).thenReturn(item);
49         serializerMock(&Akonadi::SerializerInterface::createCollectionFromDataSource).when(source).thenReturn(collection);
50 
51         // WHEN
52         QScopedPointer<Akonadi::ProjectRepository> repository(new Akonadi::ProjectRepository(storageMock.getInstance(),
53                                                                                              serializerMock.getInstance()));
54         repository->create(project, source)->exec();
55 
56         // THEN
57         QVERIFY(storageMock(&Akonadi::StorageInterface::createItem).when(item, collection).exactly(1));
58     }
59 
shouldUpdateExistingProject()60     void shouldUpdateExistingProject()
61     {
62         // GIVEN
63 
64         // A project and its corresponding item already existing in storage
65         Akonadi::Item item(42);
66         Domain::Project::Ptr project(new Domain::Project);
67 
68         // A mock modify job
69         auto itemModifyJob = new FakeJob(this);
70 
71         Utils::MockObject<Akonadi::StorageInterface> storageMock;
72         Utils::MockObject<Akonadi::SerializerInterface> serializerMock;
73         QScopedPointer<Akonadi::ProjectRepository> repository(new Akonadi::ProjectRepository(storageMock.getInstance(),
74                                                                                              serializerMock.getInstance()));
75 
76         // Storage mock returning the create job
77         storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get())
78                                                            .thenReturn(itemModifyJob);
79 
80         // Serializer mock returning the item for the project
81         serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).thenReturn(item);
82 
83         // WHEN
84         repository->update(project)->exec();
85 
86         // THEN
87         QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).exactly(1));
88         QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(item, repository.get()).exactly(1));
89     }
90 
shouldRemoveExistingProject()91     void shouldRemoveExistingProject()
92     {
93         // GIVEN
94 
95         // A project and its corresponding item already existing in storage
96         Akonadi::Item item(42);
97         auto project = Domain::Project::Ptr::create();
98 
99         // A mock remove job
100         auto itemRemoveJob = new FakeJob(this);
101 
102         Utils::MockObject<Akonadi::StorageInterface> storageMock;
103         Utils::MockObject<Akonadi::SerializerInterface> serializerMock;
104         QScopedPointer<Akonadi::ProjectRepository> repository(new Akonadi::ProjectRepository(storageMock.getInstance(),
105                                                                                              serializerMock.getInstance()));
106 
107         // Storage mock returning the create job
108         storageMock(&Akonadi::StorageInterface::removeItem).when(item, repository.get())
109                                                            .thenReturn(itemRemoveJob);
110 
111         // Serializer mock returning the item for the project
112         serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(project).thenReturn(item);
113 
114         // WHEN
115         repository->remove(project)->exec();
116 
117         // THEN
118         QVERIFY(storageMock(&Akonadi::StorageInterface::removeItem).when(item, repository.get()).exactly(1));
119     }
120 
shouldAssociateATaskToAProject_data()121     void shouldAssociateATaskToAProject_data()
122     {
123         QTest::addColumn<Akonadi::Item>("childItem");
124         QTest::addColumn<Akonadi::Item>("parentItem");
125         QTest::addColumn<Domain::Task::Ptr>("child");
126         QTest::addColumn<Domain::Project::Ptr>("parent");
127         QTest::addColumn<Testlib::AkonadiFakeItemFetchJob*>("itemFetchJob1");
128         QTest::addColumn<Testlib::AkonadiFakeItemFetchJob*>("itemFetchJob2");
129         QTest::addColumn<Testlib::AkonadiFakeItemFetchJob*>("itemFetchJob3");
130         QTest::addColumn<bool>("execJob");
131         QTest::addColumn<bool>("execParentJob");
132         QTest::addColumn<Akonadi::Item::List>("list");
133 
134         Akonadi::Collection col(40);
135 
136         Akonadi::Item childItem(42);
137         childItem.setParentCollection(col);
138         Domain::Task::Ptr childTask(new Domain::Task);
139 
140         Akonadi::Item parentItem(41);
141         parentItem.setParentCollection(col);
142         auto parent = Domain::Project::Ptr::create();
143 
144         auto itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this);
145         itemFetchJob1->setItems(Akonadi::Item::List() << childItem);
146         auto itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this);
147         itemFetchJob2->setItems(Akonadi::Item::List() << parentItem);
148         auto itemFetchJob3 = new Testlib::AkonadiFakeItemFetchJob(this);
149 
150         Akonadi::Item::List list;
151 
152         QTest::newRow("nominal case (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << true << list;
153 
154         itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this);
155         itemFetchJob1->setExpectedError(KJob::KilledJobError);
156         QTest::newRow("child job error with empty list") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << false << false << list;
157 
158         itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this);
159         itemFetchJob1->setExpectedError(KJob::KilledJobError);
160         itemFetchJob1->setItems(Akonadi::Item::List() << childItem);
161         QTest::newRow("child job error with item (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << false << false << list;
162 
163         itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this);
164         itemFetchJob1->setItems(Akonadi::Item::List() << childItem);
165         itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this);
166         itemFetchJob2->setExpectedError(KJob::KilledJobError);
167         QTest::newRow("parent job error with empty list (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << false << list;
168 
169         itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this);
170         itemFetchJob1->setItems(Akonadi::Item::List() << childItem);
171         itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this);
172         itemFetchJob2->setExpectedError(KJob::KilledJobError);
173         itemFetchJob2->setItems(Akonadi::Item::List() << parentItem);
174         QTest::newRow("parent job error with item (task)") << childItem << parentItem << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << false << list;
175 
176         itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this);
177         itemFetchJob1->setItems(Akonadi::Item::List() << childItem);
178         itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this);
179         Akonadi::Collection col2(39);
180         Akonadi::Item parentItem2(41);
181         parentItem2.setParentCollection(col2);
182         itemFetchJob2->setItems(Akonadi::Item::List() << parentItem2);
183         itemFetchJob3 = new Testlib::AkonadiFakeItemFetchJob(this);
184         QTest::newRow("update and move item (task)") << childItem << parentItem2 << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << true << list;
185 
186         itemFetchJob1 = new Testlib::AkonadiFakeItemFetchJob(this);
187         itemFetchJob1->setItems(Akonadi::Item::List() << childItem);
188         itemFetchJob2 = new Testlib::AkonadiFakeItemFetchJob(this);
189         itemFetchJob2->setItems(Akonadi::Item::List() << parentItem2);
190         itemFetchJob3 = new Testlib::AkonadiFakeItemFetchJob(this);
191         Akonadi::Item childItem2(43);
192         Akonadi::Item::List list2;
193         list2 << childItem2;
194         itemFetchJob3->setItems(list2);
195         QTest::newRow("update and move item and his child (task)") << childItem << parentItem2 << childTask << parent << itemFetchJob1 << itemFetchJob2 << itemFetchJob3 << true << true << list2;
196     }
197 
shouldAssociateATaskToAProject()198     void shouldAssociateATaskToAProject()
199     {
200         // GIVEN
201         QFETCH(Akonadi::Item, childItem);
202         QFETCH(Akonadi::Item, parentItem);
203         QFETCH(Domain::Task::Ptr, child);
204         QFETCH(Domain::Project::Ptr, parent);
205         QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob1);
206         QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob2);
207         QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob3);
208         QFETCH(bool, execJob);
209         QFETCH(bool, execParentJob);
210         QFETCH(Akonadi::Item::List, list);
211 
212         // A mock create job
213         auto itemModifyJob = new FakeJob(this);
214         auto transactionJob = new FakeJob(this);
215         auto itemsMoveJob = new FakeJob(this);
216 
217         Akonadi::Item::List movedList;
218         movedList << childItem << list;
219 
220         Utils::MockObject<Akonadi::StorageInterface> storageMock;
221         Utils::MockObject<Akonadi::SerializerInterface> serializerMock;
222         QScopedPointer<Akonadi::ProjectRepository> repository(new Akonadi::ProjectRepository(storageMock.getInstance(),
223                                                                                              serializerMock.getInstance()));
224 
225         // Storage mock returning the create job
226         storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get())
227                                                           .thenReturn(itemFetchJob1);
228         storageMock(&Akonadi::StorageInterface::fetchItem).when(parentItem, repository.get())
229                                                           .thenReturn(itemFetchJob2);
230         if (parentItem.parentCollection().id() != childItem.parentCollection().id()) {
231             storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection(), repository.get())
232                                                                .thenReturn(itemFetchJob3);
233             storageMock(&Akonadi::StorageInterface::createTransaction).when(repository.get()).thenReturn(transactionJob);
234             storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, transactionJob)
235                                                                .thenReturn(itemModifyJob);
236             storageMock(&Akonadi::StorageInterface::moveItems).when(movedList, parentItem.parentCollection(), transactionJob)
237                                                               .thenReturn(itemsMoveJob);
238         } else {
239             storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get())
240                                                                .thenReturn(itemModifyJob);
241         }
242 
243         // Serializer mock returning the item for the task
244         serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem);
245         serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(parent).thenReturn(parentItem);
246         serializerMock(&Akonadi::SerializerInterface::updateItemProject).when(childItem, parent).thenReturn();
247         if (execParentJob)
248             serializerMock(&Akonadi::SerializerInterface::filterDescendantItems).when(list, childItem).thenReturn(list);
249 
250         // WHEN
251         auto associateJob = repository->associate(parent, child);
252         if (execJob)
253             associateJob->exec();
254 
255         // THEN
256         QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get()).exactly(1));
257         if (execJob) {
258             QVERIFY(serializerMock(&Akonadi::SerializerInterface::updateItemProject).when(childItem, parent).exactly(1));
259             QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromProject).when(parent).exactly(1));
260             QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(parentItem, repository.get()).exactly(1));
261             if (execParentJob) {
262                 if (parentItem.parentCollection().id() != childItem.parentCollection().id()) {
263                     QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItems).when(childItem.parentCollection(), repository.get()).exactly(1));
264                     QVERIFY(storageMock(&Akonadi::StorageInterface::createTransaction).when(repository.get()).thenReturn(transactionJob).exactly(1));
265                     QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, transactionJob).exactly(1));
266                     QVERIFY(storageMock(&Akonadi::StorageInterface::moveItems).when(movedList, parentItem.parentCollection(), transactionJob).exactly(1));
267                 } else {
268                     QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get()).exactly(1));
269                 }
270             }
271         }
272     }
273 
shouldDissociateATaskFromItsProject_data()274     void shouldDissociateATaskFromItsProject_data()
275     {
276         QTest::addColumn<Domain::Task::Ptr>("child");
277         QTest::addColumn<Akonadi::Item>("childItem");
278         QTest::addColumn<Testlib::AkonadiFakeItemFetchJob*>("itemFetchJob");
279         QTest::addColumn<bool>("fetchJobFailed");
280 
281         Domain::Task::Ptr taskChild(new Domain::Task);
282         Akonadi::Item childItem(42);
283 
284         auto itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this);
285         itemFetchJob->setItems(Akonadi::Item::List() << childItem);
286         QTest::newRow("task nominal case") << taskChild << childItem << itemFetchJob << false;
287 
288         itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this);
289         itemFetchJob->setExpectedError(KJob::KilledJobError);
290         QTest::newRow("task job error with empty list") << taskChild << childItem << itemFetchJob << true;
291 
292         itemFetchJob = new Testlib::AkonadiFakeItemFetchJob(this);
293         itemFetchJob->setExpectedError(KJob::KilledJobError);
294         itemFetchJob->setItems(Akonadi::Item::List() << childItem);
295         QTest::newRow("task job error with item") << taskChild << childItem << itemFetchJob << true;
296     }
297 
shouldDissociateATaskFromItsProject()298     void shouldDissociateATaskFromItsProject()
299     {
300         // GIVEN
301         QFETCH(Domain::Task::Ptr, child);
302         QFETCH(Akonadi::Item, childItem);
303         QFETCH(Testlib::AkonadiFakeItemFetchJob*, itemFetchJob);
304         QFETCH(bool, fetchJobFailed);
305 
306         auto itemModifyJob = new FakeJob(this);
307 
308         Utils::MockObject<Akonadi::StorageInterface> storageMock;
309         Utils::MockObject<Akonadi::SerializerInterface> serializerMock;
310         QScopedPointer<Akonadi::ProjectRepository> repository(new Akonadi::ProjectRepository(storageMock.getInstance(),
311                                                                                              serializerMock.getInstance()));
312 
313         // Storage mock returning the delete job
314         storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get())
315                                                            .thenReturn(itemModifyJob);
316         storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get())
317                                                           .thenReturn(itemFetchJob);
318 
319         // Serializer mock returning the item for the task
320         serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).thenReturn(childItem);
321         serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).thenReturn();
322 
323         // WHEN
324         repository->dissociate(child)->exec();
325 
326         // THEN
327         QVERIFY(serializerMock(&Akonadi::SerializerInterface::createItemFromTask).when(child).exactly(1));
328 
329         QVERIFY(storageMock(&Akonadi::StorageInterface::fetchItem).when(childItem, repository.get()).exactly(1));
330         if (!fetchJobFailed) {
331             QVERIFY(serializerMock(&Akonadi::SerializerInterface::removeItemParent).when(childItem).exactly(1));;
332             QVERIFY(storageMock(&Akonadi::StorageInterface::updateItem).when(childItem, repository.get()).exactly(1));
333         }
334 
335         // Give a chance to job to delete themselves
336         // in case of an error (since they use deleteLater() internally)
337         QTest::qWait(10);
338     }
339 };
340 
341 ZANSHIN_TEST_MAIN(AkonadiProjectRepositoryTest)
342 
343 #include "akonadiprojectrepositorytest.moc"
344