diff --git a/lib/Database.php b/lib/Database.php
index 19deb8fa..9bc44afc 100644
--- a/lib/Database.php
+++ b/lib/Database.php
@@ -323,7 +323,7 @@ class Database {
throw new User\Exception("doesNotExist", ["user" => $user, "action" => __FUNCTION__]);
}
// if the desired folder name is missing or invalid, throw an exception
- if(!array_key_exists("name", $data)) {
+ if(!array_key_exists("name", $data) || $data['name']=="") {
throw new Db\ExceptionInput("missing", ["action" => __FUNCTION__, "field" => "name"]);
} else if(!strlen(trim($data['name']))) {
throw new Db\ExceptionInput("whitespace", ["action" => __FUNCTION__, "field" => "name"]);
diff --git a/sql/SQLite3/0.sql b/sql/SQLite3/0.sql
index 0b5f64f7..18d0ab82 100644
--- a/sql/SQLite3/0.sql
+++ b/sql/SQLite3/0.sql
@@ -38,21 +38,21 @@ create table arsse_feeds(
-- users' subscriptions to newsfeeds, with settings
create table arsse_subscriptions(
id integer primary key not null, -- sequence number
- owner TEXT not null references arsse_users(id) on delete cascade on update cascade, -- owner of subscription
- feed integer not null references arsse_feeds(id) on delete cascade, -- feed for the subscription
+ owner TEXT not null references arsse_users(id) on delete cascade on update cascade, -- owner of subscription
+ feed integer not null references arsse_feeds(id) on delete cascade, -- feed for the subscription
added datetime not null default CURRENT_TIMESTAMP, -- time at which feed was added
modified datetime not null default CURRENT_TIMESTAMP, -- date at which subscription properties were last modified
title TEXT, -- user-supplied title
order_type int not null default 0, -- NextCloud sort order
pinned boolean not null default 0, -- whether feed is pinned (always sorts at top)
- folder integer references arsse_folders(id) on delete cascade, -- TT-RSS category (nestable); the first-level category (which acts as NextCloud folder) is joined in when needed
+ folder integer references arsse_folders(id) on delete cascade, -- TT-RSS category (nestable); the first-level category (which acts as NextCloud folder) is joined in when needed
unique(owner,feed) -- a given feed should only appear once for a given owner
);
-- TT-RSS categories and NextCloud folders
create table arsse_folders(
id integer primary key not null, -- sequence number
- owner TEXT not null references arsse_users(id) on delete cascade on update cascade, -- owner of folder
+ owner TEXT not null references arsse_users(id) on delete cascade on update cascade, -- owner of folder
parent integer default null, -- parent folder id
root integer default null, -- first-level folder (NextCloud folder)
name TEXT not null, -- folder name
@@ -63,7 +63,7 @@ create table arsse_folders(
-- entries in newsfeeds
create table arsse_articles(
id integer primary key not null, -- sequence number
- feed integer not null references arsse_feeds(id) on delete cascade, -- feed for the subscription
+ feed integer not null references arsse_feeds(id) on delete cascade, -- feed for the subscription
url TEXT not null, -- URL of article
title TEXT, -- article title
author TEXT, -- author's name
diff --git a/tests/lib/Database/SeriesFolder.php b/tests/lib/Database/SeriesFolder.php
new file mode 100644
index 00000000..d7cea89d
--- /dev/null
+++ b/tests/lib/Database/SeriesFolder.php
@@ -0,0 +1,102 @@
+ [
+ 'columns' => [
+ 'id' => "int",
+ 'owner' => "str",
+ 'parent' => "int",
+ 'root' => "int",
+ 'name' => "str",
+ ],
+ /* Layout translates to:
+ Jane
+ Politics
+ John
+ Technology
+ Software
+ Politics
+ Rocketry
+ Politics
+ */
+ 'rows' => [
+ [1, "john.doe@example.com", null, null, "Technology"],
+ [2, "john.doe@example.com", 1, 1, "Software"],
+ [3, "john.doe@example.com", 1, 1, "Rocketry"],
+ [4, "jane.doe@example.com", null, null, "Politics"],
+ [5, "john.doe@example.com", null, null, "Politics"],
+ [6, "john.doe@example.com", 2, 1, "Politics"],
+ ]
+ ]
+ ];
+ // merge folder table with base user table
+ $this->data = array_merge($this->data, $data);
+ $this->primeDatabase($this->data);
+ }
+
+ function testAddARootFolder() {
+ $user = "john.doe@example.com";
+ $this->assertSame(7, Data::$db->folderAdd($user, ['name' => "Entertainment"]));
+ Phake::verify(Data::$user)->authorize($user, "folderAdd");
+ $state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'root', 'name']]);
+ $state['arsse_folders']['rows'][] = [7, $user, null, null, "Entertainment"];
+ $this->compareExpectations($state);
+ }
+
+ function testAddADuplicateRootFolder() {
+ $this->assertException("constraintViolation", "Db", "ExceptionInput");
+ Data::$db->folderAdd("john.doe@example.com", ['name' => "Politics"]);
+ }
+
+ function testAddANestedFolder() {
+ $user = "john.doe@example.com";
+ $this->assertSame(7, Data::$db->folderAdd($user, ['name' => "GNOME", 'parent' => 2]));
+ Phake::verify(Data::$user)->authorize($user, "folderAdd");
+ $state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'root', 'name']]);
+ $state['arsse_folders']['rows'][] = [7, $user, 2, 1, "GNOME"];
+ $this->compareExpectations($state);
+ }
+
+ function testAddANestedFolderToAMissingParent() {
+ $this->assertException("idMissing", "Db", "ExceptionInput");
+ Data::$db->folderAdd("john.doe@example.com", ['name' => "Sociology", 'parent' => 2112]);
+ }
+
+ function testAddANestedFolderForTheWrongOwner() {
+ $this->assertException("idMissing", "Db", "ExceptionInput");
+ Data::$db->folderAdd("john.doe@example.com", ['name' => "Sociology", 'parent' => 4]); // folder ID 4 belongs to Jane
+ }
+
+ function testAddAFolderForAMissingUser() {
+ $this->assertException("doesNotExist", "User");
+ Data::$db->folderAdd("john.doe@example.org", ['name' => "Sociology"]);
+ }
+
+ function testAddAFolderWithAMissingName() {
+ $this->assertException("missing", "Db", "ExceptionInput");
+ Data::$db->folderAdd("john.doe@example.com", []);
+ }
+
+ function testAddAFolderWithABlankName() {
+ $this->assertException("missing", "Db", "ExceptionInput");
+ Data::$db->folderAdd("john.doe@example.com", ['name' => ""]);
+ }
+
+ function testAddAFolderWithAWhitespaceName() {
+ $this->assertException("whitespace", "Db", "ExceptionInput");
+ Data::$db->folderAdd("john.doe@example.com", ['name' => " "]);
+ }
+
+ function testAddAFolderWithoutAuthority() {
+ Phake::when(Data::$user)->authorize->thenReturn(false);
+ $this->assertException("notAuthorized", "User", "ExceptionAuthz");
+ Data::$db->folderAdd("john.doe@example.com", ['name' => "Sociology"]);
+ }
+}
\ No newline at end of file
diff --git a/tests/lib/Database/Setup.php b/tests/lib/Database/Setup.php
index f4d71f23..472b803c 100644
--- a/tests/lib/Database/Setup.php
+++ b/tests/lib/Database/Setup.php
@@ -71,7 +71,9 @@ trait Setup {
$cols = implode(",", array_keys($info['columns']));
foreach($this->drv->prepare("SELECT $cols from $table")->run() as $num => $row) {
$row = array_values($row);
- $this->assertSame($expected[$table]['rows'][$num], $row, "Row ".($num+1)." of table $table does not match expectations at array index $num.");
+ $this->assertGreaterThan(0, sizeof($info['rows']), "Expectations contain fewer rows than the database table $table");
+ $exp = array_shift($info['rows']);
+ $this->assertSame($exp, $row, "Row ".($num+1)." of table $table does not match expectations at array index $num.");
}
}
return true;
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index 2881db88..dbd3a1b0 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -10,40 +10,36 @@
beStrictAboutTestSize="true"
stopOnError="true">
-
- Exception/TestException.php
-
-
-
- Lang/TestLang.php
- Lang/TestLangComplex.php
- Lang/TestLangErrors.php
-
-
-
- Conf/TestConf.php
-
-
-
- User/TestUserMockInternal.php
- User/TestUserMockExternal.php
- User/TestUserInternalDriver.php
- User/TestAuthorization.php
-
-
-
- Db/SQLite3/TestDbResultSQLite3.php
- Db/SQLite3/TestDbStatementSQLite3.php
- Db/SQLite3/TestDbDriverSQLite3.php
- Db/SQLite3/TestDbUpdateSQLite3.php
-
-
-
- Db/SQLite3/Database/TestDatabaseUserSQLite3.php
-
-
-
- REST/NextCloudNews/TestNCNVersionDiscovery.php
-
-
+
+
+ Exception/TestException.php
+
+
+ Lang/TestLang.php
+ Lang/TestLangComplex.php
+ Lang/TestLangErrors.php
+
+
+ Conf/TestConf.php
+
+
+ User/TestUserMockInternal.php
+ User/TestUserMockExternal.php
+ User/TestUserInternalDriver.php
+ User/TestAuthorization.php
+
+
+ Db/SQLite3/TestDbResultSQLite3.php
+ Db/SQLite3/TestDbStatementSQLite3.php
+ Db/SQLite3/TestDbDriverSQLite3.php
+ Db/SQLite3/TestDbUpdateSQLite3.php
+
+
+ Db/SQLite3/Database/TestDatabaseUserSQLite3.php
+ Db/SQLite3/Database/TestDatabaseFolderSQLite3.php
+
+
+ REST/NextCloudNews/TestNCNVersionDiscovery.php
+
+
\ No newline at end of file