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
;
11 use Slim\Http\Environment
;
12 use Slim\Http\Request
;
13 use Slim\Http\Response
;
18 * Test get Link list REST API service.
20 * @see http://shaarli.github.io/api-documentation/#links-links-collection-get
22 * @package Shaarli\Api\Controllers
24 class GetLinksTest
extends \Shaarli\TestCase
27 * @var string datastore to test write operations
29 protected static $testDatastore = 'sandbox/datastore.php';
32 * @var ConfigManager instance
37 * @var \ReferenceLinkDB instance.
39 protected $refDB = null;
42 * @var Container instance.
47 * @var Links controller instance.
49 protected $controller;
52 * Number of JSON field per link.
54 const NB_FIELDS_LINK
= 9;
57 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
59 protected function setUp(): void
61 $mutex = new NoMutex();
62 $this->conf
= new ConfigManager('tests/utils/config/configJson');
63 $this->conf
->set('resource.datastore', self
::$testDatastore);
64 $this->refDB
= new \
ReferenceLinkDB();
65 $this->refDB
->write(self
::$testDatastore);
66 $history = new History('sandbox/history.php');
68 $this->container
= new Container();
69 $this->container
['conf'] = $this->conf
;
70 $this->container
['db'] = new BookmarkFileService($this->conf
, $history, $mutex, true);
71 $this->container
['history'] = null;
73 $this->controller
= new Links($this->container
);
77 * After every test, remove the test datastore.
79 protected function tearDown(): void
81 @unlink(self
::$testDatastore);
85 * Test basic getLinks service: returns all bookmarks.
87 public function testGetLinks()
89 // Used by index_url().
90 $_SERVER['SERVER_NAME'] = 'domain.tld';
91 $_SERVER['SERVER_PORT'] = 80;
92 $_SERVER['SCRIPT_NAME'] = '/';
94 $env = Environment
::mock([
95 'REQUEST_METHOD' => 'GET',
97 $request = Request
::createFromEnvironment($env);
99 $response = $this->controller
->getLinks($request, new Response());
100 $this->assertEquals(200, $response->getStatusCode());
101 $data = json_decode((string) $response->getBody(), true);
102 $this->assertEquals($this->refDB
->countLinks(), count($data));
105 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
107 foreach ($data as $link) {
108 $this->assertEquals(self
::NB_FIELDS_LINK
, count($link));
109 $this->assertEquals($order[$cpt++
], $link['id']);
112 // Check first element fields
114 $this->assertEquals('http://domain.tld/shaare/WDWyig', $first['url']);
115 $this->assertEquals('WDWyig', $first['shorturl']);
116 $this->assertEquals('Link title: @website', $first['title']);
118 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
119 $first['description']
121 $this->assertEquals('sTuff', $first['tags'][0]);
122 $this->assertEquals(false, $first['private']);
124 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20150310_114651')->format(\DateTime
::ATOM
),
127 $this->assertEmpty($first['updated']);
131 $this->assertEquals(7, count($link['tags']));
135 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20160803_093033')->format(\DateTime
::ATOM
),
141 * Test getLinks service with offset and limit parameter:
142 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
144 public function testGetLinksOffsetLimit()
146 $env = Environment
::mock([
147 'REQUEST_METHOD' => 'GET',
148 'QUERY_STRING' => 'offset=3&limit=1'
150 $request = Request
::createFromEnvironment($env);
151 $response = $this->controller
->getLinks($request, new Response());
152 $this->assertEquals(200, $response->getStatusCode());
153 $data = json_decode((string) $response->getBody(), true);
154 $this->assertEquals(1, count($data));
155 $this->assertEquals(8, $data[0]['id']);
156 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
160 * Test getLinks with limit=all (return all link).
162 public function testGetLinksLimitAll()
164 $env = Environment
::mock([
165 'REQUEST_METHOD' => 'GET',
166 'QUERY_STRING' => 'limit=all'
168 $request = Request
::createFromEnvironment($env);
169 $response = $this->controller
->getLinks($request, new Response());
170 $this->assertEquals(200, $response->getStatusCode());
171 $data = json_decode((string) $response->getBody(), true);
172 $this->assertEquals($this->refDB
->countLinks(), count($data));
174 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
176 foreach ($data as $link) {
177 $this->assertEquals(self
::NB_FIELDS_LINK
, count($link));
178 $this->assertEquals($order[$cpt++
], $link['id']);
183 * Test getLinks service with offset and limit parameter:
184 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
186 public function testGetLinksOffsetTooHigh()
188 $env = Environment
::mock([
189 'REQUEST_METHOD' => 'GET',
190 'QUERY_STRING' => 'offset=100'
192 $request = Request
::createFromEnvironment($env);
193 $response = $this->controller
->getLinks($request, new Response());
194 $this->assertEquals(200, $response->getStatusCode());
195 $data = json_decode((string) $response->getBody(), true);
196 $this->assertEmpty(count($data));
200 * Test getLinks with visibility parameter set to all
202 public function testGetLinksVisibilityAll()
204 $env = Environment
::mock(
206 'REQUEST_METHOD' => 'GET',
207 'QUERY_STRING' => 'visibility=all'
210 $request = Request
::createFromEnvironment($env);
211 $response = $this->controller
->getLinks($request, new Response());
212 $this->assertEquals(200, $response->getStatusCode());
213 $data = json_decode((string)$response->getBody(), true);
214 $this->assertEquals($this->refDB
->countLinks(), count($data));
215 $this->assertEquals(10, $data[0]['id']);
216 $this->assertEquals(41, $data[2]['id']);
217 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
221 * Test getLinks with visibility parameter set to private
223 public function testGetLinksVisibilityPrivate()
225 $env = Environment
::mock([
226 'REQUEST_METHOD' => 'GET',
227 'QUERY_STRING' => 'visibility=private'
229 $request = Request
::createFromEnvironment($env);
230 $response = $this->controller
->getLinks($request, new Response());
231 $this->assertEquals(200, $response->getStatusCode());
232 $data = json_decode((string) $response->getBody(), true);
233 $this->assertEquals($this->refDB
->countPrivateLinks(), count($data));
234 $this->assertEquals(6, $data[0]['id']);
235 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
239 * Test getLinks with visibility parameter set to public
241 public function testGetLinksVisibilityPublic()
243 $env = Environment
::mock(
245 'REQUEST_METHOD' => 'GET',
246 'QUERY_STRING' => 'visibility=public'
249 $request = Request
::createFromEnvironment($env);
250 $response = $this->controller
->getLinks($request, new Response());
251 $this->assertEquals(200, $response->getStatusCode());
252 $data = json_decode((string)$response->getBody(), true);
253 $this->assertEquals($this->refDB
->countPublicLinks(), count($data));
254 $this->assertEquals(10, $data[0]['id']);
255 $this->assertEquals(41, $data[2]['id']);
256 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
260 * Test getLinks service with offset and limit parameter:
261 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
263 public function testGetLinksSearchTerm()
265 // Only in description - 1 result
266 $env = Environment
::mock([
267 'REQUEST_METHOD' => 'GET',
268 'QUERY_STRING' => 'searchterm=Tropical'
270 $request = Request
::createFromEnvironment($env);
271 $response = $this->controller
->getLinks($request, new Response());
272 $this->assertEquals(200, $response->getStatusCode());
273 $data = json_decode((string) $response->getBody(), true);
274 $this->assertEquals(1, count($data));
275 $this->assertEquals(1, $data[0]['id']);
276 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
278 // Only in tags - 1 result
279 $env = Environment
::mock([
280 'REQUEST_METHOD' => 'GET',
281 'QUERY_STRING' => 'searchterm=tag3'
283 $request = Request
::createFromEnvironment($env);
284 $response = $this->controller
->getLinks($request, new Response());
285 $this->assertEquals(200, $response->getStatusCode());
286 $data = json_decode((string) $response->getBody(), true);
287 $this->assertEquals(1, count($data));
288 $this->assertEquals(0, $data[0]['id']);
289 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
291 // Multiple results (2)
292 $env = Environment
::mock([
293 'REQUEST_METHOD' => 'GET',
294 'QUERY_STRING' => 'searchterm=stallman'
296 $request = Request
::createFromEnvironment($env);
297 $response = $this->controller
->getLinks($request, new Response());
298 $this->assertEquals(200, $response->getStatusCode());
299 $data = json_decode((string) $response->getBody(), true);
300 $this->assertEquals(2, count($data));
301 $this->assertEquals(41, $data[0]['id']);
302 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
303 $this->assertEquals(8, $data[1]['id']);
304 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
306 // Multiword - 2 results
307 $env = Environment
::mock([
308 'REQUEST_METHOD' => 'GET',
309 'QUERY_STRING' => 'searchterm=stallman+software'
311 $request = Request
::createFromEnvironment($env);
312 $response = $this->controller
->getLinks($request, new Response());
313 $this->assertEquals(200, $response->getStatusCode());
314 $data = json_decode((string) $response->getBody(), true);
315 $this->assertEquals(2, count($data));
316 $this->assertEquals(41, $data[0]['id']);
317 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
318 $this->assertEquals(8, $data[1]['id']);
319 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
322 $env = Environment
::mock([
323 'REQUEST_METHOD' => 'GET',
324 'QUERY_STRING' => 'searchterm='. urlencode('@web')
326 $request = Request
::createFromEnvironment($env);
327 $response = $this->controller
->getLinks($request, new Response());
328 $this->assertEquals(200, $response->getStatusCode());
329 $data = json_decode((string) $response->getBody(), true);
330 $this->assertEquals(2, count($data));
331 $this->assertEquals(41, $data[0]['id']);
332 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
333 $this->assertEquals(8, $data[1]['id']);
334 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
337 public function testGetLinksSearchTermNoResult()
339 $env = Environment
::mock([
340 'REQUEST_METHOD' => 'GET',
341 'QUERY_STRING' => 'searchterm=nope'
343 $request = Request
::createFromEnvironment($env);
344 $response = $this->controller
->getLinks($request, new Response());
345 $this->assertEquals(200, $response->getStatusCode());
346 $data = json_decode((string) $response->getBody(), true);
347 $this->assertEquals(0, count($data));
350 public function testGetLinksSearchTags()
353 $env = Environment
::mock([
354 'REQUEST_METHOD' => 'GET',
355 'QUERY_STRING' => 'searchtags=dev',
357 $request = Request
::createFromEnvironment($env);
358 $response = $this->controller
->getLinks($request, new Response());
359 $this->assertEquals(200, $response->getStatusCode());
360 $data = json_decode((string) $response->getBody(), true);
361 $this->assertEquals(2, count($data));
362 $this->assertEquals(0, $data[0]['id']);
363 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
364 $this->assertEquals(4, $data[1]['id']);
365 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
367 // Multitag + exclude
368 $env = Environment
::mock([
369 'REQUEST_METHOD' => 'GET',
370 'QUERY_STRING' => 'searchtags=stuff+-gnu',
372 $request = Request
::createFromEnvironment($env);
373 $response = $this->controller
->getLinks($request, new Response());
374 $this->assertEquals(200, $response->getStatusCode());
375 $data = json_decode((string) $response->getBody(), true);
376 $this->assertEquals(1, count($data));
377 $this->assertEquals(41, $data[0]['id']);
378 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
380 // wildcard: placeholder at the start
381 $env = Environment
::mock([
382 'REQUEST_METHOD' => 'GET',
383 'QUERY_STRING' => 'searchtags=*Tuff',
385 $request = Request
::createFromEnvironment($env);
386 $response = $this->controller
->getLinks($request, new Response());
387 $this->assertEquals(200, $response->getStatusCode());
388 $data = json_decode((string) $response->getBody(), true);
389 $this->assertEquals(2, count($data));
390 $this->assertEquals(41, $data[0]['id']);
392 // wildcard: placeholder at the end
393 $env = Environment
::mock([
394 'REQUEST_METHOD' => 'GET',
395 'QUERY_STRING' => 'searchtags=c*',
397 $request = Request
::createFromEnvironment($env);
398 $response = $this->controller
->getLinks($request, new Response());
399 $this->assertEquals(200, $response->getStatusCode());
400 $data = json_decode((string) $response->getBody(), true);
401 $this->assertEquals(5, count($data));
402 $this->assertEquals(6, $data[0]['id']);
404 // wildcard: placeholder at the middle
405 $env = Environment
::mock([
406 'REQUEST_METHOD' => 'GET',
407 'QUERY_STRING' => 'searchtags=w*b',
409 $request = Request
::createFromEnvironment($env);
410 $response = $this->controller
->getLinks($request, new Response());
411 $this->assertEquals(200, $response->getStatusCode());
412 $data = json_decode((string) $response->getBody(), true);
413 $this->assertEquals(4, count($data));
414 $this->assertEquals(6, $data[0]['id']);
416 // wildcard: match all
417 $env = Environment
::mock([
418 'REQUEST_METHOD' => 'GET',
419 'QUERY_STRING' => 'searchtags=*',
421 $request = Request
::createFromEnvironment($env);
422 $response = $this->controller
->getLinks($request, new Response());
423 $this->assertEquals(200, $response->getStatusCode());
424 $data = json_decode((string) $response->getBody(), true);
425 $this->assertEquals(\ReferenceLinkDB
::$NB_LINKS_TOTAL, count($data));
426 $this->assertEquals(10, $data[0]['id']);
427 $this->assertEquals(41, $data[2]['id']);
429 // wildcard: optional ('*' does not need to expand)
430 $env = Environment
::mock([
431 'REQUEST_METHOD' => 'GET',
432 'QUERY_STRING' => 'searchtags=*stuff*',
434 $request = Request
::createFromEnvironment($env);
435 $response = $this->controller
->getLinks($request, new Response());
436 $this->assertEquals(200, $response->getStatusCode());
437 $data = json_decode((string) $response->getBody(), true);
438 $this->assertEquals(2, count($data));
439 $this->assertEquals(41, $data[0]['id']);
441 // wildcard: exclusions
442 $env = Environment
::mock([
443 'REQUEST_METHOD' => 'GET',
444 'QUERY_STRING' => 'searchtags=*a*+-*e*',
446 $request = Request
::createFromEnvironment($env);
447 $response = $this->controller
->getLinks($request, new Response());
448 $this->assertEquals(200, $response->getStatusCode());
449 $data = json_decode((string) $response->getBody(), true);
450 $this->assertEquals(1, count($data));
451 $this->assertEquals(41, $data[0]['id']); // finds '#hashtag' in descr.
453 // wildcard: exclude all
454 $env = Environment
::mock([
455 'REQUEST_METHOD' => 'GET',
456 'QUERY_STRING' => 'searchtags=-*',
458 $request = Request
::createFromEnvironment($env);
459 $response = $this->controller
->getLinks($request, new Response());
460 $this->assertEquals(200, $response->getStatusCode());
461 $data = json_decode((string) $response->getBody(), true);
462 $this->assertEquals(0, count($data));
466 * Test getLinks service with search tags+terms.
468 public function testGetLinksSearchTermsAndTags()
470 $env = Environment
::mock([
471 'REQUEST_METHOD' => 'GET',
472 'QUERY_STRING' => 'searchterm=poke&searchtags=dev',
474 $request = Request
::createFromEnvironment($env);
475 $response = $this->controller
->getLinks($request, new Response());
476 $this->assertEquals(200, $response->getStatusCode());
477 $data = json_decode((string) $response->getBody(), true);
478 $this->assertEquals(1, count($data));
479 $this->assertEquals(0, $data[0]['id']);
480 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));