test fixes and added more tests

This commit is contained in:
Dennis Eichhorn 2021-10-02 20:49:17 +02:00
parent 2cf30d1073
commit 002112fca4
51 changed files with 763 additions and 430 deletions

View File

@ -205,7 +205,7 @@ final class BasicOcr
foreach ($candidateLabels as $i => $label) {
$predictedLabels[] = [
'label' => $label,
'prob' => $countedCandidates[$candidateLabels[$i]] / $k,
'prob' => $countedCandidates[$label] / $k,
];
}
}

View File

@ -97,7 +97,7 @@ class MazeGenerator
$pos = \array_pop($path);
if ($pos === null) {
break;
break; // @codeCoverageIgnore
}
}
}

View File

@ -64,14 +64,16 @@ final class CycleSort implements SortInterface
++$pos;
}
$old = $list[$pos];
$list[$pos] = $item;
$item = $old;
if ($pos !== $start) {
$old = $list[$pos];
$list[$pos] = $item;
$item = $old;
}
while ($pos !== $start) {
$pos = $start;
$length1 = \count($list);
for ($i = $start + 1; $i < $length1; ++$i) {
$pos = $start;
for ($i = $start + 1; $i < $n; ++$i) {
if (!$list[$i]->compare($item, $order)) {
++$pos;
}
@ -81,9 +83,11 @@ final class CycleSort implements SortInterface
++$pos;
}
$old = $list[$pos];
$list[$pos] = $item;
$item = $old;
if (!$item->equals($list[$pos])) {
$old = $list[$pos];
$list[$pos] = $item;
$item = $old;
}
}
}

View File

@ -67,7 +67,7 @@ final class QuickSort implements SortInterface
{
if ($lo < $hi) {
$i = self::partition($list, $lo, $hi, $order);
self::qsort($list, $lo, $i, $order);
self::qsort($list, $lo, $i - 1, $order);
self::qsort($list, $i + 1, $hi, $order);
}
}
@ -86,26 +86,22 @@ final class QuickSort implements SortInterface
*/
private static function partition(array &$list, int $lo, int $hi, int $order) : int
{
$pivot = $list[$lo + ((int) (($hi - $lo) / 2))];
while (true) {
while (!$list[$lo]->compare($pivot, $order)) {
++$lo;
$pivot = $list[$hi];
$i = $lo - 1;
for ($j = $lo; $j <= $hi - 1; ++$j) {
if (!$list[$j]->compare($pivot, $order)) {
++$i;
$old = $list[$i];
$list[$i] = $list[$j];
$list[$j] = $old;
}
while ($list[$hi]->compare($pivot, $order)) {
--$hi;
}
if ($lo >= $hi) {
return $hi;
}
$old = $list[$lo];
$list[$lo] = $list[$hi];
$list[$hi] = $old;
++$lo;
--$hi;
}
$old = $list[$i + 1];
$list[$i + 1] = $list[$hi];
$list[$hi] = $old;
return $i + 1;
}
}

View File

@ -98,11 +98,7 @@ final class ApplicationManager
$destination = \rtrim($destination, '\\/');
$source = \rtrim($source, '/\\');
if (!\is_dir($source) || \is_dir($destination)) {
return false;
}
if (!\is_file($source . '/Admin/Installer.php')) {
if (!\is_dir($source) || \is_dir($destination) || !\is_file($source . '/Admin/Installer.php')) {
return false;
}
@ -124,6 +120,73 @@ final class ApplicationManager
}
}
/**
* Uninstall the application
*
* @param string $source Source of the application
*
* @return bool
*
* @since 1.0.0
*/
public function uninstall(string $source) : bool
{
$source = \rtrim($source, '/\\');
if (!\is_dir($source) || !\is_file($source . '/Admin/Uninstaller.php')) {
return false;
}
try {
$info = $this->loadInfo($source . '/info.json');
$this->installed[$info->getInternalName()] = $info;
$classPath = \substr(\realpath($source) . '/Admin/Uninstaller', $t = \strlen(\realpath(__DIR__ . '/../../')));
$class = \str_replace('/', '\\', $classPath);
$class::uninstall($this->app->dbPool, $info, $this->app->appSettings);
$this->uninstallFiles($source);
return true;
} catch (\Throwable $t) {
return false; // @codeCoverageIgnore
}
}
/**
* Re-init application.
*
* @param string $appPath App path
*
* @return void
*
* @throws InvalidModuleException Throws this exception in case the installer doesn't exist
*
* @since 1.0.0
*/
public function reInit(string $appPath) : void
{
$info = $this->loadInfo($appPath . '/info.json');
if ($info === null) {
return;
}
$classPath = \substr(\realpath($appPath) . '/Admin/Installer', \strlen(\realpath(__DIR__ . '/../../')));
$class = \str_replace('/', '\\', $classPath);
/** @var $class InstallerAbstract */
$class::reInit($info);
}
/**
* Get all applications who are providing for a specific module.
*
* @param string $module Module to check providings for
*
* @return array<string, string[]>
*
* @since 1.0.0
*/
public function getProvidingForModule(string $module) : array
{
$providing = [];
@ -189,6 +252,20 @@ final class ApplicationManager
Directory::copy($source, $destination);
}
/**
* Uninstall files
*
* @param string $source Source path
*
* @return void
*
* @since 1.0.0
*/
private function uninstallFiles(string $source) : void
{
Directory::delete($source);
}
/**
* Replace placeholder string (application placeholder name)
*

View File

@ -143,6 +143,10 @@ abstract class InstallerAbstract
$classPath = \substr(\realpath(static::PATH) . '/Status', \strlen(\realpath(__DIR__ . '/../../')));
$class = \str_replace('/', '\\', $classPath);
$class::clearRoutes();
$class::clearHooks();
$class::activateRoutes($info);
$class::activateHooks($info);
}

View File

@ -63,7 +63,7 @@ abstract class StatusAbstract
*/
public static function activateRoutes(ApplicationInfo $appInfo = null) : void
{
self::installRoutes(static::PATH . '/../Routes.php', static::PATH . '/../Admin/Install/Application/Routes.php');
self::installRoutesHooks(static::PATH . '/../Routes.php', static::PATH . '/../Admin/Install/Application/Routes.php');
}
/**
@ -79,7 +79,7 @@ abstract class StatusAbstract
*/
public static function activateHooks(ApplicationInfo $appInfo = null) : void
{
self::installRoutes(static::PATH . '/../Hooks.php', static::PATH . '/../Admin/Install/Application/Hooks.php');
self::installRoutesHooks(static::PATH . '/../Hooks.php', static::PATH . '/../Admin/Install/Application/Hooks.php');
}
/**
@ -94,7 +94,7 @@ abstract class StatusAbstract
*
* @since 1.0.0
*/
protected static function installRoutes(string $destRoutePath, string $srcRoutePath) : void
protected static function installRoutesHooks(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)) {
\file_put_contents($destRoutePath, '<?php return [];');
@ -115,175 +115,40 @@ abstract class StatusAbstract
/** @noinspection PhpIncludeInspection */
$appRoutes = include $destRoutePath;
/** @noinspection PhpIncludeInspection */
$moduleRoutes = include $srcRoutePath;
$srcRoutes = include $srcRoutePath;
$appRoutes = \array_merge_recursive($appRoutes, $moduleRoutes);
$appRoutes = \array_merge_recursive($appRoutes, $srcRoutes);
\file_put_contents($destRoutePath, '<?php return ' . ArrayParser::serializeArray($appRoutes) . ';', \LOCK_EX);
}
/**
* Install hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PathException This exception is thrown if the hook file doesn't exist
* @throws PermissionException This exception is thrown if the hook file couldn't be updated (no write permission)
*
* @since 1.0.0
*/
protected static function installHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)) {
\file_put_contents($destHookPath, '<?php return [];');
}
if (!\is_file($srcHookPath)) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = \array_merge_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
}
/**
* Deactivate routes.
*
* @param ApplicationInfo $appInfo Application info
* Clear all routes.
*
* @return void
*
* @throws PathException
* @throws PermissionException
*
* @since 1.0.0
*/
public static function deactivateRoutes(ApplicationInfo $appInfo) : void
public static function clearRoutes() : void
{
self::installRoutes(static::PATH . '/../Routes.php', static::PATH . '/../Admin/Install/Application/Routes.php');
\file_put_contents(static::PATH . '/../Routes.php', '<?php return [];', \LOCK_EX);
}
/**
* Deactivate hooks.
*
* @param ApplicationInfo $appInfo Application info
* Clear all hooks.
*
* @return void
*
* @throws PathException
* @throws PermissionException
*
* @since 1.0.0
*/
public static function deactivateHooks(ApplicationInfo $appInfo) : void
public static function clearHooks() : void
{
self::installRoutes(static::PATH . '/../Hooks.php', static::PATH . '/../Admin/Install/Application/Hooks.php');
}
/**
* Uninstall routes.
*
* @param string $destRoutePath Destination route path
* @param string $srcRoutePath Source route path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
public static function uninstallRoutes(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)
|| !\is_file($srcRoutePath)
) {
return;
}
if (!\is_file($destRoutePath)) {
throw new PathException($destRoutePath);
}
if (!\is_writable($destRoutePath)) {
throw new PermissionException($destRoutePath);
}
/** @noinspection PhpIncludeInspection */
$appRoutes = include $destRoutePath;
/** @noinspection PhpIncludeInspection */
$moduleRoutes = include $srcRoutePath;
$appRoutes = ArrayUtils::array_diff_assoc_recursive($appRoutes, $moduleRoutes);
\file_put_contents($destRoutePath, '<?php return ' . ArrayParser::serializeArray($appRoutes) . ';', \LOCK_EX);
}
/**
* Uninstall hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function uninstallHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)
|| !\is_file($srcHookPath)
) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = ArrayUtils::array_diff_assoc_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
}
/**
* Deactivate app.
*
* @param DatabasePool $dbPool Database instance
* @param ApplicationInfo $info Module info
*
* @return void
*
* @since 1.0.0
*/
public static function deactivate(DatabasePool $dbPool, ApplicationInfo $info) : void
{
self::deactivateRoutes($info);
self::deactivateHooks($info);
\file_put_contents(static::PATH . '/../Hooks.php', '<?php return [];', \LOCK_EX);
}
}

