diff --git a/.travis.yml b/.travis.yml
index c3bcccd..e178bc2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,7 +25,9 @@ script:
- ./vendor/bin/phpunit -c tests/phpunit.unit.sqlite.xml
- ./vendor/bin/phpunit -c tests/phpunit.unit.postgres.xml
- ./vendor/bin/phpunit -c tests/phpunit.functional.sqlite.xml
- - cp ./tests/ci/config.postgres.php $TRAVIS_BUILD_DIR/config.php && ./vendor/bin/phpunit -c tests/phpunit.functional.postgres.xml
+ - cp ./tests/ci/config.postgres.php $TRAVIS_BUILD_DIR/config.php
+ - ./vendor/bin/phpunit -c tests/phpunit.functional.postgres.xml tests/functional/ApiTest.php
+ - ./vendor/bin/phpunit -c tests/phpunit.functional.postgres.xml tests/functional/FeverApiTest.php
after_failure:
- cat apache_error.log
diff --git a/ChangeLog b/ChangeLog
index 25c1a55..9852741 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,7 +16,7 @@ Version 1.2.0 (unreleased)
* Add support for Expires and Cache-Control headers (HTTP cache)
* Update Docker image to Ubuntu 16.04 and PHP 7.0
* Add Docker compose file
-* Add functional tests (Json-RPC API)
+* Add functional tests (Json-RPC API and Fever API)
* Add unit tests
Migration procedure from 1.1.x to 1.2.0:
diff --git a/app/models/bookmark.php b/app/models/bookmark.php
index 3f08768..21f1db1 100644
--- a/app/models/bookmark.php
+++ b/app/models/bookmark.php
@@ -56,6 +56,7 @@ function get_bookmarked_item_ids($user_id)
->table(Model\Item\TABLE)
->eq('user_id', $user_id)
->eq('bookmark', 1)
+ ->asc('id')
->findAllByColumn('id');
}
diff --git a/app/models/item.php b/app/models/item.php
index 94ba28b..936e35a 100644
--- a/app/models/item.php
+++ b/app/models/item.php
@@ -294,6 +294,7 @@ function get_item_ids_by_status($user_id, $status)
->table('items')
->eq('user_id', $user_id)
->eq('status', $status)
+ ->asc('id')
->findAllByColumn('id');
}
diff --git a/fever/index.php b/fever/index.php
index f65f39b..b1fe0c8 100644
--- a/fever/index.php
+++ b/fever/index.php
@@ -50,11 +50,20 @@ route('groups', function () {
list($user, $authenticated, $response) = auth();
if ($authenticated) {
- $response['groups'] = Model\Group\get_all($user['id']);
+ $response['groups'] = array();
$response['feeds_groups'] = array();
- $group_map = Model\Group\get_groups_feed_ids($user['id']);
- foreach ($group_map as $group_id => $feed_ids) {
+ $groups = Model\Group\get_all($user['id']);
+ $feed_groups = Model\Group\get_groups_feed_ids($user['id']);
+
+ foreach ($groups as $group) {
+ $response['groups'][] = array(
+ 'id' => $group['id'],
+ 'title' => $group['title'],
+ );
+ }
+
+ foreach ($feed_groups as $group_id => $feed_ids) {
$response['feeds_groups'][] = array(
'group_id' => $group_id,
'feed_ids' => implode(',', $feed_ids)
@@ -102,7 +111,6 @@ route('feeds', function () {
// Call: ?api&favicons
route('favicons', function () {
list($user, $authenticated, $response) = auth();
-
if ($authenticated) {
$favicons = Model\Favicon\get_favicons_with_data_url($user['id']);
$response['favicons'] = array();
@@ -110,7 +118,7 @@ route('favicons', function () {
foreach ($favicons as $favicon) {
$response['favicons'][] = array(
'id' => (int) $favicon['feed_id'],
- 'data' => $favicon['url'],
+ 'data' => $favicon['data_url'],
);
}
}
diff --git a/tests/ci/config.postgres.php b/tests/ci/config.postgres.php
index 1242b45..a0fd293 100644
--- a/tests/ci/config.postgres.php
+++ b/tests/ci/config.postgres.php
@@ -1,7 +1,7 @@
assertTrue($this->getApiClient()->removeFeed(1));
}
-
- protected function getApiClient(array $user = array())
- {
- if (empty($user)) {
- $user = $this->adminUser;
- }
-
- $apiUserClient = new Client(API_URL);
- $apiUserClient->authentication($user['username'], $user['api_token']);
-
- return $apiUserClient;
- }
}
diff --git a/tests/functional/BaseApiTest.php b/tests/functional/BaseApiTest.php
index c4f31bc..5bc81de 100644
--- a/tests/functional/BaseApiTest.php
+++ b/tests/functional/BaseApiTest.php
@@ -1,19 +1,41 @@
exec("SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '".DB_NAME."' AND pid <> pg_backend_pid()");
+ $pdo->exec('DROP DATABASE '.DB_NAME);
$pdo->exec('CREATE DATABASE '.DB_NAME.' WITH OWNER '.DB_USERNAME);
+ $pdo = null;
+ } else if (file_exists(DB_FILENAME)) {
+ unlink(DB_FILENAME);
}
+ }
+ public function setUp()
+ {
$db = Miniflux\Database\get_connection();
$this->adminUser = $db->table(Miniflux\Model\User\TABLE)->eq('username', 'admin')->findOne();
}
+
+ protected function getApiClient(array $user = array())
+ {
+ if (empty($user)) {
+ $user = $this->adminUser;
+ }
+
+ $apiUserClient = new Client(API_URL);
+ $apiUserClient->authentication($user['username'], $user['api_token']);
+
+ return $apiUserClient;
+ }
}
diff --git a/tests/functional/FeverApiTest.php b/tests/functional/FeverApiTest.php
new file mode 100644
index 0000000..3cf4769
--- /dev/null
+++ b/tests/functional/FeverApiTest.php
@@ -0,0 +1,223 @@
+executeFeverApiCall();
+ $this->assertEquals(3, $response['api_version']);
+ $this->assertEquals(1, $response['auth']);
+ $this->assertArrayHasKey('last_refreshed_on_time', $response);
+ }
+
+ public function testGetLinks()
+ {
+ $response = $this->executeFeverApiCall('links');
+ $this->assertEquals(3, $response['api_version']);
+ $this->assertEquals(1, $response['auth']);
+ $this->assertArrayHasKey('last_refreshed_on_time', $response);
+ $this->assertSame(array(), $response['links']);
+ }
+
+ public function testGetEmptyFeeds()
+ {
+ $response = $this->executeFeverApiCall('feeds');
+ $this->assertEquals(3, $response['api_version']);
+ $this->assertEquals(1, $response['auth']);
+ $this->assertArrayHasKey('last_refreshed_on_time', $response);
+ $this->assertSame(array(), $response['feeds']);
+ $this->assertSame(array(), $response['feeds_groups']);
+ }
+
+ public function testGetEmptyGroups()
+ {
+ $response = $this->executeFeverApiCall('groups');
+ $this->assertEquals(3, $response['api_version']);
+ $this->assertEquals(1, $response['auth']);
+ $this->assertArrayHasKey('last_refreshed_on_time', $response);
+ $this->assertSame(array(), $response['groups']);
+ $this->assertSame(array(), $response['feeds_groups']);
+ }
+
+ public function testGetFeedsAndGroups()
+ {
+ $this->createFeedAndGroups();
+
+ $response = $this->executeFeverApiCall('feeds');
+
+ $this->assertEquals(1, $response['feeds'][0]['id']);
+ $this->assertEquals(1, $response['feeds'][0]['favicon_id']);
+ $this->assertNotEmpty($response['feeds'][0]['title']);
+ $this->assertNotEmpty($response['feeds'][0]['url']);
+ $this->assertNotEmpty($response['feeds'][0]['site_url']);
+ $this->assertNotEmpty($response['feeds'][0]['last_updated_on_time']);
+ $this->assertEquals(0, $response['feeds'][0]['is_spark']);
+
+ $this->assertEquals(array(array('group_id' => 1, 'feed_ids' => '1')), $response['feeds_groups']);
+
+ $response = $this->executeFeverApiCall('groups');
+
+ $this->assertEquals(array(array('id' => 1, 'title' => 'open source software')), $response['groups']);
+ }
+
+ public function testGetFavicons()
+ {
+ $response = $this->executeFeverApiCall('favicons');
+
+ $this->assertEquals(1, $response['favicons'][0]['id']);
+ $this->assertNotEmpty($response['favicons'][0]['data']);
+ }
+
+ public function testGetItems()
+ {
+ $response = $this->executeFeverApiCall('items');
+
+ $this->assertGreaterThan(2, $response['total_items']);
+ $this->assertEquals(1, $response['items'][0]['id']);
+ $this->assertEquals(1, $response['items'][0]['feed_id']);
+ $this->assertNotEmpty($response['items'][0]['title']);
+ $this->assertNotEmpty($response['items'][0]['author']);
+ $this->assertNotEmpty($response['items'][0]['html']);
+ $this->assertNotEmpty($response['items'][0]['url']);
+ $this->assertEquals(0, $response['items'][0]['is_saved']);
+ $this->assertEquals(0, $response['items'][0]['is_read']);
+ $this->assertGreaterThan(0, $response['items'][0]['created_on_time']);
+ }
+
+ public function testGetItemsWithIds()
+ {
+ $response = $this->executeFeverApiCall('items&with_ids=2,3');
+
+ $this->assertGreaterThan(2, $response['total_items']);
+ $this->assertCount(2, $response['items']);
+ $this->assertEquals(2, $response['items'][0]['id']);
+ $this->assertEquals(3, $response['items'][1]['id']);
+ }
+
+ public function testGetItemsWithSinceId()
+ {
+ $response = $this->executeFeverApiCall('items&since_id=1');
+
+ $this->assertGreaterThan(2, $response['total_items']);
+ $this->assertEquals(2, $response['items'][0]['id']);
+ }
+
+ public function testGetUnreadItems()
+ {
+ $response = $this->executeFeverApiCall('unread_item_ids');
+ $this->assertStringStartsWith('1,2,', $response['unread_item_ids']);
+ }
+
+ public function testMarkItemAsRead()
+ {
+ $this->assertNotNull($this->executeFeverApiCall('', array(
+ 'mark' => 'item',
+ 'as' => 'read',
+ 'id' => 1,
+ )));
+
+ $response = $this->executeFeverApiCall('items&with_ids=1');
+ $this->assertEquals(1, $response['items'][0]['id']);
+ $this->assertEquals(1, $response['items'][0]['is_read']);
+ }
+
+ public function testMarkItemAsSaved()
+ {
+ $this->assertNotNull($this->executeFeverApiCall('', array(
+ 'mark' => 'item',
+ 'as' => 'saved',
+ 'id' => 2,
+ )));
+
+ $response = $this->executeFeverApiCall('items&with_ids=2');
+ $this->assertEquals(2, $response['items'][0]['id']);
+ $this->assertEquals(0, $response['items'][0]['is_read']);
+ $this->assertEquals(1, $response['items'][0]['is_saved']);
+
+ $response = $this->executeFeverApiCall('saved_item_ids');
+ $this->assertStringStartsWith('2', $response['saved_item_ids']);
+ }
+
+ public function testMarkItemAsUnSaved()
+ {
+ $this->assertNotNull($this->executeFeverApiCall('', array(
+ 'mark' => 'item',
+ 'as' => 'unsaved',
+ 'id' => 2,
+ )));
+
+ $response = $this->executeFeverApiCall('items&with_ids=2');
+ $this->assertEquals(2, $response['items'][0]['id']);
+ $this->assertEquals(0, $response['items'][0]['is_read']);
+ $this->assertEquals(0, $response['items'][0]['is_saved']);
+ }
+
+ public function testMarkFeedAsRead()
+ {
+ $response = $this->executeFeverApiCall('items');
+ $items = $response['items'];
+ $nbItems = count($items);
+
+ $this->assertNotNull($this->executeFeverApiCall('', array(
+ 'mark' => 'feed',
+ 'as' => 'read',
+ 'id' => 1,
+ 'before' => $items[$nbItems - 1]['created_on_time'],
+ )));
+
+ $response = $this->executeFeverApiCall('items&with_ids=' . $items[$nbItems - 2]['id']);
+ $this->assertEquals(0, $response['items'][0]['is_read']);
+
+ $response = $this->executeFeverApiCall('items&with_ids=' . $items[$nbItems - 1]['id']);
+ $this->assertEquals(1, $response['items'][0]['is_read']);
+ }
+
+ public function testMarkGroupAsRead()
+ {
+ $this->assertNotNull($this->executeFeverApiCall('', array(
+ 'mark' => 'group',
+ 'as' => 'read',
+ 'id' => 1,
+ 'before' => time(),
+ )));
+
+ $response = $this->executeFeverApiCall('unread_item_ids');
+ $this->assertSame('', $response['unread_item_ids']);
+ }
+
+ protected function executeFeverApiCall($endpoint = '', array $data = array())
+ {
+ $url = FEVER_API_URL . '?api&' . $endpoint;
+ $headers = array(
+ 'Content-type: application/x-www-form-urlencoded',
+ 'Accept: application/json',
+ );
+
+ $payload = array(
+ 'api_key' => $this->adminUser['fever_api_key'],
+ );
+
+ $context = stream_context_create(array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'protocol_version' => 1.1,
+ 'timeout' => 2,
+ 'header' => implode("\r\n", $headers),
+ 'content' => http_build_query(array_merge($payload, $data)),
+ ),
+ ));
+
+ $stream = fopen($url, 'r', false, $context);
+ return json_decode(stream_get_contents($stream), true);
+ }
+
+ protected function createFeedAndGroups()
+ {
+ $this->assertNotFalse($this->getApiClient()->createFeed(array(
+ 'url' => 'https://miniflux.net/feed',
+ 'group_name' => 'open source software',
+ )));
+ }
+}
diff --git a/tests/phpunit.functional.postgres.xml b/tests/phpunit.functional.postgres.xml
index 873d284..add6bae 100644
--- a/tests/phpunit.functional.postgres.xml
+++ b/tests/phpunit.functional.postgres.xml
@@ -6,5 +6,10 @@
+
+
+
+
+
diff --git a/tests/phpunit.functional.sqlite.xml b/tests/phpunit.functional.sqlite.xml
index 061657a..747ad9b 100644
--- a/tests/phpunit.functional.sqlite.xml
+++ b/tests/phpunit.functional.sqlite.xml
@@ -6,6 +6,7 @@
+