Browse Source

Replace Response objects with PSR-7 response messages; improves #53

While the test suite passes, this commit yields a broken server: replacing ad hoc request objectss with PSR-7 ones is still required, as is emission of PSR-7 responses. Both will come in subsequent commits, with tests

Diactoros was chosen specifically because it includes facilities for emitting responses, something which is awkward to test. The end of this refactoring should see both the Response and Request classes disappear, and the general REST class fully covered (as well as any speculative additions to AbstractHanlder).
J. King 11 months ago
parent
commit
9eadd602bd

+ 2
- 1
composer.json View File

@@ -25,7 +25,8 @@
25 25
         "fguillot/picofeed": ">=0.1.31",
26 26
         "hosteurope/password-generator": "^1.0",
27 27
         "docopt/docopt": "^1.0",
28
-        "jkingweb/druuid": "^3.0"
28
+        "jkingweb/druuid": "^3.0",
29
+        "zendframework/zend-diactoros": "^1.6"
29 30
     },
30 31
     "require-dev": {
31 32
         "bamarni/composer-bin-plugin": "*"

+ 103
- 1
composer.lock View File

@@ -4,7 +4,7 @@
4 4
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 5
         "This file is @generated automatically"
6 6
     ],
7
-    "content-hash": "8a3c7ff23f125a5fa3dac2e6a7244a90",
7
+    "content-hash": "7d381fa958169b7079c1d3c5b911f3bd",
8 8
     "packages": [
9 9
         {
10 10
             "name": "docopt/docopt",
@@ -190,6 +190,108 @@
190 190
             ],
191 191
             "time": "2017-02-09T14:17:01+00:00"
192 192
         },