View File

@ -40,7 +40,7 @@ abstract class UninstallerAbstract
*/
public static function uninstall(DatabasePool $dbPool, ApplicationInfo $info) : void
{
self::deactivate($dbPool, $info);
//self::deactivate($dbPool, $info);
self::dropTables($dbPool, $info);
self::unregisterFromDatabase($dbPool, $info);
}
@ -55,12 +55,14 @@ abstract class UninstallerAbstract
*
* @since 1.0.0
*/
/*
protected static function deactivate(DatabasePool $dbPool, ApplicationInfo $info) : void
{
/** @var StatusAbstract $class */
$class = '\Web\\' . $info->getInternalName() . '\Admin\Status';
$classPath = \substr(\realpath(static::PATH) . '/Status', \strlen(\realpath(__DIR__ . '/../../')));
$class = \str_replace('/', '\\', $classPath);
$class::deactivate($dbPool, $info);
}
}*/
/**
* Drop tables of app.
@ -74,8 +76,7 @@ abstract class UninstallerAbstract
*/
public static function dropTables(DatabasePool $dbPool, ApplicationInfo $info) : void
{
$path = __DIR__ . '/../../Web/' . $info->getInternalName() . '/Admin/Install/db.json';
$path = static::PATH . '/Install/db.json';
if (!\is_file($path)) {
return;
}
@ -88,8 +89,8 @@ abstract class UninstallerAbstract
$definitions = \json_decode($content, true);
$builder = new SchemaBuilder($dbPool->get('schema'));
foreach ($definitions as $definition) {
$builder->dropTable($definition['table'] ?? '');
foreach ($definitions as $name => $definition) {
$builder->dropTable($name ?? '');
}
$builder->execute();

View File

@ -27,7 +27,7 @@ final class CustomerValue
/**
* Simple customer lifetime value
*
* Hazard Model, same as $margin * (1 + $discountRate) / (1 + $discountRate - $retentionRate)
* Hazard Model
*
* @param float $margin Margin per period (or revenue/sales)
* @param float $retentionRate Rate of remaining customers per period (= average lifetime / (1 + average lifetime))
@ -42,32 +42,6 @@ final class CustomerValue
return $margin * $retentionRate / (1 + $discountRate - $retentionRate);
}
/**
* Basic customer lifetime value
*
* Hazard Model, same as $margin * (1 + $discountRate) / (1 + $discountRate - $retentionRate)
*
* @param array $margins Margin per period (or revenue/sales)
* @param float $retentionRate Rate of remaining customers per period (= average lifetime / (1 + average lifetime))
* @param float $discountRate Cost of capital to discount future revenue
*
* @return float
*
* @since 1.0.0
*/
public static function getBasicCLV(array $margins, float $retentionRate, float $discountRate) : float
{
$clv = 0.0;
$c = 1;
foreach ($margins as $margin) {
$clv += ($retentionRate ** $c) * $margin * \pow(1 / (1 + $discountRate), $c);
++$c;
}
return $clv;
}
/**
* Normalized measure of recurring revenue
*

View File

@ -90,26 +90,12 @@ final class Metrics
return $rc * (1 - \exp(-$r * $t));
}
/**
* Calculate the average lifetime duration
*
* @param array $retainedCustomers Retained customers per period
*
* @return float
*
* @since 1.0.0
*/
public static function averageLifetimeDuration(array $retainedCustomers) : float
{
return \array_sum($retainedCustomers) / \count($retainedCustomers);
}
/**
* Calculate the probability of a customer being active
*
* @param int $purchases Number of purchases during the periods
* @param int $periods Number of periods (e.g. number of months)
* @param int $lastPurchase In which period was the last purchase
* @param int $lastPurchase In which period was the last purchase (lastPurchase = periods: means customer purchased in this period)
*
* @return float
*

View File

@ -45,11 +45,11 @@ interface ConnectionInterface extends DataStorageConnectionInterface
* @param int|string $key Unique cache key
* @param int $value By value
*
* @return void
* @return bool
*
* @since 1.0.0
*/
public function increment(int | string $key, int $value = 1) : void;
public function increment(int | string $key, int $value = 1) : bool;
/**
* Decrement value.
@ -57,11 +57,11 @@ interface ConnectionInterface extends DataStorageConnectionInterface
* @param int|string $key Unique cache key
* @param int $value By value
*
* @return void
* @return bool
*
* @since 1.0.0
*/
public function decrement(int | string $key, int $value = 1) : void;
public function decrement(int | string $key, int $value = 1) : bool;
/**
* Rename cache key.

View File

@ -238,7 +238,7 @@ final class FileCache extends ConnectionAbstract
return '';
}
throw new InvalidEnumValue($type);
throw new InvalidEnumValue($type); // @codeCoverageIgnore
}
/**
@ -281,7 +281,7 @@ final class FileCache extends ConnectionAbstract
$raw = \file_get_contents($path);
if ($raw === false) {
return null;
return null; // @codeCoverageIgnore
}
$type = (int) $raw[0];
@ -289,7 +289,7 @@ final class FileCache extends ConnectionAbstract
$expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1);
if ($expireStart < 0 || $expireEnd < 0) {
return null;
return null; // @codeCoverageIgnore
}
$cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1));
@ -337,7 +337,7 @@ final class FileCache extends ConnectionAbstract
$namespace = \substr($raw, $namespaceStart + 1, $namespaceEnd - $namespaceStart - 1);
if ($namespace === false) {
return null;
return null; // @codeCoverageIgnore
}
return new $namespace();
@ -347,7 +347,7 @@ final class FileCache extends ConnectionAbstract
$namespace = \substr($raw, $namespaceStart + 1, $namespaceEnd - $namespaceStart - 1);
if ($namespace === false) {
return null;
return null; // @codeCoverageIgnore
}
$obj = new $namespace();
@ -385,7 +385,7 @@ final class FileCache extends ConnectionAbstract
$raw = \file_get_contents($path);
if ($raw === false) {
return false;
return false; // @codeCoverageIgnore
}
$cacheExpire = $this->getExpire($raw);
@ -426,7 +426,7 @@ final class FileCache extends ConnectionAbstract
$raw = \file_get_contents($path);
if ($raw === false) {
return false;
return false; // @codeCoverageIgnore
}
$type = (int) $raw[0];
@ -434,7 +434,7 @@ final class FileCache extends ConnectionAbstract
$expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1);
if ($expireStart < 0 || $expireEnd < 0) {
return false;
return false; // @codeCoverageIgnore
}
$cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1));
@ -452,11 +452,11 @@ final class FileCache extends ConnectionAbstract
/**
* {@inheritdoc}
*/
public function increment(int | string $key, int $value = 1) : void
public function increment(int | string $key, int $value = 1) : bool
{
$path = $this->getPath($key);
if (!File::exists($path)) {
return;
return false;
}
$created = File::created($path)->getTimestamp();
@ -464,7 +464,7 @@ final class FileCache extends ConnectionAbstract
$raw = \file_get_contents($path);
if ($raw === false) {
return;
return false; // @codeCoverageIgnore
}
$type = (int) $raw[0];
@ -472,7 +472,7 @@ final class FileCache extends ConnectionAbstract
$expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1);
if ($expireStart < 0 || $expireEnd < 0) {
return;
return false; // @codeCoverageIgnore
}
$cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1));
@ -480,16 +480,18 @@ final class FileCache extends ConnectionAbstract
$val = $this->reverseValue($type, $raw, $expireEnd);
$this->set($key, $val + $value, $cacheExpire);
return true;
}
/**
* {@inheritdoc}
*/
public function decrement(int | string $key, int $value = 1) : void
public function decrement(int | string $key, int $value = 1) : bool
{
$path = $this->getPath($key);
if (!File::exists($path)) {
return;
return false;
}
$created = File::created($path)->getTimestamp();
@ -497,7 +499,7 @@ final class FileCache extends ConnectionAbstract
$raw = \file_get_contents($path);
if ($raw === false) {
return;
return false; // @codeCoverageIgnore
}
$type = (int) $raw[0];
@ -505,7 +507,7 @@ final class FileCache extends ConnectionAbstract
$expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1);
if ($expireStart < 0 || $expireEnd < 0) {
return;
return false; // @codeCoverageIgnore
}
$cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1));
@ -513,6 +515,8 @@ final class FileCache extends ConnectionAbstract
$val = $this->reverseValue($type, $raw, $expireEnd);
$this->set($key, $val - $value, $cacheExpire);
return true;
}
/**
@ -548,7 +552,7 @@ final class FileCache extends ConnectionAbstract
$raw = \file_get_contents($path);
if ($raw === false) {
continue;
continue; // @codeCoverageIgnore
}
$type = (int) $raw[0];
@ -556,7 +560,7 @@ final class FileCache extends ConnectionAbstract
$expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1);
if ($expireStart < 0 || $expireEnd < 0) {
continue;
continue; // @codeCoverageIgnore
}
$cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1));
@ -600,7 +604,7 @@ final class FileCache extends ConnectionAbstract
$raw = \file_get_contents($path);
if ($raw === false) {
continue;
continue; // @codeCoverageIgnore
}
$cacheExpire = $this->getExpire($raw);

View File

@ -154,17 +154,21 @@ final class MemCached extends ConnectionAbstract
/**
* {@inheritdoc}
*/
public function increment(int | string $key, int $value = 1) : void
public function increment(int | string $key, int $value = 1) : bool
{
$this->con->increment($key, $value);
return true;
}
/**
* {@inheritdoc}
*/
public function decrement(int | string $key, int $value = 1) : void
public function decrement(int | string $key, int $value = 1) : bool
{
$this->con->decrement($key, $value);
return true;
}
/**

View File

@ -41,15 +41,17 @@ final class NullCache extends ConnectionAbstract
/**
* {@inheritdoc}
*/
public function increment(int | string $key, int $value = 1) : void
public function increment(int | string $key, int $value = 1) : bool
{
return true;
}
/**
* {@inheritdoc}
*/
public function decrement(int | string $key, int $value = 1) : void
public function decrement(int | string $key, int $value = 1) : bool
{
return true;
}
/**

View File

@ -182,17 +182,21 @@ final class RedisCache extends ConnectionAbstract
/**
* {@inheritdoc}
*/
public function increment(int | string $key, int $value = 1) : void
public function increment(int | string $key, int $value = 1) : bool
{
$this->con->incrBy($key, $value);
return true;
}
/**
* {@inheritdoc}
*/
public function decrement(int | string $key, int $value = 1) : void
public function decrement(int | string $key, int $value = 1) : bool
{
$this->con->decrBy($key, $value);
return true;
}
/**

View File

@ -54,10 +54,10 @@ class Builder extends QueryBuilder
/**
* Table to drop.
*
* @var string
* @var array
* @since 1.0.0
*/
public string $dropTable = '';
public array $dropTable = [];
/**
* Tables.
@ -192,8 +192,8 @@ class Builder extends QueryBuilder
*/
public function dropTable(string $table) : self
{
$this->type = QueryType::DROP_TABLE;
$this->dropTable = $table;
$this->type = QueryType::DROP_TABLE;
$this->dropTable[] = $table;
return $this;
}

