3 namespace Shaarli\Api\Controllers
;
5 use malkusch\lock\mutex\NoMutex
;
6 use Shaarli\Bookmark\Bookmark
;
7 use Shaarli\Bookmark\BookmarkFileService
;
8 use Shaarli\Config\ConfigManager
;
10 use Shaarli\Plugin\PluginManager
;
13 use Slim\Http\Environment
;
14 use Slim\Http\Request
;
15 use Slim\Http\Response
;
21 * Test POST Link REST API service.
23 * @package Shaarli\Api\Controllers
25 class PostLinkTest
extends TestCase
28 * @var string datastore to test write operations
30 protected static $testDatastore = 'sandbox/datastore.php';
33 * @var string datastore to test write operations
35 protected static $testHistory = 'sandbox/history.php';
38 * @var ConfigManager instance
43 * @var \ReferenceLinkDB instance.
45 protected $refDB = null;
48 * @var BookmarkFileService instance.
50 protected $bookmarkService;
53 * @var HistoryController instance.
58 * @var Container instance.
63 * @var Links controller instance.
65 protected $controller;
68 * Number of JSON field per link.
70 const NB_FIELDS_LINK
= 9;
73 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
75 protected function setUp(): void
77 $mutex = new NoMutex();
78 $this->conf
= new ConfigManager('tests/utils/config/configJson');
79 $this->conf
->set('resource.datastore', self
::$testDatastore);
80 $this->refDB
= new \
ReferenceLinkDB();
81 $this->refDB
->write(self
::$testDatastore);
82 $refHistory = new \
ReferenceHistory();
83 $refHistory->write(self
::$testHistory);
84 $this->history
= new History(self
::$testHistory);
85 $pluginManager = new PluginManager($this->conf
);
86 $this->bookmarkService
= new BookmarkFileService(
93 $this->container
= new Container();
94 $this->container
['conf'] = $this->conf
;
95 $this->container
['db'] = $this->bookmarkService
;
96 $this->container
['history'] = $this->history
;
98 $this->controller
= new Links($this->container
);
100 $mock = $this->createMock(Router
::class);
101 $mock->expects($this->any())
103 ->willReturn('/api/v1/bookmarks/1');
105 // affect @property-read... seems to work
106 $this->controller
->getCi()->router
= $mock;
108 // Used by index_url().
109 $this->controller
->getCi()['environment'] = [
110 'SERVER_NAME' => 'domain.tld',
112 'SCRIPT_NAME' => '/',
117 * After every test, remove the test datastore.
119 protected function tearDown(): void
121 @unlink(self
::$testDatastore);
122 @unlink(self
::$testHistory);
126 * Test link creation without any field: creates a blank note.
128 public function testPostLinkMinimal()
130 $env = Environment
::mock([
131 'REQUEST_METHOD' => 'POST',
134 $request = Request
::createFromEnvironment($env);
136 $response = $this->controller
->postLink($request, new Response());
137 $this->assertEquals(201, $response->getStatusCode());
138 $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
139 $data = json_decode((string) $response->getBody(), true);
140 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data));
141 $this->assertEquals(43, $data['id']);
142 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
143 $this->assertEquals('http://domain.tld/shaare/' . $data['shorturl'], $data['url']);
144 $this->assertEquals('/shaare/' . $data['shorturl'], $data['title']);
145 $this->assertEquals('', $data['description']);
146 $this->assertEquals([], $data['tags']);
147 $this->assertEquals(true, $data['private']);
149 new \
DateTime('5 seconds ago') < \DateTime
::createFromFormat(\DateTime
::ATOM
, $data['created'])
151 $this->assertEquals('', $data['updated']);
153 $historyEntry = $this->history
->getHistory()[0];
154 $this->assertEquals(History
::CREATED
, $historyEntry['event']);
156 (new \
DateTime())->add(\DateInterval
::createFromDateString('-5 seconds')) < $historyEntry['datetime']
158 $this->assertEquals(43, $historyEntry['id']);
162 * Test link creation with all available fields.
164 public function testPostLinkFull()
167 'url' => 'website.tld/test?foo=bar',
168 'title' => 'new entry',
169 'description' => 'shaare description',
170 'tags' => ['one', 'two'],
172 'created' => '2015-05-05T12:30:00+03:00',
173 'updated' => '2016-06-05T14:32:10+03:00',
175 $env = Environment
::mock([
176 'REQUEST_METHOD' => 'POST',
177 'CONTENT_TYPE' => 'application/json'
180 $request = Request
::createFromEnvironment($env);
181 $request = $request->withParsedBody($link);
182 $response = $this->controller
->postLink($request, new Response());
184 $this->assertEquals(201, $response->getStatusCode());
185 $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
186 $data = json_decode((string) $response->getBody(), true);
187 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data));
188 $this->assertEquals(43, $data['id']);
189 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
190 $this->assertEquals('http://' . $link['url'], $data['url']);
191 $this->assertEquals($link['title'], $data['title']);
192 $this->assertEquals($link['description'], $data['description']);
193 $this->assertEquals($link['tags'], $data['tags']);
194 $this->assertEquals(true, $data['private']);
195 $this->assertSame($link['created'], $data['created']);
196 $this->assertSame($link['updated'], $data['updated']);
200 * Test link creation with an existing link (duplicate URL). Should return a 409 HTTP error and the existing link.
202 public function testPostLinkDuplicate()
205 'url' => 'mediagoblin.org/',
206 'title' => 'new entry',
207 'description' => 'shaare description',
208 'tags' => ['one', 'two'],
211 $env = Environment
::mock([
212 'REQUEST_METHOD' => 'POST',
213 'CONTENT_TYPE' => 'application/json'
216 $request = Request
::createFromEnvironment($env);
217 $request = $request->withParsedBody($link);
218 $response = $this->controller
->postLink($request, new Response());
220 $this->assertEquals(409, $response->getStatusCode());
221 $data = json_decode((string) $response->getBody(), true);
222 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data));
223 $this->assertEquals(7, $data['id']);
224 $this->assertEquals('IuWvgA', $data['shorturl']);
225 $this->assertEquals('http://mediagoblin.org/', $data['url']);
226 $this->assertEquals('MediaGoblin', $data['title']);
227 $this->assertEquals('A free software media publishing platform #hashtagOther', $data['description']);
228 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
229 $this->assertEquals(false, $data['private']);
231 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20130614_184135'),
232 \DateTime
::createFromFormat(\DateTime
::ATOM
, $data['created'])
235 \DateTime
::createFromFormat(Bookmark
::LINK_DATE_FORMAT
, '20130615_184230'),
236 \DateTime
::createFromFormat(\DateTime
::ATOM
, $data['updated'])
241 * Test link creation with a tag string provided
243 public function testPostLinkWithTagString(): void
248 $env = Environment
::mock([
249 'REQUEST_METHOD' => 'POST',
250 'CONTENT_TYPE' => 'application/json'
253 $request = Request
::createFromEnvironment($env);
254 $request = $request->withParsedBody($link);
255 $response = $this->controller
->postLink($request, new Response());
257 $this->assertEquals(201, $response->getStatusCode());
258 $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
259 $data = json_decode((string) $response->getBody(), true);
260 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data));
261 $this->assertEquals(['one', 'two'], $data['tags']);
265 * Test link creation with a tag string provided
267 public function testPostLinkWithTagString2(): void
270 'tags' => ['one two'],
272 $env = Environment
::mock([
273 'REQUEST_METHOD' => 'POST',
274 'CONTENT_TYPE' => 'application/json'
277 $request = Request
::createFromEnvironment($env);
278 $request = $request->withParsedBody($link);
279 $response = $this->controller
->postLink($request, new Response());
281 $this->assertEquals(201, $response->getStatusCode());
282 $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
283 $data = json_decode((string) $response->getBody(), true);
284 $this->assertEquals(self
::NB_FIELDS_LINK
, count($data));
285 $this->assertEquals(['one', 'two'], $data['tags']);