193
+        {
194
+            "name": "psr/http-message",
195
+            "version": "1.0.1",
196
+            "source": {
197
+                "type": "git",
198
+                "url": "https://github.com/php-fig/http-message.git",
199
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
200
+            },
201
+            "dist": {
202
+                "type": "zip",
203
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
204
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
205
+                "shasum": ""
206
+            },
207
+            "require": {
208
+                "php": ">=5.3.0"
209
+            },
210
+            "type": "library",
211
+            "extra": {
212
+                "branch-alias": {
213
+                    "dev-master": "1.0.x-dev"
214
+                }
215
+            },
216
+            "autoload": {
217
+                "psr-4": {
218
+                    "Psr\\Http\\Message\\": "src/"
219
+                }
220
+            },
221
+            "notification-url": "https://packagist.org/downloads/",
222
+            "license": [
223
+                "MIT"
224
+            ],
225
+            "authors": [
226
+                {
227
+                    "name": "PHP-FIG",
228
+                    "homepage": "http://www.php-fig.org/"
229
+                }
230
+            ],
231
+            "description": "Common interface for HTTP messages",
232
+            "homepage": "https://github.com/php-fig/http-message",
233
+            "keywords": [
234
+                "http",
235
+                "http-message",
236
+                "psr",
237
+                "psr-7",
238
+                "request",
239
+                "response"
240
+            ],
241
+            "time": "2016-08-06T14:39:51+00:00"
242
+        },
243
+        {
244
+            "name": "zendframework/zend-diactoros",
245
+            "version": "1.6.1",
246
+            "source": {
247
+                "type": "git",
248
+                "url": "https://github.com/zendframework/zend-diactoros.git",
249
+                "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877"
250
+            },
251
+            "dist": {
252
+                "type": "zip",
253
+                "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/c8664b92a6d5bc229e48b0923486c097e45a7877",
254
+                "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877",
255
+                "shasum": ""
256
+            },
257
+            "require": {
258
+                "php": "^5.6 || ^7.0",
259
+                "psr/http-message": "^1.0"
260
+            },
261
+            "provide": {
262
+                "psr/http-message-implementation": "1.0"
263
+            },
264
+            "require-dev": {
265
+                "ext-dom": "*",
266
+                "ext-libxml": "*",
267
+                "phpunit/phpunit": "^5.7.16 || ^6.0.8",
268
+                "zendframework/zend-coding-standard": "~1.0"
269
+            },
270
+            "type": "library",
271
+            "extra": {
272
+                "branch-alias": {
273
+                    "dev-master": "1.6-dev",
274
+                    "dev-develop": "1.7-dev"
275
+                }
276
+            },
277
+            "autoload": {
278
+                "psr-4": {
279
+                    "Zend\\Diactoros\\": "src/"
280
+                }
281
+            },
282
+            "notification-url": "https://packagist.org/downloads/",
283
+            "license": [
284
+                "BSD-2-Clause"
285
+            ],
286
+            "description": "PSR HTTP Message implementations",
287
+            "homepage": "https://github.com/zendframework/zend-diactoros",
288
+            "keywords": [
289
+                "http",
290
+                "psr",
291
+                "psr-7"
292
+            ],
293
+            "time": "2017-10-12T15:24:51+00:00"
294
+        },
193 295
         {
194 296
             "name": "zendframework/zendxml",
195 297
             "version": "1.0.2",

+ 1
- 1
lib/REST.php View File

@@ -48,7 +48,7 @@ class REST {
48 48
     public function __construct() {
49 49
     }
50 50
 
51
-    public function dispatch(REST\Request $req = null): REST\Response {
51
+    public function dispatch(REST\Request $req = null): \Psr\Http\Message\ResponseInterface {
52 52
         if ($req===null) {
53 53
             $req = new REST\Request();
54 54
         }

+ 1
- 1
lib/REST/AbstractHandler.php View File

@@ -11,7 +11,7 @@ use JKingWeb\Arsse\Misc\ValueInfo;
11 11
 
12 12
 abstract class AbstractHandler implements Handler {
13 13
     abstract public function __construct();
14
-    abstract public function dispatch(Request $req): Response;
14
+    abstract public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface;
15 15
 
16 16
     protected function fieldMapNames(array $data, array $map): array {
17 17
         $out = [];

+ 1
- 1
lib/REST/Handler.php View File

@@ -8,5 +8,5 @@ namespace JKingWeb\Arsse\REST;
8 8
 
9 9
 interface Handler {
10 10
     public function __construct();
11
-    public function dispatch(Request $req): Response;
11
+    public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface;
12 12
 }

+ 98
- 96
lib/REST/NextCloudNews/V1_2.php View File

@@ -15,7 +15,9 @@ use JKingWeb\Arsse\Misc\ValueInfo;
15 15
 use JKingWeb\Arsse\AbstractException;
16 16
 use JKingWeb\Arsse\Db\ExceptionInput;
17 17
 use JKingWeb\Arsse\Feed\Exception as FeedException;
18
-use JKingWeb\Arsse\REST\Response;
18
+use \Psr\Http\Message\ResponseInterface;
19
+use Zend\Diactoros\Response\JsonResponse as Response;
20
+use Zend\Diactoros\Response\EmptyResponse;
19 21
 
20 22
 class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
21 23
     const REALM = "NextCloud News API v1-2";
@@ -72,10 +74,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
72 74
     public function __construct() {
73 75
     }
74 76
 
75
-    public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
77
+    public function dispatch(\JKingWeb\Arsse\REST\Request $req): ResponseInterface {
76 78
         // try to authenticate
77 79
         if (!Arsse::$user->authHTTP()) {
78
-            return new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.self::REALM.'"']);
80
+            return new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.self::REALM.'"']);
79 81
         }
80 82
         // handle HTTP OPTIONS requests
81 83
         if ($req->method=="OPTIONS") {
@@ -85,12 +87,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
85 87
         if ($req->body) {
86 88
             // if the entity body is not JSON according to content type, return "415 Unsupported Media Type"
87 89
             if (!preg_match("<^application/json\b|^$>", $req->type)) {
88
-                return new Response(415, "", "", ['Accept: application/json']);
90
+                return new EmptyResponse(415, ['Accept' => "application/json"]);
89 91
             }
90 92
             $data = @json_decode($req->body, true);
91 93
             if (json_last_error() != \JSON_ERROR_NONE) {
92 94
                 // if the body could not be parsed as JSON, return "400 Bad Request"
93
-                return new Response(400);
95
+                return new EmptyResponse(400);
94 96
             }
95 97
         } else {
96 98
             $data = [];
@@ -101,12 +103,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
101 103
         try {
102 104
             $func = $this->chooseCall($req->paths, $req->method);
103 105
         } catch (Exception404 $e) {
104
-            return new Response(404);
106
+            return new EmptyResponse(404);
105 107
         } catch (Exception405 $e) {
106
-            return new Response(405, "", "", ["Allow: ".$e->getMessage()]);
108
+            return new EmptyResponse(405, ['Allow' => $e->getMessage()]);
107 109
         }
108 110
         if (!method_exists($this, $func)) {
109
-            return new Response(501); // @codeCoverageIgnore
111
+            return new EmptyResponse(501); // @codeCoverageIgnore
110 112
         }
111 113
         // dispatch
112 114
         try {
@@ -114,10 +116,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
114 116
             // @codeCoverageIgnoreStart
115 117
         } catch (Exception $e) {
116 118
             // if there was a REST exception return 400
117
-            return new Response(400);
119
+            return new EmptyResponse(400);
118 120
         } catch (AbstractException $e) {
119 121
             // if there was any other Arsse exception return 500
120
-            return new Response(500);
122
+            return new EmptyResponse(500);
121 123
         }
122 124
         // @codeCoverageIgnoreEnd
123 125
     }
@@ -242,7 +244,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
242 244
         return $article;
243 245
     }
244 246
 
245
-    protected function handleHTTPOptions(array $url): Response {
247
+    protected function handleHTTPOptions(array $url): ResponseInterface {
246 248
         // normalize the URL path
247 249
         $url = $this->normalizePath($url);
248 250
         if (isset($this->paths[$url])) {
@@ -252,81 +254,81 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
252 254
             if (in_array("GET", $allowed)) {
253 255
                 array_unshift($allowed, "HEAD");
254 256
             }
255
-            return new Response(204, "", "", [
256
-                "Allow: ".implode(",", $allowed),
257
-                "Accept: application/json",
257
+            return new EmptyResponse(204, [
258
+                'Allow'  => implode(",", $allowed),
259
+                'Accept' => "application/json",
258 260
             ]);
259 261
         } else {
260 262
             // if the path is not supported, return 404
261
-            return new Response(404);
263
+            return new EmptyResponse(404);
262 264
         }
263 265
     }
264 266
     
265 267
     // list folders
266
-    protected function folderList(array $url, array $data): Response {
268
+    protected function folderList(array $url, array $data): ResponseInterface {
267 269
         $folders = [];
268 270
         foreach (Arsse::$db->folderList(Arsse::$user->id, null, false) as $folder) {
269 271
             $folders[] = $this->folderTranslate($folder);
270 272
         }
271
-        return new Response(200, ['folders' => $folders]);
273
+        return new Response(['folders' => $folders]);
272 274
     }
273 275
 
274 276
     // create a folder
275
-    protected function folderAdd(array $url, array $data): Response {
277
+    protected function folderAdd(array $url, array $data): ResponseInterface {
276 278
         try {
277 279
             $folder = Arsse::$db->folderAdd(Arsse::$user->id, ['name' => $data['name']]);
278 280
         } catch (ExceptionInput $e) {
279 281
             switch ($e->getCode()) {
280 282
                 // folder already exists
281
-                case 10236: return new Response(409);
283
+                case 10236: return new EmptyResponse(409);
282 284
                 // folder name not acceptable
283 285
                 case 10231:
284
-                case 10232: return new Response(422);
286
+                case 10232: return new EmptyResponse(422);
285 287
                 // other errors related to input
286
-                default: return new Response(400); // @codeCoverageIgnore
288
+                default: return new EmptyResponse(400); // @codeCoverageIgnore
287 289
             }
288 290
         }
289 291
         $folder = $this->folderTranslate(Arsse::$db->folderPropertiesGet(Arsse::$user->id, $folder));
290
-        return new Response(200, ['folders' => [$folder]]);
292
+        return new Response(['folders' => [$folder]]);
291 293
     }
292 294
 
293 295
     // delete a folder
294
-    protected function folderRemove(array $url, array $data): Response {
296
+    protected function folderRemove(array $url, array $data): ResponseInterface {
295 297
         // perform the deletion
296 298
         try {
297 299
             Arsse::$db->folderRemove(Arsse::$user->id, (int) $url[1]);
298 300
         } catch (ExceptionInput $e) {
299 301
             // folder does not exist
300
-            return new Response(404);
302
+            return new EmptyResponse(404);
301 303
         }
302
-        return new Response(204);
304
+        return new EmptyResponse(204);
303 305
     }
304 306
 
305 307
     // rename a folder (also supports moving nesting folders, but this is not a feature of the API)
306
-    protected function folderRename(array $url, array $data): Response {
308
+    protected function folderRename(array $url, array $data): ResponseInterface {
307 309
         try {
308 310
             Arsse::$db->folderPropertiesSet(Arsse::$user->id, (int) $url[1], ['name' => $data['name']]);
309 311
         } catch (ExceptionInput $e) {
310 312
             switch ($e->getCode()) {
311 313
                 // folder does not exist
312
-                case 10239: return new Response(404);
314
+                case 10239: return new EmptyResponse(404);
313 315
                 // folder already exists
314
-                case 10236: return new Response(409);
316
+                case 10236: return new EmptyResponse(409);
315 317
                 // folder name not acceptable
316 318
                 case 10231:
317
-                case 10232: return new Response(422);
319
+                case 10232: return new EmptyResponse(422);
318 320
                 // other errors related to input
319
-                default: return new Response(400); // @codeCoverageIgnore
321
+                default: return new EmptyResponse(400); // @codeCoverageIgnore
320 322
             }
321 323
         }
322
-        return new Response(204);
324
+        return new EmptyResponse(204);
323 325
     }
324 326
 
325 327
     // mark all articles associated with a folder as read
326
-    protected function folderMarkRead(array $url, array $data): Response {
328
+    protected function folderMarkRead(array $url, array $data): ResponseInterface {
327 329
         if (!ValueInfo::id($data['newestItemId'])) {
328 330
             // if the item ID is invalid (i.e. not a positive integer), this is an error
329
-            return new Response(422);
331
+            return new EmptyResponse(422);
330 332
         }
331 333
         // build the context
332 334
         $c = new Context;
@@ -337,16 +339,16 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
337 339
             Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
338 340
         } catch (ExceptionInput $e) {
339 341
             // folder does not exist
340
-            return new Response(404);
342
+            return new EmptyResponse(404);
341 343
         }
342
-        return new Response(204);
344
+        return new EmptyResponse(204);
343 345
     }
344 346
     
345 347
     // return list of feeds which should be refreshed
346
-    protected function feedListStale(array $url, array $data): Response {
348
+    protected function feedListStale(array $url, array $data): ResponseInterface {
347 349
         // function requires admin rights per spec
348 350
         if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) {
349
-            return new Response(403);
351
+            return new EmptyResponse(403);
350 352
         }
351 353
         // list stale feeds which should be checked for updates
352 354
         $feeds = Arsse::$db->feedListStale();
@@ -355,42 +357,42 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
355 357
             // since in our implementation feeds don't belong the users, the 'userId' field will always be an empty string
356 358
             $out[] = ['id' => $feed, 'userId' => ""];
357 359
         }
358
-        return new Response(200, ['feeds' => $out]);
360
+        return new Response(['feeds' => $out]);
359 361
     }
360 362
     
361 363
     // refresh a feed
362
-    protected function feedUpdate(array $url, array $data): Response {
364
+    protected function feedUpdate(array $url, array $data): ResponseInterface {
363 365
         // function requires admin rights per spec
364 366
         if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) {
365
-            return new Response(403);
367
+            return new EmptyResponse(403);
366 368
         }
367 369
         try {
368 370
             Arsse::$db->feedUpdate($data['feedId']);
369 371
         } catch (ExceptionInput $e) {
370 372
             switch ($e->getCode()) {
371 373
                 case 10239: // feed does not exist
372
-                    return new Response(404);
374
+                    return new EmptyResponse(404);
373 375
                 case 10237: // feed ID invalid
374
-                    return new Response(422);
376
+                    return new EmptyResponse(422);
375 377
                 default: // other errors related to input
376
-                    return new Response(400); // @codeCoverageIgnore
378
+                    return new EmptyResponse(400); // @codeCoverageIgnore
377 379
             }
378 380
         }
379
-        return new Response(204);
381
+        return new EmptyResponse(204);
380 382
     }
381 383
 
382 384
     // add a new feed
383
-    protected function subscriptionAdd(array $url, array $data): Response {
385
+    protected function subscriptionAdd(array $url, array $data): ResponseInterface {
384 386
         // try to add the feed
385 387
         $tr = Arsse::$db->begin();
386 388
         try {
387 389
             $id = Arsse::$db->subscriptionAdd(Arsse::$user->id, (string) $data['url']);
388 390
         } catch (ExceptionInput $e) {
389 391
             // feed already exists
390
-            return new Response(409);
392
+            return new EmptyResponse(409);
391 393
         } catch (FeedException $e) {
392 394
             // feed could not be retrieved
393
-            return new Response(422);
395
+            return new EmptyResponse(422);
394 396
         }
395 397
         // if a folder was specified, move the feed to the correct folder; silently ignore errors
396 398
         if ($data['folderId']) {
@@ -408,11 +410,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
408 410
         if ($newest) {
409 411
             $out['newestItemId'] = $newest;
410 412
         }
411
-        return new Response(200, $out);
413
+        return new Response($out);
412 414
     }
413 415
     
414 416
     // return list of feeds for the logged-in user
415
-    protected function subscriptionList(array $url, array $data): Response {
417
+    protected function subscriptionList(array $url, array $data): ResponseInterface {
416 418
         $subs = Arsse::$db->subscriptionList(Arsse::$user->id);
417 419
         $out = [];
418 420
         foreach ($subs as $sub) {
@@ -424,43 +426,43 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
424 426
         if ($newest) {
425 427
             $out['newestItemId'] = $newest;
426 428
         }
427
-        return new Response(200, $out);
429
+        return new Response($out);
428 430
     }
429 431
 
430 432
     // delete a feed
431
-    protected function subscriptionRemove(array $url, array $data): Response {
433
+    protected function subscriptionRemove(array $url, array $data): ResponseInterface {
432 434
         try {
433 435
             Arsse::$db->subscriptionRemove(Arsse::$user->id, (int) $url[1]);
434 436
         } catch (ExceptionInput $e) {
435 437
             // feed does not exist
436
-            return new Response(404);
438
+            return new EmptyResponse(404);
437 439
         }
438
-        return new Response(204);
440
+        return new EmptyResponse(204);
439 441
     }
440 442
 
441 443
     // rename a feed
442
-    protected function subscriptionRename(array $url, array $data): Response {
444
+    protected function subscriptionRename(array $url, array $data): ResponseInterface {
443 445
         try {
444 446
             Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $url[1], ['title' => (string) $data['feedTitle']]);
445 447
         } catch (ExceptionInput $e) {
446 448
             switch ($e->getCode()) {
447 449
                 // subscription does not exist
448
-                case 10239: return new Response(404);
450
+                case 10239: return new EmptyResponse(404);
449 451
                 // name is invalid
450 452
                 case 10231:
451
-                case 10232: return new Response(422);
453
+                case 10232: return new EmptyResponse(422);
452 454
                 // other errors related to input
453
-                default: return new Response(400); // @codeCoverageIgnore
455
+                default: return new EmptyResponse(400); // @codeCoverageIgnore
454 456
             }
455 457
         }
456
-        return new Response(204);
458
+        return new EmptyResponse(204);
457 459
     }
458 460
 
459 461
     // move a feed to a folder
460
-    protected function subscriptionMove(array $url, array $data): Response {
462
+    protected function subscriptionMove(array $url, array $data): ResponseInterface {
461 463
         // if no folder is specified this is an error
462 464
         if (!isset($data['folderId'])) {
463
-            return new Response(422);
465
+            return new EmptyResponse(422);
464 466
         }
465 467
         // perform the move
466 468
         try {
@@ -468,22 +470,22 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
468 470
         } catch (ExceptionInput $e) {
469 471
             switch ($e->getCode()) {
470 472
                 case 10239: // subscription does not exist
471
-                    return new Response(404);
473
+                    return new EmptyResponse(404);
472 474
                 case 10235: // folder does not exist
473 475
                 case 10237: // folder ID is invalid
474
-                    return new Response(422);
476
+                    return new EmptyResponse(422);
475 477
                 default: // other errors related to input
476
-                    return new Response(400); // @codeCoverageIgnore
478
+                    return new EmptyResponse(400); // @codeCoverageIgnore
477 479
             }
478 480
         }
479
-        return new Response(204);
481
+        return new EmptyResponse(204);
480 482
     }
481 483
 
482 484
     // mark all articles associated with a subscription as read
483
-    protected function subscriptionMarkRead(array $url, array $data): Response {
485
+    protected function subscriptionMarkRead(array $url, array $data): ResponseInterface {
484 486
         if (!ValueInfo::id($data['newestItemId'])) {
485 487
             // if the item ID is invalid (i.e. not a positive integer), this is an error
486
-            return new Response(422);
488
+            return new EmptyResponse(422);
487 489
         }
488 490
         // build the context
489 491
         $c = new Context;
@@ -494,13 +496,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
494 496
             Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
495 497
         } catch (ExceptionInput $e) {
496 498
             // subscription does not exist
497
-            return new Response(404);
499
+            return new EmptyResponse(404);
498 500
         }
499
-        return new Response(204);
501
+        return new EmptyResponse(204);
500 502
     }
501 503
 
502 504
     // list articles and their properties
503
-    protected function articleList(array $url, array $data): Response {
505
+    protected function articleList(array $url, array $data): ResponseInterface {
504 506
         // set the context options supplied by the client
505 507
         $c = new Context;
506 508
         // set the batch size
@@ -553,32 +555,32 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
553 555
             $items = Arsse::$db->articleList(Arsse::$user->id, $c, Database::LIST_TYPICAL);
554 556
         } catch (ExceptionInput $e) {
555 557
             // ID of subscription or folder is not valid
556
-            return new Response(422);
558
+            return new EmptyResponse(422);
557 559
         }
558 560
         $out = [];
559 561
         foreach ($items as $item) {
560 562
             $out[] = $this->articleTranslate($item);
561 563
         }
562 564
         $out = ['items' => $out];
563
-        return new Response(200, $out);
565
+        return new Response($out);
564 566
     }
565 567
 
566 568
     // mark all articles as read
567
-    protected function articleMarkReadAll(array $url, array $data): Response {
569
+    protected function articleMarkReadAll(array $url, array $data): ResponseInterface {
568 570
         if (!ValueInfo::id($data['newestItemId'])) {
569 571
             // if the item ID is invalid (i.e. not a positive integer), this is an error
570
-            return new Response(422);
572
+            return new EmptyResponse(422);
571 573
         }
572 574
         // build the context
573 575
         $c = new Context;
574 576
         $c->latestEdition((int) $data['newestItemId']);
575 577
         // perform the operation
576 578
         Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c);
577
-        return new Response(204);
579
+        return new EmptyResponse(204);
578 580
     }
579 581
 
580 582
     // mark a single article as read
581
-    protected function articleMarkRead(array $url, array $data): Response {
583
+    protected function articleMarkRead(array $url, array $data): ResponseInterface {
582 584
         // initialize the matching context
583 585
         $c = new Context;
584 586
         $c->edition((int) $url[1]);
@@ -588,13 +590,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
588 590
             Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c);
589 591
         } catch (ExceptionInput $e) {
590 592
             // ID is not valid
591
-            return new Response(404);
593
+            return new EmptyResponse(404);
592 594
         }
593
-        return new Response(204);
595
+        return new EmptyResponse(204);
594 596
     }
595 597
 
596 598
     // mark a single article as read
597
-    protected function articleMarkStarred(array $url, array $data): Response {
599
+    protected function articleMarkStarred(array $url, array $data): ResponseInterface {
598 600
         // initialize the matching context
599 601
         $c = new Context;
600 602
         $c->article((int) $url[2]);
@@ -604,13 +606,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
604 606
             Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c);
605 607
         } catch (ExceptionInput $e) {
606 608
             // ID is not valid
607
-            return new Response(404);
609
+            return new EmptyResponse(404);
608 610
         }
609
-        return new Response(204);
611
+        return new EmptyResponse(204);
610 612
     }
611 613
 
612 614
     // mark an array of articles as read
613
-    protected function articleMarkReadMulti(array $url, array $data): Response {
615
+    protected function articleMarkReadMulti(array $url, array $data): ResponseInterface {
614 616
         // determine whether to mark read or unread
615 617
         $set = ($url[1]=="read");
616 618
         // initialize the matching context
@@ -620,11 +622,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
620 622
             Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c);
621 623
         } catch (ExceptionInput $e) {
622 624
         }
623
-        return new Response(204);
625
+        return new EmptyResponse(204);
624 626
     }
625 627
 
626 628
     // mark an array of articles as starred
627
-    protected function articleMarkStarredMulti(array $url, array $data): Response {
629
+    protected function articleMarkStarredMulti(array $url, array $data): ResponseInterface {
628 630
         // determine whether to mark starred or unstarred
629 631
         $set = ($url[1]=="star");
630 632
         // initialize the matching context
@@ -634,10 +636,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
634 636
             Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c);
635 637
         } catch (ExceptionInput $e) {
636 638
         }
637
-        return new Response(204);
639
+        return new EmptyResponse(204);
638 640
     }
639 641
 
640
-    protected function userStatus(array $url, array $data): Response {
642
+    protected function userStatus(array $url, array $data): ResponseInterface {
641 643
         $data = Arsse::$user->propertiesGet(Arsse::$user->id, true);
642 644
         // construct the avatar structure, if an image is available
643 645
         if (isset($data['avatar'])) {
@@ -655,37 +657,37 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler {
655 657
             'lastLoginTimestamp' => time(),
656 658
             'avatar' => $avatar,
657 659
         ];
658
-        return new Response(200, $out);
660
+        return new Response($out);
659 661
     }
660 662
 
661
-    protected function cleanupBefore(array $url, array $data): Response {
663
+    protected function cleanupBefore(array $url, array $data): ResponseInterface {
662 664
         // function requires admin rights per spec
663 665
         if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) {
664
-            return new Response(403);
666
+            return new EmptyResponse(403);
665 667
         }
666 668
         Service::cleanupPre();
667
-        return new Response(204);
669
+        return new EmptyResponse(204);
668 670
     }
669 671
 
670
-    protected function cleanupAfter(array $url, array $data): Response {
672
+    protected function cleanupAfter(array $url, array $data): ResponseInterface {
671 673
         // function requires admin rights per spec
672 674
         if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) {
673
-            return new Response(403);
675
+            return new EmptyResponse(403);
674 676
         }
675 677
         Service::cleanupPost();
676
-        return new Response(204);
678
+        return new EmptyResponse(204);
677 679
     }
678 680
 
679 681
     // return the server version
680
-    protected function serverVersion(array $url, array $data): Response {
681
-        return new Response(200, [
682
+    protected function serverVersion(array $url, array $data): ResponseInterface {
683
+        return new Response([
682 684
             'version' => self::VERSION,
683 685
             'arsse_version' => Arsse::VERSION,
684 686
         ]);
685 687
     }
686 688
 
687
-    protected function serverStatus(array $url, array $data): Response {
688
-        return new Response(200, [
689
+    protected function serverStatus(array $url, array $data): ResponseInterface {
690
+        return new Response([
689 691
             'version' => self::VERSION,
690 692
             'arsse_version' => Arsse::VERSION,
691 693
             'warnings' => [

+ 7
- 6
lib/REST/NextCloudNews/Versions.php View File

@@ -6,22 +6,23 @@
6 6
 declare(strict_types=1);
7 7
 namespace JKingWeb\Arsse\REST\NextCloudNews;
8 8
 
9
-use JKingWeb\Arsse\REST\Response;
9
+use Zend\Diactoros\Response\JsonResponse as Response;
10
+use Zend\Diactoros\Response\EmptyResponse;
10 11
 
11 12
 class Versions implements \JKingWeb\Arsse\REST\Handler {
12 13
     public function __construct() {
13 14
     }
14 15
 
15
-    public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
16
+    public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface {
16 17
         if (!preg_match("<^/?$>", $req->path)) {
17 18
             // if the request path is an empty string or just a slash, the client is probably trying a version we don't support
18
-            return new Response(404);
19
+            return new EmptyResponse(404);
19 20
         } elseif ($req->method=="OPTIONS") {
20 21
             // if the request method is OPTIONS, respond accordingly
21
-            return new Response(204, "", "", ["Allow: HEAD,GET"]);
22
+            return new EmptyResponse(204, ['Allow' => "HEAD,GET"]);
22 23
         } elseif ($req->method != "GET") {
23 24
             // if a method other than GET was used, this is an error
24
-            return new Response(405, "", "", ["Allow: HEAD,GET"]);
25
+            return new EmptyResponse(405, ['Allow' => "HEAD,GET"]);
25 26
         } else {
26 27
             // otherwise return the supported versions
27 28
             $out = [
@@ -29,7 +30,7 @@ class Versions implements \JKingWeb\Arsse\REST\Handler {
29 30
                     'v1-2',
30 31
                 ]
31 32
             ];
32
-            return new Response(200, $out);
33
+            return new Response($out);
33 34
         }
34 35
     }
35 36
 }

+ 12
- 11
lib/REST/TinyTinyRSS/API.php View File

@@ -19,7 +19,8 @@ use JKingWeb\Arsse\ExceptionType;
19 19
 use JKingWeb\Arsse\Db\ExceptionInput;
20 20
 use JKingWeb\Arsse\Db\ResultEmpty;
21 21
 use JKingWeb\Arsse\Feed\Exception as FeedException;
22
-use JKingWeb\Arsse\REST\Response;
22
+use Zend\Diactoros\Response\JsonResponse as Response;
23
+use Zend\Diactoros\Response\EmptyResponse;
23 24
 
24 25
 class API extends \JKingWeb\Arsse\REST\AbstractHandler {
25 26
     const LEVEL = 14;           // emulated API level
@@ -88,23 +89,23 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
88 89
     public function __construct() {
89 90
     }
90 91
 
91
-    public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
92
+    public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface {
92 93
         if (!preg_match("<^(?:/(?:index\.php)?)?$>", $req->path)) {
93 94
             // reject paths other than the index
94
-            return new Response(404);
95
+            return new EmptyResponse(404);
95 96
         }
96 97
         if ($req->method=="OPTIONS") {
97 98
             // respond to OPTIONS rquests; the response is a fib, as we technically accept any type or method
98
-            return new Response(204, "", "", [
99
-                "Allow: POST",
100
-                "Accept: application/json, text/json",
99
+            return new EmptyResponse(204, [
100
+                'Allow'  => "POST",
101
+                'Accept' => "application/json, text/json",
101 102
             ]);
102 103
         }
103 104
         if ($req->body) {
104 105
             // only JSON entities are allowed, but Content-Type is ignored, as is request method
105 106
             $data = @json_decode($req->body, true);
106 107
             if (json_last_error() != \JSON_ERROR_NONE || !is_array($data)) {
107
-                return new Response(200, self::FATAL_ERR);
108
+                return new Response(self::FATAL_ERR);
108 109
             }
109 110
             try {
110 111
                 // normalize input
@@ -123,23 +124,23 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler {
123 124
                     // TT-RSS operations are case-insensitive by dint of PHP method names being case-insensitive; this will only trigger if the method really doesn't exist
124 125
                     throw new Exception("UNKNOWN_METHOD", ['method' => $data['op']]);
125 126
                 }
126
-                return new Response(200, [
127
+                return new Response([
127 128
                     'seq' => $data['seq'],
128 129
                     'status' => 0,
129 130
                     'content' => $this->$method($data),
130 131
                 ]);
131 132
             } catch (Exception $e) {
132
-                return new Response(200, [
133
+                return new Response([
133 134
                     'seq' => $data['seq'],
134 135
                     'status' => 1,
135 136
                     'content' => $e->getData(),
136 137
                 ]);
137 138
             } catch (AbstractException $e) {
138
-                return new Response(500);
139
+                return new EmptyResponse(500);
139 140
             }
140 141
         } else {
141 142
             // absence of a request body indicates an error
142
-            return new Response(200, self::FATAL_ERR);
143
+            return new Response(self::FATAL_ERR);
143 144
         }
144 145
     }
145 146
 

+ 4
- 4
lib/REST/TinyTinyRSS/Icon.php View File

@@ -7,16 +7,16 @@ declare(strict_types=1);
7 7
 namespace JKingWeb\Arsse\REST\TinyTinyRSS;
8 8
 
9 9
 use JKingWeb\Arsse\Arsse;
10
-use JKingWeb\Arsse\REST\Response;
10
+use Zend\Diactoros\Response\EmptyResponse as Response;
11 11
 
12 12
 class Icon extends \JKingWeb\Arsse\REST\AbstractHandler {
13 13
     public function __construct() {
14 14
     }
15 15
 
16
-    public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response {
16
+    public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface {
17 17
         if ($req->method != "GET") {
18 18
             // only GET requests are allowed
19
-            return new Response(405, "", "", ["Allow: GET"]);
19
+            return new Response(405, ['Allow' => "GET"]);
20 20
         } elseif (!preg_match("<^(\d+)\.ico$>", $req->url, $match) || !((int) $match[1])) {
21 21
             return new Response(404);
22 22
         }
@@ -26,7 +26,7 @@ class Icon extends \JKingWeb\Arsse\REST\AbstractHandler {
26 26
             if (($pos = strpos($url, "\r")) !== false || ($pos = strpos($url, "\n")) !== false) {
27 27
                 $url = substr($url, 0, $pos);
28 28
             }
29
-            return new Response(301, "", "", ["Location: $url"]);
29
+            return new Response(301, ['Location' => $url]);
30 30
         } else {
31 31
             return new Response(404);
32 32
         }

+ 79
- 83
tests/cases/REST/NextCloudNews/TestV1_2.php View File

@@ -12,13 +12,14 @@ use JKingWeb\Arsse\User;
12 12
 use JKingWeb\Arsse\Database;
13 13
 use JKingWeb\Arsse\Service;
14 14
 use JKingWeb\Arsse\REST\Request;
15
-use JKingWeb\Arsse\REST\Response;
16 15
 use JKingWeb\Arsse\Test\Result;
17 16
 use JKingWeb\Arsse\Misc\Date;
18 17
 use JKingWeb\Arsse\Misc\Context;
19 18
 use JKingWeb\Arsse\Db\ExceptionInput;
20 19
 use JKingWeb\Arsse\Db\Transaction;
21 20
 use JKingWeb\Arsse\REST\NextCloudNews\V1_2;
21
+use Zend\Diactoros\Response\JsonResponse as Response;
22
+use Zend\Diactoros\Response\EmptyResponse;
22 23
 use Phake;
23 24
 
24 25
 /** @covers \JKingWeb\Arsse\REST\NextCloudNews\V1_2<extended> */
@@ -317,14 +318,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
317 318
         $this->clearData();
318 319
     }
319 320
 
320
-    protected function assertResponse(Response $exp, Response $act, string $text = null) {
321
-        $this->assertEquals($exp, $act, $text);
322
-        $this->assertSame($exp->payload, $act->payload, $text);
323
-    }
324
-
325 321
     public function testSendAuthenticationChallenge() {
326 322
         Phake::when(Arsse::$user)->authHTTP->thenReturn(false);
327
-        $exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.V1_2::REALM.'"']);
323
+        $exp = new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.V1_2::REALM.'"']);
328 324
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/")));
329 325
     }
330 326
 
@@ -361,12 +357,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
361 357
             ],
362 358
         ];
363 359
         foreach ($errs[404] as $req) {
364
-            $exp = new Response(404);
360
+            $exp = new EmptyResponse(404);
365 361
             list($method, $path) = $req;
366 362
             $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 404.");
367 363
         }
368 364
         foreach ($errs[405] as $allow => $cases) {
369
-            $exp = new Response(405, "", "", ['Allow: '.$allow]);
365
+            $exp = new EmptyResponse(405, ['Allow' => $allow]);
370 366
             foreach ($cases as $req) {
371 367
                 list($method, $path) = $req;
372 368
                 $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 405.");
@@ -375,29 +371,29 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
375 371
     }
376 372
 
377 373
     public function testRespondToInvalidInputTypes() {
378
-        $exp = new Response(415, "", "", ['Accept: application/json']);
374
+        $exp = new EmptyResponse(415, ['Accept' => "application/json"]);
379 375
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '<data/>', 'application/xml')));
380
-        $exp = new Response(400);
376
+        $exp = new EmptyResponse(400);
381 377
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '<data/>', 'application/json')));
382 378
     }
383 379
 
384 380
     public function testRespondToOptionsRequests() {
385
-        $exp = new Response(204, "", "", [
386
-            "Allow: HEAD,GET,POST",
387
-            "Accept: application/json",
381
+        $exp = new EmptyResponse(204, [
382
+            'Allow'  => "HEAD,GET,POST",
383
+            'Accept' => "application/json",
388 384
         ]);
389 385
         $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds")));
390
-        $exp = new Response(204, "", "", [
391
-            "Allow: DELETE",
392
-            "Accept: application/json",
386
+        $exp = new EmptyResponse(204, [
387
+            'Allow'  => "DELETE",
388
+            'Accept' => "application/json",
393 389
         ]);
394 390
         $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds/2112")));
395
-        $exp = new Response(204, "", "", [
396
-            "Allow: HEAD,GET",
397
-            "Accept: application/json",
391
+        $exp = new EmptyResponse(204, [
392
+            'Allow'  => "HEAD,GET",
393
+            'Accept' => "application/json",
398 394
         ]);
399 395
         $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/user")));
400
-        $exp = new Response(404);
396
+        $exp = new EmptyResponse(404);
401 397
         $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/invalid/path")));
402 398
     }
403 399
 
@@ -411,9 +407,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
411 407
             ['id' => 12, 'name' => "Hardware"],
412 408
         ];
413 409
         Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([]))->thenReturn(new Result($list));
414
-        $exp = new Response(200, ['folders' => []]);
410
+        $exp = new Response(['folders' => []]);
415 411
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders")));
416
-        $exp = new Response(200, ['folders' => $out]);
412
+        $exp = new Response(['folders' => $out]);
417 413
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders")));
418 414
     }
419 415
 
@@ -441,33 +437,33 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
441 437
         Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing"));
442 438
         Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => " "])->thenThrow(new ExceptionInput("whitespace"));
443 439
         // correctly add two folders, using different means
444
-        $exp = new Response(200, ['folders' => [$out[0]]]);
440
+        $exp = new Response(['folders' => [$out[0]]]);
445 441
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json')));
446
-        $exp = new Response(200, ['folders' => [$out[1]]]);
442
+        $exp = new Response(['folders' => [$out[1]]]);
447 443
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Hardware")));
448 444
         Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[0]);
449 445
         Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[1]);
450 446
         Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1);
451 447
         Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 2);
452 448
         // test bad folder names
453
-        $exp = new Response(422);
449
+        $exp = new EmptyResponse(422);
454 450
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders")));
455 451
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":""}', 'application/json')));
456 452
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":" "}', 'application/json')));
457 453
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":{}}', 'application/json')));
458 454
         // try adding the same two folders again
459
-        $exp = new Response(409);
455
+        $exp = new EmptyResponse(409);
460 456
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Software")));
461
-        $exp = new Response(409);
457
+        $exp = new EmptyResponse(409);
462 458
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[1]), 'application/json')));
463 459
     }
464 460
 
465 461
     public function testRemoveAFolder() {
466 462
         Phake::when(Arsse::$db)->folderRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing"));
467
-        $exp = new Response(204);
463
+        $exp = new EmptyResponse(204);
468 464
         $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
469 465
         // fail on the second invocation because it no longer exists
470
-        $exp = new Response(404);
466
+        $exp = new EmptyResponse(404);
471 467
         $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
472 468
         Phake::verify(Arsse::$db, Phake::times(2))->folderRemove(Arsse::$user->id, 1);
473 469
     }
@@ -486,22 +482,22 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
486 482
         Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 1, $in[3])->thenThrow(new ExceptionInput("whitespace"));
487 483
         Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database
488 484
         Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 3, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // folder ID 3 does not exist
489
-        $exp = new Response(204);
485
+        $exp = new EmptyResponse(204);
490 486
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json')));
491
-        $exp = new Response(409);
487
+        $exp = new EmptyResponse(409);
492 488
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/2", json_encode($in[1]), 'application/json')));
493
-        $exp = new Response(422);
489
+        $exp = new EmptyResponse(422);
494 490
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[2]), 'application/json')));
495
-        $exp = new Response(422);
491
+        $exp = new EmptyResponse(422);
496 492
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[3]), 'application/json')));
497
-        $exp = new Response(422);
493
+        $exp = new EmptyResponse(422);
498 494
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[4]), 'application/json')));
499
-        $exp = new Response(404);
495
+        $exp = new EmptyResponse(404);
500 496
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/3", json_encode($in[0]), 'application/json')));
501 497
     }