View File

@ -232,16 +232,16 @@ class Grammar extends QueryGrammar
/**
* Compile drop query.
*
* @param BuilderAbstract $query Query
* @param string $table Tables to drop
* @param BuilderAbstract $query Query
* @param string $database Tables to drop
*
* @return string
*
* @since 1.0.0
*/
protected function compileDropDatabase(BuilderAbstract $query, string $table) : string
protected function compileDropDatabase(BuilderAbstract $query, string $database) : string
{
$expression = $this->expressionizeTableColumn([$table]);
$expression = $this->expressionizeTableColumn([$database]);
if ($expression === '') {
$expression = '*';
@ -253,16 +253,16 @@ class Grammar extends QueryGrammar
/**
* Compile drop query.
*
* @param BuilderAbstract $query Query
* @param string $table Tables to drop
* @param BuilderAbstract $query Query
* @param array $tables Tables to drop
*
* @return string
*
* @since 1.0.0
*/
protected function compileDropTable(BuilderAbstract $query, string $table) : string
protected function compileDropTable(BuilderAbstract $query, array $tables) : string
{
$expression = $this->expressionizeTableColumn([$table]);
$expression = $this->expressionizeTableColumn($tables);
if ($expression === '') {
$expression = '*';

View File

@ -142,7 +142,7 @@ final class Prime
$primes = \array_combine($range, $range);
if ($primes === false) {
return [];
return []; // @codeCoverageIgnore
}
while ($number * $number < $n) {

View File

@ -121,7 +121,7 @@ abstract class ModuleAbstract
public static function getLocalization(string $language, string $destination) : array
{
$lang = [];
if (\is_file($oldPath = static::PATH . static::NAME . '/Theme/' . $destination . '/Lang/' . $language . '.lang.php')) {
if (\is_file($oldPath = static::PATH . '/Theme/' . $destination . '/Lang/' . $language . '.lang.php')) {
/** @noinspection PhpIncludeInspection */
return include $oldPath;
}

View File

@ -77,7 +77,7 @@ abstract class StatusAbstract
continue;
}
self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
@ -86,7 +86,7 @@ abstract class StatusAbstract
continue;
}
self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
}
}
}
@ -103,7 +103,7 @@ abstract class StatusAbstract
*
* @since 1.0.0
*/
protected static function installRoutes(string $destRoutePath, string $srcRoutePath) : void
protected static function installRoutesHooks(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)) {
\file_put_contents($destRoutePath, '<?php return [];');
@ -157,7 +157,7 @@ abstract class StatusAbstract
continue;
}
self::installHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
@ -166,52 +166,11 @@ abstract class StatusAbstract
continue;
}
self::installHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
}
}
}
/**
* Install hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PathException This exception is thrown if the hook file doesn't exist
* @throws PermissionException This exception is thrown if the hook file couldn't be updated (no write permission)
*
* @since 1.0.0
*/
protected static function installHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)) {
\file_put_contents($destHookPath, '<?php return [];');
}
if (!\is_file($srcHookPath)) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = \array_merge_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
}
/**
* Deactivate module.
*
@ -252,7 +211,7 @@ abstract class StatusAbstract
continue;
}
self::uninstallRoutes(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
@ -261,7 +220,7 @@ abstract class StatusAbstract
continue;
}
self::uninstallRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath());
}
}
}
@ -278,7 +237,7 @@ abstract class StatusAbstract
*
* @since 1.0.0
*/
public static function uninstallRoutes(string $destRoutePath, string $srcRoutePath) : void
public static function uninstallRoutesHooks(string $destRoutePath, string $srcRoutePath) : void
{
if (!\is_file($destRoutePath)
|| !\is_file($srcRoutePath)
@ -328,7 +287,7 @@ abstract class StatusAbstract
continue;
}
self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath());
}
} elseif ($child instanceof File) {
if (!\is_dir(__DIR__ . '/../../' . $child->getName())
@ -337,46 +296,8 @@ abstract class StatusAbstract
continue;
}
self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath());
}
}
}
/**
* Uninstall hooks.
*
* @param string $destHookPath Destination hook path
* @param string $srcHookPath Source hook path
*
* @return void
*
* @throws PermissionException
*
* @since 1.0.0
*/
protected static function uninstallHooks(string $destHookPath, string $srcHookPath) : void
{
if (!\is_file($destHookPath)
|| !\is_file($srcHookPath)
) {
return;
}
if (!\is_file($destHookPath)) {
throw new PathException($destHookPath);
}
if (!\is_writable($destHookPath)) {
throw new PermissionException($destHookPath);
}
/** @noinspection PhpIncludeInspection */
$appHooks = include $destHookPath;
/** @noinspection PhpIncludeInspection */
$moduleHooks = include $srcHookPath;
$appHooks = ArrayUtils::array_diff_assoc_recursive($appHooks, $moduleHooks);
\file_put_contents($destHookPath, '<?php return ' . ArrayParser::serializeArray($appHooks) . ';', \LOCK_EX);
}
}

