1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 21:22:40 +00:00

Fix whitespace

Also fixed my editor so tabs won't happen again!
This commit is contained in:
J. King 2017-04-06 21:41:21 -04:00
parent b02abec250
commit a67fe30408
32 changed files with 1402 additions and 1402 deletions

View file

@ -5,7 +5,7 @@ namespace JKingWeb\Arsse\Db;
abstract class AbstractStatement implements Statement {
abstract function runArray(array $values): Result;
abstract static function dateFormat(int $part = self::TS_BOTH): string;
abstract static function dateFormat(int $part = self::TS_BOTH): string;
public function run(...$values): Result {
return $this->runArray($values);
@ -30,41 +30,41 @@ abstract class AbstractStatement implements Statement {
return true;
}
protected function cast($v, string $t) {
switch($t) {
case "date":
return $this->formatDate($v, self::TS_DATE);
case "time":
return $this->formatDate($v, self::TS_TIME);
case "datetime":
return $this->formatDate($v, self::TS_BOTH);
case "null":
case "integer":
case "float":
case "binary":
case "string":
case "boolean":
if($t=="binary") $t = "string";
$value = $v;
try{
settype($value, $t);
} catch(\Throwable $e) {
// handle objects
$value = $v;
if($value instanceof \DateTimeInterface) {
$value = $value->getTimestamp();
if($t=="string") $value = $this->formatDate($value, self::TS_BOTH);
settype($value, $t);
} else {
$value = null;
settype($value, $t);
}
}
return $value;
default:
throw new Exception("paramTypeUnknown", $type);
}
}
protected function cast($v, string $t) {
switch($t) {
case "date":
return $this->formatDate($v, self::TS_DATE);
case "time":
return $this->formatDate($v, self::TS_TIME);
case "datetime":
return $this->formatDate($v, self::TS_BOTH);
case "null":
case "integer":
case "float":
case "binary":
case "string":
case "boolean":
if($t=="binary") $t = "string";
$value = $v;
try{
settype($value, $t);
} catch(\Throwable $e) {
// handle objects
$value = $v;
if($value instanceof \DateTimeInterface) {
$value = $value->getTimestamp();
if($t=="string") $value = $this->formatDate($value, self::TS_BOTH);
settype($value, $t);
} else {
$value = null;
settype($value, $t);
}
}
return $value;
default:
throw new Exception("paramTypeUnknown", $type);
}
}
protected function formatDate($date, int $part = self::TS_BOTH) {
// Force UTC.

View file

@ -12,15 +12,15 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
const SQLITE_CONSTRAINT = 19;
const SQLITE_MISMATCH = 20;
const BINDINGS = [
"null" => \SQLITE3_NULL,
"integer" => \SQLITE3_INTEGER,
"float" => \SQLITE3_FLOAT,
"date" => \SQLITE3_TEXT,
"time" => \SQLITE3_TEXT,
"datetime" => \SQLITE3_TEXT,
"binary" => \SQLITE3_BLOB,
"string" => \SQLITE3_TEXT,
"boolean" => \SQLITE3_INTEGER,
"null" => \SQLITE3_NULL,
"integer" => \SQLITE3_INTEGER,
"float" => \SQLITE3_FLOAT,
"date" => \SQLITE3_TEXT,
"time" => \SQLITE3_TEXT,
"datetime" => \SQLITE3_TEXT,
"binary" => \SQLITE3_BLOB,
"string" => \SQLITE3_TEXT,
"boolean" => \SQLITE3_INTEGER,
];
protected $db;

View file

@ -6,29 +6,29 @@ interface Statement {
const TS_TIME = -1;
const TS_DATE = 0;
const TS_BOTH = 1;
const TYPES = [
"null" => "null",
"nil" => "null",
"int" => "integer",
"integer" => "integer",
"float" => "float",
"double" => "float",
"real" => "float",
"numeric" => "float",
"date" => "date",
"time" => "time",
"datetime" => "datetime",
"timestamp" => "datetime",
"blob" => "binary",
"bin" => "binary",
"binary" => "binary",
"text" => "string",
"string" => "string",
"str" => "string",
"bool" => "boolean",
"boolean" => "boolean",
"bit" => "boolean",
];
const TYPES = [
"null" => "null",
"nil" => "null",
"int" => "integer",
"integer" => "integer",
"float" => "float",
"double" => "float",
"real" => "float",
"numeric" => "float",
"date" => "date",
"time" => "time",
"datetime" => "datetime",
"timestamp" => "datetime",
"blob" => "binary",
"bin" => "binary",
"binary" => "binary",
"text" => "string",
"string" => "string",
"str" => "string",
"bool" => "boolean",
"boolean" => "boolean",
"bit" => "boolean",
];
static function dateFormat(int $part = self::TS_BOTH): string;

View file

@ -3,6 +3,6 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\REST;
abstract class AbstractHandler implements Handler {
abstract function __construct();
abstract function dispatch(Request $req): Response;
abstract function __construct();
abstract function dispatch(Request $req): Response;
}

View file

@ -4,5 +4,5 @@ namespace JKingWeb\Arsse\REST;
interface Handler {
function __construct();
function dispatch(Request $req): Response;
function dispatch(Request $req): Response;
}

View file

@ -4,25 +4,25 @@ namespace JKingWeb\Arsse\REST\NextCloudNews;
use JKingWeb\Arsse\REST\Response;
class Versions extends \JKingWeb\Arsse\REST\AbstractHandler {
function __construct() {
}
function __construct() {
}
function dispatch(\JKingWeb\Arsse\REST\Request $req): \JKingWeb\Arsse\REST\Response {
// if a method other than GET was used, this is an error
if($req->method != "GET") {
return new Response(405);
}
if(preg_match("<^/?$>",$req->path)) {
// if the request path is an empty string or just a slash, return the supported versions
$out = [
'apiLevels' => [
'v1-2',
]
];
return new Response(200, $out);
} else {
// if the URL path was anything else, the client is probably trying a version we don't support
return new Response(404);
}
}
function dispatch(\JKingWeb\Arsse\REST\Request $req): \JKingWeb\Arsse\REST\Response {
// if a method other than GET was used, this is an error
if($req->method != "GET") {
return new Response(405);
}
if(preg_match("<^/?$>",$req->path)) {
// if the request path is an empty string or just a slash, return the supported versions
$out = [
'apiLevels' => [
'v1-2',
]
];
return new Response(200, $out);
} else {
// if the URL path was anything else, the client is probably trying a version we don't support
return new Response(404);
}
}
}

View file

@ -3,59 +3,59 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\REST;
class Request {
public $method = "GET";
public $url = "";
public $path ="";
public $query = "";
public $type ="";
public $body = "";
public $method = "GET";
public $url = "";
public $path ="";
public $query = "";
public $type ="";
public $body = "";
function __construct(string $method = null, string $url = null, string $body = null, string $contentType = null) {
if(is_null($method)) $method = $_SERVER['REQUEST_METHOD'];
if(is_null($url)) $url = $_SERVER['REQUEST_URI'];
if(is_null($body)) $body = file_get_contents("php://input");
if(is_null($contentType)) {
if(isset($_SERVER['HTTP_CONTENT_TYPE'])) {
$contentType = $_SERVER['HTTP_CONTENT_TYPE'];
} else {
$contentType = "";
}
}
$this->method = strtoupper($method);
$this->url = $url;
$this->body = $body;
$this->type = $contentType;
$this->refreshURL();
}
function __construct(string $method = null, string $url = null, string $body = null, string $contentType = null) {
if(is_null($method)) $method = $_SERVER['REQUEST_METHOD'];
if(is_null($url)) $url = $_SERVER['REQUEST_URI'];
if(is_null($body)) $body = file_get_contents("php://input");
if(is_null($contentType)) {
if(isset($_SERVER['HTTP_CONTENT_TYPE'])) {
$contentType = $_SERVER['HTTP_CONTENT_TYPE'];
} else {
$contentType = "";
}
}
$this->method = strtoupper($method);
$this->url = $url;
$this->body = $body;
$this->type = $contentType;
$this->refreshURL();
}
public function refreshURL() {
$url = $this->parseURL($this->url);
$this->path = $url['path'];
$this->query = $url['query'];
}
public function refreshURL() {
$url = $this->parseURL($this->url);
$this->path = $url['path'];
$this->query = $url['query'];
}
protected function parseURL(string $url): array {
// split the query string from the path
$parts = explode("?", $url);
$out = ['path' => $parts[0], 'query' => []];
// if there is a query string, parse it
if(isset($parts[1])) {
// split along & to get key-value pairs
$query = explode("&", $parts[1]);
for($a = 0; $a < sizeof($query); $a++) {
// split each pair, into no more than two parts
$data = explode("=", $query[$a], 2);
// decode the key
$key = rawurldecode($data[0]);
// decode the value if there is one
$value = "";
if(isset($data[1])) {
$value = rawurldecode($data[1]);
}
// add the pair to the query output, overwriting earlier values for the same key, is present
$out['query'][$key] = $value;
}
}
return $out;
}
protected function parseURL(string $url): array {
// split the query string from the path
$parts = explode("?", $url);
$out = ['path' => $parts[0], 'query' => []];
// if there is a query string, parse it
if(isset($parts[1])) {
// split along & to get key-value pairs
$query = explode("&", $parts[1]);
for($a = 0; $a < sizeof($query); $a++) {
// split each pair, into no more than two parts
$data = explode("=", $query[$a], 2);
// decode the key
$key = rawurldecode($data[0]);
// decode the value if there is one
$value = "";
if(isset($data[1])) {
$value = rawurldecode($data[1]);
}
// add the pair to the query output, overwriting earlier values for the same key, is present
$out['query'][$key] = $value;
}
}
return $out;
}
}

View file

@ -3,20 +3,20 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\REST;
class Response {
const T_JSON = "application/json";
const T_XML = "application/xml";
const T_TEXT = "text/plain";
const T_JSON = "application/json";
const T_XML = "application/xml";
const T_TEXT = "text/plain";
public $code;
public $payload;
public $type;
public $fields;
public $code;
public $payload;
public $type;
public $fields;
function __construct(int $code, $payload = null, string $type = self::T_JSON, array $extraFields = []) {
$this->code = $code;
$this->payload = $payload;
$this->type = $type;
$this->fields = $extraFields;
}
function __construct(int $code, $payload = null, string $type = self::T_JSON, array $extraFields = []) {
$this->code = $code;
$this->payload = $payload;
$this->type = $type;
$this->fields = $extraFields;
}
}

View file

@ -7,279 +7,279 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
use Test\Tools;
protected $data;
protected $drv;
protected $drv;
function setUp() {
$this->clearData();
$conf = new Conf();
$conf->dbDriver = Db\SQLite3\Driver::class;
$conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook');
Data::$conf = $conf;
$this->drv = new Db\SQLite3\Driver(true);
$this->clearData();
$conf = new Conf();
$conf->dbDriver = Db\SQLite3\Driver::class;
$conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook');
Data::$conf = $conf;
$this->drv = new Db\SQLite3\Driver(true);
}
function tearDown() {
unset($this->drv);
unlink(Data::$conf->dbSQLite3File);
$this->clearData();
unlink(Data::$conf->dbSQLite3File);
$this->clearData();
}
function testFetchDriverName() {
$class = Data::$conf->dbDriver;
$this->assertTrue(strlen($class::driverName()) > 0);
}
function testFetchDriverName() {
$class = Data::$conf->dbDriver;
$this->assertTrue(strlen($class::driverName()) > 0);
}
function testExecAValidStatement() {
$this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key)"));
}
function testExecAValidStatement() {
$this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key)"));
}
function testExecAnInvalidStatement() {
$this->assertException("engineErrorGeneral", "Db");
$this->drv->exec("And the meek shall inherit the earth...");
}
function testExecAnInvalidStatement() {
$this->assertException("engineErrorGeneral", "Db");
$this->drv->exec("And the meek shall inherit the earth...");
}
function testExecMultipleStatements() {
$this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key); INSERT INTO test(id) values(2112)"));
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->assertEquals(2112, $ch->querySingle("SELECT id from test"));
}
function testExecMultipleStatements() {
$this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key); INSERT INTO test(id) values(2112)"));
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->assertEquals(2112, $ch->querySingle("SELECT id from test"));
}
function testExecTimeout() {
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$ch->exec("BEGIN EXCLUSIVE TRANSACTION");
$this->assertException("general", "Db", "ExceptionTimeout");
$this->drv->exec("CREATE TABLE test(id integer primary key)");
}
function testExecTimeout() {
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$ch->exec("BEGIN EXCLUSIVE TRANSACTION");
$this->assertException("general", "Db", "ExceptionTimeout");
$this->drv->exec("CREATE TABLE test(id integer primary key)");
}
function testExecConstraintViolation() {
$this->drv->exec("CREATE TABLE test(id integer not null)");
$this->assertException("constraintViolation", "Db", "ExceptionInput");
$this->drv->exec("INSERT INTO test(id) values(null)");
}
function testExecConstraintViolation() {
$this->drv->exec("CREATE TABLE test(id integer not null)");
$this->assertException("constraintViolation", "Db", "ExceptionInput");
$this->drv->exec("INSERT INTO test(id) values(null)");
}
function testExecTypeViolation() {
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->assertException("typeViolation", "Db", "ExceptionInput");
$this->drv->exec("INSERT INTO test(id) values('ook')");
}
function testExecTypeViolation() {
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->assertException("typeViolation", "Db", "ExceptionInput");
$this->drv->exec("INSERT INTO test(id) values('ook')");
}
function testMakeAValidQuery() {
$this->assertInstanceOf(Db\SQLite3\Result::class, $this->drv->query("SELECT 1"));
}
function testMakeAValidQuery() {
$this->assertInstanceOf(Db\SQLite3\Result::class, $this->drv->query("SELECT 1"));
}
function testMakeAnInvalidQuery() {
$this->assertException("engineErrorGeneral", "Db");
$this->drv->query("Apollo was astonished; Dionysus thought me mad");
}
function testMakeAnInvalidQuery() {
$this->assertException("engineErrorGeneral", "Db");
$this->drv->query("Apollo was astonished; Dionysus thought me mad");
}
function testQueryTimeout() {
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$ch->exec("BEGIN EXCLUSIVE TRANSACTION");
$this->assertException("general", "Db", "ExceptionTimeout");
$this->drv->query("CREATE TABLE test(id integer primary key)");
}
function testQueryTimeout() {
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$ch->exec("BEGIN EXCLUSIVE TRANSACTION");
$this->assertException("general", "Db", "ExceptionTimeout");
$this->drv->query("CREATE TABLE test(id integer primary key)");
}
function testQueryConstraintViolation() {
$this->drv->exec("CREATE TABLE test(id integer not null)");
$this->assertException("constraintViolation", "Db", "ExceptionInput");
$this->drv->query("INSERT INTO test(id) values(null)");
}
function testQueryConstraintViolation() {
$this->drv->exec("CREATE TABLE test(id integer not null)");
$this->assertException("constraintViolation", "Db", "ExceptionInput");
$this->drv->query("INSERT INTO test(id) values(null)");
}
function testQueryTypeViolation() {
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->assertException("typeViolation", "Db", "ExceptionInput");
$this->drv->query("INSERT INTO test(id) values('ook')");
}
function testQueryTypeViolation() {
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->assertException("typeViolation", "Db", "ExceptionInput");
$this->drv->query("INSERT INTO test(id) values('ook')");
}
function testPrepareAValidQuery() {
$s = $this->drv->prepare("SELECT ?, ?", "int", "int");
$this->assertInstanceOf(Db\SQLite3\Statement::class, $s);
}
function testPrepareAValidQuery() {
$s = $this->drv->prepare("SELECT ?, ?", "int", "int");
$this->assertInstanceOf(Db\SQLite3\Statement::class, $s);
}
function testPrepareAnInvalidQuery() {
$this->assertException("engineErrorGeneral", "Db");
$s = $this->drv->prepare("This is an invalid query", "int", "int");
}
function testPrepareAnInvalidQuery() {
$this->assertException("engineErrorGeneral", "Db");
$s = $this->drv->prepare("This is an invalid query", "int", "int");
}
function testBeginTransaction() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testBeginTransaction() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testCommitTransaction() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(1, $ch->querySingle($select));
}
function testCommitTransaction() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(1, $ch->querySingle($select));
}
function testRollbackTransaction() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(0, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testRollbackTransaction() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(0, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testBeginChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testBeginChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testCommitChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(2, $ch->querySingle($select));
}
function testCommitChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(2, $ch->querySingle($select));
}
function testRollbackChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(0, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testRollbackChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(0, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testPartiallyRollbackChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(1, $ch->querySingle($select));
}
function testPartiallyRollbackChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit();
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(1, $ch->querySingle($select));
}
function testFullyRollbackChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback(true);
$this->assertEquals(0, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testFullyRollbackChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->rollback(true);
$this->assertEquals(0, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
}
function testFullyCommitChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit(true);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(2, $ch->querySingle($select));
}
function testFullyCommitChainedTransactions() {
$select = "SELECT count(*) FROM test";
$insert = "INSERT INTO test(id) values(null)";
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
$this->drv->exec("CREATE TABLE test(id integer primary key)");
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(1, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->begin();
$this->drv->query($insert);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(0, $ch->querySingle($select));
$this->drv->commit(true);
$this->assertEquals(2, $this->drv->query($select)->getValue());
$this->assertEquals(2, $ch->querySingle($select));
}
function testFetchSchemaVersion() {
$this->assertSame(0, $this->drv->schemaVersion());
$this->drv->exec("PRAGMA user_version=1");
$this->assertSame(1, $this->drv->schemaVersion());
$this->drv->exec("PRAGMA user_version=2");
$this->assertSame(2, $this->drv->schemaVersion());
function testFetchSchemaVersion() {
$this->assertSame(0, $this->drv->schemaVersion());
$this->drv->exec("PRAGMA user_version=1");
$this->assertSame(1, $this->drv->schemaVersion());
$this->drv->exec("PRAGMA user_version=2");
$this->assertSame(2, $this->drv->schemaVersion());
}
}
function testManipulateAdvisoryLock() {
$this->assertTrue($this->drv->unlock());
$this->assertFalse($this->drv->isLocked());
$this->assertTrue($this->drv->lock());
$this->assertFalse($this->drv->isLocked());
$this->drv->exec("CREATE TABLE arsse_settings(key primary key, value, type) without rowid; PRAGMA user_version=1");
$this->assertTrue($this->drv->lock());
$this->assertTrue($this->drv->isLocked());
$this->assertFalse($this->drv->lock());
$this->drv->exec("PRAGMA user_version=0");
$this->assertFalse($this->drv->isLocked());
$this->assertTrue($this->drv->lock());
$this->assertFalse($this->drv->isLocked());
$this->drv->exec("PRAGMA user_version=1");
$this->assertTrue($this->drv->isLocked());
$this->assertTrue($this->drv->unlock());
$this->assertFalse($this->drv->isLocked());
}
function testManipulateAdvisoryLock() {
$this->assertTrue($this->drv->unlock());
$this->assertFalse($this->drv->isLocked());
$this->assertTrue($this->drv->lock());
$this->assertFalse($this->drv->isLocked());
$this->drv->exec("CREATE TABLE arsse_settings(key primary key, value, type) without rowid; PRAGMA user_version=1");
$this->assertTrue($this->drv->lock());
$this->assertTrue($this->drv->isLocked());
$this->assertFalse($this->drv->lock());
$this->drv->exec("PRAGMA user_version=0");
$this->assertFalse($this->drv->isLocked());
$this->assertTrue($this->drv->lock());
$this->assertFalse($this->drv->isLocked());
$this->drv->exec("PRAGMA user_version=1");
$this->assertTrue($this->drv->isLocked());
$this->assertTrue($this->drv->unlock());
$this->assertFalse($this->drv->isLocked());
}
}