502 498
 
503 499
     public function testRetrieveServerVersion() {
504
-        $exp = new Response(200, [
500
+        $exp = new Response([
505 501
             'version' => V1_2::VERSION,
506 502
             'arsse_version' => Arsse::VERSION,
507 503
             ]);
@@ -521,9 +517,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
521 517
         Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([]))->thenReturn(new Result($this->feeds['db']));
522 518
         Phake::when(Arsse::$db)->articleStarred(Arsse::$user->id)->thenReturn(['total' => 0])->thenReturn(['total' => 5]);
523 519
         Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id)->thenReturn(0)->thenReturn(4758915);
524
-        $exp = new Response(200, $exp1);
520
+        $exp = new Response($exp1);
525 521
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds")));
526
-        $exp = new Response(200, $exp2);
522
+        $exp = new Response($exp2);
527 523
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds")));
528 524
     }
529 525
 
@@ -556,31 +552,31 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
556 552
         // set up a mock for a bad feed which succeeds the second time
557 553
         Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.net/news.atom")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.net/news.atom", new \PicoFeed\Client\InvalidUrlException()))->thenReturn(47);
558 554
         // add the subscriptions
559
-        $exp = new Response(200, $out[0]);
555
+        $exp = new Response($out[0]);
560 556
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json')));
561
-        $exp = new Response(200, $out[1]);
557
+        $exp = new Response($out[1]);
562 558
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json')));
563 559
         // try to add them a second time
