diff --git a/README.md b/README.md index f857434..a00d40d 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ This SDK is compatible with [Featurevisor](https://featurevisor.com/) v2.0 proje - [Updating datafile](#updating-datafile) - [Interval-based update](#interval-based-update) - [Logging](#logging) - - [Levels](#levels) - [Customizing levels](#customizing-levels) - [Handler](#handler) - [Events](#events) @@ -354,21 +353,14 @@ Here's an example of using interval-based update: ## Logging By default, Featurevisor SDKs will print out logs to the console for `info` level and above. - -### Levels - -These are all the available log levels: - -- `error` -- `warn` -- `info` -- `debug` +Featurevisor PHP-SDK by default uses [PSR-3 standard](https://www.php-fig.org/psr/psr-3/) simple implementation. +You can also choose from many mature implementations like e.g. [Monolog](https://github.com/Seldaek/monolog) ### Customizing levels If you choose `debug` level to make the logs more verbose, you can set it at the time of SDK initialization. -Setting `debug` level will print out all logs, including `info`, `warn`, and `error` levels. +Setting `debug` level will print out all logs, including `info`, `warning`, and `error` levels. ```php use function Featurevisor\createInstance; diff --git a/composer.json b/composer.json index baaa619..d4abe64 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,9 @@ "Featurevisor\\Tests\\": "tests/" } }, - "require": {}, + "require": { + "psr/log": "^2.0" + }, "require-dev": { "php": "^8.0", "phpunit/phpunit": "^10" diff --git a/composer.lock b/composer.lock index 9723e28..3fb8c29 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,59 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c9e720f96bb8c3f6b18a731e4889d22e", - "packages": [], + "content-hash": "4912541a760f78692b60a4308b52a362", + "packages": [ + { + "name": "psr/log", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/2.0.0" + }, + "time": "2021-07-14T16:41:46+00:00" + } + ], "packages-dev": [ { "name": "myclabs/deep-copy", @@ -1641,11 +1692,13 @@ } ], "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, - "prefer-stable": false, + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": true, "prefer-lowest": false, - "platform": {}, - "platform-dev": {}, - "plugin-api-version": "2.6.0" + "platform": [], + "platform-dev": { + "php": "^8.0" + }, + "plugin-api-version": "2.2.0" } diff --git a/featurevisor b/featurevisor index 4e07e1e..12606c1 100755 --- a/featurevisor +++ b/featurevisor @@ -3,7 +3,10 @@ require __DIR__ . '/vendor/autoload.php'; +use Featurevisor\DatafileReader; +use Psr\Log\LogLevel; use function Featurevisor\createInstance; +use function Featurevisor\createLogger; /** * CLI Options @@ -93,11 +96,11 @@ function buildDatafiles(string $featurevisorProjectPath, array $environments): a } function getLoggerLevel(array $cliOptions): string { - $level = 'warn'; + $level = LogLevel::WARNING; if ($cliOptions['verbose'] === true) { - $level = 'debug'; + $level = LogLevel::DEBUG; } else if ($cliOptions['quiet'] === true) { - $level = 'error'; + $level = LogLevel::ERROR; } return $level; } @@ -252,9 +255,9 @@ function testSegment(array $assertion, array $segment, string $level): array { 'segments' => [] ]; - $datafileReader = new \Featurevisor\DatafileReader([ + $datafileReader = new DatafileReader([ 'datafile' => $datafile, - 'logger' => \Featurevisor\createLogger([ + 'logger' => createLogger([ 'level' => $level, ]), ]); @@ -303,7 +306,9 @@ function test(array $cliOptions) { $datafile = $datafilesByEnvironment[$environment]; $sdkInstancesByEnvironment[$environment] = createInstance([ 'datafile' => $datafile, - 'logLevel' => $level, + 'logger' => createLogger([ + 'level' => $level, + ]), 'hooks' => [ [ 'name' => 'tester-hook', @@ -340,7 +345,9 @@ function test(array $cliOptions) { $datafile = $datafilesByEnvironment[$environment]; $f = createInstance([ 'datafile' => $datafile, - 'logLevel' => $level, + 'logger' => createLogger([ + 'level' => $level, + ]), 'hooks' => [ [ 'name' => 'tester-hook', @@ -415,7 +422,9 @@ function benchmark(array $cliOptions) { $f = createInstance([ 'datafile' => $datafilesByEnvironment[$cliOptions['environment']], - 'logLevel' => $level, + 'logger' => createLogger([ + 'level' => $level, + ]), ]); $value = null; @@ -474,7 +483,9 @@ function assessDistribution(array $cliOptions) { $f = createInstance([ 'datafile' => $datafilesByEnvironment[$cliOptions['environment']], - 'logLevel' => $level, + 'logger' => createLogger([ + 'level' => $level, + ]), ]); $value = null; diff --git a/src/DatafileReader.php b/src/DatafileReader.php index f453461..efbb01f 100644 --- a/src/DatafileReader.php +++ b/src/DatafileReader.php @@ -2,13 +2,15 @@ namespace Featurevisor; +use Psr\Log\LoggerInterface; + class DatafileReader { private string $schemaVersion; private string $revision; private array $segments; private array $features; - private Logger $logger; + private LoggerInterface $logger; private array $regexCache; public function __construct(array $options) @@ -152,12 +154,10 @@ public function allConditionsAreMatched($conditions, array $context): bool try { return Conditions::conditionIsMatched($conditions, $context, $getRegex); } catch (\Exception $e) { - $this->logger->warn($e->getMessage(), [ - 'error' => $e, - 'details' => [ - 'condition' => $conditions, - 'context' => $context, - ], + $this->logger->warning($e->getMessage(), [ + 'exception' => $e, + 'condition' => $conditions, + 'context' => $context, ]); return false; } diff --git a/src/EvaluateNotFound.php b/src/EvaluateNotFound.php index 6115958..dea8a15 100644 --- a/src/EvaluateNotFound.php +++ b/src/EvaluateNotFound.php @@ -2,6 +2,8 @@ namespace Featurevisor; +use Psr\Log\LoggerInterface; + class EvaluateNotFound { public static function evaluate(array $options): array @@ -9,6 +11,7 @@ public static function evaluate(array $options): array $type = $options['type']; $featureKey = $options['featureKey']; $variableKey = $options['variableKey'] ?? null; + /** @var LoggerInterface $logger */ $logger = $options['logger']; $datafileReader = $options['datafileReader']; @@ -24,7 +27,7 @@ public static function evaluate(array $options): array 'reason' => Evaluation::FEATURE_NOT_FOUND ]; - $logger->warn('feature not found', $result['evaluation']); + $logger->warning('feature not found', $result['evaluation']); return $result; } @@ -33,7 +36,7 @@ public static function evaluate(array $options): array // feature: deprecated if ($type === 'flag' && ($feature['deprecated'] ?? false)) { - $logger->warn('feature is deprecated', ['featureKey' => $featureKey]); + $logger->warning('feature is deprecated', ['featureKey' => $featureKey]); } // variableSchema @@ -53,15 +56,14 @@ public static function evaluate(array $options): array 'variableKey' => $variableKey ]; - $logger->warn('variable schema not found', $result['evaluation']); + $logger->warning('variable schema not found', $result['evaluation']); return $result; } $result['variableSchema'] = $variableSchema; - if ($variableSchema['deprecated'] ?? false) { - $logger->warn('variable is deprecated', [ + $logger->warning('variable is deprecated', [ 'featureKey' => $featureKey, 'variableKey' => $variableKey ]); @@ -76,7 +78,7 @@ public static function evaluate(array $options): array 'reason' => Evaluation::NO_VARIATIONS ]; - $logger->warn('no variations', $result['evaluation']); + $logger->warning('no variations', $result['evaluation']); return $result; } diff --git a/src/HooksManager.php b/src/HooksManager.php index c4a5849..6498c5f 100644 --- a/src/HooksManager.php +++ b/src/HooksManager.php @@ -2,10 +2,12 @@ namespace Featurevisor; +use Psr\Log\LoggerInterface; + class HooksManager { private array $hooks = []; - private Logger $logger; + private LoggerInterface $logger; public function __construct(array $options) { diff --git a/src/Instance.php b/src/Instance.php index 13455d9..7f00971 100644 --- a/src/Instance.php +++ b/src/Instance.php @@ -2,10 +2,13 @@ namespace Featurevisor; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; + class Instance { private array $context = []; - private Logger $logger; + private LoggerInterface $logger; private ?array $sticky = null; private DatafileReader $datafileReader; private HooksManager $hooksManager; @@ -15,9 +18,7 @@ public function __construct(array $options = []) { // from options $this->context = $options['context'] ?? []; - $this->logger = $options['logger'] ?? createLogger([ - 'level' => $options['logLevel'] ?? Logger::DEFAULT_LEVEL - ]); + $this->logger = $options['logger'] ?? new NullLogger(); $this->hooksManager = new HooksManager([ 'hooks' => $options['hooks'] ?? [], 'logger' => $this->logger @@ -49,11 +50,6 @@ public function __construct(array $options = []) $this->logger->info('Featurevisor SDK initialized'); } - public function setLogLevel(string $level): void - { - $this->logger->setLevel($level); - } - public function setDatafile($datafile): void { try { @@ -69,7 +65,7 @@ public function setDatafile($datafile): void $this->logger->info('datafile set', $details); $this->emitter->trigger('datafile_set', $details); } catch (\Exception $e) { - $this->logger->error('could not parse datafile', ['error' => $e->getMessage()]); + $this->logger->error('could not parse datafile', ['error' => $e->getMessage(), 'exception' => $e]);; } } @@ -211,7 +207,9 @@ public function getVariation(string $featureKey, array $context = [], array $opt return null; } catch (\Exception $e) { - $this->logger->error('getVariation', [ + $this->logger->error($e->getMessage(), [ + 'exception' => $e, + 'action' => 'getVariation', 'featureKey' => $featureKey, 'error' => $e->getMessage() ]); @@ -251,10 +249,11 @@ public function getVariable(string $featureKey, string $variableKey, array $cont } return null; } catch (\Exception $e) { - $this->logger->error('getVariable', [ + $this->logger->error($e->getMessage(), [ + 'exception' => $e, + 'action' => 'getVariable', 'featureKey' => $featureKey, 'variableKey' => $variableKey, - 'error' => $e->getMessage() ]); return null; } diff --git a/src/Logger.php b/src/Logger.php index ca3ddc3..21c8e51 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -2,26 +2,56 @@ namespace Featurevisor; -class Logger +use Closure; +use InvalidArgumentException; +use Psr\Log\LoggerInterface; +use Psr\Log\LoggerTrait; +use Psr\Log\LogLevel; +use Stringable; + +class Logger implements LoggerInterface { - public const ALL_LEVELS = ['fatal', 'error', 'warn', 'info', 'debug']; - public const DEFAULT_LEVEL = 'info'; + use LoggerTrait; + private const MSG_PREFIX = '[Featurevisor]'; + + private const ALL_LEVELS = [ + LogLevel::EMERGENCY, + LogLevel::ALERT, + LogLevel::CRITICAL, + LogLevel::ERROR, + LogLevel::WARNING, + LogLevel::NOTICE, + LogLevel::INFO, + LogLevel::DEBUG, + ]; + + public const DEFAULT_LEVEL = LogLevel::INFO; private string $level; - private $handler; + private Closure $handler; + /** + * @param array{ + * level?: string, + * handler?: Closure, + * } $options + */ public function __construct(array $options = []) { $this->level = $options['level'] ?? self::DEFAULT_LEVEL; - $this->handler = $options['handler'] ?? [self::class, 'defaultLogHandler']; + $this->handler = $options['handler'] ?? self::defaultLogHandler(...); } public function setLevel(string $level): void { + if (!in_array($level, self::ALL_LEVELS, true)) { + throw new InvalidArgumentException('Invalid log level'); + } + $this->level = $level; } - public function log(string $level, string $message, array $details = []): void + public function log($level, string|Stringable $message, array $context = []): void { $shouldHandle = array_search($this->level, self::ALL_LEVELS) >= array_search($level, self::ALL_LEVELS); @@ -29,48 +59,22 @@ public function log(string $level, string $message, array $details = []): void return; } - // Pass null for details if not provided, to match TypeScript - $detailsToPass = empty($details) ? null : $details; - call_user_func($this->handler, $level, $message, $detailsToPass); - } - - public function debug(string $message, array $details = []): void - { - $this->log('debug', $message, $details); - } - - public function info(string $message, array $details = []): void - { - $this->log('info', $message, $details); - } - - public function warn(string $message, array $details = []): void - { - $this->log('warn', $message, $details); - } - - public function error(string $message, array $details = []): void - { - $this->log('error', $message, $details); + ($this->handler)($level, self::MSG_PREFIX.' '.$message, $context); } public static function defaultLogHandler(string $level, string $message, $details = null): void { - $method = 'log'; - - if ($level === 'info') { - $method = 'info'; - } elseif ($level === 'warn') { - $method = 'warn'; - } elseif ($level === 'error') { - $method = 'error'; + if (STDOUT === false) { + return; } - $prefix = '[Featurevisor]'; - echo "$prefix $message"; - if (!is_null($details)) { - echo ' ' . json_encode($details); - } - echo PHP_EOL; + fwrite( + STDOUT, + sprintf( + '%s %s', + $message, + $details !== null ? json_encode($details, JSON_THROW_ON_ERROR) : null + ) . PHP_EOL + ); } } diff --git a/tests/BucketerTest.php b/tests/BucketerTest.php index 51b5e41..e14f1f8 100644 --- a/tests/BucketerTest.php +++ b/tests/BucketerTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Featurevisor\Bucketer; +use Psr\Log\LogLevel; use function Featurevisor\createLogger; class BucketerTest extends TestCase { @@ -13,8 +14,8 @@ public function testGetBucketedNumberRange() { $keys = ['foo', 'bar', 'baz', '123adshlk348-93asdlk']; foreach ($keys as $key) { $n = Bucketer::getBucketedNumber($key); - $this->assertGreaterThanOrEqual(0, $n); - $this->assertLessThanOrEqual(Bucketer::MAX_BUCKETED_NUMBER, $n); + self::assertGreaterThanOrEqual(0, $n); + self::assertLessThanOrEqual(Bucketer::MAX_BUCKETED_NUMBER, $n); } } @@ -30,68 +31,68 @@ public function testGetBucketedNumberKnownKeys() { ]; foreach ($expectedResults as $key => $expected) { $n = Bucketer::getBucketedNumber($key); - $this->assertEquals($expected, $n, "Bucketed number for '$key' should be $expected, got $n"); + self::assertEquals($expected, $n, "Bucketed number for '$key' should be $expected, got $n"); } } public function testGetBucketKeyIsFunction() { - $this->assertTrue(is_callable([Bucketer::class, 'getBucketKey'])); + self::assertTrue(is_callable([Bucketer::class, 'getBucketKey'])); } public function testGetBucketKeyPlain() { $featureKey = 'test-feature'; $bucketBy = 'userId'; $context = ['userId' => '123', 'browser' => 'chrome']; - $logger = createLogger(['level' => 'warn']); + $logger = createLogger(['level' => LogLevel::WARNING]); $bucketKey = Bucketer::getBucketKey([ 'featureKey' => $featureKey, 'bucketBy' => $bucketBy, 'context' => $context, 'logger' => $logger, ]); - $this->assertEquals('123.test-feature', $bucketKey); + self::assertEquals('123.test-feature', $bucketKey); } public function testGetBucketKeyPlainMissingContext() { $featureKey = 'test-feature'; $bucketBy = 'userId'; $context = ['browser' => 'chrome']; - $logger = createLogger(['level' => 'warn']); + $logger = createLogger(['level' => LogLevel::WARNING]); $bucketKey = Bucketer::getBucketKey([ 'featureKey' => $featureKey, 'bucketBy' => $bucketBy, 'context' => $context, 'logger' => $logger, ]); - $this->assertEquals('test-feature', $bucketKey); + self::assertEquals('test-feature', $bucketKey); } public function testGetBucketKeyAndAllPresent() { $featureKey = 'test-feature'; $bucketBy = ['organizationId', 'userId']; $context = ['organizationId' => '123', 'userId' => '234', 'browser' => 'chrome']; - $logger = createLogger(['level' => 'warn']); + $logger = createLogger(['level' => LogLevel::WARNING]); $bucketKey = Bucketer::getBucketKey([ 'featureKey' => $featureKey, 'bucketBy' => $bucketBy, 'context' => $context, 'logger' => $logger, ]); - $this->assertEquals('123.234.test-feature', $bucketKey); + self::assertEquals('123.234.test-feature', $bucketKey); } public function testGetBucketKeyAndPartial() { $featureKey = 'test-feature'; $bucketBy = ['organizationId', 'userId']; $context = ['organizationId' => '123', 'browser' => 'chrome']; - $logger = createLogger(['level' => 'warn']); + $logger = createLogger(['level' => LogLevel::WARNING]); $bucketKey = Bucketer::getBucketKey([ 'featureKey' => $featureKey, 'bucketBy' => $bucketBy, 'context' => $context, 'logger' => $logger, ]); - $this->assertEquals('123.test-feature', $bucketKey); + self::assertEquals('123.test-feature', $bucketKey); } public function testGetBucketKeyAndDotSeparated() { @@ -102,7 +103,7 @@ public function testGetBucketKeyAndDotSeparated() { 'user' => ['id' => '234'], 'browser' => 'chrome', ]; - $logger = createLogger(['level' => 'warn']); + $logger = createLogger(['level' => LogLevel::WARNING]); $bucketKey = Bucketer::getBucketKey([ 'featureKey' => $featureKey, 'bucketBy' => $bucketBy, @@ -111,36 +112,36 @@ public function testGetBucketKeyAndDotSeparated() { ]); // Note: The current PHP implementation does not support dot-separated paths in getValueFromContext // If you add support, this should pass: - // $this->assertEquals('123.234.test-feature', $bucketKey); + // self::assertEquals('123.234.test-feature', $bucketKey); // For now, it will be '123.test-feature' (since 'user.id' is not resolved) - $this->assertEquals('123.test-feature', $bucketKey); + self::assertEquals('123.test-feature', $bucketKey); } public function testGetBucketKeyOrFirstAvailable() { $featureKey = 'test-feature'; $bucketBy = ['or' => ['userId', 'deviceId']]; $context = ['deviceId' => 'deviceIdHere', 'userId' => '234', 'browser' => 'chrome']; - $logger = createLogger(['level' => 'warn']); + $logger = createLogger(['level' => LogLevel::WARNING]); $bucketKey = Bucketer::getBucketKey([ 'featureKey' => $featureKey, 'bucketBy' => $bucketBy, 'context' => $context, 'logger' => $logger, ]); - $this->assertEquals('234.test-feature', $bucketKey); + self::assertEquals('234.test-feature', $bucketKey); } public function testGetBucketKeyOrOnlyDeviceId() { $featureKey = 'test-feature'; $bucketBy = ['or' => ['userId', 'deviceId']]; $context = ['deviceId' => 'deviceIdHere', 'browser' => 'chrome']; - $logger = createLogger(['level' => 'warn']); + $logger = createLogger(['level' => LogLevel::WARNING]); $bucketKey = Bucketer::getBucketKey([ 'featureKey' => $featureKey, 'bucketBy' => $bucketBy, 'context' => $context, 'logger' => $logger, ]); - $this->assertEquals('deviceIdHere.test-feature', $bucketKey); + self::assertEquals('deviceIdHere.test-feature', $bucketKey); } } diff --git a/tests/ChildTest.php b/tests/ChildTest.php index e506142..8aeb75f 100644 --- a/tests/ChildTest.php +++ b/tests/ChildTest.php @@ -148,15 +148,15 @@ public function testCreateChildInstanceAndAllBehaviors() { 'context' => [ 'appVersion' => '1.0.0' ], ]); - $this->assertNotNull($f); - $this->assertEquals(['appVersion' => '1.0.0'], $f->getContext()); + self::assertNotNull($f); + self::assertEquals(['appVersion' => '1.0.0'], $f->getContext()); $childF = $f->spawn([ 'userId' => '123', 'country' => 'nl', ]); - $this->assertNotNull($childF); - $this->assertEquals([ + self::assertNotNull($childF); + self::assertEquals([ 'appVersion' => '1.0.0', 'userId' => '123', 'country' => 'nl', @@ -168,50 +168,50 @@ public function testCreateChildInstanceAndAllBehaviors() { }); $childF->setContext(['country' => 'be']); - $this->assertEquals([ + self::assertEquals([ 'appVersion' => '1.0.0', 'userId' => '123', 'country' => 'be', ], $childF->getContext()); - $this->assertTrue($childF->isEnabled('test')); - $this->assertEquals('control', $childF->getVariation('test')); + self::assertTrue($childF->isEnabled('test')); + self::assertEquals('control', $childF->getVariation('test')); - $this->assertEquals('black', $childF->getVariable('test', 'color')); - $this->assertEquals('black', $childF->getVariableString('test', 'color')); + self::assertEquals('black', $childF->getVariable('test', 'color')); + self::assertEquals('black', $childF->getVariableString('test', 'color')); - $this->assertEquals(false, $childF->getVariable('test', 'showSidebar')); - $this->assertEquals(false, $childF->getVariableBoolean('test', 'showSidebar')); + self::assertEquals(false, $childF->getVariable('test', 'showSidebar')); + self::assertEquals(false, $childF->getVariableBoolean('test', 'showSidebar')); - $this->assertEquals('sidebar title', $childF->getVariable('test', 'sidebarTitle')); - $this->assertEquals('sidebar title', $childF->getVariableString('test', 'sidebarTitle')); + self::assertEquals('sidebar title', $childF->getVariable('test', 'sidebarTitle')); + self::assertEquals('sidebar title', $childF->getVariableString('test', 'sidebarTitle')); - $this->assertEquals(0, $childF->getVariable('test', 'count')); - $this->assertEquals(0, $childF->getVariableInteger('test', 'count')); + self::assertEquals(0, $childF->getVariable('test', 'count')); + self::assertEquals(0, $childF->getVariableInteger('test', 'count')); - $this->assertEquals(9.99, $childF->getVariable('test', 'price')); - $this->assertEquals(9.99, $childF->getVariableDouble('test', 'price')); + self::assertEquals(9.99, $childF->getVariable('test', 'price')); + self::assertEquals(9.99, $childF->getVariableDouble('test', 'price')); - $this->assertEquals(['paypal', 'creditcard'], $childF->getVariable('test', 'paymentMethods')); - $this->assertEquals(['paypal', 'creditcard'], $childF->getVariableArray('test', 'paymentMethods')); + self::assertEquals(['paypal', 'creditcard'], $childF->getVariable('test', 'paymentMethods')); + self::assertEquals(['paypal', 'creditcard'], $childF->getVariableArray('test', 'paymentMethods')); - $this->assertEquals(['key' => 'value'], $childF->getVariable('test', 'flatConfig')); - $this->assertEquals(['key' => 'value'], $childF->getVariableObject('test', 'flatConfig')); + self::assertEquals(['key' => 'value'], $childF->getVariable('test', 'flatConfig')); + self::assertEquals(['key' => 'value'], $childF->getVariableObject('test', 'flatConfig')); - $this->assertEquals(['key' => ['nested' => 'value']], $childF->getVariable('test', 'nestedConfig')); - $this->assertEquals(['key' => ['nested' => 'value']], $childF->getVariableJSON('test', 'nestedConfig')); + self::assertEquals(['key' => ['nested' => 'value']], $childF->getVariable('test', 'nestedConfig')); + self::assertEquals(['key' => ['nested' => 'value']], $childF->getVariableJSON('test', 'nestedConfig')); - $this->assertTrue($contextUpdated); + self::assertTrue($contextUpdated); $unsubscribeContext(); - $this->assertFalse($childF->isEnabled('newFeature')); + self::assertFalse($childF->isEnabled('newFeature')); $childF->setSticky([ 'newFeature' => [ 'enabled' => true ] ]); - $this->assertTrue($childF->isEnabled('newFeature')); + self::assertTrue($childF->isEnabled('newFeature')); $allEvaluations = $childF->getAllEvaluations(); - $this->assertEquals(['test', 'anotherTest'], array_keys($allEvaluations)); + self::assertEquals(['test', 'anotherTest'], array_keys($allEvaluations)); $childF->close(); } diff --git a/tests/ConditionsTest.php b/tests/ConditionsTest.php index 7d96de7..fc94bd9 100644 --- a/tests/ConditionsTest.php +++ b/tests/ConditionsTest.php @@ -25,232 +25,232 @@ protected function setUp(): void { } public function testMatchAllViaStar() { - $this->assertTrue($this->datafileReader->allConditionsAreMatched('*', ['browser_type' => 'chrome'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched('blah', ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched('*', ['browser_type' => 'chrome'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched('blah', ['browser_type' => 'chrome'])); } public function testOperatorEquals() { $conditions = [[ 'attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); } public function testOperatorEqualsDotSeparated() { $conditions = [[ 'attribute' => 'browser.type', 'operator' => 'equals', 'value' => 'chrome' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['type' => 'chrome']])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['type' => 'firefox']])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['blah' => 'firefox']])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => 'firefox'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['type' => 'chrome']])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['type' => 'firefox']])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['blah' => 'firefox']])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => 'firefox'])); } public function testOperatorNotEquals() { $conditions = [[ 'attribute' => 'browser_type', 'operator' => 'notEquals', 'value' => 'chrome' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); } public function testOperatorExists() { $conditions = [[ 'attribute' => 'browser_type', 'operator' => 'exists' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['not_browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['not_browser_type' => 'chrome'])); } public function testOperatorExistsDotSeparated() { $conditions = [[ 'attribute' => 'browser.name', 'operator' => 'exists' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['name' => 'chrome']])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => 'chrome'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['version' => '1.2.3']])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.2.3'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['name' => 'chrome']])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => 'chrome'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['version' => '1.2.3']])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.2.3'])); } public function testOperatorNotExists() { $conditions = [[ 'attribute' => 'name', 'operator' => 'notExists' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['not_name' => 'Hello World'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['not_name' => 'Hello Universe'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['not_name' => 'Hello World'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['not_name' => 'Hello Universe'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); } public function testOperatorNotExistsDotSeparated() { $conditions = [[ 'attribute' => 'browser.name', 'operator' => 'notExists' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['not_name' => 'Hello World']])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['not_name' => 'Hello Universe'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['name' => 'Chrome']])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['not_name' => 'Hello World']])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['not_name' => 'Hello Universe'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser' => ['name' => 'Chrome']])); } public function testOperatorEndsWith() { $conditions = [[ 'attribute' => 'name', 'operator' => 'endsWith', 'value' => 'World' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi Universe'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi Universe'])); } public function testOperatorIncludes() { $conditions = [[ 'attribute' => 'permissions', 'operator' => 'includes', 'value' => 'write' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read', 'write']])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read']])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read', 'write']])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read']])); } public function testOperatorNotIncludes() { $conditions = [[ 'attribute' => 'permissions', 'operator' => 'notIncludes', 'value' => 'write' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read', 'admin']])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read', 'write', 'admin']])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read', 'admin']])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['permissions' => ['read', 'write', 'admin']])); } public function testOperatorContains() { $conditions = [[ 'attribute' => 'name', 'operator' => 'contains', 'value' => 'Hello' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Yo! Hello!'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Yo! Hello!'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); } public function testOperatorNotContains() { $conditions = [[ 'attribute' => 'name', 'operator' => 'notContains', 'value' => 'Hello' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Yo! Hello!'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Yo! Hello!'])); } public function testOperatorMatches() { $conditions = [[ 'attribute' => 'name', 'operator' => 'matches', 'value' => '^[a-zA-Z]{2,}$' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Helloooooo'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hell123'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 123])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Helloooooo'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hell123'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 123])); } public function testOperatorMatchesWithRegexFlags() { $conditions = [[ 'attribute' => 'name', 'operator' => 'matches', 'value' => '^[a-zA-Z]{2,}$', 'regexFlags' => 'i' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Helloooooo'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hell123'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 123])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Helloooooo'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello World'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hell123'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 123])); } public function testOperatorNotMatches() { $conditions = [[ 'attribute' => 'name', 'operator' => 'notMatches', 'value' => '^[a-zA-Z]{2,}$' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hellooooooo'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hellooooooo'])); } public function testOperatorNotMatchesWithRegexFlags() { $conditions = [[ 'attribute' => 'name', 'operator' => 'notMatches', 'value' => '^[a-zA-Z]{2,}$', 'regexFlags' => 'i' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hellooooooo'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hi World'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['name' => '123'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hello'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['name' => 'Hellooooooo'])); } public function testOperatorIn() { $conditions = [[ 'attribute' => 'browser_type', 'operator' => 'in', 'value' => ['chrome', 'firefox'] ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'edge'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'safari'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'edge'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'safari'])); } public function testOperatorNotIn() { $conditions = [[ 'attribute' => 'browser_type', 'operator' => 'notIn', 'value' => ['chrome', 'firefox'] ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'edge'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'safari'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'edge'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'safari'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); } public function testOperatorGreaterThan() { $conditions = [[ 'attribute' => 'age', 'operator' => 'greaterThan', 'value' => 18 ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); } public function testOperatorGreaterThanOrEquals() { $conditions = [[ 'attribute' => 'age', 'operator' => 'greaterThanOrEquals', 'value' => 18 ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 18])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 16])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 18])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 16])); } public function testOperatorLessThan() { $conditions = [[ 'attribute' => 'age', 'operator' => 'lessThan', 'value' => 18 ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); } public function testOperatorLessThanOrEquals() { $conditions = [[ 'attribute' => 'age', 'operator' => 'lessThanOrEquals', 'value' => 18 ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 18])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 20])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 17])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 18])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 19])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['age' => 20])); } public function testOperatorSemverEquals() { $conditions = [[ 'attribute' => 'version', 'operator' => 'semverEquals', 'value' => '1.0.0' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); } public function testOperatorSemverNotEquals() { $conditions = [[ 'attribute' => 'version', 'operator' => 'semverNotEquals', 'value' => '1.0.0' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); } public function testOperatorSemverGreaterThan() { $conditions = [[ 'attribute' => 'version', 'operator' => 'semverGreaterThan', 'value' => '1.0.0' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '0.9.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '0.9.0'])); } public function testOperatorSemverGreaterThanOrEquals() { $conditions = [[ 'attribute' => 'version', 'operator' => 'semverGreaterThanOrEquals', 'value' => '1.0.0' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '0.9.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '2.0.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '0.9.0'])); } public function testOperatorSemverLessThan() { $conditions = [[ 'attribute' => 'version', 'operator' => 'semverLessThan', 'value' => '1.0.0' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '0.9.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.1.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '0.9.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.1.0'])); } public function testOperatorSemverLessThanOrEquals() { $conditions = [[ 'attribute' => 'version', 'operator' => 'semverLessThanOrEquals', 'value' => '1.0.0' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.1.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.0.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['version' => '1.1.0'])); } public function testOperatorBefore() { $conditions = [[ 'attribute' => 'date', 'operator' => 'before', 'value' => '2023-05-13T16:23:59Z' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-12T00:00:00Z'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-12T00:00:00Z')])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-14T00:00:00Z'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-14T00:00:00Z')])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-12T00:00:00Z'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-12T00:00:00Z')])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-14T00:00:00Z'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-14T00:00:00Z')])); } public function testOperatorAfter() { $conditions = [[ 'attribute' => 'date', 'operator' => 'after', 'value' => '2023-05-13T16:23:59Z' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-14T00:00:00Z'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-14T00:00:00Z')])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-12T00:00:00Z'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-12T00:00:00Z')])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-14T00:00:00Z'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-14T00:00:00Z')])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => '2023-05-12T00:00:00Z'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['date' => new DateTime('2023-05-12T00:00:00Z')])); } public function testSimpleConditionVariants() { $conditions = [[ 'attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome' ]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions[0], ['browser_type' => 'chrome'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched([], ['browser_type' => 'chrome'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched([], ['browser_type' => 'firefox'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched([ + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions[0], ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched([], ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched([], ['browser_type' => 'firefox'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched([ ['attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome'], ['attribute' => 'browser_version', 'operator' => 'equals', 'value' => '1.0'], ], ['browser_type' => 'chrome', 'browser_version' => '1.0', 'foo' => 'bar'])); @@ -260,29 +260,29 @@ public function testAndCondition() { $conditions = [[ 'and' => [ [ 'attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome' ], ]]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); $conditions = [[ 'and' => [ [ 'attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome' ], [ 'attribute' => 'browser_version', 'operator' => 'equals', 'value' => '1.0' ], ]]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); } public function testOrCondition() { $conditions = [[ 'or' => [ [ 'attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome' ], ]]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); $conditions = [[ 'or' => [ [ 'attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome' ], [ 'attribute' => 'browser_version', 'operator' => 'equals', 'value' => '1.0' ], ]]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_version' => '1.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_version' => '1.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox'])); } public function testNotCondition() { @@ -290,10 +290,10 @@ public function testNotCondition() { [ 'attribute' => 'browser_type', 'operator' => 'equals', 'value' => 'chrome' ], [ 'attribute' => 'browser_version', 'operator' => 'equals', 'value' => '1.0' ], ]]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'browser_version' => '2.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '2.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'browser_version' => '2.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '2.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); } public function testNestedConditions() { @@ -305,10 +305,10 @@ public function testNestedConditions() { [ 'attribute' => 'browser_version', 'operator' => 'equals', 'value' => '2.0' ], ]], ]]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '2.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '3.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_version' => '2.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '1.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '2.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '3.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_version' => '2.0'])); // plain, then OR inside AND $conditions = [ @@ -321,10 +321,10 @@ public function testNestedConditions() { ]], ]], ]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'chrome', 'browser_version' => '1.0'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'chrome', 'browser_version' => '2.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '3.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'us', 'browser_version' => '2.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'chrome', 'browser_version' => '1.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'chrome', 'browser_version' => '2.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '3.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'us', 'browser_version' => '2.0'])); // AND inside OR $conditions = [[ 'or' => [ @@ -334,10 +334,10 @@ public function testNestedConditions() { [ 'attribute' => 'orientation', 'operator' => 'equals', 'value' => 'portrait' ], ]], ]]]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '2.0'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'device_type' => 'mobile', 'orientation' => 'portrait'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'browser_version' => '2.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'device_type' => 'desktop'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'chrome', 'browser_version' => '2.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'device_type' => 'mobile', 'orientation' => 'portrait'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'browser_version' => '2.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'device_type' => 'desktop'])); // plain, then AND inside OR $conditions = [ @@ -350,9 +350,9 @@ public function testNestedConditions() { ]], ]], ]; - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'chrome', 'browser_version' => '2.0'])); - $this->assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'firefox', 'device_type' => 'mobile', 'orientation' => 'portrait'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'browser_version' => '2.0'])); - $this->assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'de', 'browser_type' => 'firefox', 'device_type' => 'desktop'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'chrome', 'browser_version' => '2.0'])); + self::assertTrue($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'nl', 'browser_type' => 'firefox', 'device_type' => 'mobile', 'orientation' => 'portrait'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['browser_type' => 'firefox', 'browser_version' => '2.0'])); + self::assertFalse($this->datafileReader->allConditionsAreMatched($conditions, ['country' => 'de', 'browser_type' => 'firefox', 'device_type' => 'desktop'])); } } diff --git a/tests/DatafileReaderTest.php b/tests/DatafileReaderTest.php index 16c6de0..21a59ea 100644 --- a/tests/DatafileReaderTest.php +++ b/tests/DatafileReaderTest.php @@ -54,13 +54,13 @@ public function testV2DatafileSchemaEntities() { 'datafile' => $datafileJson, 'logger' => $logger, ]); - $this->assertEquals('1', $reader->getRevision()); - $this->assertEquals('2', $reader->getSchemaVersion()); - $this->assertEquals($datafileJson['segments']['netherlands'], $reader->getSegment('netherlands')); - $this->assertEquals('de', $reader->getSegment('germany')['conditions'][0]['value']); - $this->assertNull($reader->getSegment('belgium')); - $this->assertEquals($datafileJson['features']['test'], $reader->getFeature('test')); - $this->assertNull($reader->getFeature('test2')); + self::assertEquals('1', $reader->getRevision()); + self::assertEquals('2', $reader->getSchemaVersion()); + self::assertEquals($datafileJson['segments']['netherlands'], $reader->getSegment('netherlands')); + self::assertEquals('de', $reader->getSegment('germany')['conditions'][0]['value']); + self::assertNull($reader->getSegment('belgium')); + self::assertEquals($datafileJson['features']['test'], $reader->getFeature('test')); + self::assertNull($reader->getFeature('test2')); } public function testSegmentsMatching() { @@ -119,60 +119,60 @@ public function testSegmentsMatching() { ]); // everyone $group = $groups[0]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['foo' => 'foo'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['bar' => 'bar'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['foo' => 'foo'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['bar' => 'bar'])); // dutchMobileUsers $group = $groups[1]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); // dutchMobileUsers2 (same as above) $group = $groups[1]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); // dutchMobileOrDesktopUsers $group = $groups[3]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop', 'browser' => 'chrome'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop', 'browser' => 'chrome'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop'])); // dutchMobileOrDesktopUsers2 $group = $groups[4]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop', 'browser' => 'chrome'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile', 'browser' => 'chrome'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop', 'browser' => 'chrome'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop'])); // germanMobileUsers $group = $groups[5]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile', 'browser' => 'chrome'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'mobile', 'browser' => 'chrome'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'mobile'])); // germanNonMobileUsers $group = $groups[6]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop', 'browser' => 'chrome'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'de', 'deviceType' => 'desktop', 'browser' => 'chrome'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['country' => 'nl', 'deviceType' => 'desktop'])); // notVersion5.5 $group = $groups[7]; - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], [])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => '5.6'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => 5.6])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => '5.7'])); - $this->assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => 5.7])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => '5.5'])); - $this->assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => 5.5])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], [])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => '5.6'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => 5.6])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => '5.7'])); + self::assertTrue($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => 5.7])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => '5.5'])); + self::assertFalse($datafileReader->allSegmentsAreMatched($group['segments'], ['version' => 5.5])); } } diff --git a/tests/EmitterTest.php b/tests/EmitterTest.php index 3463f54..7ae80a0 100644 --- a/tests/EmitterTest.php +++ b/tests/EmitterTest.php @@ -16,26 +16,26 @@ public function testAddListenerForEvent() { $unsubscribe = $emitter->on('datafile_set', $handleDetails); - $this->assertContains($handleDetails, $emitter->listeners['datafile_set']); - $this->assertArrayNotHasKey('datafile_changed', $emitter->listeners); - $this->assertArrayNotHasKey('context_set', $emitter->listeners); - $this->assertCount(1, $emitter->listeners['datafile_set']); + self::assertContains($handleDetails, $emitter->listeners['datafile_set']); + self::assertArrayNotHasKey('datafile_changed', $emitter->listeners); + self::assertArrayNotHasKey('context_set', $emitter->listeners); + self::assertCount(1, $emitter->listeners['datafile_set']); // trigger already subscribed event $emitter->trigger('datafile_set', ['key' => 'value']); - $this->assertCount(1, $handledDetails); - $this->assertEquals(['key' => 'value'], $handledDetails[0]); + self::assertCount(1, $handledDetails); + self::assertEquals(['key' => 'value'], $handledDetails[0]); // trigger unsubscribed event $emitter->trigger('sticky_set', ['key' => 'value2']); - $this->assertCount(1, $handledDetails); + self::assertCount(1, $handledDetails); // unsubscribe $unsubscribe(); - $this->assertCount(0, $emitter->listeners['datafile_set']); + self::assertCount(0, $emitter->listeners['datafile_set']); // clear all $emitter->clearAll(); - $this->assertEquals([], $emitter->listeners); + self::assertEquals([], $emitter->listeners); } } diff --git a/tests/EventsTest.php b/tests/EventsTest.php index 9376a8d..ff695eb 100644 --- a/tests/EventsTest.php +++ b/tests/EventsTest.php @@ -21,7 +21,7 @@ public function testGetParamsForStickySetEventEmptyToNew() $result = Events::getParamsForStickySetEvent($previousStickyFeatures, $newStickyFeatures, $replace); - $this->assertEquals([ + self::assertEquals([ 'features' => ['feature2', 'feature3'], 'replaced' => $replace, ], $result); @@ -41,7 +41,7 @@ public function testGetParamsForStickySetEventAddChangeRemove() $result = Events::getParamsForStickySetEvent($previousStickyFeatures, $newStickyFeatures, $replace); - $this->assertEquals([ + self::assertEquals([ 'features' => ['feature1', 'feature2', 'feature3'], 'replaced' => $replace, ], $result); @@ -78,7 +78,7 @@ public function testGetParamsForDatafileSetEventEmptyToNew() $result = Events::getParamsForDatafileSetEvent($previousDatafileReader, $newDatafileReader); - $this->assertEquals([ + self::assertEquals([ 'revision' => '2', 'previousRevision' => '1', 'revisionChanged' => true, @@ -121,7 +121,7 @@ public function testGetParamsForDatafileSetEventChangeHashAddition() $result = Events::getParamsForDatafileSetEvent($previousDatafileReader, $newDatafileReader); - $this->assertEquals([ + self::assertEquals([ 'revision' => '2', 'previousRevision' => '1', 'revisionChanged' => true, @@ -162,7 +162,7 @@ public function testGetParamsForDatafileSetEventChangeHashRemoval() $result = Events::getParamsForDatafileSetEvent($previousDatafileReader, $newDatafileReader); - $this->assertEquals([ + self::assertEquals([ 'revision' => '2', 'previousRevision' => '1', 'revisionChanged' => true, diff --git a/tests/InstanceTest.php b/tests/InstanceTest.php index 0a9871c..c32ff62 100644 --- a/tests/InstanceTest.php +++ b/tests/InstanceTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; +use Psr\Log\LogLevel; use function Featurevisor\createInstance; use function Featurevisor\createLogger; @@ -20,7 +21,7 @@ public function testShouldCreateInstanceWithDatafileContent() ], ]); - $this->assertTrue(method_exists($sdk, 'getVariation')); + self::assertTrue(method_exists($sdk, 'getVariation')); } public function testShouldConfigurePlainBucketBy() @@ -67,9 +68,9 @@ public function testShouldConfigurePlainBucketBy() 'userId' => '123', ]; - $this->assertTrue($sdk->isEnabled($featureKey, $context)); - $this->assertEquals('control', $sdk->getVariation($featureKey, $context)); - $this->assertEquals('123.test', $capturedBucketKey); + self::assertTrue($sdk->isEnabled($featureKey, $context)); + self::assertEquals('control', $sdk->getVariation($featureKey, $context)); + self::assertEquals('123.test', $capturedBucketKey); } public function testShouldConfigureAndBucketBy() @@ -117,8 +118,8 @@ public function testShouldConfigureAndBucketBy() 'organizationId' => '456', ]; - $this->assertEquals('control', $sdk->getVariation($featureKey, $context)); - $this->assertEquals('123.456.test', $capturedBucketKey); + self::assertEquals('control', $sdk->getVariation($featureKey, $context)); + self::assertEquals('123.456.test', $capturedBucketKey); } public function testShouldConfigureOrBucketBy() @@ -160,20 +161,20 @@ public function testShouldConfigureOrBucketBy() ], ]); - $this->assertTrue($sdk->isEnabled('test', [ + self::assertTrue($sdk->isEnabled('test', [ 'userId' => '123', 'deviceId' => '456', ])); - $this->assertEquals('control', $sdk->getVariation('test', [ + self::assertEquals('control', $sdk->getVariation('test', [ 'userId' => '123', 'deviceId' => '456', ])); - $this->assertEquals('123.test', $capturedBucketKey); + self::assertEquals('123.test', $capturedBucketKey); - $this->assertEquals('control', $sdk->getVariation('test', [ + self::assertEquals('control', $sdk->getVariation('test', [ 'deviceId' => '456', ])); - $this->assertEquals('456.test', $capturedBucketKey); + self::assertEquals('456.test', $capturedBucketKey); } public function testShouldInterceptContextBeforeHook() @@ -223,10 +224,10 @@ public function testShouldInterceptContextBeforeHook() 'userId' => '123', ]); - $this->assertEquals('control', $variation); - $this->assertTrue($intercepted); - $this->assertEquals('test', $interceptedFeatureKey); - $this->assertEquals('', $interceptedVariableKey); + self::assertEquals('control', $variation); + self::assertTrue($intercepted); + self::assertEquals('test', $interceptedFeatureKey); + self::assertEquals('', $interceptedVariableKey); } public function testShouldInterceptValueAfterHook() @@ -277,10 +278,10 @@ public function testShouldInterceptValueAfterHook() 'userId' => '123', ]); - $this->assertEquals('control_intercepted', $variation); // should not be "control" any more - $this->assertTrue($intercepted); - $this->assertEquals('test', $interceptedFeatureKey); - $this->assertEquals('', $interceptedVariableKey); + self::assertEquals('control_intercepted', $variation); // should not be "control" any more + self::assertTrue($intercepted); + self::assertEquals('test', $interceptedFeatureKey); + self::assertEquals('', $interceptedVariableKey); } public function testShouldInitializeWithStickyFeatures() @@ -322,23 +323,23 @@ public function testShouldInitializeWithStickyFeatures() ]); // initially control - $this->assertEquals('control', $sdk->getVariation('test', [ + self::assertEquals('control', $sdk->getVariation('test', [ 'userId' => '123', ])); - $this->assertEquals('red', $sdk->getVariable('test', 'color', [ + self::assertEquals('red', $sdk->getVariable('test', 'color', [ 'userId' => '123', ])); $sdk->setDatafile($datafileContent); // still control after setting datafile - $this->assertEquals('control', $sdk->getVariation('test', [ + self::assertEquals('control', $sdk->getVariation('test', [ 'userId' => '123', ])); // unsetting sticky features will make it treatment $sdk->setSticky([], true); - $this->assertEquals('treatment', $sdk->getVariation('test', [ + self::assertEquals('treatment', $sdk->getVariation('test', [ 'userId' => '123', ])); } @@ -381,7 +382,7 @@ public function testShouldHonourSimpleRequiredFeatures() ]); // should be disabled because required is disabled - $this->assertFalse($sdk->isEnabled('myKey')); + self::assertFalse($sdk->isEnabled('myKey')); // enabling required should enable the feature too $sdk2 = createInstance([ @@ -418,7 +419,7 @@ public function testShouldHonourSimpleRequiredFeatures() 'segments' => [], ], ]); - $this->assertTrue($sdk2->isEnabled('myKey')); + self::assertTrue($sdk2->isEnabled('myKey')); } public function testShouldHonourRequiredFeaturesWithVariation() @@ -468,7 +469,7 @@ public function testShouldHonourRequiredFeaturesWithVariation() ], ]); - $this->assertFalse($sdk->isEnabled('myKey')); + self::assertFalse($sdk->isEnabled('myKey')); // child should be enabled because required has desired variation $sdk2 = createInstance([ @@ -514,7 +515,7 @@ public function testShouldHonourRequiredFeaturesWithVariation() 'segments' => [], ], ]); - $this->assertTrue($sdk2->isEnabled('myKey')); + self::assertTrue($sdk2->isEnabled('myKey')); } public function testShouldEmitWarningsForDeprecatedFeature() @@ -564,7 +565,7 @@ public function testShouldEmitWarningsForDeprecatedFeature() ], 'logger' => createLogger([ 'handler' => function($level, $message) use (&$deprecatedCount) { - if ($level === 'warn' && strpos($message, 'is deprecated') !== false) { + if ($level === LogLevel::WARNING && strpos($message, 'is deprecated') !== false) { $deprecatedCount += 1; } }, @@ -578,9 +579,9 @@ public function testShouldEmitWarningsForDeprecatedFeature() 'userId' => '123', ]); - $this->assertEquals('control', $testVariation); - $this->assertEquals('control', $deprecatedTestVariation); - $this->assertEquals(1, $deprecatedCount); + self::assertEquals('control', $testVariation); + self::assertEquals('control', $deprecatedTestVariation); + self::assertEquals(1, $deprecatedCount); } public function testShouldCheckIfEnabledForOverriddenFlagsFromRules() @@ -625,8 +626,8 @@ public function testShouldCheckIfEnabledForOverriddenFlagsFromRules() ], ]); - $this->assertTrue($sdk->isEnabled('test', ['userId' => 'user-123', 'country' => 'de'])); - $this->assertFalse($sdk->isEnabled('test', ['userId' => 'user-123', 'country' => 'nl'])); + self::assertTrue($sdk->isEnabled('test', ['userId' => 'user-123', 'country' => 'de'])); + self::assertFalse($sdk->isEnabled('test', ['userId' => 'user-123', 'country' => 'nl'])); } public function testShouldCheckIfEnabledForMutuallyExclusiveFeatures() @@ -657,14 +658,14 @@ public function testShouldCheckIfEnabledForMutuallyExclusiveFeatures() ], ]); - $this->assertFalse($sdk->isEnabled('test')); - $this->assertFalse($sdk->isEnabled('test', ['userId' => '123'])); + self::assertFalse($sdk->isEnabled('test')); + self::assertFalse($sdk->isEnabled('test', ['userId' => '123'])); $bucketValue = 40000; - $this->assertTrue($sdk->isEnabled('mutex', ['userId' => '123'])); + self::assertTrue($sdk->isEnabled('mutex', ['userId' => '123'])); $bucketValue = 60000; - $this->assertFalse($sdk->isEnabled('mutex', ['userId' => '123'])); + self::assertFalse($sdk->isEnabled('mutex', ['userId' => '123'])); } public function testShouldGetVariation() @@ -732,19 +733,19 @@ public function testShouldGetVariation() 'userId' => '123', ]; - $this->assertEquals('treatment', $sdk->getVariation('test', $context)); - $this->assertEquals('treatment', $sdk->getVariation('test', ['userId' => 'user-ch'])); + self::assertEquals('treatment', $sdk->getVariation('test', $context)); + self::assertEquals('treatment', $sdk->getVariation('test', ['userId' => 'user-ch'])); // non existing - $this->assertNull($sdk->getVariation('nonExistingFeature', $context)); + self::assertNull($sdk->getVariation('nonExistingFeature', $context)); // disabled - $this->assertNull($sdk->getVariation('test', ['userId' => 'user-gb'])); - $this->assertNull($sdk->getVariation('test', ['userId' => 'user-gb'])); - $this->assertNull($sdk->getVariation('test', ['userId' => '123', 'country' => 'nl'])); + self::assertNull($sdk->getVariation('test', ['userId' => 'user-gb'])); + self::assertNull($sdk->getVariation('test', ['userId' => 'user-gb'])); + self::assertNull($sdk->getVariation('test', ['userId' => '123', 'country' => 'nl'])); // no variation - $this->assertNull($sdk->getVariation('testWithNoVariation', $context)); + self::assertNull($sdk->getVariation('testWithNoVariation', $context)); } public function testShouldGetVariable() @@ -946,7 +947,7 @@ public function testShouldGetVariable() ]; $evaluatedFeatures = $sdk->getAllEvaluations($context); - $this->assertEquals([ + self::assertEquals([ 'test' => [ 'enabled' => true, 'variation' => 'treatment', @@ -972,66 +973,66 @@ public function testShouldGetVariable() ], ], $evaluatedFeatures); - $this->assertEquals('treatment', $sdk->getVariation('test', $context)); - $this->assertEquals('control', $sdk->getVariation('test', array_merge($context, ['country' => 'be']))); - $this->assertEquals('control', $sdk->getVariation('test', ['userId' => 'user-ch'])); + self::assertEquals('treatment', $sdk->getVariation('test', $context)); + self::assertEquals('control', $sdk->getVariation('test', array_merge($context, ['country' => 'be']))); + self::assertEquals('control', $sdk->getVariation('test', ['userId' => 'user-ch'])); - $this->assertEquals('red', $sdk->getVariable('test', 'color', $context)); - $this->assertEquals('red', $sdk->getVariableString('test', 'color', $context)); - $this->assertEquals('black', $sdk->getVariable('test', 'color', array_merge($context, ['country' => 'be']))); - $this->assertEquals('red and white', $sdk->getVariable('test', 'color', ['userId' => 'user-ch'])); + self::assertEquals('red', $sdk->getVariable('test', 'color', $context)); + self::assertEquals('red', $sdk->getVariableString('test', 'color', $context)); + self::assertEquals('black', $sdk->getVariable('test', 'color', array_merge($context, ['country' => 'be']))); + self::assertEquals('red and white', $sdk->getVariable('test', 'color', ['userId' => 'user-ch'])); - $this->assertEquals(true, $sdk->getVariable('test', 'showSidebar', $context)); - $this->assertEquals(true, $sdk->getVariableBoolean('test', 'showSidebar', $context)); - $this->assertEquals(false, $sdk->getVariableBoolean('test', 'showSidebar', array_merge($context, ['country' => 'nl']))); - $this->assertEquals(false, $sdk->getVariableBoolean('test', 'showSidebar', array_merge($context, ['country' => 'de']))); + self::assertEquals(true, $sdk->getVariable('test', 'showSidebar', $context)); + self::assertEquals(true, $sdk->getVariableBoolean('test', 'showSidebar', $context)); + self::assertEquals(false, $sdk->getVariableBoolean('test', 'showSidebar', array_merge($context, ['country' => 'nl']))); + self::assertEquals(false, $sdk->getVariableBoolean('test', 'showSidebar', array_merge($context, ['country' => 'de']))); - $this->assertEquals('German title', $sdk->getVariableString('test', 'sidebarTitle', [ + self::assertEquals('German title', $sdk->getVariableString('test', 'sidebarTitle', [ 'userId' => 'user-forced-variation', 'country' => 'de', ])); - $this->assertEquals('Dutch title', $sdk->getVariableString('test', 'sidebarTitle', [ + self::assertEquals('Dutch title', $sdk->getVariableString('test', 'sidebarTitle', [ 'userId' => 'user-forced-variation', 'country' => 'nl', ])); - $this->assertEquals('sidebar title from variation', $sdk->getVariableString('test', 'sidebarTitle', [ + self::assertEquals('sidebar title from variation', $sdk->getVariableString('test', 'sidebarTitle', [ 'userId' => 'user-forced-variation', 'country' => 'be', ])); - $this->assertEquals(0, $sdk->getVariable('test', 'count', $context)); - $this->assertEquals(0, $sdk->getVariableInteger('test', 'count', $context)); + self::assertEquals(0, $sdk->getVariable('test', 'count', $context)); + self::assertEquals(0, $sdk->getVariableInteger('test', 'count', $context)); - $this->assertEquals(9.99, $sdk->getVariable('test', 'price', $context)); - $this->assertEquals(9.99, $sdk->getVariableDouble('test', 'price', $context)); + self::assertEquals(9.99, $sdk->getVariable('test', 'price', $context)); + self::assertEquals(9.99, $sdk->getVariableDouble('test', 'price', $context)); - $this->assertEquals(['paypal', 'creditcard'], $sdk->getVariable('test', 'paymentMethods', $context)); - $this->assertEquals(['paypal', 'creditcard'], $sdk->getVariableArray('test', 'paymentMethods', $context)); + self::assertEquals(['paypal', 'creditcard'], $sdk->getVariable('test', 'paymentMethods', $context)); + self::assertEquals(['paypal', 'creditcard'], $sdk->getVariableArray('test', 'paymentMethods', $context)); - $this->assertEquals([ + self::assertEquals([ 'key' => 'value', ], $sdk->getVariable('test', 'flatConfig', $context)); - $this->assertEquals([ + self::assertEquals([ 'key' => 'value', ], $sdk->getVariableObject('test', 'flatConfig', $context)); - $this->assertEquals([ + self::assertEquals([ 'key' => [ 'nested' => 'value', ], ], $sdk->getVariable('test', 'nestedConfig', $context)); - $this->assertEquals([ + self::assertEquals([ 'key' => [ 'nested' => 'value', ], ], $sdk->getVariableJSON('test', 'nestedConfig', $context)); // non existing - $this->assertNull($sdk->getVariable('test', 'nonExisting', $context)); - $this->assertNull($sdk->getVariable('nonExistingFeature', 'nonExisting', $context)); + self::assertNull($sdk->getVariable('test', 'nonExisting', $context)); + self::assertNull($sdk->getVariable('nonExistingFeature', 'nonExisting', $context)); // disabled - $this->assertNull($sdk->getVariable('test', 'color', ['userId' => 'user-gb'])); + self::assertNull($sdk->getVariable('test', 'color', ['userId' => 'user-gb'])); } public function testShouldGetVariablesWithoutAnyVariations() @@ -1090,10 +1091,10 @@ public function testShouldGetVariablesWithoutAnyVariations() ]; // test default value - $this->assertEquals('red', $sdk->getVariable('test', 'color', $defaultContext)); + self::assertEquals('red', $sdk->getVariable('test', 'color', $defaultContext)); // test override - $this->assertEquals('orange', $sdk->getVariable('test', 'color', array_merge($defaultContext, ['country' => 'nl']))); + self::assertEquals('orange', $sdk->getVariable('test', 'color', array_merge($defaultContext, ['country' => 'nl']))); } public function testShouldCheckIfEnabledForIndividuallyNamedSegments() @@ -1152,12 +1153,12 @@ public function testShouldCheckIfEnabledForIndividuallyNamedSegments() ], ]); - $this->assertFalse($sdk->isEnabled('test')); - $this->assertFalse($sdk->isEnabled('test', ['userId' => '123'])); - $this->assertFalse($sdk->isEnabled('test', ['userId' => '123', 'country' => 'de'])); - $this->assertFalse($sdk->isEnabled('test', ['userId' => '123', 'country' => 'us'])); + self::assertFalse($sdk->isEnabled('test')); + self::assertFalse($sdk->isEnabled('test', ['userId' => '123'])); + self::assertFalse($sdk->isEnabled('test', ['userId' => '123', 'country' => 'de'])); + self::assertFalse($sdk->isEnabled('test', ['userId' => '123', 'country' => 'us'])); - $this->assertTrue($sdk->isEnabled('test', ['userId' => '123', 'country' => 'nl'])); - $this->assertTrue($sdk->isEnabled('test', ['userId' => '123', 'country' => 'us', 'device' => 'iphone'])); + self::assertTrue($sdk->isEnabled('test', ['userId' => '123', 'country' => 'nl'])); + self::assertTrue($sdk->isEnabled('test', ['userId' => '123', 'country' => 'us', 'device' => 'iphone'])); } } diff --git a/tests/LoggerTest.php b/tests/LoggerTest.php index 500026a..3106a47 100644 --- a/tests/LoggerTest.php +++ b/tests/LoggerTest.php @@ -2,371 +2,232 @@ namespace Featurevisor\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Featurevisor\Logger; +use Psr\Log\LogLevel; use function Featurevisor\createLogger; class LoggerTest extends TestCase { - private $originalOutput; + private string $logBuffer; - protected function setUp(): void + public static function levelsLoggingTestDataProvider(): iterable { - parent::setUp(); - // Capture original output functions - $this->originalOutput = [ - 'log' => function_exists('console_log') ? 'console_log' : null, - 'info' => function_exists('console_info') ? 'console_info' : null, - 'warn' => function_exists('console_warn') ? 'console_warn' : null, - 'error' => function_exists('console_error') ? 'console_error' : null, - ]; + yield LogLevel::DEBUG => [LogLevel::DEBUG]; + yield LogLevel::INFO => [LogLevel::INFO]; + yield LogLevel::WARNING => [LogLevel::WARNING]; + yield LogLevel::ERROR => [LogLevel::ERROR]; } - protected function tearDown(): void + protected function setUp(): void { - parent::tearDown(); - // Restore original output functions if needed + parent::setUp(); + $this->logBuffer = ''; } - public function testCreateLoggerWithDefaultOptions() + public function testCreateLoggerWithDefaultOptions(): void { $logger = createLogger(); - $this->assertInstanceOf(Logger::class, $logger); + self::assertInstanceOf(Logger::class, $logger); } - public function testCreateLoggerWithCustomLevel() + public function testCreateLoggerWithCustomLevel(): void { $logger = createLogger(['level' => 'debug']); - $this->assertInstanceOf(Logger::class, $logger); + self::assertInstanceOf(Logger::class, $logger); } - public function testCreateLoggerWithCustomHandler() + public function testCreateLoggerWithCustomHandler(): void { $customHandlerCalled = false; $customHandler = function($level, $message, $details) use (&$customHandlerCalled) { $customHandlerCalled = true; - $this->assertEquals('info', $level); - $this->assertEquals('test message', $message); - $this->assertNull($details); + self::assertEquals('info', $level); + self::assertEquals('[Featurevisor] test message', $message); + self::assertSame([], $details); }; $logger = createLogger(['handler' => $customHandler]); $logger->info('test message'); - $this->assertTrue($customHandlerCalled); + self::assertTrue($customHandlerCalled); } - public function testLoggerConstructorUsesDefaultLogLevelWhenNoneProvided() + public function testLoggerConstructorUsesDefaultLogLevelWhenNoneProvided(): void { $logger = new Logger([]); // Capture output to verify debug is not logged with default level (info) - ob_start(); $logger->debug('debug message'); - $output = ob_get_clean(); // Debug should not be logged with default level (info) - $this->assertEmpty($output); + self::assertEmpty($this->logBuffer); } - public function testLoggerConstructorUsesProvidedLogLevel() + public function testLoggerConstructorUsesProvidedLogLevel(): void { - $logger = new Logger(['level' => 'debug']); + $logger = new Logger(['level' => 'debug', 'handler' => $this->spyHandler(...)]); - // Capture output to verify debug is logged with debug level - ob_start(); $logger->debug('debug message'); - $output = ob_get_clean(); - // Debug should be logged with debug level - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('debug message', $output); + self::assertEquals('[Featurevisor] debug message' . PHP_EOL, $this->logBuffer); } - public function testLoggerConstructorUsesDefaultHandlerWhenNoneProvided() + public function testLoggerConstructorUsesDefaultHandlerWhenNoneProvided(): void { - $logger = new Logger([]); + $logger = new Logger(['handler' => $this->spyHandler(...)]); - // Capture output to verify info is logged - ob_start(); $logger->info('test message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('test message', $output); + self::assertEquals('[Featurevisor] test message' . PHP_EOL, $this->logBuffer); } - public function testLoggerConstructorUsesProvidedHandler() + public function testLoggerConstructorUsesProvidedHandler(): void { $customHandlerCalled = false; $customHandler = function($level, $message, $details) use (&$customHandlerCalled) { $customHandlerCalled = true; - $this->assertEquals('info', $level); - $this->assertEquals('test message', $message); - $this->assertNull($details); + self::assertEquals('info', $level); + self::assertEquals('[Featurevisor] test message', $message); + self::assertSame([], $details); }; $logger = new Logger(['handler' => $customHandler]); $logger->info('test message'); - $this->assertTrue($customHandlerCalled); + self::assertTrue($customHandlerCalled); } public function testSetLevelUpdatesTheLogLevel() { - $logger = new Logger(['level' => 'info']); + $logger = new Logger(['level' => LogLevel::INFO, 'handler' => $this->spyHandler(...)]); // Debug should not be logged initially - ob_start(); - $logger->debug('debug message'); - $output = ob_get_clean(); - $this->assertEmpty($output); + $logger->debug('first debug message'); // Set to debug level - $logger->setLevel('debug'); - ob_start(); - $logger->debug('debug message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('debug message', $output); - } + $logger->setLevel(LogLevel::DEBUG); + $logger->debug('second debug message'); - public function testLogErrorMessagesAtAllLevels() - { - $levels = ['debug', 'info', 'warn', 'error']; - - foreach ($levels as $level) { - $logger = new Logger(['level' => $level]); - - ob_start(); - $logger->error('error message'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('error message', $output); - } + self::assertEquals( + '[Featurevisor] second debug message' . PHP_EOL, + $this->logBuffer + ); } - public function testLogWarnMessagesAtWarnLevelAndAbove() + #[DataProvider('levelsLoggingTestDataProvider')] + public function testLogErrorMessagesAtAllLevels(string $level): void { - $logger = new Logger(['level' => 'warn']); + $logger = new Logger(['level' => $level, 'handler' => $this->spyHandler(...)]); - ob_start(); - $logger->warn('warn message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('warn message', $output); - - ob_start(); $logger->error('error message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('error message', $output); - } - public function testNotLogInfoMessagesAtWarnLevel() - { - $logger = new Logger(['level' => 'warn']); - - ob_start(); - $logger->info('info message'); - $output = ob_get_clean(); - - $this->assertEmpty($output); + self::assertEquals( + '[Featurevisor] error message' . PHP_EOL, + $this->logBuffer + ); } - public function testNotLogDebugMessagesAtInfoLevel() + public function testLogWarnMessagesAtWarnLevelAndAbove(): void { - $logger = new Logger(['level' => 'info']); + $logger = new Logger(['level' => LogLevel::WARNING, 'handler' => $this->spyHandler(...)]); - ob_start(); - $logger->debug('debug message'); - $output = ob_get_clean(); + $logger->warning('warn message'); + $logger->error('error message'); - $this->assertEmpty($output); + self::assertEquals( + '[Featurevisor] warn message' . PHP_EOL . + '[Featurevisor] error message' . PHP_EOL, + $this->logBuffer + ); } - public function testLogAllMessagesAtDebugLevel() + public function testNotLogInfoMessagesAtWarnLevel(): void { - $logger = new Logger(['level' => 'debug']); - - ob_start(); - $logger->debug('debug message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('debug message', $output); + $logger = new Logger(['level' => LogLevel::WARNING, 'handler' => $this->spyHandler(...)]); - ob_start(); $logger->info('info message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('info message', $output); - ob_start(); - $logger->warn('warn message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('warn message', $output); - - ob_start(); - $logger->error('error message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('error message', $output); + self::assertEmpty($this->logBuffer); } - public function testDebugMethodCallsCorrectly() + public function testNotLogDebugMessagesAtInfoLevel(): void { - $logger = new Logger(['level' => 'debug']); + $logger = new Logger(['level' => 'info', 'handler' => $this->spyHandler(...)]); - ob_start(); $logger->debug('debug message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('debug message', $output); + self::assertEmpty($this->logBuffer); } - public function testInfoMethodCallsCorrectly() + public function testLogAllMessagesAtDebugLevel(): void { - $logger = new Logger(['level' => 'debug']); + $logger = new Logger(['level' => 'debug', 'handler' => $this->spyHandler(...)]); - ob_start(); + $logger->debug('debug message'); $logger->info('info message'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('info message', $output); - } - - public function testWarnMethodCallsCorrectly() - { - $logger = new Logger(['level' => 'debug']); - - ob_start(); - $logger->warn('warn message'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('warn message', $output); - } - - public function testErrorMethodCallsCorrectly() - { - $logger = new Logger(['level' => 'debug']); - - ob_start(); + $logger->warning('warn message'); $logger->error('error message'); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('error message', $output); + self::assertEquals( + '[Featurevisor] debug message' . PHP_EOL . + '[Featurevisor] info message' . PHP_EOL . + '[Featurevisor] warn message' . PHP_EOL . + '[Featurevisor] error message' . PHP_EOL, + $this->logBuffer + ); } - public function testHandleDetailsParameter() + public function testHandleDetailsParameter(): void { - $logger = new Logger(['level' => 'debug']); + $logger = new Logger(['level' => 'debug', 'handler' => $this->spyHandler(...)]); $details = ['key' => 'value', 'number' => 42]; - ob_start(); $logger->info('message with details', $details); - $output = ob_get_clean(); - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('message with details', $output); - // Note: In PHP, the details might be serialized differently than in JS + self::assertEquals( + '[Featurevisor] message with details {"key":"value","number":42}' . PHP_EOL, + $this->logBuffer + ); } - public function testLogMethodCallsHandlerWithCorrectParameters() + public function testLogMethodCallsHandlerWithCorrectParameters(): void { $customHandlerCalled = false; $customHandler = function($level, $message, $details) use (&$customHandlerCalled) { $customHandlerCalled = true; - $this->assertEquals('info', $level); - $this->assertEquals('test message', $message); - $this->assertEquals(['test' => true], $details); + self::assertEquals('info', $level); + self::assertEquals('[Featurevisor] test message', $message); + self::assertEquals(['test' => true], $details); }; $logger = new Logger(['handler' => $customHandler, 'level' => 'debug']); $details = ['test' => true]; $logger->log('info', 'test message', $details); - $this->assertTrue($customHandlerCalled); + + self::assertTrue($customHandlerCalled); } - public function testLogMethodNotCallHandlerWhenLevelIsFilteredOut() + public function testLogMethodNotCallHandlerWhenLevelIsFilteredOut(): void { $customHandlerCalled = false; $customHandler = function($level, $message, $details) use (&$customHandlerCalled) { $customHandlerCalled = true; }; - $logger = new Logger(['handler' => $customHandler, 'level' => 'warn']); + $logger = new Logger(['handler' => $customHandler, 'level' => LogLevel::WARNING]); $logger->log('debug', 'debug message'); - $this->assertFalse($customHandlerCalled); - } - - public function testDefaultLogHandlerUsesConsoleLogForDebugLevel() - { - ob_start(); - Logger::defaultLogHandler('debug', 'debug message'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('debug message', $output); - } - - public function testDefaultLogHandlerUsesConsoleInfoForInfoLevel() - { - ob_start(); - Logger::defaultLogHandler('info', 'info message'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('info message', $output); - } - - public function testDefaultLogHandlerUsesConsoleWarnForWarnLevel() - { - ob_start(); - Logger::defaultLogHandler('warn', 'warn message'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('warn message', $output); - } - - public function testDefaultLogHandlerUsesConsoleErrorForErrorLevel() - { - ob_start(); - Logger::defaultLogHandler('error', 'error message'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('error message', $output); + self::assertFalse($customHandlerCalled); } - public function testDefaultLogHandlerHandlesUndefinedDetails() + private function spyHandler(string $level, string|Stringable $message, array $context): void { - ob_start(); - Logger::defaultLogHandler('info', 'message without details'); - $output = ob_get_clean(); - - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('message without details', $output); - } - - public function testDefaultLogHandlerHandlesProvidedDetails() - { - $details = ['key' => 'value']; - - ob_start(); - Logger::defaultLogHandler('info', 'message with details', $details); - $output = ob_get_clean(); + $context = $context !== [] ? ' ' . json_encode($context, JSON_THROW_ON_ERROR) : ''; - $this->assertStringContainsString('[Featurevisor]', $output); - $this->assertStringContainsString('message with details', $output); + $this->logBuffer .= $message.$context.PHP_EOL; } }