2 namespace Shaarli\Api\Controllers
;
4 use Shaarli\Bookmark\Bookmark
;
5 use Shaarli\Bookmark\BookmarkFileService
;
6 use Shaarli\Bookmark\LinkDB
;
7 use Shaarli\Config\ConfigManager
;
10 use Slim\Http\Environment
;
11 use Slim\Http\Request
;
12 use Slim\Http\Response
;
17 * Test get Link list REST API service.
19 * @see http://shaarli.github.io/api-documentation/#links-links-collection-get
21 * @package Shaarli\Api\Controllers
23 class GetLinksTest
extends \PHPUnit\Framework\TestCase
26 * @var string datastore to test write operations
28 protected static $testDatastore = 'sandbox/datastore.php';
31 * @var ConfigManager instance
36 * @var \ReferenceLinkDB instance.
38 protected $refDB = null;
41 * @var Container instance.
46 * @var Links controller instance.
48 protected $controller;
51 * Number of JSON field per link.
53 const NB_FIELDS_LINK
= 9;
56 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
58 protected function setUp(): void
60 $this->conf
= new ConfigManager('tests/utils/config/configJson');
61 $this->conf
->set('resource.datastore', self
::$testDatastore);
62 $this->refDB
= new \
ReferenceLinkDB();
63 $this->refDB
->write(self
::$testDatastore);
64 $history = new History('sandbox/history.php');
66 $this->container
= new Container();
67 $this->container
['conf'] = $this->conf
;
68 $this->container
['db'] = new BookmarkFileService($this->conf
, $history, true);
69 $this->container
['history'] = null;
71 $this->controller
= new Links($this->container
);
75 * After every test, remove the test datastore.
77 protected function tearDown(): void
79 @unlink(self
::$testDatastore);
83 * Test basic getLinks service: returns all bookmarks.
85 public function testGetLinks()
87 // Used by index_url().
88 $_SERVER['SERVER_NAME'] = 'domain.tld';
89 $_SERVER['SERVER_PORT'] = 80;
90 $_SERVER['SCRIPT_NAME'] = '/';
92 $env = Environment
::mock([
93 'REQUEST_METHOD' => 'GET',
95 $request = Request
::createFromEnvironment($env);
97 $response = $this->controller
->getLinks($request, new Response());
98 $this->assertEquals(200, $response->getStatusCode());
99 $data = json_decode((string) $response->getBody(), true);
100 $this->assertEquals($this->refDB
->countLinks(), count($data));
103 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
105 foreach ($data as $link) {
106 $this->assertEquals(self
::NB_FIELDS_LINK
, count($link));
107 $this->assertEquals($order[$cpt++
], $link['id']);
110 // Check first element fields
112 $this->assertEquals('http://domain.tld/shaare/WDWyig', $first['url']);
113 $this->assertEquals('WDWyig', $first['shorturl']);
114 $this->assertEquals('Link title: @website', $first['title']);
116 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
117 $first['description']
119 $this->assertEquals('sTuff', $first['tags'][0]);
120 $this->assertEquals(false, $first['private']);
122 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20150310_114651')->format(\DateTime
::ATOM
),
125 $this->assertEmpty($first['updated']);
129 $this->assertEquals(7, count($link['tags']));
133 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20160803_093033')->format(\DateTime
::ATOM
),
139 * Test getLinks service with offset and limit parameter:
140 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
142 public function testGetLinksOffsetLimit()
144 $env = Environment
::mock([
145 'REQUEST_METHOD' => 'GET',
146 'QUERY_STRING' => 'offset=3&limit=1'
148 $request = Request
::createFromEnvironment($env);
149 $response = $this->controller
->getLinks($request, new Response());
150 $this->assertEquals(200, $response->getStatusCode());
151 $data = json_decode((string) $response->getBody(), true);
152 $this->assertEquals(1, count($data));
153 $this->assertEquals(8, $data[0]['id']);
154 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
158 * Test getLinks with limit=all (return all link).
160 public function testGetLinksLimitAll()
162 $env = Environment
::mock([
163 'REQUEST_METHOD' => 'GET',
164 'QUERY_STRING' => 'limit=all'
166 $request = Request
::createFromEnvironment($env);
167 $response = $this->controller
->getLinks($request, new Response());
168 $this->assertEquals(200, $response->getStatusCode());
169 $data = json_decode((string) $response->getBody(), true);
170 $this->assertEquals($this->refDB
->countLinks(), count($data));
172 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
174 foreach ($data as $link) {
175 $this->assertEquals(self
::NB_FIELDS_LINK
, count($link));
176 $this->assertEquals($order[$cpt++
], $link['id']);
181 * Test getLinks service with offset and limit parameter:
182 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
184 public function testGetLinksOffsetTooHigh()
186 $env = Environment
::mock([
187 'REQUEST_METHOD' => 'GET',
188 'QUERY_STRING' => 'offset=100'
190 $request = Request
::createFromEnvironment($env);
191 $response = $this->controller
->getLinks($request, new Response());
192 $this->assertEquals(200, $response->getStatusCode());
193 $data = json_decode((string) $response->getBody(), true);
194 $this->assertEmpty(count($data));
198 * Test getLinks with visibility parameter set to all
200 public function testGetLinksVisibilityAll()
202 $env = Environment
::mock(
204 'REQUEST_METHOD' => 'GET',
205 'QUERY_STRING' => 'visibility=all'
208 $request = Request
::createFromEnvironment($env);
209 $response = $this->controller
->getLinks($request, new Response());
210 $this->assertEquals(200, $response->getStatusCode());
211 $data = json_decode((string)$response->getBody(), true);
212 $this->assertEquals($this->refDB
->countLinks(), count($data));
213 $this->assertEquals(10, $data[0]['id']);
214 $this->assertEquals(41, $data[2]['id']);
215 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
219 * Test getLinks with visibility parameter set to private
221 public function testGetLinksVisibilityPrivate()
223 $env = Environment
::mock([
224 'REQUEST_METHOD' => 'GET',
225 'QUERY_STRING' => 'visibility=private'
227 $request = Request
::createFromEnvironment($env);
228 $response = $this->controller
->getLinks($request, new Response());
229 $this->assertEquals(200, $response->getStatusCode());
230 $data = json_decode((string) $response->getBody(), true);
231 $this->assertEquals($this->refDB
->countPrivateLinks(), count($data));
232 $this->assertEquals(6, $data[0]['id']);
233 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
237 * Test getLinks with visibility parameter set to public
239 public function testGetLinksVisibilityPublic()
241 $env = Environment
::mock(
243 'REQUEST_METHOD' => 'GET',
244 'QUERY_STRING' => 'visibility=public'
247 $request = Request
::createFromEnvironment($env);
248 $response = $this->controller
->getLinks($request, new Response());
249 $this->assertEquals(200, $response->getStatusCode());
250 $data = json_decode((string)$response->getBody(), true);
251 $this->assertEquals($this->refDB
->countPublicLinks(), count($data));
252 $this->assertEquals(10, $data[0]['id']);
253 $this->assertEquals(41, $data[2]['id']);
254 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
258 * Test getLinks service with offset and limit parameter:
259 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
261 public function testGetLinksSearchTerm()
263 // Only in description - 1 result
264 $env = Environment
::mock([
265 'REQUEST_METHOD' => 'GET',
266 'QUERY_STRING' => 'searchterm=Tropical'
268 $request = Request
::createFromEnvironment($env);
269 $response = $this->controller
->getLinks($request, new Response());
270 $this->assertEquals(200, $response->getStatusCode());
271 $data = json_decode((string) $response->getBody(), true);
272 $this->assertEquals(1, count($data));
273 $this->assertEquals(1, $data[0]['id']);
274 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
276 // Only in tags - 1 result
277 $env = Environment
::mock([
278 'REQUEST_METHOD' => 'GET',
279 'QUERY_STRING' => 'searchterm=tag3'
281 $request = Request
::createFromEnvironment($env);
282 $response = $this->controller
->getLinks($request, new Response());
283 $this->assertEquals(200, $response->getStatusCode());
284 $data = json_decode((string) $response->getBody(), true);
285 $this->assertEquals(1, count($data));
286 $this->assertEquals(0, $data[0]['id']);
287 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
289 // Multiple results (2)
290 $env = Environment
::mock([
291 'REQUEST_METHOD' => 'GET',
292 'QUERY_STRING' => 'searchterm=stallman'
294 $request = Request
::createFromEnvironment($env);
295 $response = $this->controller
->getLinks($request, new Response());
296 $this->assertEquals(200, $response->getStatusCode());
297 $data = json_decode((string) $response->getBody(), true);
298 $this->assertEquals(2, count($data));
299 $this->assertEquals(41, $data[0]['id']);
300 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
301 $this->assertEquals(8, $data[1]['id']);
302 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
304 // Multiword - 2 results
305 $env = Environment
::mock([
306 'REQUEST_METHOD' => 'GET',
307 'QUERY_STRING' => 'searchterm=stallman+software'
309 $request = Request
::createFromEnvironment($env);
310 $response = $this->controller
->getLinks($request, new Response());
311 $this->assertEquals(200, $response->getStatusCode());
312 $data = json_decode((string) $response->getBody(), true);
313 $this->assertEquals(2, count($data));
314 $this->assertEquals(41, $data[0]['id']);
315 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
316 $this->assertEquals(8, $data[1]['id']);
317 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
320 $env = Environment
::mock([
321 'REQUEST_METHOD' => 'GET',
322 'QUERY_STRING' => 'searchterm='. urlencode('@web')
324 $request = Request
::createFromEnvironment($env);
325 $response = $this->controller
->getLinks($request, new Response());
326 $this->assertEquals(200, $response->getStatusCode());
327 $data = json_decode((string) $response->getBody(), true);
328 $this->assertEquals(2, count($data));
329 $this->assertEquals(41, $data[0]['id']);
330 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
331 $this->assertEquals(8, $data[1]['id']);
332 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
335 public function testGetLinksSearchTermNoResult()
337 $env = Environment
::mock([
338 'REQUEST_METHOD' => 'GET',
339 'QUERY_STRING' => 'searchterm=nope'
341 $request = Request
::createFromEnvironment($env);
342 $response = $this->controller
->getLinks($request, new Response());
343 $this->assertEquals(200, $response->getStatusCode());
344 $data = json_decode((string) $response->getBody(), true);
345 $this->assertEquals(0, count($data));
348 public function testGetLinksSearchTags()
351 $env = Environment
::mock([
352 'REQUEST_METHOD' => 'GET',
353 'QUERY_STRING' => 'searchtags=dev',
355 $request = Request
::createFromEnvironment($env);
356 $response = $this->controller
->getLinks($request, new Response());
357 $this->assertEquals(200, $response->getStatusCode());
358 $data = json_decode((string) $response->getBody(), true);
359 $this->assertEquals(2, count($data));
360 $this->assertEquals(0, $data[0]['id']);
361 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
362 $this->assertEquals(4, $data[1]['id']);
363 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[1]));
365 // Multitag + exclude
366 $env = Environment
::mock([
367 'REQUEST_METHOD' => 'GET',
368 'QUERY_STRING' => 'searchtags=stuff+-gnu',
370 $request = Request
::createFromEnvironment($env);
371 $response = $this->controller
->getLinks($request, new Response());
372 $this->assertEquals(200, $response->getStatusCode());
373 $data = json_decode((string) $response->getBody(), true);
374 $this->assertEquals(1, count($data));
375 $this->assertEquals(41, $data[0]['id']);
376 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));
378 // wildcard: placeholder at the start
379 $env = Environment
::mock([
380 'REQUEST_METHOD' => 'GET',
381 'QUERY_STRING' => 'searchtags=*Tuff',
383 $request = Request
::createFromEnvironment($env);
384 $response = $this->controller
->getLinks($request, new Response());
385 $this->assertEquals(200, $response->getStatusCode());
386 $data = json_decode((string) $response->getBody(), true);
387 $this->assertEquals(2, count($data));
388 $this->assertEquals(41, $data[0]['id']);
390 // wildcard: placeholder at the end
391 $env = Environment
::mock([
392 'REQUEST_METHOD' => 'GET',
393 'QUERY_STRING' => 'searchtags=c*',
395 $request = Request
::createFromEnvironment($env);
396 $response = $this->controller
->getLinks($request, new Response());
397 $this->assertEquals(200, $response->getStatusCode());
398 $data = json_decode((string) $response->getBody(), true);
399 $this->assertEquals(4, count($data));
400 $this->assertEquals(6, $data[0]['id']);
402 // wildcard: placeholder at the middle
403 $env = Environment
::mock([
404 'REQUEST_METHOD' => 'GET',
405 'QUERY_STRING' => 'searchtags=w*b',
407 $request = Request
::createFromEnvironment($env);
408 $response = $this->controller
->getLinks($request, new Response());
409 $this->assertEquals(200, $response->getStatusCode());
410 $data = json_decode((string) $response->getBody(), true);
411 $this->assertEquals(4, count($data));
412 $this->assertEquals(6, $data[0]['id']);
414 // wildcard: match all
415 $env = Environment
::mock([
416 'REQUEST_METHOD' => 'GET',
417 'QUERY_STRING' => 'searchtags=*',
419 $request = Request
::createFromEnvironment($env);
420 $response = $this->controller
->getLinks($request, new Response());
421 $this->assertEquals(200, $response->getStatusCode());
422 $data = json_decode((string) $response->getBody(), true);
423 $this->assertEquals(\ReferenceLinkDB
::$NB_LINKS_TOTAL, count($data));
424 $this->assertEquals(10, $data[0]['id']);
425 $this->assertEquals(41, $data[2]['id']);
427 // wildcard: optional ('*' does not need to expand)
428 $env = Environment
::mock([
429 'REQUEST_METHOD' => 'GET',
430 'QUERY_STRING' => 'searchtags=*stuff*',
432 $request = Request
::createFromEnvironment($env);
433 $response = $this->controller
->getLinks($request, new Response());
434 $this->assertEquals(200, $response->getStatusCode());
435 $data = json_decode((string) $response->getBody(), true);
436 $this->assertEquals(2, count($data));
437 $this->assertEquals(41, $data[0]['id']);
439 // wildcard: exclusions
440 $env = Environment
::mock([
441 'REQUEST_METHOD' => 'GET',
442 'QUERY_STRING' => 'searchtags=*a*+-*e*',
444 $request = Request
::createFromEnvironment($env);
445 $response = $this->controller
->getLinks($request, new Response());
446 $this->assertEquals(200, $response->getStatusCode());
447 $data = json_decode((string) $response->getBody(), true);
448 $this->assertEquals(1, count($data));
449 $this->assertEquals(41, $data[0]['id']); // finds '#hashtag' in descr.
451 // wildcard: exclude all
452 $env = Environment
::mock([
453 'REQUEST_METHOD' => 'GET',
454 'QUERY_STRING' => 'searchtags=-*',
456 $request = Request
::createFromEnvironment($env);
457 $response = $this->controller
->getLinks($request, new Response());
458 $this->assertEquals(200, $response->getStatusCode());
459 $data = json_decode((string) $response->getBody(), true);
460 $this->assertEquals(0, count($data));
464 * Test getLinks service with search tags+terms.
466 public function testGetLinksSearchTermsAndTags()
468 $env = Environment
::mock([
469 'REQUEST_METHOD' => 'GET',
470 'QUERY_STRING' => 'searchterm=poke&searchtags=dev',
472 $request = Request
::createFromEnvironment($env);
473 $response = $this->controller
->getLinks($request, new Response());
474 $this->assertEquals(200, $response->getStatusCode());
475 $data = json_decode((string) $response->getBody(), true);
476 $this->assertEquals(1, count($data));
477 $this->assertEquals(0, $data[0]['id']);
478 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data[0]));