View file

@ -8,88 +8,88 @@ class TestDbStatementSQLite3 extends \PHPUnit\Framework\TestCase {
use Test\Tools, Test\Db\BindingTests;
protected $c;
static protected $imp = Db\SQLite3\Statement::class;
static protected $imp = Db\SQLite3\Statement::class;
function setUp() {
date_default_timezone_set("UTC");
date_default_timezone_set("UTC");
$c = new \SQLite3(":memory:");
$c->enableExceptions(true);
$this->c = $c;
}
function tearDown() {
try {$this->s->close();} catch(\Exception $e) {}
try {$this->s->close();} catch(\Exception $e) {}
$this->c->close();
unset($this->c);
}
protected function checkBinding($input, array $expectations) {
$nativeStatement = $this->c->prepare("SELECT ? as value");
$s = new self::$imp($this->c, $nativeStatement);
$types = array_unique(Statement::TYPES);
foreach($types as $type) {
$s->rebindArray([$type]);
$val = $s->runArray([$input])->getRow()['value'];
$this->assertSame($expectations[$type], $val, "Type $type failed comparison.");
}
}
function testConstructStatement() {
protected function checkBinding($input, array $expectations) {
$nativeStatement = $this->c->prepare("SELECT ? as value");
$this->assertInstanceOf(Statement::class, new Db\SQLite3\Statement($this->c, $nativeStatement));
}
$s = new self::$imp($this->c, $nativeStatement);
$types = array_unique(Statement::TYPES);
foreach($types as $type) {
$s->rebindArray([$type]);
$val = $s->runArray([$input])->getRow()['value'];
$this->assertSame($expectations[$type], $val, "Type $type failed comparison.");
}
}
function testBindMissingValue() {
$nativeStatement = $this->c->prepare("SELECT ? as value");
$s = new self::$imp($this->c, $nativeStatement);
$val = $s->runArray()->getRow()['value'];
$this->assertSame(null, $val);
}
function testConstructStatement() {
$nativeStatement = $this->c->prepare("SELECT ? as value");
$this->assertInstanceOf(Statement::class, new Db\SQLite3\Statement($this->c, $nativeStatement));
}
function testBindMissingValue() {
$nativeStatement = $this->c->prepare("SELECT ? as value");
$s = new self::$imp($this->c, $nativeStatement);
$val = $s->runArray()->getRow()['value'];
$this->assertSame(null, $val);
}
function testBindMultipleValues() {
$exp = [
'one' => 1,
'two' => 2,
];
$nativeStatement = $this->c->prepare("SELECT ? as one, ? as two");
$s = new self::$imp($this->c, $nativeStatement, ["int", "int"]);
$val = $s->runArray([1,2])->getRow();
$this->assertSame($exp, $val);
$nativeStatement = $this->c->prepare("SELECT ? as one, ? as two");
$s = new self::$imp($this->c, $nativeStatement, ["int", "int"]);
$val = $s->runArray([1,2])->getRow();
$this->assertSame($exp, $val);
}
function testBindRecursively() {
function testBindRecursively() {
$exp = [
'one' => 1,
'two' => 2,
'three' => 3,
'four' => 4,
'four' => 4,
];
$nativeStatement = $this->c->prepare("SELECT ? as one, ? as two, ? as three, ? as four");
$s = new self::$imp($this->c, $nativeStatement, ["int", ["int", "int"], "int"]);
$val = $s->runArray([1, [2, 3], 4])->getRow();
$this->assertSame($exp, $val);
}
$nativeStatement = $this->c->prepare("SELECT ? as one, ? as two, ? as three, ? as four");
$s = new self::$imp($this->c, $nativeStatement, ["int", ["int", "int"], "int"]);
$val = $s->runArray([1, [2, 3], 4])->getRow();
$this->assertSame($exp, $val);
}
function testBindWithoutType() {
$nativeStatement = $this->c->prepare("SELECT ? as value");
$this->assertException("paramTypeMissing", "Db");
$s = new self::$imp($this->c, $nativeStatement, []);
$s->runArray([1]);
$this->assertException("paramTypeMissing", "Db");
$s = new self::$imp($this->c, $nativeStatement, []);
$s->runArray([1]);
}
function testViolateConstraint() {
$this->c->exec("CREATE TABLE test(id integer not null)");
$nativeStatement = $this->c->prepare("INSERT INTO test(id) values(?)");
$s = new self::$imp($this->c, $nativeStatement, ["int"]);
$this->assertException("constraintViolation", "Db", "ExceptionInput");
$s->runArray([null]);
}
function testViolateConstraint() {
$this->c->exec("CREATE TABLE test(id integer not null)");
$nativeStatement = $this->c->prepare("INSERT INTO test(id) values(?)");
$s = new self::$imp($this->c, $nativeStatement, ["int"]);
$this->assertException("constraintViolation", "Db", "ExceptionInput");
$s->runArray([null]);
}
function testMismatchTypes() {
$this->c->exec("CREATE TABLE test(id integer primary key)");
$nativeStatement = $this->c->prepare("INSERT INTO test(id) values(?)");
$s = new self::$imp($this->c, $nativeStatement, ["str"]);
$this->assertException("typeViolation", "Db", "ExceptionInput");
$s->runArray(['ook']);
}
function testMismatchTypes() {
$this->c->exec("CREATE TABLE test(id integer primary key)");
$nativeStatement = $this->c->prepare("INSERT INTO test(id) values(?)");
$s = new self::$imp($this->c, $nativeStatement, ["str"]);
$this->assertException("typeViolation", "Db", "ExceptionInput");
$s->runArray(['ook']);
}
}