View File

@ -88,8 +88,8 @@ abstract class UninstallerAbstract
$definitions = \json_decode($content, true);
$builder = new SchemaBuilder($dbPool->get('schema'));
foreach ($definitions as $definition) {
$builder->dropTable($definition['table'] ?? '');
foreach ($definitions as $name => $definition) {
$builder->dropTable($name ?? '');
}
$builder->execute();

View File

@ -18,6 +18,7 @@ use phpOMS\Account\Account;
use phpOMS\Account\AccountStatus;
use phpOMS\Account\AccountType;
use phpOMS\Account\Group;
use phpOMS\Account\NullGroup;
use phpOMS\Account\PermissionAbstract;
use phpOMS\Account\PermissionType;
use phpOMS\Localization\L11nManager;
@ -107,6 +108,7 @@ class AccountTest extends \PHPUnit\Framework\TestCase
self::assertEquals(AccountType::USER, $account->getType());
self::assertEquals([], $account->getPermissions());
self::assertFalse($account->hasGroup(2));
self::assertInstanceOf('\DateTimeInterface', $account->getLastActive());
self::assertInstanceOf('\DateTimeImmutable', $account->createdAt);
@ -151,8 +153,9 @@ class AccountTest extends \PHPUnit\Framework\TestCase
$account = new Account();
$account->generatePassword('abcd');
$account->addGroup(new Group());
$account->addGroup(new NullGroup(2));
self::assertCount(1, $account->getGroups());
self::assertTrue($account->hasGroup(2));
}
/**

View File

@ -32,9 +32,9 @@ class WeightedTest extends \PHPUnit\Framework\TestCase
public function testNoOverlappingScheduling() : void
{
$jobs = [
new Job(10, new \DateTime('2000-01-01'), null, '0'),
new Job(20, new \DateTime('2003-01-01'), new \DateTime('2010-01-01'), 'A'),
new Job(50, new \DateTime('2001-01-01'), new \DateTime('2002-01-01'), 'B'),
new Job(10, new \DateTime('2000-01-01'), null, '0'),
new Job(100, new \DateTime('2006-01-01'), new \DateTime('2019-01-01'), 'C'),
new Job(200, new \DateTime('2002-01-01'), new \DateTime('2020-01-01'), 'D'),
new Job(300, new \DateTime('2004-01-01'), null, '1'),

View File

@ -17,6 +17,7 @@ namespace phpOMS\tests\Algorithm\PathFinding;
use phpOMS\Algorithm\PathFinding\AStar;
use phpOMS\Algorithm\PathFinding\AStarNode;
use phpOMS\Algorithm\PathFinding\Grid;
use phpOMS\Algorithm\PathFinding\Path;
use phpOMS\Algorithm\PathFinding\HeuristicType;
use phpOMS\Algorithm\PathFinding\MovementType;
@ -249,4 +250,23 @@ class AStarTest extends \PHPUnit\Framework\TestCase
[0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, ],
], $this->gridArray);
}
/**
* @testdox A invalid start or end node returns the grid
* @covers phpOMS\Algorithm\PathFinding\AStar
* @group framework
*/
public function testInvalidStartEndNode() : void
{
$grid = Grid::createGridFromArray($this->gridArray, AStarNode::class);
self::assertEquals(
new Path($grid),
$path = AStar::findPath(
999, 999,
-999, -999,
$grid, HeuristicType::EUCLIDEAN, MovementType::DIAGONAL_NO_OBSTACLE
)
);
}
}