564
-        $exp = new Response(409);
560
+        $exp = new EmptyResponse(409);
565 561
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json')));
566 562
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json')));
567 563
         // try to add a bad feed
568
-        $exp = new Response(422);
564
+        $exp = new EmptyResponse(422);
569 565
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[2]), 'application/json')));
570 566
         // try again (this will succeed), with an invalid folder ID
571
-        $exp = new Response(200, $out[2]);
567
+        $exp = new Response($out[2]);
572 568
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[3]), 'application/json')));
573 569
         // try to add no feed
574
-        $exp = new Response(422);
570
+        $exp = new EmptyResponse(422);
575 571
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[4]), 'application/json')));
576 572
     }
577 573
 
578 574
     public function testRemoveASubscription() {
579 575
         Phake::when(Arsse::$db)->subscriptionRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing"));
580
-        $exp = new Response(204);
576
+        $exp = new EmptyResponse(204);
581 577
         $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1")));
582 578
         // fail on the second invocation because it no longer exists
583
-        $exp = new Response(404);
579
+        $exp = new EmptyResponse(404);
584 580
         $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1")));
585 581
         Phake::verify(Arsse::$db, Phake::times(2))->subscriptionRemove(Arsse::$user->id, 1);
586 582
     }
@@ -599,17 +595,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
599 595
         Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, ['folder' => 2112])->thenThrow(new ExceptionInput("idMissing")); // folder does not exist
