From fbe69b54dc1504ec758bb9d260d9ad3c7c0f3a5e Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Mon, 26 Dec 2016 15:07:18 -0500 Subject: [PATCH] Add Postgres database support --- .travis.yml | 3 +- Makefile | 7 +- app/common.php | 30 +--- app/constants.php | 6 + app/controllers/feed.php | 2 +- app/controllers/item.php | 16 +-- app/core/database.php | 45 ++++++ app/helpers/config.php | 19 +-- app/models/feed.php | 42 ++++-- app/models/item.php | 17 ++- app/schemas/postgres.php | 133 ++++++++++++++++++ app/schemas/sqlite.php | 14 +- assets/js/all.js | 25 ++-- assets/js/item.js | 25 ++-- composer.json | 2 +- config.default.php | 15 +- tests/phpunit.unit.postgres.xml | 13 ++ ...punit.unit.xml => phpunit.unit.sqlite.xml} | 2 +- tests/unit/BaseTest.php | 20 ++- tests/unit/FeedModelTest.php | 8 +- tests/unit/ItemModelTest.php | 2 +- vendor/composer/autoload_files.php | 2 +- vendor/composer/autoload_static.php | 2 +- 23 files changed, 322 insertions(+), 128 deletions(-) create mode 100644 app/core/database.php create mode 100644 app/schemas/postgres.php create mode 100644 tests/phpunit.unit.postgres.xml rename tests/{phpunit.unit.xml => phpunit.unit.sqlite.xml} (82%) diff --git a/.travis.yml b/.travis.yml index 91d6ddd..36b4c38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,4 +16,5 @@ before_script: - composer install script: - - ./vendor/bin/phpunit -c tests/phpunit.unit.xml + - ./vendor/bin/phpunit -c tests/phpunit.unit.sqlite.xml + - ./vendor/bin/phpunit -c tests/phpunit.unit.postgres.xml diff --git a/Makefile b/Makefile index 3b14703..7006a0c 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ .PHONY: docker-run .PHONY: js .PHONY: unit-test-sqlite +.PHONY: unit-test-postgres JS_FILE = assets/js/all.js CONTAINER = miniflux @@ -39,4 +40,8 @@ archive: @ git archive --format=zip --prefix=miniflux/ v${version} -o ${dst}/miniflux-${version}.zip unit-test-sqlite: - @ ./vendor/bin/phpunit -c tests/phpunit.unit.xml + @ ./vendor/bin/phpunit -c tests/phpunit.unit.sqlite.xml + +unit-test-postgres: + @ ./vendor/bin/phpunit -c tests/phpunit.unit.postgres.xml + diff --git a/app/common.php b/app/common.php index c5f36aa..9d04a14 100644 --- a/app/common.php +++ b/app/common.php @@ -10,30 +10,10 @@ require_once __DIR__.'/constants.php'; require_once __DIR__.'/check_setup.php'; require_once __DIR__.'/functions.php'; -PicoDb\Database::setInstance('db', function() { - $db = new PicoDb\Database(array( - 'driver' => 'sqlite', - 'filename' => DB_FILENAME, - )); - - $db->getStatementHandler()->withLogging(); - - if ($db->schema('\Miniflux\Schema')->check(Miniflux\Schema\VERSION)) { - return $db; - } else { - $errors = $db->getLogMessages(); - $nb_errors = count($errors); - - $html = 'Unable to migrate the database schema, please copy and paste this message and create a bug report:
'; - $html .= '
';
-        $html .= (isset($errors[$nb_errors - 1]) ? $errors[$nb_errors - 1] : 'Unknown SQL error').PHP_EOL.PHP_EOL;
-        $html .= '- PHP version: '.phpversion().PHP_EOL;
-        $html .= '- SAPI: '.php_sapi_name().PHP_EOL;
-        $html .= '- PDO Sqlite version: '.phpversion('pdo_sqlite').PHP_EOL;
-        $html .= '- Sqlite version: '.$db->getDriver()->getDatabaseVersion().PHP_EOL;
-        $html .= '- OS: '.php_uname();
-        $html .= '
'; - - die($html); +PicoDb\Database::setInstance('db', function () { + try { + return Miniflux\Database\get_connection(); + } catch (Exception $e) { + die($e->getMessage()); } }); diff --git a/app/constants.php b/app/constants.php index 0fd5dba..c711576 100644 --- a/app/constants.php +++ b/app/constants.php @@ -13,7 +13,13 @@ defined('DATA_DIRECTORY') or define('DATA_DIRECTORY', ROOT_DIRECTORY.DIRECTORY_S defined('FAVICON_DIRECTORY') or define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons'); defined('FAVICON_URL_PATH') or define('FAVICON_URL_PATH', 'data/favicons'); +defined('DB_DRIVER') or define('DB_DRIVER', 'sqlite'); defined('DB_FILENAME') or define('DB_FILENAME', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'db.sqlite'); +defined('DB_HOSTNAME') or define('DB_HOSTNAME', 'localhost'); +defined('DB_PORT') or define('DB_PORT', null); +defined('DB_NAME') or define('DB_NAME', 'miniflux'); +defined('DB_USERNAME') or define('DB_USERNAME', ''); +defined('DB_PASSWORD') or define('DB_PASSWORD', ''); defined('DEBUG_MODE') or define('DEBUG_MODE', false); defined('DEBUG_FILENAME') or define('DEBUG_FILENAME', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'debug.log'); diff --git a/app/controllers/feed.php b/app/controllers/feed.php index 9dff1d7..6a426a9 100644 --- a/app/controllers/feed.php +++ b/app/controllers/feed.php @@ -197,7 +197,7 @@ Router\action('subscribe', function () { $values['rtl'], $values['cloak_referrer'], $values['feed_group_ids'], - $values['groups'] + $values['group_name'] ); if ($feed_id >= 1) { diff --git a/app/controllers/item.php b/app/controllers/item.php index 53c2010..8a3f6eb 100644 --- a/app/controllers/item.php +++ b/app/controllers/item.php @@ -222,21 +222,13 @@ Router\get_action('mark-item-removed', function () { Response\redirect('?action='.$redirect.'&offset='.$offset.'&feed_id='.$feed_id); }); -Router\post_action('latest-feeds-items', function () { +Router\get_action('latest-feeds-items', function () { $user_id = SessionStorage::getInstance()->getUserId(); - $items = Model\Item\get_latest_feeds_items($user_id); + $items_timestamps = Model\Item\get_latest_unread_items_timestamps($user_id); $nb_unread_items = Model\Item\count_by_status($user_id, 'unread'); - $feeds = array_reduce($items, function ($result, $item) { - $result[$item['id']] = array( - 'time' => $item['updated'] ?: 0, - 'status' => $item['status'] - ); - return $result; - }, array()); - Response\json(array( - 'feeds' => $feeds, - 'nbUnread' => $nb_unread_items + 'last_items_timestamps' => $items_timestamps, + 'nb_unread_items' => $nb_unread_items )); }); diff --git a/app/core/database.php b/app/core/database.php new file mode 100644 index 0000000..08b135a --- /dev/null +++ b/app/core/database.php @@ -0,0 +1,45 @@ +getStatementHandler()->withLogging(); + + if ($db->schema('\Miniflux\Schema')->check(Schema\VERSION)) { + return $db; + } else { + $errors = $db->getLogMessages(); + $nb_errors = count($errors); + $last_error = isset($errors[$nb_errors - 1]) ? $errors[$nb_errors - 1] : 'Unknown SQL error'; + throw new RuntimeException('Unable to migrate the database schema: '.$last_error); + } +} + +function get_connection_parameters() +{ + if (DB_DRIVER === 'postgres') { + require_once __DIR__.'/../schemas/postgres.php'; + $params = array( + 'driver' => 'postgres', + 'hostname' => DB_HOSTNAME, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + 'port' => DB_PORT, + ); + } else { + require_once __DIR__.'/../schemas/sqlite.php'; + $params = array( + 'driver' => 'sqlite', + 'filename' => DB_FILENAME, + ); + } + + return $params; +} diff --git a/app/helpers/config.php b/app/helpers/config.php index e4f4ceb..cf56c05 100644 --- a/app/helpers/config.php +++ b/app/helpers/config.php @@ -7,17 +7,20 @@ use Miniflux\Session\SessionStorage; function config($parameter, $default = null) { - $session = SessionStorage::getInstance(); - $cache = $session->getConfig(); $value = null; + $session = SessionStorage::getInstance(); - if (empty($cache)) { - $cache = Model\Config\get_all($session->getUserId()); - $session->setConfig($cache); - } + if ($session->isLogged()) { + $cache = $session->getConfig(); - if (array_key_exists($parameter, $cache)) { - $value = $cache[$parameter]; + if (empty($cache)) { + $cache = Model\Config\get_all($session->getUserId()); + $session->setConfig($cache); + } + + if (array_key_exists($parameter, $cache)) { + $value = $cache[$parameter]; + } } if ($value === null) { diff --git a/app/models/feed.php b/app/models/feed.php index 78a7420..f1c7b26 100644 --- a/app/models/feed.php +++ b/app/models/feed.php @@ -46,26 +46,42 @@ function get_feeds($user_id) return Database::getInstance('db') ->table(TABLE) ->eq('user_id', $user_id) + ->desc('parsing_error') + ->desc('enabled') ->asc('title') ->findAll(); } function get_feeds_with_items_count($user_id) { - return Database::getInstance('db') - ->table(TABLE) - ->columns( - 'feeds.*', - 'SUM(CASE WHEN items.status IN ("unread") THEN 1 ELSE 0 END) as "items_unread"', - 'SUM(CASE WHEN items.status IN ("read", "unread") THEN 1 ELSE 0 END) as "items_total"' - ) - ->join('items', 'feed_id', 'id') - ->eq('feeds.user_id', $user_id) - ->groupBy('feeds.id') - ->desc('feeds.parsing_error') - ->desc('feeds.enabled') - ->asc('feeds.title') + $feeds_count = array(); + $feeds = get_feeds($user_id); + $feed_items = Database::getInstance('db') + ->table(Item\TABLE) + ->columns('feed_id', 'status', 'count(*) as nb') + ->eq('user_id', $user_id) + ->neq('status', Item\STATUS_REMOVED) + ->groupBy('feed_id', 'status') ->findAll(); + + foreach ($feed_items as $row) { + if (! isset($feeds_count[$row['feed_id']])) { + $feeds_count[$row['feed_id']] = array('unread' => 0, 'total' => 0); + } + + if ($row['status'] === 'unread') { + $feeds_count[$row['feed_id']]['unread'] = $row['nb']; + } + + $feeds_count[$row['feed_id']]['total'] += $row['nb']; + } + + foreach ($feeds as &$feed) { + $feed['items_unread'] = isset($feeds_count[$feed['id']]) ? $feeds_count[$feed['id']]['unread'] : 0; + $feed['items_total'] = isset($feeds_count[$feed['id']]) ? $feeds_count[$feed['id']]['total'] : 0; + } + + return $feeds; } function get_feed_ids($user_id, $limit = null) diff --git a/app/models/item.php b/app/models/item.php index 5d8a90a..94ba28b 100644 --- a/app/models/item.php +++ b/app/models/item.php @@ -297,19 +297,18 @@ function get_item_ids_by_status($user_id, $status) ->findAllByColumn('id'); } -function get_latest_feeds_items($user_id) +function get_latest_unread_items_timestamps($user_id) { return Database::getInstance('db') - ->table(Feed\TABLE) + ->table(TABLE) ->columns( - 'feeds.id', - 'MAX(items.updated) as updated', - 'items.status' + 'feed_id', + 'MAX(updated) as updated' ) - ->join(TABLE, 'feed_id', 'id') - ->eq('feeds.user_id', $user_id) - ->groupBy('feeds.id') - ->orderBy('feeds.id') + ->eq('user_id', $user_id) + ->eq('status', STATUS_UNREAD) + ->groupBy('feed_id') + ->desc('updated') ->findAll(); } diff --git a/app/schemas/postgres.php b/app/schemas/postgres.php new file mode 100644 index 0000000..a83890b --- /dev/null +++ b/app/schemas/postgres.php @@ -0,0 +1,133 @@ +exec("CREATE TABLE users ( + id SERIAL PRIMARY KEY, + username VARCHAR(50) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + is_admin BOOLEAN DEFAULT FALSE, + last_login BIGINT, + api_token VARCHAR(255) NOT NULL UNIQUE, + bookmarklet_token VARCHAR(255) NOT NULL UNIQUE, + cronjob_token VARCHAR(255) NOT NULL UNIQUE, + feed_token VARCHAR(255) NOT NULL UNIQUE, + fever_token VARCHAR(255) NOT NULL UNIQUE, + fever_api_key VARCHAR(255) NOT NULL UNIQUE + )"); + + $pdo->exec('CREATE TABLE user_settings ( + "user_id" INTEGER NOT NULL, + "key" VARCHAR(255) NOT NULL, + "value" TEXT NOT NULL, + PRIMARY KEY("user_id", "key"), + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + )'); + + $pdo->exec('CREATE TABLE feeds ( + id BIGSERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + feed_url VARCHAR(255) NOT NULL, + site_url VARCHAR(255), + title VARCHAR(255) NOT NULL, + last_checked BIGINT DEFAULT 0, + last_modified VARCHAR(255), + etag VARCHAR(255), + enabled BOOLEAN DEFAULT TRUE, + download_content BOOLEAN DEFAULT FALSE, + parsing_error BOOLEAN DEFAULT FALSE, + rtl BOOLEAN DEFAULT FALSE, + cloak_referrer BOOLEAN DEFAULT FALSE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(user_id, feed_url) + )'); + + $pdo->exec('CREATE TABLE items ( + id BIGSERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + feed_id BIGINT NOT NULL, + checksum VARCHAR(255) NOT NULL, + status VARCHAR(10) NOT NULL, + bookmark INTEGER DEFAULT 0, + url VARCHAR(255) NOT NULL, + title VARCHAR(255) NOT NULL, + author VARCHAR(255), + content TEXT, + updated BIGINT, + enclosure_url VARCHAR(255), + enclosure_type VARCHAR(50), + language VARCHAR(50), + rtl INTEGER DEFAULT 0, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE, + UNIQUE(feed_id, checksum) + )'); + + $pdo->exec('CREATE TABLE "groups" ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR(255) NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(user_id, title) + )'); + + $pdo->exec('CREATE TABLE "feeds_groups" ( + feed_id BIGINT NOT NULL, + group_id INTEGER NOT NULL, + PRIMARY KEY(feed_id, group_id), + FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE, + FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE + )'); + + $pdo->exec('CREATE TABLE favicons ( + id SERIAL PRIMARY KEY, + hash VARCHAR(255) UNIQUE, + type VARCHAR(50) + )'); + + $pdo->exec('CREATE TABLE "favicons_feeds" ( + feed_id BIGINT NOT NULL, + favicon_id INTEGER NOT NULL, + PRIMARY KEY(feed_id, favicon_id), + FOREIGN KEY(favicon_id) REFERENCES favicons(id) ON DELETE CASCADE, + FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE + )'); + + $pdo->exec('CREATE TABLE remember_me ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + ip VARCHAR(255), + user_agent VARCHAR(255), + token VARCHAR(255), + sequence VARCHAR(255), + expiration BIGINT, + date_creation BIGINT, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + )'); + + $fever_token = Helper\generate_token(); + $rq = $pdo->prepare(' + INSERT INTO users + (username, password, is_admin, api_token, bookmarklet_token, cronjob_token, feed_token, fever_token, fever_api_key) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + '); + + $rq->execute(array( + 'admin', + password_hash('admin', PASSWORD_BCRYPT), + '1', + Helper\generate_token(), + Helper\generate_token(), + Helper\generate_token(), + Helper\generate_token(), + $fever_token, + md5('admin:'.$fever_token), + )); +} diff --git a/app/schemas/sqlite.php b/app/schemas/sqlite.php index 611cdf5..3a5c929 100644 --- a/app/schemas/sqlite.php +++ b/app/schemas/sqlite.php @@ -11,7 +11,7 @@ function version_1(PDO $pdo) { $pdo->exec('CREATE TABLE users ( id INTEGER PRIMARY KEY, - username TEXT UNIQUE, + username TEXT NOT NULL UNIQUE, password TEXT NOT NULL, is_admin INTEGER DEFAULT 0, last_login INTEGER, @@ -36,7 +36,7 @@ function version_1(PDO $pdo) user_id INTEGER NOT NULL, feed_url TEXT NOT NULL, site_url TEXT, - title TEXT COLLATE NOCASE, + title TEXT COLLATE NOCASE NOT NULL, last_checked INTEGER DEFAULT 0, last_modified TEXT, etag TEXT, @@ -54,10 +54,10 @@ function version_1(PDO $pdo) user_id INTEGER NOT NULL, feed_id INTEGER NOT NULL, checksum TEXT NOT NULL, - status TEXT, + status TEXT NOT NULL, bookmark INTEGER DEFAULT 0, - url TEXT, - title TEXT COLLATE NOCASE, + url TEXT NOT NULL, + title TEXT COLLATE NOCASE NOT NULL, author TEXT, content TEXT, updated INTEGER, @@ -73,13 +73,13 @@ function version_1(PDO $pdo) $pdo->exec('CREATE TABLE "groups" ( id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL, - title TEXT COLLATE NOCASE, + title TEXT COLLATE NOCASE NOT NULL, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE(user_id, title) )'); $pdo->exec('CREATE TABLE "feeds_groups" ( - feed_id INTEGER NOT NULL, + feed_id INTEGER NOT NULL, group_id INTEGER NOT NULL, PRIMARY KEY(feed_id, group_id), FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE, diff --git a/assets/js/all.js b/assets/js/all.js index 286627a..09ccc60 100644 --- a/assets/js/all.js +++ b/assets/js/all.js @@ -113,7 +113,7 @@ Miniflux.Feed = (function() { Miniflux.Item = (function() { // timestamp of the latest item per feed ever seen - var latest_feeds_items = []; + var latest_feeds_items = {}; // indicator for new unread items var unreadItems = false; @@ -474,27 +474,24 @@ Miniflux.Item = (function() { var first_run = (latest_feeds_items.length === 0); var current_unread = false; var response = JSON.parse(this.responseText); + var last_items_timestamps = response['last_items_timestamps']; - for (var feed_id in response['feeds']) { - var current_feed = response['feeds'][feed_id]; + for (var i = 0; i < last_items_timestamps.length; i++) { + var current_feed = last_items_timestamps[i]; + var feed_id = current_feed.feed_id; - if (! latest_feeds_items.hasOwnProperty(feed_id) || current_feed.time > latest_feeds_items[feed_id]) { - Miniflux.App.Log('feed ' + feed_id + ': New item(s)'); - latest_feeds_items[feed_id] = current_feed.time; - - if (current_feed.status === 'unread') { - Miniflux.App.Log('feed ' + feed_id + ': New unread item(s)'); - current_unread = true; - } + if (! latest_feeds_items.hasOwnProperty(feed_id) || current_feed.updated > latest_feeds_items[feed_id]) { + latest_feeds_items[feed_id] = current_feed.updated; + current_unread = true; } } Miniflux.App.Log('first_run: ' + first_run + ', current_unread: ' + current_unread + ', response.nbUnread: ' + response['nbUnread'] + ', nbUnreadItems: ' + nbUnreadItems); - if (! document.hidden && (response['nbUnread'] !== nbUnreadItems || unreadItems)) { + if (! document.hidden && (response['nb_unread_items'] !== nbUnreadItems || unreadItems)) { Miniflux.App.Log('Counter changed! Updating unread counter.'); unreadItems = false; - nbUnreadItems = response['nbUnread']; + nbUnreadItems = response['nb_unread_items']; updateCounters(); } else if (document.hidden && ! first_run && current_unread) { @@ -509,7 +506,7 @@ Miniflux.Item = (function() { Miniflux.App.Log('unreadItems: ' + unreadItems); }; - request.open("POST", "?action=latest-feeds-items", true); + request.open("GET", "?action=latest-feeds-items", true); request.send(); } }; diff --git a/assets/js/item.js b/assets/js/item.js index d21b612..b3c5285 100644 --- a/assets/js/item.js +++ b/assets/js/item.js @@ -1,7 +1,7 @@ Miniflux.Item = (function() { // timestamp of the latest item per feed ever seen - var latest_feeds_items = []; + var latest_feeds_items = {}; // indicator for new unread items var unreadItems = false; @@ -362,27 +362,24 @@ Miniflux.Item = (function() { var first_run = (latest_feeds_items.length === 0); var current_unread = false; var response = JSON.parse(this.responseText); + var last_items_timestamps = response['last_items_timestamps']; - for (var feed_id in response['feeds']) { - var current_feed = response['feeds'][feed_id]; + for (var i = 0; i < last_items_timestamps.length; i++) { + var current_feed = last_items_timestamps[i]; + var feed_id = current_feed.feed_id; - if (! latest_feeds_items.hasOwnProperty(feed_id) || current_feed.time > latest_feeds_items[feed_id]) { - Miniflux.App.Log('feed ' + feed_id + ': New item(s)'); - latest_feeds_items[feed_id] = current_feed.time; - - if (current_feed.status === 'unread') { - Miniflux.App.Log('feed ' + feed_id + ': New unread item(s)'); - current_unread = true; - } + if (! latest_feeds_items.hasOwnProperty(feed_id) || current_feed.updated > latest_feeds_items[feed_id]) { + latest_feeds_items[feed_id] = current_feed.updated; + current_unread = true; } } Miniflux.App.Log('first_run: ' + first_run + ', current_unread: ' + current_unread + ', response.nbUnread: ' + response['nbUnread'] + ', nbUnreadItems: ' + nbUnreadItems); - if (! document.hidden && (response['nbUnread'] !== nbUnreadItems || unreadItems)) { + if (! document.hidden && (response['nb_unread_items'] !== nbUnreadItems || unreadItems)) { Miniflux.App.Log('Counter changed! Updating unread counter.'); unreadItems = false; - nbUnreadItems = response['nbUnread']; + nbUnreadItems = response['nb_unread_items']; updateCounters(); } else if (document.hidden && ! first_run && current_unread) { @@ -397,7 +394,7 @@ Miniflux.Item = (function() { Miniflux.App.Log('unreadItems: ' + unreadItems); }; - request.open("POST", "?action=latest-feeds-items", true); + request.open("GET", "?action=latest-feeds-items", true); request.send(); } }; diff --git a/composer.json b/composer.json index 2f14600..605f731 100644 --- a/composer.json +++ b/composer.json @@ -26,13 +26,13 @@ }, "autoload": { "files": [ - "app/schemas/sqlite.php", "app/helpers/app.php", "app/helpers/config.php", "app/helpers/csrf.php", "app/helpers/favicon.php", "app/helpers/form.php", "app/helpers/template.php", + "app/core/database.php", "app/core/translator.php", "app/core/request.php", "app/core/response.php", diff --git a/config.default.php b/config.default.php index fea66a8..7234341 100644 --- a/config.default.php +++ b/config.default.php @@ -7,7 +7,7 @@ define('HTTP_TIMEOUT', '20'); define('HTTP_MAX_RESPONSE_SIZE', 2097152); // DATA_DIRECTORY => default is data (writable directory) -define('DATA_DIRECTORY', __DIR__.'/data'); +define('DATA_DIRECTORY', 'data'); // FAVICON_DIRECTORY => default is favicons (writable directory) define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons'); @@ -15,8 +15,17 @@ define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons'); // FAVICON_URL_PATH => default is data/favicons/ define('FAVICON_URL_PATH', 'data/favicons'); -// DB_FILENAME => default value is db.sqlite (default database filename) -define('DB_FILENAME', 'db.sqlite'); +// Database driver: "sqlite" or "postgres", default is sqlite +define('DB_DRIVER', 'sqlite'); + +// Database connection parameters when Postgres is used +define('DB_HOSTNAME', 'localhost'); +define('DB_NAME', 'miniflux'); +define('DB_USERNAME', 'postgres'); +define('DB_PASSWORD', ''); + +// DB_FILENAME => database file when Sqlite is used +define('DB_FILENAME', DATA_DIRECTORY.'/db.sqlite'); // Enable/disable debug mode define('DEBUG_MODE', false); diff --git a/tests/phpunit.unit.postgres.xml b/tests/phpunit.unit.postgres.xml new file mode 100644 index 0000000..880ccea --- /dev/null +++ b/tests/phpunit.unit.postgres.xml @@ -0,0 +1,13 @@ + + + + unit + + + + + + + + + diff --git a/tests/phpunit.unit.xml b/tests/phpunit.unit.sqlite.xml similarity index 82% rename from tests/phpunit.unit.xml rename to tests/phpunit.unit.sqlite.xml index fd91a00..03204e9 100644 --- a/tests/phpunit.unit.xml +++ b/tests/phpunit.unit.sqlite.xml @@ -1,6 +1,6 @@ - + unit diff --git a/tests/unit/BaseTest.php b/tests/unit/BaseTest.php index 8343205..1996a92 100644 --- a/tests/unit/BaseTest.php +++ b/tests/unit/BaseTest.php @@ -12,20 +12,18 @@ abstract class BaseTest extends PHPUnit_Framework_TestCase { public function setUp() { - SessionStorage::getInstance()->flush(); + if (DB_DRIVER === 'postgres') { + $pdo = new PDO('pgsql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD); + $pdo->exec('DROP DATABASE '.DB_NAME); + $pdo->exec('CREATE DATABASE '.DB_NAME.' WITH OWNER '.DB_USERNAME); + $pdo = null; + } PicoDb\Database::setInstance('db', function () { - $db = new PicoDb\Database(array( - 'driver' => 'sqlite', - 'filename' => DB_FILENAME, - )); - - $db->getStatementHandler()->withLogging(); - if (! $db->schema('\Miniflux\Schema')->check(Miniflux\Schema\VERSION)) { - var_dump($db->getLogMessages()); - } - return $db; + return Miniflux\Database\get_connection(); }); + + SessionStorage::getInstance()->flush(); } public function tearDown() diff --git a/tests/unit/FeedModelTest.php b/tests/unit/FeedModelTest.php index 3a7ca85..9010748 100644 --- a/tests/unit/FeedModelTest.php +++ b/tests/unit/FeedModelTest.php @@ -26,10 +26,10 @@ class FeedModelTest extends BaseTest $this->assertEquals('feed url', $subscription['feed_url']); $this->assertEquals('etag', $subscription['etag']); $this->assertEquals('last modified', $subscription['last_modified']); - $this->assertEquals('0', $subscription['download_content']); - $this->assertEquals('0', $subscription['rtl']); - $this->assertEquals('0', $subscription['cloak_referrer']); - $this->assertEquals('1', $subscription['enabled']); + $this->assertFalse((bool) $subscription['download_content']); + $this->assertFalse((bool) $subscription['rtl']); + $this->assertFalse((bool) $subscription['cloak_referrer']); + $this->assertTrue((bool) $subscription['enabled']); } public function testGetAll() diff --git a/tests/unit/ItemModelTest.php b/tests/unit/ItemModelTest.php index 031d599..9ad3cbe 100644 --- a/tests/unit/ItemModelTest.php +++ b/tests/unit/ItemModelTest.php @@ -27,7 +27,7 @@ class ItemModelTest extends BaseTest $this->assertEquals('', $item['enclosure_type']); $this->assertEquals('', $item['language']); - $item = Model\Item\get_item(2, 'ID 1'); + $item = Model\Item\get_item(2, 1); $this->assertNull($item); } diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 36f2883..1e2e285 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -7,13 +7,13 @@ $baseDir = dirname($vendorDir); return array( 'e40631d46120a9c38ea139981f8dab26' => $vendorDir . '/ircmaxell/password-compat/lib/password.php', - '1c6478a893aa3a9ae898668d7af7b441' => $baseDir . '/app/schemas/sqlite.php', 'f0aaf41fd213d5a97cf56c060c89445c' => $baseDir . '/app/helpers/app.php', '640b6eca1c41bc50addc30eb8c8e3b5b' => $baseDir . '/app/helpers/config.php', '5d8e3de7e01942090f1b7b4dbdfb5577' => $baseDir . '/app/helpers/csrf.php', '3f7c6586b45b98746d18450089390313' => $baseDir . '/app/helpers/favicon.php', 'e96e91c85d691b966391981f72a31834' => $baseDir . '/app/helpers/form.php', 'b8d3001d29a919647064eeaec3f6551e' => $baseDir . '/app/helpers/template.php', + 'a79069799c6e73d978adffa1caf9894d' => $baseDir . '/app/core/database.php', '7793918f03299c5c8c900e412e166b4f' => $baseDir . '/app/core/translator.php', '69d59b59e8b15a53cc782e283523018d' => $baseDir . '/app/core/request.php', '3fd802226a33a97ae29d1f8315ff66a9' => $baseDir . '/app/core/response.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 5774acc..2255369 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -8,13 +8,13 @@ class ComposerStaticInitfd7e8d436e1dc450edc3153ac8bc31b4 { public static $files = array ( 'e40631d46120a9c38ea139981f8dab26' => __DIR__ . '/..' . '/ircmaxell/password-compat/lib/password.php', - '1c6478a893aa3a9ae898668d7af7b441' => __DIR__ . '/../..' . '/app/schemas/sqlite.php', 'f0aaf41fd213d5a97cf56c060c89445c' => __DIR__ . '/../..' . '/app/helpers/app.php', '640b6eca1c41bc50addc30eb8c8e3b5b' => __DIR__ . '/../..' . '/app/helpers/config.php', '5d8e3de7e01942090f1b7b4dbdfb5577' => __DIR__ . '/../..' . '/app/helpers/csrf.php', '3f7c6586b45b98746d18450089390313' => __DIR__ . '/../..' . '/app/helpers/favicon.php', 'e96e91c85d691b966391981f72a31834' => __DIR__ . '/../..' . '/app/helpers/form.php', 'b8d3001d29a919647064eeaec3f6551e' => __DIR__ . '/../..' . '/app/helpers/template.php', + 'a79069799c6e73d978adffa1caf9894d' => __DIR__ . '/../..' . '/app/core/database.php', '7793918f03299c5c8c900e412e166b4f' => __DIR__ . '/../..' . '/app/core/translator.php', '69d59b59e8b15a53cc782e283523018d' => __DIR__ . '/../..' . '/app/core/request.php', '3fd802226a33a97ae29d1f8315ff66a9' => __DIR__ . '/../..' . '/app/core/response.php',