The clean & modern RSS server that doesn't give you any crap. https://thearsse.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SeriesArticle.php 48KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. <?php
  2. /** @license MIT
  3. * Copyright 2017 J. King, Dustin Wilson et al.
  4. * See LICENSE and AUTHORS files for details */
  5. declare(strict_types=1);
  6. namespace JKingWeb\Arsse\TestCase\Database;
  7. use JKingWeb\Arsse\Database;
  8. use JKingWeb\Arsse\Arsse;
  9. use JKingWeb\Arsse\Misc\Context;
  10. use JKingWeb\Arsse\Misc\Date;
  11. use Phake;
  12. trait SeriesArticle {
  13. protected function setUpSeriesArticle() {
  14. $this->data = [
  15. 'arsse_users' => [
  16. 'columns' => [
  17. 'id' => 'str',
  18. 'password' => 'str',
  19. 'name' => 'str',
  20. ],
  21. 'rows' => [
  22. ["jane.doe@example.com", "", "Jane Doe"],
  23. ["john.doe@example.com", "", "John Doe"],
  24. ["john.doe@example.org", "", "John Doe"],
  25. ["john.doe@example.net", "", "John Doe"],
  26. ],
  27. ],
  28. 'arsse_folders' => [
  29. 'columns' => [
  30. 'id' => "int",
  31. 'owner' => "str",
  32. 'parent' => "int",
  33. 'name' => "str",
  34. ],
  35. 'rows' => [
  36. [1, "john.doe@example.com", null, "Technology"],
  37. [2, "john.doe@example.com", 1, "Software"],
  38. [3, "john.doe@example.com", 1, "Rocketry"],
  39. [4, "jane.doe@example.com", null, "Politics"],
  40. [5, "john.doe@example.com", null, "Politics"],
  41. [6, "john.doe@example.com", 2, "Politics"],
  42. [7, "john.doe@example.net", null, "Technology"],
  43. [8, "john.doe@example.net", 7, "Software"],
  44. [9, "john.doe@example.net", null, "Politics"],
  45. ]
  46. ],
  47. 'arsse_feeds' => [
  48. 'columns' => [
  49. 'id' => "int",
  50. 'url' => "str",
  51. 'title' => "str",
  52. ],
  53. 'rows' => [
  54. [1,"http://example.com/1", "Feed 1"],
  55. [2,"http://example.com/2", "Feed 2"],
  56. [3,"http://example.com/3", "Feed 3"],
  57. [4,"http://example.com/4", "Feed 4"],
  58. [5,"http://example.com/5", "Feed 5"],
  59. [6,"http://example.com/6", "Feed 6"],
  60. [7,"http://example.com/7", "Feed 7"],
  61. [8,"http://example.com/8", "Feed 8"],
  62. [9,"http://example.com/9", "Feed 9"],
  63. [10,"http://example.com/10", "Feed 10"],
  64. [11,"http://example.com/11", "Feed 11"],
  65. [12,"http://example.com/12", "Feed 12"],
  66. [13,"http://example.com/13", "Feed 13"],
  67. ]
  68. ],
  69. 'arsse_subscriptions' => [
  70. 'columns' => [
  71. 'id' => "int",
  72. 'owner' => "str",
  73. 'feed' => "int",
  74. 'folder' => "int",
  75. 'title' => "str",
  76. ],
  77. 'rows' => [
  78. [1, "john.doe@example.com",1, null,"Subscription 1"],
  79. [2, "john.doe@example.com",2, null,null],
  80. [3, "john.doe@example.com",3, 1,"Subscription 3"],
  81. [4, "john.doe@example.com",4, 6,null],
  82. [5, "john.doe@example.com",10, 5,"Subscription 5"],
  83. [6, "jane.doe@example.com",1, null,null],
  84. [7, "jane.doe@example.com",10,null,"Subscription 7"],
  85. [8, "john.doe@example.org",11,null,null],
  86. [9, "john.doe@example.org",12,null,"Subscription 9"],
  87. [10,"john.doe@example.org",13,null,null],
  88. [11,"john.doe@example.net",10,null,"Subscription 11"],
  89. [12,"john.doe@example.net",2, 9,null],
  90. [13,"john.doe@example.net",3, 8,"Subscription 13"],
  91. [14,"john.doe@example.net",4, 7,null],
  92. ]
  93. ],
  94. 'arsse_articles' => [
  95. 'columns' => [
  96. 'id' => "int",
  97. 'feed' => "int",
  98. 'url' => "str",
  99. 'title' => "str",
  100. 'author' => "str",
  101. 'published' => "datetime",
  102. 'edited' => "datetime",
  103. 'content' => "str",
  104. 'guid' => "str",
  105. 'url_title_hash' => "str",
  106. 'url_content_hash' => "str",
  107. 'title_content_hash' => "str",
  108. 'modified' => "datetime",
  109. ],
  110. 'rows' => [
  111. [1,1,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  112. [2,1,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  113. [3,2,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  114. [4,2,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  115. [5,3,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  116. [6,3,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  117. [7,4,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  118. [8,4,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  119. [9,5,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  120. [10,5,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  121. [11,6,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  122. [12,6,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  123. [13,7,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  124. [14,7,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  125. [15,8,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  126. [16,8,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  127. [17,9,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  128. [18,9,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  129. [19,10,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
  130. [20,10,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
  131. [101,11,'http://example.com/1','Article title 1','','2000-01-01 00:00:00','2000-01-01 00:00:01','<p>Article content 1</p>','e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda','f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6','fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4','18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207','2000-01-01 01:00:00'],
  132. [102,11,'http://example.com/2','Article title 2','','2000-01-02 00:00:00','2000-01-02 00:00:02','<p>Article content 2</p>','5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7','0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153','13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9','2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e','2000-01-02 02:00:00'],
  133. [103,12,'http://example.com/3','Article title 3','','2000-01-03 00:00:00','2000-01-03 00:00:03','<p>Article content 3</p>','31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92','f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b','b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406','ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b','2000-01-03 03:00:00'],
  134. [104,12,'http://example.com/4','Article title 4','','2000-01-04 00:00:00','2000-01-04 00:00:04','<p>Article content 4</p>','804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180','f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8','f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3','ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9','2000-01-04 04:00:00'],
  135. [105,13,'http://example.com/5','Article title 5','','2000-01-05 00:00:00','2000-01-05 00:00:05','<p>Article content 5</p>','db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41','d40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022','834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900','43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba','2000-01-05 05:00:00'],
  136. ]
  137. ],
  138. 'arsse_enclosures' => [
  139. 'columns' => [
  140. 'article' => "int",
  141. 'url' => "str",
  142. 'type' => "str",
  143. ],
  144. 'rows' => [
  145. [102,"http://example.com/text","text/plain"],
  146. [103,"http://example.com/video","video/webm"],
  147. [104,"http://example.com/image","image/svg+xml"],
  148. [105,"http://example.com/audio","audio/ogg"],
  149. ]
  150. ],
  151. 'arsse_editions' => [
  152. 'columns' => [
  153. 'id' => "int",
  154. 'article' => "int",
  155. ],
  156. 'rows' => [
  157. [1,1],
  158. [2,2],
  159. [3,3],
  160. [4,4],
  161. [5,5],
  162. [6,6],
  163. [7,7],
  164. [8,8],
  165. [9,9],
  166. [10,10],
  167. [11,11],
  168. [12,12],
  169. [13,13],
  170. [14,14],
  171. [15,15],
  172. [16,16],
  173. [17,17],
  174. [18,18],
  175. [19,19],
  176. [20,20],
  177. [101,101],
  178. [102,102],
  179. [103,103],
  180. [104,104],
  181. [105,105],
  182. [202,102],
  183. [203,103],
  184. [204,104],
  185. [205,105],
  186. [305,105],
  187. [1001,20],
  188. ]
  189. ],
  190. 'arsse_marks' => [
  191. 'columns' => [
  192. 'subscription' => "int",
  193. 'article' => "int",
  194. 'read' => "bool",
  195. 'starred' => "bool",
  196. 'modified' => "datetime",
  197. 'note' => "str",
  198. ],
  199. 'rows' => [
  200. [1, 1,1,1,'2000-01-01 00:00:00',''],
  201. [5, 19,1,0,'2016-01-01 00:00:00',''],
  202. [5, 20,0,1,'2005-01-01 00:00:00',''],
  203. [7, 20,1,0,'2010-01-01 00:00:00',''],
  204. [8, 102,1,0,'2000-01-02 02:00:00','Note 2'],
  205. [9, 103,0,1,'2000-01-03 03:00:00','Note 3'],
  206. [9, 104,1,1,'2000-01-04 04:00:00','Note 4'],
  207. [10,105,0,0,'2000-01-05 05:00:00',''],
  208. [11, 19,0,0,'2017-01-01 00:00:00','ook'],
  209. [11, 20,1,0,'2017-01-01 00:00:00','eek'],
  210. [12, 3,0,1,'2017-01-01 00:00:00','ack'],
  211. [12, 4,1,1,'2017-01-01 00:00:00','ach'],
  212. [1, 2,0,0,'2010-01-01 00:00:00','Some Note'],
  213. ]
  214. ],
  215. 'arsse_categories' => [ // author-supplied categories
  216. 'columns' => [
  217. 'article' => "int",
  218. 'name' => "str",
  219. ],
  220. 'rows' => [
  221. [19,"Fascinating"],
  222. [19,"Logical"],
  223. [20,"Interesting"],
  224. [20,"Logical"],
  225. ],
  226. ],
  227. 'arsse_labels' => [
  228. 'columns' => [
  229. 'id' => "int",
  230. 'owner' => "str",
  231. 'name' => "str",
  232. ],
  233. 'rows' => [
  234. [1,"john.doe@example.com","Interesting"],
  235. [2,"john.doe@example.com","Fascinating"],
  236. [3,"jane.doe@example.com","Boring"],
  237. [4,"john.doe@example.com","Lonely"],
  238. ],
  239. ],
  240. 'arsse_label_members' => [
  241. 'columns' => [
  242. 'label' => "int",
  243. 'article' => "int",
  244. 'subscription' => "int",
  245. 'assigned' => "bool",
  246. 'modified' => "datetime",
  247. ],
  248. 'rows' => [
  249. [1, 1,1,1,'2000-01-01 00:00:00'],
  250. [2, 1,1,1,'2000-01-01 00:00:00'],
  251. [1,19,5,1,'2000-01-01 00:00:00'],
  252. [2,20,5,1,'2000-01-01 00:00:00'],
  253. [1, 5,3,0,'2000-01-01 00:00:00'],
  254. [2, 5,3,1,'2000-01-01 00:00:00'],
  255. [4, 7,4,0,'2000-01-01 00:00:00'],
  256. [4, 8,4,1,'2015-01-01 00:00:00'],
  257. ],
  258. ],
  259. ];
  260. $this->matches = [
  261. [
  262. 'id' => 101,
  263. 'url' => 'http://example.com/1',
  264. 'title' => 'Article title 1',
  265. 'subscription_title' => "Feed 11",
  266. 'author' => '',
  267. 'content' => '<p>Article content 1</p>',
  268. 'guid' => 'e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda',
  269. 'published_date' => '2000-01-01 00:00:00',
  270. 'edited_date' => '2000-01-01 00:00:01',
  271. 'modified_date' => '2000-01-01 01:00:00',
  272. 'unread' => 1,
  273. 'starred' => 0,
  274. 'edition' => 101,
  275. 'subscription' => 8,
  276. 'fingerprint' => 'f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6:fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4:18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207',
  277. 'media_url' => null,
  278. 'media_type' => null,
  279. 'note' => "",
  280. ],
  281. [
  282. 'id' => 102,
  283. 'url' => 'http://example.com/2',
  284. 'title' => 'Article title 2',
  285. 'subscription_title' => "Feed 11",
  286. 'author' => '',
  287. 'content' => '<p>Article content 2</p>',
  288. 'guid' => '5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7',
  289. 'published_date' => '2000-01-02 00:00:00',
  290. 'edited_date' => '2000-01-02 00:00:02',
  291. 'modified_date' => '2000-01-02 02:00:00',
  292. 'unread' => 0,
  293. 'starred' => 0,
  294. 'edition' => 202,
  295. 'subscription' => 8,
  296. 'fingerprint' => '0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153:13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9:2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e',
  297. 'media_url' => "http://example.com/text",
  298. 'media_type' => "text/plain",
  299. 'note' => "Note 2",
  300. ],
  301. [
  302. 'id' => 103,
  303. 'url' => 'http://example.com/3',
  304. 'title' => 'Article title 3',
  305. 'subscription_title' => "Subscription 9",
  306. 'author' => '',
  307. 'content' => '<p>Article content 3</p>',
  308. 'guid' => '31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92',
  309. 'published_date' => '2000-01-03 00:00:00',
  310. 'edited_date' => '2000-01-03 00:00:03',
  311. 'modified_date' => '2000-01-03 03:00:00',
  312. 'unread' => 1,
  313. 'starred' => 1,
  314. 'edition' => 203,
  315. 'subscription' => 9,
  316. 'fingerprint' => 'f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b:b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406:ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b',
  317. 'media_url' => "http://example.com/video",
  318. 'media_type' => "video/webm",
  319. 'note' => "Note 3",
  320. ],
  321. [
  322. 'id' => 104,
  323. 'url' => 'http://example.com/4',
  324. 'title' => 'Article title 4',
  325. 'subscription_title' => "Subscription 9",
  326. 'author' => '',
  327. 'content' => '<p>Article content 4</p>',
  328. 'guid' => '804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180',
  329. 'published_date' => '2000-01-04 00:00:00',
  330. 'edited_date' => '2000-01-04 00:00:04',
  331. 'modified_date' => '2000-01-04 04:00:00',
  332. 'unread' => 0,
  333. 'starred' => 1,
  334. 'edition' => 204,
  335. 'subscription' => 9,
  336. 'fingerprint' => 'f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8:f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3:ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9',
  337. 'media_url' => "http://example.com/image",
  338. 'media_type' => "image/svg+xml",
  339. 'note' => "Note 4",
  340. ],
  341. [
  342. 'id' => 105,
  343. 'url' => 'http://example.com/5',
  344. 'title' => 'Article title 5',
  345. 'subscription_title' => "Feed 13",
  346. 'author' => '',
  347. 'content' => '<p>Article content 5</p>',
  348. 'guid' => 'db3e736c2c492f5def5c5da33ddcbea1824040e9ced2142069276b0a6e291a41',
  349. 'published_date' => '2000-01-05 00:00:00',
  350. 'edited_date' => '2000-01-05 00:00:05',
  351. 'modified_date' => '2000-01-05 05:00:00',
  352. 'unread' => 1,
  353. 'starred' => 0,
  354. 'edition' => 305,
  355. 'subscription' => 10,
  356. 'fingerprint' => 'd40da96e39eea6c55948ccbe9b3d275b5f931298288dbe953990c5f496097022:834240f84501b5341d375414718204ec421561f3825d34c22bf9182203e42900:43b970ac6ec5f8a9647b2c7e4eed8b1d7f62e154a95eed748b0294c1256764ba',
  357. 'media_url' => "http://example.com/audio",
  358. 'media_type' => "audio/ogg",
  359. 'note' => "",
  360. ],
  361. ];
  362. $this->fields = [
  363. "id", "subscription", "feed", "modified_date", "marked_date", "unread", "starred", "edition", "edited_date",
  364. "url", "title", "subscription_title", "author", "guid", "published_date", "fingerprint",
  365. "content", "media_url", "media_type",
  366. "note",
  367. ];
  368. $this->checkTables = ['arsse_marks' => ["subscription","article","read","starred","modified","note"],];
  369. $this->user = "john.doe@example.net";
  370. }
  371. protected function tearDownSeriesArticle() {
  372. unset($this->data, $this->matches, $this->fields, $this->checkTables, $this->user);
  373. }
  374. public function testRetrieveArticleIdsForEditions() {
  375. $exp = [
  376. 1 => 1,
  377. 2 => 2,
  378. 3 => 3,
  379. 4 => 4,
  380. 5 => 5,
  381. 6 => 6,
  382. 7 => 7,
  383. 8 => 8,
  384. 9 => 9,
  385. 10 => 10,
  386. 11 => 11,
  387. 12 => 12,
  388. 13 => 13,
  389. 14 => 14,
  390. 15 => 15,
  391. 16 => 16,
  392. 17 => 17,
  393. 18 => 18,
  394. 19 => 19,
  395. 20 => 20,
  396. 101 => 101,
  397. 102 => 102,
  398. 103 => 103,
  399. 104 => 104,
  400. 105 => 105,
  401. 202 => 102,
  402. 203 => 103,
  403. 204 => 104,
  404. 205 => 105,
  405. 305 => 105,
  406. 1001 => 20,
  407. ];
  408. $this->assertEquals($exp, Arsse::$db->editionArticle(...range(1, 1001)));
  409. }
  410. public function testListArticlesCheckingContext() {
  411. $compareIds = function(array $exp, Context $c) {
  412. $ids = array_column($ids = Arsse::$db->articleList("john.doe@example.com", $c)->getAll(), "id");
  413. sort($ids);
  414. sort($exp);
  415. $this->assertEquals($exp, $ids);
  416. };
  417. // get all items for user
  418. $exp = [1,2,3,4,5,6,7,8,19,20];
  419. $compareIds($exp, new Context);
  420. $compareIds($exp, (new Context)->articles(range(1, Database::LIMIT_ARTICLES * 3)));
  421. // get items from a folder tree
  422. $compareIds([5,6,7,8], (new Context)->folder(1));
  423. // get items from a leaf folder
  424. $compareIds([7,8], (new Context)->folder(6));
  425. // get items from a non-leaf folder without descending
  426. $compareIds([1,2,3,4], (new Context)->folderShallow(0));
  427. $compareIds([5,6], (new Context)->folderShallow(1));
  428. // get items from a single subscription
  429. $exp = [19,20];
  430. $compareIds($exp, (new Context)->subscription(5));
  431. // get un/read items from a single subscription
  432. $compareIds([20], (new Context)->subscription(5)->unread(true));
  433. $compareIds([19], (new Context)->subscription(5)->unread(false));
  434. // get starred articles
  435. $compareIds([1,20], (new Context)->starred(true));
  436. $compareIds([2,3,4,5,6,7,8,19], (new Context)->starred(false));
  437. $compareIds([1], (new Context)->starred(true)->unread(false));
  438. $compareIds([], (new Context)->starred(true)->unread(false)->subscription(5));
  439. // get items relative to edition
  440. $compareIds([19], (new Context)->subscription(5)->latestEdition(999));
  441. $compareIds([19], (new Context)->subscription(5)->latestEdition(19));
  442. $compareIds([20], (new Context)->subscription(5)->oldestEdition(999));
  443. $compareIds([20], (new Context)->subscription(5)->oldestEdition(1001));
  444. // get items relative to article ID
  445. $compareIds([1,2,3], (new Context)->latestArticle(3));
  446. $compareIds([19,20], (new Context)->oldestArticle(19));
  447. // get items relative to (feed) modification date
  448. $exp = [2,4,6,8,20];
  449. $compareIds($exp, (new Context)->modifiedSince("2005-01-01T00:00:00Z"));
  450. $compareIds($exp, (new Context)->modifiedSince("2010-01-01T00:00:00Z"));
  451. $exp = [1,3,5,7,19];
  452. $compareIds($exp, (new Context)->notModifiedSince("2005-01-01T00:00:00Z"));
  453. $compareIds($exp, (new Context)->notModifiedSince("2000-01-01T00:00:00Z"));
  454. // get items relative to (user) modification date (both marks and labels apply)
  455. $compareIds([8,19], (new Context)->markedSince("2014-01-01T00:00:00Z"));
  456. $compareIds([2,4,6,8,19,20], (new Context)->markedSince("2010-01-01T00:00:00Z"));
  457. $compareIds([1,2,3,4,5,6,7,20], (new Context)->notMarkedSince("2014-01-01T00:00:00Z"));
  458. $compareIds([1,3,5,7], (new Context)->notMarkedSince("2005-01-01T00:00:00Z"));
  459. // paged results
  460. $compareIds([1], (new Context)->limit(1));
  461. $compareIds([2], (new Context)->limit(1)->oldestEdition(1+1));
  462. $compareIds([3], (new Context)->limit(1)->oldestEdition(2+1));
  463. $compareIds([4,5], (new Context)->limit(2)->oldestEdition(3+1));
  464. // reversed results
  465. $compareIds([20], (new Context)->reverse(true)->limit(1));
  466. $compareIds([19], (new Context)->reverse(true)->limit(1)->latestEdition(1001-1));
  467. $compareIds([8], (new Context)->reverse(true)->limit(1)->latestEdition(19-1));
  468. $compareIds([7,6], (new Context)->reverse(true)->limit(2)->latestEdition(8-1));
  469. // get articles by label ID
  470. $compareIds([1,19], (new Context)->label(1));
  471. $compareIds([1,5,20], (new Context)->label(2));
  472. // get articles by label name
  473. $compareIds([1,19], (new Context)->labelName("Interesting"));
  474. $compareIds([1,5,20], (new Context)->labelName("Fascinating"));
  475. // get articles with any or no label
  476. $compareIds([1,5,8,19,20], (new Context)->labelled(true));
  477. $compareIds([2,3,4,6,7], (new Context)->labelled(false));
  478. // get a specific article or edition
  479. $compareIds([20], (new Context)->article(20));
  480. $compareIds([20], (new Context)->edition(1001));
  481. // get multiple specific articles or editions
  482. $compareIds([1,20], (new Context)->articles([1,20,50]));
  483. $compareIds([1,20], (new Context)->editions([1,1001,50]));
  484. // get articles base on whether or not they have notes
  485. $compareIds([1,3,4,5,6,7,8,19,20], (new Context)->annotated(false));
  486. $compareIds([2], (new Context)->annotated(true));
  487. // get specific starred articles
  488. $compareIds([1], (new Context)->articles([1,2,3])->starred(true));
  489. $compareIds([2,3], (new Context)->articles([1,2,3])->starred(false));
  490. }
  491. public function testListArticlesOfAMissingFolder() {
  492. $this->assertException("idMissing", "Db", "ExceptionInput");
  493. Arsse::$db->articleList($this->user, (new Context)->folder(1));
  494. }
  495. public function testListArticlesOfAMissingSubscription() {
  496. $this->assertException("idMissing", "Db", "ExceptionInput");
  497. Arsse::$db->articleList($this->user, (new Context)->subscription(1));
  498. }
  499. public function testListArticlesCheckingProperties() {
  500. $this->user = "john.doe@example.org";
  501. // check that the different fieldset groups return the expected columns
  502. foreach ($this->fields as $column) {
  503. $test = array_keys(Arsse::$db->articleList($this->user, (new Context)->article(101), [$column])->getRow());
  504. $this->assertEquals([$column], $test);
  505. }
  506. // check that an unknown field is silently ignored
  507. $columns = array_merge($this->fields, ["unknown_column", "bogus_column"]);
  508. $test = array_keys(Arsse::$db->articleList($this->user, (new Context)->article(101), $columns)->getRow());
  509. $this->assertEquals($this->fields, $test);
  510. }
  511. public function testListArticlesWithoutAuthority() {
  512. Phake::when(Arsse::$user)->authorize->thenReturn(false);
  513. $this->assertException("notAuthorized", "User", "ExceptionAuthz");
  514. Arsse::$db->articleList($this->user);
  515. }
  516. public function testMarkNothing() {
  517. $this->assertSame(0, Arsse::$db->articleMark($this->user, []));
  518. }
  519. public function testMarkAllArticlesUnread() {
  520. Arsse::$db->articleMark($this->user, ['read'=>false]);
  521. $now = Date::transform(time(), "sql");
  522. $state = $this->primeExpectations($this->data, $this->checkTables);
  523. $state['arsse_marks']['rows'][9][2] = 0;
  524. $state['arsse_marks']['rows'][9][4] = $now;
  525. $state['arsse_marks']['rows'][11][2] = 0;
  526. $state['arsse_marks']['rows'][11][4] = $now;
  527. $this->compareExpectations($state);
  528. }
  529. public function testMarkAllArticlesRead() {
  530. Arsse::$db->articleMark($this->user, ['read'=>true]);
  531. $now = Date::transform(time(), "sql");
  532. $state = $this->primeExpectations($this->data, $this->checkTables);
  533. $state['arsse_marks']['rows'][8][2] = 1;
  534. $state['arsse_marks']['rows'][8][4] = $now;
  535. $state['arsse_marks']['rows'][10][2] = 1;
  536. $state['arsse_marks']['rows'][10][4] = $now;
  537. $state['arsse_marks']['rows'][] = [13,5,1,0,$now,''];
  538. $state['arsse_marks']['rows'][] = [13,6,1,0,$now,''];
  539. $state['arsse_marks']['rows'][] = [14,7,1,0,$now,''];
  540. $state['arsse_marks']['rows'][] = [14,8,1,0,$now,''];
  541. $this->compareExpectations($state);
  542. }
  543. public function testMarkAllArticlesUnstarred() {
  544. Arsse::$db->articleMark($this->user, ['starred'=>false]);
  545. $now = Date::transform(time(), "sql");
  546. $state = $this->primeExpectations($this->data, $this->checkTables);
  547. $state['arsse_marks']['rows'][10][3] = 0;
  548. $state['arsse_marks']['rows'][10][4] = $now;
  549. $state['arsse_marks']['rows'][11][3] = 0;
  550. $state['arsse_marks']['rows'][11][4] = $now;
  551. $this->compareExpectations($state);
  552. }
  553. public function testMarkAllArticlesStarred() {
  554. Arsse::$db->articleMark($this->user, ['starred'=>true]);
  555. $now = Date::transform(time(), "sql");
  556. $state = $this->primeExpectations($this->data, $this->checkTables);
  557. $state['arsse_marks']['rows'][8][3] = 1;
  558. $state['arsse_marks']['rows'][8][4] = $now;
  559. $state['arsse_marks']['rows'][9][3] = 1;
  560. $state['arsse_marks']['rows'][9][4] = $now;
  561. $state['arsse_marks']['rows'][] = [13,5,0,1,$now,''];
  562. $state['arsse_marks']['rows'][] = [13,6,0,1,$now,''];
  563. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  564. $state['arsse_marks']['rows'][] = [14,8,0,1,$now,''];
  565. $this->compareExpectations($state);
  566. }
  567. public function testMarkAllArticlesUnreadAndUnstarred() {
  568. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>false]);
  569. $now = Date::transform(time(), "sql");
  570. $state = $this->primeExpectations($this->data, $this->checkTables);
  571. $state['arsse_marks']['rows'][9][2] = 0;
  572. $state['arsse_marks']['rows'][9][4] = $now;
  573. $state['arsse_marks']['rows'][10][3] = 0;
  574. $state['arsse_marks']['rows'][10][4] = $now;
  575. $state['arsse_marks']['rows'][11][2] = 0;
  576. $state['arsse_marks']['rows'][11][3] = 0;
  577. $state['arsse_marks']['rows'][11][4] = $now;
  578. $this->compareExpectations($state);
  579. }
  580. public function testMarkAllArticlesReadAndStarred() {
  581. Arsse::$db->articleMark($this->user, ['read'=>true,'starred'=>true]);
  582. $now = Date::transform(time(), "sql");
  583. $state = $this->primeExpectations($this->data, $this->checkTables);
  584. $state['arsse_marks']['rows'][8][2] = 1;
  585. $state['arsse_marks']['rows'][8][3] = 1;
  586. $state['arsse_marks']['rows'][8][4] = $now;
  587. $state['arsse_marks']['rows'][9][3] = 1;
  588. $state['arsse_marks']['rows'][9][4] = $now;
  589. $state['arsse_marks']['rows'][10][2] = 1;
  590. $state['arsse_marks']['rows'][10][4] = $now;
  591. $state['arsse_marks']['rows'][] = [13,5,1,1,$now,''];
  592. $state['arsse_marks']['rows'][] = [13,6,1,1,$now,''];
  593. $state['arsse_marks']['rows'][] = [14,7,1,1,$now,''];
  594. $state['arsse_marks']['rows'][] = [14,8,1,1,$now,''];
  595. $this->compareExpectations($state);
  596. }
  597. public function testMarkAllArticlesUnreadAndStarred() {
  598. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true]);
  599. $now = Date::transform(time(), "sql");
  600. $state = $this->primeExpectations($this->data, $this->checkTables);
  601. $state['arsse_marks']['rows'][8][3] = 1;
  602. $state['arsse_marks']['rows'][8][4] = $now;
  603. $state['arsse_marks']['rows'][9][2] = 0;
  604. $state['arsse_marks']['rows'][9][3] = 1;
  605. $state['arsse_marks']['rows'][9][4] = $now;
  606. $state['arsse_marks']['rows'][11][2] = 0;
  607. $state['arsse_marks']['rows'][11][4] = $now;
  608. $state['arsse_marks']['rows'][] = [13,5,0,1,$now,''];
  609. $state['arsse_marks']['rows'][] = [13,6,0,1,$now,''];
  610. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  611. $state['arsse_marks']['rows'][] = [14,8,0,1,$now,''];
  612. $this->compareExpectations($state);
  613. }
  614. public function testMarkAllArticlesReadAndUnstarred() {
  615. Arsse::$db->articleMark($this->user, ['read'=>true,'starred'=>false]);
  616. $now = Date::transform(time(), "sql");
  617. $state = $this->primeExpectations($this->data, $this->checkTables);
  618. $state['arsse_marks']['rows'][8][2] = 1;
  619. $state['arsse_marks']['rows'][8][4] = $now;
  620. $state['arsse_marks']['rows'][10][2] = 1;
  621. $state['arsse_marks']['rows'][10][3] = 0;
  622. $state['arsse_marks']['rows'][10][4] = $now;
  623. $state['arsse_marks']['rows'][11][3] = 0;
  624. $state['arsse_marks']['rows'][11][4] = $now;
  625. $state['arsse_marks']['rows'][] = [13,5,1,0,$now,''];
  626. $state['arsse_marks']['rows'][] = [13,6,1,0,$now,''];
  627. $state['arsse_marks']['rows'][] = [14,7,1,0,$now,''];
  628. $state['arsse_marks']['rows'][] = [14,8,1,0,$now,''];
  629. $this->compareExpectations($state);
  630. }
  631. public function testSetNoteForAllArticles() {
  632. Arsse::$db->articleMark($this->user, ['note'=>"New note"]);
  633. $now = Date::transform(time(), "sql");
  634. $state = $this->primeExpectations($this->data, $this->checkTables);
  635. $state['arsse_marks']['rows'][8][5] = "New note";
  636. $state['arsse_marks']['rows'][8][4] = $now;
  637. $state['arsse_marks']['rows'][9][5] = "New note";
  638. $state['arsse_marks']['rows'][9][4] = $now;
  639. $state['arsse_marks']['rows'][10][5] = "New note";
  640. $state['arsse_marks']['rows'][10][4] = $now;
  641. $state['arsse_marks']['rows'][11][5] = "New note";
  642. $state['arsse_marks']['rows'][11][4] = $now;
  643. $state['arsse_marks']['rows'][] = [13,5,0,0,$now,'New note'];
  644. $state['arsse_marks']['rows'][] = [13,6,0,0,$now,'New note'];
  645. $state['arsse_marks']['rows'][] = [14,7,0,0,$now,'New note'];
  646. $state['arsse_marks']['rows'][] = [14,8,0,0,$now,'New note'];
  647. $this->compareExpectations($state);
  648. }
  649. public function testMarkATreeFolder() {
  650. Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(7));
  651. $now = Date::transform(time(), "sql");
  652. $state = $this->primeExpectations($this->data, $this->checkTables);
  653. $state['arsse_marks']['rows'][] = [13,5,1,0,$now,''];
  654. $state['arsse_marks']['rows'][] = [13,6,1,0,$now,''];
  655. $state['arsse_marks']['rows'][] = [14,7,1,0,$now,''];
  656. $state['arsse_marks']['rows'][] = [14,8,1,0,$now,''];
  657. $this->compareExpectations($state);
  658. }
  659. public function testMarkALeafFolder() {
  660. Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(8));
  661. $now = Date::transform(time(), "sql");
  662. $state = $this->primeExpectations($this->data, $this->checkTables);
  663. $state['arsse_marks']['rows'][] = [13,5,1,0,$now,''];
  664. $state['arsse_marks']['rows'][] = [13,6,1,0,$now,''];
  665. $this->compareExpectations($state);
  666. }
  667. public function testMarkAMissingFolder() {
  668. $this->assertException("idMissing", "Db", "ExceptionInput");
  669. Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(42));
  670. }
  671. public function testMarkASubscription() {
  672. Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->subscription(13));
  673. $now = Date::transform(time(), "sql");
  674. $state = $this->primeExpectations($this->data, $this->checkTables);
  675. $state['arsse_marks']['rows'][] = [13,5,1,0,$now,''];
  676. $state['arsse_marks']['rows'][] = [13,6,1,0,$now,''];
  677. $this->compareExpectations($state);
  678. }
  679. public function testMarkAMissingSubscription() {
  680. $this->assertException("idMissing", "Db", "ExceptionInput");
  681. Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(2112));
  682. }
  683. public function testMarkAnArticle() {
  684. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->article(20));
  685. $now = Date::transform(time(), "sql");
  686. $state = $this->primeExpectations($this->data, $this->checkTables);
  687. $state['arsse_marks']['rows'][9][3] = 1;
  688. $state['arsse_marks']['rows'][9][4] = $now;
  689. $this->compareExpectations($state);
  690. }
  691. public function testMarkMultipleArticles() {
  692. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->articles([2,4,7,20]));
  693. $now = Date::transform(time(), "sql");
  694. $state = $this->primeExpectations($this->data, $this->checkTables);
  695. $state['arsse_marks']['rows'][9][3] = 1;
  696. $state['arsse_marks']['rows'][9][4] = $now;
  697. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  698. $this->compareExpectations($state);
  699. }
  700. public function testMarkMultipleArticlessUnreadAndStarred() {
  701. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true], (new Context)->articles([2,4,7,20]));
  702. $now = Date::transform(time(), "sql");
  703. $state = $this->primeExpectations($this->data, $this->checkTables);
  704. $state['arsse_marks']['rows'][9][2] = 0;
  705. $state['arsse_marks']['rows'][9][3] = 1;
  706. $state['arsse_marks']['rows'][9][4] = $now;
  707. $state['arsse_marks']['rows'][11][2] = 0;
  708. $state['arsse_marks']['rows'][11][4] = $now;
  709. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  710. $this->compareExpectations($state);
  711. }
  712. public function testMarkTooFewMultipleArticles() {
  713. $this->assertException("tooShort", "Db", "ExceptionInput");
  714. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true], (new Context)->articles([]));
  715. }
  716. public function testMarkTooManyMultipleArticles() {
  717. $this->assertSame(7, Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true], (new Context)->articles(range(1, Database::LIMIT_ARTICLES * 3))));
  718. }
  719. public function testMarkAMissingArticle() {
  720. $this->assertException("subjectMissing", "Db", "ExceptionInput");
  721. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->article(1));
  722. }
  723. public function testMarkAnEdition() {
  724. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->edition(1001));
  725. $now = Date::transform(time(), "sql");
  726. $state = $this->primeExpectations($this->data, $this->checkTables);
  727. $state['arsse_marks']['rows'][9][3] = 1;
  728. $state['arsse_marks']['rows'][9][4] = $now;
  729. $this->compareExpectations($state);
  730. }
  731. public function testMarkMultipleEditions() {
  732. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->editions([2,4,7,20]));
  733. $now = Date::transform(time(), "sql");
  734. $state = $this->primeExpectations($this->data, $this->checkTables);
  735. $state['arsse_marks']['rows'][9][3] = 1;
  736. $state['arsse_marks']['rows'][9][4] = $now;
  737. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  738. $this->compareExpectations($state);
  739. }
  740. public function testMarkMultipleMissingEditions() {
  741. $this->assertSame(0, Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->editions([500,501])));
  742. $state = $this->primeExpectations($this->data, $this->checkTables);
  743. $this->compareExpectations($state);
  744. }
  745. public function testMarkMultipleEditionsUnread() {
  746. Arsse::$db->articleMark($this->user, ['read'=>false], (new Context)->editions([2,4,7,1001]));
  747. $now = Date::transform(time(), "sql");
  748. $state = $this->primeExpectations($this->data, $this->checkTables);
  749. $state['arsse_marks']['rows'][9][2] = 0;
  750. $state['arsse_marks']['rows'][9][4] = $now;
  751. $state['arsse_marks']['rows'][11][2] = 0;
  752. $state['arsse_marks']['rows'][11][4] = $now;
  753. $this->compareExpectations($state);
  754. }
  755. public function testMarkMultipleEditionsUnreadWithStale() {
  756. Arsse::$db->articleMark($this->user, ['read'=>false], (new Context)->editions([2,4,7,20]));
  757. $now = Date::transform(time(), "sql");
  758. $state = $this->primeExpectations($this->data, $this->checkTables);
  759. $state['arsse_marks']['rows'][11][2] = 0;
  760. $state['arsse_marks']['rows'][11][4] = $now;
  761. $this->compareExpectations($state);
  762. }
  763. public function testMarkMultipleEditionsUnreadAndStarredWithStale() {
  764. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true], (new Context)->editions([2,4,7,20]));
  765. $now = Date::transform(time(), "sql");
  766. $state = $this->primeExpectations($this->data, $this->checkTables);
  767. $state['arsse_marks']['rows'][9][3] = 1;
  768. $state['arsse_marks']['rows'][9][4] = $now;
  769. $state['arsse_marks']['rows'][11][2] = 0;
  770. $state['arsse_marks']['rows'][11][4] = $now;
  771. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  772. $this->compareExpectations($state);
  773. }
  774. public function testMarkTooFewMultipleEditions() {
  775. $this->assertException("tooShort", "Db", "ExceptionInput");
  776. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true], (new Context)->editions([]));
  777. }
  778. public function testMarkTooManyMultipleEditions() {
  779. $this->assertSame(7, Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true], (new Context)->editions(range(1, 51))));
  780. }
  781. public function testMarkAStaleEditionUnread() {
  782. Arsse::$db->articleMark($this->user, ['read'=>false], (new Context)->edition(20)); // no changes occur
  783. $state = $this->primeExpectations($this->data, $this->checkTables);
  784. $this->compareExpectations($state);
  785. }
  786. public function testMarkAStaleEditionStarred() {
  787. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->edition(20));
  788. $now = Date::transform(time(), "sql");
  789. $state = $this->primeExpectations($this->data, $this->checkTables);
  790. $state['arsse_marks']['rows'][9][3] = 1;
  791. $state['arsse_marks']['rows'][9][4] = $now;
  792. $this->compareExpectations($state);
  793. }
  794. public function testMarkAStaleEditionUnreadAndStarred() {
  795. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>true], (new Context)->edition(20)); // only starred is changed
  796. $now = Date::transform(time(), "sql");
  797. $state = $this->primeExpectations($this->data, $this->checkTables);
  798. $state['arsse_marks']['rows'][9][3] = 1;
  799. $state['arsse_marks']['rows'][9][4] = $now;
  800. $this->compareExpectations($state);
  801. }
  802. public function testMarkAStaleEditionUnreadAndUnstarred() {
  803. Arsse::$db->articleMark($this->user, ['read'=>false,'starred'=>false], (new Context)->edition(20)); // no changes occur
  804. $state = $this->primeExpectations($this->data, $this->checkTables);
  805. $this->compareExpectations($state);
  806. }
  807. public function testMarkAMissingEdition() {
  808. $this->assertException("subjectMissing", "Db", "ExceptionInput");
  809. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->edition(2));
  810. }
  811. public function testMarkByOldestEdition() {
  812. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->oldestEdition(19));
  813. $now = Date::transform(time(), "sql");
  814. $state = $this->primeExpectations($this->data, $this->checkTables);
  815. $state['arsse_marks']['rows'][8][3] = 1;
  816. $state['arsse_marks']['rows'][8][4] = $now;
  817. $state['arsse_marks']['rows'][9][3] = 1;
  818. $state['arsse_marks']['rows'][9][4] = $now;
  819. $this->compareExpectations($state);
  820. }
  821. public function testMarkByLatestEdition() {
  822. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->latestEdition(20));
  823. $now = Date::transform(time(), "sql");
  824. $state = $this->primeExpectations($this->data, $this->checkTables);
  825. $state['arsse_marks']['rows'][8][3] = 1;
  826. $state['arsse_marks']['rows'][8][4] = $now;
  827. $state['arsse_marks']['rows'][] = [13,5,0,1,$now,''];
  828. $state['arsse_marks']['rows'][] = [13,6,0,1,$now,''];
  829. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  830. $state['arsse_marks']['rows'][] = [14,8,0,1,$now,''];
  831. $this->compareExpectations($state);
  832. }
  833. public function testMarkByLastMarked() {
  834. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->markedSince('2017-01-01T00:00:00Z'));
  835. $now = Date::transform(time(), "sql");
  836. $state = $this->primeExpectations($this->data, $this->checkTables);
  837. $state['arsse_marks']['rows'][8][3] = 1;
  838. $state['arsse_marks']['rows'][8][4] = $now;
  839. $state['arsse_marks']['rows'][9][3] = 1;
  840. $state['arsse_marks']['rows'][9][4] = $now;
  841. $this->compareExpectations($state);
  842. }
  843. public function testMarkByNotLastMarked() {
  844. Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->notMarkedSince('2000-01-01T00:00:00Z'));
  845. $now = Date::transform(time(), "sql");
  846. $state = $this->primeExpectations($this->data, $this->checkTables);
  847. $state['arsse_marks']['rows'][] = [13,5,0,1,$now,''];
  848. $state['arsse_marks']['rows'][] = [14,7,0,1,$now,''];
  849. $this->compareExpectations($state);
  850. }
  851. public function testMarkArticlesWithoutAuthority() {
  852. Phake::when(Arsse::$user)->authorize->thenReturn(false);
  853. $this->assertException("notAuthorized", "User", "ExceptionAuthz");
  854. Arsse::$db->articleMark($this->user, ['read'=>false]);
  855. }
  856. public function testCountArticles() {
  857. $this->assertSame(2, Arsse::$db->articleCount("john.doe@example.com", (new Context)->starred(true)));
  858. $this->assertSame(4, Arsse::$db->articleCount("john.doe@example.com", (new Context)->folder(1)));
  859. $this->assertSame(0, Arsse::$db->articleCount("jane.doe@example.com", (new Context)->starred(true)));
  860. $this->assertSame(10, Arsse::$db->articleCount("john.doe@example.com", (new Context)->articles(range(1, Database::LIMIT_ARTICLES *3))));
  861. }
  862. public function testCountArticlesWithoutAuthority() {
  863. Phake::when(Arsse::$user)->authorize->thenReturn(false);
  864. $this->assertException("notAuthorized", "User", "ExceptionAuthz");
  865. Arsse::$db->articleCount($this->user);
  866. }
  867. public function testFetchStarredCounts() {
  868. $exp1 = ['total' => 2, 'unread' => 1, 'read' => 1];
  869. $exp2 = ['total' => 0, 'unread' => 0, 'read' => 0];
  870. $this->assertEquals($exp1, Arsse::$db->articleStarred("john.doe@example.com"));
  871. $this->assertEquals($exp2, Arsse::$db->articleStarred("jane.doe@example.com"));
  872. }
  873. public function testFetchStarredCountsWithoutAuthority() {
  874. Phake::when(Arsse::$user)->authorize->thenReturn(false);
  875. $this->assertException("notAuthorized", "User", "ExceptionAuthz");
  876. Arsse::$db->articleStarred($this->user);
  877. }
  878. public function testFetchLatestEdition() {
  879. $this->assertSame(1001, Arsse::$db->editionLatest($this->user));
  880. $this->assertSame(4, Arsse::$db->editionLatest($this->user, (new Context)->subscription(12)));
  881. }
  882. public function testFetchLatestEditionOfMissingSubscription() {
  883. $this->assertException("idMissing", "Db", "ExceptionInput");
  884. Arsse::$db->editionLatest($this->user, (new Context)->subscription(1));
  885. }
  886. public function testFetchLatestEditionWithoutAuthority() {
  887. Phake::when(Arsse::$user)->authorize->thenReturn(false);
  888. $this->assertException("notAuthorized", "User", "ExceptionAuthz");
  889. Arsse::$db->editionLatest($this->user);
  890. }
  891. public function testListTheLabelsOfAnArticle() {
  892. $this->assertEquals([1,2], Arsse::$db->articleLabelsGet("john.doe@example.com", 1));
  893. $this->assertEquals([2], Arsse::$db->articleLabelsGet("john.doe@example.com", 5));
  894. $this->assertEquals([], Arsse::$db->articleLabelsGet("john.doe@example.com", 2));
  895. $this->assertEquals(["Fascinating","Interesting"], Arsse::$db->articleLabelsGet("john.doe@example.com", 1, true));
  896. $this->assertEquals(["Fascinating"], Arsse::$db->articleLabelsGet("john.doe@example.com", 5, true));
  897. $this->assertEquals([], Arsse::$db->articleLabelsGet("john.doe@example.com", 2, true));
  898. }
  899. public function testListTheLabelsOfAMissingArticle() {
  900. $this->assertException("subjectMissing", "Db", "ExceptionInput");
  901. Arsse::$db->articleLabelsGet($this->user, 101);
  902. }
  903. public function testListTheLabelsOfAnArticleWithoutAuthority() {
  904. Phake::when(Arsse::$user)->authorize->thenReturn(false);
  905. $this->assertException("notAuthorized", "User", "ExceptionAuthz");
  906. Arsse::$db->articleLabelsGet("john.doe@example.com", 1);
  907. }
  908. public function testListTheCategoriesOfAnArticle() {
  909. $exp = ["Fascinating", "Logical"];
  910. $this->assertSame($exp, Arsse::$db->articleCategoriesGet($this->user, 19));
  911. $exp = ["Interesting", "Logical"];
  912. $this->assertSame($exp, Arsse::$db->articleCategoriesGet($this->user, 20));
  913. $exp = [];
  914. $this->assertSame($exp, Arsse::$db->articleCategoriesGet($this->user, 4));
  915. }
  916. public function testListTheCategoriesOfAMissingArticle() {
  917. $this->assertException("subjectMissing", "Db", "ExceptionInput");
  918. Arsse::$db->articleCategoriesGet($this->user, 101);
  919. }
  920. public function testListTheCategoriesOfAnArticleWithoutAuthority() {
  921. Phake::when(Arsse::$user)->authorize->thenReturn(false);
  922. $this->assertException("notAuthorized", "User", "ExceptionAuthz");
  923. Arsse::$db->articleCategoriesGet($this->user, 19);
  924. }
  925. }