TestEntry.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. /*
  2. * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
  3. * Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 or (at your option)
  8. * version 3 of the License.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <QTest>
  19. #include "TestEntry.h"
  20. #include "core/Clock.h"
  21. #include "core/Group.h"
  22. #include "core/Metadata.h"
  23. #include "core/TimeInfo.h"
  24. #include "crypto/Crypto.h"
  25. QTEST_GUILESS_MAIN(TestEntry)
  26. void TestEntry::initTestCase()
  27. {
  28. QVERIFY(Crypto::init());
  29. }
  30. void TestEntry::testHistoryItemDeletion()
  31. {
  32. QScopedPointer<Entry> entry(new Entry());
  33. QPointer<Entry> historyEntry = new Entry();
  34. entry->addHistoryItem(historyEntry);
  35. QCOMPARE(entry->historyItems().size(), 1);
  36. QList<Entry*> historyEntriesToRemove;
  37. historyEntriesToRemove.append(historyEntry);
  38. entry->removeHistoryItems(historyEntriesToRemove);
  39. QCOMPARE(entry->historyItems().size(), 0);
  40. QVERIFY(historyEntry.isNull());
  41. }
  42. void TestEntry::testCopyDataFrom()
  43. {
  44. QScopedPointer<Entry> entry(new Entry());
  45. entry->setTitle("testtitle");
  46. entry->attributes()->set("attr1", "abc");
  47. entry->attributes()->set("attr2", "def");
  48. entry->attachments()->set("test", "123");
  49. entry->attachments()->set("test2", "456");
  50. AutoTypeAssociations::Association assoc;
  51. assoc.window = "1";
  52. assoc.sequence = "2";
  53. entry->autoTypeAssociations()->add(assoc);
  54. assoc.window = "3";
  55. assoc.sequence = "4";
  56. entry->autoTypeAssociations()->add(assoc);
  57. QScopedPointer<Entry> entry2(new Entry());
  58. entry2->copyDataFrom(entry.data());
  59. QCOMPARE(entry2->title(), QString("testtitle"));
  60. QCOMPARE(entry2->attributes()->value("attr1"), QString("abc"));
  61. QCOMPARE(entry2->attributes()->value("attr2"), QString("def"));
  62. QCOMPARE(entry2->attachments()->keys().size(), 2);
  63. QCOMPARE(entry2->attachments()->value("test"), QByteArray("123"));
  64. QCOMPARE(entry2->attachments()->value("test2"), QByteArray("456"));
  65. QCOMPARE(entry2->autoTypeAssociations()->size(), 2);
  66. QCOMPARE(entry2->autoTypeAssociations()->get(0).window, QString("1"));
  67. QCOMPARE(entry2->autoTypeAssociations()->get(1).window, QString("3"));
  68. }
  69. void TestEntry::testClone()
  70. {
  71. QScopedPointer<Entry> entryOrg(new Entry());
  72. entryOrg->setUuid(QUuid::createUuid());
  73. entryOrg->setPassword("pass");
  74. entryOrg->setTitle("Original Title");
  75. entryOrg->beginUpdate();
  76. entryOrg->setTitle("New Title");
  77. entryOrg->endUpdate();
  78. TimeInfo entryOrgTime = entryOrg->timeInfo();
  79. QDateTime dateTime = Clock::datetimeUtc(60);
  80. entryOrgTime.setCreationTime(dateTime);
  81. entryOrg->setTimeInfo(entryOrgTime);
  82. QScopedPointer<Entry> entryCloneNone(entryOrg->clone(Entry::CloneNoFlags));
  83. QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid());
  84. QCOMPARE(entryCloneNone->title(), QString("New Title"));
  85. QCOMPARE(entryCloneNone->historyItems().size(), 0);
  86. QCOMPARE(entryCloneNone->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
  87. QScopedPointer<Entry> entryCloneNewUuid(entryOrg->clone(Entry::CloneNewUuid));
  88. QVERIFY(entryCloneNewUuid->uuid() != entryOrg->uuid());
  89. QVERIFY(!entryCloneNewUuid->uuid().isNull());
  90. QCOMPARE(entryCloneNewUuid->title(), QString("New Title"));
  91. QCOMPARE(entryCloneNewUuid->historyItems().size(), 0);
  92. QCOMPARE(entryCloneNewUuid->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
  93. // Reset modification time
  94. entryOrgTime.setLastModificationTime(Clock::datetimeUtc(60));
  95. entryOrg->setTimeInfo(entryOrgTime);
  96. QScopedPointer<Entry> entryCloneRename(entryOrg->clone(Entry::CloneRenameTitle));
  97. QCOMPARE(entryCloneRename->uuid(), entryOrg->uuid());
  98. QCOMPARE(entryCloneRename->title(), QString("New Title - Clone"));
  99. // Cloning should not modify time info unless explicitly requested
  100. QCOMPARE(entryCloneRename->timeInfo(), entryOrg->timeInfo());
  101. QScopedPointer<Entry> entryCloneResetTime(entryOrg->clone(Entry::CloneResetTimeInfo));
  102. QCOMPARE(entryCloneResetTime->uuid(), entryOrg->uuid());
  103. QCOMPARE(entryCloneResetTime->title(), QString("New Title"));
  104. QCOMPARE(entryCloneResetTime->historyItems().size(), 0);
  105. QVERIFY(entryCloneResetTime->timeInfo().creationTime() != entryOrg->timeInfo().creationTime());
  106. // Date back history of original entry
  107. Entry* firstHistoryItem = entryOrg->historyItems()[0];
  108. TimeInfo entryOrgHistoryTimeInfo = firstHistoryItem->timeInfo();
  109. QDateTime datedBackEntryOrgModificationTime = entryOrgHistoryTimeInfo.lastModificationTime().addMSecs(-10);
  110. entryOrgHistoryTimeInfo.setLastModificationTime(datedBackEntryOrgModificationTime);
  111. entryOrgHistoryTimeInfo.setCreationTime(datedBackEntryOrgModificationTime);
  112. firstHistoryItem->setTimeInfo(entryOrgHistoryTimeInfo);
  113. QScopedPointer<Entry> entryCloneHistory(entryOrg->clone(Entry::CloneIncludeHistory | Entry::CloneResetTimeInfo));
  114. QCOMPARE(entryCloneHistory->uuid(), entryOrg->uuid());
  115. QCOMPARE(entryCloneHistory->title(), QString("New Title"));
  116. QCOMPARE(entryCloneHistory->historyItems().size(), entryOrg->historyItems().size());
  117. QCOMPARE(entryCloneHistory->historyItems().at(0)->title(), QString("Original Title"));
  118. QVERIFY(entryCloneHistory->timeInfo().creationTime() != entryOrg->timeInfo().creationTime());
  119. // Timeinfo of history items should not be modified
  120. QList<Entry*> entryOrgHistory = entryOrg->historyItems(), clonedHistory = entryCloneHistory->historyItems();
  121. auto entryOrgHistoryItem = entryOrgHistory.constBegin();
  122. for (auto entryCloneHistoryItem = clonedHistory.constBegin(); entryCloneHistoryItem != clonedHistory.constEnd();
  123. ++entryCloneHistoryItem, ++entryOrgHistoryItem) {
  124. QCOMPARE((*entryOrgHistoryItem)->timeInfo(), (*entryCloneHistoryItem)->timeInfo());
  125. }
  126. Database db;
  127. auto* entryOrgClone = entryOrg->clone(Entry::CloneNoFlags);
  128. entryOrgClone->setGroup(db.rootGroup());
  129. Entry* entryCloneUserRef = entryOrgClone->clone(Entry::CloneUserAsRef);
  130. entryCloneUserRef->setGroup(db.rootGroup());
  131. QCOMPARE(entryCloneUserRef->uuid(), entryOrgClone->uuid());
  132. QCOMPARE(entryCloneUserRef->title(), QString("New Title"));
  133. QCOMPARE(entryCloneUserRef->historyItems().size(), 0);
  134. QCOMPARE(entryCloneUserRef->timeInfo().creationTime(), entryOrgClone->timeInfo().creationTime());
  135. QVERIFY(entryCloneUserRef->attributes()->isReference(EntryAttributes::UserNameKey));
  136. QCOMPARE(entryCloneUserRef->resolvePlaceholder(entryCloneUserRef->username()), entryOrgClone->username());
  137. Entry* entryClonePassRef = entryOrgClone->clone(Entry::ClonePassAsRef);
  138. entryClonePassRef->setGroup(db.rootGroup());
  139. QCOMPARE(entryClonePassRef->uuid(), entryOrgClone->uuid());
  140. QCOMPARE(entryClonePassRef->title(), QString("New Title"));
  141. QCOMPARE(entryClonePassRef->historyItems().size(), 0);
  142. QCOMPARE(entryClonePassRef->timeInfo().creationTime(), entryOrgClone->timeInfo().creationTime());
  143. QVERIFY(entryClonePassRef->attributes()->isReference(EntryAttributes::PasswordKey));
  144. QCOMPARE(entryClonePassRef->resolvePlaceholder(entryCloneUserRef->password()), entryOrg->password());
  145. QCOMPARE(entryClonePassRef->attributes()->referenceUuid(EntryAttributes::PasswordKey), entryOrgClone->uuid());
  146. }
  147. void TestEntry::testResolveUrl()
  148. {
  149. QScopedPointer<Entry> entry(new Entry());
  150. QString testUrl("www.google.com");
  151. QString testCmd("cmd://firefox " + testUrl);
  152. QString testFileUnix("/home/example/test.txt");
  153. QString testFileWindows("c:/WINDOWS/test.txt");
  154. QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit");
  155. QString nonHttpUrl("ftp://google.com");
  156. QString noUrl("random text inserted here");
  157. // Test standard URL's
  158. QCOMPARE(entry->resolveUrl(""), QString(""));
  159. QCOMPARE(entry->resolveUrl(testUrl), "https://" + testUrl);
  160. QCOMPARE(entry->resolveUrl("http://" + testUrl), "http://" + testUrl);
  161. // Test file:// URL's
  162. QCOMPARE(entry->resolveUrl("file://" + testFileUnix), "file://" + testFileUnix);
  163. QCOMPARE(entry->resolveUrl(testFileUnix), "file://" + testFileUnix);
  164. QCOMPARE(entry->resolveUrl("file:///" + testFileWindows), "file:///" + testFileWindows);
  165. QCOMPARE(entry->resolveUrl(testFileWindows), "file:///" + testFileWindows);
  166. // Test cmd:// with no URL
  167. QCOMPARE(entry->resolveUrl("cmd://firefox"), QString(""));
  168. QCOMPARE(entry->resolveUrl("cmd://firefox --no-url"), QString(""));
  169. // Test cmd:// with URL's
  170. QCOMPARE(entry->resolveUrl(testCmd), "https://" + testUrl);
  171. QCOMPARE(entry->resolveUrl(testComplexCmd), "http://" + testUrl);
  172. // Test non-http URL
  173. QCOMPARE(entry->resolveUrl(nonHttpUrl), QString(""));
  174. // Test no URL
  175. QCOMPARE(entry->resolveUrl(noUrl), QString(""));
  176. }
  177. void TestEntry::testResolveUrlPlaceholders()
  178. {
  179. Entry entry;
  180. entry.setUrl("https://user:pw@keepassxc.org:80/path/example.php?q=e&s=t+2#fragment");
  181. QString rmvscm("//user:pw@keepassxc.org:80/path/example.php?q=e&s=t+2#fragment"); // Entry URL without scheme name.
  182. QString scm("https"); // Scheme name of the entry URL.
  183. QString host("keepassxc.org"); // Host component of the entry URL.
  184. QString port("80"); // Port number of the entry URL.
  185. QString path("/path/example.php"); // Path component of the entry URL.
  186. QString query("q=e&s=t+2"); // Query information of the entry URL.
  187. QString userinfo("user:pw"); // User information of the entry URL.
  188. QString username("user"); // User name of the entry URL.
  189. QString password("pw"); // Password of the entry URL.
  190. QString fragment("fragment"); // Fragment of the entry URL.
  191. QCOMPARE(entry.resolvePlaceholder("{URL:RMVSCM}"), rmvscm);
  192. QCOMPARE(entry.resolvePlaceholder("{URL:WITHOUTSCHEME}"), rmvscm);
  193. QCOMPARE(entry.resolvePlaceholder("{URL:SCM}"), scm);
  194. QCOMPARE(entry.resolvePlaceholder("{URL:SCHEME}"), scm);
  195. QCOMPARE(entry.resolvePlaceholder("{URL:HOST}"), host);
  196. QCOMPARE(entry.resolvePlaceholder("{URL:PORT}"), port);
  197. QCOMPARE(entry.resolvePlaceholder("{URL:PATH}"), path);
  198. QCOMPARE(entry.resolvePlaceholder("{URL:QUERY}"), query);
  199. QCOMPARE(entry.resolvePlaceholder("{URL:USERINFO}"), userinfo);
  200. QCOMPARE(entry.resolvePlaceholder("{URL:USERNAME}"), username);
  201. QCOMPARE(entry.resolvePlaceholder("{URL:PASSWORD}"), password);
  202. QCOMPARE(entry.resolvePlaceholder("{URL:FRAGMENT}"), fragment);
  203. }
  204. void TestEntry::testResolveRecursivePlaceholders()
  205. {
  206. Database db;
  207. auto* root = db.rootGroup();
  208. auto* entry1 = new Entry();
  209. entry1->setGroup(root);
  210. entry1->setUuid(QUuid::createUuid());
  211. entry1->setTitle("{USERNAME}");
  212. entry1->setUsername("{PASSWORD}");
  213. entry1->setPassword("{URL}");
  214. entry1->setUrl("{S:CustomTitle}");
  215. entry1->attributes()->set("CustomTitle", "RecursiveValue");
  216. QCOMPARE(entry1->resolveMultiplePlaceholders(entry1->title()), QString("RecursiveValue"));
  217. auto* entry2 = new Entry();
  218. entry2->setGroup(root);
  219. entry2->setUuid(QUuid::createUuid());
  220. entry2->setTitle("Entry2Title");
  221. entry2->setUsername("{S:CustomUserNameAttribute}");
  222. entry2->setPassword(QString("{REF:P@I:%1}").arg(entry1->uuidToHex()));
  223. entry2->setUrl("http://{S:IpAddress}:{S:Port}/{S:Uri}");
  224. entry2->attributes()->set("CustomUserNameAttribute", "CustomUserNameValue");
  225. entry2->attributes()->set("IpAddress", "127.0.0.1");
  226. entry2->attributes()->set("Port", "1234");
  227. entry2->attributes()->set("Uri", "uri/path");
  228. auto* entry3 = new Entry();
  229. entry3->setGroup(root);
  230. entry3->setUuid(QUuid::createUuid());
  231. entry3->setTitle(QString("{REF:T@I:%1}").arg(entry2->uuidToHex()));
  232. entry3->setUsername(QString("{REF:U@I:%1}").arg(entry2->uuidToHex()));
  233. entry3->setPassword(QString("{REF:P@I:%1}").arg(entry2->uuidToHex()));
  234. entry3->setUrl(QString("{REF:A@I:%1}").arg(entry2->uuidToHex()));
  235. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->title()), QString("Entry2Title"));
  236. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->username()), QString("CustomUserNameValue"));
  237. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->password()), QString("RecursiveValue"));
  238. QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->url()), QString("http://127.0.0.1:1234/uri/path"));
  239. auto* entry4 = new Entry();
  240. entry4->setGroup(root);
  241. entry4->setUuid(QUuid::createUuid());
  242. entry4->setTitle(QString("{REF:T@I:%1}").arg(entry3->uuidToHex()));
  243. entry4->setUsername(QString("{REF:U@I:%1}").arg(entry3->uuidToHex()));
  244. entry4->setPassword(QString("{REF:P@I:%1}").arg(entry3->uuidToHex()));
  245. entry4->setUrl(QString("{REF:A@I:%1}").arg(entry3->uuidToHex()));
  246. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->title()), QString("Entry2Title"));
  247. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->username()), QString("CustomUserNameValue"));
  248. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->password()), QString("RecursiveValue"));
  249. QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->url()), QString("http://127.0.0.1:1234/uri/path"));
  250. auto* entry5 = new Entry();
  251. entry5->setGroup(root);
  252. entry5->setUuid(QUuid::createUuid());
  253. entry5->attributes()->set("Scheme", "http");
  254. entry5->attributes()->set("Host", "host.org");
  255. entry5->attributes()->set("Port", "2017");
  256. entry5->attributes()->set("Path", "/some/path");
  257. entry5->attributes()->set("UserName", "username");
  258. entry5->attributes()->set("Password", "password");
  259. entry5->attributes()->set("Query", "q=e&t=s");
  260. entry5->attributes()->set("Fragment", "fragment");
  261. entry5->setUrl("{S:Scheme}://{S:UserName}:{S:Password}@{S:Host}:{S:Port}{S:Path}?{S:Query}#{S:Fragment}");
  262. entry5->setTitle("title+{URL:Path}+{URL:Fragment}+title");
  263. const QString url("http://username:password@host.org:2017/some/path?q=e&t=s#fragment");
  264. QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->url()), url);
  265. QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->title()), QString("title+/some/path+fragment+title"));
  266. auto* entry6 = new Entry();
  267. entry6->setGroup(root);
  268. entry6->setUuid(QUuid::createUuid());
  269. entry6->setTitle(QString("{REF:T@I:%1}").arg(entry3->uuidToHex()));
  270. entry6->setUsername(QString("{TITLE}"));
  271. entry6->setPassword(QString("{PASSWORD}"));
  272. QCOMPARE(entry6->resolvePlaceholder(entry6->title()), QString("Entry2Title"));
  273. QCOMPARE(entry6->resolvePlaceholder(entry6->username()), QString("Entry2Title"));
  274. QCOMPARE(entry6->resolvePlaceholder(entry6->password()), QString("{PASSWORD}"));
  275. auto* entry7 = new Entry();
  276. entry7->setGroup(root);
  277. entry7->setUuid(QUuid::createUuid());
  278. entry7->setTitle(QString("{REF:T@I:%1} and something else").arg(entry3->uuidToHex()));
  279. entry7->setUsername(QString("{TITLE}"));
  280. entry7->setPassword(QString("PASSWORD"));
  281. entry7->setNotes(QString("{lots} {of} {braces}"));
  282. QCOMPARE(entry7->resolvePlaceholder(entry7->title()), QString("Entry2Title and something else"));
  283. QCOMPARE(entry7->resolvePlaceholder(entry7->username()), QString("Entry2Title and something else"));
  284. QCOMPARE(entry7->resolvePlaceholder(entry7->password()), QString("PASSWORD"));
  285. QCOMPARE(entry7->resolvePlaceholder(entry7->notes()), QString("{lots} {of} {braces}"));
  286. }
  287. void TestEntry::testResolveReferencePlaceholders()
  288. {
  289. Database db;
  290. auto* root = db.rootGroup();
  291. auto* entry1 = new Entry();
  292. entry1->setGroup(root);
  293. entry1->setUuid(QUuid::createUuid());
  294. entry1->setTitle("Title1");
  295. entry1->setUsername("Username1");
  296. entry1->setPassword("Password1");
  297. entry1->setUrl("Url1");
  298. entry1->setNotes("Notes1");
  299. entry1->attributes()->set("CustomAttribute1", "CustomAttributeValue1");
  300. auto* group = new Group();
  301. group->setParent(root);
  302. auto* entry2 = new Entry();
  303. entry2->setGroup(group);
  304. entry2->setUuid(QUuid::createUuid());
  305. entry2->setTitle("Title2");
  306. entry2->setUsername("Username2");
  307. entry2->setPassword("Password2");
  308. entry2->setUrl("Url2");
  309. entry2->setNotes("Notes2");
  310. entry2->attributes()->set("CustomAttribute2", "CustomAttributeValue2");
  311. auto* entry3 = new Entry();
  312. entry3->setGroup(group);
  313. entry3->setUuid(QUuid::createUuid());
  314. entry3->setTitle("{S:AttributeTitle}");
  315. entry3->setUsername("{S:AttributeUsername}");
  316. entry3->setPassword("{S:AttributePassword}");
  317. entry3->setUrl("{S:AttributeUrl}");
  318. entry3->setNotes("{S:AttributeNotes}");
  319. entry3->attributes()->set("AttributeTitle", "TitleValue");
  320. entry3->attributes()->set("AttributeUsername", "UsernameValue");
  321. entry3->attributes()->set("AttributePassword", "PasswordValue");
  322. entry3->attributes()->set("AttributeUrl", "UrlValue");
  323. entry3->attributes()->set("AttributeNotes", "NotesValue");
  324. auto* tstEntry = new Entry();
  325. tstEntry->setGroup(root);
  326. tstEntry->setUuid(QUuid::createUuid());
  327. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), entry1->title());
  328. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title());
  329. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry1->username())), entry1->title());
  330. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry1->password())), entry1->title());
  331. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@A:%1}").arg(entry1->url())), entry1->title());
  332. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@N:%1}").arg(entry1->notes())), entry1->title());
  333. QCOMPARE(tstEntry->resolveMultiplePlaceholders(
  334. QString("{REF:T@O:%1}").arg(entry1->attributes()->value("CustomAttribute1"))),
  335. entry1->title());
  336. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry1->uuidToHex())), entry1->title());
  337. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry1->title())), entry1->title());
  338. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@U:%1}").arg(entry1->username())),
  339. entry1->username());
  340. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@P:%1}").arg(entry1->password())),
  341. entry1->password());
  342. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry1->url())), entry1->url());
  343. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry1->notes())), entry1->notes());
  344. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry2->uuidToHex())), entry2->title());
  345. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry2->title())), entry2->title());
  346. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@U:%1}").arg(entry2->username())), entry2->title());
  347. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@P:%1}").arg(entry2->password())), entry2->title());
  348. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@A:%1}").arg(entry2->url())), entry2->title());
  349. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@N:%1}").arg(entry2->notes())), entry2->title());
  350. QCOMPARE(tstEntry->resolveMultiplePlaceholders(
  351. QString("{REF:T@O:%1}").arg(entry2->attributes()->value("CustomAttribute2"))),
  352. entry2->title());
  353. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@T:%1}").arg(entry2->title())), entry2->title());
  354. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@U:%1}").arg(entry2->username())),
  355. entry2->username());
  356. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@P:%1}").arg(entry2->password())),
  357. entry2->password());
  358. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@A:%1}").arg(entry2->url())), entry2->url());
  359. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@N:%1}").arg(entry2->notes())), entry2->notes());
  360. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex())),
  361. entry3->attributes()->value("AttributeTitle"));
  362. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex())),
  363. entry3->attributes()->value("AttributeUsername"));
  364. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex())),
  365. entry3->attributes()->value("AttributePassword"));
  366. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex())),
  367. entry3->attributes()->value("AttributeUrl"));
  368. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex())),
  369. entry3->attributes()->value("AttributeNotes"));
  370. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:T@I:%1}").arg(entry3->uuidToHex().toUpper())),
  371. entry3->attributes()->value("AttributeTitle"));
  372. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:U@I:%1}").arg(entry3->uuidToHex().toUpper())),
  373. entry3->attributes()->value("AttributeUsername"));
  374. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:P@I:%1}").arg(entry3->uuidToHex().toUpper())),
  375. entry3->attributes()->value("AttributePassword"));
  376. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:A@I:%1}").arg(entry3->uuidToHex().toUpper())),
  377. entry3->attributes()->value("AttributeUrl"));
  378. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:N@I:%1}").arg(entry3->uuidToHex().toUpper())),
  379. entry3->attributes()->value("AttributeNotes"));
  380. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:t@i:%1}").arg(entry3->uuidToHex().toLower())),
  381. entry3->attributes()->value("AttributeTitle"));
  382. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:u@i:%1}").arg(entry3->uuidToHex().toLower())),
  383. entry3->attributes()->value("AttributeUsername"));
  384. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:p@i:%1}").arg(entry3->uuidToHex().toLower())),
  385. entry3->attributes()->value("AttributePassword"));
  386. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:a@i:%1}").arg(entry3->uuidToHex().toLower())),
  387. entry3->attributes()->value("AttributeUrl"));
  388. QCOMPARE(tstEntry->resolveMultiplePlaceholders(QString("{REF:n@i:%1}").arg(entry3->uuidToHex().toLower())),
  389. entry3->attributes()->value("AttributeNotes"));
  390. }
  391. void TestEntry::testResolveNonIdPlaceholdersToUuid()
  392. {
  393. Database db;
  394. auto* root = db.rootGroup();
  395. auto* referencedEntryTitle = new Entry();
  396. referencedEntryTitle->setGroup(root);
  397. referencedEntryTitle->setTitle("myTitle");
  398. referencedEntryTitle->setUuid(QUuid::createUuid());
  399. auto* referencedEntryUsername = new Entry();
  400. referencedEntryUsername->setGroup(root);
  401. referencedEntryUsername->setUsername("myUser");
  402. referencedEntryUsername->setUuid(QUuid::createUuid());
  403. auto* referencedEntryPassword = new Entry();
  404. referencedEntryPassword->setGroup(root);
  405. referencedEntryPassword->setPassword("myPassword");
  406. referencedEntryPassword->setUuid(QUuid::createUuid());
  407. auto* referencedEntryUrl = new Entry();
  408. referencedEntryUrl->setGroup(root);
  409. referencedEntryUrl->setUrl("myUrl");
  410. referencedEntryUrl->setUuid(QUuid::createUuid());
  411. auto* referencedEntryNotes = new Entry();
  412. referencedEntryNotes->setGroup(root);
  413. referencedEntryNotes->setNotes("myNotes");
  414. referencedEntryNotes->setUuid(QUuid::createUuid());
  415. const QList<QChar> placeholders{'T', 'U', 'P', 'A', 'N'};
  416. for (const QChar& searchIn : placeholders) {
  417. const Entry* referencedEntry = nullptr;
  418. QString newEntryNotesRaw("{REF:I@%1:%2}");
  419. switch (searchIn.toLatin1()) {
  420. case 'T':
  421. referencedEntry = referencedEntryTitle;
  422. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->title());
  423. break;
  424. case 'U':
  425. referencedEntry = referencedEntryUsername;
  426. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->username());
  427. break;
  428. case 'P':
  429. referencedEntry = referencedEntryPassword;
  430. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->password());
  431. break;
  432. case 'A':
  433. referencedEntry = referencedEntryUrl;
  434. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->url());
  435. break;
  436. case 'N':
  437. referencedEntry = referencedEntryNotes;
  438. newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->notes());
  439. break;
  440. default:
  441. break;
  442. }
  443. auto* newEntry = new Entry();
  444. newEntry->setGroup(root);
  445. newEntry->setNotes(newEntryNotesRaw);
  446. const QString newEntryNotesResolved = newEntry->resolveMultiplePlaceholders(newEntry->notes());
  447. QCOMPARE(newEntryNotesResolved, referencedEntry->uuidToHex());
  448. }
  449. }
  450. void TestEntry::testResolveConversionPlaceholders()
  451. {
  452. Database db;
  453. auto* root = db.rootGroup();
  454. auto* entry1 = new Entry();
  455. entry1->setGroup(root);
  456. entry1->setUuid(QUuid::createUuid());
  457. entry1->setTitle("Title1 {T-CONV:/{USERNAME}/lower/} {T-CONV:/{PASSWORD}/upper/}");
  458. entry1->setUsername("Username1");
  459. entry1->setPassword("Password1");
  460. entry1->setUrl("https://example.com/?test=3423&h=sdsds");
  461. auto* entry2 = new Entry();
  462. entry2->setGroup(root);
  463. entry2->setUuid(QUuid::createUuid());
  464. entry2->setTitle("Title2");
  465. entry2->setUsername(QString("{T-CONV:/{REF:U@I:%1}/UPPER/}").arg(entry1->uuidToHex()));
  466. entry2->setPassword(QString("{REF:P@I:%1}").arg(entry1->uuidToHex()));
  467. entry2->setUrl("cmd://ssh {USERNAME}@server.com -p {PASSWORD}");
  468. // Test complicated and nested conversions
  469. QCOMPARE(entry1->resolveMultiplePlaceholders(entry1->title()), QString("Title1 username1 PASSWORD1"));
  470. QCOMPARE(entry2->resolveMultiplePlaceholders(entry2->url()),
  471. QString("cmd://ssh USERNAME1@server.com -p Password1"));
  472. // Test base64 and hex conversions
  473. QCOMPARE(entry1->resolveMultiplePlaceholders("{T-CONV:/{PASSWORD}/base64/}"), QString("UGFzc3dvcmQx"));
  474. QCOMPARE(entry1->resolveMultiplePlaceholders("{T-CONV:/{PASSWORD}/hex/}"), QString("50617373776f726431"));
  475. // Test URL encode and decode
  476. auto encodedURL = entry1->resolveMultiplePlaceholders("{T-CONV:/{URL}/uri/}");
  477. QCOMPARE(encodedURL, QString("https%3A%2F%2Fexample.com%2F%3Ftest%3D3423%26h%3Dsdsds"));
  478. QCOMPARE(entry1->resolveMultiplePlaceholders(
  479. "{T-CONV:/https%3A%2F%2Fexample.com%2F%3Ftest%3D3423%26h%3Dsdsds/uri-dec/}"),
  480. entry1->url());
  481. // Test invalid syntax
  482. QString error;
  483. entry1->resolveConversionPlaceholder("{T-CONV:/{USERNAME}/junk/}", &error);
  484. QVERIFY(!error.isEmpty());
  485. entry1->resolveConversionPlaceholder("{T-CONV:}", &error);
  486. QVERIFY(!error.isEmpty());
  487. // Check that error gets cleared
  488. entry1->resolveConversionPlaceholder("{T-CONV:/a/upper/}", &error);
  489. QVERIFY(error.isEmpty());
  490. }
  491. void TestEntry::testResolveReplacePlaceholders()
  492. {
  493. Database db;
  494. auto* root = db.rootGroup();
  495. auto* entry1 = new Entry();
  496. entry1->setGroup(root);
  497. entry1->setUuid(QUuid::createUuid());
  498. entry1->setTitle("Title1");
  499. entry1->setUsername("Username1");
  500. entry1->setPassword("Password1");
  501. auto* entry2 = new Entry();
  502. entry2->setGroup(root);
  503. entry2->setUuid(QUuid::createUuid());
  504. entry2->setTitle("SAP server1 12345");
  505. entry2->setUsername(QString("{T-REPLACE-RX:/{REF:U@I:%1}/\\d$/2/}").arg(entry1->uuidToHex()));
  506. entry2->setPassword(QString("{REF:P@I:%1}").arg(entry1->uuidToHex()));
  507. entry2->setUrl(
  508. R"(cmd://sap.exe -system={T-REPLACE-RX:/{Title}/(?i)^(.* )?(\w+(?=(\s* \d+$)))\3/$2/} -client={T-REPLACE-RX:/{Title}/(?i)^.* (?=\d+$)//} -user={USERNAME} -pw={PASSWORD})");
  509. // Test complicated and nested replacements
  510. QCOMPARE(entry2->resolveMultiplePlaceholders(entry2->url()),
  511. QString("cmd://sap.exe -system=server1 -client=12345 -user=Username2 -pw=Password1"));
  512. // Test invalid syntax
  513. QString error;
  514. entry1->resolveRegexPlaceholder("{T-REPLACE-RX:/{USERNAME}/.*+?/test/}", &error); // invalid regex
  515. QVERIFY(!error.isEmpty());
  516. entry1->resolveRegexPlaceholder("{T-REPLACE-RX:/{USERNAME}/.*/}", &error); // no replacement
  517. QVERIFY(!error.isEmpty());
  518. // Check that error gets cleared
  519. entry1->resolveRegexPlaceholder("{T-REPLACE-RX:/{USERNAME}/\\d/2/}", &error);
  520. QVERIFY(error.isEmpty());
  521. }
  522. void TestEntry::testResolveClonedEntry()
  523. {
  524. Database db;
  525. auto* root = db.rootGroup();
  526. auto* original = new Entry();
  527. original->setGroup(root);
  528. original->setUuid(QUuid::createUuid());
  529. original->setTitle("Title");
  530. original->setUsername("SomeUsername");
  531. original->setPassword("SomePassword");
  532. QCOMPARE(original->resolveMultiplePlaceholders(original->username()), original->username());
  533. QCOMPARE(original->resolveMultiplePlaceholders(original->password()), original->password());
  534. // Top-level clones.
  535. Entry* clone1 = original->clone(Entry::CloneNewUuid);
  536. clone1->setGroup(root);
  537. Entry* clone2 = original->clone(Entry::CloneUserAsRef | Entry::CloneNewUuid);
  538. clone2->setGroup(root);
  539. Entry* clone3 = original->clone(Entry::ClonePassAsRef | Entry::CloneNewUuid);
  540. clone3->setGroup(root);
  541. Entry* clone4 = original->clone(Entry::CloneUserAsRef | Entry::ClonePassAsRef | Entry::CloneNewUuid);
  542. clone4->setGroup(root);
  543. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->username()), original->username());
  544. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->password()), original->password());
  545. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->username()), original->username());
  546. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->password()), original->password());
  547. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->username()), original->username());
  548. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->password()), original->password());
  549. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->username()), original->username());
  550. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->password()), original->password());
  551. // Second-level clones.
  552. Entry* cclone1 = clone4->clone(Entry::CloneNewUuid);
  553. cclone1->setGroup(root);
  554. Entry* cclone2 = clone4->clone(Entry::CloneUserAsRef | Entry::CloneNewUuid);
  555. cclone2->setGroup(root);
  556. Entry* cclone3 = clone4->clone(Entry::ClonePassAsRef | Entry::CloneNewUuid);
  557. cclone3->setGroup(root);
  558. Entry* cclone4 = clone4->clone(Entry::CloneUserAsRef | Entry::ClonePassAsRef | Entry::CloneNewUuid);
  559. cclone4->setGroup(root);
  560. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->username()), original->username());
  561. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->password()), original->password());
  562. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->username()), original->username());
  563. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->password()), original->password());
  564. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->username()), original->username());
  565. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->password()), original->password());
  566. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->username()), original->username());
  567. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->password()), original->password());
  568. // Change the original's attributes and make sure that the changes are tracked.
  569. QString oldUsername = original->username();
  570. QString oldPassword = original->password();
  571. original->setUsername("DifferentUsername");
  572. original->setPassword("DifferentPassword");
  573. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->username()), oldUsername);
  574. QCOMPARE(clone1->resolveMultiplePlaceholders(clone1->password()), oldPassword);
  575. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->username()), original->username());
  576. QCOMPARE(clone2->resolveMultiplePlaceholders(clone2->password()), oldPassword);
  577. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->username()), oldUsername);
  578. QCOMPARE(clone3->resolveMultiplePlaceholders(clone3->password()), original->password());
  579. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->username()), original->username());
  580. QCOMPARE(clone4->resolveMultiplePlaceholders(clone4->password()), original->password());
  581. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->username()), original->username());
  582. QCOMPARE(cclone1->resolveMultiplePlaceholders(cclone1->password()), original->password());
  583. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->username()), original->username());
  584. QCOMPARE(cclone2->resolveMultiplePlaceholders(cclone2->password()), original->password());
  585. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->username()), original->username());
  586. QCOMPARE(cclone3->resolveMultiplePlaceholders(cclone3->password()), original->password());
  587. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->username()), original->username());
  588. QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->password()), original->password());
  589. }
  590. void TestEntry::testIsRecycled()
  591. {
  592. auto entry = new Entry();
  593. QVERIFY(!entry->isRecycled());
  594. Database db;
  595. Group* root = db.rootGroup();
  596. QVERIFY(root);
  597. entry->setGroup(root);
  598. QVERIFY(!entry->isRecycled());
  599. QVERIFY(db.metadata()->recycleBinEnabled());
  600. db.recycleEntry(entry);
  601. QVERIFY(entry->isRecycled());
  602. auto group1 = new Group();
  603. group1->setParent(root);
  604. auto entry1 = new Entry();
  605. entry1->setGroup(group1);
  606. QVERIFY(!entry1->isRecycled());
  607. db.recycleGroup(group1);
  608. QVERIFY(entry1->isRecycled());
  609. }
  610. void TestEntry::testMoveUpDown()
  611. {
  612. Database db;
  613. Group* root = db.rootGroup();
  614. QVERIFY(root);
  615. auto entry0 = new Entry();
  616. QVERIFY(entry0);
  617. entry0->setGroup(root);
  618. auto entry1 = new Entry();
  619. QVERIFY(entry1);
  620. entry1->setGroup(root);
  621. auto entry2 = new Entry();
  622. QVERIFY(entry2);
  623. entry2->setGroup(root);
  624. auto entry3 = new Entry();
  625. QVERIFY(entry3);
  626. entry3->setGroup(root);
  627. // default order, straight
  628. QCOMPARE(root->entries().at(0), entry0);
  629. QCOMPARE(root->entries().at(1), entry1);
  630. QCOMPARE(root->entries().at(2), entry2);
  631. QCOMPARE(root->entries().at(3), entry3);
  632. entry0->moveDown();
  633. QCOMPARE(root->entries().at(0), entry1);
  634. QCOMPARE(root->entries().at(1), entry0);
  635. QCOMPARE(root->entries().at(2), entry2);
  636. QCOMPARE(root->entries().at(3), entry3);
  637. entry0->moveDown();
  638. QCOMPARE(root->entries().at(0), entry1);
  639. QCOMPARE(root->entries().at(1), entry2);
  640. QCOMPARE(root->entries().at(2), entry0);
  641. QCOMPARE(root->entries().at(3), entry3);
  642. entry0->moveDown();
  643. QCOMPARE(root->entries().at(0), entry1);
  644. QCOMPARE(root->entries().at(1), entry2);
  645. QCOMPARE(root->entries().at(2), entry3);
  646. QCOMPARE(root->entries().at(3), entry0);
  647. // no effect
  648. entry0->moveDown();
  649. QCOMPARE(root->entries().at(0), entry1);
  650. QCOMPARE(root->entries().at(1), entry2);
  651. QCOMPARE(root->entries().at(2), entry3);
  652. QCOMPARE(root->entries().at(3), entry0);
  653. entry0->moveUp();
  654. QCOMPARE(root->entries().at(0), entry1);
  655. QCOMPARE(root->entries().at(1), entry2);
  656. QCOMPARE(root->entries().at(2), entry0);
  657. QCOMPARE(root->entries().at(3), entry3);
  658. entry0->moveUp();
  659. QCOMPARE(root->entries().at(0), entry1);
  660. QCOMPARE(root->entries().at(1), entry0);
  661. QCOMPARE(root->entries().at(2), entry2);
  662. QCOMPARE(root->entries().at(3), entry3);
  663. entry0->moveUp();
  664. QCOMPARE(root->entries().at(0), entry0);
  665. QCOMPARE(root->entries().at(1), entry1);
  666. QCOMPARE(root->entries().at(2), entry2);
  667. QCOMPARE(root->entries().at(3), entry3);
  668. // no effect
  669. entry0->moveUp();
  670. QCOMPARE(root->entries().at(0), entry0);
  671. QCOMPARE(root->entries().at(1), entry1);
  672. QCOMPARE(root->entries().at(2), entry2);
  673. QCOMPARE(root->entries().at(3), entry3);
  674. entry2->moveUp();
  675. QCOMPARE(root->entries().at(0), entry0);
  676. QCOMPARE(root->entries().at(1), entry2);
  677. QCOMPARE(root->entries().at(2), entry1);
  678. QCOMPARE(root->entries().at(3), entry3);
  679. entry0->moveDown();
  680. QCOMPARE(root->entries().at(0), entry2);
  681. QCOMPARE(root->entries().at(1), entry0);
  682. QCOMPARE(root->entries().at(2), entry1);
  683. QCOMPARE(root->entries().at(3), entry3);
  684. entry3->moveUp();
  685. QCOMPARE(root->entries().at(0), entry2);
  686. QCOMPARE(root->entries().at(1), entry0);
  687. QCOMPARE(root->entries().at(2), entry3);
  688. QCOMPARE(root->entries().at(3), entry1);
  689. entry3->moveUp();
  690. QCOMPARE(root->entries().at(0), entry2);
  691. QCOMPARE(root->entries().at(1), entry3);
  692. QCOMPARE(root->entries().at(2), entry0);
  693. QCOMPARE(root->entries().at(3), entry1);
  694. entry2->moveDown();
  695. QCOMPARE(root->entries().at(0), entry3);
  696. QCOMPARE(root->entries().at(1), entry2);
  697. QCOMPARE(root->entries().at(2), entry0);
  698. QCOMPARE(root->entries().at(3), entry1);
  699. entry1->moveUp();
  700. QCOMPARE(root->entries().at(0), entry3);
  701. QCOMPARE(root->entries().at(1), entry2);
  702. QCOMPARE(root->entries().at(2), entry1);
  703. QCOMPARE(root->entries().at(3), entry0);
  704. }
  705. void TestEntry::testPreviousParentGroup()
  706. {
  707. Database db;
  708. auto* root = db.rootGroup();
  709. root->setUuid(QUuid::createUuid());
  710. QVERIFY(!root->uuid().isNull());
  711. auto* group1 = new Group();
  712. group1->setUuid(QUuid::createUuid());
  713. group1->setParent(root);
  714. QVERIFY(!group1->uuid().isNull());
  715. auto* group2 = new Group();
  716. group2->setParent(root);
  717. group2->setUuid(QUuid::createUuid());
  718. QVERIFY(!group2->uuid().isNull());
  719. auto* entry = new Entry();
  720. QVERIFY(entry);
  721. QVERIFY(entry->previousParentGroupUuid().isNull());
  722. QVERIFY(!entry->previousParentGroup());
  723. entry->setGroup(root);
  724. QVERIFY(entry->previousParentGroupUuid().isNull());
  725. QVERIFY(!entry->previousParentGroup());
  726. // Previous parent shouldn't be recorded if new and old parent are the same
  727. entry->setGroup(root);
  728. QVERIFY(entry->previousParentGroupUuid().isNull());
  729. QVERIFY(!entry->previousParentGroup());
  730. entry->setGroup(group1);
  731. QVERIFY(entry->previousParentGroupUuid() == root->uuid());
  732. QVERIFY(entry->previousParentGroup() == root);
  733. entry->setGroup(group2);
  734. QVERIFY(entry->previousParentGroupUuid() == group1->uuid());
  735. QVERIFY(entry->previousParentGroup() == group1);
  736. entry->setGroup(group2);
  737. QVERIFY(entry->previousParentGroupUuid() == group1->uuid());
  738. QVERIFY(entry->previousParentGroup() == group1);
  739. }