600 596
         Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, ['folder' =>   -1])->thenThrow(new ExceptionInput("typeViolation")); // folder is invalid
601 597
         Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // subscription does not exist
602
-        $exp = new Response(204);
598
+        $exp = new EmptyResponse(204);
603 599
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[0]), 'application/json')));
604
-        $exp = new Response(204);
600
+        $exp = new EmptyResponse(204);
605 601
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[1]), 'application/json')));
606
-        $exp = new Response(422);
602
+        $exp = new EmptyResponse(422);
607 603
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[2]), 'application/json')));
608
-        $exp = new Response(404);
604
+        $exp = new EmptyResponse(404);
609 605
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/move", json_encode($in[3]), 'application/json')));
610
-        $exp = new Response(422);
606
+        $exp = new EmptyResponse(422);
611 607
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[4]), 'application/json')));
612
-        $exp = new Response(422);
608
+        $exp = new EmptyResponse(422);
613 609
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[5]), 'application/json')));
614 610
     }
615 611
 
@@ -629,17 +625,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
629 625
         Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' =>    ""]))->thenThrow(new ExceptionInput("missing"));
630 626
         Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new ExceptionInput("missing"));
631 627
         Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing"));
632
-        $exp = new Response(422);
628
+        $exp = new EmptyResponse(422);
633 629
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json')));
634
-        $exp = new Response(204);
630
+        $exp = new EmptyResponse(204);
635 631
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[1]), 'application/json')));
636
-        $exp = new Response(422);
632
+        $exp = new EmptyResponse(422);
637 633
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[2]), 'application/json')));
638
-        $exp = new Response(422);
634
+        $exp = new EmptyResponse(422);
639 635
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[3]), 'application/json')));
640
-        $exp = new Response(404);
636
+        $exp = new EmptyResponse(404);
641 637
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/rename", json_encode($in[4]), 'application/json')));
642
-        $exp = new Response(422);
638
+        $exp = new EmptyResponse(422);
643 639
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[6]), 'application/json')));
644 640
     }
645 641
 
@@ -655,11 +651,11 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
655 651
             ],
656 652
         ];
657 653
         Phake::when(Arsse::$db)->feedListStale->thenReturn(array_column($out, "id"));
658
-        $exp = new Response(200, ['feeds' => $out]);
654
+        $exp = new Response(['feeds' => $out]);
659 655
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all")));
660 656
         // retrieving the list when not an admin fails
661 657
         Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
662
-        $exp = new Response(403);
658
+        $exp = new EmptyResponse(403);
663 659
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all")));
664 660
     }
665 661
 
@@ -674,17 +670,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
674 670
         Phake::when(Arsse::$db)->feedUpdate(42)->thenReturn(true);
675 671
         Phake::when(Arsse::$db)->feedUpdate(2112)->thenThrow(new ExceptionInput("subjectMissing"));
676 672
         Phake::when(Arsse::$db)->feedUpdate($this->lessThan(1))->thenThrow(new ExceptionInput("typeViolation"));
677
-        $exp = new Response(204);
673
+        $exp = new EmptyResponse(204);
678 674
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
679
-        $exp = new Response(404);
675
+        $exp = new EmptyResponse(404);
680 676
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[1]), 'application/json')));
681
-        $exp = new Response(422);
677
+        $exp = new EmptyResponse(422);
682 678
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[2]), 'application/json')));
683 679
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[3]), 'application/json')));
684 680
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[4]), 'application/json')));
685 681
         // updating a feed when not an admin fails
686 682
         Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
687
-        $exp = new Response(403);
683
+        $exp = new EmptyResponse(403);
688 684
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json')));
689 685
     }
690 686
 
@@ -710,12 +706,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
710 706
         Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(2112), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("idMissing"));
