Browse Source

Start on replacing Phake with Phony

rpm
J. King 3 years ago
parent
commit
2348786a92
  1. 2
      tests/bootstrap.php
  2. 190
      tests/cases/CLI/TestCLI.php
  3. 4
      tests/cases/Conf/TestConf.php
  4. 3
      tests/cases/Database/AbstractTest.php
  5. 26
      tests/cases/Database/SeriesSubscription.php
  6. 16
      tests/cases/Database/TestDatabase.php
  7. 6
      tests/cases/Db/BaseDriver.php
  8. 8
      tests/cases/Db/BaseResult.php
  9. 8
      tests/cases/Db/BaseStatement.php
  10. 6
      tests/cases/Db/BaseUpdate.php
  11. 6
      tests/cases/Db/SQLite3/TestCreation.php
  12. 6
      tests/cases/Db/SQLite3PDO/TestCreation.php
  13. 41
      tests/cases/Db/TestTransaction.php
  14. 13
      tests/cases/Exception/TestException.php
  15. 34
      tests/cases/Feed/TestFeed.php
  16. 2
      tests/cases/Feed/TestFetching.php
  17. 22
      tests/cases/ImportExport/TestFile.php
  18. 40
      tests/cases/ImportExport/TestImportExport.php
  19. 32
      tests/cases/ImportExport/TestOPML.php
  20. 4
      tests/cases/Misc/TestDate.php
  21. 3
      tests/cases/Misc/TestURL.php
  22. 3
      tests/cases/Misc/TestValueInfo.php
  23. 169
      tests/cases/REST/Fever/TestAPI.php
  24. 6
      tests/cases/REST/Fever/TestUser.php
  25. 6
      tests/cases/REST/Miniflux/TestToken.php
  26. 6
      tests/cases/REST/Miniflux/TestV1.php
  27. 6
      tests/cases/REST/NextcloudNews/TestV1_2.php
  28. 2
      tests/cases/REST/NextcloudNews/TestVersions.php
  29. 51
      tests/cases/REST/TestREST.php
  30. 6
      tests/cases/REST/TinyTinyRSS/TestIcon.php
  31. 2
      tests/cases/Service/TestSerial.php
  32. 2
      tests/cases/Service/TestService.php
  33. 2
      tests/cases/Service/TestSubprocess.php
  34. 23
      tests/cases/TestArsse.php
  35. 2
      tests/cases/User/TestInternal.php
  36. 2
      tests/cases/User/TestUser.php
  37. 39
      tests/lib/AbstractTest.php
  38. 3
      vendor-bin/phpunit/composer.json
  39. 149
      vendor-bin/phpunit/composer.lock
  40. 2
      vendor-bin/robo/composer.json
  41. 108
      vendor-bin/robo/composer.lock

2
tests/bootstrap.php

@ -12,7 +12,7 @@ const DOCROOT = BASE."tests".DIRECTORY_SEPARATOR."docroot".DIRECTORY_SEPARATOR;
ini_set("memory_limit", "-1");
ini_set("zend.assertions", "1");
ini_set("assert.exception", "true");
error_reporting(\E_ALL);
error_reporting(\E_ALL ^ \E_DEPRECATED);
require_once BASE."vendor".DIRECTORY_SEPARATOR."autoload.php";
if (function_exists("xdebug_set_filter")) {

190
tests/cases/CLI/TestCLI.php

@ -20,13 +20,16 @@ use JKingWeb\Arsse\ImportExport\OPML;
/** @covers \JKingWeb\Arsse\CLI */
class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
$this->cli = \Phake::partialMock(CLI::class);
\Phake::when($this->cli)->logError->thenReturn(null);
\Phake::when($this->cli)->loadConf->thenReturn(true);
parent::setUp();
$this->cli = $this->partialMock(CLI::class);
$this->cli->logError->returns(null);
$this->cli->loadConf->returns(true);
$this->dbMock = $this->mock(Database::class);
}
public function assertConsole(CLI $cli, string $command, int $exitStatus, string $output = "", bool $pattern = false): void {
public function assertConsole(string $command, int $exitStatus, string $output = "", bool $pattern = false): void {
Arsse::$obj = $this->objMock->get();
Arsse::$db = $this->dbMock->get();
$argv = \Clue\Arguments\split($command);
$output = strlen($output) ? $output.\PHP_EOL : "";
if ($pattern) {
@ -34,18 +37,18 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
} else {
$this->expectOutputString($output);
}
$this->assertSame($exitStatus, $cli->dispatch($argv));
$this->assertSame($exitStatus, $this->cli->get()->dispatch($argv));
}
public function testPrintVersion(): void {
$this->assertConsole($this->cli, "arsse.php --version", 0, Arsse::VERSION);
\Phake::verify($this->cli, \Phake::times(0))->loadConf;
$this->assertConsole("arsse.php --version", 0, Arsse::VERSION);
$this->cli->loadConf->never()->called();
}
/** @dataProvider provideHelpText */
public function testPrintHelp(string $cmd, string $name): void {
$this->assertConsole($this->cli, $cmd, 0, str_replace("arsse.php", $name, CLI::USAGE));
\Phake::verify($this->cli, \Phake::times(0))->loadConf;
$this->assertConsole($cmd, 0, str_replace("arsse.php", $name, CLI::USAGE));
$this->cli->loadConf->never()->called();
}
public function provideHelpText(): iterable {
@ -60,31 +63,30 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testStartTheDaemon(): void {
$srv = \Phake::mock(Service::class);
\Phake::when(Arsse::$obj)->get(Service::class)->thenReturn($srv);
\Phake::when($srv)->watch->thenReturn(new \DateTimeImmutable);
$this->assertConsole($this->cli, "arsse.php daemon", 0);
\Phake::verify($this->cli)->loadConf;
\Phake::verify($srv)->watch(true);
$srv = $this->mock(Service::class);
$srv->watch->returns(new \DateTimeImmutable);
$this->objMock->get->with(Service::class)->returns($srv->get());
$this->assertConsole("arsse.php daemon", 0);
$this->cli->loadConf->called();
$srv->watch->calledWith(true);
}
public function testRefreshAllFeeds(): void {
$srv = \Phake::mock(Service::class);
\Phake::when(Arsse::$obj)->get(Service::class)->thenReturn($srv);
\Phake::when($srv)->watch->thenReturn(new \DateTimeImmutable);
$this->assertConsole($this->cli, "arsse.php feed refresh-all", 0);
\Phake::verify($this->cli)->loadConf;
\Phake::verify($srv)->watch(false);
$srv = $this->mock(Service::class);
$srv->watch->returns(new \DateTimeImmutable);
$this->objMock->get->with(Service::class)->returns($srv->get());
$this->assertConsole("arsse.php feed refresh-all", 0);
$this->cli->loadConf->called();
$srv->watch->calledWith(false);
}
/** @dataProvider provideFeedUpdates */
public function testRefreshAFeed(string $cmd, int $exitStatus, string $output): void {
Arsse::$db = \Phake::mock(Database::class);
\Phake::when(Arsse::$db)->feedUpdate(1, true)->thenReturn(true);
\Phake::when(Arsse::$db)->feedUpdate(2, true)->thenThrow(new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/"], $this->mockGuzzleException(ClientException::class, "", 404)));
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
\Phake::verify($this->cli)->loadConf;
\Phake::verify(Arsse::$db)->feedUpdate;
$this->dbMock->feedUpdate->with(1, true)->returns(true);
$this->dbMock->feedUpdate->with(2, true)->throws(new \JKingWeb\Arsse\Feed\Exception("", ['url' => "http://example.com/"], $this->mockGuzzleException(ClientException::class, "", 404)));
$this->assertConsole($cmd, $exitStatus, $output);
$this->cli->loadConf->called();
$this->dbMock->feedUpdate->called();
}
public function provideFeedUpdates(): iterable {
@ -96,14 +98,14 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideDefaultConfigurationSaves */
public function testSaveTheDefaultConfiguration(string $cmd, int $exitStatus, string $file): void {
$conf = \Phake::mock(Conf::class);
\Phake::when(Arsse::$obj)->get(Conf::class)->thenReturn($conf);
\Phake::when($conf)->exportFile("php://output", true)->thenReturn(true);
\Phake::when($conf)->exportFile("good.conf", true)->thenReturn(true);
\Phake::when($conf)->exportFile("bad.conf", true)->thenThrow(new \JKingWeb\Arsse\Conf\Exception("fileUnwritable"));
$this->assertConsole($this->cli, $cmd, $exitStatus);
\Phake::verify($this->cli, \Phake::times(0))->loadConf;
\Phake::verify($conf)->exportFile($file, true);
$conf = $this->mock(Conf::class);
$conf->exportFile->with("php://output", true)->returns(true);
$conf->exportFile->with("good.conf", true)->returns(true);
$conf->exportFile->with("bad.conf", true)->throws(new \JKingWeb\Arsse\Conf\Exception("fileUnwritable"));
$this->objMock->get->with(Conf::class)->returns($conf->get());
$this->assertConsole($cmd, $exitStatus);
$this->cli->loadConf->never()->called();
$conf->exportFile->calledWith($file, true);
}
public function provideDefaultConfigurationSaves(): iterable {
@ -120,7 +122,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
// FIXME: Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
Arsse::$user = $this->createMock(User::class);
Arsse::$user->method("list")->willReturn($list);
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
$this->assertConsole($cmd, $exitStatus, $output);
}
public function provideUserList(): iterable {
@ -146,7 +148,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
return is_null($pass) ? "random password" : $pass;
}
}));
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
$this->assertConsole($cmd, $exitStatus, $output);
}
public function provideUserAdditions(): iterable {
@ -163,7 +165,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
Arsse::$user->method("propertiesSet")->willReturn([]);
Arsse::$user->expects($this->exactly(1))->method("add")->with("jane.doe@example.com", null);
Arsse::$user->expects($this->exactly(1))->method("propertiesSet")->with("jane.doe@example.com", ['admin' => true]);
$this->assertConsole($this->cli, "arsse.php user add jane.doe@example.com --admin", 0, "random password");
$this->assertConsole("arsse.php user add jane.doe@example.com --admin", 0, "random password");
}
/** @dataProvider provideUserAuthentication */
@ -176,12 +178,12 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
($user === "jane.doe@example.com" && $pass === "superman")
;
}));
$fever = \Phake::mock(FeverUser::class);
\Phake::when(Arsse::$obj)->get(FeverUser::class)->thenReturn($fever);
\Phake::when($fever)->authenticate->thenReturn(false);
\Phake::when($fever)->authenticate("john.doe@example.com", "ashalla")->thenReturn(true);
\Phake::when($fever)->authenticate("jane.doe@example.com", "thx1138")->thenReturn(true);
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
$fever = $this->mock(FeverUser::class);
$fever->authenticate->returns(false);
$fever->authenticate->with("john.doe@example.com", "ashalla")->returns(true);
$fever->authenticate->with("jane.doe@example.com", "thx1138")->returns(true);
$this->objMock->get->with(FeverUser::class)->returns($fever->get());
$this->assertConsole($cmd, $exitStatus, $output);
}
public function provideUserAuthentication(): iterable {
@ -210,7 +212,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
}
throw new \JKingWeb\Arsse\User\ExceptionConflict("doesNotExist");
}));
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
$this->assertConsole($cmd, $exitStatus, $output);
}
public function provideUserRemovals(): iterable {
@ -233,10 +235,10 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
// FIXME: Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
Arsse::$user = $this->createMock(User::class);
Arsse::$user->method("passwordSet")->will($this->returnCallback($passwordChange));
$fever = \Phake::mock(FeverUser::class);
\Phake::when(Arsse::$obj)->get(FeverUser::class)->thenReturn($fever);
\Phake::when($fever)->register->thenReturnCallback($passwordChange);
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
$fever = $this->mock(FeverUser::class);
$fever->register->does($passwordChange);
$this->objMock->get->with(FeverUser::class)->returns($fever->get());
$this->assertConsole($cmd, $exitStatus, $output);
}
public function provideUserPasswordChanges(): iterable {
@ -263,10 +265,10 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
// FIXME: Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
Arsse::$user = $this->createMock(User::class);
Arsse::$user->method("passwordUnset")->will($this->returnCallback($passwordClear));
$fever = \Phake::mock(FeverUser::class);
\Phake::when(Arsse::$obj)->get(FeverUser::class)->thenReturn($fever);
\Phake::when($fever)->unregister->thenReturnCallback($passwordClear);
$this->assertConsole($this->cli, $cmd, $exitStatus, $output);
$fever = $this->mock(FeverUser::class);
$fever->unregister->does($passwordClear);
$this->objMock->get->with(FeverUser::class)->returns($fever->get());
$this->assertConsole($cmd, $exitStatus, $output);
}
public function provideUserPasswordClearings(): iterable {
@ -280,14 +282,14 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideOpmlExports */
public function testExportToOpml(string $cmd, int $exitStatus, string $file, string $user, bool $flat): void {
$opml = \Phake::mock(OPML::class);
\Phake::when(Arsse::$obj)->get(OPML::class)->thenReturn($opml);
\Phake::when($opml)->exportFile("php://output", $user, $flat)->thenReturn(true);
\Phake::when($opml)->exportFile("good.opml", $user, $flat)->thenReturn(true);
\Phake::when($opml)->exportFile("bad.opml", $user, $flat)->thenThrow(new \JKingWeb\Arsse\ImportExport\Exception("fileUnwritable"));
$this->assertConsole($this->cli, $cmd, $exitStatus);
\Phake::verify($this->cli)->loadConf;
\Phake::verify($opml)->exportFile($file, $user, $flat);
$opml = $this->mock(OPML::class);
$opml->exportFile->with("php://output", $user, $flat)->returns(true);
$opml->exportFile->with("good.opml", $user, $flat)->returns(true);
$opml->exportFile->with("bad.opml", $user, $flat)->throws(new \JKingWeb\Arsse\ImportExport\Exception("fileUnwritable"));
$this->objMock->get->with(OPML::class)->returns($opml->get());
$this->assertConsole($cmd, $exitStatus);
$this->cli->loadConf->called();
$opml->exportFile->calledWith($file, $user, $flat);
}
public function provideOpmlExports(): iterable {
@ -321,14 +323,14 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideOpmlImports */
public function testImportFromOpml(string $cmd, int $exitStatus, string $file, string $user, bool $flat, bool $replace): void {
$opml = \Phake::mock(OPML::class);
\Phake::when(Arsse::$obj)->get(OPML::class)->thenReturn($opml);
\Phake::when($opml)->importFile("php://input", $user, $flat, $replace)->thenReturn(true);
\Phake::when($opml)->importFile("good.opml", $user, $flat, $replace)->thenReturn(true);
\Phake::when($opml)->importFile("bad.opml", $user, $flat, $replace)->thenThrow(new \JKingWeb\Arsse\ImportExport\Exception("fileUnreadable"));
$this->assertConsole($this->cli, $cmd, $exitStatus);
\Phake::verify($this->cli)->loadConf;
\Phake::verify($opml)->importFile($file, $user, $flat, $replace);
$opml = $this->mock(OPML::class);
$opml->importFile->with("php://input", $user, $flat, $replace)->returns(true);
$opml->importFile->with("good.opml", $user, $flat, $replace)->returns(true);
$opml->importFile->with("bad.opml", $user, $flat, $replace)->throws(new \JKingWeb\Arsse\ImportExport\Exception("fileUnreadable"));
$this->objMock->get->with(OPML::class)->returns($opml->get());
$this->assertConsole($cmd, $exitStatus);
$this->cli->loadConf->called();
$opml->importFile->calledWith($file, $user, $flat, $replace);
}
public function provideOpmlImports(): iterable {
@ -400,7 +402,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
Arsse::$user = $this->createMock(User::class);
Arsse::$user->method("propertiesGet")->willReturn($data);
Arsse::$user->expects($this->once())->method("propertiesGet")->with("john.doe@example.com", true);
$this->assertConsole($this->cli, "arsse.php user show john.doe@example.com", 0, $exp);
$this->assertConsole("arsse.php user show john.doe@example.com", 0, $exp);
}
/** @dataProvider provideMetadataChanges */
@ -408,7 +410,7 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
Arsse::$user = $this->createMock(User::class);
Arsse::$user->method("propertiesSet")->willReturn($out);
Arsse::$user->expects($this->once())->method("propertiesSet")->with($user, $in);
$this->assertConsole($this->cli, $cmd, $exp, "");
$this->assertConsole($cmd, $exp, "");
}
public function provideMetadataChanges(): iterable {
@ -433,40 +435,38 @@ class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
"TOKEN 2 Eek",
"TOKEN 1 Ook",
]);
$t = \Phake::mock(MinifluxToken::class);
\Phake::when(Arsse::$obj)->get(MinifluxToken::class)->thenReturn($t);
\Phake::when($t)->tokenList->thenReturn($data);
$this->assertConsole($this->cli, "arsse.php token list john", 0, $exp);
\Phake::verify($t)->tokenList("john");
$t = $this->mock(MinifluxToken::class);
$t->tokenList->returns($data);
$this->objMock->get->with(MinifluxToken::class)->returns($t->get());
$this->assertConsole("arsse.php token list john", 0, $exp);
$t->tokenList->calledWith("john");
}
public function testCreateToken(): void {
$t = \Phake::mock(MinifluxToken::class);
\Phake::when(Arsse::$obj)->get(MinifluxToken::class)->thenReturn($t);
\Phake::when($t)->tokenGenerate->thenReturn("RANDOM TOKEN");
$this->assertConsole($this->cli, "arse.php token create jane", 0, "RANDOM TOKEN");
\Phake::verify($t)->tokenGenerate("jane", null);
$t = $this->mock(MinifluxToken::class);
$t->tokenGenerate->returns("RANDOM TOKEN");
$this->objMock->get->with(MinifluxToken::class)->returns($t->get());
$this->assertConsole("arse.php token create jane", 0, "RANDOM TOKEN");
$t->tokenGenerate->calledWith("jane", null);
}
public function testCreateTokenWithLabel(): void {
$t = \Phake::mock(MinifluxToken::class);
\Phake::when(Arsse::$obj)->get(MinifluxToken::class)->thenReturn($t);
\Phake::when($t)->tokenGenerate->thenReturn("RANDOM TOKEN");
$this->assertConsole($this->cli, "arse.php token create jane Ook", 0, "RANDOM TOKEN");
\Phake::verify($t)->tokenGenerate("jane", "Ook");
$t = $this->mock(MinifluxToken::class);
$t->tokenGenerate->returns("RANDOM TOKEN");
$this->objMock->get->with(MinifluxToken::class)->returns($t->get());
$this->assertConsole("arse.php token create jane Ook", 0, "RANDOM TOKEN");
$t->tokenGenerate->calledWith("jane", "Ook");
}
public function testRevokeAToken(): void {
Arsse::$db = \Phake::mock(Database::class);
\Phake::when(Arsse::$db)->tokenRevoke->thenReturn(true);
$this->assertConsole($this->cli, "arse.php token revoke jane TOKEN_ID", 0);
\Phake::verify(Arsse::$db)->tokenRevoke("jane", "miniflux.login", "TOKEN_ID");
$this->dbMock->tokenRevoke->returns(true);
$this->assertConsole("arse.php token revoke jane TOKEN_ID", 0);
$this->dbMock->tokenRevoke->calledWith("jane", "miniflux.login", "TOKEN_ID");
}
public function testRevokeAllTokens(): void {
Arsse::$db = \Phake::mock(Database::class);
\Phake::when(Arsse::$db)->tokenRevoke->thenReturn(true);
$this->assertConsole($this->cli, "arse.php token revoke jane", 0);
\Phake::verify(Arsse::$db)->tokenRevoke("jane", "miniflux.login", null);
$this->dbMock->tokenRevoke->returns(true);
$this->assertConsole("arse.php token revoke jane", 0);
$this->dbMock->tokenRevoke->calledWith("jane", "miniflux.login", null);
}
}