View file

@ -8,84 +8,84 @@ class TestDbUpdateSQLite3 extends \PHPUnit\Framework\TestCase {
use Test\Tools;
protected $data;
protected $drv;
protected $vfs;
protected $base;
protected $drv;
protected $vfs;
protected $base;
const MINIMAL1 = "create table arsse_settings(key text primary key not null, value text, type text not null); pragma user_version=1";
const MINIMAL2 = "pragma user_version=2";
const MINIMAL1 = "create table arsse_settings(key text primary key not null, value text, type text not null); pragma user_version=1";
const MINIMAL2 = "pragma user_version=2";
function setUp() {
$this->clearData();
$this->clearData();
$this->vfs = vfsStream::setup("schemata", null, ['SQLite3' => []]);
$conf = new Conf();
$conf->dbDriver = Db\SQLite3\Driver::class;
$conf->dbSchemaBase = $this->vfs->url();
$this->base = $this->vfs->url()."/SQLite3/";
$conf->dbSQLite3File = ":memory:";
Data::$conf = $conf;
$this->drv = new Db\SQLite3\Driver(true);
$conf = new Conf();
$conf->dbDriver = Db\SQLite3\Driver::class;
$conf->dbSchemaBase = $this->vfs->url();
$this->base = $this->vfs->url()."/SQLite3/";
$conf->dbSQLite3File = ":memory:";
Data::$conf = $conf;
$this->drv = new Db\SQLite3\Driver(true);
}
function tearDown() {
unset($this->drv);
unset($this->data);
unset($this->vfs);
$this->clearData();
$this->clearData();
}
function testLoadMissingFile() {
$this->assertException("updateFileMissing", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadMissingFile() {
$this->assertException("updateFileMissing", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadUnreadableFile() {
touch($this->base."0.sql");
chmod($this->base."0.sql", 0000);
$this->assertException("updateFileUnreadable", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadUnreadableFile() {
touch($this->base."0.sql");
chmod($this->base."0.sql", 0000);
$this->assertException("updateFileUnreadable", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadCorruptFile() {
file_put_contents($this->base."0.sql", "This is a corrupt file");
$this->assertException("updateFileError", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadCorruptFile() {
file_put_contents($this->base."0.sql", "This is a corrupt file");
$this->assertException("updateFileError", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadIncompleteFile() {
file_put_contents($this->base."0.sql", "create table arsse_settings(key text primary key not null, value text, type text not null);");
$this->assertException("updateFileIncomplete", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadIncompleteFile() {
file_put_contents($this->base."0.sql", "create table arsse_settings(key text primary key not null, value text, type text not null);");
$this->assertException("updateFileIncomplete", "Db");
$this->drv->schemaUpdate(1);
}
function testLoadCorrectFile() {
file_put_contents($this->base."0.sql", self::MINIMAL1);
$this->drv->schemaUpdate(1);
$this->assertEquals(1, $this->drv->schemaVersion());
}
function testLoadCorrectFile() {
file_put_contents($this->base."0.sql", self::MINIMAL1);
$this->drv->schemaUpdate(1);
$this->assertEquals(1, $this->drv->schemaVersion());
}
function testPerformPartialUpdate() {
file_put_contents($this->base."0.sql", self::MINIMAL1);
file_put_contents($this->base."1.sql", "");
$this->assertException("updateFileIncomplete", "Db");
try {
$this->drv->schemaUpdate(2);
} catch(Exception $e) {
$this->assertEquals(1, $this->drv->schemaVersion());
throw $e;
}
}
function testPerformPartialUpdate() {
file_put_contents($this->base."0.sql", self::MINIMAL1);
file_put_contents($this->base."1.sql", "");
$this->assertException("updateFileIncomplete", "Db");
try {
$this->drv->schemaUpdate(2);
} catch(Exception $e) {
$this->assertEquals(1, $this->drv->schemaVersion());
throw $e;
}
}
function testPerformSequentialUpdate() {
file_put_contents($this->base."0.sql", self::MINIMAL1);
file_put_contents($this->base."1.sql", self::MINIMAL2);
$this->drv->schemaUpdate(2);
$this->assertEquals(2, $this->drv->schemaVersion());
}
function testPerformSequentialUpdate() {
file_put_contents($this->base."0.sql", self::MINIMAL1);
file_put_contents($this->base."1.sql", self::MINIMAL2);
$this->drv->schemaUpdate(2);
$this->assertEquals(2, $this->drv->schemaVersion());
}
function testPerformActualUpdate() {
Data::$conf->dbSchemaBase = (new Conf())->dbSchemaBase;
$this->drv->schemaUpdate(Database::SCHEMA_VERSION);
$this->assertEquals(Database::SCHEMA_VERSION, $this->drv->schemaVersion());
}
function testPerformActualUpdate() {
Data::$conf->dbSchemaBase = (new Conf())->dbSchemaBase;
$this->drv->schemaUpdate(Database::SCHEMA_VERSION);
$this->assertEquals(Database::SCHEMA_VERSION, $this->drv->schemaVersion());
}
}

View file

@ -10,161 +10,161 @@ use Phake;
class TestNCNV1_2 extends \PHPUnit\Framework\TestCase {
use Test\Tools;
protected $h;
protected $h;
function setUp() {
$this->clearData();
function setUp() {
$this->clearData();
// create a mock user manager
Data::$user = Phake::mock(User::class);
Phake::when(Data::$user)->authHTTP->thenReturn(true);
Data::$user->id = "john.doe@example.com";
// create a mock database interface
Data::$db = Phake::mock(Database::Class);
$this->h = new REST\NextCloudNews\V1_2();
}
Data::$user->id = "john.doe@example.com";
// create a mock database interface
Data::$db = Phake::mock(Database::Class);
$this->h = new REST\NextCloudNews\V1_2();
}
function tearDown() {
$this->clearData();
}
function tearDown() {
$this->clearData();
}
function testRespondToInvalidPaths() {
$errs = [
404 => [
['GET', "/"],
['PUT', "/"],
['POST', "/"],
['DELETE', "/"],
['GET', "/folders/1/invalid"],
['PUT', "/folders/1/invalid"],
['POST', "/folders/1/invalid"],
['DELETE', "/folders/1/invalid"],
['GET', "/version/invalid"],
['PUT', "/version/invalid"],
['POST', "/version/invalid"],
['DELETE', "/version/invalid"],
],
405 => [
'GET' => [
['PUT', "/version"],
['POST', "/version"],
['DELETE', "/version"],
],
'GET, POST' => [
['PUT', "/folders"],
['DELETE', "/folders"],
],
'PUT, DELETE' => [
['GET', "/folders/1"],
['POST', "/folders/1"],
],
],
];
foreach($errs[404] as $req) {
$exp = new Response(404);
list($method, $path) = $req;
$this->assertEquals($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 404.");
}
foreach($errs[405] as $allow => $cases) {
$exp = new Response(405, "", "", ['Allow: '.$allow]);
foreach($cases as $req) {
list($method, $path) = $req;
$this->assertEquals($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 405.");
}
}
}
function testRespondToInvalidPaths() {
$errs = [
404 => [
['GET', "/"],
['PUT', "/"],
['POST', "/"],
['DELETE', "/"],
['GET', "/folders/1/invalid"],
['PUT', "/folders/1/invalid"],
['POST', "/folders/1/invalid"],
['DELETE', "/folders/1/invalid"],
['GET', "/version/invalid"],
['PUT', "/version/invalid"],
['POST', "/version/invalid"],
['DELETE', "/version/invalid"],
],
405 => [
'GET' => [
['PUT', "/version"],
['POST', "/version"],
['DELETE', "/version"],
],
'GET, POST' => [
['PUT', "/folders"],
['DELETE', "/folders"],
],
'PUT, DELETE' => [
['GET', "/folders/1"],
['POST', "/folders/1"],
],
],
];
foreach($errs[404] as $req) {
$exp = new Response(404);
list($method, $path) = $req;
$this->assertEquals($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 404.");
}
foreach($errs[405] as $allow => $cases) {
$exp = new Response(405, "", "", ['Allow: '.$allow]);
foreach($cases as $req) {
list($method, $path) = $req;
$this->assertEquals($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 405.");
}
}
}
function testListFolders() {
$list = [
['id' => 1, 'name' => "Software", 'parent' => null],
['id' => 12, 'name' => "Hardware", 'parent' => null],
];
Phake::when(Data::$db)->folderList(Data::$user->id, null, false)->thenReturn(new Result($list));
$exp = new Response(200, ['folders' => $list]);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/folders")));
}
function testListFolders() {
$list = [
['id' => 1, 'name' => "Software", 'parent' => null],
['id' => 12, 'name' => "Hardware", 'parent' => null],
];
Phake::when(Data::$db)->folderList(Data::$user->id, null, false)->thenReturn(new Result($list));
$exp = new Response(200, ['folders' => $list]);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/folders")));
}
function testAddAFolder() {
$in = [
["name" => "Software"],
["name" => "Hardware"],
];
$out = [
['id' => 1, 'name' => "Software", 'parent' => null],
['id' => 2, 'name' => "Hardware", 'parent' => null],
];
// set of various mocks for testing
Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[0])->thenReturn(1)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[1])->thenReturn(2)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 1)->thenReturn($out[0]);
Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 2)->thenReturn($out[1]);
// set up mocks that produce errors
Phake::when(Data::$db)->folderAdd(Data::$user->id, [])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => ""])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => " "])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace"));
// correctly add two folders, using different means
$exp = new Response(200, ['folders' => [$out[0]]]);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json')));
$exp = new Response(200, ['folders' => [$out[1]]]);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders?name=Hardware")));
Phake::verify(Data::$db)->folderAdd(Data::$user->id, $in[0]);
Phake::verify(Data::$db)->folderAdd(Data::$user->id, $in[1]);
Phake::verify(Data::$db)->folderPropertiesGet(Data::$user->id, 1);
Phake::verify(Data::$db)->folderPropertiesGet(Data::$user->id, 2);
// test bad folder names
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders")));
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":""}', 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":" "}', 'application/json')));
// try adding the same two folders again
$exp = new Response(409);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders?name=Software")));
$exp = new Response(409);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[1]), 'application/json')));
}
function testAddAFolder() {
$in = [
["name" => "Software"],
["name" => "Hardware"],
];
$out = [
['id' => 1, 'name' => "Software", 'parent' => null],
['id' => 2, 'name' => "Hardware", 'parent' => null],
];
// set of various mocks for testing
Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[0])->thenReturn(1)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->folderAdd(Data::$user->id, $in[1])->thenReturn(2)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation")); // error on the second call
Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 1)->thenReturn($out[0]);
Phake::when(Data::$db)->folderPropertiesGet(Data::$user->id, 2)->thenReturn($out[1]);
// set up mocks that produce errors
Phake::when(Data::$db)->folderAdd(Data::$user->id, [])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => ""])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
Phake::when(Data::$db)->folderAdd(Data::$user->id, ['name' => " "])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace"));
// correctly add two folders, using different means
$exp = new Response(200, ['folders' => [$out[0]]]);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json')));
$exp = new Response(200, ['folders' => [$out[1]]]);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders?name=Hardware")));
Phake::verify(Data::$db)->folderAdd(Data::$user->id, $in[0]);
Phake::verify(Data::$db)->folderAdd(Data::$user->id, $in[1]);
Phake::verify(Data::$db)->folderPropertiesGet(Data::$user->id, 1);
Phake::verify(Data::$db)->folderPropertiesGet(Data::$user->id, 2);
// test bad folder names
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders")));
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":""}', 'application/json')));
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":" "}', 'application/json')));
// try adding the same two folders again
$exp = new Response(409);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders?name=Software")));
$exp = new Response(409);
$this->assertEquals($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[1]), 'application/json')));
}
function testRemoveAFolder() {
Phake::when(Data::$db)->folderRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing"));
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
// fail on the second invocation because it no longer exists
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
Phake::verify(Data::$db, Phake::times(2))->folderRemove(Data::$user->id, 1);
// use a non-integer folder ID
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/invalid")));
}
function testRemoveAFolder() {
Phake::when(Data::$db)->folderRemove(Data::$user->id, 1)->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing"));
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
// fail on the second invocation because it no longer exists
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/1")));
Phake::verify(Data::$db, Phake::times(2))->folderRemove(Data::$user->id, 1);
// use a non-integer folder ID
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("DELETE", "/folders/invalid")));
}
function testRenameAFolder() {
$in = [
["name" => "Software"],
["name" => "Software"],
["name" => ""],
["name" => " "],
[],
];
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[0])->thenReturn(true);
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 2, $in[1])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[2])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[3])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 3, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing")); // folder ID 3 does not exist
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json')));
$exp = new Response(409);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/2", json_encode($in[1]), 'application/json')));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[2]), 'application/json')));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[3]), 'application/json')));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[4]), 'application/json')));
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/3", json_encode($in[0]), 'application/json')));
}
function testRenameAFolder() {
$in = [
["name" => "Software"],
["name" => "Software"],
["name" => ""],
["name" => " "],
[],
];
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[0])->thenReturn(true);
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 2, $in[1])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("constraintViolation"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[2])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("missing"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[3])->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("whitespace"));
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database
Phake::when(Data::$db)->folderPropertiesSet(Data::$user->id, 3, $this->anything())->thenThrow(new \JKingWeb\Arsse\Db\ExceptionInput("idMissing")); // folder ID 3 does not exist
$exp = new Response(204);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json')));
$exp = new Response(409);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/2", json_encode($in[1]), 'application/json')));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[2]), 'application/json')));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[3]), 'application/json')));
$exp = new Response(422);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[4]), 'application/json')));
$exp = new Response(404);
$this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "/folders/3", json_encode($in[0]), 'application/json')));
}
function testRetrieveServerVersion() {
$exp = new Response(200, ['version' => \JKingWeb\Arsse\VERSION]);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/version")));
}
function testRetrieveServerVersion() {
$exp = new Response(200, ['version' => \JKingWeb\Arsse\VERSION]);
$this->assertEquals($exp, $this->h->dispatch(new Request("GET", "/version")));
}
}