View File

@ -15,6 +15,7 @@ declare(strict_types=1);
namespace phpOMS\tests\Algorithm\PathFinding;
use phpOMS\Algorithm\PathFinding\Grid;
use phpOMS\Algorithm\PathFinding\Path;
use phpOMS\Algorithm\PathFinding\HeuristicType;
use phpOMS\Algorithm\PathFinding\JumpPointNode;
use phpOMS\Algorithm\PathFinding\JumpPointSearch;
@ -249,4 +250,23 @@ class JumpPointSearchTest extends \PHPUnit\Framework\TestCase
[0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, ],
], $this->gridArray);
}
/**
* @testdox A invalid start or end node returns the grid
* @covers phpOMS\Algorithm\PathFinding\JumpPointSearch
* @group framework
*/
public function testInvalidStartEndNode() : void
{
$grid = Grid::createGridFromArray($this->gridArray, JumpPointNode::class);
self::assertEquals(
new Path($grid),
$path = JumpPointSearch::findPath(
999, 999,
-999, -999,
$grid, HeuristicType::EUCLIDEAN, MovementType::DIAGONAL_NO_OBSTACLE
)
);
}
}

View File

@ -38,6 +38,7 @@ class CycleSortTest extends \PHPUnit\Framework\TestCase
new NumericElement(1),
new NumericElement(4),
new NumericElement(2),
new NumericElement(2),
new NumericElement(8),
];
}
@ -62,11 +63,11 @@ class CycleSortTest extends \PHPUnit\Framework\TestCase
{
$newList = CycleSort::sort($this->list);
self::assertEquals(
[1, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,]
[1, 2, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,]
);
self::assertEquals(
[5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,]
[5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,]
);
}
@ -78,11 +79,11 @@ class CycleSortTest extends \PHPUnit\Framework\TestCase
{
$newList = CycleSort::sort($this->list, SortOrder::DESC);
self::assertEquals(
[8, 5, 4, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,]
[8, 5, 4, 2, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,]
);
self::assertEquals(
[5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,]
[5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,]
);
}
}

View File

@ -32,7 +32,7 @@ float $value = 0;
public function compare(SortableInterface $obj, int $order = SortOrder::ASC) : bool
{
return $order === SortOrder::ASC ? $this->value > $obj->value : $this->value < $obj->value;
return $order === SortOrder::ASC ? $this->value >= $obj->value : $this->value <= $obj->value;
}
public function equals(SortableInterface $obj) : bool

View File

@ -38,6 +38,7 @@ class StoogeSortTest extends \PHPUnit\Framework\TestCase
new NumericElement(1),
new NumericElement(4),
new NumericElement(2),
new NumericElement(2),
new NumericElement(8),
];
}
@ -62,11 +63,11 @@ class StoogeSortTest extends \PHPUnit\Framework\TestCase
{
$newList = StoogeSort::sort($this->list);
self::assertEquals(
[1, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,]
[1, 2, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,]
);
self::assertEquals(
[5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,]
[5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,]
);
}
@ -78,11 +79,11 @@ class StoogeSortTest extends \PHPUnit\Framework\TestCase
{
$newList = StoogeSort::sort($this->list, SortOrder::DESC);
self::assertEquals(
[8, 5, 4, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,]
[8, 5, 4, 2, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,]
);
self::assertEquals(
[5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,]
[5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,]
);
}
}

View File

