aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/api/controllers/links
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2018-05-19 15:04:04 +0200
committerArthurHoaro <arthur@hoa.ro>2018-06-04 18:51:22 +0200
commitd3f42ca487287447efb81061609644108044a038 (patch)
tree6e9d5a33290f857c8f7a04dbdf8cf0ca749db149 /tests/api/controllers/links
parent17e45b2e9c33c736751e059276fadb480f98e621 (diff)
downloadShaarli-d3f42ca487287447efb81061609644108044a038.tar.gz
Shaarli-d3f42ca487287447efb81061609644108044a038.tar.zst
Shaarli-d3f42ca487287447efb81061609644108044a038.zip
Implements Tags endpoints for Shaarli's REST API
Endpoints: * List All Tags [GET] * Get a tag [GET] * Update a tag [PUT] * Delete a tag [DELETE] Fixes #904 References shaarli/api-documentation#34
Diffstat (limited to 'tests/api/controllers/links')
-rw-r--r--tests/api/controllers/links/DeleteLinkTest.php126
-rw-r--r--tests/api/controllers/links/GetLinkIdTest.php132
-rw-r--r--tests/api/controllers/links/GetLinksTest.php472
-rw-r--r--tests/api/controllers/links/PostLinkTest.php218
-rw-r--r--tests/api/controllers/links/PutLinkTest.php222
5 files changed, 1170 insertions, 0 deletions
diff --git a/tests/api/controllers/links/DeleteLinkTest.php b/tests/api/controllers/links/DeleteLinkTest.php
new file mode 100644
index 00000000..7d797137
--- /dev/null
+++ b/tests/api/controllers/links/DeleteLinkTest.php
@@ -0,0 +1,126 @@
1<?php
2
3
4namespace Shaarli\Api\Controllers;
5
6use Shaarli\Config\ConfigManager;
7use Slim\Container;
8use Slim\Http\Environment;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class DeleteLinkTest extends \PHPUnit_Framework_TestCase
13{
14 /**
15 * @var string datastore to test write operations
16 */
17 protected static $testDatastore = 'sandbox/datastore.php';
18
19 /**
20 * @var string datastore to test write operations
21 */
22 protected static $testHistory = 'sandbox/history.php';
23
24 /**
25 * @var ConfigManager instance
26 */
27 protected $conf;
28
29 /**
30 * @var \ReferenceLinkDB instance.
31 */
32 protected $refDB = null;
33
34 /**
35 * @var \LinkDB instance.
36 */
37 protected $linkDB;
38
39 /**
40 * @var \History instance.
41 */
42 protected $history;
43
44 /**
45 * @var Container instance.
46 */
47 protected $container;
48
49 /**
50 * @var Links controller instance.
51 */
52 protected $controller;
53
54 /**
55 * Before each test, instantiate a new Api with its config, plugins and links.
56 */
57 public function setUp()
58 {
59 $this->conf = new ConfigManager('tests/utils/config/configJson');
60 $this->refDB = new \ReferenceLinkDB();
61 $this->refDB->write(self::$testDatastore);
62 $this->linkDB = new \LinkDB(self::$testDatastore, true, false);
63 $refHistory = new \ReferenceHistory();
64 $refHistory->write(self::$testHistory);
65 $this->history = new \History(self::$testHistory);
66 $this->container = new Container();
67 $this->container['conf'] = $this->conf;
68 $this->container['db'] = $this->linkDB;
69 $this->container['history'] = $this->history;
70
71 $this->controller = new Links($this->container);
72 }
73
74 /**
75 * After each test, remove the test datastore.
76 */
77 public function tearDown()
78 {
79 @unlink(self::$testDatastore);
80 @unlink(self::$testHistory);
81 }
82
83 /**
84 * Test DELETE link endpoint: the link should be removed.
85 */
86 public function testDeleteLinkValid()
87 {
88 $id = '41';
89 $this->assertTrue(isset($this->linkDB[$id]));
90 $env = Environment::mock([
91 'REQUEST_METHOD' => 'DELETE',
92 ]);
93 $request = Request::createFromEnvironment($env);
94
95 $response = $this->controller->deleteLink($request, new Response(), ['id' => $id]);
96 $this->assertEquals(204, $response->getStatusCode());
97 $this->assertEmpty((string) $response->getBody());
98
99 $this->linkDB = new \LinkDB(self::$testDatastore, true, false);
100 $this->assertFalse(isset($this->linkDB[$id]));
101
102 $historyEntry = $this->history->getHistory()[0];
103 $this->assertEquals(\History::DELETED, $historyEntry['event']);
104 $this->assertTrue(
105 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
106 );
107 $this->assertEquals($id, $historyEntry['id']);
108 }
109
110 /**
111 * Test DELETE link endpoint: reach not existing ID.
112 *
113 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
114 */
115 public function testDeleteLink404()
116 {
117 $id = -1;
118 $this->assertFalse(isset($this->linkDB[$id]));
119 $env = Environment::mock([
120 'REQUEST_METHOD' => 'DELETE',
121 ]);
122 $request = Request::createFromEnvironment($env);
123
124 $this->controller->deleteLink($request, new Response(), ['id' => $id]);
125 }
126}
diff --git a/tests/api/controllers/links/GetLinkIdTest.php b/tests/api/controllers/links/GetLinkIdTest.php
new file mode 100644
index 00000000..57528d5a
--- /dev/null
+++ b/tests/api/controllers/links/GetLinkIdTest.php
@@ -0,0 +1,132 @@
1<?php
2
3namespace Shaarli\Api\Controllers;
4
5use Shaarli\Config\ConfigManager;
6
7use Slim\Container;
8use Slim\Http\Environment;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12/**
13 * Class GetLinkIdTest
14 *
15 * Test getLink by ID API service.
16 *
17 * @see http://shaarli.github.io/api-documentation/#links-link-get
18 *
19 * @package Shaarli\Api\Controllers
20 */
21class GetLinkIdTest extends \PHPUnit_Framework_TestCase
22{
23 /**
24 * @var string datastore to test write operations
25 */
26 protected static $testDatastore = 'sandbox/datastore.php';
27
28 /**
29 * @var ConfigManager instance
30 */
31 protected $conf;
32
33 /**
34 * @var \ReferenceLinkDB instance.
35 */
36 protected $refDB = null;
37
38 /**
39 * @var Container instance.
40 */
41 protected $container;
42
43 /**
44 * @var Links controller instance.
45 */
46 protected $controller;
47
48 /**
49 * Number of JSON fields per link.
50 */
51 const NB_FIELDS_LINK = 9;
52
53 /**
54 * Before each test, instantiate a new Api with its config, plugins and links.
55 */
56 public function setUp()
57 {
58 $this->conf = new ConfigManager('tests/utils/config/configJson');
59 $this->refDB = new \ReferenceLinkDB();
60 $this->refDB->write(self::$testDatastore);
61
62 $this->container = new Container();
63 $this->container['conf'] = $this->conf;
64 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
65 $this->container['history'] = null;
66
67 $this->controller = new Links($this->container);
68 }
69
70 /**
71 * After each test, remove the test datastore.
72 */
73 public function tearDown()
74 {
75 @unlink(self::$testDatastore);
76 }
77
78 /**
79 * Test basic getLink service: return link ID=41.
80 */
81 public function testGetLinkId()
82 {
83 // Used by index_url().
84 $_SERVER['SERVER_NAME'] = 'domain.tld';
85 $_SERVER['SERVER_PORT'] = 80;
86 $_SERVER['SCRIPT_NAME'] = '/';
87
88 $id = 41;
89 $env = Environment::mock([
90 'REQUEST_METHOD' => 'GET',
91 ]);
92 $request = Request::createFromEnvironment($env);
93
94 $response = $this->controller->getLink($request, new Response(), ['id' => $id]);
95 $this->assertEquals(200, $response->getStatusCode());
96 $data = json_decode((string) $response->getBody(), true);
97 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
98 $this->assertEquals($id, $data['id']);
99
100 // Check link elements
101 $this->assertEquals('http://domain.tld/?WDWyig', $data['url']);
102 $this->assertEquals('WDWyig', $data['shorturl']);
103 $this->assertEquals('Link title: @website', $data['title']);
104 $this->assertEquals(
105 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
106 $data['description']
107 );
108 $this->assertEquals('sTuff', $data['tags'][0]);
109 $this->assertEquals(false, $data['private']);
110 $this->assertEquals(
111 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
112 $data['created']
113 );
114 $this->assertEmpty($data['updated']);
115 }
116
117 /**
118 * Test basic getLink service: get non existent link => ApiLinkNotFoundException.
119 *
120 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
121 * @expectedExceptionMessage Link not found
122 */
123 public function testGetLink404()
124 {
125 $env = Environment::mock([
126 'REQUEST_METHOD' => 'GET',
127 ]);
128 $request = Request::createFromEnvironment($env);
129
130 $this->controller->getLink($request, new Response(), ['id' => -1]);
131 }
132}
diff --git a/tests/api/controllers/links/GetLinksTest.php b/tests/api/controllers/links/GetLinksTest.php
new file mode 100644
index 00000000..d22ed3bf
--- /dev/null
+++ b/tests/api/controllers/links/GetLinksTest.php
@@ -0,0 +1,472 @@
1<?php
2namespace Shaarli\Api\Controllers;
3
4use Shaarli\Config\ConfigManager;
5
6use Slim\Container;
7use Slim\Http\Environment;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11/**
12 * Class GetLinksTest
13 *
14 * Test get Link list REST API service.
15 *
16 * @see http://shaarli.github.io/api-documentation/#links-links-collection-get
17 *
18 * @package Shaarli\Api\Controllers
19 */
20class GetLinksTest extends \PHPUnit_Framework_TestCase
21{
22 /**
23 * @var string datastore to test write operations
24 */
25 protected static $testDatastore = 'sandbox/datastore.php';
26
27 /**
28 * @var ConfigManager instance
29 */
30 protected $conf;
31
32 /**
33 * @var \ReferenceLinkDB instance.
34 */
35 protected $refDB = null;
36
37 /**
38 * @var Container instance.
39 */
40 protected $container;
41
42 /**
43 * @var Links controller instance.
44 */
45 protected $controller;
46
47 /**
48 * Number of JSON field per link.
49 */
50 const NB_FIELDS_LINK = 9;
51
52 /**
53 * Before every test, instantiate a new Api with its config, plugins and links.
54 */
55 public function setUp()
56 {
57 $this->conf = new ConfigManager('tests/utils/config/configJson');
58 $this->refDB = new \ReferenceLinkDB();
59 $this->refDB->write(self::$testDatastore);
60
61 $this->container = new Container();
62 $this->container['conf'] = $this->conf;
63 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
64 $this->container['history'] = null;
65
66 $this->controller = new Links($this->container);
67 }
68
69 /**
70 * After every test, remove the test datastore.
71 */
72 public function tearDown()
73 {
74 @unlink(self::$testDatastore);
75 }
76
77 /**
78 * Test basic getLinks service: returns all links.
79 */
80 public function testGetLinks()
81 {
82 // Used by index_url().
83 $_SERVER['SERVER_NAME'] = 'domain.tld';
84 $_SERVER['SERVER_PORT'] = 80;
85 $_SERVER['SCRIPT_NAME'] = '/';
86
87 $env = Environment::mock([
88 'REQUEST_METHOD' => 'GET',
89 ]);
90 $request = Request::createFromEnvironment($env);
91
92 $response = $this->controller->getLinks($request, new Response());
93 $this->assertEquals(200, $response->getStatusCode());
94 $data = json_decode((string) $response->getBody(), true);
95 $this->assertEquals($this->refDB->countLinks(), count($data));
96
97 // Check order
98 $order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
99 $cpt = 0;
100 foreach ($data as $link) {
101 $this->assertEquals(self::NB_FIELDS_LINK, count($link));
102 $this->assertEquals($order[$cpt++], $link['id']);
103 }
104
105 // Check first element fields
106 $first = $data[0];
107 $this->assertEquals('http://domain.tld/?WDWyig', $first['url']);
108 $this->assertEquals('WDWyig', $first['shorturl']);
109 $this->assertEquals('Link title: @website', $first['title']);
110 $this->assertEquals(
111 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
112 $first['description']
113 );
114 $this->assertEquals('sTuff', $first['tags'][0]);
115 $this->assertEquals(false, $first['private']);
116 $this->assertEquals(
117 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
118 $first['created']
119 );
120 $this->assertEmpty($first['updated']);
121
122 // Multi tags
123 $link = $data[1];
124 $this->assertEquals(7, count($link['tags']));
125
126 // Update date
127 $this->assertEquals(
128 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM),
129 $link['updated']
130 );
131 }
132
133 /**
134 * Test getLinks service with offset and limit parameter:
135 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
136 */
137 public function testGetLinksOffsetLimit()
138 {
139 $env = Environment::mock([
140 'REQUEST_METHOD' => 'GET',
141 'QUERY_STRING' => 'offset=1&limit=1'
142 ]);
143 $request = Request::createFromEnvironment($env);
144 $response = $this->controller->getLinks($request, new Response());
145 $this->assertEquals(200, $response->getStatusCode());
146 $data = json_decode((string) $response->getBody(), true);
147 $this->assertEquals(1, count($data));
148 $this->assertEquals(8, $data[0]['id']);
149 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
150 }
151
152 /**
153 * Test getLinks with limit=all (return all link).
154 */
155 public function testGetLinksLimitAll()
156 {
157 $env = Environment::mock([
158 'REQUEST_METHOD' => 'GET',
159 'QUERY_STRING' => 'limit=all'
160 ]);
161 $request = Request::createFromEnvironment($env);
162 $response = $this->controller->getLinks($request, new Response());
163 $this->assertEquals(200, $response->getStatusCode());
164 $data = json_decode((string) $response->getBody(), true);
165 $this->assertEquals($this->refDB->countLinks(), count($data));
166 // Check order
167 $order = [41, 8, 6, 7, 0, 1, 9, 4, 42];
168 $cpt = 0;
169 foreach ($data as $link) {
170 $this->assertEquals(self::NB_FIELDS_LINK, count($link));
171 $this->assertEquals($order[$cpt++], $link['id']);
172 }
173 }
174
175 /**
176 * Test getLinks service with offset and limit parameter:
177 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
178 */
179 public function testGetLinksOffsetTooHigh()
180 {
181 $env = Environment::mock([
182 'REQUEST_METHOD' => 'GET',
183 'QUERY_STRING' => 'offset=100'
184 ]);
185 $request = Request::createFromEnvironment($env);
186 $response = $this->controller->getLinks($request, new Response());
187 $this->assertEquals(200, $response->getStatusCode());
188 $data = json_decode((string) $response->getBody(), true);
189 $this->assertEmpty(count($data));
190 }
191
192 /**
193 * Test getLinks with visibility parameter set to all
194 */
195 public function testGetLinksVisibilityAll()
196 {
197 $env = Environment::mock(
198 [
199 'REQUEST_METHOD' => 'GET',
200 'QUERY_STRING' => 'visibility=all'
201 ]
202 );
203 $request = Request::createFromEnvironment($env);
204 $response = $this->controller->getLinks($request, new Response());
205 $this->assertEquals(200, $response->getStatusCode());
206 $data = json_decode((string)$response->getBody(), true);
207 $this->assertEquals($this->refDB->countLinks(), count($data));
208 $this->assertEquals(41, $data[0]['id']);
209 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
210 }
211
212 /**
213 * Test getLinks with visibility parameter set to private
214 */
215 public function testGetLinksVisibilityPrivate()
216 {
217 $env = Environment::mock([
218 'REQUEST_METHOD' => 'GET',
219 'QUERY_STRING' => 'visibility=private'
220 ]);
221 $request = Request::createFromEnvironment($env);
222 $response = $this->controller->getLinks($request, new Response());
223 $this->assertEquals(200, $response->getStatusCode());
224 $data = json_decode((string) $response->getBody(), true);
225 $this->assertEquals($this->refDB->countPrivateLinks(), count($data));
226 $this->assertEquals(6, $data[0]['id']);
227 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
228 }
229
230 /**
231 * Test getLinks with visibility parameter set to public
232 */
233 public function testGetLinksVisibilityPublic()
234 {
235 $env = Environment::mock(
236 [
237 'REQUEST_METHOD' => 'GET',
238 'QUERY_STRING' => 'visibility=public'
239 ]
240 );
241 $request = Request::createFromEnvironment($env);
242 $response = $this->controller->getLinks($request, new Response());
243 $this->assertEquals(200, $response->getStatusCode());
244 $data = json_decode((string)$response->getBody(), true);
245 $this->assertEquals($this->refDB->countPublicLinks(), count($data));
246 $this->assertEquals(41, $data[0]['id']);
247 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
248 }
249
250 /**
251 * Test getLinks service with offset and limit parameter:
252 * limit=1 and offset=1 should return only the second link, ID=8 (ordered by creation date DESC).
253 */
254 public function testGetLinksSearchTerm()
255 {
256 // Only in description - 1 result
257 $env = Environment::mock([
258 'REQUEST_METHOD' => 'GET',
259 'QUERY_STRING' => 'searchterm=Tropical'
260 ]);
261 $request = Request::createFromEnvironment($env);
262 $response = $this->controller->getLinks($request, new Response());
263 $this->assertEquals(200, $response->getStatusCode());
264 $data = json_decode((string) $response->getBody(), true);
265 $this->assertEquals(1, count($data));
266 $this->assertEquals(1, $data[0]['id']);
267 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
268
269 // Only in tags - 1 result
270 $env = Environment::mock([
271 'REQUEST_METHOD' => 'GET',
272 'QUERY_STRING' => 'searchterm=tag3'
273 ]);
274 $request = Request::createFromEnvironment($env);
275 $response = $this->controller->getLinks($request, new Response());
276 $this->assertEquals(200, $response->getStatusCode());
277 $data = json_decode((string) $response->getBody(), true);
278 $this->assertEquals(1, count($data));
279 $this->assertEquals(0, $data[0]['id']);
280 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
281
282 // Multiple results (2)
283 $env = Environment::mock([
284 'REQUEST_METHOD' => 'GET',
285 'QUERY_STRING' => 'searchterm=stallman'
286 ]);
287 $request = Request::createFromEnvironment($env);
288 $response = $this->controller->getLinks($request, new Response());
289 $this->assertEquals(200, $response->getStatusCode());
290 $data = json_decode((string) $response->getBody(), true);
291 $this->assertEquals(2, count($data));
292 $this->assertEquals(41, $data[0]['id']);
293 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
294 $this->assertEquals(8, $data[1]['id']);
295 $this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
296
297 // Multiword - 2 results
298 $env = Environment::mock([
299 'REQUEST_METHOD' => 'GET',
300 'QUERY_STRING' => 'searchterm=stallman+software'
301 ]);
302 $request = Request::createFromEnvironment($env);
303 $response = $this->controller->getLinks($request, new Response());
304 $this->assertEquals(200, $response->getStatusCode());
305 $data = json_decode((string) $response->getBody(), true);
306 $this->assertEquals(2, count($data));
307 $this->assertEquals(41, $data[0]['id']);
308 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
309 $this->assertEquals(8, $data[1]['id']);
310 $this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
311
312 // URL encoding
313 $env = Environment::mock([
314 'REQUEST_METHOD' => 'GET',
315 'QUERY_STRING' => 'searchterm='. urlencode('@web')
316 ]);
317 $request = Request::createFromEnvironment($env);
318 $response = $this->controller->getLinks($request, new Response());
319 $this->assertEquals(200, $response->getStatusCode());
320 $data = json_decode((string) $response->getBody(), true);
321 $this->assertEquals(2, count($data));
322 $this->assertEquals(41, $data[0]['id']);
323 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
324 $this->assertEquals(8, $data[1]['id']);
325 $this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
326 }
327
328 public function testGetLinksSearchTermNoResult()
329 {
330 $env = Environment::mock([
331 'REQUEST_METHOD' => 'GET',
332 'QUERY_STRING' => 'searchterm=nope'
333 ]);
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(0, count($data));
339 }
340
341 public function testGetLinksSearchTags()
342 {
343 // Single tag
344 $env = Environment::mock([
345 'REQUEST_METHOD' => 'GET',
346 'QUERY_STRING' => 'searchtags=dev',
347 ]);
348 $request = Request::createFromEnvironment($env);
349 $response = $this->controller->getLinks($request, new Response());
350 $this->assertEquals(200, $response->getStatusCode());
351 $data = json_decode((string) $response->getBody(), true);
352 $this->assertEquals(2, count($data));
353 $this->assertEquals(0, $data[0]['id']);
354 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
355 $this->assertEquals(4, $data[1]['id']);
356 $this->assertEquals(self::NB_FIELDS_LINK, count($data[1]));
357
358 // Multitag + exclude
359 $env = Environment::mock([
360 'REQUEST_METHOD' => 'GET',
361 'QUERY_STRING' => 'searchtags=stuff+-gnu',
362 ]);
363 $request = Request::createFromEnvironment($env);
364 $response = $this->controller->getLinks($request, new Response());
365 $this->assertEquals(200, $response->getStatusCode());
366 $data = json_decode((string) $response->getBody(), true);
367 $this->assertEquals(1, count($data));
368 $this->assertEquals(41, $data[0]['id']);
369 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
370
371 // wildcard: placeholder at the start
372 $env = Environment::mock([
373 'REQUEST_METHOD' => 'GET',
374 'QUERY_STRING' => 'searchtags=*Tuff',
375 ]);
376 $request = Request::createFromEnvironment($env);
377 $response = $this->controller->getLinks($request, new Response());
378 $this->assertEquals(200, $response->getStatusCode());
379 $data = json_decode((string) $response->getBody(), true);
380 $this->assertEquals(2, count($data));
381 $this->assertEquals(41, $data[0]['id']);
382
383 // wildcard: placeholder at the end
384 $env = Environment::mock([
385 'REQUEST_METHOD' => 'GET',
386 'QUERY_STRING' => 'searchtags=c*',
387 ]);
388 $request = Request::createFromEnvironment($env);
389 $response = $this->controller->getLinks($request, new Response());
390 $this->assertEquals(200, $response->getStatusCode());
391 $data = json_decode((string) $response->getBody(), true);
392 $this->assertEquals(4, count($data));
393 $this->assertEquals(6, $data[0]['id']);
394
395 // wildcard: placeholder at the middle
396 $env = Environment::mock([
397 'REQUEST_METHOD' => 'GET',
398 'QUERY_STRING' => 'searchtags=w*b',
399 ]);
400 $request = Request::createFromEnvironment($env);
401 $response = $this->controller->getLinks($request, new Response());
402 $this->assertEquals(200, $response->getStatusCode());
403 $data = json_decode((string) $response->getBody(), true);
404 $this->assertEquals(4, count($data));
405 $this->assertEquals(6, $data[0]['id']);
406
407 // wildcard: match all
408 $env = Environment::mock([
409 'REQUEST_METHOD' => 'GET',
410 'QUERY_STRING' => 'searchtags=*',
411 ]);
412 $request = Request::createFromEnvironment($env);
413 $response = $this->controller->getLinks($request, new Response());
414 $this->assertEquals(200, $response->getStatusCode());
415 $data = json_decode((string) $response->getBody(), true);
416 $this->assertEquals(9, count($data));
417 $this->assertEquals(41, $data[0]['id']);
418
419 // wildcard: optional ('*' does not need to expand)
420 $env = Environment::mock([
421 'REQUEST_METHOD' => 'GET',
422 'QUERY_STRING' => 'searchtags=*stuff*',
423 ]);
424 $request = Request::createFromEnvironment($env);
425 $response = $this->controller->getLinks($request, new Response());
426 $this->assertEquals(200, $response->getStatusCode());
427 $data = json_decode((string) $response->getBody(), true);
428 $this->assertEquals(2, count($data));
429 $this->assertEquals(41, $data[0]['id']);
430
431 // wildcard: exclusions
432 $env = Environment::mock([
433 'REQUEST_METHOD' => 'GET',
434 'QUERY_STRING' => 'searchtags=*a*+-*e*',
435 ]);
436 $request = Request::createFromEnvironment($env);
437 $response = $this->controller->getLinks($request, new Response());
438 $this->assertEquals(200, $response->getStatusCode());
439 $data = json_decode((string) $response->getBody(), true);
440 $this->assertEquals(1, count($data));
441 $this->assertEquals(41, $data[0]['id']); // finds '#hashtag' in descr.
442
443 // wildcard: exclude all
444 $env = Environment::mock([
445 'REQUEST_METHOD' => 'GET',
446 'QUERY_STRING' => 'searchtags=-*',
447 ]);
448 $request = Request::createFromEnvironment($env);
449 $response = $this->controller->getLinks($request, new Response());
450 $this->assertEquals(200, $response->getStatusCode());
451 $data = json_decode((string) $response->getBody(), true);
452 $this->assertEquals(0, count($data));
453 }
454
455 /**
456 * Test getLinks service with search tags+terms.
457 */
458 public function testGetLinksSearchTermsAndTags()
459 {
460 $env = Environment::mock([
461 'REQUEST_METHOD' => 'GET',
462 'QUERY_STRING' => 'searchterm=poke&searchtags=dev',
463 ]);
464 $request = Request::createFromEnvironment($env);
465 $response = $this->controller->getLinks($request, new Response());
466 $this->assertEquals(200, $response->getStatusCode());
467 $data = json_decode((string) $response->getBody(), true);
468 $this->assertEquals(1, count($data));
469 $this->assertEquals(0, $data[0]['id']);
470 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
471 }
472}
diff --git a/tests/api/controllers/links/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php
new file mode 100644
index 00000000..100a9170
--- /dev/null
+++ b/tests/api/controllers/links/PostLinkTest.php
@@ -0,0 +1,218 @@
1<?php
2
3namespace Shaarli\Api\Controllers;
4
5
6use PHPUnit\Framework\TestCase;
7use Shaarli\Config\ConfigManager;
8use Slim\Container;
9use Slim\Http\Environment;
10use Slim\Http\Request;
11use Slim\Http\Response;
12use Slim\Router;
13
14/**
15 * Class PostLinkTest
16 *
17 * Test POST Link REST API service.
18 *
19 * @package Shaarli\Api\Controllers
20 */
21class PostLinkTest extends TestCase
22{
23 /**
24 * @var string datastore to test write operations
25 */
26 protected static $testDatastore = 'sandbox/datastore.php';
27
28 /**
29 * @var string datastore to test write operations
30 */
31 protected static $testHistory = 'sandbox/history.php';
32
33 /**
34 * @var ConfigManager instance
35 */
36 protected $conf;
37
38 /**
39 * @var \ReferenceLinkDB instance.
40 */
41 protected $refDB = null;
42
43 /**
44 * @var \History instance.
45 */
46 protected $history;
47
48 /**
49 * @var Container instance.
50 */
51 protected $container;
52
53 /**
54 * @var Links controller instance.
55 */
56 protected $controller;
57
58 /**
59 * Number of JSON field per link.
60 */
61 const NB_FIELDS_LINK = 9;
62
63 /**
64 * Before every test, instantiate a new Api with its config, plugins and links.
65 */
66 public function setUp()
67 {
68 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php');
69 $this->refDB = new \ReferenceLinkDB();
70 $this->refDB->write(self::$testDatastore);
71
72 $refHistory = new \ReferenceHistory();
73 $refHistory->write(self::$testHistory);
74 $this->history = new \History(self::$testHistory);
75
76 $this->container = new Container();
77 $this->container['conf'] = $this->conf;
78 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
79 $this->container['history'] = new \History(self::$testHistory);
80
81 $this->controller = new Links($this->container);
82
83 $mock = $this->createMock(Router::class);
84 $mock->expects($this->any())
85 ->method('relativePathFor')
86 ->willReturn('api/v1/links/1');
87
88 // affect @property-read... seems to work
89 $this->controller->getCi()->router = $mock;
90
91 // Used by index_url().
92 $this->controller->getCi()['environment'] = [
93 'SERVER_NAME' => 'domain.tld',
94 'SERVER_PORT' => 80,
95 'SCRIPT_NAME' => '/',
96 ];
97 }
98
99 /**
100 * After every test, remove the test datastore.
101 */
102 public function tearDown()
103 {
104 @unlink(self::$testDatastore);
105 @unlink(self::$testHistory);
106 }
107
108 /**
109 * Test link creation without any field: creates a blank note.
110 */
111 public function testPostLinkMinimal()
112 {
113 $env = Environment::mock([
114 'REQUEST_METHOD' => 'POST',
115 ]);
116
117 $request = Request::createFromEnvironment($env);
118
119 $response = $this->controller->postLink($request, new Response());
120 $this->assertEquals(201, $response->getStatusCode());
121 $this->assertEquals('api/v1/links/1', $response->getHeader('Location')[0]);
122 $data = json_decode((string) $response->getBody(), true);
123 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
124 $this->assertEquals(43, $data['id']);
125 $this->assertRegExp('/[\w-_]{6}/', $data['shorturl']);
126 $this->assertEquals('http://domain.tld/?' . $data['shorturl'], $data['url']);
127 $this->assertEquals('?' . $data['shorturl'], $data['title']);
128 $this->assertEquals('', $data['description']);
129 $this->assertEquals([], $data['tags']);
130 $this->assertEquals(false, $data['private']);
131 $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created']));
132 $this->assertEquals('', $data['updated']);
133
134 $historyEntry = $this->history->getHistory()[0];
135 $this->assertEquals(\History::CREATED, $historyEntry['event']);
136 $this->assertTrue(
137 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
138 );
139 $this->assertEquals(43, $historyEntry['id']);
140 }
141
142 /**
143 * Test link creation with all available fields.
144 */
145 public function testPostLinkFull()
146 {
147 $link = [
148 'url' => 'website.tld/test?foo=bar',
149 'title' => 'new entry',
150 'description' => 'shaare description',
151 'tags' => ['one', 'two'],
152 'private' => true,
153 ];
154 $env = Environment::mock([
155 'REQUEST_METHOD' => 'POST',
156 'CONTENT_TYPE' => 'application/json'
157 ]);
158
159 $request = Request::createFromEnvironment($env);
160 $request = $request->withParsedBody($link);
161 $response = $this->controller->postLink($request, new Response());
162
163 $this->assertEquals(201, $response->getStatusCode());
164 $this->assertEquals('api/v1/links/1', $response->getHeader('Location')[0]);
165 $data = json_decode((string) $response->getBody(), true);
166 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
167 $this->assertEquals(43, $data['id']);
168 $this->assertRegExp('/[\w-_]{6}/', $data['shorturl']);
169 $this->assertEquals('http://' . $link['url'], $data['url']);
170 $this->assertEquals($link['title'], $data['title']);
171 $this->assertEquals($link['description'], $data['description']);
172 $this->assertEquals($link['tags'], $data['tags']);
173 $this->assertEquals(true, $data['private']);
174 $this->assertTrue(new \DateTime('2 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created']));
175 $this->assertEquals('', $data['updated']);
176 }
177
178 /**
179 * Test link creation with an existing link (duplicate URL). Should return a 409 HTTP error and the existing link.
180 */
181 public function testPostLinkDuplicate()
182 {
183 $link = [
184 'url' => 'mediagoblin.org/',
185 'title' => 'new entry',
186 'description' => 'shaare description',
187 'tags' => ['one', 'two'],
188 'private' => true,
189 ];
190 $env = Environment::mock([
191 'REQUEST_METHOD' => 'POST',
192 'CONTENT_TYPE' => 'application/json'
193 ]);
194
195 $request = Request::createFromEnvironment($env);
196 $request = $request->withParsedBody($link);
197 $response = $this->controller->postLink($request, new Response());
198
199 $this->assertEquals(409, $response->getStatusCode());
200 $data = json_decode((string) $response->getBody(), true);
201 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
202 $this->assertEquals(7, $data['id']);
203 $this->assertEquals('IuWvgA', $data['shorturl']);
204 $this->assertEquals('http://mediagoblin.org/', $data['url']);
205 $this->assertEquals('MediaGoblin', $data['title']);
206 $this->assertEquals('A free software media publishing platform #hashtagOther', $data['description']);
207 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
208 $this->assertEquals(false, $data['private']);
209 $this->assertEquals(
210 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
211 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
212 );
213 $this->assertEquals(
214 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
215 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
216 );
217 }
218}
diff --git a/tests/api/controllers/links/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php
new file mode 100644
index 00000000..8a562571
--- /dev/null
+++ b/tests/api/controllers/links/PutLinkTest.php
@@ -0,0 +1,222 @@
1<?php
2
3
4namespace Shaarli\Api\Controllers;
5
6
7use Shaarli\Config\ConfigManager;
8use Slim\Container;
9use Slim\Http\Environment;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13class PutLinkTest extends \PHPUnit_Framework_TestCase
14{
15 /**
16 * @var string datastore to test write operations
17 */
18 protected static $testDatastore = 'sandbox/datastore.php';
19
20 /**
21 * @var string datastore to test write operations
22 */
23 protected static $testHistory = 'sandbox/history.php';
24
25 /**
26 * @var ConfigManager instance
27 */
28 protected $conf;
29
30 /**
31 * @var \ReferenceLinkDB instance.
32 */
33 protected $refDB = null;
34
35 /**
36 * @var \History instance.
37 */
38 protected $history;
39
40 /**
41 * @var Container instance.
42 */
43 protected $container;
44
45 /**
46 * @var Links controller instance.
47 */
48 protected $controller;
49
50 /**
51 * Number of JSON field per link.
52 */
53 const NB_FIELDS_LINK = 9;
54
55 /**
56 * Before every test, instantiate a new Api with its config, plugins and links.
57 */
58 public function setUp()
59 {
60 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php');
61 $this->refDB = new \ReferenceLinkDB();
62 $this->refDB->write(self::$testDatastore);
63
64 $refHistory = new \ReferenceHistory();
65 $refHistory->write(self::$testHistory);
66 $this->history = new \History(self::$testHistory);
67
68 $this->container = new Container();
69 $this->container['conf'] = $this->conf;
70 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false);
71 $this->container['history'] = new \History(self::$testHistory);
72
73 $this->controller = new Links($this->container);
74
75 // Used by index_url().
76 $this->controller->getCi()['environment'] = [
77 'SERVER_NAME' => 'domain.tld',
78 'SERVER_PORT' => 80,
79 'SCRIPT_NAME' => '/',
80 ];
81 }
82
83 /**
84 * After every test, remove the test datastore.
85 */
86 public function tearDown()
87 {
88 @unlink(self::$testDatastore);
89 @unlink(self::$testHistory);
90 }
91
92 /**
93 * Test link update without value: reset the link to default values
94 */
95 public function testPutLinkMinimal()
96 {
97 $env = Environment::mock([
98 'REQUEST_METHOD' => 'PUT',
99 ]);
100 $id = '41';
101 $request = Request::createFromEnvironment($env);
102
103 $response = $this->controller->putLink($request, new Response(), ['id' => $id]);
104 $this->assertEquals(200, $response->getStatusCode());
105 $data = json_decode((string) $response->getBody(), true);
106 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
107 $this->assertEquals($id, $data['id']);
108 $this->assertEquals('WDWyig', $data['shorturl']);
109 $this->assertEquals('http://domain.tld/?WDWyig', $data['url']);
110 $this->assertEquals('?WDWyig', $data['title']);
111 $this->assertEquals('', $data['description']);
112 $this->assertEquals([], $data['tags']);
113 $this->assertEquals(false, $data['private']);
114 $this->assertEquals(
115 \DateTime::createFromFormat('Ymd_His', '20150310_114651'),
116 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
117 );
118 $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']));
119
120 $historyEntry = $this->history->getHistory()[0];
121 $this->assertEquals(\History::UPDATED, $historyEntry['event']);
122 $this->assertTrue(
123 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
124 );
125 $this->assertEquals($id, $historyEntry['id']);
126 }
127
128 /**
129 * Test link update with new values
130 */
131 public function testPutLinkWithValues()
132 {
133 $env = Environment::mock([
134 'REQUEST_METHOD' => 'PUT',
135 'CONTENT_TYPE' => 'application/json'
136 ]);
137 $id = 41;
138 $update = [
139 'url' => 'http://somewhere.else',
140 'title' => 'Le Cid',
141 'description' => 'Percé jusques au fond du cœur [...]',
142 'tags' => ['corneille', 'rodrigue'],
143 'private' => true,
144 ];
145 $request = Request::createFromEnvironment($env);
146 $request = $request->withParsedBody($update);
147
148 $response = $this->controller->putLink($request, new Response(), ['id' => $id]);
149 $this->assertEquals(200, $response->getStatusCode());
150 $data = json_decode((string) $response->getBody(), true);
151 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
152 $this->assertEquals($id, $data['id']);
153 $this->assertEquals('WDWyig', $data['shorturl']);
154 $this->assertEquals('http://somewhere.else', $data['url']);
155 $this->assertEquals('Le Cid', $data['title']);
156 $this->assertEquals('Percé jusques au fond du cœur [...]', $data['description']);
157 $this->assertEquals(['corneille', 'rodrigue'], $data['tags']);
158 $this->assertEquals(true, $data['private']);
159 $this->assertEquals(
160 \DateTime::createFromFormat('Ymd_His', '20150310_114651'),
161 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
162 );
163 $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']));
164 }
165
166 /**
167 * Test link update with an existing URL: 409 Conflict with the existing link as body
168 */
169 public function testPutLinkDuplicate()
170 {
171 $link = [
172 'url' => 'mediagoblin.org/',
173 'title' => 'new entry',
174 'description' => 'shaare description',
175 'tags' => ['one', 'two'],
176 'private' => true,
177 ];
178 $env = Environment::mock([
179 'REQUEST_METHOD' => 'PUT',
180 'CONTENT_TYPE' => 'application/json'
181 ]);
182
183 $request = Request::createFromEnvironment($env);
184 $request = $request->withParsedBody($link);
185 $response = $this->controller->putLink($request, new Response(), ['id' => 41]);
186
187 $this->assertEquals(409, $response->getStatusCode());
188 $data = json_decode((string) $response->getBody(), true);
189 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
190 $this->assertEquals(7, $data['id']);
191 $this->assertEquals('IuWvgA', $data['shorturl']);
192 $this->assertEquals('http://mediagoblin.org/', $data['url']);
193 $this->assertEquals('MediaGoblin', $data['title']);
194 $this->assertEquals('A free software media publishing platform #hashtagOther', $data['description']);
195 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
196 $this->assertEquals(false, $data['private']);
197 $this->assertEquals(
198 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
199 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
200 );
201 $this->assertEquals(
202 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
203 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
204 );
205 }
206
207 /**
208 * Test link update on non existent link => ApiLinkNotFoundException.
209 *
210 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
211 * @expectedExceptionMessage Link not found
212 */
213 public function testGetLink404()
214 {
215 $env = Environment::mock([
216 'REQUEST_METHOD' => 'PUT',
217 ]);
218 $request = Request::createFromEnvironment($env);
219
220 $this->controller->putLink($request, new Response(), ['id' => -1]);
221 }
222}