2 namespace Shaarli\Api\Controllers
;
4 use malkusch\lock\mutex\NoMutex
;
5 use Shaarli\Bookmark\Bookmark
;
6 use Shaarli\Bookmark\BookmarkFileService
;
7 use Shaarli\Bookmark\LinkDB
;
8 use Shaarli\Config\ConfigManager
;
10 use Shaarli\Plugin\PluginManager
;
12 use Slim\Http\Environment
;
13 use Slim\Http\Request
;
14 use Slim\Http\Response
;
19 * Test get Link list REST API service.
21 * @see http://shaarli.github.io/api-documentation/#links-links-collection-get
23 * @package Shaarli\Api\Controllers
25 class GetLinksTest
extends \Shaarli\TestCase
28 * @var string datastore to test write operations
30 protected static $testDatastore = 'sandbox/datastore.php';
33 * @var ConfigManager instance
38 * @var \ReferenceLinkDB instance.
40 protected $refDB = null;
43 * @var Container instance.
48 * @var Links controller instance.
50 protected $controller;
53 * Number of JSON field per link.
55 const NB_FIELDS_LINK
= 9;
58 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
60 protected function setUp(): void
62 $mutex = new NoMutex();
63 $this->conf
= new ConfigManager('tests/utils/config/configJson');
64 $this->conf
->set('resource.datastore', self
::$testDatastore);
65 $this->refDB
= new \
ReferenceLinkDB();
66 $this->refDB
->write(self
::$testDatastore);
67 $history = new History('sandbox/history.php');
69 $this->container
= new Container();
70 $this->container
['conf'] = $this->conf
;
71 $pluginManager = new PluginManager($this->conf
);
72 $this->container
['db'] = new BookmarkFileService(
79 $this->container
['history'] = null;
81 $this->controller
= new Links($this->container
);
85 * After every test, remove the test datastore.
87 protected function tearDown(): void
89 @unlink(self
::$testDatastore);
93 * Test basic getLinks service: returns all bookmarks.
95 public function testGetLinks()
97 // Used by index_url().
98 $_SERVER['SERVER_NAME'] = 'domain.tld';
99 $_SERVER['SERVER_PORT'] = 80;
100 $_SERVER['SCRIPT_NAME'] = '/';
102 $env = Environment
::mock([
103 'REQUEST_METHOD' => 'GET',
105 $request = Request
::createFromEnvironment($env);
107 $response = $this->controller
->getLinks($request, new Response());
108 $this->assertEquals(200, $response->getStatusCode());
109 $data = json_decode((string) $response->getBody(), true);
110 $this->assertEquals($this->refDB
->countLinks(), count($data));
113 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
115 foreach ($data as $link) {
116 $this->assertEquals(self
::NB_FIELDS_LINK
, count($link));
117 $this->assertEquals($order[$cpt++
], $link['id']);
120 // Check first element fields
122 $this->assertEquals('http://domain.tld/shaare/WDWyig', $first['url']);
123 $this->assertEquals('WDWyig', $first['shorturl']);
124 $this->assertEquals('Link title: @website', $first['title']);
126 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
127 $first['description']
129 $this->assertEquals('sTuff', $first['tags'][0]);
130 $this->assertEquals(false, $first['private']);
132 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20150310_114651')->format(\DateTime
::ATOM
),
135 $this->assertEmpty($first['updated']);
139 $this->assertEquals(7, count($link['tags']));
143 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20160803_093033')->format(\DateTime
::ATOM
),
149 * Test getLinks service with offset and limit parameter:
150 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
152 public function testGetLinksOffsetLimit()
154 $env = Environment
::mock([
155 'REQUEST_METHOD' => 'GET',
156 'QUERY_STRING' => 'offset=3&limit=1'
158 $request = Request
::createFromEnvironment($env);
159 $response = $this->controller
->getLinks($request, new Response());
160 $this->assertEquals(200, $response->getStatusCode());
161 $data = json_decode((string) $response->getBody(), true);
162 $this->assertEquals(1, count($data));
163 $this->assertEquals(8, $data[0]['id']);
164 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
168 * Test getLinks with limit=all (return all link).
170 public function testGetLinksLimitAll()
172 $env = Environment
::mock([
173 'REQUEST_METHOD' => 'GET',
174 'QUERY_STRING' => 'limit=all'
176 $request = Request
::createFromEnvironment($env);
177 $response = $this->controller
->getLinks($request, new Response());
178 $this->assertEquals(200, $response->getStatusCode());
179 $data = json_decode((string) $response->getBody(), true);
180 $this->assertEquals($this->refDB
->countLinks(), count($data));
182 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
184 foreach ($data as $link) {
185 $this->assertEquals(self
::NB_FIELDS_LINK
, count($link));
186 $this->assertEquals($order[$cpt++
], $link['id']);
191 * Test getLinks service with offset and limit parameter:
192 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
194 public function testGetLinksOffsetTooHigh()
196 $env = Environment
::mock([
197 'REQUEST_METHOD' => 'GET',
198 'QUERY_STRING' => 'offset=100'
200 $request = Request
::createFromEnvironment($env);
201 $response = $this->controller
->getLinks($request, new Response());
202 $this->assertEquals(200, $response->getStatusCode());
203 $data = json_decode((string) $response->getBody(), true);
204 $this->assertEmpty(count($data));
208 * Test getLinks with visibility parameter set to all
210 public function testGetLinksVisibilityAll()
212 $env = Environment
::mock(
214 'REQUEST_METHOD' => 'GET',
215 'QUERY_STRING' => 'visibility=all'
218 $request = Request
::createFromEnvironment($env);
219 $response = $this->controller
->getLinks($request, new Response());
220 $this->assertEquals(200, $response->getStatusCode());
221 $data = json_decode((string)$response->getBody(), true);
222 $this->assertEquals($this->refDB
->countLinks(), count($data));
223 $this->assertEquals(10, $data[0]['id']);
224 $this->assertEquals(41, $data[2]['id']);
225 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
229 * Test getLinks with visibility parameter set to private
231 public function testGetLinksVisibilityPrivate()
233 $env = Environment
::mock([
234 'REQUEST_METHOD' => 'GET',
235 'QUERY_STRING' => 'visibility=private'
237 $request = Request
::createFromEnvironment($env);
238 $response = $this->controller
->getLinks($request, new Response());
239 $this->assertEquals(200, $response->getStatusCode());
240 $data = json_decode((string) $response->getBody(), true);
241 $this->assertEquals($this->refDB
->countPrivateLinks(), count($data));
242 $this->assertEquals(6, $data[0]['id']);
243 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
247 * Test getLinks with visibility parameter set to public
249 public function testGetLinksVisibilityPublic()
251 $env = Environment
::mock(
253 'REQUEST_METHOD' => 'GET',
254 'QUERY_STRING' => 'visibility=public'
257 $request = Request
::createFromEnvironment($env);
258 $response = $this->controller
->getLinks($request, new Response());
259 $this->assertEquals(200, $response->getStatusCode());
260 $data = json_decode((string)$response->getBody(), true);
261 $this->assertEquals($this->refDB
->countPublicLinks(), count($data));
262 $this->assertEquals(10, $data[0]['id']);
263 $this->assertEquals(41, $data[2]['id']);
264 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
268 * Test getLinks service with offset and limit parameter:
269 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
271 public function testGetLinksSearchTerm()
273 // Only in description - 1 result
274 $env = Environment
::mock([
275 'REQUEST_METHOD' => 'GET',
276 'QUERY_STRING' => 'searchterm=Tropical'
278 $request = Request
::createFromEnvironment($env);
279 $response = $this->controller
->getLinks($request, new Response());
280 $this->assertEquals(200, $response->getStatusCode());
281 $data = json_decode((string) $response->getBody(), true);
282 $this->assertEquals(1, count($data));
283 $this->assertEquals(1, $data[0]['id']);
284 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
286 // Only in tags - 1 result
287 $env = Environment
::mock([
288 'REQUEST_METHOD' => 'GET',
289 'QUERY_STRING' => 'searchterm=tag3'
291 $request = Request
::createFromEnvironment($env);
292 $response = $this->controller
->getLinks($request, new Response());
293 $this->assertEquals(200, $response->getStatusCode());
294 $data = json_decode((string) $response->getBody(), true);
295 $this->assertEquals(1, count($data));
296 $this->assertEquals(0, $data[0]['id']);
297 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
299 // Multiple results (2)
300 $env = Environment
::mock([
301 'REQUEST_METHOD' => 'GET',
302 'QUERY_STRING' => 'searchterm=stallman'
304 $request = Request
::createFromEnvironment($env);
305 $response = $this->controller
->getLinks($request, new Response());
306 $this->assertEquals(200, $response->getStatusCode());
307 $data = json_decode((string) $response->getBody(), true);
308 $this->assertEquals(2, count($data));
309 $this->assertEquals(41, $data[0]['id']);
310 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
311 $this->assertEquals(8, $data[1]['id']);
312 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
314 // Multiword - 2 results
315 $env = Environment
::mock([
316 'REQUEST_METHOD' => 'GET',
317 'QUERY_STRING' => 'searchterm=stallman+software'
319 $request = Request
::createFromEnvironment($env);
320 $response = $this->controller
->getLinks($request, new Response());
321 $this->assertEquals(200, $response->getStatusCode());
322 $data = json_decode((string) $response->getBody(), true);
323 $this->assertEquals(2, count($data));
324 $this->assertEquals(41, $data[0]['id']);
325 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
326 $this->assertEquals(8, $data[1]['id']);
327 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
330 $env = Environment
::mock([
331 'REQUEST_METHOD' => 'GET',
332 'QUERY_STRING' => 'searchterm='. urlencode('@web')
334 $request = Request
::createFromEnvironment($env);
335 $response = $this->controller
->getLinks($request, new Response());
336 $this->assertEquals(200, $response->getStatusCode());
337 $data = json_decode((string) $response->getBody(), true);
338 $this->assertEquals(2, count($data));
339 $this->assertEquals(41, $data[0]['id']);
340 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
341 $this->assertEquals(8, $data[1]['id']);
342 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
345 public function testGetLinksSearchTermNoResult()
347 $env = Environment
::mock([
348 'REQUEST_METHOD' => 'GET',
349 'QUERY_STRING' => 'searchterm=nope'
351 $request = Request
::createFromEnvironment($env);
352 $response = $this->controller
->getLinks($request, new Response());
353 $this->assertEquals(200, $response->getStatusCode());
354 $data = json_decode((string) $response->getBody(), true);
355 $this->assertEquals(0, count($data));
358 public function testGetLinksSearchTags()
361 $env = Environment
::mock([
362 'REQUEST_METHOD' => 'GET',
363 'QUERY_STRING' => 'searchtags=dev',
365 $request = Request
::createFromEnvironment($env);
366 $response = $this->controller
->getLinks($request, new Response());
367 $this->assertEquals(200, $response->getStatusCode());
368 $data = json_decode((string) $response->getBody(), true);
369 $this->assertEquals(2, count($data));
370 $this->assertEquals(0, $data[0]['id']);
371 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
372 $this->assertEquals(4, $data[1]['id']);
373 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
375 // Multitag + exclude
376 $env = Environment
::mock([
377 'REQUEST_METHOD' => 'GET',
378 'QUERY_STRING' => 'searchtags=stuff+-gnu',
380 $request = Request
::createFromEnvironment($env);
381 $response = $this->controller
->getLinks($request, new Response());
382 $this->assertEquals(200, $response->getStatusCode());
383 $data = json_decode((string) $response->getBody(), true);
384 $this->assertEquals(1, count($data));
385 $this->assertEquals(41, $data[0]['id']);
386 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
388 // wildcard: placeholder at the start
389 $env = Environment
::mock([
390 'REQUEST_METHOD' => 'GET',
391 'QUERY_STRING' => 'searchtags=*Tuff',
393 $request = Request
::createFromEnvironment($env);
394 $response = $this->controller
->getLinks($request, new Response());
395 $this->assertEquals(200, $response->getStatusCode());
396 $data = json_decode((string) $response->getBody(), true);
397 $this->assertEquals(2, count($data));
398 $this->assertEquals(41, $data[0]['id']);
400 // wildcard: placeholder at the end
401 $env = Environment
::mock([
402 'REQUEST_METHOD' => 'GET',
403 'QUERY_STRING' => 'searchtags=c*',
405 $request = Request
::createFromEnvironment($env);
406 $response = $this->controller
->getLinks($request, new Response());
407 $this->assertEquals(200, $response->getStatusCode());
408 $data = json_decode((string) $response->getBody(), true);
409 $this->assertEquals(5, count($data));
410 $this->assertEquals(6, $data[0]['id']);
412 // wildcard: placeholder at the middle
413 $env = Environment
::mock([
414 'REQUEST_METHOD' => 'GET',
415 'QUERY_STRING' => 'searchtags=w*b',
417 $request = Request
::createFromEnvironment($env);
418 $response = $this->controller
->getLinks($request, new Response());
419 $this->assertEquals(200, $response->getStatusCode());
420 $data = json_decode((string) $response->getBody(), true);
421 $this->assertEquals(4, count($data));
422 $this->assertEquals(6, $data[0]['id']);
424 // wildcard: match all
425 $env = Environment
::mock([
426 'REQUEST_METHOD' => 'GET',
427 'QUERY_STRING' => 'searchtags=*',
429 $request = Request
::createFromEnvironment($env);
430 $response = $this->controller
->getLinks($request, new Response());
431 $this->assertEquals(200, $response->getStatusCode());
432 $data = json_decode((string) $response->getBody(), true);
433 $this->assertEquals(\ReferenceLinkDB
::$NB_LINKS_TOTAL, count($data));
434 $this->assertEquals(10, $data[0]['id']);
435 $this->assertEquals(41, $data[2]['id']);
437 // wildcard: optional ('*' does not need to expand)
438 $env = Environment
::mock([
439 'REQUEST_METHOD' => 'GET',
440 'QUERY_STRING' => 'searchtags=*stuff*',
442 $request = Request
::createFromEnvironment($env);
443 $response = $this->controller
->getLinks($request, new Response());
444 $this->assertEquals(200, $response->getStatusCode());
445 $data = json_decode((string) $response->getBody(), true);
446 $this->assertEquals(2, count($data));
447 $this->assertEquals(41, $data[0]['id']);
449 // wildcard: exclusions
450 $env = Environment
::mock([
451 'REQUEST_METHOD' => 'GET',
452 'QUERY_STRING' => 'searchtags=*a*+-*e*',
454 $request = Request
::createFromEnvironment($env);
455 $response = $this->controller
->getLinks($request, new Response());
456 $this->assertEquals(200, $response->getStatusCode());
457 $data = json_decode((string) $response->getBody(), true);
458 $this->assertEquals(1, count($data));
459 $this->assertEquals(41, $data[0]['id']); // finds '#hashtag' in descr.
461 // wildcard: exclude all
462 $env = Environment
::mock([
463 'REQUEST_METHOD' => 'GET',
464 'QUERY_STRING' => 'searchtags=-*',
466 $request = Request
::createFromEnvironment($env);
467 $response = $this->controller
->getLinks($request, new Response());
468 $this->assertEquals(200, $response->getStatusCode());
469 $data = json_decode((string) $response->getBody(), true);
470 $this->assertEquals(0, count($data));
474 * Test getLinks service with search tags+terms.
476 public function testGetLinksSearchTermsAndTags()
478 $env = Environment
::mock([
479 'REQUEST_METHOD' => 'GET',
480 'QUERY_STRING' => 'searchterm=poke&searchtags=dev',
482 $request = Request
::createFromEnvironment($env);
483 $response = $this->controller
->getLinks($request, new Response());
484 $this->assertEquals(200, $response->getStatusCode());
485 $data = json_decode((string) $response->getBody(), true);
486 $this->assertEquals(1, count($data));
487 $this->assertEquals(0, $data[0]['id']);
488 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));