@ -76,10 +76,11 @@ class ApplicationManagerTest extends \PHPUnit\Framework\TestCase
/**
* @covers phpOMS\Application\ApplicationManager
* @covers phpOMS\Application\InstallerAbstract
* @covers phpOMS\Application\UninstallerAbstract
* @covers phpOMS\Application\StatusAbstract
* @group framework
*/
public function testInstall() : void
public function testInstallUninstall() : void
{
self::assertTrue($this->appManager->install(__DIR__ . '/Testapp', __DIR__ . '/Apps/Testapp'));
self::assertTrue(\is_dir(__DIR__ . '/Apps/Testapp'));
@ -89,10 +90,32 @@ class ApplicationManagerTest extends \PHPUnit\Framework\TestCase
self::assertTrue(isset($apps['Testapp']));
$providing = $this->appManager->getProvidingForModule('Navigation');
Directory::delete(__DIR__ . '/Apps/Testapp');
self::assertTrue(isset($providing['Testapp']));
self::assertTrue(\in_array('Navigation', $providing['Testapp']));
$this->appManager->uninstall(__DIR__ . '/Apps/Testapp');
self::assertFalse(\is_dir(__DIR__ . '/Apps/Testapp'));
}
/**
* @testdox A module can be re-initialized
* @covers phpOMS\Application\ApplicationManager
* @covers phpOMS\Application\InstallerAbstract
* @covers phpOMS\Application\StatusAbstract
* @group framework
*/
public function testReInit() : void
{
Directory::delete(__DIR__ . '/Apps/Testapp');
$this->appManager->install(__DIR__ . '/Testapp', __DIR__ . '/Apps/Testapp');
$this->appManager->reInit(__DIR__ . '/Apps/Testapp');
self::assertEquals($r1 = include __DIR__ . '/Testapp/Admin/Install/Application/Routes.php', $r2 = include __DIR__ . '/Apps/Testapp/Routes.php');
self::assertEquals($h1 = include __DIR__ . '/Testapp/Admin/Install/Application/Hooks.php', $h2 = include __DIR__ . '/Apps/Testapp/Hooks.php');
Directory::delete(__DIR__ . '/Apps/Testapp');
}
/**
@ -105,12 +128,40 @@ class ApplicationManagerTest extends \PHPUnit\Framework\TestCase
self::assertFalse($this->appManager->install(__DIR__, __DIR__));
}
/**
* @covers phpOMS\Application\ApplicationManager
* @group framework
*/
public function testMissingInstallerPath() : void
{
self::assertFalse($this->appManager->install(__DIR__ . '/MissingInstaller', __DIR__ . '/Apps/MissingInstaller'));
}
/**
* @covers phpOMS\Application\ApplicationManager
* @group framework
*/
public function testMissingApplicationInfoFile() : void
{
self::assertFalse($this->appManager->install(__DIR__, __DIR__ . '/newapp', __DIR__ . '/Apps/newapp'));
self::assertFalse($this->appManager->install(__DIR__ . '/MissingInfo', __DIR__ . '/Apps/MissingInfo'));
}
/**
* @covers phpOMS\Application\ApplicationManager
* @group framework
*/
public function testInvalidSourceUninstallPath() : void
{
self::assertFalse($this->appManager->uninstall(__DIR__ . '/invalid', __DIR__));
self::assertFalse($this->appManager->uninstall(__DIR__, __DIR__));
}
/**
* @covers phpOMS\Application\ApplicationManager
* @group framework
*/
public function testMissingUninstallerPath() : void
{
self::assertFalse($this->appManager->uninstall(__DIR__ . '/Apps/MissingInstaller'));
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package tests
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\tests\Application;
require_once __DIR__ . '/../Autoloader.php';
use phpOMS\Application\InstallerAbstract;
/**
* @testdox phpOMS\tests\Application\InstallerAbstractTest: Application installer
*
* @internal
*/
class InstallerAbstractTest extends \PHPUnit\Framework\TestCase
{
protected InstallerAbstract $installer;
/**
* {@inheritdoc}
*/
protected function setUp() : void
{
$this->installer = new class() extends InstallerAbstract {
public const PATH = __DIR__ . '/Invalid';
};
}
/**
* @covers phpOMS\Application\InstallerAbstract
* @group framework
*/
public function testInvalidTheme() : void
{
$this->installer::installTheme(__DIR__, 'Invalid');
self::assertFalse(\is_dir(__DIR__ . '/css'));
}
}

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,51 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package tests
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\tests\Application;
require_once __DIR__ . '/../Autoloader.php';
use phpOMS\Application\StatusAbstract;
/**
* @testdox phpOMS\tests\Application\StatusAbstractTest: Application status
*
* @internal
*/
class StatusAbstractTest extends \PHPUnit\Framework\TestCase
{
protected StatusAbstract $status;
/**
* {@inheritdoc}
*/
protected function setUp() : void
{
$this->status = new class() extends StatusAbstract {
public const PATH = __DIR__ . '/Invalid';
};
}
/**
* @covers phpOMS\Application\StatusAbstract
* @group framework
*/
public function testInvalidAppPathActivation() : void
{
$this->status::activateRoutes();
$this->status::activateHooks();
self::assertFalse(\is_file(__DIR__ . '/Routes.php'));
}
}

View File

@ -4,7 +4,7 @@
*
* PHP Version 8.0
*
* @package Web\Api\Admin
* @package phpOMS\tests\Application\Apps\{APPNAME}\Admin
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
@ -19,7 +19,7 @@ use phpOMS\Application\InstallerAbstract;
/**
* Installer class.
*
* @package Web\Api\Admin
* @package phpOMS\tests\Application\Apps\{APPNAME}\Admin
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0

View File

@ -4,7 +4,7 @@
*
* PHP Version 8.0
*
* @package Web\Api\Admin
* @package phpOMS\tests\Application\Apps\{APPNAME}\Admin
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
@ -19,7 +19,7 @@ use phpOMS\Application\StatusAbstract;
/**
* Status class.
*
* @package Web\Api\Admin
* @package phpOMS\tests\Application\Apps\{APPNAME}\Admin
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0

View File

@ -0,0 +1,30 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package phpOMS\tests\Application\Apps\{APPNAME}\Admin
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\tests\Application\Apps\{APPNAME}\Admin;
use phpOMS\Application\UninstallerAbstract;
/**
* Uninstaller class.
*
* @package phpOMS\tests\Application\Apps\{APPNAME}\Admin
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class Uninstaller extends UninstallerAbstract
{
public const PATH = __DIR__;
}

View File

@ -26,4 +26,4 @@ use phpOMS\Module\ModuleAbstract;
*/
final class Controller extends ModuleAbstract
{
}
}

View File

@ -1,3 +1 @@
#test {
color: #000;
}
html {}

View File

@ -0,0 +1 @@
body {}

View File

@ -17,7 +17,8 @@
"description": "The administration module.",
"directory": "Admin",
"providing": {
"Navigation": "*"
"Navigation": "*",
"Invalid": "*"
},
"dependencies": []
}

View File

@ -83,7 +83,7 @@ class MetricsTest extends \PHPUnit\Framework\TestCase
}
/**
* @testdox The CLTV can be calculated using the migration model
* @testdox The CLTV can be calculated using the migration model
* @group framework
*/
public function testMigrationModel() : void
@ -101,7 +101,7 @@ class MetricsTest extends \PHPUnit\Framework\TestCase
}
/**
* @testdox The migration model can be used in order to determin which buying/none-buying customer group should receive a mailing
* @testdox The migration model can be used in order to determin which buying/none-buying customer group should receive a mailing
* @group framework
*/
public function testMailingSuccessEstimation() : void
@ -116,4 +116,17 @@ class MetricsTest extends \PHPUnit\Framework\TestCase
0.1
);
}
/**
* @testdox The probability of a customer buying can be calculated based on his previous purchase behavior
* @group framework
*/
public function testCustomerActiveProbability() : void
{
$purchases = 10;
$periods = 36; // months
self::assertEqualsWithDelta(0.017, Metrics::customerActiveProbability($purchases, $periods, 24), 0.001);
self::assertEqualsWithDelta(1.0, Metrics::customerActiveProbability($purchases, $periods, 36), 0.001);
}
}

View File

