From f902346b6c637b3c61923972e1064096de246eb2 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Tue, 28 Mar 2017 18:50:00 -0400 Subject: [PATCH] Eliminated passing of RuntimeData instances - RuntimeData has now been replaced by a single static Data class - The Data class has a load() method which fills the same role as the constructor of RuntimeData - The static Lang class is now an instantiable class and is a member of Data - All tests have been adjusted and pass - The Exception tests no longer require convoluted workarounds: a simple mock for Data::$l suffices; Lang tests also use a mock to prevent loops now instead of using a workaround --- autoload.php | 2 +- lib/AbstractException.php | 3 +- lib/Data.php | 18 +++ lib/Database.php | 43 ++++--- lib/Db/Driver.php | 2 +- lib/Db/SQLite3/Driver.php | 16 ++- lib/Lang.php | 116 +++++++++-------- lib/Lang/Exception.php | 18 --- lib/REST.php | 5 +- lib/REST/AbstractHandler.php | 2 +- lib/REST/Handler.php | 2 +- lib/REST/NextCloudNews/Versions.php | 3 +- lib/RuntimeData.php | 16 --- lib/User.php | 56 ++++----- lib/User/Driver.php | 4 +- lib/User/Internal/Driver.php | 8 +- lib/User/Internal/InternalFunctions.php | 8 +- tests/Conf/TestConf.php | 8 +- tests/Db/SQLite3/TestDbDriverSQLite3.php | 34 ++--- tests/Db/SQLite3/TestDbUpdateSQLite3.php | 10 +- tests/Exception/TestException.php | 20 ++- tests/Lang/TestLang.php | 39 +++--- tests/Lang/TestLangErrors.php | 36 +++--- tests/Lang/testLangComplex.php | 67 +++++----- .../NextCloudNews/TestNCNVersionDiscovery.php | 9 +- tests/User/TestAuthorization.php | 119 +++++++++--------- tests/User/TestUserInternalDriver.php | 15 +-- tests/User/TestUserMockExternal.php | 15 +-- tests/User/TestUserMockInternal.php | 14 +-- tests/lib/Db/Tools.php | 15 ++- tests/lib/Lang/Setup.php | 40 +++--- tests/lib/RuntimeData.php | 13 -- tests/lib/Tools.php | 15 ++- tests/lib/User/CommonTests.php | 107 ++++++++++------ tests/lib/User/Database.php | 30 ++--- tests/lib/User/DriverExternalMock.php | 13 +- tests/lib/User/DriverInternalMock.php | 8 +- tests/lib/User/DriverSkeleton.php | 1 - tests/phpunit.xml | 7 +- tests/test.php | 19 +-- 40 files changed, 472 insertions(+), 504 deletions(-) create mode 100644 lib/Data.php delete mode 100644 lib/RuntimeData.php delete mode 100644 tests/lib/RuntimeData.php diff --git a/autoload.php b/autoload.php index 6bb5f111..260e06ac 100644 --- a/autoload.php +++ b/autoload.php @@ -1,3 +1,3 @@ -1, - "Exception.invalid" => 1, // this exception MUST NOT have a message string defined "Exception.unknown" => 10000, "Lang/Exception.defaultFileMissing" => 10101, "Lang/Exception.fileMissing" => 10102, @@ -79,7 +78,7 @@ abstract class AbstractException extends \Exception { $code = self::CODES[$codeID]; $msg = "Exception.".str_replace("\\", "/", $class).".$msgID"; } - $msg = Lang::msg($msg, $vars); + $msg = Data::$l->msg($msg, $vars); } parent::__construct($msg, $code, $e); } diff --git a/lib/Data.php b/lib/Data.php new file mode 100644 index 00000000..30b981b9 --- /dev/null +++ b/lib/Data.php @@ -0,0 +1,18 @@ +set($conf->lang); + static::$db = new Database(); + static::$user = new User(); + } +} \ No newline at end of file diff --git a/lib/Database.php b/lib/Database.php index 95ad4164..d683fdad 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -18,10 +18,9 @@ class Database { return (string) preg_filter("[^0-9a-zA-Z_\.]", "", $name); } - public function __construct(RuntimeData $data) { - $this->data = $data; - $this->driver = $driver = $data->conf->dbDriver; - $this->db = new $driver($data, INSTALL); + public function __construct() { + $this->driver = $driver = Data::$conf->dbDriver; + $this->db = new $driver(INSTALL); $ver = $this->db->schemaVersion(); if(!INSTALL && $ver < self::SCHEMA_VERSION) { $this->db->schemaUpdate(self::SCHEMA_VERSION); @@ -166,14 +165,14 @@ class Database { } public function userExists(string $user): bool { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return (bool) $this->db->prepare("SELECT count(*) from arsse_users where id is ?", "str")->run($user)->getValue(); } public function userAdd(string $user, string $password = null): string { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if($this->userExists($user)) throw new User\Exception("alreadyExists", ["action" => __FUNCTION__, "user" => $user]); - if($password===null) $password = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get(); + if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get(); $hash = ""; if(strlen($password) > 0) $hash = password_hash($password, \PASSWORD_DEFAULT); $this->db->prepare("INSERT INTO arsse_users(id,password) values(?,?)", "str", "str")->runArray([$user,$hash]); @@ -181,33 +180,33 @@ class Database { } public function userRemove(string $user): bool { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if($this->db->prepare("DELETE from arsse_users where id is ?", "str")->run($user)->changes() < 1) throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); return true; } public function userList(string $domain = null): array { if($domain !== null) { - if(!$this->data->user->authorize("@".$domain, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]); + if(!Data::$user->authorize("@".$domain, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]); $domain = str_replace(["\\","%","_"],["\\\\", "\\%", "\\_"], $domain); $domain = "%@".$domain; return $this->db->prepare("SELECT id from arsse_users where id like ?", "str")->run($domain)->getAll(); } else { - if(!$this->data->user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]); + if(!Data::$user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]); return $this->db->prepare("SELECT id from arsse_users")->run()->getAll(); } } public function userPasswordGet(string $user): string { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); return (string) $this->db->prepare("SELECT password from arsse_users where id is ?", "str")->run($user)->getValue(); } public function userPasswordSet(string $user, string $password = null): string { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); - if($password===null) $password = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get(); + if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get(); $hash = ""; if(strlen($password > 0)) $hash = password_hash($password, \PASSWORD_DEFAULT); $this->db->prepare("UPDATE arsse_users set password = ? where id is ?", "str", "str")->run($hash, $user); @@ -215,14 +214,14 @@ class Database { } public function userPropertiesGet(string $user): array { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); $prop = $this->db->prepare("SELECT name,rights from arsse_users where id is ?", "str")->run($user)->getRow(); if(!$prop) return []; return $prop; } public function userPropertiesSet(string $user, array &$properties): array { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); $valid = [ // FIXME: add future properties "name" => "str", ]; @@ -237,12 +236,12 @@ class Database { } public function userRightsGet(string $user): int { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return (int) $this->db->prepare("SELECT rights from arsse_users where id is ?", "str")->run($user)->getValue(); } public function userRightsSet(string $user, int $rights): bool { - if(!$this->data->user->authorize($user, __FUNCTION__, $rights)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__, $rights)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) return false; $this->db->prepare("UPDATE arsse_users set rights = ? where id is ?", "int", "str")->run($rights, $user); return true; @@ -250,7 +249,7 @@ class Database { public function subscriptionAdd(string $user, string $url, string $fetchUser = "", string $fetchPassword = ""): int { // If the user isn't authorized to perform this action then throw an exception. - if (!$this->data->user->authorize($user, __FUNCTION__)) { + if (!Data::$user->authorize($user, __FUNCTION__)) { throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); } // If the user doesn't exist throw an exception. @@ -299,13 +298,13 @@ class Database { } public function subscriptionRemove(string $user, int $id): bool { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return (bool) $this->db->prepare("DELETE from arsse_subscriptions where id is ?", "int")->run($id)->changes(); } public function folderAdd(string $user, array $data): int { // If the user isn't authorized to perform this action then throw an exception. - if (!$this->data->user->authorize($user, __FUNCTION__)) { + if (!Data::$user->authorize($user, __FUNCTION__)) { throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); } // If the user doesn't exist throw an exception. @@ -345,7 +344,7 @@ class Database { public function folderList(string $user, int $parent = null, bool $recursive = true): Db\Result { // if the user isn't authorized to perform this action then throw an exception. - if (!$this->data->user->authorize($user, __FUNCTION__)) { + if (!Data::$user->authorize($user, __FUNCTION__)) { throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); } // if the user doesn't exist throw an exception. @@ -487,7 +486,7 @@ class Database { } public function folderRemove(string $user, int $id): bool { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); // common table expression to list all descendant folders of the target folder $cte = "RECURSIVE folders(id) as (SELECT id from arsse_folders where owner is ? and id is ? union select arsse_folders.id from arsse_folders join folders on arsse_folders.parent=folders.id) "; $changes = 0; diff --git a/lib/Db/Driver.php b/lib/Db/Driver.php index 115c5294..0f0f02b1 100644 --- a/lib/Db/Driver.php +++ b/lib/Db/Driver.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace JKingWeb\Arsse\Db; interface Driver { - function __construct(\JKingWeb\Arsse\RuntimeData $data, bool $install = false); + function __construct(bool $install = false); // returns a human-friendly name for the driver (for display in installer, for example) static function driverName(): string; // returns the version of the scheme of the opened database; if uninitialized should return 0 diff --git a/lib/Db/SQLite3/Driver.php b/lib/Db/SQLite3/Driver.php index bdbcf05f..37c4c57c 100644 --- a/lib/Db/SQLite3/Driver.php +++ b/lib/Db/SQLite3/Driver.php @@ -1,7 +1,7 @@ data = $data; - $file = $data->conf->dbSQLite3File; + $file = Data::$conf->dbSQLite3File; // if the file exists (or we're initializing the database), try to open it try { - $this->db = new \SQLite3($file, ($install) ? \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE : \SQLITE3_OPEN_READWRITE, $data->conf->dbSQLite3Key); + $this->db = new \SQLite3($file, ($install) ? \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE : \SQLITE3_OPEN_READWRITE, Data::$conf->dbSQLite3Key); } catch(\Throwable $e) { // if opening the database doesn't work, check various pre-conditions to find out what the problem might be if(!file_exists($file)) { @@ -57,7 +55,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { static public function driverName(): string { - return Lang::msg("Driver.Db.SQLite3.Name"); + return Data::$l->msg("Driver.Db.SQLite3.Name"); } public function schemaVersion(): int { @@ -66,10 +64,10 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { public function schemaUpdate(int $to): bool { $ver = $this->schemaVersion(); - if(!$this->data->conf->dbSQLite3AutoUpd) throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]); + if(!Data::$conf->dbSQLite3AutoUpd) throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]); if($ver >= $to) throw new Exception("updateTooNew", ['difference' => ($ver - $to), 'current' => $ver, 'target' => $to, 'driver_name' => $this->driverName()]); $sep = \DIRECTORY_SEPARATOR; - $path = $this->data->conf->dbSchemaBase.$sep."SQLite3".$sep; + $path = Data::$conf->dbSchemaBase.$sep."SQLite3".$sep; $this->lock(); $this->begin(); for($a = $ver; $a < $to; $a++) { diff --git a/lib/Lang.php b/lib/Lang.php index 1a2f9c10..1ca84e16 100644 --- a/lib/Lang.php +++ b/lib/Lang.php @@ -1,7 +1,7 @@ 'Message string "{msgID}" is not a valid ICU message string (language files loaded: {fileList})', ]; - static public $path = BASE."locale".DIRECTORY_SEPARATOR; // path to locale files; this is a public property to facilitate unit testing - static protected $requirementsMet = false; // whether the Intl extension is loaded - static protected $synched = false; // whether the wanted locale is actually loaded (lazy loading is used by default) - static protected $wanted = self::DEFAULT; // the currently requested locale - static protected $locale = ""; // the currently loaded locale - static protected $loaded = []; // the cascade of loaded locale file names - static protected $strings = self::REQUIRED; // the loaded locale strings, merged + public $path; // path to locale files; this is a public property to facilitate unit testing + static protected $requirementsMet = false; // whether the Intl extension is loaded + protected $synched = false; // whether the wanted locale is actually loaded (lazy loading is used by default) + protected $wanted = self::DEFAULT; // the currently requested locale + protected $locale = ""; // the currently loaded locale + protected $loaded = []; // the cascade of loaded locale file names + protected $strings = self::REQUIRED; // the loaded locale strings, merged - protected function __construct() {} + function __construct(string $path = BASE."locale".DIRECTORY_SEPARATOR) { + $this->path = $path; + } - static public function set(string $locale, bool $immediate = false): string { + public function set(string $locale, bool $immediate = false): string { // make sure the Intl extension is loaded - if(!self::$requirementsMet) self::checkRequirements(); + if(!static::$requirementsMet) static::checkRequirements(); // if requesting the same locale as already wanted, just return (but load first if we've requested an immediate load) - if($locale==self::$wanted) { - if($immediate && !self::$synched) self::load(); + if($locale==$this->wanted) { + if($immediate && !$this->synched) $this->load(); return $locale; } // if we've requested a locale other than the null locale, fetch the list of available files and find the closest match e.g. en_ca_somedialect -> en_ca if($locale != "") { - $list = self::listFiles(); + $list = $this->listFiles(); // if the default locale is unavailable, this is (for now) an error if(!in_array(self::DEFAULT, $list)) throw new Lang\Exception("defaultFileMissing", self::DEFAULT); - self::$wanted = self::match($locale, $list); + $this->wanted = $this->match($locale, $list); } else { - self::$wanted = ""; + $this->wanted = ""; } - self::$synched = false; + $this->synched = false; // load right now if asked to, otherwise load later when actually required - if($immediate) self::load(); - return self::$wanted; + if($immediate) $this->load(); + return $this->wanted; } - static public function get(bool $loaded = false): string { + public function get(bool $loaded = false): string { // we can either return the wanted locale (default) or the currently loaded locale - return $loaded ? self::$locale : self::$wanted; + return $loaded ? $this->locale : $this->wanted; } - static public function dump(): array { - return self::$strings; + public function dump(): array { + return $this->strings; } - static public function msg(string $msgID, $vars = null): string { + public function msg(string $msgID, $vars = null): string { + return $this($msgID, $vars); + } + + public function __invoke(string $msgID, $vars = null): string { // if we're trying to load the system default language and it fails, we have a chicken and egg problem, so we catch the exception and load no language file instead - if(!self::$synched) try {self::load();} catch(Lang\Exception $e) { - if(self::$wanted==self::DEFAULT) { - self::set("", true); + if(!$this->synched) try {$this->load();} catch(Lang\Exception $e) { + if($this->wanted==self::DEFAULT) { + $this->set("", true); } else { throw $e; } } // if the requested message is not present in any of the currently loaded language files, throw an exception // note that this is indicative of a programming error since the default locale should have all strings - if(!array_key_exists($msgID, self::$strings)) throw new Lang\Exception("stringMissing", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]); - $msg = self::$strings[$msgID]; + if(!array_key_exists($msgID, $this->strings)) throw new Lang\Exception("stringMissing", ['msgID' => $msgID, 'fileList' => implode(", ",$this->loaded)]); + $msg = $this->strings[$msgID]; // variables fed to MessageFormatter must be contained in an array if($vars===null) { // even though strings not given parameters will not get formatted, we do not optimize this case away: we still want to catch invalid strings @@ -78,36 +84,36 @@ class Lang { } else if(!is_array($vars)) { $vars = [$vars]; } - $msg = \MessageFormatter::formatMessage(self::$locale, $msg, $vars); - if($msg===false) throw new Lang\Exception("stringInvalid", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]); + $msg = \MessageFormatter::formatMessage($this->locale, $msg, $vars); + if($msg===false) throw new Lang\Exception("stringInvalid", ['msgID' => $msgID, 'fileList' => implode(", ",$this->loaded)]); return $msg; } - static public function list(string $locale = ""): array { + public function list(string $locale = ""): array { $out = []; - $files = self::listFiles(); + $files = $this->listFiles(); foreach($files as $tag) { $out[$tag] = \Locale::getDisplayName($tag, ($locale=="") ? $tag : $locale); } return $out; } - static public function match(string $locale, array $list = null): string { - if($list===null) $list = self::listFiles(); - $default = (self::$locale=="") ? self::DEFAULT : self::$locale; + public function match(string $locale, array $list = null): string { + if($list===null) $list = $this->listFiles(); + $default = ($this->locale=="") ? self::DEFAULT : $this->locale; return \Locale::lookup($list,$locale, true, $default); } static protected function checkRequirements(): bool { if(!extension_loaded("intl")) throw new ExceptionFatal("The \"Intl\" extension is required, but not loaded"); - self::$requirementsMet = true; + static::$requirementsMet = true; return true; } - static protected function listFiles(): array { - $out = glob(self::$path."*.php"); + protected function listFiles(): array { + $out = glob($this->path."*.php"); // built-in glob doesn't work with vfsStream (and this other glob doesn't seem to work with Windows paths), so we try both - if(empty($out)) $out = Glob::glob(self::$path."*.php"); // FIXME: we should just mock glob() in tests instead and make this a dev dependency + if(empty($out)) $out = Glob::glob($this->path."*.php"); // FIXME: we should just mock glob() in tests instead and make this a dev dependency // trim the returned file paths to return just the language tag $out = array_map(function($file) { $file = str_replace(DIRECTORY_SEPARATOR, "/", $file); @@ -119,17 +125,17 @@ class Lang { return $out; } - static protected function load(): bool { + protected function load(): bool { if(!self::$requirementsMet) self::checkRequirements(); // if we've requested no locale (""), just load the fallback strings and return - if(self::$wanted=="") { - self::$strings = self::REQUIRED; - self::$locale = self::$wanted; - self::$synched = true; + if($this->wanted=="") { + $this->strings = self::REQUIRED; + $this->locale = $this->wanted; + $this->synched = true; return true; } // decompose the requested locale from specific to general, building a list of files to load - $tags = \Locale::parseLocale(self::$wanted); + $tags = \Locale::parseLocale($this->wanted); $files = []; while(sizeof($tags) > 0) { $files[] = strtolower(\Locale::composeLocale($tags)); @@ -142,7 +148,7 @@ class Lang { // reduce the list of files to be loaded to the minimum necessary (e.g. if we go from "fr" to "fr_ca", we don't need to load "fr" or "en") $files = []; foreach($loaded as $file) { - if($file==self::$locale) break; + if($file==$this->locale) break; $files[] = $file; } // if we need to load all files, start with the fallback strings @@ -151,17 +157,17 @@ class Lang { $strings[] = self::REQUIRED; } else { // otherwise start with the strings we already have if we're going from e.g. "fr" to "fr_ca" - $strings[] = self::$strings; + $strings[] = $this->strings; } // read files in reverse order $files = array_reverse($files); foreach($files as $file) { - if(!file_exists(self::$path."$file.php")) throw new Lang\Exception("fileMissing", $file); - if(!is_readable(self::$path."$file.php")) throw new Lang\Exception("fileUnreadable", $file); + if(!file_exists($this->path."$file.php")) throw new Lang\Exception("fileMissing", $file); + if(!is_readable($this->path."$file.php")) throw new Lang\Exception("fileUnreadable", $file); try { // we use output buffering in case the language file is corrupted ob_start(); - $arr = (include self::$path."$file.php"); + $arr = (include $this->path."$file.php"); } catch(\Throwable $e) { $arr = null; } finally { @@ -171,10 +177,10 @@ class Lang { $strings[] = $arr; } // apply the results and return - self::$strings = call_user_func_array("array_replace_recursive", $strings); - self::$loaded = $loaded; - self::$locale = self::$wanted; - self::$synched = true; + $this->strings = call_user_func_array("array_replace_recursive", $strings); + $this->loaded = $loaded; + $this->locale = $this->wanted; + $this->synched = true; return true; } } \ No newline at end of file diff --git a/lib/Lang/Exception.php b/lib/Lang/Exception.php index 772b6959..53404370 100644 --- a/lib/Lang/Exception.php +++ b/lib/Lang/Exception.php @@ -3,22 +3,4 @@ declare(strict_types=1); namespace JKingWeb\Arsse\Lang; class Exception extends \JKingWeb\Arsse\AbstractException { - static $test = false; // used during PHPUnit testing only - - function __construct(string $msgID = "", $vars = null, \Throwable $e = null) { - if(!self::$test) { - parent::__construct($msgID, $vars, $e); - } else { - $codeID = "Lang/Exception.$msgID"; - if(!array_key_exists($codeID,self::CODES)) { - $code = -1; - $msg = "Exception.".str_replace("\\","/",parent::class).".uncoded"; - $vars = $msgID; - } else { - $code = self::CODES[$codeID]; - $msg = "Exception.".str_replace("\\","/",__CLASS__).".$msgID"; - } - \Exception::__construct($msg, $code, $e); - } - } } \ No newline at end of file diff --git a/lib/REST.php b/lib/REST.php index 964f2867..41693cc9 100644 --- a/lib/REST.php +++ b/lib/REST.php @@ -26,8 +26,7 @@ class REST { ]; protected $data; - function __construct(RuntimeData $data) { - $this->data = $data; + function __construct() { } function dispatch(REST\Request $req = null): bool { @@ -35,7 +34,7 @@ class REST { $api = $this->apiMatch($url, $this->apis); $req->url = substr($url,strlen($this->apis[$api]['strip'])); $class = $this->apis[$api]['class']; - $drv = new $class($this->data); + $drv = new $class(); $drv->dispatch($req); return true; } diff --git a/lib/REST/AbstractHandler.php b/lib/REST/AbstractHandler.php index ac609d88..d4fa891a 100644 --- a/lib/REST/AbstractHandler.php +++ b/lib/REST/AbstractHandler.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace JKingWeb\Arsse\REST; abstract class AbstractHandler implements Handler { - abstract function __construct(\JKingWeb\Arsse\RuntimeData $data); + abstract function __construct(); abstract function dispatch(Request $req): Response; protected function parseURL(string $url): array { diff --git a/lib/REST/Handler.php b/lib/REST/Handler.php index bc86dd64..a07bcd15 100644 --- a/lib/REST/Handler.php +++ b/lib/REST/Handler.php @@ -3,6 +3,6 @@ declare(strict_types=1); namespace JKingWeb\Arsse\REST; interface Handler { - function __construct(\JKingWeb\Arsse\RuntimeData $data); + function __construct(); function dispatch(Request $req): Response; } \ No newline at end of file diff --git a/lib/REST/NextCloudNews/Versions.php b/lib/REST/NextCloudNews/Versions.php index caf0f464..aa923e22 100644 --- a/lib/REST/NextCloudNews/Versions.php +++ b/lib/REST/NextCloudNews/Versions.php @@ -4,8 +4,7 @@ namespace JKingWeb\Arsse\REST\NextCloudNews; use JKingWeb\Arsse\REST\Response; class Versions extends \JKingWeb\Arsse\REST\AbstractHandler { - function __construct(\JKingWeb\Arsse\RuntimeData $data) { - // runtime data is not needed; this method is deliberately empty + function __construct() { } function dispatch(\JKingWeb\Arsse\REST\Request $req): \JKingWeb\Arsse\REST\Response { diff --git a/lib/RuntimeData.php b/lib/RuntimeData.php deleted file mode 100644 index dc594eab..00000000 --- a/lib/RuntimeData.php +++ /dev/null @@ -1,16 +0,0 @@ -conf = $conf; - Lang::set($conf->lang); - $this->db = new Database($this); - $this->user = new User($this); - } -} \ No newline at end of file diff --git a/lib/User.php b/lib/User.php index da0f6914..9b9a45a5 100644 --- a/lib/User.php +++ b/lib/User.php @@ -5,7 +5,6 @@ namespace JKingWeb\Arsse; class User { public $id = null; - protected $data; protected $u; protected $authz = true; protected $authzSupported = 0; @@ -23,10 +22,9 @@ class User { return $classes; } - public function __construct(\JKingWeb\Arsse\RuntimeData $data) { - $this->data = $data; - $driver = $data->conf->userDriver; - $this->u = new $driver($data); + public function __construct() { + $driver = Data::$conf->userDriver; + $this->u = new $driver(); $this->authzSupported = $this->u->driverFunctions("authorize"); } @@ -42,9 +40,9 @@ class User { // if we don't have a logged-in user, fetch credentials if($this->id===null) $this->credentials(); // if the affected user is the actor and the actor is not trying to grant themselves rights, accept the request - if($affectedUser==$this->data->user->id && $action != "userRightsSet") return true; + if($affectedUser==Data::$user->id && $action != "userRightsSet") return true; // get properties of actor if not already available - if(!sizeof($this->actor)) $this->actor = $this->propertiesGet($this->data->user->id); + if(!sizeof($this->actor)) $this->actor = $this->propertiesGet(Data::$user->id); $rights =& $this->actor["rights"]; // if actor is a global admin, accept the request if($rights==User\Driver::RIGHTS_GLOBAL_ADMIN) return true; @@ -53,7 +51,7 @@ class User { // if actor is not some other sort of admin, deny the request if(!in_array($rights,[User\Driver::RIGHTS_GLOBAL_MANAGER,User\Driver::RIGHTS_DOMAIN_MANAGER,User\Driver::RIGHTS_DOMAIN_ADMIN],true)) return false; // if actor is a domain admin/manager and domains don't match, deny the request - if($this->data->conf->userComposeNames && $this->actor["domain"] && $rights != User\Driver::RIGHTS_GLOBAL_MANAGER) { + if(Data::$conf->userComposeNames && $this->actor["domain"] && $rights != User\Driver::RIGHTS_GLOBAL_MANAGER) { $test = "@".$this->actor["domain"]; if(substr($affectedUser,-1*strlen($test)) != $test) return false; } @@ -79,7 +77,7 @@ class User { } public function credentials(): array { - if($this->data->conf->userAuthPreferHTTP) { + if(Data::$conf->userAuthPreferHTTP) { return $this->credentialsHTTP(); } else { return $this->credentialsForm(); @@ -100,7 +98,7 @@ class User { } else { $out = ["user" => "", "password" => ""]; } - if($this->data->conf->userComposeNames && $out["user"] != "") { + if(Data::$conf->userComposeNames && $out["user"] != "") { $out["user"] = $this->composeName($out["user"]); } $this->id = $out["user"]; @@ -109,7 +107,7 @@ class User { public function auth(string $user = null, string $password = null): bool { if($user===null) { - if($this->data->conf->userAuthPreferHTTP) return $this->authHTTP(); + if(Data::$conf->userAuthPreferHTTP) return $this->authHTTP(); return $this->authForm(); } else { $this->id = $user; @@ -117,7 +115,7 @@ class User { switch($this->u->driverFunctions("auth")) { case User\Driver::FUNC_EXTERNAL: $out = $this->u->auth($user, $password); - if($out && !$this->data->db->userExists($user)) $this->autoProvision($user, $password); + if($out && !Data::$db->userExists($user)) $this->autoProvision($user, $password); return $out; case User\Driver::FUNC_INTERNAL: return $this->u->auth($user, $password); @@ -176,7 +174,7 @@ class User { // we handle authorization checks for external drivers if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]); $out = $this->u->userExists($user); - if($out && !$this->data->db->userExists($user)) $this->autoProvision($user, ""); + if($out && !Data::$db->userExists($user)) $this->autoProvision($user, ""); return $out; case User\Driver::FUNC_INTERNAL: // internal functions handle their own authorization @@ -195,7 +193,7 @@ class User { if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]); $newPassword = $this->u->userAdd($user, $password); // if there was no exception and we don't have the user in the internal database, add it - if(!$this->data->db->userExists($user)) $this->autoProvision($user, $newPassword); + if(!Data::$db->userExists($user)) $this->autoProvision($user, $newPassword); return $newPassword; case User\Driver::FUNC_INTERNAL: // internal functions handle their own authorization @@ -212,9 +210,9 @@ class User { // we handle authorization checks for external drivers if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]); $out = $this->u->userRemove($user); - if($out && $this->data->db->userExists($user)) { + if($out && Data::$db->userExists($user)) { // if the user was removed and we have it in our data, remove it there - if(!$this->data->db->userExists($user)) $this->data->db->userRemove($user); + if(!Data::$db->userExists($user)) Data::$db->userRemove($user); } return $out; case User\Driver::FUNC_INTERNAL: @@ -232,9 +230,9 @@ class User { // we handle authorization checks for external drivers if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]); $out = $this->u->userPasswordSet($user, $newPassword, $oldPassword); - if($this->data->db->userExists($user)) { + if(Data::$db->userExists($user)) { // if the password change was successful and the user exists, set the internal password to the same value - $this->data->db->userPasswordSet($user, $out); + Data::$db->userPasswordSet($user, $out); } else { // if the user does not exists in the internal database, create it $this->autoProvision($user, $out); @@ -251,7 +249,7 @@ class User { public function propertiesGet(string $user): array { // prepare default values $domain = null; - if($this->data->conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1); + if(Data::$conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1); $init = [ "id" => $user, "name" => $user, @@ -267,7 +265,7 @@ class User { // remove password if it is return (not exhaustive, but...) if(array_key_exists('password', $out)) unset($out['password']); // if the user does not exist in the internal database, add it - if(!$this->data->db->userExists($user)) $this->autoProvision($user, "", $out); + if(!Data::$db->userExists($user)) $this->autoProvision($user, "", $out); return $out; case User\Driver::FUNC_INTERNAL: // internal functions handle their own authorization @@ -289,9 +287,9 @@ class User { // we handle authorization checks for external drivers if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]); $out = $this->u->userPropertiesSet($user, $properties); - if($this->data->db->userExists($user)) { + if(Data::$db->userExists($user)) { // if the property change was successful and the user exists, set the internal properties to the same values - $this->data->db->userPropertiesSet($user, $out); + Data::$db->userPropertiesSet($user, $out); } else { // if the user does not exists in the internal database, create it $this->autoProvision($user, "", $out); @@ -313,7 +311,7 @@ class User { if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]); $out = $this->u->userRightsGet($user); // if the user does not exist in the internal database, add it - if(!$this->data->db->userExists($user)) $this->autoProvision($user, "", null, $out); + if(!Data::$db->userExists($user)) $this->autoProvision($user, "", null, $out); return $out; case User\Driver::FUNC_INTERNAL: // internal functions handle their own authorization @@ -332,10 +330,10 @@ class User { if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]); $out = $this->u->userRightsSet($user, $level); // if the user does not exist in the internal database, add it - if($out && $this->data->db->userExists($user)) { + if($out && Data::$db->userExists($user)) { $authz = $this->authorizationEnabled(); $this->authorizationEnabled(false); - $this->data->db->userRightsSet($user, $level); + Data::$db->userRightsSet($user, $level); $this->authorizationEnabled($authz); } else if($out) { $this->autoProvision($user, "", null, $level); @@ -367,18 +365,18 @@ class User { $authz = $this->authorizationEnabled(); $this->authorizationEnabled(false); // create the user - $out = $this->data->db->userAdd($user, $password); + $out = Data::$db->userAdd($user, $password); // set the user rights - $this->data->db->userRightsSet($user, $rights); + Data::$db->userRightsSet($user, $rights); // set the user properties... if($properties===null) { // if nothing is provided but the driver uses an external function, try to get the current values from the external source try { - if($this->u->driverFunctions("userPropertiesGet")==User\Driver::FUNC_EXTERNAL) $this->data->db->userPropertiesSet($user, $this->u->userPropertiesGet($user)); + if($this->u->driverFunctions("userPropertiesGet")==User\Driver::FUNC_EXTERNAL) Data::$db->userPropertiesSet($user, $this->u->userPropertiesGet($user)); } catch(\Throwable $e) {} } else { // otherwise if values are provided, use those - $this->data->db->userPropertiesSet($user, $properties); + Data::$db->userPropertiesSet($user, $properties); } // re-enable authorization and return $this->authorizationEnabled($authz); diff --git a/lib/User/Driver.php b/lib/User/Driver.php index 4df2d167..b8428272 100644 --- a/lib/User/Driver.php +++ b/lib/User/Driver.php @@ -13,8 +13,8 @@ Interface Driver { const RIGHTS_GLOBAL_MANAGER = 75; // able to act for any normal users on any domain; cannot elevate other users const RIGHTS_GLOBAL_ADMIN = 100; // is completely unrestricted - // returns an instance of a class implementing this interface. Implemented as a static method for consistency with database classes - function __construct(\JKingWeb\Arsse\RuntimeData $data); + // returns an instance of a class implementing this interface. + function __construct(); // returns a human-friendly name for the driver (for display in installer, for example) static function driverName(): string; // returns an array (or single queried member of same) of methods defined by this interface and whether the class implements the internal function or a custom version diff --git a/lib/User/Internal/Driver.php b/lib/User/Internal/Driver.php index 93d56a86..62b4f764 100644 --- a/lib/User/Internal/Driver.php +++ b/lib/User/Internal/Driver.php @@ -1,13 +1,11 @@ Iface::FUNC_INTERNAL, @@ -22,12 +20,8 @@ final class Driver implements Iface { "userRightsSet" => Iface::FUNC_INTERNAL, ]; - static public function create(\JKingWeb\Arsse\RuntimeData $data): Driver { - return new static($data); - } - static public function driverName(): string { - return Lang::msg("Driver.User.Internal.Name"); + return Data::$l->msg("Driver.User.Internal.Name"); } public function driverFunctions(string $function = null) { diff --git a/lib/User/Internal/InternalFunctions.php b/lib/User/Internal/InternalFunctions.php index 6a3693a2..ef5f2ee2 100644 --- a/lib/User/Internal/InternalFunctions.php +++ b/lib/User/Internal/InternalFunctions.php @@ -1,17 +1,17 @@ data = $data; - $this->db = $this->data->db; + public function __construct() { + $this->db = Data::$db; } function auth(string $user, string $password): bool { - if(!$this->data->user->exists($user)) return false; + if(!Data::$user->exists($user)) return false; $hash = $this->db->userPasswordGet($user); if($password==="" && $hash==="") return true; return password_verify($password, $hash); diff --git a/tests/Conf/TestConf.php b/tests/Conf/TestConf.php index 70fffa32..aafd35b0 100644 --- a/tests/Conf/TestConf.php +++ b/tests/Conf/TestConf.php @@ -1,7 +1,7 @@ clearData(); self::$vfs = vfsStream::setup("root", null, [ 'confGood' => ' "xx");', 'confNotArray' => 'clearData(); } function testLoadDefaultValues() { diff --git a/tests/Db/SQLite3/TestDbDriverSQLite3.php b/tests/Db/SQLite3/TestDbDriverSQLite3.php index c787b3e8..0e0300c8 100644 --- a/tests/Db/SQLite3/TestDbDriverSQLite3.php +++ b/tests/Db/SQLite3/TestDbDriverSQLite3.php @@ -10,20 +10,22 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { protected $drv; function setUp() { + $this->clearData(); $conf = new Conf(); $conf->dbDriver = Db\SQLite3\Driver::class; $conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook'); - $this->data = new Test\RuntimeData($conf); - $this->drv = new Db\SQLite3\Driver($this->data, true); + Data::$conf = $conf; + $this->drv = new Db\SQLite3\Driver(true); } function tearDown() { unset($this->drv); - unlink($this->data->conf->dbSQLite3File); + unlink(Data::$conf->dbSQLite3File); + $this->clearData(); } function testFetchDriverName() { - $class = $this->data->conf->dbDriver; + $class = Data::$conf->dbDriver; $this->assertTrue(strlen($class::driverName()) > 0); } @@ -38,12 +40,12 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testExecMultipleStatements() { $this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key); INSERT INTO test(id) values(2112)")); - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->assertEquals(2112, $ch->querySingle("SELECT id from test")); } function testExecTimeout() { - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $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)"); @@ -71,7 +73,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { } function testQueryTimeout() { - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $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)"); @@ -102,7 +104,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testBeginTransaction() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -116,7 +118,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testCommitTransaction() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -130,7 +132,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testRollbackTransaction() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -144,7 +146,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testBeginChainedTransactions() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -159,7 +161,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testCommitChainedTransactions() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -178,7 +180,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testRollbackChainedTransactions() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -199,7 +201,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testPartiallyRollbackChainedTransactions() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -220,7 +222,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testFullyRollbackChainedTransactions() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); @@ -238,7 +240,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase { function testFullyCommitChainedTransactions() { $select = "SELECT count(*) FROM test"; $insert = "INSERT INTO test(id) values(null)"; - $ch = new \SQLite3($this->data->conf->dbSQLite3File); + $ch = new \SQLite3(Data::$conf->dbSQLite3File); $this->drv->exec("CREATE TABLE test(id integer primary key)"); $this->drv->begin(); $this->drv->query($insert); diff --git a/tests/Db/SQLite3/TestDbUpdateSQLite3.php b/tests/Db/SQLite3/TestDbUpdateSQLite3.php index 54af6699..84006756 100644 --- a/tests/Db/SQLite3/TestDbUpdateSQLite3.php +++ b/tests/Db/SQLite3/TestDbUpdateSQLite3.php @@ -1,7 +1,7 @@ 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:"; - $this->data = new Test\RuntimeData($conf); - $this->drv = new Db\SQLite3\Driver($this->data, true); + Data::$conf = $conf; + $this->drv = new Db\SQLite3\Driver(true); } function tearDown() { unset($this->drv); unset($this->data); unset($this->vfs); + $this->clearData(); } function testLoadMissingFile() { @@ -82,7 +84,7 @@ class TestDbUpdateSQLite3 extends \PHPUnit\Framework\TestCase { } function testPerformActualUpdate() { - $this->data->conf->dbSchemaBase = (new Conf())->dbSchemaBase; + Data::$conf->dbSchemaBase = (new Conf())->dbSchemaBase; $this->drv->schemaUpdate(Database::SCHEMA_VERSION); $this->assertEquals(Database::SCHEMA_VERSION, $this->drv->schemaVersion()); } diff --git a/tests/Exception/TestException.php b/tests/Exception/TestException.php index 0352f2dc..12baeee5 100644 --- a/tests/Exception/TestException.php +++ b/tests/Exception/TestException.php @@ -6,12 +6,15 @@ namespace JKingWeb\Arsse; class TestException extends \PHPUnit\Framework\TestCase { use Test\Tools; - static function setUpBeforeClass() { - Lang::set(""); + function setUp() { + $this->clearData(false); + $m = $this->getMockBuilder(Lang::class)->setMethods(['__invoke'])->getMock(); + $m->expects($this->any())->method("__invoke")->with($this->anything(), $this->anything())->will($this->returnValue("")); + Data::$l = $m; } - static function tearDownAfterClass() { - Lang::set(Lang::DEFAULT); + function tearDown() { + $this->clearData(true); } function testBaseClass() { @@ -51,14 +54,6 @@ class TestException extends \PHPUnit\Framework\TestCase { throw new Exception("testThisExceptionMessageDoesNotExist"); } - /** - * @depends testBaseClass - */ - function testBaseClassWithMissingMessage() { - $this->assertException("stringMissing", "Lang"); - throw new Exception("invalid"); - } - /** * @depends testBaseClassWithUnknownCode */ @@ -66,5 +61,4 @@ class TestException extends \PHPUnit\Framework\TestCase { $this->assertException("uncoded"); throw new Lang\Exception("testThisExceptionMessageDoesNotExist"); } - } diff --git a/tests/Lang/TestLang.php b/tests/Lang/TestLang.php index e5711ba5..54b94a80 100644 --- a/tests/Lang/TestLang.php +++ b/tests/Lang/TestLang.php @@ -1,48 +1,47 @@ assertCount(sizeof(self::$files), Lang::list("en")); + $this->assertCount(sizeof($this->files), $this->l->list("en")); } /** * @depends testListLanguages */ function testSetLanguage() { - $this->assertEquals("en", Lang::set("en")); - $this->assertEquals("en_ca", Lang::set("en_ca")); - $this->assertEquals("de", Lang::set("de_ch")); - $this->assertEquals("en", Lang::set("en_gb_hixie")); - $this->assertEquals("en_ca", Lang::set("en_ca_jking")); - $this->assertEquals("en", Lang::set("es")); - $this->assertEquals("", Lang::set("")); + $this->assertEquals("en", $this->l->set("en")); + $this->assertEquals("en_ca", $this->l->set("en_ca")); + $this->assertEquals("de", $this->l->set("de_ch")); + $this->assertEquals("en", $this->l->set("en_gb_hixie")); + $this->assertEquals("en_ca", $this->l->set("en_ca_jking")); + $this->assertEquals("en", $this->l->set("es")); + $this->assertEquals("", $this->l->set("")); } /** * @depends testSetLanguage */ function testLoadInternalStrings() { - $this->assertEquals("", Lang::set("", true)); - $this->assertCount(sizeof(Lang::REQUIRED), Lang::dump()); + $this->assertEquals("", $this->l->set("", true)); + $this->assertCount(sizeof(Lang::REQUIRED), $this->l->dump()); } /** * @depends testLoadInternalStrings */ function testLoadDefaultLanguage() { - $this->assertEquals(Lang::DEFAULT, Lang::set(Lang::DEFAULT, true)); - $str = Lang::dump(); + $this->assertEquals(Lang::DEFAULT, $this->l->set(Lang::DEFAULT, true)); + $str = $this->l->dump(); $this->assertArrayHasKey('Exception.JKingWeb/Arsse/Exception.uncoded', $str); $this->assertArrayHasKey('Test.presentText', $str); } @@ -51,9 +50,9 @@ class TestLang extends \PHPUnit\Framework\TestCase { * @depends testLoadDefaultLanguage */ function testLoadSupplementaryLanguage() { - Lang::set(Lang::DEFAULT, true); - $this->assertEquals("ja", Lang::set("ja", true)); - $str = Lang::dump(); + $this->l->set(Lang::DEFAULT, true); + $this->assertEquals("ja", $this->l->set("ja", true)); + $str = $this->l->dump(); $this->assertArrayHasKey('Exception.JKingWeb/Arsse/Exception.uncoded', $str); $this->assertArrayHasKey('Test.presentText', $str); $this->assertArrayHasKey('Test.absentText', $str); diff --git a/tests/Lang/TestLangErrors.php b/tests/Lang/TestLangErrors.php index 7875a0ce..6a096852 100644 --- a/tests/Lang/TestLangErrors.php +++ b/tests/Lang/TestLangErrors.php @@ -1,66 +1,64 @@ l->set("", true); } function testLoadEmptyFile() { $this->assertException("fileCorrupt", "Lang"); - Lang::set("fr_ca", true); + $this->l->set("fr_ca", true); } function testLoadFileWhichDoesNotReturnAnArray() { $this->assertException("fileCorrupt", "Lang"); - Lang::set("it", true); + $this->l->set("it", true); } function testLoadFileWhichIsNotPhp() { $this->assertException("fileCorrupt", "Lang"); - Lang::set("ko", true); + $this->l->set("ko", true); } function testLoadFileWhichIsCorrupt() { $this->assertException("fileCorrupt", "Lang"); - Lang::set("zh", true); + $this->l->set("zh", true); } function testLoadFileWithooutReadPermission() { $this->assertException("fileUnreadable", "Lang"); - Lang::set("ru", true); + $this->l->set("ru", true); } function testLoadSubtagOfMissingLanguage() { $this->assertException("fileMissing", "Lang"); - Lang::set("pt_br", true); + $this->l->set("pt_br", true); } function testFetchInvalidMessage() { $this->assertException("stringInvalid", "Lang"); - Lang::set("vi", true); - $txt = Lang::msg('Test.presentText'); + $this->l->set("vi", true); + $txt = $this->l->msg('Test.presentText'); } function testFetchMissingMessage() { $this->assertException("stringMissing", "Lang"); - $txt = Lang::msg('Test.absentText'); + $txt = $this->l->msg('Test.absentText'); } function testLoadMissingDefaultLanguage() { - // this should be the last test of the series - unlink(self::$path.Lang::DEFAULT.".php"); + unlink($this->path.Lang::DEFAULT.".php"); $this->assertException("defaultFileMissing", "Lang"); - Lang::set("fr", true); + $this->l->set("fr", true); } } \ No newline at end of file diff --git a/tests/Lang/testLangComplex.php b/tests/Lang/testLangComplex.php index 000f2e3c..72ab4168 100644 --- a/tests/Lang/testLangComplex.php +++ b/tests/Lang/testLangComplex.php @@ -1,40 +1,39 @@ l->set(Lang::DEFAULT, true); } function testLazyLoad() { - Lang::set("ja"); - $this->assertArrayNotHasKey('Test.absentText', Lang::dump()); + $this->l->set("ja"); + $this->assertArrayNotHasKey('Test.absentText', $this->l->dump()); } /** * @depends testLazyLoad */ function testGetWantedAndLoadedLocale() { - Lang::set("en", true); - Lang::set("ja"); - $this->assertEquals("ja", Lang::get()); - $this->assertEquals("en", Lang::get(true)); + $this->l->set("en", true); + $this->l->set("ja"); + $this->assertEquals("ja", $this->l->get()); + $this->assertEquals("en", $this->l->get(true)); } function testLoadCascadeOfFiles() { - Lang::set("ja", true); - $this->assertEquals("de", Lang::set("de", true)); - $str = Lang::dump(); + $this->l->set("ja", true); + $this->assertEquals("de", $this->l->set("de", true)); + $str = $this->l->dump(); $this->assertArrayNotHasKey('Test.absentText', $str); $this->assertEquals('und der Stein der Weisen', $str['Test.presentText']); } @@ -43,62 +42,62 @@ class TestLangComplex extends \PHPUnit\Framework\TestCase { * @depends testLoadCascadeOfFiles */ function testLoadSubtag() { - $this->assertEquals("en_ca", Lang::set("en_ca", true)); + $this->assertEquals("en_ca", $this->l->set("en_ca", true)); } function testFetchAMessage() { - Lang::set("de", true); - $this->assertEquals('und der Stein der Weisen', Lang::msg('Test.presentText')); + $this->l->set("de", true); + $this->assertEquals('und der Stein der Weisen', $this->l->msg('Test.presentText')); } /** * @depends testFetchAMessage */ function testFetchAMessageWithMissingParameters() { - Lang::set("en_ca", true); - $this->assertEquals('{0} and {1}', Lang::msg('Test.presentText')); + $this->l->set("en_ca", true); + $this->assertEquals('{0} and {1}', $this->l->msg('Test.presentText')); } /** * @depends testFetchAMessage */ function testFetchAMessageWithSingleNumericParameter() { - Lang::set("en_ca", true); - $this->assertEquals('Default language file "en" missing', Lang::msg('Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing', Lang::DEFAULT)); + $this->l->set("en_ca", true); + $this->assertEquals('Default language file "en" missing', $this->l->msg('Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing', Lang::DEFAULT)); } /** * @depends testFetchAMessage */ function testFetchAMessageWithMultipleNumericParameters() { - Lang::set("en_ca", true); - $this->assertEquals('Happy Rotter and the Philosopher\'s Stone', Lang::msg('Test.presentText', ['Happy Rotter', 'the Philosopher\'s Stone'])); + $this->l->set("en_ca", true); + $this->assertEquals('Happy Rotter and the Philosopher\'s Stone', $this->l->msg('Test.presentText', ['Happy Rotter', 'the Philosopher\'s Stone'])); } /** * @depends testFetchAMessage */ function testFetchAMessageWithNamedParameters() { - $this->assertEquals('Message string "Test.absentText" missing from all loaded language files (en)', Lang::msg('Exception.JKingWeb/Arsse/Lang/Exception.stringMissing', ['msgID' => 'Test.absentText', 'fileList' => 'en'])); + $this->assertEquals('Message string "Test.absentText" missing from all loaded language files (en)', $this->l->msg('Exception.JKingWeb/Arsse/Lang/Exception.stringMissing', ['msgID' => 'Test.absentText', 'fileList' => 'en'])); } /** * @depends testFetchAMessage */ function testReloadDefaultStrings() { - Lang::set("de", true); - Lang::set("en", true); - $this->assertEquals('and the Philosopher\'s Stone', Lang::msg('Test.presentText')); + $this->l->set("de", true); + $this->l->set("en", true); + $this->assertEquals('and the Philosopher\'s Stone', $this->l->msg('Test.presentText')); } /** * @depends testFetchAMessage */ function testReloadGeneralTagAfterSubtag() { - Lang::set("en", true); - Lang::set("en_us", true); - $this->assertEquals('and the Sorcerer\'s Stone', Lang::msg('Test.presentText')); - Lang::set("en", true); - $this->assertEquals('and the Philosopher\'s Stone', Lang::msg('Test.presentText')); + $this->l->set("en", true); + $this->l->set("en_us", true); + $this->assertEquals('and the Sorcerer\'s Stone', $this->l->msg('Test.presentText')); + $this->l->set("en", true); + $this->assertEquals('and the Philosopher\'s Stone', $this->l->msg('Test.presentText')); } } \ No newline at end of file diff --git a/tests/REST/NextCloudNews/TestNCNVersionDiscovery.php b/tests/REST/NextCloudNews/TestNCNVersionDiscovery.php index 7be6f646..f44b67e3 100644 --- a/tests/REST/NextCloudNews/TestNCNVersionDiscovery.php +++ b/tests/REST/NextCloudNews/TestNCNVersionDiscovery.php @@ -9,13 +9,12 @@ class TestNCNVersionDiscovery extends \PHPUnit\Framework\TestCase { use Test\Tools; function setUp() { - $conf = new Conf(); - $this->data = new Test\RuntimeData($conf); + $this->clearData(); } function testFetchVersionList() { $exp = new Response(200, ['apiLevels' => ['v1-2']]); - $h = new Rest\NextCloudNews\Versions($this->data); + $h = new Rest\NextCloudNews\Versions(); $req = new Request("GET", "/"); $res = $h->dispatch($req); $this->assertEquals($exp, $res); @@ -29,7 +28,7 @@ class TestNCNVersionDiscovery extends \PHPUnit\Framework\TestCase { function testUseIncorrectMethod() { $exp = new Response(405); - $h = new Rest\NextCloudNews\Versions($this->data); + $h = new Rest\NextCloudNews\Versions(); $req = new Request("POST", "/"); $res = $h->dispatch($req); $this->assertEquals($exp, $res); @@ -37,7 +36,7 @@ class TestNCNVersionDiscovery extends \PHPUnit\Framework\TestCase { function testUseIncorrectPath() { $exp = new Response(404); - $h = new Rest\NextCloudNews\Versions($this->data); + $h = new Rest\NextCloudNews\Versions(); $req = new Request("GET", "/ook"); $res = $h->dispatch($req); $this->assertEquals($exp, $res); diff --git a/tests/User/TestAuthorization.php b/tests/User/TestAuthorization.php index 60e0b1cc..e1870a3b 100644 --- a/tests/User/TestAuthorization.php +++ b/tests/User/TestAuthorization.php @@ -46,53 +46,58 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { 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; - $this->data = new Test\RuntimeData($conf); + Data::$conf = $conf; if($db !== null) { - $this->data->db = new $db($this->data); + Data::$db = new $db(); } - $this->data->user = new User($this->data); - $this->data->user->authorizationEnabled(false); + Data::$user = new User(); + Data::$user->authorizationEnabled(false); foreach(self::USERS as $user => $level) { - $this->data->user->add($user, ""); - $this->data->user->rightsSet($user, $level); + Data::$user->add($user, ""); + Data::$user->rightsSet($user, $level); } - $this->data->user->authorizationEnabled(true); + Data::$user->authorizationEnabled(true); + } + + function tearDown() { + $this->clearData(); } function testSelfActionLogic() { foreach(array_keys(self::USERS) as $user) { - $this->data->user->auth($user, ""); + Data::$user->auth($user, ""); // users should be able to do basic actions for themselves - $this->assertTrue($this->data->user->authorize($user, "userExists"), "User $user could not act for themselves."); - $this->assertTrue($this->data->user->authorize($user, "userRemove"), "User $user could not act 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; - $this->data->user->auth($actor, ""); + 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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); - $this->assertTrue($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); + $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($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); - $this->assertFalse($this->data->user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed."); + $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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); + $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($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); + $this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); } } } @@ -101,36 +106,36 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { foreach(self::USERS as $actor => $actorRights) { if($actorRights != User\Driver::RIGHTS_DOMAIN_MANAGER) continue; $actorDomain = substr($actor,strrpos($actor,"@")+1); - $this->data->user->auth($actor, ""); + 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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); + $this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); } else { - $this->assertFalse($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); + $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($this->data->user->authorize($affected, "userRemove"), "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($this->data->user->authorize($affected, "userRemove"), "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."); } // 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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); + $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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); + $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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); + $this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); } else { - $this->assertFalse($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); + $this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); } } } @@ -140,37 +145,37 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { foreach(self::USERS as $actor => $actorRights) { if($actorRights != User\Driver::RIGHTS_DOMAIN_ADMIN) continue; $actorDomain = substr($actor,strrpos($actor,"@")+1); - $this->data->user->auth($actor, ""); + 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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); + $this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); } else { - $this->assertFalse($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); + $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($this->data->user->authorize($affected, "userRemove"), "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($this->data->user->authorize($affected, "userRemove"), "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 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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); + $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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); + $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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); + $this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); } else { - $this->assertFalse($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); + $this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); } } } @@ -180,29 +185,29 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { foreach(self::USERS as $actor => $actorRights) { if($actorRights != User\Driver::RIGHTS_GLOBAL_MANAGER) continue; $actorDomain = substr($actor,strrpos($actor,"@")+1); - $this->data->user->auth($actor, ""); + 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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); + $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($this->data->user->authorize($affected, "userRemove"), "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($this->data->user->authorize($affected, "userRemove"), "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."); } // 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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); + $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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); + $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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); + $this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); } } } @@ -210,17 +215,17 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { function testGlobalAdministratorLogic() { foreach(self::USERS as $actor => $actorRights) { if($actorRights != User\Driver::RIGHTS_GLOBAL_ADMIN) continue; - $this->data->user->auth($actor, ""); + Data::$user->auth($actor, ""); // global admins can do anything foreach(self::USERS as $affected => $affectedRights) { - $this->assertTrue($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); - $this->assertTrue($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); + $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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); + $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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); + $this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); } } } @@ -228,24 +233,24 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { function testInvalidLevelLogic() { foreach(self::USERS as $actor => $rights) { if(in_array($rights, self::LEVELS)) continue; - $this->data->user->auth($actor, ""); + 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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); - $this->assertTrue($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); + $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($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); - $this->assertFalse($this->data->user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed."); + $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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); + $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($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); + $this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); } } } @@ -264,10 +269,10 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { 'list' => [], ]; // try first with a global admin (there should be no exception) - $this->data->user->auth("gadm@example.com", ""); + 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) - $this->data->user->auth("user@example.com", ""); + Data::$user->auth("user@example.com", ""); $this->assertCount(sizeof($tests), $this->checkExceptions("user@example.org", $tests)); } @@ -286,7 +291,7 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase { // 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($this->data->user, $func), $args); + call_user_func_array(array(Data::$user, $func), $args); } catch(User\ExceptionAuthz $e) { $err[] = $func; } diff --git a/tests/User/TestUserInternalDriver.php b/tests/User/TestUserInternalDriver.php index 6125abf3..cd36c4a8 100644 --- a/tests/User/TestUserInternalDriver.php +++ b/tests/User/TestUserInternalDriver.php @@ -9,18 +9,5 @@ class TestUserInternalDriver extends \PHPUnit\Framework\TestCase { const USER1 = "john.doe@example.com"; const USER2 = "jane.doe@example.com"; - protected $data; - - function setUp() { - $drv = User\Internal\Driver::class; - $conf = new Conf(); - $conf->userDriver = $drv; - $conf->userAuthPreferHTTP = true; - $this->data = new Test\RuntimeData($conf); - $this->data->db = new Test\User\Database($this->data); - $this->data->user = new User($this->data); - $this->data->user->authorizationEnabled(false); - $_SERVER['PHP_AUTH_USER'] = self::USER1; - $_SERVER['PHP_AUTH_PW'] = "secret"; - } + public $drv = User\Internal\Driver::class; } diff --git a/tests/User/TestUserMockExternal.php b/tests/User/TestUserMockExternal.php index b24d607b..98a3d4c5 100644 --- a/tests/User/TestUserMockExternal.php +++ b/tests/User/TestUserMockExternal.php @@ -9,18 +9,5 @@ class TestUserMockExternal extends \PHPUnit\Framework\TestCase { const USER1 = "john.doe@example.com"; const USER2 = "jane.doe@example.com"; - protected $data; - - function setUp() { - $drv = Test\User\DriverExternalMock::class; - $conf = new Conf(); - $conf->userDriver = $drv; - $conf->userAuthPreferHTTP = true; - $this->data = new Test\RuntimeData($conf); - $this->data->db = new Test\User\Database($this->data); - $this->data->user = new User($this->data); - $this->data->user->authorizationEnabled(false); - $_SERVER['PHP_AUTH_USER'] = self::USER1; - $_SERVER['PHP_AUTH_PW'] = "secret"; - } + public $drv = Test\User\DriverExternalMock::class; } \ No newline at end of file diff --git a/tests/User/TestUserMockInternal.php b/tests/User/TestUserMockInternal.php index db3b703e..3278d4de 100644 --- a/tests/User/TestUserMockInternal.php +++ b/tests/User/TestUserMockInternal.php @@ -9,17 +9,9 @@ class TestUserMockInternal extends \PHPUnit\Framework\TestCase { const USER1 = "john.doe@example.com"; const USER2 = "jane.doe@example.com"; - protected $data; + public $drv = Test\User\DriverInternalMock::class; - function setUp() { - $drv = Test\User\DriverInternalMock::class; - $conf = new Conf(); - $conf->userDriver = $drv; - $conf->userAuthPreferHTTP = true; - $this->data = new Test\RuntimeData($conf); - $this->data->user = new User($this->data); - $this->data->user->authorizationEnabled(false); - $_SERVER['PHP_AUTH_USER'] = self::USER1; - $_SERVER['PHP_AUTH_PW'] = "secret"; + function setUpSeries() { + Data::$db = null; } } diff --git a/tests/lib/Db/Tools.php b/tests/lib/Db/Tools.php index 10d44cab..f18ae318 100644 --- a/tests/lib/Db/Tools.php +++ b/tests/lib/Db/Tools.php @@ -3,13 +3,16 @@ declare(strict_types=1); namespace JKingWeb\Arsse\Test\Db; trait Tools { - function prime(\JKingWeb\Arsse\Db\Driver $drv, array $data): bool { + protected $drv; + + + function prime(array $data): bool { $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 = $drv->prepareArray("INSERT INTO $table($cols) values($params)", $bindings); + $s = $this->drv->prepareArray("INSERT INTO $table($cols) values($params)", $bindings); foreach($info['rows'] as $row) { $this->assertEquals(1, $s->runArray($row)->changes()); } @@ -18,13 +21,17 @@ trait Tools { return true; } - function compare(\JKingWeb\Arsse\Db\Driver $drv, array $expected): bool { + function compare(array $expected): bool { foreach($expected as $table => $info) { $cols = implode(",", array_keys($info['columns'])); - foreach($drv->prepare("SELECT $cols from $table")->run() as $num => $row) { + foreach($this->drv->prepare("SELECT $cols from $table")->run() as $num => $row) { $row = array_values($row); $assertSame($expected[$table]['rows'][$num], $row, "Row $num of table $table does not match expectation."); } } } + + function setUp() { + + } } \ No newline at end of file diff --git a/tests/lib/Lang/Setup.php b/tests/lib/Lang/Setup.php index d1f9ec4c..e5e67f39 100644 --- a/tests/lib/Lang/Setup.php +++ b/tests/lib/Lang/Setup.php @@ -1,16 +1,16 @@ files = [ 'en.php' => ' "and the Philosopher\'s Stone"];', 'en_ca.php' => ' "{0} and {1}"];', 'en_us.php' => ' "and the Sorcerer\'s Stone"];', @@ -28,22 +28,24 @@ trait Setup { // unreadable file 'ru.php' => '', ]; - self::$vfs = vfsStream::setup("langtest", 0777, self::$files); - self::$path = self::$vfs->url()."/"; + $vfs = vfsStream::setup("langtest", 0777, $this->files); + $this->path = $vfs->url()."/"; // set up a file without read access - chmod(self::$path."ru.php", 0000); - // make the Lang class use the vfs files - self::$defaultPath = Lang::$path; - Lang::$path = self::$path; + chmod($this->path."ru.php", 0000); + // make the test Lang class use the vfs files + $this->l = new Lang($this->path); + // create a mock Lang object to keep exceptions from creating loops + $this->clearData(false); + $m = $this->getMockBuilder(Lang::class)->setMethods(['__invoke'])->getMock(); + $m->expects($this->any())->method("__invoke")->with($this->anything(), $this->anything())->will($this->returnValue("")); + Data::$l = $m; + // call the additional setup method if it exists + if(method_exists($this, "setUpSeries")) $this->setUpSeries(); } - static function tearDownAfterClass() { - \JKingWeb\Arsse\Lang\Exception::$test = false; - Lang::$path = self::$defaultPath; - self::$path = null; - self::$vfs = null; - self::$files = null; - Lang::set("", true); - Lang::set(Lang::DEFAULT); + function tearDown() { + $this->clearData(true); + // call the additional teardiwn method if it exists + if(method_exists($this, "tearDownSeries")) $this->tearDownSeries(); } } \ No newline at end of file diff --git a/tests/lib/RuntimeData.php b/tests/lib/RuntimeData.php deleted file mode 100644 index b3e1ce6e..00000000 --- a/tests/lib/RuntimeData.php +++ /dev/null @@ -1,13 +0,0 @@ -conf = $conf; - } -} \ No newline at end of file diff --git a/tests/lib/Tools.php b/tests/lib/Tools.php index ca3d7343..a79d3fda 100644 --- a/tests/lib/Tools.php +++ b/tests/lib/Tools.php @@ -1,7 +1,8 @@ expectException($class); $this->expectExceptionCode($code); } + + function clearData(bool $loadLang = true): bool { + $r = new \ReflectionClass(\JKingWeb\Arsse\Data::class); + $props = array_keys($r->getStaticProperties()); + foreach($props as $prop) { + Data::$$prop = null; + } + if($loadLang) { + Data::$l = new \JKingWeb\Arsse\Lang(); + } + return true; + } } \ No newline at end of file diff --git a/tests/lib/User/CommonTests.php b/tests/lib/User/CommonTests.php index e80c7623..146db9c4 100644 --- a/tests/lib/User/CommonTests.php +++ b/tests/lib/User/CommonTests.php @@ -1,80 +1,105 @@ 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(); + // call the additional teardiwn method if it exists + if(method_exists($this, "tearDownSeries")) $this->tearDownSeries(); + } + function testListUsers() { - $this->assertCount(0,$this->data->user->list()); + $this->assertCount(0,Data::$user->list()); } function testCheckIfAUserDoesNotExist() { - $this->assertFalse($this->data->user->exists(self::USER1)); + $this->assertFalse(Data::$user->exists(self::USER1)); } function testAddAUser() { - $this->data->user->add(self::USER1, ""); - $this->assertCount(1,$this->data->user->list()); + Data::$user->add(self::USER1, ""); + $this->assertCount(1,Data::$user->list()); } function testCheckIfAUserDoesExist() { - $this->data->user->add(self::USER1, ""); - $this->assertTrue($this->data->user->exists(self::USER1)); + Data::$user->add(self::USER1, ""); + $this->assertTrue(Data::$user->exists(self::USER1)); } function testAddADuplicateUser() { - $this->data->user->add(self::USER1, ""); + Data::$user->add(self::USER1, ""); $this->assertException("alreadyExists", "User"); - $this->data->user->add(self::USER1, ""); + Data::$user->add(self::USER1, ""); } function testAddMultipleUsers() { - $this->data->user->add(self::USER1, ""); - $this->data->user->add(self::USER2, ""); - $this->assertCount(2,$this->data->user->list()); + Data::$user->add(self::USER1, ""); + Data::$user->add(self::USER2, ""); + $this->assertCount(2,Data::$user->list()); } function testRemoveAUser() { - $this->data->user->add(self::USER1, ""); - $this->assertCount(1,$this->data->user->list()); - $this->data->user->remove(self::USER1); - $this->assertCount(0,$this->data->user->list()); + 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"); - $this->data->user->remove(self::USER1); + Data::$user->remove(self::USER1); } function testAuthenticateAUser() { $_SERVER['PHP_AUTH_USER'] = self::USER1; $_SERVER['PHP_AUTH_PW'] = "secret"; - $this->data->user->add(self::USER1, "secret"); - $this->data->user->add(self::USER2, ""); - $this->assertTrue($this->data->user->auth()); - $this->assertTrue($this->data->user->auth(self::USER1, "secret")); - $this->assertFalse($this->data->user->auth(self::USER1, "superman")); - $this->assertTrue($this->data->user->auth(self::USER2, "")); + 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() { - $this->data->user->add(self::USER1, "secret"); - $this->assertEquals("superman", $this->data->user->passwordSet(self::USER1, "superman")); - $this->assertTrue($this->data->user->auth(self::USER1, "superman")); - $this->assertFalse($this->data->user->auth(self::USER1, "secret")); - $this->assertEquals("", $this->data->user->passwordSet(self::USER1, "")); - $this->assertTrue($this->data->user->auth(self::USER1, "")); - $this->assertEquals($this->data->conf->userTempPasswordLength, strlen($this->data->user->passwordSet(self::USER1))); + 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"); - $this->data->user->passwordSet(self::USER1, "superman"); + Data::$user->passwordSet(self::USER1, "superman"); } function testGetThePropertiesOfAUser() { - $this->data->user->add(self::USER1, "secret"); - $p = $this->data->user->propertiesGet(self::USER1); + Data::$user->add(self::USER1, "secret"); + $p = Data::$user->propertiesGet(self::USER1); $this->assertArrayHasKey('id', $p); $this->assertArrayHasKey('name', $p); $this->assertArrayHasKey('domain', $p); @@ -97,22 +122,22 @@ trait CommonTests { 'domain' => 'example.com', 'rights' => Driver::RIGHTS_NONE, ]; - $this->data->user->add(self::USER1, "secret"); - $this->data->user->propertiesSet(self::USER1, $pSet); - $p = $this->data->user->propertiesGet(self::USER1); + 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($this->data->user->auth(self::USER1, "superman")); + $this->assertFalse(Data::$user->auth(self::USER1, "superman")); } function testGetTheRightsOfAUser() { - $this->data->user->add(self::USER1, ""); - $this->assertEquals(Driver::RIGHTS_NONE, $this->data->user->rightsGet(self::USER1)); + Data::$user->add(self::USER1, ""); + $this->assertEquals(Driver::RIGHTS_NONE, Data::$user->rightsGet(self::USER1)); } function testSetTheRightsOfAUser() { - $this->data->user->add(self::USER1, ""); - $this->data->user->rightsSet(self::USER1, Driver::RIGHTS_GLOBAL_ADMIN); - $this->assertEquals(Driver::RIGHTS_GLOBAL_ADMIN, $this->data->user->rightsGet(self::USER1)); + 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)); } } \ No newline at end of file diff --git a/tests/lib/User/Database.php b/tests/lib/User/Database.php index 458c1368..0e3d7619 100644 --- a/tests/lib/User/Database.php +++ b/tests/lib/User/Database.php @@ -1,6 +1,7 @@ data = $data; + public function __construct() { } function userExists(string $user): bool { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return parent::userExists($user); } function userAdd(string $user, string $password = null): string { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if($this->userExists($user)) throw new Exception("alreadyExists", ["action" => __FUNCTION__, "user" => $user]); - if($password===null) $password = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get(); + if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get(); return parent::userAdd($user, $password); } function userRemove(string $user): bool { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); return parent::userRemove($user); } function userList(string $domain = null): array { if($domain===null) { - if(!$this->data->user->authorize("", __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]); + if(!Data::$user->authorize("", __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]); return parent::userList(); } else { $suffix = '@'.$domain; - if(!$this->data->user->authorize($suffix, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]); + if(!Data::$user->authorize($suffix, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]); return parent::userList($domain); } } function userPasswordSet(string $user, string $newPassword = null, string $oldPassword = null): string { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); - if($newPassword===null) $newPassword = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get(); + if($newPassword===null) $newPassword = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get(); return parent::userPasswordSet($user, $newPassword); } function userPropertiesGet(string $user): array { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); $out = parent::userPropertiesGet($user); unset($out['password']); @@ -59,20 +59,20 @@ class Database extends DriverSkeleton { } function userPropertiesSet(string $user, array $properties): array { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); parent::userPropertiesSet($user, $properties); return $this->userPropertiesGet($user); } function userRightsGet(string $user): int { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); return parent::userRightsGet($user); } function userRightsSet(string $user, int $level): bool { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); return parent::userRightsSet($user, $level); } @@ -80,7 +80,7 @@ class Database extends DriverSkeleton { // specific to mock database function userPasswordGet(string $user): string { - if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); + if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); return $this->db[$user]['password']; } diff --git a/tests/lib/User/DriverExternalMock.php b/tests/lib/User/DriverExternalMock.php index f6553b1c..5de6eb21 100644 --- a/tests/lib/User/DriverExternalMock.php +++ b/tests/lib/User/DriverExternalMock.php @@ -1,6 +1,7 @@ Driver::FUNC_EXTERNAL, "userList" => Driver::FUNC_EXTERNAL, @@ -22,10 +22,6 @@ class DriverExternalMock extends DriverSkeleton implements Driver { "userRightsSet" => Driver::FUNC_EXTERNAL, ]; - static public function create(\JKingWeb\Arsse\RuntimeData $data): Driver { - return new static($data); - } - static public function driverName(): string { return "Mock External Driver"; } @@ -39,8 +35,7 @@ class DriverExternalMock extends DriverSkeleton implements Driver { } } - public function __construct(\JKingWeb\Arsse\RuntimeData $data) { - $this->data = $data; + public function __construct() { } function auth(string $user, string $password): bool { @@ -56,7 +51,7 @@ class DriverExternalMock extends DriverSkeleton implements Driver { function userAdd(string $user, string $password = null): string { if($this->userExists($user)) throw new Exception("alreadyExists", ["action" => __FUNCTION__, "user" => $user]); - if($password===null) $password = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get(); + if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get(); return parent::userAdd($user, $password); } @@ -75,7 +70,7 @@ class DriverExternalMock extends DriverSkeleton implements Driver { function userPasswordSet(string $user, string $newPassword = null, string $oldPassword = null): string { if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); - if($newPassword===null) $newPassword = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get(); + if($newPassword===null) $newPassword = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get(); return parent::userPasswordSet($user, $newPassword); } diff --git a/tests/lib/User/DriverInternalMock.php b/tests/lib/User/DriverInternalMock.php index b78c1403..f9cec1cf 100644 --- a/tests/lib/User/DriverInternalMock.php +++ b/tests/lib/User/DriverInternalMock.php @@ -6,7 +6,6 @@ use JKingWeb\Arsse\User\Driver; class DriverInternalMock extends Database implements Driver { public $db = []; - protected $data; protected $functions = [ "auth" => Driver::FUNC_INTERNAL, "userList" => Driver::FUNC_INTERNAL, @@ -20,10 +19,6 @@ class DriverInternalMock extends Database implements Driver { "userRightsSet" => Driver::FUNC_INTERNAL, ]; - static public function create(\JKingWeb\Arsse\RuntimeData $data): Driver { - return new static($data); - } - static public function driverName(): string { return "Mock Internal Driver"; } @@ -37,8 +32,7 @@ class DriverInternalMock extends Database implements Driver { } } - public function __construct(\JKingWeb\Arsse\RuntimeData $data) { - $this->data = $data; + public function __construct() { } function auth(string $user, string $password): bool { diff --git a/tests/lib/User/DriverSkeleton.php b/tests/lib/User/DriverSkeleton.php index d63680e4..8d49a266 100644 --- a/tests/lib/User/DriverSkeleton.php +++ b/tests/lib/User/DriverSkeleton.php @@ -10,7 +10,6 @@ use PasswordGenerator\Generator as PassGen; abstract class DriverSkeleton { protected $db = []; - protected $data; function userExists(string $user): bool { return array_key_exists($user, $this->db); diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 6809e32e..2331c18f 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -10,10 +10,13 @@ beStrictAboutTestSize="true" stopOnError="true"> - + + Exception/TestException.php + + + Lang/TestLang.php Lang/TestLangComplex.php - Lang/TestException.php Lang/TestLangErrors.php diff --git a/tests/test.php b/tests/test.php index 12eb7648..449f7295 100644 --- a/tests/test.php +++ b/tests/test.php @@ -3,6 +3,7 @@ namespace JKingWeb\Arsse; const INSTALL = true; require_once "../bootstrap.php"; + $user = "john.doe@example.com"; $pass = "secret"; $_SERVER['PHP_AUTH_USER'] = $user; @@ -10,13 +11,13 @@ $_SERVER['PHP_AUTH_PW'] = $pass; $conf = new Conf(); $conf->dbSQLite3File = ":memory:"; $conf->userAuthPreferHTTP = true; -$data = new RuntimeData($conf); -$data->db->schemaUpdate(); +Data::load($conf); +Data::$db->schemaUpdate(); -$data->user->add($user, $pass); -$data->user->auth(); -$data->user->authorizationEnabled(false); -$data->user->rightsSet($user, User\Driver::RIGHTS_GLOBAL_ADMIN); -$data->user->authorizationEnabled(true); -$data->db->folderAdd($user, ['name' => 'ook']); -$data->db->subscriptionAdd($user, "http://www.tbray.org/ongoing/ongoing.atom"); \ No newline at end of file +Data::$user->add($user, $pass); +Data::$user->auth(); +Data::$user->authorizationEnabled(false); +Data::$user->rightsSet($user, User\Driver::RIGHTS_GLOBAL_ADMIN); +Data::$user->authorizationEnabled(true); +Data::$db->folderAdd($user, ['name' => 'ook']); +Data::$db->subscriptionAdd($user, "http://www.tbray.org/ongoing/ongoing.atom"); \ No newline at end of file