4
tests/cases/Conf/TestConf.php

@ -15,7 +15,7 @@ class TestConf extends \JKingWeb\Arsse\Test\AbstractTest {
public static $path;
public function setUp(): void {
self::clearData();
parent::setUp();
self::$vfs = vfsStream::setup("root", null, [
'confGood' => '<?php return Array("lang" => "xx");',
'confNotArray' => '<?php return 0;',
@ -35,7 +35,7 @@ class TestConf extends \JKingWeb\Arsse\Test\AbstractTest {
public function tearDown(): void {
self::$path = null;
self::$vfs = null;
self::clearData();
parent::tearDown();
}
public function testLoadDefaultValues(): void {

3
tests/cases/Database/AbstractTest.php

@ -73,7 +73,8 @@ abstract class AbstractTest extends \JKingWeb\Arsse\Test\AbstractTest {
Arsse::$db = new Database(static::$drv);
Arsse::$db->driverSchemaUpdate();
// create a mock user manager
Arsse::$user = \Phake::mock(User::class);
$this->userMock = $this->mock(User::class);
Arsse::$user = $this->userMock->get();
// call the series-specific setup method
$setUp = "setUp".$this->series;
$this->$setUp();

26
tests/cases/Database/SeriesSubscription.php

@ -192,8 +192,6 @@ trait SeriesSubscription {
],
],
];
// initialize a partial mock of the Database object to later manipulate the feedUpdate method
Arsse::$db = \Phake::partialMock(Database::class, static::$drv);
$this->user = "john.doe@example.com";
}
@ -204,9 +202,11 @@ trait SeriesSubscription {
public function testAddASubscriptionToAnExistingFeed(): void {
$url = "http://example.com/feed1";
$subID = $this->nextID("arsse_subscriptions");
\Phake::when(Arsse::$db)->feedUpdate->thenReturn(true);
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->returns(true);
Arsse::$db = $db->get();
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url));
\Phake::verify(Arsse::$db, \Phake::times(0))->feedUpdate(1, true);
$db->feedUpdate->never()->called();
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
@ -219,9 +219,11 @@ trait SeriesSubscription {
$url = "http://example.org/feed1";
$feedID = $this->nextID("arsse_feeds");
$subID = $this->nextID("arsse_subscriptions");
\Phake::when(Arsse::$db)->feedUpdate->thenReturn(true);
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->returns(true);
Arsse::$db = $db->get();
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url, "", "", false));
\Phake::verify(Arsse::$db)->feedUpdate($feedID, true, false);
$db->feedUpdate->calledWith($feedID, true, false);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
@ -236,9 +238,11 @@ trait SeriesSubscription {
$discovered = "http://localhost:8000/Feed/Discovery/Feed";
$feedID = $this->nextID("arsse_feeds");
$subID = $this->nextID("arsse_subscriptions");
\Phake::when(Arsse::$db)->feedUpdate->thenReturn(true);
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->returns(true);
Arsse::$db = $db->get();
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url, "", "", true));
\Phake::verify(Arsse::$db)->feedUpdate($feedID, true, false);
$db->feedUpdate->calledWith($feedID, true, false);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
@ -251,12 +255,14 @@ trait SeriesSubscription {
public function testAddASubscriptionToAnInvalidFeed(): void {
$url = "http://example.org/feed1";
$feedID = $this->nextID("arsse_feeds");
\Phake::when(Arsse::$db)->feedUpdate->thenThrow(new FeedException("", ['url' => $url], $this->mockGuzzleException(ClientException::class, "", 404)));
$db = $this->partialMock(Database::class, static::$drv);
$db->feedUpdate->throws(new FeedException("", ['url' => $url], $this->mockGuzzleException(ClientException::class, "", 404)));
Arsse::$db = $db->get();
$this->assertException("invalidUrl", "Feed");
try {
Arsse::$db->subscriptionAdd($this->user, $url, "", "", false);
} finally {
\Phake::verify(Arsse::$db)->feedUpdate($feedID, true, false);
$db->feedUpdate->calledWith($feedID, true, false);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],

16
tests/cases/Database/TestDatabase.php

@ -13,10 +13,10 @@ class TestDatabase extends \JKingWeb\Arsse\Test\AbstractTest {
protected $db = null;
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
try {
$this->db = \Phake::makeVisible(\Phake::partialMock(Database::class));
$this->db = new Database;
} catch (\JKingWeb\Arsse\Db\Exception $e) {
$this->markTestSkipped("SQLite 3 database driver not available");
}
@ -24,14 +24,20 @@ class TestDatabase extends \JKingWeb\Arsse\Test\AbstractTest {
public function tearDown(): void {
$this->db = null;
self::clearData();
parent::tearDown();
}
protected function invoke(string $method, ...$arg) {
$m = new \ReflectionMethod($this->db, $method);
$m->setAccessible(true);
return $m->invoke($this->db, ...$arg);
}
/** @dataProvider provideInClauses */
public function testGenerateInClause(string $clause, array $values, array $inV, string $inT): void {
$types = array_fill(0, sizeof($values), $inT);
$exp = [$clause, $types, $values];
$this->assertSame($exp, $this->db->generateIn($inV, $inT));
$this->assertSame($exp, $this->invoke("generateIn", $inV, $inT));
}
public function provideInClauses(): iterable {
@ -66,7 +72,7 @@ class TestDatabase extends \JKingWeb\Arsse\Test\AbstractTest {
// this is not an exhaustive test; integration tests already cover the ins and outs of the functionality
$types = array_fill(0, sizeof($values), "str");
$exp = [$clause, $types, $values];
$this->assertSame($exp, $this->db->generateSearch($inV, $inC, $inAny));
$this->assertSame($exp, $this->invoke("generateSearch", $inV, $inC, $inAny));
}
public function provideSearchClauses(): iterable {

6
tests/cases/Db/BaseDriver.php

@ -31,7 +31,7 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf(static::$conf);
if (!static::$interface) {
$this->markTestSkipped(static::$implementation." database driver not available");
@ -48,7 +48,7 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
public function tearDown(): void {
// deconstruct the driver
unset($this->drv);
self::clearData();
parent::tearDown();
}
public static function tearDownAfterClass(): void {
@ -57,7 +57,7 @@ abstract class BaseDriver extends \JKingWeb\Arsse\Test\AbstractTest {
static::dbRaze(static::$interface);
}
static::$interface = null;
self::clearData();
self::clearData(true);
}
protected function exec($q): bool {

8
tests/cases/Db/BaseResult.php

@ -25,7 +25,7 @@ abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
if (!static::$interface) {
$this->markTestSkipped(static::$implementation." database driver not available");
@ -35,17 +35,13 @@ abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest {
$this->resultClass = static::$dbResultClass;
}
public function tearDown(): void {
self::clearData();
}
public static function tearDownAfterClass(): void {
if (static::$interface) {
// completely clear the database
static::dbRaze(static::$interface);
}
static::$interface = null;
self::clearData();
self::clearData(true);
}
public function testConstructResult(): void {

8
tests/cases/Db/BaseStatement.php

@ -23,7 +23,7 @@ abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
if (!static::$interface) {
$this->markTestSkipped(static::$implementation." database driver not available");
@ -33,17 +33,13 @@ abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
$this->statementClass = static::$dbStatementClass;
}
public function tearDown(): void {
self::clearData();
}
public static function tearDownAfterClass(): void {
if (static::$interface) {
// completely clear the database
static::dbRaze(static::$interface);
}
static::$interface = null;
self::clearData();
self::clearData(true);
}
public function testConstructStatement(): void {

6
tests/cases/Db/BaseUpdate.php

@ -29,7 +29,7 @@ class BaseUpdate extends \JKingWeb\Arsse\Test\AbstractTest {
if (!static::$interface) {
$this->markTestSkipped(static::$implementation." database driver not available");
}
self::clearData();
parent::setUp();
self::setConf();
// construct a fresh driver for each test
$this->drv = new static::$dbDriverClass;
@ -46,7 +46,7 @@ class BaseUpdate extends \JKingWeb\Arsse\Test\AbstractTest {
// deconstruct the driver
unset($this->drv);
unset($this->path, $this->base, $this->vfs);
self::clearData();
parent::tearDown();
}
public static function tearDownAfterClass(): void {
@ -55,7 +55,7 @@ class BaseUpdate extends \JKingWeb\Arsse\Test\AbstractTest {
static::dbRaze(static::$interface);
}
static::$interface = null;
self::clearData();
self::clearData(true);
}
public function testLoadMissingFile(): void {

6
tests/cases/Db/SQLite3/TestCreation.php

@ -22,7 +22,7 @@ class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest {
if (!Driver::requirementsMet()) {
$this->markTestSkipped("SQLite extension not loaded");
}
self::clearData();
parent::setUp();
// test files
$this->files = [
// cannot create files
@ -108,10 +108,6 @@ class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest {
self::setConf();
}
public function tearDown(): void {
self::clearData();
}
public function testFailToCreateDatabase(): void {
Arsse::$conf->dbSQLite3File = $this->path."Cmain/arsse.db";
$this->assertException("fileUncreatable", "Db");

6
tests/cases/Db/SQLite3PDO/TestCreation.php

@ -24,7 +24,7 @@ class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest {
if (!Driver::requirementsMet()) {
$this->markTestSkipped("PDO-SQLite extension not loaded");
}
self::clearData();
parent::setUp();
// test files
$this->files = [
// cannot create files
@ -110,10 +110,6 @@ class TestCreation extends \JKingWeb\Arsse\Test\AbstractTest {
self::setConf();
}
public function tearDown(): void {
self::clearData();
}
public function testFailToCreateDatabase(): void {
Arsse::$conf->dbSQLite3File = $this->path."Cmain/arsse.db";
$this->assertException("fileUncreatable", "Db");

41
tests/cases/Db/TestTransaction.php

@ -15,47 +15,50 @@ class TestTransaction extends \JKingWeb\Arsse\Test\AbstractTest {
protected $drv;
public function setUp(): void {
self::clearData();
$drv = \Phake::mock(\JKingWeb\Arsse\Db\SQLite3\Driver::class);
\Phake::when($drv)->savepointRelease->thenReturn(true);
\Phake::when($drv)->savepointUndo->thenReturn(true);
\Phake::when($drv)->savepointCreate->thenReturn(1)->thenReturn(2);
parent::setUp();
$drv = $this->mock(\JKingWeb\Arsse\Db\SQLite3\Driver::class);
$drv->savepointRelease->returns(true);
$drv->savepointUndo->returns(true);
$drv->savepointCreate->returns(1, 2);
$this->drv = $drv;
}
public function testManipulateTransactions(): void {
$tr1 = new Transaction($this->drv);
$tr2 = new Transaction($this->drv);
\Phake::verify($this->drv, \Phake::times(2))->savepointCreate;
$drv = $this->drv->get();
$tr1 = new Transaction($drv);
$tr2 = new Transaction($drv);
$this->drv->savepointCreate->twice()->called();
$this->assertSame(1, $tr1->getIndex());
$this->assertSame(2, $tr2->getIndex());
unset($tr1);
\Phake::verify($this->drv)->savepointUndo(1);
$this->drv->savepointUndo->calledWith(1);
unset($tr2);
\Phake::verify($this->drv)->savepointUndo(2);
$this->drv->savepointUndo->calledWith(2);
}
public function testCloseTransactions(): void {
$tr1 = new Transaction($this->drv);
$tr2 = new Transaction($this->drv);
$drv = $this->drv->get();
$tr1 = new Transaction($drv);
$tr2 = new Transaction($drv);
$this->assertTrue($tr1->isPending());
$this->assertTrue($tr2->isPending());
$tr1->commit();
$this->assertFalse($tr1->isPending());
$this->assertTrue($tr2->isPending());
\Phake::verify($this->drv)->savepointRelease(1);
$this->drv->savepointRelease->calledWith(1);
$tr2->rollback();
$this->assertFalse($tr1->isPending());
$this->assertFalse($tr2->isPending());
\Phake::verify($this->drv)->savepointUndo(2);
$this->drv->savepointUndo->calledWith(2);
}
public function testIgnoreRollbackErrors(): void {
\Phake::when($this->drv)->savepointUndo->thenThrow(new Exception("savepointStale"));
$tr1 = new Transaction($this->drv);
$tr2 = new Transaction($this->drv);
$this->drv->savepointUndo->throws(new Exception("savepointStale"));
$drv = $this->drv->get();
$tr1 = new Transaction($drv);
$tr2 = new Transaction($drv);
unset($tr1, $tr2); // no exception should bubble up
\Phake::verify($this->drv)->savepointUndo(1);
\Phake::verify($this->drv)->savepointUndo(2);
$this->drv->savepointUndo->calledWith(1);
$this->drv->savepointUndo->calledWith(2);
}
}

13
tests/cases/Exception/TestException.php

@ -16,16 +16,9 @@ class TestException extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData(false);
// create a mock Lang object so as not to create a dependency loop
Arsse::$lang = \Phake::mock(Lang::class);
\Phake::when(Arsse::$lang)->msg->thenReturn("");
}
public function tearDown(): void {
// verify calls to the mock Lang object
\Phake::verify(Arsse::$lang, \Phake::atLeast(0))->msg($this->isType("string"), $this->anything());
\Phake::verifyNoOtherInteractions(Arsse::$lang);
// clean up
self::clearData(true);
$this->langMock = $this->mock(Lang::class);
$this->langMock->msg->returns("");
Arsse::$lang = $this->langMock->get();
}
public function testBaseClass(): void {

34
tests/cases/Feed/TestFeed.php

@ -11,6 +11,7 @@ use JKingWeb\Arsse\Feed;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Misc\Date;
use JKingWeb\Arsse\Test\Result;
use Eloquent\Phony\Phpunit\Phony;
/**
* @covers \JKingWeb\Arsse\Feed
@ -92,12 +93,15 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
$this->markTestSkipped("Test Web server is not accepting requests");
}
$this->base = self::$host."Feed/";
self::clearData();
parent::setUp();
self::setConf();
Arsse::$db = \Phake::mock(Database::class);
\Phake::when(Arsse::$db)->feedMatchLatest->thenReturn(new Result([]));
\Phake::when(Arsse::$db)->feedMatchIds->thenReturn(new Result([]));
\Phake::when(Arsse::$db)->feedRulesGet->thenReturn([]);
$this->dbMock = $this->mock(Database::class);
$this->dbMock->feedMatchLatest->with(Phony::wildcard())->returns(new Result([]));
$this->dbMock->feedMatchLatest->with(1, Phony::any())->returns(new Result($this->latest));
$this->dbMock->feedMatchIds->with(Phony::wildcard())->returns(new Result([]));
$this->dbMock->feedMatchIds->with(1, Phony::wildcard())->returns(new Result($this->others));
$this->dbMock->feedRulesGet->returns([]);
Arsse::$db = $this->dbMock->get();
}
public function testParseAFeed(): void {
@ -338,7 +342,10 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testMatchLatestArticles(): void {
\Phake::when(Arsse::$db)->feedMatchLatest(1, $this->anything())->thenReturn(new Result($this->latest));
$this->dbMock = $this->mock(Database::class);
$this->dbMock->feedMatchLatest->with(Phony::wildcard())->returns(new Result([]));
$this->dbMock->feedMatchLatest->with(1, Phony::any())->returns(new Result($this->latest));
Arsse::$db = $this->dbMock->get();
$f = new Feed(1, $this->base."Matching/1");
$this->assertCount(0, $f->newItems);
$this->assertCount(0, $f->changedItems);
@ -354,8 +361,6 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testMatchHistoricalArticles(): void {
\Phake::when(Arsse::$db)->feedMatchLatest(1, $this->anything())->thenReturn(new Result($this->latest));
\Phake::when(Arsse::$db)->feedMatchIds(1, $this->anything(), $this->anything(), $this->anything(), $this->anything())->thenReturn(new Result($this->others));
$f = new Feed(1, $this->base."Matching/5");
$this->assertCount(0, $f->newItems);
$this->assertCount(0, $f->changedItems);
@ -383,7 +388,11 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testApplyFilterRules(): void {
\Phake::when(Arsse::$db)->feedMatchIds->thenReturn(new Result([
$exp = [
'jack' => ['new' => [false, true, true, false, true], 'changed' => [7 => true, 47 => true, 2112 => false, 1 => true, 42 => false]],
'sam' => ['new' => [false, true, false, false, false], 'changed' => [7 => false, 47 => true, 2112 => false, 1 => false, 42 => false]],
];
$this->dbMock->feedMatchIds->returns(new Result([
// these are the sixth through tenth entries in the feed; the title hashes have been omitted for brevity
['id' => 7, 'guid' => '0f2a218c311e3d8105f1b075142a5d26dabf056ffc61abe77e96c8f071bbf4a7', 'edited' => null, 'url_title_hash' => "", 'url_content_hash' => '', 'title_content_hash' => ''],
['id' => 47, 'guid' => '1c19e3b9018bc246b7414ae919ddebc88d0c575129e8c4a57b84b826c00f6db5', 'edited' => null, 'url_title_hash' => "", 'url_content_hash' => '', 'title_content_hash' => ''],
@ -391,15 +400,12 @@ class TestFeed extends \JKingWeb\Arsse\Test\AbstractTest {
['id' => 1, 'guid' => '436070cda5713a0d9a8fdc8652c7ab142f0550697acfd5206a16c18aee355039', 'edited' => null, 'url_title_hash' => "", 'url_content_hash' => '', 'title_content_hash' => ''],
['id' => 42, 'guid' => '1a731433a1904220ef26e731ada7262e1d5bcecae53e7b5df9e1f5713af6e5d3', 'edited' => null, 'url_title_hash' => "", 'url_content_hash' => '', 'title_content_hash' => ''],
]));
\Phake::when(Arsse::$db)->feedRulesGet->thenReturn([
$this->dbMock->feedRulesGet->returns([
'jack' => ['keep' => "", 'block' => '`A|W|J|S`u'],
'sam' => ['keep' => "`B|T|X`u", 'block' => '`C`u'],
]);
Arsse::$db = $this->dbMock->get();
$f = new Feed(5, $this->base."Filtering/1");
$exp = [
'jack' => ['new' => [false, true, true, false, true], 'changed' => [7 => true, 47 => true, 2112 => false, 1 => true, 42 => false]],
'sam' => ['new' => [false, true, false, false, false], 'changed' => [7 => false, 47 => true, 2112 => false, 1 => false, 42 => false]],
];
$this->assertSame($exp, $f->filteredItems);
}
}

2
tests/cases/Feed/TestFetching.php

@ -23,7 +23,7 @@ class TestFetching extends \JKingWeb\Arsse\Test\AbstractTest {
$this->markTestSkipped("Test Web server is not accepting requests");
}
$this->base = self::$host."Feed/";
self::clearData();
parent::setUp();
self::setConf();
}

22
tests/cases/ImportExport/TestFile.php

@ -17,11 +17,11 @@ class TestFile extends \JKingWeb\Arsse\Test\AbstractTest {
protected $proc;
public function setUp(): void {
self::clearData();
parent::setUp();
// create a mock Import/Export processor with stubbed underlying import/export routines
$this->proc = \Phake::partialMock(AbstractImportExport::class);
\Phake::when($this->proc)->export->thenReturn("EXPORT_FILE");
\Phake::when($this->proc)->import->thenReturn(true);
$this->proc = $this->partialMock(AbstractImportExport::class);
$this->proc->export->returns("EXPORT_FILE");
$this->proc->import->returns(true);
$this->vfs = vfsStream::setup("root", null, [
'exportGoodFile' => "",
'exportGoodDir' => [],
@ -41,7 +41,7 @@ class TestFile extends \JKingWeb\Arsse\Test\AbstractTest {
$this->path = null;
$this->vfs = null;
$this->proc = null;
self::clearData();
parent::tearDown();
}
/** @dataProvider provideFileExports */
@ -50,13 +50,13 @@ class TestFile extends \JKingWeb\Arsse\Test\AbstractTest {
try {
if ($exp instanceof \JKingWeb\Arsse\AbstractException) {
$this->assertException($exp);
$this->proc->exportFile($path, $user, $flat);
$this->proc->get()->exportFile($path, $user, $flat);
} else {
$this->assertSame($exp, $this->proc->exportFile($path, $user, $flat));
$this->assertSame($exp, $this->proc->get()->exportFile($path, $user, $flat));
$this->assertSame("EXPORT_FILE", $this->vfs->getChild($file)->getContent());
}
} finally {
\Phake::verify($this->proc)->export($user, $flat);
$this->proc->export->calledWith($user, $flat);
}
}
@ -89,12 +89,12 @@ class TestFile extends \JKingWeb\Arsse\Test\AbstractTest {
try {
if ($exp instanceof \JKingWeb\Arsse\AbstractException) {
$this->assertException($exp);
$this->proc->importFile($path, $user, $flat, $replace);
$this->proc->get()->importFile($path, $user, $flat, $replace);
} else {
$this->assertSame($exp, $this->proc->importFile($path, $user, $flat, $replace));
$this->assertSame($exp, $this->proc->get()->importFile($path, $user, $flat, $replace));
}
} finally {
\Phake::verify($this->proc, \Phake::times((int) ($exp === true)))->import($user, "GOOD_FILE", $flat, $replace);
$this->proc->import->times((int) ($exp === true))->calledWith($user, "GOOD_FILE", $flat, $replace);
}
}

40
tests/cases/ImportExport/TestImportExport.php

@ -24,11 +24,11 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
];
public function setUp(): void {
self::clearData();
parent::setUp();
// create a mock user manager
Arsse::$user = \Phake::mock(\JKingWeb\Arsse\User::class);
Arsse::$user = $this->mock(\JKingWeb\Arsse\User::class)->get();
// create a mock Import/Export processor
$this->proc = \Phake::partialMock(AbstractImportExport::class);
$this->proc = $this->partialMock(AbstractImportExport::class);
// initialize an SQLite memeory database
static::setConf();
try {
@ -142,12 +142,12 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
public function tearDown(): void {
$this->drv = null;
$this->proc = null;
self::clearData();
parent::tearDown();
}
public function testImportForAMissingUser(): void {
$this->assertException("doesNotExist", "User", "ExceptionConflict");
$this->proc->import("no.one@example.com", "", false, false);
$this->proc->get()->import("no.one@example.com", "", false, false);
}
public function testImportWithInvalidFolder(): void {
@ -155,9 +155,9 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
], [1 =>
['id' => 1, 'name' => "", 'parent' => 0],
]];
\Phake::when($this->proc)->parse->thenReturn($in);
$this->proc->parse->returns($in);
$this->assertException("invalidFolderName", "ImportExport");
$this->proc->import("john.doe@example.com", "", false, false);
$this->proc->get()->import("john.doe@example.com", "", false, false);
}
public function testImportWithDuplicateFolder(): void {
@ -166,9 +166,9 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
['id' => 1, 'name' => "New", 'parent' => 0],
['id' => 2, 'name' => "New", 'parent' => 0],
]];
\Phake::when($this->proc)->parse->thenReturn($in);
$this->proc->parse->returns($in);
$this->assertException("invalidFolderCopy", "ImportExport");
$this->proc->import("john.doe@example.com", "", false, false);
$this->proc->get()->import("john.doe@example.com", "", false, false);
}
public function testMakeNoEffectiveChanges(): void {
@ -187,11 +187,11 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
['id' => 5, 'name' => "Local", 'parent' => 4],
['id' => 6, 'name' => "National", 'parent' => 4],
]];
\Phake::when($this->proc)->parse->thenReturn($in);
$this->proc->parse->returns($in);
$exp = $this->primeExpectations($this->data, $this->checkTables);
$this->proc->import("john.doe@example.com", "", false, false);
$this->proc->get()->import("john.doe@example.com", "", false, false);
$this->compareExpectations($this->drv, $exp);
$this->proc->import("john.doe@example.com", "", false, true);
$this->proc->get()->import("john.doe@example.com", "", false, true);
$this->compareExpectations($this->drv, $exp);
}
@ -212,8 +212,8 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
['id' => 6, 'name' => "National", 'parent' => 4],
['id' => 7, 'name' => "Nature", 'parent' => 0], // new folder
]];
\Phake::when($this->proc)->parse->thenReturn($in);
$this->proc->import("john.doe@example.com", "", false, true);
$this->proc->parse->returns($in);
$this->proc->get()->import("john.doe@example.com", "", false, true);
$exp = $this->primeExpectations($this->data, $this->checkTables);
$exp['arsse_subscriptions']['rows'][3] = [4, "john.doe@example.com", null, 4, "CBC"];
$exp['arsse_folders']['rows'][] = [7, "john.doe@example.com", null, "Nature"];
@ -224,8 +224,8 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
$in = [[
['url' => "http://localhost:8000/Import/some-feed", 'title' => "Some Feed", 'folder' => 0, 'tags' => ["frequent", "cryptic"]], //one existing tag and one new one
], []];
\Phake::when($this->proc)->parse->thenReturn($in);
$this->proc->import("john.doe@example.com", "", false, false);
$this->proc->parse->returns($in);
$this->proc->get()->import("john.doe@example.com", "", false, false);
$exp = $this->primeExpectations($this->data, $this->checkTables);
$exp['arsse_feeds']['rows'][] = [7, "http://localhost:8000/Import/some-feed", "Some feed"]; // author-supplied and user-supplied titles differ
$exp['arsse_subscriptions']['rows'][] = [7, "john.doe@example.com", null, 7, "Some Feed"];
@ -239,9 +239,9 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
$in = [[
['url' => "http://localhost:8000/Import/some-feed", 'title' => "Some Feed", 'folder' => 0, 'tags' => [""]],
], []];
\Phake::when($this->proc)->parse->thenReturn($in);
$this->proc->parse->returns($in);
$this->assertException("invalidTagName", "ImportExport");
$this->proc->import("john.doe@example.com", "", false, false);
$this->proc->get()->import("john.doe@example.com", "", false, false);
}
public function testReplaceData(): void {
@ -250,8 +250,8 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
], [1 =>
['id' => 1, 'name' => "Photography", 'parent' => 0],
]];
\Phake::when($this->proc)->parse->thenReturn($in);
$this->proc->import("john.doe@example.com", "", false, true);
$this->proc->parse->returns($in);
$this->proc->get()->import("john.doe@example.com", "", false, true);
$exp = $this->primeExpectations($this->data, $this->checkTables);
$exp['arsse_feeds']['rows'][] = [7, "http://localhost:8000/Import/some-feed", "Some feed"]; // author-supplied and user-supplied titles differ
$exp['arsse_subscriptions']['rows'] = [[7, "john.doe@example.com", 4, 7, "Some Feed"]];

32
tests/cases/ImportExport/TestOPML.php

@ -7,9 +7,11 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\ImportExport;
use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\Database;
use JKingWeb\Arsse\Test\Result;
use JKingWeb\Arsse\ImportExport\OPML;
use JKingWeb\Arsse\ImportExport\Exception;
use ReflectionMethod;
/** @covers \JKingWeb\Arsse\ImportExport\OPML<extended> */
class TestOPML extends \JKingWeb\Arsse\Test\AbstractTest {
@ -80,27 +82,25 @@ OPML_EXPORT_SERIALIZATION;
OPML_EXPORT_SERIALIZATION;
public function setUp(): void {
self::clearData();
Arsse::$db = \Phake::mock(\JKingWeb\Arsse\Database::class);
\Phake::when(Arsse::$db)->userExists->thenReturn(true);
parent::setUp();
$this->dbMock = $this->mock(Database::class);
$this->dbMock->userExists->returns(true);
$this->dbMock->folderList->with("john.doe@example.com")->returns(new Result($this->folders));
$this->dbMock->subscriptionList->with("john.doe@example.com")->returns(new Result($this->subscriptions));
$this->dbMock->tagSummarize->with("john.doe@example.com")->returns(new Result($this->tags));
Arsse::$db = $this->dbMock->get();
}
public function testExportToOpml(): void {
\Phake::when(Arsse::$db)->folderList("john.doe@example.com")->thenReturn(new Result($this->folders));
\Phake::when(Arsse::$db)->subscriptionList("john.doe@example.com")->thenReturn(new Result($this->subscriptions));
\Phake::when(Arsse::$db)->tagSummarize("john.doe@example.com")->thenReturn(new Result($this->tags));
$this->assertXmlStringEqualsXmlString($this->serialization, (new OPML)->export("john.doe@example.com"));
}
public function testExportToFlatOpml(): void {
\Phake::when(Arsse::$db)->folderList("john.doe@example.com")->thenReturn(new Result($this->folders));
\Phake::when(Arsse::$db)->subscriptionList("john.doe@example.com")->thenReturn(new Result($this->subscriptions));
\Phake::when(Arsse::$db)->tagSummarize("john.doe@example.com")->thenReturn(new Result($this->tags));
$this->assertXmlStringEqualsXmlString($this->serializationFlat, (new OPML)->export("john.doe@example.com", true));
}
public function testExportToOpmlAMissingUser(): void {
\Phake::when(Arsse::$db)->userExists->thenReturn(false);
$this->dbMock->userExists->returns(false);
$this->assertException("doesNotExist", "User", "ExceptionConflict");
(new OPML)->export("john.doe@example.com");
}
@ -108,13 +108,15 @@ OPML_EXPORT_SERIALIZATION;
/** @dataProvider provideParserData */
public function testParseOpmlForImport(string $file, bool $flat, $exp): void {
$data = file_get_contents(\JKingWeb\Arsse\DOCROOT."Import/OPML/$file");
// set up a partial mock to make the ImportExport::parse() method visible
$parser = \Phake::makeVisible(\Phake::partialMock(OPML::class));
if ($exp instanceof \JKingWeb\Arsse\AbstractException) {
// make the ImportExport::parse() method visible
$parser = new OPML;
$parseFunc = new \ReflectionMethod($parser, "parse");
$parseFunc->setAccessible(true);
if ($exp instanceof \Exception) {
$this->assertException($exp);
$parser->parse($data, $flat);
$parseFunc->invoke($parser, $data, $flat);
} else {
$this->assertSame($exp, $parser->parse($data, $flat));
$this->assertSame($exp, $parseFunc->invoke($parser, $data, $flat));
}
}

4
tests/cases/Misc/TestDate.php

@ -10,10 +10,6 @@ use JKingWeb\Arsse\Misc\Date;
/** @covers \JKingWeb\Arsse\Misc\Date */
class TestDate extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
}
public function testNormalizeADate(): void {
$exp = new \DateTimeImmutable("2018-01-01T00:00:00Z");
$this->assertEquals($exp, Date::normalize(1514764800));

3
tests/cases/Misc/TestURL.php

@ -10,9 +10,6 @@ use JKingWeb\Arsse\Misc\URL;
/** @covers \JKingWeb\Arsse\Misc\URL */
class TestURL extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
}
/** @dataProvider provideNormalizations */
public function testNormalizeAUrl(string $url, string $exp, string $user = null, string $pass = null): void {

3
tests/cases/Misc/TestValueInfo.php

@ -12,9 +12,6 @@ use JKingWeb\Arsse\Test\Result;
/** @covers \JKingWeb\Arsse\Misc\ValueInfo */
class TestValueInfo extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
}
public function testGetIntegerInfo(): void {
$tests = [

169
tests/cases/REST/Fever/TestAPI.php

@ -24,7 +24,8 @@ use Laminas\Diactoros\Response\EmptyResponse;
class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
/** @var \JKingWeb\Arsse\REST\Fever\API */
protected $h;
protected $hMock;
protected $userId = "john.doe@example.com";
protected $articles = [
'db' => [
[
@ -141,35 +142,35 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
],
],
];
protected function v($value) {
return $value;
}
protected function req($dataGet, $dataPost = "", string $method = "POST", string $type = null, string $target = "", string $user = null): ServerRequest {
protected function req($dataGet, $dataPost = "", string $method = "POST", ?string $type = null, string $target = "", ?string $user = null): ResponseInterface {
Arsse::$db = $this->dbMock->get();
$this->h = $this->hMock->get();
$prefix = "/fever/";
$url = $prefix.$target;
$type = $type ?? "application/x-www-form-urlencoded";
return $this->serverRequest($method, $url, $prefix, [], [], $dataPost, $type, $dataGet, $user);
return $this->h->dispatch($this->serverRequest($method, $url, $prefix, [], [], $dataPost, $type, $dataGet, $user));
}
public function setUp(): void {
self::clearData();
self::setConf();
// create a mock user manager
Arsse::$user = \Phake::mock(User::class);
\Phake::when(Arsse::$user)->auth->thenReturn(true);
Arsse::$user->id = "john.doe@example.com";
$this->userMock = $this->mock(User::class);
$this->userMock->auth->returns(true);
Arsse::$user = $this->userMock->get();
Arsse::$user->id = $this->userId;
// create a mock database interface
Arsse::$db = \Phake::mock(Database::class);
\Phake::when(Arsse::$db)->begin->thenReturn(\Phake::mock(Transaction::class));
\Phake::when(Arsse::$db)->tokenLookup->thenReturn(['user' => "john.doe@example.com"]);
$this->dbMock = $this->mock(Database::class);
$this->dbMock->begin->returns($this->mock(Transaction::class));
$this->dbMock->tokenLookup->returns(['user' => "john.doe@example.com"]);
// instantiate the handler as a partial mock to simplify testing
$this->h = \Phake::partialMock(API::class);
\Phake::when($this->h)->baseResponse->thenReturn([]);
}
public function tearDown(): void {
self::clearData();
$this->hMock = $this->partialMock(API::class);
$this->hMock->baseResponse->returns([]);
}
/** @dataProvider provideTokenAuthenticationRequests */
@ -179,17 +180,16 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
'userSessionEnforced' => $tokenEnforced,
], true);
Arsse::$user->id = null;
\Phake::when(Arsse::$db)->tokenLookup->thenThrow(new ExceptionInput("subjectMissing"));
\Phake::when(Arsse::$db)->tokenLookup("fever.login", "validtoken")->thenReturn(['user' => "jane.doe@example.com"]);
$this->dbMock->tokenLookup->throws(new ExceptionInput("subjectMissing"));
$this->dbMock->tokenLookup->with("fever.login", "validtoken")->returns(['user' => "jane.doe@example.com"]);
// test only the authentication process
\Phake::when($this->h)->baseResponse->thenReturnCallback(function(bool $authenticated) {
$this->hMock->baseResponse->does(function(bool $authenticated) {
return ['auth' => (int) $authenticated];
});
\Phake::when($this->h)->processRequest->thenReturnCallback(function($out, $G, $P) {
$this->hMock->processRequest->does(function($out, $G, $P) {
return $out;
});
$act = $this->h->dispatch($this->req($dataGet, $dataPost, "POST", null, "", $httpUser));
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req($dataGet, $dataPost, "POST", null, "", $httpUser));
}
public function provideTokenAuthenticationRequests(): iterable {
@ -245,12 +245,12 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function testListGroups(): void {
\Phake::when(Arsse::$db)->tagList(Arsse::$user->id)->thenReturn(new Result([
$this->dbMock->tagList->with($this->userId)->returns(new Result([
['id' => 1, 'name' => "Fascinating", 'subscriptions' => 2],
['id' => 2, 'name' => "Interesting", 'subscriptions' => 2],
['id' => 3, 'name' => "Boring", 'subscriptions' => 0],
]));
\Phake::when(Arsse::$db)->tagSummarize(Arsse::$user->id)->thenReturn(new Result([
$this->dbMock->tagSummarize->with($this->userId)->returns(new Result([
['id' => 1, 'name' => "Fascinating", 'subscription' => 1],
['id' => 1, 'name' => "Fascinating", 'subscription' => 2],
['id' => 2, 'name' => "Interesting", 'subscription' => 1],
@ -267,17 +267,16 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
['group_id' => 2, 'feed_ids' => "1,3"],
],
]);
$act = $this->h->dispatch($this->req("api&groups"));
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api&groups"));
}
public function testListFeeds(): void {
\Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([
$this->dbMock->subscriptionList->with($this->userId)->returns(new Result([
['id' => 1, 'feed' => 5, 'title' => "Ankh-Morpork News", 'url' => "http://example.com/feed", 'source' => "http://example.com/", 'edited' => "2019-01-01 21:12:00", 'icon_url' => "http://example.com/favicon.ico", 'icon_id' => 42],
['id' => 2, 'feed' => 9, 'title' => "Ook, Ook Eek Ook!", 'url' => "http://example.net/feed", 'source' => "http://example.net/", 'edited' => "1988-06-24 12:21:00", 'icon_url' => "", 'icon_id' => null],
['id' => 3, 'feed' => 1, 'title' => "The Last Soul", 'url' => "http://example.org/feed", 'source' => "http://example.org/", 'edited' => "1991-08-12 03:22:00", 'icon_url' => "http://example.org/favicon.ico", 'icon_id' => 42],
]));
\Phake::when(Arsse::$db)->tagSummarize(Arsse::$user->id)->thenReturn(new Result([
$this->dbMock->tagSummarize->with($this->userId)->returns(new Result([
['id' => 1, 'name' => "Fascinating", 'subscription' => 1],
['id' => 1, 'name' => "Fascinating", 'subscription' => 2],
['id' => 2, 'name' => "Interesting", 'subscription' => 1],
@ -294,23 +293,21 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
['group_id' => 2, 'feed_ids' => "1,3"],
],
]);
$act = $this->h->dispatch($this->req("api&feeds"));
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api&feeds"));
}
/** @dataProvider provideItemListContexts */
public function testListItems(string $url, Context $c, bool $desc): void {
$fields = ["id", "subscription", "title", "author", "content", "url", "starred", "unread", "published_date"];
$order = [$desc ? "id desc" : "id"];
\Phake::when(Arsse::$db)->articleList->thenReturn(new Result($this->articles['db']));
\Phake::when(Arsse::$db)->articleCount(Arsse::$user->id, (new Context)->hidden(false))->thenReturn(1024);
$this->dbMock->articleList->returns(new Result($this->articles['db']));
$this->dbMock->articleCount->with($this->userId, (new Context)->hidden(false))->returns(1024);
$exp = new JsonResponse([
'items' => $this->articles['rest'],
'total_items' => 1024,
]);
$act = $this->h->dispatch($this->req("api&$url"));
$this->assertMessage($exp, $act);
\Phake::verify(Arsse::$db)->articleList(Arsse::$user->id, $c, $fields, $order);
$this->assertMessage($exp, $this->req("api&$url"));
$this->dbMock->articleList->calledWith($this->userId, $this->equalTo($c), $fields, $order);
}
public function provideItemListContexts(): iterable {
@ -332,35 +329,34 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
public function testListItemIds(): void {
$saved = [['id' => 1],['id' => 2],['id' => 3]];
$unread = [['id' => 4],['id' => 5],['id' => 6]];
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->starred(true)->hidden(false))->thenReturn(new Result($saved));
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->unread(true)->hidden(false))->thenReturn(new Result($unread));
$this->dbMock->articleList->with($this->userId, (new Context)->starred(true)->hidden(false))->returns(new Result($saved));
$this->dbMock->articleList->with($this->userId, (new Context)->unread(true)->hidden(false))->returns(new Result($unread));
$exp = new JsonResponse(['saved_item_ids' => "1,2,3"]);
$this->assertMessage($exp, $this->h->dispatch($this->req("api&saved_item_ids")));
$this->assertMessage($exp, $this->req("api&saved_item_ids"));
$exp = new JsonResponse(['unread_item_ids' => "4,5,6"]);
$this->assertMessage($exp, $this->h->dispatch($this->req("api&unread_item_ids")));
$this->assertMessage($exp, $this->req("api&unread_item_ids"));
}
public function testListHotLinks(): void {
// hot links are not actually implemented, so an empty array should be all we get
$exp = new JsonResponse(['links' => []]);
$this->assertMessage($exp, $this->h->dispatch($this->req("api&links")));
$this->assertMessage($exp, $this->req("api&links"));
}
/** @dataProvider provideMarkingContexts */
public function testSetMarks(string $post, Context $c, array $data, array $out): void {
$saved = [['id' => 1],['id' => 2],['id' => 3]];
$unread = [['id' => 4],['id' => 5],['id' => 6]];
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->starred(true)->hidden(false))->thenReturn(new Result($saved));
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->unread(true)->hidden(false))->thenReturn(new Result($unread));
\Phake::when(Arsse::$db)->articleMark->thenReturn(0);
\Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->article(2112))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
$this->dbMock->articleList->with($this->userId, (new Context)->starred(true)->hidden(false))->returns(new Result($saved));
$this->dbMock->articleList->with($this->userId, (new Context)->unread(true)->hidden(false))->returns(new Result($unread));
$this->dbMock->articleMark->returns(0);
$this->dbMock->articleMark->with($this->userId, $this->anything(), (new Context)->article(2112))->throws(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
$exp = new JsonResponse($out);
$act = $this->h->dispatch($this->req("api", $post));
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api", $post));
if ($c && $data) {
\Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $data, $c);
$this->dbMock->articleMark->calledWith($this->userId, $data, $this->equalTo($c));
} else {
\Phake::verify(Arsse::$db, \Phake::times(0))->articleMark;
$this->dbMock->articleMark->never()->called();
}
}
@ -368,17 +364,16 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
public function testSetMarksWithQuery(string $get, Context $c, array $data, array $out): void {
$saved = [['id' => 1],['id' => 2],['id' => 3]];
$unread = [['id' => 4],['id' => 5],['id' => 6]];
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->starred(true)->hidden(false))->thenReturn(new Result($saved));
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->unread(true)->hidden(false))->thenReturn(new Result($unread));
\Phake::when(Arsse::$db)->articleMark->thenReturn(0);
\Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->article(2112))->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
$this->dbMock->articleList->with($this->userId, (new Context)->starred(true)->hidden(false))->returns(new Result($saved));
$this->dbMock->articleList->with($this->userId, (new Context)->unread(true)->hidden(false))->returns(new Result($unread));
$this->dbMock->articleMark->returns(0);
$this->dbMock->articleMark->with($this->userId, $this->anything(), (new Context)->article(2112))->throws(new \JKingWeb\Arsse\Db\ExceptionInput("subjectMissing"));
$exp = new JsonResponse($out);
$act = $this->h->dispatch($this->req("api&$get"));
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api&$get"));
if ($c && $data) {
\Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $data, $c);
$this->dbMock->articleMark->calledWith($this->userId, $data, $this->equalTo($c));
} else {
\Phake::verify(Arsse::$db, \Phake::times(0))->articleMark;
$this->dbMock->articleMark->never()->called();
}
}
@ -421,97 +416,89 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest {
}
/** @dataProvider provideInvalidRequests */
public function testSendInvalidRequests(ServerRequest $req, ResponseInterface $exp): void {
$this->assertMessage($exp, $this->h->dispatch($req));
public function testSendInvalidRequests(string $get, string $post, string $method, ?string $type, ResponseInterface $exp): void {
$this->assertMessage($exp, $this->req($get, $post, $method, $type));
}
public function provideInvalidRequests(): iterable {
return [
'Not an API request' => [$this->req(""), new EmptyResponse(404)],
'Wrong method' => [$this->req("api", "", "PUT"), new EmptyResponse(405, ['Allow' => "OPTIONS,POST"])],
'Non-standard method' => [$this->req("api", "", "GET"), new JsonResponse([])],
'Wrong content type' => [$this->req("api", '{"api_key":"validToken"}', "POST", "application/json"), new EmptyResponse(415, ['Accept' => "application/x-www-form-urlencoded, multipart/form-data"])],
'Non-standard content type' => [$this->req("api", '{"api_key":"validToken"}', "POST", "multipart/form-data; boundary=33b68964f0de4c1f-5144aa6caaa6e4a8-18bfaf416a1786c8-5c5053a45f221bc1"), new JsonResponse([])],
'Not an API request' => ["", "", "POST", null, new EmptyResponse(404)],
'Wrong method' => ["api", "", "PUT", null, new EmptyResponse(405, ['Allow' => "OPTIONS,POST"])],
'Non-standard method' => ["api", "", "GET", null, new JsonResponse([])],
'Wrong content type' => ["api", '{"api_key":"validToken"}', "POST", "application/json", new EmptyResponse(415, ['Accept' => "application/x-www-form-urlencoded, multipart/form-data"])],
'Non-standard content type' => ["api", '{"api_key":"validToken"}', "POST", "multipart/form-data; boundary=33b68964f0de4c1f-5144aa6caaa6e4a8-18bfaf416a1786c8-5c5053a45f221bc1", new JsonResponse([])],
];
}
public function testMakeABaseQuery(): void {
$this->h = \Phake::partialMock(API::class);
\Phake::when($this->h)->logIn->thenReturn(true);
\Phake::when(Arsse::$db)->subscriptionRefreshed(Arsse::$user->id)->thenReturn(new \DateTimeImmutable("2000-01-01T00:00:00Z"));
$this->hMock->baseResponse->forwards();
$this->hMock->logIn->returns(true);
$this->dbMock->subscriptionRefreshed->with($this->userId)->returns(new \DateTimeImmutable("2000-01-01T00:00:00Z"));
$exp = new JsonResponse([
'api_version' => API::LEVEL,
'auth' => 1,
'last_refreshed_on_time' => 946684800,
]);
$act = $this->h->dispatch($this->req("api"));
$this->assertMessage($exp, $act);
\Phake::when(Arsse::$db)->subscriptionRefreshed(Arsse::$user->id)->thenReturn(null); // no subscriptions
$this->assertMessage($exp, $this->req("api"));
$this->dbMock->subscriptionRefreshed->with($this->userId)->returns(null); // no subscriptions
$exp = new JsonResponse([
'api_version' => API::LEVEL,
'auth' => 1,
'last_refreshed_on_time' => null,
]);
$act = $this->h->dispatch($this->req("api"));
$this->assertMessage($exp, $act);
\Phake::when($this->h)->logIn->thenReturn(false);
$this->assertMessage($exp, $this->req("api"));
$this->hMock->logIn->returns(false);
$exp = new JsonResponse([
'api_version' => API::LEVEL,
'auth' => 0,
]);
$act = $this->h->dispatch($this->req("api"));
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api"));
}
public function testUndoReadMarks(): void {
$unread = [['id' => 4],['id' => 5],['id' => 6]];
$out = ['unread_item_ids' => "4,5,6"];
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->limit(1)->hidden(false), ["marked_date"], ["marked_date desc"])->thenReturn(new Result([['marked_date' => "2000-01-01 00:00:00"]]));
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->unread(true)->hidden(false))->thenReturn(new Result($unread));
\Phake::when(Arsse::$db)->articleMark->thenReturn(0);
$this->dbMock->articleList->with($this->userId, $this->equalTo((new Context)->limit(1)->hidden(false)), ["marked_date"], ["marked_date desc"])->returns(new Result([['marked_date' => "2000-01-01 00:00:00"]]));
$this->dbMock->articleList->with($this->userId, $this->equalTo((new Context)->unread(true)->hidden(false)))->returns(new Result($unread));
$this->dbMock->articleMark->returns(0);
$exp = new JsonResponse($out);
$act = $this->h->dispatch($this->req("api", ['unread_recently_read' => 1]));
$this->assertMessage($exp, $act);
\Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, ['read' => false], (new Context)->unread(false)->markedSince("1999-12-31T23:59:45Z")->hidden(false));
\Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->limit(1)->hidden(false), ["marked_date"], ["marked_date desc"])->thenReturn(new Result([]));
$act = $this->h->dispatch($this->req("api", ['unread_recently_read' => 1]));
$this->assertMessage($exp, $act);
\Phake::verify(Arsse::$db)->articleMark; // only called one time, above
$this->assertMessage($exp, $this->req("api", ['unread_recently_read' => 1]));
$this->dbMock->articleMark->calledWith($this->userId, ['read' => false], $this->equalTo((new Context)->unread(false)->markedSince("1999-12-31T23:59:45Z")->hidden(false)));
$this->dbMock->articleList->with($this->userId, (new Context)->limit(1)->hidden(false), ["marked_date"], ["marked_date desc"])->returns(new Result([]));
$this->assertMessage($exp, $this->req("api", ['unread_recently_read' => 1]));
$this->dbMock->articleMark->once()->called(); // only called one time, above
}
public function testOutputToXml(): void {
\Phake::when($this->h)->processRequest->thenReturn([
$this->hMock->processRequest->returns([
'items' => $this->articles['rest'],
'total_items' => 1024,
]);
$exp = new XmlResponse("<response><items><item><id>101</id><feed_id>8</feed_id><title>Article title 1</title><author></author><html>&lt;p&gt;Article content 1&lt;/p&gt;</html><url>http://example.com/1</url><is_saved>0</is_saved><is_read>0</is_read><created_on_time>946684800</created_on_time></item><item><id>102</id><feed_id>8</feed_id><title>Article title 2</title><author></author><html>&lt;p&gt;Article content 2&lt;/p&gt;</html><url>http://example.com/2</url><is_saved>0</is_saved><is_read>1</is_read><created_on_time>946771200</created_on_time></item><item><id>103</id><feed_id>9</feed_id><title>Article title 3</title><author></author><html>&lt;p&gt;Article content 3&lt;/p&gt;</html><url>http://example.com/3</url><is_saved>1</is_saved><is_read>0</is_read><created_on_time>946857600</created_on_time></item><item><id>104</id><feed_id>9</feed_id><title>Article title 4</title><author></author><html>&lt;p&gt;Article content 4&lt;/p&gt;</html><url>http://example.com/4</url><is_saved>1</is_saved><is_read>1</is_read><created_on_time>946944000</created_on_time></item><item><id>105</id><feed_id>10</feed_id><title>Article title 5</title><author></author><html>&lt;p&gt;Article content 5&lt;/p&gt;</html><url>http://example.com/5</url><is_saved>0</is_saved><is_read>0</is_read><created_on_time>947030400</created_on_time></item></items><total_items>1024</total_items></response>");
$act = $this->h->dispatch($this->req("api=xml"));
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api=xml"));
}
public function testListFeedIcons(): void {
$iconType = (new \ReflectionClassConstant(API::class, "GENERIC_ICON_TYPE"))->getValue();
$iconData = (new \ReflectionClassConstant(API::class, "GENERIC_ICON_DATA"))->getValue();
\Phake::when(Arsse::$db)->iconList->thenReturn(new Result($this->v([
$this->dbMock->iconList->returns(new Result($this->v([
['id' => 42, 'type' => "image/svg+xml", 'data' => "<svg/>"],
['id' => 44, 'type' => null, 'data' => "IMAGE DATA"],
['id' => 47, 'type' => null, 'data' => null],
])));
$act = $this->h->dispatch($this->req("api&favicons"));
$exp = new JsonResponse(['favicons' => [
['id' => 0, 'data' => $iconType.",".$iconData],
['id' => 42, 'data' => "image/svg+xml;base64,PHN2Zy8+"],
['id' => 44, 'data' => "application/octet-stream;base64,SU1BR0UgREFUQQ=="],
]]);
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api&favicons"));
}
public function testAnswerOptionsRequest(): void {
$act = $this->h->dispatch($this->req("api", "", "OPTIONS"));
$exp = new EmptyResponse(204, [
'Allow' => "POST",
'Accept' => "application/x-www-form-urlencoded, multipart/form-data",
]);
$this->assertMessage($exp, $act);
$this->assertMessage($exp, $this->req("api", "", "OPTIONS"));
}
}

6
tests/cases/REST/Fever/TestUser.php

@ -19,7 +19,7 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
protected $u;
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
// create a mock user manager
Arsse::$user = \Phake::mock(User::class);
@ -31,10 +31,6 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
$this->u = new FeverUser();
}
public function tearDown(): void {
self::clearData();
}
/** @dataProvider providePasswordCreations */
public function testRegisterAUserPassword(string $user, string $password = null, $exp): void {
\Phake::when(Arsse::$user)->generatePassword->thenReturn("RANDOM_PASSWORD");

6
tests/cases/REST/Miniflux/TestToken.php

@ -21,7 +21,7 @@ class TestToken extends \JKingWeb\Arsse\Test\AbstractTest {
protected $transaction;
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
// create a mock database interface
Arsse::$db = \Phake::mock(Database::class);
@ -30,10 +30,6 @@ class TestToken extends \JKingWeb\Arsse\Test\AbstractTest {
$this->h = new Token();
}
public function tearDown(): void {
self::clearData();
}
protected function v($value) {
return $value;
}

6
tests/cases/REST/Miniflux/TestV1.php

@ -71,7 +71,7 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
// create a mock database interface
Arsse::$db = \Phake::mock(Database::class);
@ -85,10 +85,6 @@ class TestV1 extends \JKingWeb\Arsse\Test\AbstractTest {
$this->h = new V1();
}
public function tearDown(): void {
self::clearData();
}
protected function v($value) {
return $value;
}

6
tests/cases/REST/NextcloudNews/TestV1_2.php

@ -312,7 +312,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
}
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
// create a mock user manager
Arsse::$user = \Phake::mock(User::class);
@ -326,10 +326,6 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest {
$this->h = new V1_2();
}
public function tearDown(): void {
self::clearData();
}
protected function v($value) {
return $value;
}

2
tests/cases/REST/NextcloudNews/TestVersions.php

@ -14,7 +14,7 @@ use Laminas\Diactoros\Response\EmptyResponse;
/** @covers \JKingWeb\Arsse\REST\NextcloudNews\Versions */
class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
parent::setUp();
}
protected function req(string $method, string $target): ResponseInterface {

51
tests/cases/REST/TestREST.php

@ -63,11 +63,12 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
public function testAuthenticateRequests(array $serverParams, array $expAttr): void {
$r = new REST();
// create a mock user manager
Arsse::$user = \Phake::mock(User::class);
\Phake::when(Arsse::$user)->auth->thenReturn(false);
\Phake::when(Arsse::$user)->auth("john.doe@example.com", "secret")->thenReturn(true);
\Phake::when(Arsse::$user)->auth("john.doe@example.com", "")->thenReturn(true);
\Phake::when(Arsse::$user)->auth("someone.else@example.com", "")->thenReturn(true);
$this->userMock = $this->mock(User::class);
$this->userMock->auth->returns(false);
$this->userMock->auth->with("john.doe@example.com", "secret")->returns(true);
$this->userMock->auth->with("john.doe@example.com", "")->returns(true);
$this->userMock->auth->with("someone.else@example.com", "")->returns(true);
Arsse::$user = $this->userMock->get();
// create an input server request
$req = new ServerRequest($serverParams);
// create the expected output
@ -150,13 +151,13 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideCorsNegotiations */
public function testNegotiateCors($origin, bool $exp, string $allowed = null, string $denied = null): void {
self::setConf();
$r = \Phake::partialMock(REST::class);
\Phake::when($r)->corsNormalizeOrigin->thenReturnCallback(function($origin) {
$rMock = $this->partialMock(REST::class);
$rMock->corsNormalizeOrigin->does(function($origin) {
return $origin;
});
$headers = isset($origin) ? ['Origin' => $origin] : [];
$req = new Request("", "GET", "php://memory", $headers);
$act = $r->corsNegotiate($req, $allowed, $denied);
$act = $rMock->get()->corsNegotiate($req, $allowed, $denied);
$this->assertSame($exp, $act);
}
@ -251,15 +252,15 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideUnnormalizedResponses */
public function testNormalizeHttpResponses(ResponseInterface $res, ResponseInterface $exp, RequestInterface $req = null): void {
$r = \Phake::partialMock(REST::class);
\Phake::when($r)->corsNegotiate->thenReturn(true);
\Phake::when($r)->challenge->thenReturnCallback(function($res) {
$rMock = $this->partialMock(REST::class);
$rMock->corsNegotiate->returns(true);
$rMock->challenge->does(function($res) {
return $res->withHeader("WWW-Authenticate", "Fake Value");
});
\Phake::when($r)->corsApply->thenReturnCallback(function($res) {
$rMock->corsApply->does(function($res) {
return $res;
});
$act = $r->normalizeResponse($res, $req);
$act = $rMock->get()->normalizeResponse($res, $req);
$this->assertMessage($exp, $act);
}
@ -287,30 +288,32 @@ class TestREST extends \JKingWeb\Arsse\Test\AbstractTest {
/** @dataProvider provideMockRequests */
public function testDispatchRequests(ServerRequest $req, string $method, bool $called, string $class = "", string $target = ""): void {
$r = \Phake::partialMock(REST::class);
\Phake::when($r)->normalizeResponse->thenReturnCallback(function($res) {
$rMock = $this->partialMock(REST::class);
$rMock->normalizeResponse->does(function($res) {
return $res;
});
\Phake::when($r)->authenticateRequest->thenReturnCallback(function($req) {
$rMock->authenticateRequest->does(function($req) {
return $req;
});
if ($called) {
$h = \Phake::mock($class);
\Phake::when(Arsse::$obj)->get($class)->thenReturn($h);
\Phake::when($h)->dispatch->thenReturn(new EmptyResponse(204));
$hMock = $this->mock($class);
$hMock->dispatch->returns(new EmptyResponse(204));
$this->objMock->get->with($class)->returns($hMock);
Arsse::$obj = $this->objMock->get();
}
$out = $r->dispatch($req);
$out = $rMock->get()->dispatch($req);
$this->assertInstanceOf(ResponseInterface::class, $out);
if ($called) {
\Phake::verify($r)->authenticateRequest;
\Phake::verify($h)->dispatch(\Phake::capture($in));
$rMock->authenticateRequest->called();
$hMock->dispatch->once()->called();
$in = $hMock->dispatch->firstCall()->argument();;
$this->assertSame($method, $in->getMethod());
$this->assertSame($target, $in->getRequestTarget());
} else {
$this->assertSame(501, $out->getStatusCode());
}
\Phake::verify($r)->apiMatch;
\Phake::verify($r)->normalizeResponse;
$rMock->apiMatch->called();
$rMock->normalizeResponse->called();
}
public function provideMockRequests(): iterable {

6
tests/cases/REST/TinyTinyRSS/TestIcon.php

@ -20,7 +20,7 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
protected $user = "john.doe@example.com";
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
// create a mock user manager
Arsse::$user = \Phake::mock(User::class);
@ -29,10 +29,6 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest {
$this->h = new Icon();
}
public function tearDown(): void {
self::clearData();
}
protected function req(string $target, string $method = "GET", string $user = null): ResponseInterface {
$prefix = "/tt-rss/feed-icons/";
$url = $prefix.$target;

2
tests/cases/Service/TestSerial.php

@ -14,7 +14,7 @@ use JKingWeb\Arsse\Service\Serial\Driver;
/** @covers \JKingWeb\Arsse\Service\Serial\Driver */
class TestSerial extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
Arsse::$db = \Phake::mock(Database::class);
}

2
tests/cases/Service/TestService.php

@ -16,7 +16,7 @@ class TestService extends \JKingWeb\Arsse\Test\AbstractTest {
protected $srv;
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
Arsse::$db = \Phake::mock(Database::class);
$this->srv = new Service();

2
tests/cases/Service/TestSubprocess.php

@ -13,7 +13,7 @@ use JKingWeb\Arsse\Service\Subprocess\Driver;
/** @covers \JKingWeb\Arsse\Service\Subprocess\Driver */
class TestSubprocess extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
}

23
tests/cases/TestArsse.php

@ -17,22 +17,23 @@ class TestArsse extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData(false);
}
public function tearDown(): void {
self::clearData();
}
public function testLoadExistingData(): void {
$lang = Arsse::$lang = \Phake::mock(Lang::class);
$db = Arsse::$db = \Phake::mock(Database::class);
$user = Arsse::$user = \Phake::mock(User::class);
$conf1 = Arsse::$conf = \Phake::mock(Conf::class);
$lang = $this->mock(Lang::class);
$db = $this->mock(Database::class);
$user = $this->mock(User::class);
$conf1 = $this->mock(Conf::class);
Arsse::$lang = $lang->get();
Arsse::$db = $db->get();
Arsse::$user = $user->get();
Arsse::$conf = $conf1->get();
$conf2 = (new Conf)->import(['lang' => "test"]);
Arsse::load($conf2);
$this->assertSame($conf2, Arsse::$conf);
$this->assertSame($lang, Arsse::$lang);
$this->assertSame($db, Arsse::$db);
$this->assertSame($user, Arsse::$user);
\Phake::verify($lang)->set("test");
$this->assertSame($lang->get(), Arsse::$lang);
$this->assertSame($db->get(), Arsse::$db);
$this->assertSame($user->get(), Arsse::$user);
$lang->set->calledWith("test");
}
public function testLoadNewData(): void {

2
tests/cases/User/TestInternal.php

@ -14,7 +14,7 @@ use JKingWeb\Arsse\User\Internal\Driver;
/** @covers \JKingWeb\Arsse\User\Internal\Driver */
class TestInternal extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
// create a mock database interface
Arsse::$db = \Phake::mock(Database::class);

2
tests/cases/User/TestUser.php

@ -17,7 +17,7 @@ use JKingWeb\Arsse\User\Driver;
/** @covers \JKingWeb\Arsse\User */
class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
public function setUp(): void {
self::clearData();
parent::setUp();
self::setConf();
// create a mock database interface
Arsse::$db = \Phake::mock(Database::class);

39
tests/lib/AbstractTest.php

@ -6,6 +6,8 @@
declare(strict_types=1);
namespace JKingWeb\Arsse\Test;
use Eloquent\Phony\Mock\Handle\InstanceHandle;
use Eloquent\Phony\Phpunit\Phony;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use JKingWeb\Arsse\Exception;
@ -29,12 +31,20 @@ use Laminas\Diactoros\Response\XmlResponse;
abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
use \DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
public function setUp(): void {
self::clearData();
}
protected $objMock;
protected $confMock;
protected $langMock;
protected $dbMock;
protected $userMock;
public function tearDown(): void {
public function setUp(): void {
self::clearData();
// create the object factory as a mock
$this->objMock = Arsse::$obj = $this->mock(Factory::class);
$this->objMock->get->does(function(string $class) {
return new $class;
});
}
public static function clearData(bool $loadLang = true): void {
@ -46,11 +56,6 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
}
if ($loadLang) {
Arsse::$lang = new \JKingWeb\Arsse\Lang();
// also create the object factory as a mock
Arsse::$obj = \Phake::mock(Factory::class);
\Phake::when(Arsse::$obj)->get->thenReturnCallback(function(string $class) {
return new $class;
});
}
}
@ -340,12 +345,20 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase {
/** Guzzle's exception classes require some fairly complicated construction; this abstracts it all away so that only message and code need be supplied */
protected function mockGuzzleException(string $class, ?string $message = null, ?int $code = null, ?\Throwable $e = null): GuzzleException {
if (is_a($class, RequestException::class, true)) {
$req = \Phake::mock(RequestInterface::class);
$res = \Phake::mock(ResponseInterface::class);
\Phake::when($res)->getStatusCode->thenReturn($code ?? 0);
return new $class($message ?? "", $req, $res, $e);
$req = $this->mock(RequestInterface::class);
$res = $this->mock(ResponseInterface::class);
$res->getStatusCode->returns($code ?? 0);
return new $class($message ?? "", $req->get(), $res->get(), $e);
} else {
return new $class($message ?? "", $code ?? 0, $e);
}
}
protected function mock(string $class): InstanceHandle {
return Phony::mock($class);
}
protected function partialMock(string $class, ...$argument): InstanceHandle {
return Phony::partialMock($class, $argument);
}
}

3
vendor-bin/phpunit/composer.json

@ -5,6 +5,7 @@
"phake/phake": "^3.0",
"clue/arguments": "^2.0",
"mikey179/vfsstream": "^1.6",
"webmozart/glob": "^4.1"
"webmozart/glob": "^4.1",
"eloquent/phony-phpunit": "^6.0 || ^7.0"
}
}

149
vendor-bin/phpunit/composer.lock

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9c0d657e9fcab3d3be0467bbdc300a0a",
"content-hash": "fda9bd2446005dfe56a223890cad0849",
"packages": [],
"packages-dev": [
{
@ -188,6 +188,153 @@
],
"time": "2020-11-10T18:47:58+00:00"
},
{
"name": "eloquent/phony",
"version": "5.0.2",
"source": {
"type": "git",
"url": "https://github.com/eloquent/phony.git",
"reference": "f34d67d6db6b6f351ea7e8aa8066107e756ec26b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/eloquent/phony/zipball/f34d67d6db6b6f351ea7e8aa8066107e756ec26b",
"reference": "f34d67d6db6b6f351ea7e8aa8066107e756ec26b",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8"
},
"require-dev": {
"eloquent/code-style": "^1.0",
"eloquent/phpstan-phony": "^0.7",
"errors/exceptions": "^0.2",
"ext-pdo": "*",
"friendsofphp/php-cs-fixer": "^2",
"hamcrest/hamcrest-php": "^2",
"phpstan/extension-installer": "^1",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpunit/phpunit": "^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Eloquent\\Phony\\": "src"
},
"files": [
"src/initialize.php",
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Erin Millard",
"email": "ezzatron@gmail.com",
"homepage": "http://ezzatron.com/"
}
],
"description": "Mocks, stubs, and spies for PHP.",
"homepage": "http://eloquent-software.com/phony/",
"keywords": [
"Double",
"Dummy",
"fake",
"mock",
"mocking",
"spy",
"stub",
"stubbing",
"test"
],
"support": {
"issues": "https://github.com/eloquent/phony/issues",
"source": "https://github.com/eloquent/phony/tree/5.0.2"
},
"time": "2021-02-17T01:45:10+00:00"
},
{
"name": "eloquent/phony-phpunit",
"version": "7.1.0",
"source": {
"type": "git",
"url": "https://github.com/eloquent/phony-phpunit.git",
"reference": "e77ff95ea6235211d4aae7e5f53488a5faebc2e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/eloquent/phony-phpunit/zipball/e77ff95ea6235211d4aae7e5f53488a5faebc2e0",
"reference": "e77ff95ea6235211d4aae7e5f53488a5faebc2e0",
"shasum": ""
},
"require": {
"eloquent/phony": "^5",
"php": "^7.3 || ^8",
"phpunit/phpunit": "^9"
},
"require-dev": {
"eloquent/code-style": "^1",
"eloquent/phpstan-phony": "^0.7",
"errors/exceptions": "^0.2",
"friendsofphp/php-cs-fixer": "^2",
"phpstan/extension-installer": "^1",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-phpunit": "^0.12"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "7.2.x-dev"
}
},
"autoload": {
"psr-4": {
"Eloquent\\Phony\\Phpunit\\": "src"
},
"files": [
"src/initialize.php",
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Erin Millard",
"email": "ezzatron@gmail.com",
"homepage": "http://ezzatron.com/"
}
],
"description": "Phony for PHPUnit.",
"homepage": "http://eloquent-software.com/phony/",
"keywords": [
"Double",
"Dummy",
"fake",
"mock",
"mocking",
"spy",
"stub",
"stubbing",
"test"
],
"support": {
"issues": "https://github.com/eloquent/phony-phpunit/issues",
"source": "https://github.com/eloquent/phony-phpunit/tree/7.1.0"
},
"time": "2020-12-21T09:36:47+00:00"
},
{
"name": "mikey179/vfsstream",
"version": "v1.6.8",

2
vendor-bin/robo/composer.json

@ -1,6 +1,6 @@
{
"require-dev": {
"consolidation/robo": "^2.2",
"consolidation/robo": "^3.0",
"pear/archive_tar": "^1.4"
}
}

108
vendor-bin/robo/composer.lock

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1bd6e46df17a215ef177f27dadac115f",
"content-hash": "ee0b828426eaa5ff905ef60d9a4b9aca",
"packages": [],
"packages-dev": [
{
@ -235,51 +235,49 @@
},
{
"name": "consolidation/robo",
"version": "2.2.2",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/consolidation/Robo.git",
"reference": "b365df174d9cfb0f5814e4f3275a1c558b17bc4c"
"reference": "734620ad3f9bb457fda1a52338b42439115cf941"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/consolidation/Robo/zipball/b365df174d9cfb0f5814e4f3275a1c558b17bc4c",
"reference": "b365df174d9cfb0f5814e4f3275a1c558b17bc4c",
"url": "https://api.github.com/repos/consolidation/Robo/zipball/734620ad3f9bb457fda1a52338b42439115cf941",
"reference": "734620ad3f9bb457fda1a52338b42439115cf941",
"shasum": ""
},
"require": {
"consolidation/annotated-command": "^4.2.1",
"consolidation/config": "^1.2.1|^2",
"consolidation/log": "^1.1.1|^2.0.1",
"consolidation/output-formatters": "^4.1.1",
"consolidation/annotated-command": "^4.2.4",
"consolidation/config": "^1.2.1|^2.0.1",
"consolidation/log": "^1.1.1|^2.0.2",
"consolidation/output-formatters": "^4.1.2",
"consolidation/self-update": "^1.2",
"league/container": "^2.4.1",
"league/container": "^3.3.1",
"php": ">=7.1.3",
"symfony/console": "^4.4.11|^5",
"symfony/event-dispatcher": "^4.4.11|^5",
"symfony/filesystem": "^4.4.11|^5",
"symfony/finder": "^4.4.11|^5",
"symfony/process": "^4.4.11|^5",
"symfony/yaml": "^4.0 || ^5.0"
"symfony/console": "^4.4.19 || ^5",
"symfony/event-dispatcher": "^4.4.19 || ^5",
"symfony/filesystem": "^4.4.9 || ^5",
"symfony/finder": "^4.4.9 || ^5",
"symfony/process": "^4.4.9 || ^5",
"symfony/yaml": "^4.4 || ^5"
},
"conflict": {
"codegyre/robo": "*"
},
"require-dev": {
"g1a/composer-test-scenarios": "^3",
"natxet/cssmin": "3.0.4",
"patchwork/jsqueeze": "^2",
"pear/archive_tar": "^1.4.4",
"php-coveralls/php-coveralls": "^2.2",
"phpdocumentor/reflection-docblock": "^4.3.2",
"phpunit/phpunit": "^6.5.14",
"squizlabs/php_codesniffer": "^3"
"phpunit/phpunit": "^7.5.20 | ^8",
"squizlabs/php_codesniffer": "^3",
"yoast/phpunit-polyfills": "^0.2.0"
},
"suggest": {
"henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch",
"natxet/cssmin": "For minifying CSS files in taskMinify",
"patchwork/jsqueeze": "For minifying JS files in taskMinify",
"pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively."
"pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively.",
"totten/lurkerlite": "For monitoring filesystem changes in taskWatch"
},
"bin": [
"robo"
@ -330,9 +328,9 @@
"description": "Modern task runner",
"support": {
"issues": "https://github.com/consolidation/Robo/issues",
"source": "https://github.com/consolidation/Robo/tree/2.2.2"
"source": "https://github.com/consolidation/Robo/tree/3.0.3"
},
"time": "2020-12-18T22:09:18+00:00"
"time": "2021-02-21T19:19:43+00:00"
},
{
"name": "consolidation/self-update",
@ -388,42 +386,6 @@
},
"time": "2020-04-13T02:49:20+00:00"
},
{
"name": "container-interop/container-interop",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"support": {
"issues": "https://github.com/container-interop/container-interop/issues",
"source": "https://github.com/container-interop/container-interop/tree/master"
},
"abandoned": "psr/container",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "dflydev/dot-access-data",
"version": "v1.1.0",
@ -540,37 +502,39 @@
},
{
"name": "league/container",
"version": "2.5.0",
"version": "3.3.4",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/container.git",
"reference": "8438dc47a0674e3378bcce893a0a04d79a2c22b3"
"reference": "40aed0f11d16bc23f9d04a27acc3549cd1bb42ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/container/zipball/8438dc47a0674e3378bcce893a0a04d79a2c22b3",
"reference": "8438dc47a0674e3378bcce893a0a04d79a2c22b3",
"url": "https://api.github.com/repos/thephpleague/container/zipball/40aed0f11d16bc23f9d04a27acc3549cd1bb42ab",
"reference": "40aed0f11d16bc23f9d04a27acc3549cd1bb42ab",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.2",
"php": "^5.4 || ^7.0 || ^8.0"
"php": "^7.0 || ^8.0",
"psr/container": "^1.0"
},
"provide": {
"container-interop/container-interop-implementation": "^1.2",
"psr/container-implementation": "^1.0"
},
"replace": {
"orno/di": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.36",
"scrutinizer/ocular": "^1.3",
"phpunit/phpunit": "^6.0",
"roave/security-advisories": "dev-master",
"scrutinizer/ocular": "^1.8",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev",
"dev-3.x": "3.x-dev",
"dev-2.x": "2.x-dev",
"dev-1.x": "1.x-dev"
}
@ -605,7 +569,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/container/issues",
"source": "https://github.com/thephpleague/container/tree/2.5.0"
"source": "https://github.com/thephpleague/container/tree/3.3.4"
},
"funding": [
{
@ -613,7 +577,7 @@
"type": "github"
}
],
"time": "2021-02-22T09:20:06+00:00"
"time": "2021-02-22T10:35:05+00:00"
},
{
"name": "pear/archive_tar",

Loading…
Cancel
Save