@ -127,6 +127,29 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals('testValAdd', $this->cache->get('addKey'));
}
public function testExists() : void
{
self::assertTrue($this->cache->add('addKey', 'testValAdd'));
self::assertTrue($this->cache->exists('addKey'));
self::assertFalse($this->cache->exists('invalid'));
}
public function testExpiredExists() : void
{
$this->cache->set('key2', 'testVal2', 2);
\sleep(1);
self::assertTrue($this->cache->exists('key2'));
self::assertFalse($this->cache->exists('key2', 0));
\sleep(3);
self::assertFalse($this->cache->exists('key2'));
}
public function testExistsInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertFalse($this->cache->exists('invalid'));
}
public function testGetLike() : void
{
$this->cache->set('key1', 'testVal1');
@ -134,20 +157,47 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d')));
}
public function testExpiredGetLike() : void
{
$this->cache->set('key1', 'testVal1', 2);
$this->cache->set('key2', 'testVal2', 2);
\sleep(1);
self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d')));
self::assertEquals([], $this->cache->getLike('key\d', 0));
\sleep(3);
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testGetLikeInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testIncrement() : void
{
$this->cache->set(1, 1);
$this->cache->increment(1, 2);
self::assertTrue($this->cache->increment(1, 2));
self::assertEquals(3, $this->cache->get(1));
}
public function testInvalidKeyIncrement() : void
{
self::assertFalse($this->cache->increment('invalid', 2));
}
public function testDecrement() : void
{
$this->cache->set(1, 3);
$this->cache->decrement(1, 2);
self::assertTrue($this->cache->decrement(1, 2));
self::assertEquals(1, $this->cache->get(1));
}
public function testInvalidKeyDecrement() : void
{
self::assertFalse($this->cache->decrement('invalid', 2));
}
public function testRename() : void
{
$this->cache->set('a', 'testVal1');
@ -163,6 +213,23 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testExpiredDelteLike() : void
{
$this->cache->set('key1', 'testVal1', 2);
$this->cache->set('key2', 'testVal2', 2);
\sleep(1);
self::assertTrue($this->cache->deleteLike('key\d', 0));
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testDeleteLikeInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertFalse($this->cache->deleteLike('key\d'));
}
public function testUpdateExpire() : void
{
$this->cache->set('key2', 'testVal2', 1);
@ -222,6 +289,11 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase
self::assertNull($this->cache->get('key4'));
}
public function testInvalidKeyDelete() : void
{
self::assertTrue($this->cache->delete('invalid'));
}
/**
* @testdox The cache correctly handles general cache information
* @covers phpOMS\DataStorage\Cache\Connection\FileCache<extended>

View File

@ -128,6 +128,28 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase
self::assertEquals('testValAdd', $this->cache->get('addKey'));
}
public function testExists() : void
{
self::assertTrue($this->cache->add('addKey', 'testValAdd'));
self::assertTrue($this->cache->exists('addKey'));
}
public function testExpiredExists() : void
{
$this->cache->set('key2', 'testVal2', 2);
\sleep(1);
self::assertTrue($this->cache->exists('key2'));
self::assertFalse($this->cache->exists('key2', 0));
\sleep(3);
self::assertFalse($this->cache->exists('key2'));
}
public function testExistsInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertFalse($this->cache->exists('invalid'));
}
public function testGetLike() : void
{
$this->cache->set('key1', 'testVal1');
@ -135,20 +157,47 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase
self::assertEquals(['testVal1', 'testVal2'], $this->cache->getLike('key\d'));
}
public function testExpiredGetLike() : void
{
$this->cache->set('key1', 'testVal1', 2);
$this->cache->set('key2', 'testVal2', 2);
\sleep(1);
self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d')));
self::assertEquals([], $this->cache->getLike('key\d', 0));
\sleep(3);
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testGetLikeInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testIncrement() : void
{
$this->cache->set(1, 1);
$this->cache->increment(1, 2);
self::assertTrue($this->cache->increment(1, 2));
self::assertEquals(3, $this->cache->get(1));
}
public function testInvalidKeyIncrement() : void
{
self::assertFalse($this->cache->increment('invalid', 2));
}
public function testDecrement() : void
{
$this->cache->set(1, 3);
$this->cache->decrement(1, 2);
self::assertTrue($this->cache->decrement(1, 2));
self::assertEquals(1, $this->cache->get(1));
}
public function testInvalidKeyDecrement() : void
{
self::assertFalse($this->cache->decrement('invalid', 2));
}
public function testRename() : void
{
$this->cache->set('a', 'testVal1');
@ -164,6 +213,12 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testDeleteLikeInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertFalse($this->cache->deleteLike('key\d'));
}
public function testUpdateExpire() : void
{
$this->cache->set('key2', 'testVal2', 1);
@ -223,6 +278,11 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase
self::assertNull($this->cache->get('key4'));
}
public function testInvalidKeyDelete() : void
{
self::assertTrue($this->cache->delete('invalid'));
}
/**
* @testdox The cache correctly handles general cache information
* @covers phpOMS\DataStorage\Cache\Connection\MemCached<extended>

View File

@ -78,7 +78,7 @@ final class NullCacheTest extends \PHPUnit\Framework\TestCase
public function testDecrement() : void
{
$this->cache->increment(1, 1);
$this->cache->decrement(1, 1);
self::assertNull($this->cache->get(1));
}

View File

@ -124,6 +124,28 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals('testValAdd', $this->cache->get('addKey'));
}
public function testExists() : void
{
self::assertTrue($this->cache->add('addKey', 'testValAdd'));
self::assertTrue($this->cache->exists('addKey'));
}
public function testExpiredExists() : void
{
$this->cache->set('key2', 'testVal2', 2);
\sleep(1);
self::assertTrue($this->cache->exists('key2'));
self::assertFalse($this->cache->exists('key2', 0));
\sleep(3);
self::assertFalse($this->cache->exists('key2'));
}
public function testExistsInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertFalse($this->cache->exists('invalid'));
}
public function testGetLike() : void
{
$this->cache->set('key1', 'testVal1');
@ -131,20 +153,47 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals(['testVal1', 'testVal2'], $this->cache->getLike('key\d'));
}
public function testExpiredGetLike() : void
{
$this->cache->set('key1', 'testVal1', 2);
$this->cache->set('key2', 'testVal2', 2);
\sleep(1);
self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d')));
self::assertEquals([], $this->cache->getLike('key\d', 0));
\sleep(3);
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testGetLikeInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testIncrement() : void
{
$this->cache->set(1, 1);
$this->cache->increment(1, 2);
self::assertTrue($this->cache->increment(1, 2));
self::assertEquals(3, $this->cache->get(1));
}
public function testInvalidKeyIncrement() : void
{
self::assertFalse($this->cache->increment('invalid', 2));
}
public function testDecrement() : void
{
$this->cache->set(1, 3);
$this->cache->decrement(1, 2);
self::assertTrue($this->cache->decrement(1, 2));
self::assertEquals(1, $this->cache->get(1));
}
public function testInvalidKeyDecrement() : void
{
self::assertFalse($this->cache->decrement('invalid', 2));
}
public function testRename() : void
{
$this->cache->set('a', 'testVal1');
@ -160,6 +209,12 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase
self::assertEquals([], $this->cache->getLike('key\d'));
}
public function testDeleteLikeInvalidStatus() : void
{
TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE);
self::assertFalse($this->cache->deleteLike('key\d'));
}
public function testUpdateExpire() : void
{
$this->cache->set('key2', 'testVal2', 1);
@ -219,6 +274,11 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase
self::assertNull($this->cache->get('key4'));
}
public function testInvalidKeyDelete() : void
{
self::assertTrue($this->cache->delete('invalid'));
}
/**
* @testdox The cache correctly handles general cache information
* @covers phpOMS\DataStorage\Cache\Connection\RedisCache<extended>

View File

@ -108,29 +108,19 @@ class BuilderTest extends \PHPUnit\Framework\TestCase
);*/
}
/**
* @testdox The grammar correctly deletes a table
* @covers phpOMS\DataStorage\Database\Schema\Grammar\MysqlGrammar<extended>
* @group framework
*/
public function testMysqlCreateFromSchema() : void
{
Builder::createFromSchema(
\json_decode(
\file_get_contents(__DIR__ . '/Grammar/testSchema.json'), true
)['test_foreign'],
$this->con
)->execute();
$query = new Builder($this->con);
$sql = 'DROP TABLE `test`, `test_foreign`;';
Builder::createFromSchema(
\json_decode(
\file_get_contents(__DIR__ . '/Grammar/testSchema.json'), true
)['test'],
$this->con
)->execute();
$table = new Builder($this->con);
$tables = $table->selectTables()->execute()->fetchAll(\PDO::FETCH_COLUMN);
self::assertContains('test', $tables);
self::assertContains('test_foreign', $tables);
$delete = new Builder($this->con);
$delete->dropTable('test')->execute();
$delete->dropTable('test_foreign')->execute();
self::assertEquals(
$sql,
$query->dropTable('test')->dropTable('test_foreign')->toSql()
);
}
}