711 707
         Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->subscription(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation"));
712 708
         Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation"));
713
-        $exp = new Response(200, ['items' => $this->articles['rest']]);
709
+        $exp = new Response(['items' => $this->articles['rest']]);
714 710
         // check the contents of the response
715 711
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items"))); // first instance of base context
716 712
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items/updated"))); // second instance of base context
717 713
         // check error conditions
718
-        $exp = new Response(422);
714
+        $exp = new EmptyResponse(422);
719 715
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[0]), 'application/json')));
720 716
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[1]), 'application/json')));
721 717
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[2]), 'application/json')));
@@ -748,13 +744,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
748 744
         $in = json_encode(['newestItemId' => 2112]);
749 745
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(1)->latestEdition(2112))->thenReturn(42);
750 746
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // folder doesn't exist
751
-        $exp = new Response(204);
747
+        $exp = new EmptyResponse(204);
752 748
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read", $in, 'application/json')));
753 749
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=2112")));
754
-        $exp = new Response(422);
750
+        $exp = new EmptyResponse(422);
755 751
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read")));
756 752
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=ook")));
757
-        $exp = new Response(404);
753
+        $exp = new EmptyResponse(404);
758 754
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/42/read", $in, 'application/json')));
759 755
     }
760 756
 
@@ -763,13 +759,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
763 759
         $in = json_encode(['newestItemId' => 2112]);
764 760
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(1)->latestEdition(2112))->thenReturn(42);
765 761
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // subscription doesn't exist
766
-        $exp = new Response(204);
762
+        $exp = new EmptyResponse(204);
767 763
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read", $in, 'application/json')));
768 764
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=2112")));
769
-        $exp = new Response(422);
765
+        $exp = new EmptyResponse(422);
770 766
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read")));
771 767
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=ook")));
772
-        $exp = new Response(404);
768
+        $exp = new EmptyResponse(404);
773 769
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/read", $in, 'application/json')));
774 770
     }
775 771
 
@@ -777,10 +773,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
777 773
         $read = ['read' => true];
778 774
         $in = json_encode(['newestItemId' => 2112]);
779 775
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->latestEdition(2112))->thenReturn(42);
780
-        $exp = new Response(204);
776
+        $exp = new EmptyResponse(204);
781 777
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read", $in, 'application/json')));
782 778
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=2112")));
783
-        $exp = new Response(422);
779
+        $exp = new EmptyResponse(422);
784 780
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read")));
785 781
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=ook")));
786 782
     }
@@ -798,12 +794,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
798 794
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $star, (new Context)->article(2112))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist
799 795
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(4))->thenReturn(42);
800 796
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(1337))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist
801
-        $exp = new Response(204);
797
+        $exp = new EmptyResponse(204);
802 798
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/read")));
803 799
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/2/unread")));
804 800
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/3/star")));
805 801
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/4400/4/unstar")));
806
-        $exp = new Response(404);
802
+        $exp = new EmptyResponse(404);
807 803
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/42/read")));
808 804
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/47/unread")));
809 805
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/2112/star")));
@@ -829,7 +825,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
829 825
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), $this->anything())->thenReturn(42);
830 826
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
831 827
         Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples
832
-        $exp = new Response(204);
828
+        $exp = new EmptyResponse(204);
833 829
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple")));
834 830
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple")));
835 831
         $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple")));
@@ -882,29 +878,29 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
882 878
         ];
883 879
         $arr2['warnings']['improperlyConfiguredCron'] = true;
884 880
         $arr2['warnings']['incorrectDbCharset'] = true;
885
-        $exp = new Response(200, $arr1);
881
+        $exp = new Response($arr1);
886 882
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/status")));
887 883
     }
888 884
 
889 885
     public function testCleanUpBeforeUpdate() {
890 886
         Phake::when(Arsse::$db)->feedCleanup()->thenReturn(true);
891
-        $exp = new Response(204);
887
+        $exp = new EmptyResponse(204);
892 888
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update")));
893 889
         Phake::verify(Arsse::$db)->feedCleanup();
894 890
         // performing a cleanup when not an admin fails
895 891
         Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
896
-        $exp = new Response(403);
892
+        $exp = new EmptyResponse(403);
897 893
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update")));
898 894
     }
899 895
     
900 896
     public function testCleanUpAfterUpdate() {
901 897
         Phake::when(Arsse::$db)->articleCleanup()->thenReturn(true);
902
-        $exp = new Response(204);
898
+        $exp = new EmptyResponse(204);
903 899
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update")));
904 900
         Phake::verify(Arsse::$db)->articleCleanup();
905 901
         // performing a cleanup when not an admin fails
906 902
         Phake::when(Arsse::$user)->rightsGet->thenReturn(0);
907
-        $exp = new Response(403);
903
+        $exp = new EmptyResponse(403);
908 904
         $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update")));
909 905
     }
910 906
 }

+ 13
- 12
tests/cases/REST/NextCloudNews/TestVersions.php View File

@@ -8,7 +8,8 @@ namespace JKingWeb\Arsse\TestCase\REST\NextCloudNews;
8 8
 
9 9
 use JKingWeb\Arsse\REST\NextCloudNews\Versions;
10 10
 use JKingWeb\Arsse\REST\Request;
11
-use JKingWeb\Arsse\REST\Response;
11
+use Zend\Diactoros\Response\JsonResponse as Response;
12
+use Zend\Diactoros\Response\EmptyResponse;
12 13
 
13 14
 /** @covers \JKingWeb\Arsse\REST\NextCloudNews\Versions */
14 15
 class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest {
@@ -17,43 +18,43 @@ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest {
17 18
     }
18 19
 
19 20
     public function testFetchVersionList() {
20
-        $exp = new Response(200, ['apiLevels' => ['v1-2']]);
21
+        $exp = new Response(['apiLevels' => ['v1-2']]);
21 22
         $h = new Versions;
22 23
         $req = new Request("GET", "/");
23 24
         $res = $h->dispatch($req);
24
-        $this->assertEquals($exp, $res);
25
+        $this->assertResponse($exp, $res);
25 26
         $req = new Request("GET", "");
26 27
         $res = $h->dispatch($req);
27
-        $this->assertEquals($exp, $res);
28
+        $this->assertResponse($exp, $res);
28 29
         $req = new Request("GET", "/?id=1827");
29 30
         $res = $h->dispatch($req);
30
-        $this->assertEquals($exp, $res);
31
+        $this->assertResponse($exp, $res);
31 32
     }
32 33
 
33 34
     public function testRespondToOptionsRequest() {
34
-        $exp = new Response(204, "", "", ["Allow: HEAD,GET"]);
35
+        $exp = new EmptyResponse(204, ['Allow' => "HEAD,GET"]);
35 36
         $h = new Versions;
36 37
         $req = new Request("OPTIONS", "/");
37 38
         $res = $h->dispatch($req);
38
-        $this->assertEquals($exp, $res);
39
+        $this->assertResponse($exp, $res);
39 40
     }
40 41
 
41 42
     public function testUseIncorrectMethod() {
42
-        $exp = new Response(405, "", "", ["Allow: HEAD,GET"]);
43
+        $exp = new EmptyResponse(405, ['Allow' => "HEAD,GET"]);
43 44
         $h = new Versions;
44 45
         $req = new Request("POST", "/");
45 46
         $res = $h->dispatch($req);
46
-        $this->assertEquals($exp, $res);
47
+        $this->assertResponse($exp, $res);
47 48
     }
48 49
 
49 50
     public function testUseIncorrectPath() {
50
-        $exp = new Response(404);
51
+        $exp = new EmptyResponse(404);
51 52
         $h = new Versions;
52 53
         $req = new Request("GET", "/ook");
53 54
         $res = $h->dispatch($req);
54
-        $this->assertEquals($exp, $res);
55
+        $this->assertResponse($exp, $res);
55 56
         $req = new Request("OPTIONS", "/ook");
56 57
         $res = $h->dispatch($req);
57
-        $this->assertEquals($exp, $res);
58
+        $this->assertResponse($exp, $res);
58 59
     }
59 60
 }

+ 37
- 45
tests/cases/REST/TinyTinyRSS/TestAPI.php View File

@@ -12,13 +12,15 @@ use JKingWeb\Arsse\User;
12 12
 use JKingWeb\Arsse\Database;
13 13
 use JKingWeb\Arsse\Service;
14 14
 use JKingWeb\Arsse\REST\Request;
15
-use JKingWeb\Arsse\REST\Response;
16 15
 use JKingWeb\Arsse\Test\Result;
17 16
 use JKingWeb\Arsse\Misc\Date;
18 17
 use JKingWeb\Arsse\Misc\Context;
19 18
 use JKingWeb\Arsse\Db\ExceptionInput;
20 19
 use JKingWeb\Arsse\Db\Transaction;
21 20
 use JKingWeb\Arsse\REST\TinyTinyRSS\API;
21
+use Psr\Http\Message\ResponseInterface;
22
+use Zend\Diactoros\Response\JsonResponse as Response;
23
+use Zend\Diactoros\Response\EmptyResponse;
22 24
 use Phake;
23 25
 
24 26
 /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API<extended>
@@ -122,12 +124,12 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
122 124
 </section>
123 125
 LONG_STRING;
124 126
 