View file

@ -8,37 +8,37 @@ use JKingWeb\Arsse\Rest\Response;
class TestNCNVersionDiscovery extends \PHPUnit\Framework\TestCase {
use Test\Tools;
function setUp() {
$this->clearData();
}
function setUp() {
$this->clearData();
}
function testFetchVersionList() {
$exp = new Response(200, ['apiLevels' => ['v1-2']]);
$h = new Rest\NextCloudNews\Versions();
$req = new Request("GET", "/");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
$req = new Request("GET", "");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
$req = new Request("GET", "/?id=1827");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
function testFetchVersionList() {
$exp = new Response(200, ['apiLevels' => ['v1-2']]);
$h = new Rest\NextCloudNews\Versions();
$req = new Request("GET", "/");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
$req = new Request("GET", "");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
$req = new Request("GET", "/?id=1827");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
function testUseIncorrectMethod() {
$exp = new Response(405);
$h = new Rest\NextCloudNews\Versions();
$req = new Request("POST", "/");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
function testUseIncorrectMethod() {
$exp = new Response(405);
$h = new Rest\NextCloudNews\Versions();
$req = new Request("POST", "/");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
function testUseIncorrectPath() {
$exp = new Response(404);
$h = new Rest\NextCloudNews\Versions();
$req = new Request("GET", "/ook");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
function testUseIncorrectPath() {
$exp = new Response(404);
$h = new Rest\NextCloudNews\Versions();
$req = new Request("GET", "/ook");
$res = $h->dispatch($req);
$this->assertEquals($exp, $res);
}
}

View file

@ -6,296 +6,296 @@ namespace JKingWeb\Arsse;
class TestAuthorization extends \PHPUnit\Framework\TestCase {
use Test\Tools;
const USERS = [
'user@example.com' => User\Driver::RIGHTS_NONE,
'user@example.org' => User\Driver::RIGHTS_NONE,
'dman@example.com' => User\Driver::RIGHTS_DOMAIN_MANAGER,
'dman@example.org' => User\Driver::RIGHTS_DOMAIN_MANAGER,
'dadm@example.com' => User\Driver::RIGHTS_DOMAIN_ADMIN,
'dadm@example.org' => User\Driver::RIGHTS_DOMAIN_ADMIN,
'gman@example.com' => User\Driver::RIGHTS_GLOBAL_MANAGER,
'gman@example.org' => User\Driver::RIGHTS_GLOBAL_MANAGER,
'gadm@example.com' => User\Driver::RIGHTS_GLOBAL_ADMIN,
'gadm@example.org' => User\Driver::RIGHTS_GLOBAL_ADMIN,
// invalid rights levels
'bad1@example.com' => User\Driver::RIGHTS_NONE+1,
'bad1@example.org' => User\Driver::RIGHTS_NONE+1,
'bad2@example.com' => User\Driver::RIGHTS_DOMAIN_MANAGER+1,
'bad2@example.org' => User\Driver::RIGHTS_DOMAIN_MANAGER+1,
'bad3@example.com' => User\Driver::RIGHTS_DOMAIN_ADMIN+1,
'bad3@example.org' => User\Driver::RIGHTS_DOMAIN_ADMIN+1,
'bad4@example.com' => User\Driver::RIGHTS_GLOBAL_MANAGER+1,
'bad4@example.org' => User\Driver::RIGHTS_GLOBAL_MANAGER+1,
'bad5@example.com' => User\Driver::RIGHTS_GLOBAL_ADMIN+1,
'bad5@example.org' => User\Driver::RIGHTS_GLOBAL_ADMIN+1,
const USERS = [
'user@example.com' => User\Driver::RIGHTS_NONE,
'user@example.org' => User\Driver::RIGHTS_NONE,
'dman@example.com' => User\Driver::RIGHTS_DOMAIN_MANAGER,
'dman@example.org' => User\Driver::RIGHTS_DOMAIN_MANAGER,
'dadm@example.com' => User\Driver::RIGHTS_DOMAIN_ADMIN,
'dadm@example.org' => User\Driver::RIGHTS_DOMAIN_ADMIN,
'gman@example.com' => User\Driver::RIGHTS_GLOBAL_MANAGER,
'gman@example.org' => User\Driver::RIGHTS_GLOBAL_MANAGER,
'gadm@example.com' => User\Driver::RIGHTS_GLOBAL_ADMIN,
'gadm@example.org' => User\Driver::RIGHTS_GLOBAL_ADMIN,
// invalid rights levels
'bad1@example.com' => User\Driver::RIGHTS_NONE+1,
'bad1@example.org' => User\Driver::RIGHTS_NONE+1,
'bad2@example.com' => User\Driver::RIGHTS_DOMAIN_MANAGER+1,
'bad2@example.org' => User\Driver::RIGHTS_DOMAIN_MANAGER+1,
'bad3@example.com' => User\Driver::RIGHTS_DOMAIN_ADMIN+1,
'bad3@example.org' => User\Driver::RIGHTS_DOMAIN_ADMIN+1,
'bad4@example.com' => User\Driver::RIGHTS_GLOBAL_MANAGER+1,
'bad4@example.org' => User\Driver::RIGHTS_GLOBAL_MANAGER+1,
'bad5@example.com' => User\Driver::RIGHTS_GLOBAL_ADMIN+1,
'bad5@example.org' => User\Driver::RIGHTS_GLOBAL_ADMIN+1,
];
const LEVELS = [
User\Driver::RIGHTS_NONE,
User\Driver::RIGHTS_DOMAIN_MANAGER,
User\Driver::RIGHTS_DOMAIN_ADMIN,
User\Driver::RIGHTS_GLOBAL_MANAGER,
User\Driver::RIGHTS_GLOBAL_ADMIN,
];
const DOMAINS = [
'@example.com',
'@example.org',
"",
];
];
const LEVELS = [
User\Driver::RIGHTS_NONE,
User\Driver::RIGHTS_DOMAIN_MANAGER,
User\Driver::RIGHTS_DOMAIN_ADMIN,
User\Driver::RIGHTS_GLOBAL_MANAGER,
User\Driver::RIGHTS_GLOBAL_ADMIN,
];
const DOMAINS = [
'@example.com',
'@example.org',
"",
];
protected $data;
protected $data;
function setUp(string $drv = Test\User\DriverInternalMock::class, string $db = null) {
$this->clearData();
$conf = new Conf();
$conf->userDriver = $drv;
$conf->userAuthPreferHTTP = true;
$conf->userComposeNames = true;
Data::$conf = $conf;
if($db !== null) {
Data::$db = new $db();
}
Data::$user = new User();
Data::$user->authorizationEnabled(false);
foreach(self::USERS as $user => $level) {
Data::$user->add($user, "");
Data::$user->rightsSet($user, $level);
}
Data::$user->authorizationEnabled(true);
}
$this->clearData();
$conf = new Conf();
$conf->userDriver = $drv;
$conf->userAuthPreferHTTP = true;
$conf->userComposeNames = true;
Data::$conf = $conf;
if($db !== null) {
Data::$db = new $db();
}
Data::$user = new User();
Data::$user->authorizationEnabled(false);
foreach(self::USERS as $user => $level) {
Data::$user->add($user, "");
Data::$user->rightsSet($user, $level);
}
Data::$user->authorizationEnabled(true);
}
function tearDown() {
$this->clearData();
}
function tearDown() {
$this->clearData();
}
function testSelfActionLogic() {
foreach(array_keys(self::USERS) as $user) {
Data::$user->auth($user, "");
// users should be able to do basic actions for themselves
$this->assertTrue(Data::$user->authorize($user, "userExists"), "User $user could not act for themselves.");
$this->assertTrue(Data::$user->authorize($user, "userRemove"), "User $user could not act for themselves.");
}
}
function testSelfActionLogic() {
foreach(array_keys(self::USERS) as $user) {
Data::$user->auth($user, "");
// users should be able to do basic actions for themselves
$this->assertTrue(Data::$user->authorize($user, "userExists"), "User $user could not act for themselves.");
$this->assertTrue(Data::$user->authorize($user, "userRemove"), "User $user could not act for themselves.");
}
}
function testRegularUserLogic() {
foreach(self::USERS as $actor => $rights) {
if($rights != User\Driver::RIGHTS_NONE) continue;
Data::$user->auth($actor, "");
foreach(array_keys(self::USERS) as $affected) {
// regular users should only be able to act for themselves
if($actor==$affected) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should never be able to set rights
foreach(self::LEVELS as $level) {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
// they should not be able to list users
foreach(self::DOMAINS as $domain) {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
function testRegularUserLogic() {
foreach(self::USERS as $actor => $rights) {
if($rights != User\Driver::RIGHTS_NONE) continue;
Data::$user->auth($actor, "");
foreach(array_keys(self::USERS) as $affected) {
// regular users should only be able to act for themselves
if($actor==$affected) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should never be able to set rights
foreach(self::LEVELS as $level) {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
// they should not be able to list users
foreach(self::DOMAINS as $domain) {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
function testDomainManagerLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_DOMAIN_MANAGER) continue;
$actorDomain = substr($actor,strrpos($actor,"@")+1);
Data::$user->auth($actor, "");
foreach(self::USERS as $affected => $affectedRights) {
$affectedDomain = substr($affected,strrpos($affected,"@")+1);
// domain managers should be able to check any user on the same domain
if($actorDomain==$affectedDomain) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should only be able to act for regular users on the same domain
if($actor==$affected || ($actorDomain==$affectedDomain && $affectedRights==User\Driver::RIGHTS_NONE)) {
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// and they should only be able to set their own rights to regular user
foreach(self::LEVELS as $level) {
if($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, User\Driver::RIGHTS_DOMAIN_MANAGER])) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
}
// they should also be able to list all users on their own domain
foreach(self::DOMAINS as $domain) {
if($domain=="@".$actorDomain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
}
function testDomainManagerLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_DOMAIN_MANAGER) continue;
$actorDomain = substr($actor,strrpos($actor,"@")+1);
Data::$user->auth($actor, "");
foreach(self::USERS as $affected => $affectedRights) {
$affectedDomain = substr($affected,strrpos($affected,"@")+1);
// domain managers should be able to check any user on the same domain
if($actorDomain==$affectedDomain) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should only be able to act for regular users on the same domain
if($actor==$affected || ($actorDomain==$affectedDomain && $affectedRights==User\Driver::RIGHTS_NONE)) {
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// and they should only be able to set their own rights to regular user
foreach(self::LEVELS as $level) {
if($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, User\Driver::RIGHTS_DOMAIN_MANAGER])) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
}
// they should also be able to list all users on their own domain
foreach(self::DOMAINS as $domain) {
if($domain=="@".$actorDomain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
}
function testDomainAdministratorLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_DOMAIN_ADMIN) continue;
$actorDomain = substr($actor,strrpos($actor,"@")+1);
Data::$user->auth($actor, "");
$allowed = [User\Driver::RIGHTS_NONE,User\Driver::RIGHTS_DOMAIN_MANAGER,User\Driver::RIGHTS_DOMAIN_ADMIN];
foreach(self::USERS as $affected => $affectedRights) {
$affectedDomain = substr($affected,strrpos($affected,"@")+1);
// domain admins should be able to check any user on the same domain
if($actorDomain==$affectedDomain) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should be able to act for any user on the same domain who is not a global manager or admin
if($actorDomain==$affectedDomain && in_array($affectedRights, $allowed)) {
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should be able to set rights for any user on their domain who is not a global manager or admin, up to domain admin level
foreach(self::LEVELS as $level) {
if($actorDomain==$affectedDomain && in_array($affectedRights, $allowed) && in_array($level, $allowed)) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
}
// they should also be able to list all users on their own domain
foreach(self::DOMAINS as $domain) {
if($domain=="@".$actorDomain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
}
function testDomainAdministratorLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_DOMAIN_ADMIN) continue;
$actorDomain = substr($actor,strrpos($actor,"@")+1);
Data::$user->auth($actor, "");
$allowed = [User\Driver::RIGHTS_NONE,User\Driver::RIGHTS_DOMAIN_MANAGER,User\Driver::RIGHTS_DOMAIN_ADMIN];
foreach(self::USERS as $affected => $affectedRights) {
$affectedDomain = substr($affected,strrpos($affected,"@")+1);
// domain admins should be able to check any user on the same domain
if($actorDomain==$affectedDomain) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should be able to act for any user on the same domain who is not a global manager or admin
if($actorDomain==$affectedDomain && in_array($affectedRights, $allowed)) {
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should be able to set rights for any user on their domain who is not a global manager or admin, up to domain admin level
foreach(self::LEVELS as $level) {
if($actorDomain==$affectedDomain && in_array($affectedRights, $allowed) && in_array($level, $allowed)) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
}
// they should also be able to list all users on their own domain
foreach(self::DOMAINS as $domain) {
if($domain=="@".$actorDomain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
}
function testGlobalManagerLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_GLOBAL_MANAGER) continue;
$actorDomain = substr($actor,strrpos($actor,"@")+1);
Data::$user->auth($actor, "");
foreach(self::USERS as $affected => $affectedRights) {
$affectedDomain = substr($affected,strrpos($affected,"@")+1);
// global managers should be able to check any user
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
// they should only be able to act for regular users
if($actor==$affected || $affectedRights==User\Driver::RIGHTS_NONE) {
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// and they should only be able to set their own rights to regular user
foreach(self::LEVELS as $level) {
if($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, User\Driver::RIGHTS_GLOBAL_MANAGER])) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
}
// they should also be able to list all users
foreach(self::DOMAINS as $domain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
}
}
}
function testGlobalManagerLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_GLOBAL_MANAGER) continue;
$actorDomain = substr($actor,strrpos($actor,"@")+1);
Data::$user->auth($actor, "");
foreach(self::USERS as $affected => $affectedRights) {
$affectedDomain = substr($affected,strrpos($affected,"@")+1);
// global managers should be able to check any user
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
// they should only be able to act for regular users
if($actor==$affected || $affectedRights==User\Driver::RIGHTS_NONE) {
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// and they should only be able to set their own rights to regular user
foreach(self::LEVELS as $level) {
if($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, User\Driver::RIGHTS_GLOBAL_MANAGER])) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
}
// they should also be able to list all users
foreach(self::DOMAINS as $domain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
}
}
}
function testGlobalAdministratorLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_GLOBAL_ADMIN) continue;
Data::$user->auth($actor, "");
// global admins can do anything
foreach(self::USERS as $affected => $affectedRights) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
foreach(self::LEVELS as $level) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
}
}
foreach(self::DOMAINS as $domain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
}
}
}
function testGlobalAdministratorLogic() {
foreach(self::USERS as $actor => $actorRights) {
if($actorRights != User\Driver::RIGHTS_GLOBAL_ADMIN) continue;
Data::$user->auth($actor, "");
// global admins can do anything
foreach(self::USERS as $affected => $affectedRights) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
foreach(self::LEVELS as $level) {
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
}
}
foreach(self::DOMAINS as $domain) {
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
}
}
}
function testInvalidLevelLogic() {
foreach(self::USERS as $actor => $rights) {
if(in_array($rights, self::LEVELS)) continue;
Data::$user->auth($actor, "");
foreach(array_keys(self::USERS) as $affected) {
// users with unknown/invalid rights should be treated just like regular users and only be able to act for themselves
if($actor==$affected) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should never be able to set rights
foreach(self::LEVELS as $level) {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
// they should not be able to list users
foreach(self::DOMAINS as $domain) {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
function testInvalidLevelLogic() {
foreach(self::USERS as $actor => $rights) {
if(in_array($rights, self::LEVELS)) continue;
Data::$user->auth($actor, "");
foreach(array_keys(self::USERS) as $affected) {
// users with unknown/invalid rights should be treated just like regular users and only be able to act for themselves
if($actor==$affected) {
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
} else {
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
$this->assertFalse(Data::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
}
// they should never be able to set rights
foreach(self::LEVELS as $level) {
$this->assertFalse(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
}
}
// they should not be able to list users
foreach(self::DOMAINS as $domain) {
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
}
}
}
function testInternalExceptionLogic() {
$tests = [
// methods of User class to test, with parameters besides affected user
'exists' => [],
'remove' => [],
'add' => [''],
'passwordSet' => [''],
'propertiesGet' => [],
'propertiesSet' => [[]],
'rightsGet' => [],
'rightsSet' => [User\Driver::RIGHTS_GLOBAL_ADMIN],
'list' => [],
];
// try first with a global admin (there should be no exception)
Data::$user->auth("gadm@example.com", "");
$this->assertCount(0, $this->checkExceptions("user@example.org", $tests));
// next try with a regular user acting on another user (everything should fail)
Data::$user->auth("user@example.com", "");
$this->assertCount(sizeof($tests), $this->checkExceptions("user@example.org", $tests));
}
function testInternalExceptionLogic() {
$tests = [
// methods of User class to test, with parameters besides affected user
'exists' => [],
'remove' => [],
'add' => [''],
'passwordSet' => [''],
'propertiesGet' => [],
'propertiesSet' => [[]],
'rightsGet' => [],
'rightsSet' => [User\Driver::RIGHTS_GLOBAL_ADMIN],
'list' => [],
];
// try first with a global admin (there should be no exception)
Data::$user->auth("gadm@example.com", "");
$this->assertCount(0, $this->checkExceptions("user@example.org", $tests));
// next try with a regular user acting on another user (everything should fail)
Data::$user->auth("user@example.com", "");
$this->assertCount(sizeof($tests), $this->checkExceptions("user@example.org", $tests));
}
function testExternalExceptionLogic() {
// set up the test for an external driver
$this->setUp(Test\User\DriverExternalMock::class, Test\User\Database::class);
// run the previous test with the external driver set up
$this->testInternalExceptionLogic();
}
function testExternalExceptionLogic() {
// set up the test for an external driver
$this->setUp(Test\User\DriverExternalMock::class, Test\User\Database::class);
// run the previous test with the external driver set up
$this->testInternalExceptionLogic();
}
// meat of testInternalExceptionLogic and testExternalExceptionLogic
// calls each requested function with supplied arguments, catches authorization exceptions, and returns an array of caught failed calls
protected function checkExceptions(string $user, $tests): array {
$err = [];
foreach($tests as $func => $args) {
// list method does not take an affected user, so do not unshift for that one
if($func != "list") array_unshift($args, $user);
try {
call_user_func_array(array(Data::$user, $func), $args);
} catch(User\ExceptionAuthz $e) {
$err[] = $func;
}
}
return $err;
}
// meat of testInternalExceptionLogic and testExternalExceptionLogic
// calls each requested function with supplied arguments, catches authorization exceptions, and returns an array of caught failed calls
protected function checkExceptions(string $user, $tests): array {
$err = [];
foreach($tests as $func => $args) {
// list method does not take an affected user, so do not unshift for that one
if($func != "list") array_unshift($args, $user);
try {
call_user_func_array(array(Data::$user, $func), $args);
} catch(User\ExceptionAuthz $e) {
$err[] = $func;
}
}
return $err;
}
}

View file

@ -6,8 +6,8 @@ namespace JKingWeb\Arsse;
class TestUserInternalDriver extends \PHPUnit\Framework\TestCase {
use Test\Tools, Test\User\CommonTests;
const USER1 = "john.doe@example.com";
const USER2 = "jane.doe@example.com";
const USER1 = "john.doe@example.com";
const USER2 = "jane.doe@example.com";
public $drv = User\Internal\Driver::class;
public $drv = User\Internal\Driver::class;
}

View file

@ -6,8 +6,8 @@ namespace JKingWeb\Arsse;
class TestUserMockExternal extends \PHPUnit\Framework\TestCase {
use Test\Tools, Test\User\CommonTests;
const USER1 = "john.doe@example.com";
const USER2 = "jane.doe@example.com";
const USER1 = "john.doe@example.com";
const USER2 = "jane.doe@example.com";
public $drv = Test\User\DriverExternalMock::class;
public $drv = Test\User\DriverExternalMock::class;
}

View file

@ -6,12 +6,12 @@ namespace JKingWeb\Arsse;
class TestUserMockInternal extends \PHPUnit\Framework\TestCase {
use Test\Tools, Test\User\CommonTests;
const USER1 = "john.doe@example.com";
const USER2 = "jane.doe@example.com";
const USER1 = "john.doe@example.com";
const USER2 = "jane.doe@example.com";
public $drv = Test\User\DriverInternalMock::class;
public $drv = Test\User\DriverInternalMock::class;
function setUpSeries() {
Data::$db = null;
}
Data::$db = null;
}
}

View file

@ -9,8 +9,8 @@ use JKingWeb\Arsse\Database;
use Phake;
trait Setup {
protected $drv;
protected $data = [
protected $drv;
protected $data = [
'arsse_users' => [
'columns' => [
'id' => 'str',
@ -32,7 +32,7 @@ trait Setup {
// create a default configuration
Data::$conf = new Conf();
// configure and create the relevant database driver
$this->setUpDriver();
$this->setUpDriver();
// create the database interface with the suitable driver
Data::$db = new Database($this->drv);
Data::$db->schemaUpdate();
@ -43,77 +43,77 @@ trait Setup {
if(method_exists($this, "setUpSeries")) $this->setUpSeries();
}
function tearDown() {
function tearDown() {
// call the additional teardiwn method if it exists
if(method_exists($this, "tearDownSeries")) $this->tearDownSeries();
// clean up
$this->drv = null;
$this->drv = null;
$this->clearData();
}
}
function primeDatabase(array $data): bool {
$this->drv->begin();
foreach($data as $table => $info) {
$cols = implode(",", array_keys($info['columns']));
$bindings = array_values($info['columns']);
$params = implode(",", array_fill(0, sizeof($info['columns']), "?"));
$s = $this->drv->prepareArray("INSERT INTO $table($cols) values($params)", $bindings);
foreach($info['rows'] as $row) {
$this->assertEquals(1, $s->runArray($row)->changes());
}
}
$this->drv->commit();
return true;
}
function primeDatabase(array $data): bool {
$this->drv->begin();
foreach($data as $table => $info) {
$cols = implode(",", array_keys($info['columns']));
$bindings = array_values($info['columns']);
$params = implode(",", array_fill(0, sizeof($info['columns']), "?"));
$s = $this->drv->prepareArray("INSERT INTO $table($cols) values($params)", $bindings);
foreach($info['rows'] as $row) {
$this->assertEquals(1, $s->runArray($row)->changes());
}
}
$this->drv->commit();
return true;
}
function compareExpectations(array $expected): bool {
foreach($expected as $table => $info) {
$cols = implode(",", array_keys($info['columns']));
$data = $this->drv->prepare("SELECT $cols from $table")->run()->getAll();
$cols = array_keys($info['columns']);
foreach($info['rows'] as $index => $values) {
$row = [];
foreach($values as $key => $value) {
$row[$cols[$key]] = $value;
}
$found = array_search($row, $data);
$this->assertNotSame(false, $found, "Table $table does not contain record at array index $index.");
unset($data[$found]);
}
$this->assertSame([], $data);
}
return true;
}
function compareExpectations(array $expected): bool {
foreach($expected as $table => $info) {
$cols = implode(",", array_keys($info['columns']));
$data = $this->drv->prepare("SELECT $cols from $table")->run()->getAll();
$cols = array_keys($info['columns']);
foreach($info['rows'] as $index => $values) {
$row = [];
foreach($values as $key => $value) {
$row[$cols[$key]] = $value;
}
$found = array_search($row, $data);
$this->assertNotSame(false, $found, "Table $table does not contain record at array index $index.");
unset($data[$found]);
}
$this->assertSame([], $data);
}
return true;
}
function primeExpectations(array $source, array $tableSpecs = null): array {
$out = [];
foreach($tableSpecs as $table => $columns) {
if(!isset($source[$table])) {
$this->assertTrue(false, "Source for expectations does not contain requested table $table.");
return [];
}
$out[$table] = [
'columns' => [],
'rows' => [],
];
$transformations = [];
foreach($columns as $target => $col) {
if(!isset($source[$table]['columns'][$col])) {
$this->assertTrue(false, "Source for expectations does not contain requested column $col of table $table.");
return [];
}
$found = array_search($col, array_keys($source[$table]['columns']));
$transformations[$found] = $target;
$out[$table]['columns'][$col] = $source[$table]['columns'][$col];
}
foreach($source[$table]['rows'] as $sourceRow) {
$newRow = [];
foreach($transformations as $from => $to) {
$newRow[$to] = $sourceRow[$from];
}
$out[$table]['rows'][] = $newRow;
}
}
return $out;
}
function primeExpectations(array $source, array $tableSpecs = null): array {
$out = [];
foreach($tableSpecs as $table => $columns) {
if(!isset($source[$table])) {
$this->assertTrue(false, "Source for expectations does not contain requested table $table.");
return [];
}
$out[$table] = [
'columns' => [],
'rows' => [],
];
$transformations = [];
foreach($columns as $target => $col) {
if(!isset($source[$table]['columns'][$col])) {
$this->assertTrue(false, "Source for expectations does not contain requested column $col of table $table.");
return [];
}
$found = array_search($col, array_keys($source[$table]['columns']));
$transformations[$found] = $target;
$out[$table]['columns'][$col] = $source[$table]['columns'][$col];
}
foreach($source[$table]['rows'] as $sourceRow) {
$newRow = [];
foreach($transformations as $from => $to) {
$newRow[$to] = $sourceRow[$from];
}
$out[$table]['rows'][] = $newRow;
}
}
return $out;
}
}

View file

@ -4,219 +4,219 @@ namespace JKingWeb\Arsse\Test\Db;
use JKingWeb\Arsse\Db\Statement;
trait BindingTests {
function testBindNull() {
$input = null;
$exp = [
"null" => null,
"integer" => null,
"float" => null,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => null,
"string" => null,
"boolean" => null,
];
$this->checkBinding($input, $exp);
}
function testBindNull() {
$input = null;
$exp = [
"null" => null,
"integer" => null,
"float" => null,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => null,
"string" => null,
"boolean" => null,
];
$this->checkBinding($input, $exp);
}
function testBindTrue() {
$input = true;
$exp = [
"null" => null,
"integer" => 1,
"float" => 1.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => "1",
"string" => "1",
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindTrue() {
$input = true;
$exp = [
"null" => null,
"integer" => 1,
"float" => 1.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => "1",
"string" => "1",
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindFalse() {
$input = false;
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => "",
"string" => "",
"boolean" => 0,
];
$this->checkBinding($input, $exp);
}
function testBindFalse() {
$input = false;
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => "",
"string" => "",
"boolean" => 0,
];
$this->checkBinding($input, $exp);
}
function testBindInteger() {
$input = 2112;
$exp = [
"null" => null,
"integer" => 2112,
"float" => 2112.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
"binary" => "2112",
"string" => "2112",
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindInteger() {
$input = 2112;
$exp = [
"null" => null,
"integer" => 2112,
"float" => 2112.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
"binary" => "2112",
"string" => "2112",
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindIntegerZero() {
$input = 0;
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
"binary" => "0",
"string" => "0",
"boolean" => 0,
];
$this->checkBinding($input, $exp);
}
function testBindIntegerZero() {
$input = 0;
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
"binary" => "0",
"string" => "0",
"boolean" => 0,
];
$this->checkBinding($input, $exp);
}
function testBindFloat() {
$input = 2112.0;
$exp = [
"null" => null,
"integer" => 2112,
"float" => 2112.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
"binary" => "2112",
"string" => "2112",
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindFloat() {
$input = 2112.0;
$exp = [
"null" => null,
"integer" => 2112,
"float" => 2112.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
"binary" => "2112",
"string" => "2112",
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindFloatZero() {
$input = 0.0;
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
"binary" => "0",
"string" => "0",
"boolean" => 0,
];
$this->checkBinding($input, $exp);
}
function testBindFloatZero() {
$input = 0.0;
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
"binary" => "0",
"string" => "0",
"boolean" => 0,
];
$this->checkBinding($input, $exp);
}
function testBindAsciiString() {
$input = "Random string";
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindAsciiString() {
$input = "Random string";
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindUtf8String() {
$input = "é";
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindUtf8String() {
$input = "é";
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindBinaryString() {
// FIXME: This test may be unreliable; SQLite happily stores invalid UTF-8 text as bytes untouched, but other engines probably don't do this
$input = chr(233);
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindBinaryString() {
// FIXME: This test may be unreliable; SQLite happily stores invalid UTF-8 text as bytes untouched, but other engines probably don't do this
$input = chr(233);
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => null,
"time" => null,
"datetime" => null,
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindIso8601DateString() {
$input = "2017-01-09T13:11:17";
$time = strtotime($input);
$exp = [
"null" => null,
"integer" => 2017,
"float" => 2017.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindIso8601DateString() {
$input = "2017-01-09T13:11:17";
$time = strtotime($input);
$exp = [
"null" => null,
"integer" => 2017,
"float" => 2017.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindArbitraryDateString() {
$input = "Today";
$time = strtotime($input);
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindArbitraryDateString() {
$input = "Today";
$time = strtotime($input);
$exp = [
"null" => null,
"integer" => 0,
"float" => 0.0,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"binary" => $input,
"string" => $input,
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindMutableDateObject($class = '\DateTime') {
$input = new $class("Noon Today");
$time = $input->getTimestamp();
$exp = [
"null" => null,
"integer" => $time,
"float" => (float) $time,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"binary" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"string" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindMutableDateObject($class = '\DateTime') {
$input = new $class("Noon Today");
$time = $input->getTimestamp();
$exp = [
"null" => null,
"integer" => $time,
"float" => (float) $time,
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"binary" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"string" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
"boolean" => 1,
];
$this->checkBinding($input, $exp);
}
function testBindImmutableDateObject() {
$this->testBindMutableDateObject('\DateTimeImmutable');
}
function testBindImmutableDateObject() {
$this->testBindMutableDateObject('\DateTimeImmutable');
}
}

View file

@ -9,135 +9,135 @@ use JKingWeb\Arsse\User\Driver;
trait CommonTests {
function setUp() {
$this->clearData();
$conf = new Conf();
$conf->userDriver = $this->drv;
$conf->userAuthPreferHTTP = true;
Data::$conf = $conf;
Data::$db = new Database();
Data::$user = new User();
Data::$user->authorizationEnabled(false);
$_SERVER['PHP_AUTH_USER'] = self::USER1;
$_SERVER['PHP_AUTH_PW'] = "secret";
$this->clearData();
$conf = new Conf();
$conf->userDriver = $this->drv;
$conf->userAuthPreferHTTP = true;
Data::$conf = $conf;
Data::$db = new Database();
Data::$user = new User();
Data::$user->authorizationEnabled(false);
$_SERVER['PHP_AUTH_USER'] = self::USER1;
$_SERVER['PHP_AUTH_PW'] = "secret";
// call the additional setup method if it exists
if(method_exists($this, "setUpSeries")) $this->setUpSeries();
}
}
function tearDown() {
$this->clearData();
function tearDown() {
$this->clearData();
// call the additional teardiwn method if it exists
if(method_exists($this, "tearDownSeries")) $this->tearDownSeries();
}
}
function testListUsers() {
$this->assertCount(0,Data::$user->list());
}
function testListUsers() {
$this->assertCount(0,Data::$user->list());
}
function testCheckIfAUserDoesNotExist() {
$this->assertFalse(Data::$user->exists(self::USER1));
}
function testCheckIfAUserDoesNotExist() {
$this->assertFalse(Data::$user->exists(self::USER1));
}
function testAddAUser() {
Data::$user->add(self::USER1, "");
$this->assertCount(1,Data::$user->list());
}
function testAddAUser() {
Data::$user->add(self::USER1, "");
$this->assertCount(1,Data::$user->list());
}
function testCheckIfAUserDoesExist() {
Data::$user->add(self::USER1, "");
$this->assertTrue(Data::$user->exists(self::USER1));
}
function testCheckIfAUserDoesExist() {
Data::$user->add(self::USER1, "");
$this->assertTrue(Data::$user->exists(self::USER1));
}
function testAddADuplicateUser() {
Data::$user->add(self::USER1, "");
$this->assertException("alreadyExists", "User");
Data::$user->add(self::USER1, "");
}
function testAddADuplicateUser() {
Data::$user->add(self::USER1, "");
$this->assertException("alreadyExists", "User");
Data::$user->add(self::USER1, "");
}
function testAddMultipleUsers() {
Data::$user->add(self::USER1, "");
Data::$user->add(self::USER2, "");
$this->assertCount(2,Data::$user->list());
}
function testAddMultipleUsers() {
Data::$user->add(self::USER1, "");
Data::$user->add(self::USER2, "");
$this->assertCount(2,Data::$user->list());
}
function testRemoveAUser() {
Data::$user->add(self::USER1, "");
$this->assertCount(1,Data::$user->list());
Data::$user->remove(self::USER1);
$this->assertCount(0,Data::$user->list());
}
function testRemoveAUser() {
Data::$user->add(self::USER1, "");
$this->assertCount(1,Data::$user->list());
Data::$user->remove(self::USER1);
$this->assertCount(0,Data::$user->list());
}
function testRemoveAMissingUser() {
$this->assertException("doesNotExist", "User");
Data::$user->remove(self::USER1);
}
function testRemoveAMissingUser() {
$this->assertException("doesNotExist", "User");
Data::$user->remove(self::USER1);
}
function testAuthenticateAUser() {
$_SERVER['PHP_AUTH_USER'] = self::USER1;
$_SERVER['PHP_AUTH_PW'] = "secret";
Data::$user->add(self::USER1, "secret");
Data::$user->add(self::USER2, "");
$this->assertTrue(Data::$user->auth());
$this->assertTrue(Data::$user->auth(self::USER1, "secret"));
$this->assertFalse(Data::$user->auth(self::USER1, "superman"));
$this->assertTrue(Data::$user->auth(self::USER2, ""));
}
function testAuthenticateAUser() {
$_SERVER['PHP_AUTH_USER'] = self::USER1;
$_SERVER['PHP_AUTH_PW'] = "secret";
Data::$user->add(self::USER1, "secret");
Data::$user->add(self::USER2, "");
$this->assertTrue(Data::$user->auth());
$this->assertTrue(Data::$user->auth(self::USER1, "secret"));
$this->assertFalse(Data::$user->auth(self::USER1, "superman"));
$this->assertTrue(Data::$user->auth(self::USER2, ""));
}
function testChangeAPassword() {
Data::$user->add(self::USER1, "secret");
$this->assertEquals("superman", Data::$user->passwordSet(self::USER1, "superman"));
$this->assertTrue(Data::$user->auth(self::USER1, "superman"));
$this->assertFalse(Data::$user->auth(self::USER1, "secret"));
$this->assertEquals("", Data::$user->passwordSet(self::USER1, ""));
$this->assertTrue(Data::$user->auth(self::USER1, ""));
$this->assertEquals(Data::$conf->userTempPasswordLength, strlen(Data::$user->passwordSet(self::USER1)));
}
function testChangeAPassword() {
Data::$user->add(self::USER1, "secret");
$this->assertEquals("superman", Data::$user->passwordSet(self::USER1, "superman"));
$this->assertTrue(Data::$user->auth(self::USER1, "superman"));
$this->assertFalse(Data::$user->auth(self::USER1, "secret"));
$this->assertEquals("", Data::$user->passwordSet(self::USER1, ""));
$this->assertTrue(Data::$user->auth(self::USER1, ""));
$this->assertEquals(Data::$conf->userTempPasswordLength, strlen(Data::$user->passwordSet(self::USER1)));
}
function testChangeAPasswordForAMissingUser() {
$this->assertException("doesNotExist", "User");
Data::$user->passwordSet(self::USER1, "superman");
}
function testChangeAPasswordForAMissingUser() {
$this->assertException("doesNotExist", "User");
Data::$user->passwordSet(self::USER1, "superman");
}
function testGetThePropertiesOfAUser() {
Data::$user->add(self::USER1, "secret");
$p = Data::$user->propertiesGet(self::USER1);
$this->assertArrayHasKey('id', $p);
$this->assertArrayHasKey('name', $p);
$this->assertArrayHasKey('domain', $p);
$this->assertArrayHasKey('rights', $p);
$this->assertArrayNotHasKey('password', $p);
$this->assertEquals(self::USER1, $p['name']);
}
function testGetThePropertiesOfAUser() {
Data::$user->add(self::USER1, "secret");
$p = Data::$user->propertiesGet(self::USER1);
$this->assertArrayHasKey('id', $p);
$this->assertArrayHasKey('name', $p);
$this->assertArrayHasKey('domain', $p);
$this->assertArrayHasKey('rights', $p);
$this->assertArrayNotHasKey('password', $p);
$this->assertEquals(self::USER1, $p['name']);
}
function testSetThePropertiesOfAUser() {
$pSet = [
'name' => 'John Doe',
'id' => 'invalid',
'domain' => 'localhost',
'rights' => Driver::RIGHTS_GLOBAL_ADMIN,
'password' => 'superman',
];
$pGet = [
'name' => 'John Doe',
'id' => self::USER1,
'domain' => 'example.com',
'rights' => Driver::RIGHTS_NONE,
];
Data::$user->add(self::USER1, "secret");
Data::$user->propertiesSet(self::USER1, $pSet);
$p = Data::$user->propertiesGet(self::USER1);
$this->assertArraySubset($pGet, $p);
$this->assertArrayNotHasKey('password', $p);
$this->assertFalse(Data::$user->auth(self::USER1, "superman"));
}
function testSetThePropertiesOfAUser() {
$pSet = [
'name' => 'John Doe',
'id' => 'invalid',
'domain' => 'localhost',
'rights' => Driver::RIGHTS_GLOBAL_ADMIN,
'password' => 'superman',
];
$pGet = [
'name' => 'John Doe',
'id' => self::USER1,
'domain' => 'example.com',
'rights' => Driver::RIGHTS_NONE,
];
Data::$user->add(self::USER1, "secret");
Data::$user->propertiesSet(self::USER1, $pSet);
$p = Data::$user->propertiesGet(self::USER1);
$this->assertArraySubset($pGet, $p);
$this->assertArrayNotHasKey('password', $p);
$this->assertFalse(Data::$user->auth(self::USER1, "superman"));
}
function testGetTheRightsOfAUser() {
Data::$user->add(self::USER1, "");
$this->assertEquals(Driver::RIGHTS_NONE, Data::$user->rightsGet(self::USER1));
}
function testGetTheRightsOfAUser() {
Data::$user->add(self::USER1, "");
$this->assertEquals(Driver::RIGHTS_NONE, Data::$user->rightsGet(self::USER1));
}
function testSetTheRightsOfAUser() {
Data::$user->add(self::USER1, "");
Data::$user->rightsSet(self::USER1, Driver::RIGHTS_GLOBAL_ADMIN);
$this->assertEquals(Driver::RIGHTS_GLOBAL_ADMIN, Data::$user->rightsGet(self::USER1));
}
function testSetTheRightsOfAUser() {
Data::$user->add(self::USER1, "");
Data::$user->rightsSet(self::USER1, Driver::RIGHTS_GLOBAL_ADMIN);
$this->assertEquals(Driver::RIGHTS_GLOBAL_ADMIN, Data::$user->rightsGet(self::USER1));
}
}

View file

@ -9,9 +9,9 @@ use PasswordGenerator\Generator as PassGen;
abstract class DriverSkeleton {
protected $db = [];
protected $db = [];
function userExists(string $user): bool {
function userExists(string $user): bool {
return array_key_exists($user, $this->db);
}