View File

@ -15,7 +15,7 @@ declare(strict_types=1);
namespace phpOMS\tests\DataStorage\Database\Schema\Grammar;
use phpOMS\DataStorage\Database\Connection\MysqlConnection;
use phpOMS\DataStorage\Database\Schema\Builder as SchemaBuilder;
use phpOMS\DataStorage\Database\Schema\Builder;
use phpOMS\DataStorage\Database\Schema\Grammar\MysqlGrammar;
use phpOMS\Utils\ArrayUtils;
use phpOMS\Utils\TestUtils;
@ -58,15 +58,15 @@ class MysqlGrammarTest extends \PHPUnit\Framework\TestCase
{
$definitions = \json_decode(\file_get_contents(__DIR__ . '/testSchema.json'), true);
foreach ($definitions as $definition) {
SchemaBuilder::createFromSchema($definition, $this->con)->execute();
Builder::createFromSchema($definition, $this->con)->execute();
}
$table = new SchemaBuilder($this->con);
$table = new Builder($this->con);
$tables = $table->selectTables()->execute()->fetchAll(\PDO::FETCH_COLUMN);
self::assertContains('test', $tables);
self::assertContains('test_foreign', $tables);
$field = new SchemaBuilder($this->con);
$field = new Builder($this->con);
$fields = $field->selectFields('test')->execute()->fetchAll();
foreach ($definitions['test']['fields'] as $key => $field) {
@ -75,6 +75,11 @@ class MysqlGrammarTest extends \PHPUnit\Framework\TestCase
'Couldn\'t find "' . $key . '" in array'
);
}
$delete = new Builder($this->con);
$delete->dropTable('test')
->dropTable('test_foreign')
->execute();
}
/**
@ -84,13 +89,22 @@ class MysqlGrammarTest extends \PHPUnit\Framework\TestCase
*/
public function testDelete() : void
{
$table = new SchemaBuilder($this->con);
$definitions = \json_decode(\file_get_contents(__DIR__ . '/testSchema.json'), true);
foreach ($definitions as $definition) {
Builder::createFromSchema($definition, $this->con)->execute();
}
$table = new Builder($this->con);
$tables = $table->selectTables()->execute()->fetchAll(\PDO::FETCH_COLUMN);
self::assertContains('test', $tables);
self::assertContains('test_foreign', $tables);
$delete = new SchemaBuilder($this->con);
$delete->dropTable('test')->execute();
$delete->dropTable('test_foreign')->execute();
$delete = new Builder($this->con);
$delete->dropTable('test')
->dropTable('test_foreign')
->execute();
$table = new Builder($this->con);
$tables = $table->selectTables()->execute()->fetchAll();
self::assertNotContains('test', $tables);
self::assertNotContains('test_foreign', $tables);

View File

@ -91,6 +91,16 @@ class L11nManagerTest extends \PHPUnit\Framework\TestCase
self::assertEquals('Test strin&amp;g2', $this->l11nManager->getHtml('en', 'Admin', 'RandomThemeDoesNotMatterAlreadyLoaded', 'Test2'));
}
/**
* @testdox An invalid localization source returns an error string
* @covers phpOMS\Localization\L11nManager
* @group framework
*/
public function testInvalidControllerSource() : void
{
self::assertEquals('ERROR-Key', $this->l11nManager->getText('en', 'InvalidSource', 'RandomThemeDoesNotMatterAlreadyLoaded', 'Key'));
}
/**
* @testdox Language data can be loaded from a file
* @covers phpOMS\Localization\L11nManager

View File

@ -0,0 +1,32 @@
<?php
/**
* Orange Management
*
* PHP Version 8.0
*
* @package tests
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\tests\Math\Statistic\Forecast;
use phpOMS\Math\Statistic\Forecast\Forecasts;
/**
* @internal
*/
class ForecastsTest extends \PHPUnit\Framework\TestCase
{
public function testForecastInterval() : void
{
self::assertEqualsWithDelta(
[519.3, 543.6],
Forecasts::getForecastInteval(531.48, 6.21, 1.96),
0.1
);
}
}

View File

@ -43,7 +43,7 @@ class ModuleAbstractTest extends \PHPUnit\Framework\TestCase
{
$this->module = new class() extends ModuleAbstract
{
public const PATH = __DIR__ . '/';
public const PATH = __DIR__ . '/Test';
const VERSION = '1.2.3';

View File

@ -211,6 +211,18 @@ class ModuleManagerTest extends \PHPUnit\Framework\TestCase
/**
* @testdox A module can be re-initialized
* @covers phpOMS\Module\ModuleManager
* @group framework
*/
public function testInvalidModuleReInit() : void
{
$this->moduleManager->reInit('Invalid');
self::assertFalse($this->moduleManager->isActive('Invalid'));
}
/**
* @testdox A module can be re-initialized
* @covers phpOMS\Module\ModuleManager
* @covers phpOMS\Module\InstallerAbstract
* @covers phpOMS\Module\StatusAbstract
* @group framework
*/