125
-    protected function req($data) : Response {
127
+    protected function req($data): ResponseInterface {
126 128
         return $this->h->dispatch(new Request("POST", "", json_encode($data)));
127 129
     }
128 130
     
129 131
     protected function respGood($content = null, $seq = 0): Response {
130
-        return new Response(200, [
132
+        return new Response([
131 133
             'seq' => $seq,
132 134
             'status' => 0,
133 135
             'content' => $content,
@@ -136,18 +138,13 @@ LONG_STRING;
136 138
 
137 139
     protected function respErr(string $msg, $content = [], $seq = 0): Response {
138 140
         $err = ['error' => $msg];
139
-        return new Response(200, [
141
+        return new Response([
140 142
             'seq' => $seq,
141 143
             'status' => 1,
142 144
             'content' => array_merge($err, $content, $err),
143 145
         ]);
144 146
     }
145 147
 
146
-    protected function assertResponse(Response $exp, Response $act, string $text = null) {
147
-        $this->assertEquals($exp, $act, $text);
148
-        $this->assertSame($exp->payload, $act->payload, $text);
149
-    }
150
-
151 148
     public function setUp() {
152 149
         $this->clearData();
153 150
         Arsse::$conf = new Conf();
@@ -178,14 +175,14 @@ LONG_STRING;
178 175
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "", "")));
179 176
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/", "")));
180 177
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/index.php", "")));
181
-        $exp = new Response(404);
178
+        $exp = new EmptyResponse(404);
182 179
         $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/bad/path", "")));
183 180
     }
184 181
 
185 182
     public function testHandleOptionsRequest() {
186
-        $exp = new Response(204, "", "", [
187
-            "Allow: POST",
188
-            "Accept: application/json, text/json",
183
+        $exp = new EmptyResponse(204, [
184
+            'Allow'  => "POST",
185
+            'Accept' => "application/json, text/json",
189 186
         ]);
190 187
         $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "")));
191 188
     }
@@ -226,7 +223,7 @@ LONG_STRING;
226 223
             'user'     => Arsse::$user->id,
227 224
             'password' => "secret",
228 225
         ];
229
-        $exp = new Response(500);
226
+        $exp = new EmptyResponse(500);
230 227
         $this->assertResponse($exp, $this->req($data));
231 228
     }
232 229
 
@@ -1630,10 +1627,10 @@ LONG_STRING;
1630 1627
         $this->assertResponse($this->outputHeadlines(1), $test);
1631 1628
         // test 'show_content'
1632 1629
         $test = $this->req($in[1]);
1633
-        $this->assertArrayHasKey("content", $test->payload['content'][0]);
1634
-        $this->assertArrayHasKey("content", $test->payload['content'][1]);
1630
+        $this->assertArrayHasKey("content", $test->getPayload()['content'][0]);
1631
+        $this->assertArrayHasKey("content", $test->getPayload()['content'][1]);
1635 1632
         foreach ($this->generateHeadlines(1) as $key => $row) {
1636
-            $this->assertSame($row['content'], $test->payload['content'][$key]['content']);
1633
+            $this->assertSame($row['content'], $test->getPayload()['content'][$key]['content']);
1637 1634
         }
1638 1635
         // test 'include_attachments'
1639 1636
         $test = $this->req($in[2]);
@@ -1649,25 +1646,23 @@ LONG_STRING;
1649 1646
                 'post_id'      => "2112",
1650 1647
             ],
1651 1648
         ];
1652
-        $this->assertArrayHasKey("attachments", $test->payload['content'][0]);
1653
-        $this->assertArrayHasKey("attachments", $test->payload['content'][1]);
1654
-        $this->assertSame([], $test->payload['content'][0]['attachments']);
1655
-        $this->assertSame($exp, $test->payload['content'][1]['attachments']);
1649
+        $this->assertArrayHasKey("attachments", $test->getPayload()['content'][0]);
1650
+        $this->assertArrayHasKey("attachments", $test->getPayload()['content'][1]);
1651
+        $this->assertSame([], $test->getPayload()['content'][0]['attachments']);
1652
+        $this->assertSame($exp, $test->getPayload()['content'][1]['attachments']);
1656 1653
         // test 'include_header'
1657 1654
         $test = $this->req($in[3]);
1658
-        $exp = $this->outputHeadlines(1);
1659
-        $exp->payload['content'] = [
1655
+        $exp = $this->respGood([
1660 1656
             ['id' => -4, 'is_cat' => false, 'first_id' => 1],
1661
-            $exp->payload['content'],
1662
-        ];
1657
+            $this->outputHeadlines(1)->getPayload()['content'],
1658
+        ]);
1663 1659
         $this->assertResponse($exp, $test);
1664 1660
         // test 'include_header' with a category
1665 1661
         $test = $this->req($in[4]);
1666
-        $exp = $this->outputHeadlines(1);
1667
-        $exp->payload['content'] = [
1662
+        $exp = $this->respGood([
1668 1663
             ['id' => -3, 'is_cat' => true, 'first_id' => 1],
1669
-            $exp->payload['content'],
1670
-        ];
1664
+            $this->outputHeadlines(1)->getPayload()['content'],
1665
+        ]);
1671 1666
         $this->assertResponse($exp, $test);
1672 1667
         // test 'include_header' with an empty result
1673 1668
         $test = $this->req($in[5]);
@@ -1686,37 +1681,34 @@ LONG_STRING;
1686 1681
         $this->assertResponse($exp, $test);
1687 1682
         // test 'include_header' with ascending order
1688 1683
         $test = $this->req($in[7]);
1689
-        $exp = $this->outputHeadlines(1);
1690
-        $exp->payload['content'] = [
1684
+        $exp = $this->respGood([
1691 1685
             ['id' => -4, 'is_cat' => false, 'first_id' => 0],
1692
-            $exp->payload['content'],
1693
-        ];
1686
+            $this->outputHeadlines(1)->getPayload()['content'],
1687
+        ]);
1694 1688
         $this->assertResponse($exp, $test);
1695 1689
         // test 'include_header' with skip
1696 1690
         Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->reverse(true)->limit(1)->subscription(42), Database::LIST_MINIMAL)->thenReturn($this->generateHeadlines(1867));
1697 1691
         $test = $this->req($in[8]);
1698
-        $exp = $this->outputHeadlines(1);
1699
-        $exp->payload['content'] = [
1692
+        $exp = $this->respGood([
1700 1693
             ['id' => 42, 'is_cat' => false, 'first_id' => 1867],
1701
-            $exp->payload['content'],
1702
-        ];
1694
+            $this->outputHeadlines(1)->getPayload()['content'],
1695
+        ]);
1703 1696
         $this->assertResponse($exp, $test);
1704 1697
         // test 'include_header' with skip and ascending order
1705 1698
         $test = $this->req($in[9]);
1706
-        $exp = $this->outputHeadlines(1);
1707
-        $exp->payload['content'] = [
1699
+        $exp = $this->respGood([
1708 1700
             ['id' => 42, 'is_cat' => false, 'first_id' => 0],
1709
-            $exp->payload['content'],
1710
-        ];
1701
+            $this->outputHeadlines(1)->getPayload()['content'],
1702
+        ]);
1711 1703
         $this->assertResponse($exp, $test);
1712 1704
         // test 'show_excerpt'
1713 1705
         $exp1 = "“This & that, you know‽”";
1714 1706
         $exp2 = "Pour vous faire mieux connaitre d’ou\u{300} vient l’erreur de ceux qui bla\u{302}ment la volupte\u{301}, et qui louent en…";
1715 1707
         $test = $this->req($in[10]);
1716
-        $this->assertArrayHasKey("excerpt", $test->payload['content'][0]);
1717
-        $this->assertArrayHasKey("excerpt", $test->payload['content'][1]);
1718
-        $this->assertSame($exp1, $test->payload['content'][0]['excerpt']);
1719
-        $this->assertSame($exp2, $test->payload['content'][1]['excerpt']);
1708
+        $this->assertArrayHasKey("excerpt", $test->getPayload()['content'][0]);
1709
+        $this->assertArrayHasKey("excerpt", $test->getPayload()['content'][1]);
1710
+        $this->assertSame($exp1, $test->getPayload()['content'][0]['excerpt']);
1711
+        $this->assertSame($exp2, $test->getPayload()['content'][1]['excerpt']);
1720 1712
     }
1721 1713
 
1722 1714
     protected function generateHeadlines(int $id): Result {

+ 13
- 13
tests/cases/REST/TinyTinyRSS/TestIcon.php View File

@@ -12,7 +12,7 @@ use JKingWeb\Arsse\User;
12 12
 use JKingWeb\Arsse\Database;
13 13
 use JKingWeb\Arsse\REST\TinyTinyRSS\Icon;
14 14
 use JKingWeb\Arsse\REST\Request;
15
-use JKingWeb\Arsse\REST\Response;
15
+use Zend\Diactoros\Response\EmptyResponse as Response;
16 16
 use Phake;
17 17
 
18 18
 /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Icon<extended> */
@@ -38,20 +38,20 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
38 38
         Phake::when(Arsse::$db)->subscriptionFavicon(2112)->thenReturn("http://example.net/logo.png");
39 39
         Phake::when(Arsse::$db)->subscriptionFavicon(1337)->thenReturn("http://example.org/icon.gif\r\nLocation: http://bad.example.com/");
40 40
         // these requests should succeed
41
-        $exp = new Response(301, "", "", ["Location: http://example.com/favicon.ico"]);
42
-        $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "42.ico")));
43
-        $exp = new Response(301, "", "", ["Location: http://example.net/logo.png"]);
44
-        $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.ico")));
45
-        $exp = new Response(301, "", "", ["Location: http://example.org/icon.gif"]);
46
-        $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "1337.ico")));
41
+        $exp = new Response(301, ['Location' => "http://example.com/favicon.ico"]);
42
+        $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "42.ico")));
43
+        $exp = new Response(301, ['Location' => "http://example.net/logo.png"]);
44
+        $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.ico")));
45
+        $exp = new Response(301, ['Location' => "http://example.org/icon.gif"]);
46
+        $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "1337.ico")));
47 47
         // these requests should fail
