From 4cded011ff82099ee7e14cde1a5c15897459e447 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Wed, 19 Jul 2017 18:07:36 -0400 Subject: [PATCH] Complete implementations of server status and user status REST calls - Fixes #30 - Fixes #31 - Avatars are not yet supported by the data model; blocked by issue #52 --- lib/REST/NextCloudNews/V1_2.php | 18 ++++++--- lib/Service.php | 24 ++++++----- lib/User.php | 2 +- tests/REST/NextCloudNews/TestNCNV1_2.php | 21 +++++++++- tests/Service/TestService.php | 51 ++++++++++++++++++++++++ tests/phpunit.xml | 4 ++ 6 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 tests/Service/TestService.php diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php index 5badc2e..5015aed 100644 --- a/lib/REST/NextCloudNews/V1_2.php +++ b/lib/REST/NextCloudNews/V1_2.php @@ -595,13 +595,22 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } protected function userStatus(array $url, array $data): Response { - // FIXME: stub - $data = Arsse::$db->userPropertiesGet(Arsse::$user->id); + $data = Arsse::$user::propertiesGet(Arsse::$user->id, true); + // construct the avatar structure, if an image is available + if(isset($data['avatar'])) { + $avatar = [ + 'data' => base64_encode($data['avatar']['data']), + 'mime' => $data['avatar']['type'], + ]; + } else { + $avatar = null; + } + // construct the rest of the structure $out = [ 'userId' => Arsse::$user->id, 'displayName' => $data['name'] ?? Arsse::$user->id, 'lastLoginTimestamp' => time(), - 'avatar' => null, + 'avatar' => $avatar, ]; return new Response(200, $out); } @@ -629,12 +638,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } protected function serverStatus(array $url, array $data): Response { - // FIXME: stub return new Response(200, [ 'version' => self::VERSION, 'arsse_version' => \JKingWeb\Arsse\VERSION, 'warnings' => [ - 'improperlyConfiguredCron' => false, + 'improperlyConfiguredCron' => !\JKingWeb\Arsse\Service::hasCheckedIn(), ] ]); } diff --git a/lib/Service.php b/lib/Service.php index 2fed9a1..f5f0aab 100644 --- a/lib/Service.php +++ b/lib/Service.php @@ -22,8 +22,12 @@ class Service { return $classes; } - protected static function interval(): \DateInterval { - return new \DateInterval(Arsse::$conf->serviceFrequency); // FIXME: this needs to fall back in case of incorrect input + public static function interval(): \DateInterval { + try{ + return new \DateInterval(Arsse::$conf->serviceFrequency); + } catch(\Exception $e) { + return new \DateInterval("PT2M"); + } } function __construct() { @@ -32,14 +36,13 @@ class Service { $this->interval = static::interval(); } - function watch(bool $loop = true) { + function watch(bool $loop = true): \DateTimeInterface { $t = new \DateTime(); do { $this->checkIn(); static::cleanupPre(); $list = Arsse::$db->feedListStale(); if($list) { - echo date("H:i:s")." Updating feeds ".json_encode($list)."\n"; $this->drv->queue(...$list); $this->drv->exec(); $this->drv->clean(); @@ -47,10 +50,13 @@ class Service { unset($list); } $t->add($this->interval); - do { - @time_sleep_until($t->getTimestamp()); - } while($t->getTimestamp() > time()); + if($loop) { + do { + @time_sleep_until($t->getTimestamp()); + } while($t->getTimestamp() > time()); + } } while($loop); + return $t; } function checkIn(): bool { @@ -69,8 +75,8 @@ class Service { $limit = new \DateTime(); $limit->sub($int); $limit->sub($int); - // return whether the check-in time is less than the acceptable limit - return ($checkin < $limit); + // return whether the check-in time is within the acceptable limit + return ($checkin >= $limit); } static function cleanupPre(): bool { diff --git a/lib/User.php b/lib/User.php index 13ab49f..b8a89f5 100644 --- a/lib/User.php +++ b/lib/User.php @@ -245,7 +245,7 @@ class User { } } - public function propertiesGet(string $user): array { + public function propertiesGet(string $user, bool $withAvatar = false): array { // prepare default values $domain = null; if(Arsse::$conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1); diff --git a/tests/REST/NextCloudNews/TestNCNV1_2.php b/tests/REST/NextCloudNews/TestNCNV1_2.php index a745a56..4641a4e 100644 --- a/tests/REST/NextCloudNews/TestNCNV1_2.php +++ b/tests/REST/NextCloudNews/TestNCNV1_2.php @@ -4,6 +4,7 @@ namespace JKingWeb\Arsse; use JKingWeb\Arsse\REST\Request; use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\Test\Result; +use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; @@ -260,6 +261,7 @@ class TestNCNV1_2 extends Test\AbstractTest { function setUp() { $this->clearData(); + Arsse::$conf = new Conf(); // create a mock user manager Arsse::$user = Phake::mock(User::class); Phake::when(Arsse::$user)->authHTTP->thenReturn(true); @@ -715,7 +717,7 @@ class TestNCNV1_2 extends Test\AbstractTest { } Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), $this->anything())->thenReturn(true); 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 - Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function for limited to 50 items for multiples + Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions($in[1]))->thenThrow(new ExceptionInput("tooLong")); // data model function limited to 50 items for multiples $exp = new Response(422); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple"))); $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple"))); @@ -762,4 +764,21 @@ class TestNCNV1_2 extends Test\AbstractTest { Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->articles($in[2])); Phake::verify(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->articles($in[3])); } + + function testQueryTheServerStatus() { + $interval = Service::interval(); + $valid = (new \DateTimeImmutable("now", new \DateTimezone("UTC")))->sub($interval); + $invalid = $valid->sub($interval)->sub($interval); + Phake::when(Arsse::$db)->metaGet("service_last_checkin")->thenReturn(Date::transform($valid, "sql"))->thenReturn(Date::transform($invalid, "sql")); + $arr1 = $arr2 = [ + 'version' => REST\NextCloudNews\V1_2::VERSION, + 'arsse_version' => VERSION, + 'warnings' => [ + 'improperlyConfiguredCron' => false, + ] + ]; + $arr2['warnings']['improperlyConfiguredCron'] = true; + $exp = new Response(200, $arr1); + $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/status"))); + } } \ No newline at end of file diff --git a/tests/Service/TestService.php b/tests/Service/TestService.php new file mode 100644 index 0000000..a97516d --- /dev/null +++ b/tests/Service/TestService.php @@ -0,0 +1,51 @@ +clearData(); + Arsse::$conf = new Conf(); + Arsse::$db = Phake::mock(Database::class); + $this->srv = new Service(); + } + + function testComputeInterval() { + $in = [ + Arsse::$conf->serviceFrequency, + "PT2M", + "PT5M", + "P2M", + "5M", + "interval", + ]; + foreach($in as $index => $spec) { + try{$exp = new \DateInterval($spec);} catch(\Exception $e) {$exp = new \DateInterval("PT2M");} + Arsse::$conf->serviceFrequency = $spec; + $this->assertEquals($exp, Service::interval(), "Interval #$index '$spec' was not correctly calculated"); + } + } + + function testCheckIn() { + $now = time(); + $this->srv->checkIn(); + Phake::verify(Arsse::$db)->metaSet("service_last_checkin", Phake::capture($then), "datetime"); + $this->assertTime($now, $then); + } + + function testReportHavingCheckedIn() { + // the mock's metaGet() returns null by default + $this->assertFalse(Service::hasCheckedIn()); + $interval = Service::interval(); + $valid = (new \DateTimeImmutable("now", new \DateTimezone("UTC")))->sub($interval); + $invalid = $valid->sub($interval)->sub($interval); + Phake::when(Arsse::$db)->metaGet("service_last_checkin")->thenReturn(Date::transform($valid, "sql"))->thenReturn(Date::transform($invalid, "sql")); + $this->assertTrue(Service::hasCheckedIn()); + $this->assertFalse(Service::hasCheckedIn()); + } +} \ No newline at end of file diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 04178c4..5114ee8 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -53,5 +53,9 @@ REST/NextCloudNews/TestNCNVersionDiscovery.php REST/NextCloudNews/TestNCNV1_2.php + + Service/TestService.php + + \ No newline at end of file