48 48
         $exp = new Response(404);
49
-        $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook.ico")));
50
-        $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook")));
51
-        $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "47.ico")));
52
-        $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.png")));
49
+        $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook.ico")));
50
+        $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook")));
51
+        $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "47.ico")));
52
+        $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.png")));
53 53
         // only GET is allowed
54
-        $exp = new Response(405, "", "", ["Allow: GET"]);
55
-        $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "2112.ico")));
54
+        $exp = new Response(405, ['Allow' => "GET"]);
55
+        $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "2112.ico")));
56 56
     }
57 57
 }

+ 13
- 0
tests/lib/AbstractTest.php View File

@@ -9,6 +9,9 @@ namespace JKingWeb\Arsse\Test;
9 9
 use JKingWeb\Arsse\Exception;
10 10
 use JKingWeb\Arsse\Arsse;
11 11
 use JKingWeb\Arsse\Misc\Date;
12
+use Psr\Http\Message\ResponseInterface;
13
+use Zend\Diactoros\Response\JsonResponse;
14
+use Zend\Diactoros\Response\EmptyResponse;
12 15
 
13 16
 /** @coversNothing */
14 17
 abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
@@ -29,6 +32,16 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
29 32
         }
30 33
     }
31 34
 
35
+    protected function assertResponse(ResponseInterface $exp, ResponseInterface $act, string $text = null) {
36
+        $this->assertEquals($exp->getHeaders(), $act->getHeaders(), $text);
37
+        $this->assertEquals($exp->getStatusCode(), $act->getStatusCode(), $text);
38
+        $this->assertInstanceOf(get_class($exp), $act);
39
+        if ($exp instanceof JsonResponse) {
40
+            $this->assertEquals($exp->getPayload(), $act->getPayload(), $text);
41
+            $this->assertSame($exp->getPayload(), $act->getPayload(), $text);
42
+        }
43
+    }
44
+
32 45
     public function approximateTime($exp, $act) {
33 46
         if (is_null($act)) {
34 47
             return null;

+ 10
- 10
vendor-bin/phpunit/composer.lock View File

@@ -777,16 +777,16 @@
777 777
         },
778 778
         {
779 779
             "name": "phpunit/phpunit",
780
-            "version": "6.5.4",
780
+            "version": "6.5.5",
781 781
             "source": {
782 782
                 "type": "git",
783 783
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
784
-                "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c"
784
+                "reference": "83d27937a310f2984fd575686138597147bdc7df"
785 785
             },
786 786
             "dist": {
787 787
                 "type": "zip",
788
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b2f933d5775f9237369deaa2d2bfbf9d652be4c",
789
-                "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c",
788
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/83d27937a310f2984fd575686138597147bdc7df",
789
+                "reference": "83d27937a310f2984fd575686138597147bdc7df",
790 790
                 "shasum": ""
791 791
             },
792 792
             "require": {
@@ -857,7 +857,7 @@
857 857
                 "testing",
858 858
                 "xunit"
859 859
             ],
860
-            "time": "2017-12-10T08:06:19+00:00"
860
+            "time": "2017-12-17T06:31:19+00:00"
861 861
         },
862 862
         {
863 863
             "name": "phpunit/phpunit-mock-objects",
@@ -965,16 +965,16 @@
965 965
         },
966 966
         {
967 967
             "name": "sebastian/comparator",
968
-            "version": "2.1.0",
968
+            "version": "2.1.1",
969 969
             "source": {
970 970
                 "type": "git",
971 971
                 "url": "https://github.com/sebastianbergmann/comparator.git",
972
-                "reference": "1174d9018191e93cb9d719edec01257fc05f8158"
972
+                "reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f"
973 973
             },
974 974
             "dist": {
975 975
                 "type": "zip",
976
-                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158",
977
-                "reference": "1174d9018191e93cb9d719edec01257fc05f8158",
976
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b11c729f95109b56a0fe9650c6a63a0fcd8c439f",
977
+                "reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f",
978 978
                 "shasum": ""
979 979
             },
980 980
             "require": {
@@ -1025,7 +1025,7 @@
1025 1025
                 "compare",
1026 1026
                 "equality"
1027 1027
             ],
1028
-            "time": "2017-11-03T07:16:52+00:00"
1028
+            "time": "2017-12-22T14:50:35+00:00"
1029 1029
         },
1030 1030
         {
1031 1031
             "name": "sebastian/diff",

+ 64
- 12
vendor-bin/robo/composer.lock View File

@@ -59,28 +59,33 @@
59 59
         },
60 60
         {
61 61
             "name": "consolidation/config",
62
-            "version": "1.0.7",
62
+            "version": "1.0.9",
63 63
             "source": {
64 64
                 "type": "git",
65 65
                 "url": "https://github.com/consolidation/config.git",
66
-                "reference": "b59a3b9ea750c21397f26a68fd2e04d9580af42e"
66
+                "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c"
67 67
             },
68 68
             "dist": {
69 69
                 "type": "zip",
70
-                "url": "https://api.github.com/repos/consolidation/config/zipball/b59a3b9ea750c21397f26a68fd2e04d9580af42e",
71
-                "reference": "b59a3b9ea750c21397f26a68fd2e04d9580af42e",
70
+                "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c",
71
+                "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c",
72 72
                 "shasum": ""
73 73
             },
74 74
             "require": {
75 75
                 "dflydev/dot-access-data": "^1.1.0",
76
-                "grasmash/yaml-expander": "^1.1",
76
+                "grasmash/expander": "^1",
77 77
                 "php": ">=5.4.0"
78 78
             },
79 79
             "require-dev": {
80
+                "greg-1-anderson/composer-test-scenarios": "^1",
80 81
                 "phpunit/phpunit": "^4",
81 82
                 "satooshi/php-coveralls": "^1.0",
82 83
                 "squizlabs/php_codesniffer": "2.*",
83
-                "symfony/console": "^2.5|^3"
84
+                "symfony/console": "^2.5|^3|^4",
85
+                "symfony/yaml": "^2.8.11|^3|^4"
86
+            },
87
+            "suggest": {
88
+                "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader"
84 89
             },
85 90
             "type": "library",
86 91
             "extra": {
@@ -104,7 +109,7 @@
104 109
                 }
105 110
             ],
106 111
             "description": "Provide configuration services for a commandline tool.",
107
-            "time": "2017-10-25T05:50:10+00:00"
112
+            "time": "2017-12-22T17:28:19+00:00"
108 113
         },
109 114
         {
110 115
             "name": "consolidation/log",
@@ -205,16 +210,16 @@
205 210
         },
206 211
         {
207 212
             "name": "consolidation/robo",
208
-            "version": "1.2.0",
213
+            "version": "1.2.1",
209 214
             "source": {
210 215
                 "type": "git",
211 216
                 "url": "https://github.com/consolidation/Robo.git",
212
-                "reference": "c46c13de3eca55e6b3635f363688ce85e845adf0"
217
+                "reference": "b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9"
213 218
             },
214 219
             "dist": {
215 220
                 "type": "zip",
216
-                "url": "https://api.github.com/repos/consolidation/Robo/zipball/c46c13de3eca55e6b3635f363688ce85e845adf0",
217
-                "reference": "c46c13de3eca55e6b3635f363688ce85e845adf0",
221
+                "url": "https://api.github.com/repos/consolidation/Robo/zipball/b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9",
222
+                "reference": "b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9",
218 223
                 "shasum": ""
219 224
             },
220 225
             "require": {
@@ -278,7 +283,7 @@
278 283
                 }
279 284
             ],
280 285
             "description": "Modern task runner",
281
-            "time": "2017-12-13T02:10:49+00:00"
286
+            "time": "2017-12-29T06:48:35+00:00"
282 287
         },
283 288
         {
284 289
             "name": "container-interop/container-interop",
@@ -370,6 +375,53 @@
370 375
             ],
371 376
             "time": "2017-01-20T21:14:22+00:00"
372 377
         },
378
+        {
379
+            "name": "grasmash/expander",
380
+            "version": "1.0.0",
381
+            "source": {
382
+                "type": "git",
383
+                "url": "https://github.com/grasmash/expander.git",
384
+                "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f"
385
+            },
386
+            "dist": {
387
+                "type": "zip",
388
+                "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f",
389
+                "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f",
390
+                "shasum": ""
391
+            },
392
+            "require": {
393
+                "dflydev/dot-access-data": "^1.1.0",
394
+                "php": ">=5.4"
395
+            },
396
+            "require-dev": {
397
+                "greg-1-anderson/composer-test-scenarios": "^1",
398
+                "phpunit/phpunit": "^4|^5.5.4",
399
+                "satooshi/php-coveralls": "^1.0.2|dev-master",
400
+                "squizlabs/php_codesniffer": "^2.7"
401
+            },
402
+            "type": "library",
403
+            "extra": {
404
+                "branch-alias": {
405
+                    "dev-master": "1.x-dev"
406
+                }
407
+            },
408
+            "autoload": {
409
+                "psr-4": {
410
+                    "Grasmash\\Expander\\": "src/"
411
+                }
412
+            },
413
+            "notification-url": "https://packagist.org/downloads/",
414
+            "license": [
415
+                "MIT"
416
+            ],
417
+            "authors": [
418
+                {
419
+                    "name": "Matthew Grasmick"
420
+                }
421
+            ],
422
+            "description": "Expands internal property references in PHP arrays file.",
423
+            "time": "2017-12-21T22:14:55+00:00"
424
+        },
373 425
         {
374 426
             "name": "grasmash/yaml-expander",
375 427
             "version": "1.4.0",

Loading…
Cancel
Save