+ * The sdk returns the default treatment of this feature if: + *
+ * This method does not throw any exceptions. + * It also never returns null. + * + * @param $key + * @param $flagSets + * @param $attributes + * @return array + */ + public function getTreatmentsWithConfigByFlagSets($key, $flagSets, array $attributes = null); + + /** + * Returns an associative array which each key will be + * the treatment result and the config for each + * feature associated with flag sets passed as parameter. + * The set of treatments for a feature can be configured + * on the Split web console and the config for + * that treatment. + *
+ * The sdk returns the default treatment of this feature if: + *
+ * This method does not throw any exceptions. + * It also never returns null. + * + * @param $key + * @param $flagSets + * @param $attributes + * @return array + */ + public function getTreatmentsByFlagSets($key, $flagSets, array $attributes = null); + + /** + * Returns an associative array which each key will be + * the treatment result for each feature associated with + * flag set passed as parameter. + * The set of treatments for a feature can be configured + * on the Split web console. + * This method returns the string 'control' if: + *
+ * The sdk returns the default treatment of this feature if: + *
+ * This method does not throw any exceptions. + * It also never returns null. + * + * @param $key + * @param $flagSet + * @param $attributes + * @return array + */ + public function getTreatmentsByFlagSet($key, $flagSet, array $attributes = null); + + /** + * Returns an associative array which each key will be + * the treatment result and the config for each + * feature associated with flag sets passed as parameter. + * The set of treatments for a feature can be configured + * on the Split web console and the config for + * that treatment. + *
+ * The sdk returns the default treatment of this feature if: + *
+ * This method does not throw any exceptions. + * It also never returns null. + * + * @param $key + * @param $flagSet + * @param $attributes + * @return array + */ + public function getTreatmentsWithConfigByFlagSet($key, $flagSet, array $attributes = null); + /** * A short-hand for *
@@ -168,8 +286,9 @@ public function isTreatment($key, $featureFlagName, $treatment); * @param $key * @param $trafficType * @param $eventType - * @param null $value + * @param $value + * @param $properties * @return boolean */ - public function track($key, $trafficType, $eventType, $value = null); + public function track($key, $trafficType, $eventType, $value = null, $properties = null); } diff --git a/src/SplitIO/Sdk/Evaluator.php b/src/SplitIO/Sdk/Evaluator.php index 8377f9d8..6853850c 100644 --- a/src/SplitIO/Sdk/Evaluator.php +++ b/src/SplitIO/Sdk/Evaluator.php @@ -3,7 +3,7 @@ namespace SplitIO\Sdk; use SplitIO\Component\Cache\SplitCache; -use SplitIO\Component\Common\Di; +use SplitIO\Component\Cache\SegmentCache; use SplitIO\Engine; use SplitIO\Grammar\Condition\Partition\TreatmentEnum; use SplitIO\Grammar\Split; @@ -13,15 +13,22 @@ class Evaluator { + /** + * @var \SplitIO\Component\Cache\SplitCache + */ + private $splitCache; - private $splitCache = null; + /** + * @var \SplitIO\Component\Cache\SegmentCache + */ + private $segmentCache; - public function __construct() + public function __construct(SplitCache $splitCache, SegmentCache $segmentCache) { - $this->splitCache = new SplitCache(); + $this->splitCache = $splitCache; + $this->segmentCache = $segmentCache; } - private function fetchSplit($featureName) { $splitCachedItem = $this->splitCache->getSplit($featureName); @@ -47,6 +54,25 @@ private function fetchSplits($featureNames) return $toReturn; } + private function fetchFeatureFlagNamesByFlagSets($flagSets) + { + $namesByFlagSets = $this->splitCache->getNamesByFlagSets($flagSets); + $toReturn = array(); + + foreach ($namesByFlagSets as $flagSet => $flagNames) { + if (empty($flagNames)) { + SplitApp::logger()->warning("you passed $flagSet Flag Set that does not contain" . + 'cached feature flag names, please double check what Flag Sets are in use in the' . + 'Split user interface.'); + continue; + } + + array_push($toReturn, ...$flagNames); + } + + return array_values(array_unique($toReturn)); + } + public function evaluateFeature($matchingKey, $bucketingKey, $featureName, array $attributes = null) { $timeStart = Metrics::startMeasuringLatency(); @@ -70,8 +96,21 @@ public function evaluateFeatures($matchingKey, $bucketingKey, array $featureName return $toReturn; } + public function evaluateFeaturesByFlagSets($matchingKey, $bucketingKey, array $flagSets, array $attributes = null) + { + $timeStart = Metrics::startMeasuringLatency(); + $featureFlagNames = $this->fetchFeatureFlagNamesByFlagSets($flagSets); + $toReturn = $this->evaluateFeatures($matchingKey, $bucketingKey, $featureFlagNames, $attributes); + $toReturn['latency'] = Metrics::calculateLatency($timeStart); + return $toReturn; + } + private function evalTreatment($key, $bucketingKey, $split, array $attributes = null) { + $context = array( + 'segmentCache' => $this->segmentCache, + 'evaluator' => $this, + ); $result = array( 'treatment' => TreatmentEnum::CONTROL, 'impression' => array( @@ -98,7 +137,7 @@ private function evalTreatment($key, $bucketingKey, $split, array $attributes = return $result; } - $evaluationResult = Engine::getTreatment($key, $bucketingKey, $split, $attributes); + $evaluationResult = Engine::getTreatment($key, $bucketingKey, $split, $attributes, $context); if (!is_null($evaluationResult[Engine::EVALUATION_RESULT_TREATMENT])) { $result['treatment'] = $evaluationResult[Engine::EVALUATION_RESULT_TREATMENT]; $result['impression']['label'] = $evaluationResult[Engine::EVALUATION_RESULT_LABEL]; diff --git a/src/SplitIO/Sdk/Factory/SplitFactory.php b/src/SplitIO/Sdk/Factory/SplitFactory.php index 87852808..0058ee50 100644 --- a/src/SplitIO/Sdk/Factory/SplitFactory.php +++ b/src/SplitIO/Sdk/Factory/SplitFactory.php @@ -1,11 +1,13 @@ options = $options; + $this->cache = $cache; - //Block until ready - $this->doBUR(); - - $this->client = new Client($options); + $eventCache = new EventsCache($cache); + $impressionCache = new ImpressionCache($cache); + $segmentCache = new SegmentCache($cache); + $splitCache = new SplitCache($cache); - $this->manager = new SplitManager(); - } - - private function doBUR() - { - /* - Deprecated - $ready = (isset($this->options['ready']) && $this->options['ready'] > 0) ? $this->options['ready'] : null; + $this->client = new Client(array( + 'splitCache' => $splitCache, + 'segmentCache' => $segmentCache, + 'impressionCache' => $impressionCache, + 'eventCache' => $eventCache, + ), $options); - //Block Until Ready - if ($ready) { - if (!$this->blockUntilReady($ready)) { - throw new TimeOutException("Cache data is not ready yet"); - } - } - */ + $this->manager = new SplitManager($splitCache); } /** diff --git a/src/SplitIO/Sdk/LocalhostClient.php b/src/SplitIO/Sdk/LocalhostClient.php index ac86afa6..139446a1 100644 --- a/src/SplitIO/Sdk/LocalhostClient.php +++ b/src/SplitIO/Sdk/LocalhostClient.php @@ -253,8 +253,32 @@ public function isTreatment($key, $featureFlagName, $treatment) /** * @inheritdoc */ - public function track($key, $trafficType, $eventType, $value = null) + public function track($key, $trafficType, $eventType, $value = null, $properties = null) { return true; } + + public function getTreatmentsWithConfigByFlagSets($key, $flagSets, array $attributes = null) + { + // no-op + return array(); + } + + public function getTreatmentsByFlagSets($key, $flagSets, array $attributes = null) + { + // no-op + return array(); + } + + public function getTreatmentsWithConfigByFlagSet($key, $flagSet, array $attributes = null) + { + // no-op + return array(); + } + + public function getTreatmentsByFlagSet($key, $flagSet, array $attributes = null) + { + // no-op + return array(); + } } diff --git a/src/SplitIO/Sdk/Manager/LocalhostSplitManager.php b/src/SplitIO/Sdk/Manager/LocalhostSplitManager.php index 2bb3f7a5..0d138faa 100644 --- a/src/SplitIO/Sdk/Manager/LocalhostSplitManager.php +++ b/src/SplitIO/Sdk/Manager/LocalhostSplitManager.php @@ -74,6 +74,11 @@ public function splits() return $_splits; } + /** + * Return split + * @param mixed $featureName + * @return SplitView|null + */ public function split($featureFlagName) { $featureFlagName = InputValidator::validateFeatureFlagName($featureFlagName, 'split'); @@ -90,7 +95,9 @@ public function split($featureFlagName) false, $this->splits[$featureFlagName]["treatments"], 0, - $configs + $configs, + null, + array() ); } diff --git a/src/SplitIO/Sdk/Manager/SplitManager.php b/src/SplitIO/Sdk/Manager/SplitManager.php index 4a755eaf..44554aa1 100644 --- a/src/SplitIO/Sdk/Manager/SplitManager.php +++ b/src/SplitIO/Sdk/Manager/SplitManager.php @@ -9,10 +9,19 @@ class SplitManager implements SplitManagerInterface { + /** + * @var \SplitIO\Component\Cache\SplitCache + */ + private $splitCache; + + public function __construct(SplitCache $splitCache) + { + $this->splitCache = $splitCache; + } + public function splitNames() { - $cache = new SplitCache(); - return $cache->getSplitNames(); + return $this->splitCache->getSplitNames(); } /** @@ -20,8 +29,7 @@ public function splitNames() */ public function splits() { - $cache = new SplitCache(); - $rawSplits = $cache->getAllSplits(); + $rawSplits = $this->splitCache->getAllSplits(); return array_map([self::class, 'parseSplitView'], $rawSplits); } @@ -36,8 +44,7 @@ public function split($featureFlagName) return null; } - $cache = new SplitCache(); - $raw = $cache->getSplit($featureFlagName); + $raw = $this->splitCache->getSplit($featureFlagName); if (is_null($raw)) { SplitApp::logger()->warning("split: you passed " . $featureFlagName . " that does not exist in this environment, please double check what" @@ -49,7 +56,7 @@ public function split($featureFlagName) /** * @param $splitRepresentation - * @return SplitView + * @return null|SplitView */ private static function parseSplitView($splitRepresentation) { @@ -58,7 +65,6 @@ private static function parseSplitView($splitRepresentation) } $split = new Split(json_decode($splitRepresentation, true)); - $configs = !is_null($split->getConfigurations()) ? $split->getConfigurations() : new StdClass; return new SplitView( @@ -67,7 +73,9 @@ private static function parseSplitView($splitRepresentation) $split->killed(), $split->getTreatments(), $split->getChangeNumber(), - $configs + $configs, + $split->getDefaultTratment(), + $split->getSets() ); } } diff --git a/src/SplitIO/Sdk/Manager/SplitView.php b/src/SplitIO/Sdk/Manager/SplitView.php index 493bf068..558635db 100644 --- a/src/SplitIO/Sdk/Manager/SplitView.php +++ b/src/SplitIO/Sdk/Manager/SplitView.php @@ -9,6 +9,8 @@ class SplitView private $treatments; private $changeNumber; private $configs; + private $defaultTreatment; + private $sets; /** * SplitView constructor. @@ -18,15 +20,27 @@ class SplitView * @param $treatments * @param $changeNumber * @param $configurations + * @param $defaultTreatment + * @param $sets */ - public function __construct($name, $trafficType, $killed, $treatments, $changeNumber, $configs) - { + public function __construct( + $name, + $trafficType, + $killed, + $treatments, + $changeNumber, + $configs, + $defaultTreatment, + $sets + ) { $this->name = $name; $this->trafficType = $trafficType; $this->killed = $killed; $this->treatments = $treatments; $this->changeNumber = $changeNumber; $this->configs = $configs; + $this->defaultTreatment = $defaultTreatment; + $this->sets = $sets; } @@ -125,4 +139,36 @@ public function setConfigs($configs) { $this->configs = $configs; } + + /** + * @param mixed $defaultTreatment + */ + public function setDefaultTreatment($defaultTreatment) + { + $this->defaultTreatment = $defaultTreatment; + } + + /** + * @return mixed + */ + public function getDefaultTreatment() + { + return $this->defaultTreatment; + } + + /** + * @param mixed $sets + */ + public function setSets($sets) + { + $this->sets = $sets; + } + + /** + * @return mixed + */ + public function getSets() + { + return $this->sets; + } } diff --git a/src/SplitIO/Sdk/Validator/FlagSetsValidator.php b/src/SplitIO/Sdk/Validator/FlagSetsValidator.php new file mode 100644 index 00000000..69893d84 --- /dev/null +++ b/src/SplitIO/Sdk/Validator/FlagSetsValidator.php @@ -0,0 +1,62 @@ +error($operation . ': FlagSets must be a non-empty list.'); + return array(); + } + + $sanitized = []; + foreach ($flagSets as $flagSet) { + $sanitizedFlagSet = self::sanitize($flagSet, $operation); + if (!is_null($sanitizedFlagSet)) { + array_push($sanitized, $sanitizedFlagSet); + } + } + + return array_values(array_unique($sanitized)); + } + + private static function sanitize($flagSet, $operation) + { + if ($flagSet == null) { + return null; + } + + if (!is_string($flagSet)) { + SplitApp::logger()->error($operation . ': FlagSet must be a string and not null. ' . + $flagSet . ' was discarded.'); + return null; + } + + $trimmed = trim($flagSet); + if ($trimmed !== $flagSet) { + SplitApp::logger()->warning($operation . ': Flag Set name "' . $flagSet . + '" has extra whitespace, trimming.'); + } + $toLowercase = strtolower($trimmed); + if ($toLowercase !== $trimmed) { + SplitApp::logger()->warning($operation . ': Flag Set name "' . $flagSet . + '" should be all lowercase - converting string to lowercase.'); + } + if (!preg_match(REG_EXP_FLAG_SET, $toLowercase)) { + SplitApp::logger()->warning($operation . ': you passed "' . $flagSet . + '", Flag Set must adhere to the regular expressions {' .REG_EXP_FLAG_SET . + '} This means a Flag Set must start with a letter or number, be in lowercase, alphanumeric and ' . + 'have a max length of 50 characters. "' . $flagSet . '" was discarded.'); + return null; + } + + return $toLowercase; + } +} diff --git a/src/SplitIO/Sdk/Validator/InputValidator.php b/src/SplitIO/Sdk/Validator/InputValidator.php index de86f1ab..9862da6c 100644 --- a/src/SplitIO/Sdk/Validator/InputValidator.php +++ b/src/SplitIO/Sdk/Validator/InputValidator.php @@ -5,8 +5,6 @@ use SplitIO\Split as SplitApp; use SplitIO\Sdk\Key; use SplitIO\Component\Utils as SplitIOUtils; -use SplitIO\Component\Cache\SplitCache; -use SplitIO\Grammar\Condition\Partition\TreatmentEnum; use SplitIO\Sdk\Impressions\ImpressionLabel; const MAX_LENGTH = 250; @@ -69,7 +67,7 @@ private static function checkIsNull($value, $name, $nameType, $operation) private static function checkIsEmpty($value, $name, $nameType, $operation) { $trimmed = trim($value); - if (empty($trimmed)) { + if (0 == strlen($trimmed)) { SplitApp::logger()->critical($operation . ": you passed an empty " . $name . ", " . $nameType . " must be a non-empty string."); return true; @@ -175,7 +173,7 @@ public static function validateTrackKey($key) * @param $trafficType * @return string|null */ - public static function validateTrafficType($trafficType) + public static function validateTrafficType(\SplitIO\Component\Cache\SplitCache $splitCache, $trafficType) { if (!self::validString($trafficType, 'traffic type', 'traffic type', 'track')) { return null; @@ -185,7 +183,6 @@ public static function validateTrafficType($trafficType) SplitApp::logger()->warning("track: '" . $trafficType . "' should be all lowercase - converting string to " . "lowercase."); } - $splitCache = new SplitCache(); if (!$splitCache->trafficTypeExists($toLowercase)) { SplitApp::logger()->warning("track: Traffic Type '". $toLowercase . "' does not have any corresponding " . "Splits in this environment, make sure you’re tracking your events to a valid traffic type " @@ -265,7 +262,7 @@ function ($featureFlagName) use ($operation) { ) ) ); - if (empty($filteredArray)) { + if (0 == count($filteredArray)) { SplitApp::logger()->critical($operation . ': featureFlagNames must be a non-empty array.'); return null; } diff --git a/src/SplitIO/Split.php b/src/SplitIO/Split.php index 087896dd..7e17da36 100644 --- a/src/SplitIO/Split.php +++ b/src/SplitIO/Split.php @@ -1,24 +1,16 @@ debug("no impressions were sent"); - return null; - } - $impressionCache = new ImpressionCache(); - $toStore = (is_array($impressions)) ? $impressions : array($impressions); - return $impressionCache->logImpressions( - $toStore, - $metadata - ); - } catch (\Exception $e) { - Di::getLogger()->warning('Unable to write impression back to redis.'); - Di::getLogger()->warning($e->getMessage()); - } - } -} diff --git a/src/SplitIO/Version.php b/src/SplitIO/Version.php index 67a6c644..2f4d6399 100644 --- a/src/SplitIO/Version.php +++ b/src/SplitIO/Version.php @@ -3,5 +3,5 @@ class Version { - const CURRENT = '7.1.7'; + const CURRENT = '8.0.0-rc4'; } diff --git a/src/SplitIO/functions.php b/src/SplitIO/functions.php index 0ef2da0f..c06c72ea 100644 --- a/src/SplitIO/functions.php +++ b/src/SplitIO/functions.php @@ -1,9 +1,6 @@ getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); - Di::set(Di::KEY_LOG, $logger); + ReflectiveTools::overrideLogger($logger); return $logger; } - public function testRedisWithNullValues() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', "Wrong configuration of redis."); - - $predis = new PRedis(array()); - } - public function testRedisWithOnlyParameters() { $predis = new PRedis(array( @@ -81,243 +71,17 @@ public function testRedisWithParametersAndPrefix() $predisClient->del('test-redis-assertion.this_is_a_test_key'); } - public function testRedisWithParametersPrefixAndSentinels() - { - $predis = new PRedis(array( - 'parameters' => array( - 'scheme' => 'tcp', - 'host' => 'localhost', - 'port' => 6379, - 'timeout' => 881, - 'database' => 0 - ), - 'sentinels' => array('something', 'other'), - 'options' => array( - 'prefix' => 'test-redis-assertion' - ) - )); - $predisClient = new \Predis\Client([ - 'host' => REDIS_HOST, - 'port' => REDIS_PORT, - ]); - $predisClient->set('test-redis-assertion.this_is_a_test_key', 'this-is-a-test-value'); - - $value = $predis->get('this_is_a_test_key'); - $this->assertEquals('this-is-a-test-value', $value); - - $predisClient->del('test-redis-assertion.this_is_a_test_key'); - } - - public function testRedisWithEmptySentinels() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', 'At least one sentinel is required.'); - - $predis = new PRedis(array( - 'sentinels' => array(), - 'options' => array( - 'distributedStrategy' => 'sentinel' - ) - )); - } - - public function testRedisWithSentinelsWithoutOptions() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', "Wrong configuration of redis."); - - $predis = new PRedis(array( - 'sentinels' => array( - '127.0.0.1' - ), - )); - } - - public function testRedisWithSentinelsWithoutReplicationOption() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', "Wrong configuration of redis."); - $predis = new PRedis(array( - 'sentinels' => array( - '127.0.0.1' - ), - 'options' => array() - )); - } - - public function testRedisWithSentinelsWithWrongReplicationOption() - { - $logger = $this->getMockedLogger(); - $logger->expects($this->once()) - ->method('warning') - ->with($this->equalTo("'replication' option was deprecated please use 'distributedStrategy'")); - - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', "Wrong configuration of redis 'distributedStrategy'."); - - $predis = new PRedis(array( - 'sentinels' => array( - '127.0.0.1' - ), - 'options' => array( - 'replication' => 'test' - ) - )); - } - - public function testRedisWithSentinelsWithoutServiceOption() - { - $logger = $this->getMockedLogger(); - $logger->expects($this->once()) - ->method('warning') - ->with($this->equalTo("'replication' option was deprecated please use 'distributedStrategy'")); - - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - 'Master name is required in replication mode for sentinel.' - ); - - $predis = new PRedis(array( - 'sentinels' => array( - '127.0.0.1' - ), - 'options' => array( - 'replication' => 'sentinel' - ) - )); - } - - public function testRedisWithWrongTypeOfSentinels() - { - $logger = $this->getMockedLogger(); - - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', 'sentinels must be an array.'); - - $predis = new PRedis(array( - 'sentinels' => "test", - 'options' => array( - 'replication' => 'sentinel' - ) - )); - } - - public function testRedisSentinelWithWrongRedisDistributedStrategy() - { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "Wrong configuration of redis 'distributedStrategy'." - ); - - $predis = new PRedis(array( - 'sentinels' => array( - '127.0.0.1' - ), - 'options' => array( - 'distributedStrategy' => 'test' - ) - )); - } - - public function testRedisWithSentinels() - { - $logger = $this->getMockedLogger(); - $logger->expects($this->once()) - ->method('warning') - ->with($this->equalTo("'replication' option was deprecated please use 'distributedStrategy'")); - - $this->expectException('\Predis\ClientException'); - $predis = new PRedis(array( - 'sentinels' => array( - 'tcp://MYIP:26379?timeout=3' - ), - 'options' => array( - 'replication' => 'sentinel', - 'service' => 'master' - ) - )); - - $predis->get('this_is_a_test_key'); - } - - public function testRedisWithSentinelsAndDistributedStrategy() - { - $this->expectException('\Predis\Response\ServerException'); - $predis = new PRedis(array( - 'sentinels' => array( - 'tcp:/MYIP:26379?timeout=3' - ), - 'options' => array( - 'service' => 'master', - 'distributedStrategy' => 'sentinel' - ) - )); - - $predis->get('this_is_a_test_key'); - } - - public function testRedisWithEmptyClusters() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', 'At least one clusterNode is required.'); - - $predis = new PRedis(array( - 'clusterNodes' => array(), - 'options' => array( - 'distributedStrategy' => 'cluster', - 'keyHashTag' => '{TEST}' - ) - )); - } - - public function testRedisWithClustersWithoutOptions() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', "Wrong configuration of redis."); - - $predis = new PRedis(array( - 'clusterNodes' => array( - '127.0.0.1' - ), - )); - } - - public function testRedisWithWrongTypeOfClusters() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException', 'clusterNodes must be an array.'); - - $predis = new PRedis(array( - 'clusterNodes' => "test", - 'options' => array( - 'distributedStrategy' => 'cluster', - 'keyHashTag' => '{TEST}' - ) - )); - } - - public function testRedisClusterWithWrongRedisDistributedStrategy() - { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "Wrong configuration of redis 'distributedStrategy'." - ); - - $predis = new PRedis(array( - 'clusterNodes' => array( - '127.0.0.1' - ), - 'options' => array( - 'distributedStrategy' => 'test' - ) - )); - } - public function testRedisWithInvalidKeyHashtagInClusters() { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "keyHashTag is not valid." - ); + $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); + $this->expectExceptionMessage("keyHashTag is not valid."); $predis = new PRedis(array( 'clusterNodes' => array( 'tcp://MYIP:26379?timeout=3' ), 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTag' => '{TEST' ) )); @@ -327,17 +91,15 @@ public function testRedisWithInvalidKeyHashtagInClusters() public function testRedisWithInvalidBeginingKeyHashtagInClusters() { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "keyHashTag is not valid." - ); + $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); + $this->expectExceptionMessage("keyHashTag is not valid."); $predis = new PRedis(array( 'clusterNodes' => array( 'tcp://MYIP:26379?timeout=3' ), 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'redis', 'keyHashTag' => 'TEST}' ) )); @@ -347,17 +109,15 @@ public function testRedisWithInvalidBeginingKeyHashtagInClusters() public function testRedisWithWrongTypeKeyHashtagInClusters() { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "keyHashTag must be string." - ); + $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); + $this->expectExceptionMessage("keyHashTag must be string."); $predis = new PRedis(array( 'clusterNodes' => array( 'tcp://MYIP:26379?timeout=3' ), 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTag' => array() ) )); @@ -367,17 +127,15 @@ public function testRedisWithWrongTypeKeyHashtagInClusters() public function testRedisWithWrongLengthKeyHashtagInClusters() { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "keyHashTag is not valid." - ); + $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); + $this->expectExceptionMessage("keyHashTag is not valid."); $predis = new PRedis(array( 'clusterNodes' => array( 'tcp://MYIP:26379?timeout=3' ), 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTag' => "{}" ) )); @@ -387,15 +145,13 @@ public function testRedisWithWrongLengthKeyHashtagInClusters() public function testRedisWithClusters() { - $this->expectException('\Predis\ClientException'); + $this->expectException('\Predis\Connection\ConnectionException'); $predis = new PRedis(array( - 'clusterNodes' => array( - 'tcp://MYIP:26379?timeout=3' - ), + 'parameters' => ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'], 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTag' => '{TEST}' - ) + ), )); $predis->get('this_is_a_test_key'); @@ -403,13 +159,11 @@ public function testRedisWithClusters() public function testRedisWithoutCustomKeyHashtagClusters() { - $this->expectException('\Predis\ClientException'); + $this->expectException('\Predis\Connection\ConnectionException'); $predis = new PRedis(array( - 'clusterNodes' => array( - 'tcp://MYIP:26379?timeout=3' - ), + 'parameters' => ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'], 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', ) )); @@ -418,16 +172,13 @@ public function testRedisWithoutCustomKeyHashtagClusters() public function testRedisWithClustersKeyHashTags() { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "keyHashTags must be array." - ); + $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); + $this->expectExceptionMessage("keyHashTags must be an array."); + $predis = new PRedis(array( - 'clusterNodes' => array( - 'tcp://MYIP:26379?timeout=3' - ), + 'parameters' => ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'], 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTags' => '{TEST}' ) )); @@ -437,16 +188,13 @@ public function testRedisWithClustersKeyHashTags() public function testRedisWithClustersKeyHashTagsInvalid() { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "keyHashTags size is zero after filtering valid elements." - ); + $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); + $this->expectExceptionMessage("keyHashTags size is zero after filtering valid elements."); + $predis = new PRedis(array( - 'clusterNodes' => array( - 'tcp://MYIP:26379?timeout=3' - ), + 'parameters' => ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'], 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTags' => array(1, 2) ) )); @@ -456,16 +204,13 @@ public function testRedisWithClustersKeyHashTagsInvalid() public function testRedisWithClustersKeyHashTagsInvalidHashTags() { - $this->expectException( - 'SplitIO\Component\Cache\Storage\Exception\AdapterException', - "keyHashTags size is zero after filtering valid elements." - ); + $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); + $this->expectExceptionMessage("keyHashTags size is zero after filtering valid elements."); + $predis = new PRedis(array( - 'clusterNodes' => array( - 'tcp://MYIP:26379?timeout=3' - ), + 'parameters' => ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'], 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTags' => array("one", "two", "three") ) )); @@ -475,13 +220,11 @@ public function testRedisWithClustersKeyHashTagsInvalidHashTags() public function testRedisWithClustersKeyHashTagsValid() { - $this->expectException('\Predis\ClientException'); + $this->expectException('\Predis\Connection\ConnectionException'); $predis = new PRedis(array( - 'clusterNodes' => array( - 'tcp://MYIP:26379?timeout=3' - ), + 'parameters' => ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'], 'options' => array( - 'distributedStrategy' => 'cluster', + 'cluster' => 'predis', 'keyHashTags' => array("{one}", "{two}", "{three}") ) )); @@ -489,32 +232,6 @@ public function testRedisWithClustersKeyHashTagsValid() $predis->get('this_is_a_test_key'); } - public function testRedisSSLWithClusterFails() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); - $predis = new PRedis(array( - 'options' => array( - 'distributedStrategy' => 'cluster', - ), - 'parameters' => array( - 'tls' => array(), - ), - )); - } - - public function testRedisSSLWithSentinelFails() - { - $this->expectException('SplitIO\Component\Cache\Storage\Exception\AdapterException'); - $predis = new PRedis(array( - 'options' => array( - 'distributedStrategy' => 'sentinel', - ), - 'parameters' => array( - 'tls' => array(), - ), - )); - } - public function testRedisWithWrongPrefix() { $predis = new PRedis(array( diff --git a/tests/Suite/Attributes/SdkAttributesTest.php b/tests/Suite/Attributes/SdkAttributesTest.php index 6b3e050c..1ce2af0f 100644 --- a/tests/Suite/Attributes/SdkAttributesTest.php +++ b/tests/Suite/Attributes/SdkAttributesTest.php @@ -1,20 +1,12 @@ assertTrue(is_string(\SplitIO\version())); diff --git a/tests/Suite/Attributes/files/splitChanges.json b/tests/Suite/Attributes/files/splitChanges.json index 63dc2398..0460ef22 100644 --- a/tests/Suite/Attributes/files/splitChanges.json +++ b/tests/Suite/Attributes/files/splitChanges.json @@ -10,6 +10,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -57,6 +58,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "on", + "sets": [], "conditions": [ { "matcherGroup": { @@ -130,6 +132,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -187,6 +190,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -233,6 +237,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -273,6 +278,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -311,6 +317,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -353,6 +360,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -395,6 +403,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -437,6 +446,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -478,6 +488,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -519,6 +530,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -560,6 +572,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -601,6 +614,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -642,6 +656,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -683,6 +698,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -724,6 +740,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -765,6 +782,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -806,6 +824,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -847,6 +866,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -888,6 +908,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -929,6 +950,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -970,6 +992,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1011,6 +1034,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1052,6 +1076,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1093,6 +1118,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1134,6 +1160,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "on", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1176,6 +1203,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1244,6 +1272,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1286,6 +1315,7 @@ "status": "ACTIVE", "killed": true, "defaultTreatment": "defTreatment", + "sets": [], "conditions": [ { "matcherGroup": { @@ -1328,6 +1358,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { diff --git a/tests/Suite/Component/KeysStaticMethodsTest.php b/tests/Suite/Component/KeysStaticMethodsTest.php index 41e9f331..9cb3d715 100644 --- a/tests/Suite/Component/KeysStaticMethodsTest.php +++ b/tests/Suite/Component/KeysStaticMethodsTest.php @@ -2,10 +2,7 @@ namespace SplitIO\Test\Suite\Component; -use SplitIO\Component\Cache\ImpressionCache; use SplitIO\Component\Cache\SplitCache; -use SplitIO\Component\Cache\SegmentCache; -use SplitIO\Component\Cache\TrafficTypeCache; class KeyTest extends \PHPUnit\Framework\TestCase { diff --git a/tests/Suite/Component/TrafficTypeTests.php b/tests/Suite/Component/TrafficTypeTests.php index 69e5c781..701e3e5d 100644 --- a/tests/Suite/Component/TrafficTypeTests.php +++ b/tests/Suite/Component/TrafficTypeTests.php @@ -3,7 +3,7 @@ use SplitIO\Component\Cache\SplitCache; use SplitIO\Test\Suite\Redis\ReflectiveTools; -use SplitIO\Component\Common\Di; +use SplitIO\Component\Common\Context; class TrafficTypeTest extends \PHPUnit\Framework\TestCase { @@ -13,11 +13,11 @@ private function getMockedLogger() $logger = $this ->getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', 'alert', 'notice', 'write', 'log')) ->getMock(); - Di::set(Di::KEY_LOG, $logger); + Context::setLogger($logger); return $logger; } @@ -42,10 +42,11 @@ public function testTrafficTypeWarning() $this->assertEquals($keyTrafficType, 'SPLITIO.trafficType.abc'); - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); + $cachePool = ReflectiveTools::cacheFromFactory($splitFactory); $redisClient->del($keyTrafficType); - $splitCache = new SplitCache(); + $splitCache = new SplitCache($cachePool); $this->assertEquals($splitCache->trafficTypeExists("abc"), false); diff --git a/tests/Suite/DynamicConfigurations/EvaluatorTest.php b/tests/Suite/DynamicConfigurations/EvaluatorTest.php index 9bc75489..c1dfbfd3 100644 --- a/tests/Suite/DynamicConfigurations/EvaluatorTest.php +++ b/tests/Suite/DynamicConfigurations/EvaluatorTest.php @@ -1,11 +1,9 @@ del('SPLITIO.split.mysplittest'); - $redisClient->set('SPLITIO.split.mysplittest', $this->split1); - $evaluator = new Evaluator($options); + $segmentCache = new SegmentCache($cachePool); + $splitCache = new SplitCache($cachePool); + $evaluator = new Evaluator($splitCache, $segmentCache); + $result = $evaluator->evaluateFeature('test', '', 'mysplittest', null); $this->assertEquals('off', $result['treatment']); @@ -97,15 +97,16 @@ public function testSplitWithConfigurations() ); //Initializing the SDK instance. - \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); - - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $cachePool = ReflectiveTools::cacheFromFactory($factory); + $redisClient = ReflectiveTools::clientFromFactory($factory); $redisClient->del('SPLITIO.split.mysplittest2'); - $redisClient->set('SPLITIO.split.mysplittest2', $this->split2); - $evaluator = new Evaluator($options); + $segmentCache = new SegmentCache($cachePool); + $splitCache = new SplitCache($cachePool); + $evaluator = new Evaluator($splitCache, $segmentCache); $result = $evaluator->evaluateFeature('test', '', 'mysplittest2', null); $this->assertEquals('on', $result['treatment']); @@ -125,15 +126,16 @@ public function testSplitWithConfigurationsButKilled() ); //Initializing the SDK instance. - \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); - - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $cachePool = ReflectiveTools::cacheFromFactory($factory); + $redisClient = ReflectiveTools::clientFromFactory($factory); $redisClient->del('SPLITIO.split.mysplittest3'); - $redisClient->set('SPLITIO.split.mysplittest3', $this->split3); - $evaluator = new Evaluator($options); + $segmentCache = new SegmentCache($cachePool); + $splitCache = new SplitCache($cachePool); + $evaluator = new Evaluator($splitCache, $segmentCache); $result = $evaluator->evaluateFeature('test', '', 'mysplittest3', null); $this->assertEquals('killed', $result['treatment']); @@ -153,15 +155,16 @@ public function testSplitWithConfigurationsButKilledWithConfigsOnDefault() ); //Initializing the SDK instance. - \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); - - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $cachePool = ReflectiveTools::cacheFromFactory($factory); + $redisClient = ReflectiveTools::clientFromFactory($factory); $redisClient->del('SPLITIO.split.mysplittest4'); - $redisClient->set('SPLITIO.split.mysplittest4', $this->split4); - $evaluator = new Evaluator($options); + $segmentCache = new SegmentCache($cachePool); + $splitCache = new SplitCache($cachePool); + $evaluator = new Evaluator($splitCache, $segmentCache); $result = $evaluator->evaluateFeature('test', '', 'mysplittest4', null); $this->assertEquals('killed', $result['treatment']); @@ -169,4 +172,51 @@ public function testSplitWithConfigurationsButKilledWithConfigsOnDefault() $redisClient->del('SPLITIO.split.mysplittest4'); } + + public function testEvaluateFeaturesByFlagSets() + { + $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + ); + + //Initializing the SDK instance. + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $cachePool = ReflectiveTools::cacheFromFactory($factory); + $redisClient = ReflectiveTools::clientFromFactory($factory); + + $redisClient->del('SPLITIO.flagSet.set_1'); + $redisClient->del('SPLITIO.flagSet.set_2'); + $redisClient->del('SPLITIO.split.mysplittest'); + $redisClient->del('SPLITIO.split.mysplittest2'); + $redisClient->del('SPLITIO.split.mysplittest4'); + + $redisClient->set('SPLITIO.split.mysplittest', $this->split1); + $redisClient->set('SPLITIO.split.mysplittest2', $this->split2); + $redisClient->set('SPLITIO.split.mysplittest4', $this->split4); + $redisClient->sadd('SPLITIO.flagSet.set_1', 'mysplittest2'); + $redisClient->sadd('SPLITIO.flagSet.set_2', 'mysplittest2'); + $redisClient->sadd('SPLITIO.flagSet.set_2', 'mysplittest4'); + $redisClient->sadd('SPLITIO.flagSet.set_5', 'mysplittest'); + + $segmentCache = new SegmentCache($cachePool); + $splitCache = new SplitCache($cachePool); + $evaluator = new Evaluator($splitCache, $segmentCache); + + $result = $evaluator->evaluateFeaturesByFlagSets('test', '', ['set_1', 'set_2', 'set_3']); + + $this->assertEquals('on', $result['evaluations']['mysplittest2']['treatment']); + $this->assertEquals('killed', $result['evaluations']['mysplittest4']['treatment']); + $this->assertFalse(array_key_exists('mysplittest', $result['evaluations'])); + $this->assertGreaterThan(0, $result['latency']); + + $redisClient->del('SPLITIO.flagSet.set_1'); + $redisClient->del('SPLITIO.flagSet.set_2'); + $redisClient->del('SPLITIO.split.mysplittest'); + $redisClient->del('SPLITIO.split.mysplittest2'); + $redisClient->del('SPLITIO.split.mysplittest4'); + } } diff --git a/tests/Suite/DynamicConfigurations/SplitTest.php b/tests/Suite/DynamicConfigurations/SplitTest.php index 44bb0e3b..ec918375 100644 --- a/tests/Suite/DynamicConfigurations/SplitTest.php +++ b/tests/Suite/DynamicConfigurations/SplitTest.php @@ -1,10 +1,8 @@ del('SPLITIO.split.mysplittest'); - $redisClient->set('SPLITIO.split.mysplittest', $this->split1); - $splitCache = new SplitCache(); + $splitCache = new SplitCache($cachePool); $splitRepresentation = $splitCache->getSplit('mysplittest'); $split = new Split(json_decode($splitRepresentation, true)); @@ -73,15 +70,15 @@ public function testSplitWithConfigurations() ); //Initializing the SDK instance. - \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); - - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $cachePool = ReflectiveTools::cacheFromFactory($factory); + $redisClient = ReflectiveTools::clientFromFactory($factory); $redisClient->del('SPLITIO.split.mysplittest2'); $redisClient->set('SPLITIO.split.mysplittest2', $this->split2); - $splitCache = new SplitCache(); + $splitCache = new SplitCache($cachePool); $splitRepresentation = $splitCache->getSplit('mysplittest2'); $split = new Split(json_decode($splitRepresentation, true)); diff --git a/tests/Suite/Engine/HashTest.php b/tests/Suite/Engine/HashTest.php index a5a73be2..edda9979 100644 --- a/tests/Suite/Engine/HashTest.php +++ b/tests/Suite/Engine/HashTest.php @@ -7,8 +7,7 @@ use SplitIO\Component\Cache\SplitCache; use SplitIO\Engine\Hash\HashAlgorithmEnum; use SplitIO\Grammar\Split; -use SplitIO\Split as SplitApp; -use SplitIO\Component\Common\Di; +use SplitIO\Test\Suite\Redis\ReflectiveTools; use SplitIO\Test\Utils; @@ -79,8 +78,6 @@ public function testMurmur3HashFunction() public function testAlgoField() { - Di::set(Di::KEY_FACTORY_TRACKER, false); - $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); @@ -119,7 +116,8 @@ public function testAlgoField() ), ); - $splitCache = new SplitCache(); + $cachePool = ReflectiveTools::cacheFromFactory($splitFactory); + $splitCache = new SplitCache($cachePool); foreach ($cases as $case) { $split = new Split(json_decode($splitCache->getSplit($case['key']), true)); $this->assertEquals($split->getAlgo(), $case['algo']); diff --git a/tests/Suite/Engine/SplitterTest.php b/tests/Suite/Engine/SplitterTest.php index 3e48ba90..4c0eb506 100644 --- a/tests/Suite/Engine/SplitterTest.php +++ b/tests/Suite/Engine/SplitterTest.php @@ -2,14 +2,10 @@ namespace SplitIO\Test\Suite\Engine; use SplitIO\Component\Initialization\LoggerFactory; -use SplitIO\Component\Common\ServiceProvider; -use SplitIO\Component\Log\LogLevelEnum; use SplitIO\Engine\Splitter; use SplitIO\Grammar\Condition\Partition; use SplitIO\Engine\Hash\HashAlgorithmEnum; -use SplitIO\Grammar\Split; -use SplitIO\Engine; -use SplitIO\Component\Common\Di; +use SplitIO\Component\Common\Context; class SplitterTest extends \PHPUnit\Framework\TestCase { @@ -17,7 +13,7 @@ class SplitterTest extends \PHPUnit\Framework\TestCase public function testDiLog() { $logger = LoggerFactory::setupLogger(array('adapter' => 'stdout', 'level' => 'error')); - ServiceProvider::registerLogger($logger); + Context::setLogger($logger); $this->assertTrue(true); } @@ -27,7 +23,6 @@ public function testDiLog() */ public function testWorks() { - Di::set('splitter', new Splitter()); $partitions = array(); for ($i = 1; $i <= 100; $i++) { $partitions[$i] = new Partition(array('treatment' => "$i", 'size' => 1)); @@ -43,7 +38,7 @@ public function testWorks() for ($i = 0; $i < $n; $i++) { $key = uniqid('', true); - $treatment = Di::get('splitter')->getTreatment($key, 32126754, $partitions, HashAlgorithmEnum::LEGACY); + $treatment = Splitter::getTreatment($key, 32126754, $partitions, HashAlgorithmEnum::LEGACY); $treatments[(int)$treatment - 1]++; } @@ -69,98 +64,7 @@ public function testSplitterErrorPartions() $partition = new Partition(array('treatment' => "on", 'size' => -1)); $this->assertNull( - Di::get('splitter')->getTreatment('someValidKey', 123123545, array($partition), HashAlgorithmEnum::LEGACY) + Splitter::getTreatment('someValidKey', 123123545, array($partition), HashAlgorithmEnum::LEGACY) ); } - - public function testTrafficAllocation() - { - $rawSplit = array( - 'name' => 'test1', - 'algo' => 1, - 'killed' => false, - 'status' => 'ACTIVE', - 'defaultTreatment' => 'default', - 'seed' => -1222652054, - 'orgId' => null, - 'environment' => null, - 'trafficTypeId' => null, - 'trafficTypeName' => null, - 'conditions' => array( - array( - 'conditionType' => 'WHITELIST', - 'matcherGroup' => array( - 'combiner' => 'AND', - 'matchers' => array( - array( - 'matcherType' => 'ALL_KEYS', - 'negate' => false, - 'userDefinedSegmentMatcherData' => null, - 'whitelistMatcherData' => null - ) - ) - ), - 'partitions' => array( - array( - 'treatment' => 'on', - 'size' => 100 - ) - ), - 'label' => 'in segment all' - ) - ) - ); - - // Test that conditionType = WHITELIST works normally - $split1 = new Split($rawSplit); - $treatment1 = Engine::getTreatment('testKey', null, $split1); - $this->assertEquals('on', $treatment1[Engine::EVALUATION_RESULT_TREATMENT]); - - // Test that conditionType = ROLLOUT w/o trafficAllocation behaves like WHITELIST - $rawSplit['conditions'][0]['conditionType'] = 'ROLLOUT'; - $split2 = new Split($rawSplit); - $treatment2 = Engine::getTreatment('testKey', null, $split2); - $this->assertEquals('on', $treatment2[Engine::EVALUATION_RESULT_TREATMENT]); - - // Set a low trafficAllocation to force the bucket outside it. - $rawSplit['trafficAllocation'] = 1; - $rawSplit['trafficAllocationSeed'] = -1; - $split3 = new Split($rawSplit); - $treatment3 = Engine::getTreatment('testKey', null, $split3); - $this->assertEquals('default', $treatment3[Engine::EVALUATION_RESULT_TREATMENT]); - - // Set bucket to 1 with low traffic allocation. - $splitterMocked = $this - ->getMockBuilder('\SplitIO\Engine\Splitter') - ->disableOriginalConstructor() - ->setMethods(array('getBucket')) - ->getMock(); - - $splitterMocked->method('getBucket')->willReturn(1); - - Di::set('splitter', $splitterMocked); - - $rawSplit['trafficAllocation'] = 1; - $rawSplit['trafficAllocationSeed'] = -1; - $split4 = new Split($rawSplit); - $treatment4 = Engine::getTreatment('testKey', null, $split4); - $this->assertEquals('on', $treatment4[Engine::EVALUATION_RESULT_TREATMENT]); - - // Set bucket to 100 with high traffic allocation. - $splitterMocked2 = $this - ->getMockBuilder('\SplitIO\Engine\Splitter') - ->disableOriginalConstructor() - ->setMethods(array('getBucket')) - ->getMock(); - - $splitterMocked2->method('getBucket')->willReturn(100); - - Di::set('splitter', $splitterMocked2); - - $rawSplit['trafficAllocation'] = 99; - $rawSplit['trafficAllocationSeed'] = -1; - $split5 = new Split($rawSplit); - $treatment5 = Engine::getTreatment('testKey', null, $split5); - $this->assertEquals('default', $treatment5[Engine::EVALUATION_RESULT_TREATMENT]); - } } diff --git a/tests/Suite/InputValidation/FactoryTrackerTest.php b/tests/Suite/InputValidation/FactoryTrackerTest.php index 0a86a281..d867ac61 100644 --- a/tests/Suite/InputValidation/FactoryTrackerTest.php +++ b/tests/Suite/InputValidation/FactoryTrackerTest.php @@ -1,55 +1,66 @@ 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); - $options = array('prefix' => TEST_PREFIX); - - $sdkConfig = array( - 'log' => array('adapter' => 'stdout'), - 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) - ); - - //Initializing the SDK instance. - $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); - - return $splitFactory; - } - private function getMockedLogger() { //Initialize mock logger $logger = $this ->getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); - Di::set(Di::KEY_LOG, $logger); + ReflectiveTools::overrideLogger($logger); return $logger; } public function testMultipleClientInstantiation() { - Di::set(Di::KEY_FACTORY_TRACKER, false); - $splitFactory = $this->getFactoryClient(); + ReflectiveTools::overrideTracker(); + $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + ); + + //Initializing the SDK instance. + $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); $this->assertNotNull($splitFactory->client()); $logger = $this->getMockedLogger(); + $logger->expects($this->any()) + ->method('warning') + ->with($this->logicalOr( + $this->equalTo("Factory Instantiation: You already have 1 factory/factories with this SDK Key. " + . "We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing " + . "it throughout your application."), + $this->equalTo("Factory Instantiation: You already have 2 factory/factories with this SDK Key. " + . "We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing " + . "it throughout your application."), + $this->equalTo("Factory Instantiation: You already have an instance of the Split factory. " + . "Make sure you definitely want this additional instance. We recommend keeping only one instance " + . "of the factory at all times (Singleton pattern) and reusing it throughout your application.") + ) + ); + + //Initializing the SDK instance2. + $splitFactory2 = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $this->assertNotNull($splitFactory2->client()); + + //Initializing the SDK instance3. + $splitFactory3 = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $this->assertNotNull($splitFactory3->client()); - $logger->expects($this->once()) - ->method('critical') - ->with($this->equalTo("Factory Instantiation: creating multiple factories is not possible. " - . "You have already created a factory.")); - - $splitFactory2 = $this->getFactoryClient(); - $this->assertEquals(null, $splitFactory2); + //Initializing the SDK instance4. + $splitFactory4 = \SplitIO\Sdk::factory('other', $sdkConfig); + $this->assertNotNull($splitFactory4->client()); } } diff --git a/tests/Suite/InputValidation/FlagSetsValidatorTest.php b/tests/Suite/InputValidation/FlagSetsValidatorTest.php new file mode 100644 index 00000000..fa6f5214 --- /dev/null +++ b/tests/Suite/InputValidation/FlagSetsValidatorTest.php @@ -0,0 +1,136 @@ +getMockBuilder('\SplitIO\Component\Log\Logger') + ->disableOriginalConstructor() + ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'write', 'log')) + ->getMock(); + + ReflectiveTools::overrideLogger($logger); + + return $logger; + } + + public function testAreValidWithEmptyArray() + { + $logger = $this->getMockedLogger(); + + $logger->expects($this->once()) + ->method('error') + ->with($this->equalTo('test: FlagSets must be a non-empty list.')); + + $result = FlagSetsValidator::areValid([], "test"); + $this->assertEquals(0, count($result)); + } + + public function testAreValidWithWhitespaces() + { + $logger = $this->getMockedLogger(); + + $logger->expects($this->once()) + ->method('warning') + ->with($this->equalTo('test: Flag Set name " set_1 " has extra whitespace, trimming.')); + + $result = FlagSetsValidator::areValid([" set_1 "], "test"); + $this->assertEquals(1, count($result)); + } + + public function testAreValidWithUppercases() + { + $logger = $this->getMockedLogger(); + + $logger->expects($this->once()) + ->method('warning') + ->with($this->equalTo('test: Flag Set name "SET_1" should be all lowercase - converting string to lowercase.')); + + $result = FlagSetsValidator::areValid(["SET_1"], "test"); + $this->assertEquals(1, count($result)); + } + + public function testAreValidWithIncorrectCharacters() + { + $logger = $this->getMockedLogger(); + + $logger->expects($this->once()) + ->method('warning') + ->with($this->equalTo('test: you passed "set-2", Flag Set must adhere to the regular expressions {/^[a-z0-9][_a-z0-9]{0,49}$/} This means a Flag Set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. "set-2" was discarded.')); + + $result = FlagSetsValidator::areValid(["set-2"], "test"); + $this->assertEquals(0, count($result)); + } + + public function testAreValidWithFlagSetToLong() + { + $logger = $this->getMockedLogger(); + + $logger->expects($this->once()) + ->method('warning') + ->with($this->equalTo('test: you passed "set_123123123123123123123123123123123123123123123123", Flag Set must adhere to the regular expressions {/^[a-z0-9][_a-z0-9]{0,49}$/} This means a Flag Set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. "set_123123123123123123123123123123123123123123123123" was discarded.')); + + $result = FlagSetsValidator::areValid(["set_123123123123123123123123123123123123123123123123"], "test"); + $this->assertEquals(0, count($result)); + } + + public function testAreValidWithFlagSetDupiclated() + { + $result = FlagSetsValidator::areValid(["set_4", "set_1", "SET_1", "set_2", " set_2 ", "set_3", "set_3"], "test"); + $this->assertEquals(4, count($result)); + $this->assertEquals("set_4", $result[0]); + $this->assertEquals("set_1", $result[1]); + $this->assertEquals("set_2", $result[2]); + $this->assertEquals("set_3", $result[3]); + } + + public function testAreValidWithIncorrectTypes() + { + $logger = $this->getMockedLogger(); + + $logger->expects($this->once()) + ->method('error') + ->with($this->equalTo('test: FlagSet must be a string and not null. 123 was discarded.')); + + $result = FlagSetsValidator::areValid([null, 123, "set_1", "SET_1"], "test"); + $this->assertEquals(1, count($result)); + } + + public function testAreValidConsecutive() + { + $logger = $this->getMockedLogger(); + + $logger + ->expects($this->exactly(6)) + ->method('warning') + ->withConsecutive( + ['test: Flag Set name " A " has extra whitespace, trimming.'], + ['test: Flag Set name " A " should be all lowercase - converting string to lowercase.'], + ['test: Flag Set name "@FAIL" should be all lowercase - converting string to lowercase.'], + ['test: you passed "@FAIL", Flag Set must adhere to the regular expressions ' . + '{/^[a-z0-9][_a-z0-9]{0,49}$/} This means a Flag Set must start with a letter or number, be in lowercase, alphanumeric and ' . + 'have a max length of 50 characters. "@FAIL" was discarded.'], + ['test: Flag Set name "TEST" should be all lowercase - converting string to lowercase.'], + ['test: Flag Set name " a" has extra whitespace, trimming.'], + ); + $logger + ->expects($this->exactly(2)) + ->method('error') + ->withConsecutive( + ['test: FlagSets must be a non-empty list.'], + ['test: FlagSets must be a non-empty list.'] + ); + + $this->assertEquals(['a', 'test'], FlagSetsValidator::areValid([' A ', '@FAIL', 'TEST'], 'test')); + $this->assertEquals(array(), FlagSetsValidator::areValid([], 'test')); + $this->assertEquals(array(), FlagSetsValidator::areValid(['some' => 'some1'], 'test')); + $this->assertEquals(['a', 'test'], FlagSetsValidator::areValid(['a', 'test', ' a'], 'test')); + } +} diff --git a/tests/Suite/InputValidation/GetTreatmentValidationTest.php b/tests/Suite/InputValidation/GetTreatmentValidationTest.php index 9d0158e2..2c83e812 100644 --- a/tests/Suite/InputValidation/GetTreatmentValidationTest.php +++ b/tests/Suite/InputValidation/GetTreatmentValidationTest.php @@ -1,7 +1,7 @@ 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); @@ -32,11 +32,11 @@ private function getMockedLogger() $logger = $this ->getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); - Di::set(Di::KEY_LOG, $logger); + ReflectiveTools::overrideLogger($logger); return $logger; } @@ -57,6 +57,7 @@ public function testGetTreatmentWithEmptyMatchingKeyObject() $splitSdk = $this->getFactoryClient(); $this->assertEquals('control', $splitSdk->getTreatment(new Key('', 'some_bucketing_key'), 'some_feature')); + $this->assertNotEquals('control', $splitSdk->getTreatment(new Key("0", 'some_bucketing_key'), 'some_feature')); } public function testGetTreatmentWithWrongTypeMatchingKeyObject() diff --git a/tests/Suite/InputValidation/GetTreatmentsValidationTest.php b/tests/Suite/InputValidation/GetTreatmentsValidationTest.php index 921f36c2..12b6222f 100644 --- a/tests/Suite/InputValidation/GetTreatmentsValidationTest.php +++ b/tests/Suite/InputValidation/GetTreatmentsValidationTest.php @@ -1,8 +1,8 @@ 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); @@ -32,11 +32,11 @@ private function getMockedLogger() $logger = $this ->getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); - Di::set(Di::KEY_LOG, $logger); + ReflectiveTools::overrideLogger($logger); return $logger; } diff --git a/tests/Suite/InputValidation/ManagerValidationTest.php b/tests/Suite/InputValidation/ManagerValidationTest.php index 7035fc1f..3729d1b2 100644 --- a/tests/Suite/InputValidation/ManagerValidationTest.php +++ b/tests/Suite/InputValidation/ManagerValidationTest.php @@ -1,13 +1,13 @@ 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); @@ -29,11 +29,11 @@ private function getMockedLogger() $logger = $this ->getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); - Di::set(Di::KEY_LOG, $logger); + ReflectiveTools::overrideLogger($logger); return $logger; } diff --git a/tests/Suite/InputValidation/TrackValidationTest.php b/tests/Suite/InputValidation/TrackValidationTest.php index e42bb603..c11ebef9 100644 --- a/tests/Suite/InputValidation/TrackValidationTest.php +++ b/tests/Suite/InputValidation/TrackValidationTest.php @@ -1,7 +1,7 @@ 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); @@ -32,11 +32,11 @@ private function getMockedLogger() $logger = $this ->getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); - Di::set(Di::KEY_LOG, $logger); + ReflectiveTools::overrideLogger($logger); return $logger; } diff --git a/tests/Suite/Matchers/MatchersTest.php b/tests/Suite/Matchers/MatchersTest.php index f0a1c57a..b5e4146e 100644 --- a/tests/Suite/Matchers/MatchersTest.php +++ b/tests/Suite/Matchers/MatchersTest.php @@ -1,22 +1,19 @@ 'redis', 'host' => "localhost", @@ -32,7 +29,12 @@ private function setupSplitApp() ) ); - $splitFactory = \SplitIO\Sdk::factory('apikey', $sdkConfig); + $splitFactory = \SplitIO\Sdk::factory('sdkKey', $sdkConfig); + $cachePool = ReflectiveTools::cacheFromFactory($splitFactory); + $segmentCache = new SegmentCache($cachePool); + $this->context = array( + 'segmentCache' => $segmentCache, + ); $splitFactory->client(); } @@ -152,12 +154,12 @@ public function testInSegmentMatcher() ); $matcher = Matcher::factory($condition); - $this->assertEquals($matcher->evaluate('id1'), true); - $this->assertEquals($matcher->evaluate('id2'), true); - $this->assertEquals($matcher->evaluate('id3'), true); - $this->assertEquals($matcher->evaluate('id4'), false); - $this->assertEquals($matcher->evaluate(''), false); - $this->assertEquals($matcher->evaluate(null), false); + $this->assertEquals($matcher->evaluate('id1', $this->context), true); + $this->assertEquals($matcher->evaluate('id2', $this->context), true); + $this->assertEquals($matcher->evaluate('id3', $this->context), true); + $this->assertEquals($matcher->evaluate('id4', $this->context), false); + $this->assertEquals($matcher->evaluate('', $this->context), false); + $this->assertEquals($matcher->evaluate(null, $this->context), false); } public function testWhitelistMatcher() @@ -450,7 +452,7 @@ private function setDependencyMatcherTestMocks() $evaluator = $this ->getMockBuilder('\SplitIO\Sdk\Evaluator') ->disableOriginalConstructor() - ->setMethods(array('evaluateFeature')) + ->onlyMethods(array('evaluateFeature')) ->getMock(); $evaluator->method('evaluateFeature')->willReturn(array('treatment' => 'on')); @@ -458,21 +460,21 @@ private function setDependencyMatcherTestMocks() ->method('evaluateFeature') ->with('test_key', null, 'test_feature', array('test_attribute1' => 'test_value1')); - Di::setEvaluator($evaluator); + $this->context['evaluator'] = $evaluator; } public function testDependencyMatcherTrue() { $this->setDependencyMatcherTestMocks(); $matcher = new Matcher\Dependency(array('split' => 'test_feature', 'treatments' => array('on'))); - $this->assertEquals($matcher->evalKey('test_key', array('test_attribute1' => 'test_value1')), true); + $this->assertEquals($matcher->evalKey('test_key', array('test_attribute1' => 'test_value1'), null, $this->context), true); } public function testDependencyMatcherFalse() { $this->setDependencyMatcherTestMocks(); $matcher = new Matcher\Dependency(array('split' => 'test_feature', 'treatments' => array('off'))); - $this->assertEquals($matcher->evalKey('test_key', array('test_attribute1' => 'test_value1')), false); + $this->assertEquals($matcher->evalKey('test_key', array('test_attribute1' => 'test_value1'), null, $this->context), false); } public function testRegexMatcher() diff --git a/tests/Suite/Redis/CacheInterfacesTest.php b/tests/Suite/Redis/CacheInterfacesTest.php index 75f10b8e..6767b1a2 100644 --- a/tests/Suite/Redis/CacheInterfacesTest.php +++ b/tests/Suite/Redis/CacheInterfacesTest.php @@ -5,8 +5,7 @@ use SplitIO\Component\Cache\Pool; use SplitIO\Component\Cache\SegmentCache; use SplitIO\Component\Cache\SplitCache; -use SplitIO\Component\Common\Di; -use SplitIO\Component\Cache\BlockUntilReadyCache; +use SplitIO\Component\Common\Context; use SplitIO\Component\Log\Handler\Stdout; use SplitIO\Component\Log\Logger; use SplitIO\Component\Log\LogLevelEnum; @@ -19,70 +18,65 @@ class CacheInterfacesTest extends \PHPUnit\Framework\TestCase { + private $cachePool; + + private function setupCachePool() + { + $parameters = array( + 'host' => REDIS_HOST, + 'port' => REDIS_PORT, + ); + $this->cachePool = new Pool(array('adapter' => array( + 'name' => 'predis', + 'options' => array( + 'options' => array('prefix' => TEST_PREFIX), + 'parameters' => $parameters, + ), + ))); + } + public function testDiLog() { $logAdapter = new Stdout(); $logger = new Logger($logAdapter, LogLevelEnum::INFO); - Di::getInstance()->setLogger($logger); - - $this->assertTrue(true); - } - - /** - * @depends testDiLog - */ - public function testDiCache() - { - try { - $parameters = array( - 'host' => REDIS_HOST, - 'port' => REDIS_PORT, - ); - - $cachePool = new Pool(array('adapter' => array( - 'name' => 'predis', - 'options' => array( - 'options' => array('prefix' => TEST_PREFIX), - 'parameters' => $parameters, - ), - ))); - Di::getInstance()->setCache($cachePool); - } catch (\Exception $e) { - $this->assertTrue(false, "Error setting cache on Di". $e); - } + Context::getInstance()->setLogger($logger); $this->assertTrue(true); } /** * @depends testDiLog - * @depends testDiCache */ public function testSplitCacheInterface() { + $this->setupCachePool(); $splitChanges = file_get_contents(__DIR__."/../../files/splitChanges.json"); $this->assertJson($splitChanges); Utils\Utils::addSplitsInCache($splitChanges); - $splitCache = new SplitCache(); + $splitCache = new SplitCache($this->cachePool); $splitChanges = json_decode($splitChanges, true); $splits = $splitChanges['splits']; $split = $splits[0]; - $splitName = $split['name']; + $flagSets = array('set_a', 'set_b'); $this->assertEquals(strlen(json_encode($split)), strlen($splitCache->getSplit($splitName))); $this->assertEquals($splitChanges['till'], $splitCache->getChangeNumber()); + $result = $splitCache->getNamesByFlagSets($flagSets); + $this->assertEquals(2, count($result['set_a'])); + $this->assertEquals(2, count($result['set_b'])); } /** - * @depends testSplitCacheInterface + * @depends testDiLog */ public function testSegmentCacheInterface() { + $this->setupCachePool(); $segmentChanges = file_get_contents(__DIR__."/../../files/segmentEmployeesChanges.json"); $this->assertJson($segmentChanges); @@ -90,7 +84,8 @@ public function testSegmentCacheInterface() $segmentData = json_decode($segmentChanges, true); $segmentName = $segmentData['name']; - $segmentCache = new SegmentCache(); + $segmentCache = new SegmentCache($this->cachePool); + $this->assertTrue(boolval($segmentCache->isInSegment($segmentName, "fake_user_id_4"))); $this->assertFalse(boolval($segmentCache->isInSegment($segmentName, "fake_user_id_4_"))); @@ -99,10 +94,10 @@ public function testSegmentCacheInterface() /** * @depends testDiLog - * @depends testDiCache */ public function testEventsCache() { + $this->setupCachePool(); $key= "some_key"; $trafficType = "some_trafficType"; $eventType = "some_event_type"; @@ -110,16 +105,16 @@ public function testEventsCache() $eventDTO = new EventDTO($key, $trafficType, $eventType, $value, null); $eventQueueMessage = new EventQueueMessage(new QueueMetadataMessage(), $eventDTO); - - $this->assertTrue(EventsCache::addEvent($eventQueueMessage)); + $eventsCache = new EventsCache($this->cachePool); + $this->assertTrue($eventsCache->addEvent($eventQueueMessage)); } /** * @depends testDiLog - * @depends testDiCache */ public function testEventsCacheWithProperties() { + $this->setupCachePool(); $key= "some_key"; $trafficType = "some_trafficType"; $eventType = "some_event_type"; @@ -133,8 +128,8 @@ public function testEventsCacheWithProperties() $eventDTO = new EventDTO($key, $trafficType, $eventType, $value, $properties); $eventQueueMessage = new EventQueueMessage(new QueueMetadataMessage(), $eventDTO); - - $this->assertTrue(EventsCache::addEvent($eventQueueMessage)); + $eventsCache = new EventsCache($this->cachePool); + $this->assertTrue($eventsCache->addEvent($eventQueueMessage)); $this->assertEquals($properties, $eventDTO->getProperties()); } diff --git a/tests/Suite/Redis/ReflectiveTools.php b/tests/Suite/Redis/ReflectiveTools.php index 675cec3c..f7080370 100644 --- a/tests/Suite/Redis/ReflectiveTools.php +++ b/tests/Suite/Redis/ReflectiveTools.php @@ -3,9 +3,41 @@ namespace SplitIO\Test\Suite\Redis; use ReflectionClass; +use SplitIO\Component\Common\Context; class ReflectiveTools { + public static function cacheFromFactory(\SplitIO\Sdk\Factory\SplitFactory $factory) + { + $reflectionFactory = new ReflectionClass('\SplitIO\Sdk\Factory\SplitFactory'); + $reflectionCache = $reflectionFactory->getProperty('cache'); + $reflectionCache->setAccessible(true); + return $reflectionCache->getValue($factory); + } + + public static function clientFromFactory(\SplitIO\Sdk\Factory\SplitFactory $factory) + { + $reflectionFactory = new ReflectionClass('\SplitIO\Sdk\Factory\SplitFactory'); + $reflectionCache = $reflectionFactory->getProperty('cache'); + $reflectionCache->setAccessible(true); + $cachePool = $reflectionCache->getValue($factory); + + $reflectionPool = new ReflectionClass('\SplitIO\Component\Cache\Pool'); + $reflectionAdapter = $reflectionPool->getProperty('adapter'); + $reflectionAdapter->setAccessible(true); + $adapter = $reflectionAdapter->getValue($cachePool); + + $reflectionSafeRedis = new ReflectionClass('SplitIO\Component\Cache\Storage\Adapter\SafeRedisWrapper'); + $reflectionCacheAdapter= $reflectionSafeRedis->getProperty('cacheAdapter'); + $reflectionCacheAdapter->setAccessible(true); + $adapter = $reflectionCacheAdapter->getValue($adapter); + + $reflectionPRedis = new ReflectionClass('SplitIO\Component\Cache\Storage\Adapter\PRedis'); + $reflectionClient= $reflectionPRedis->getProperty('client'); + $reflectionClient->setAccessible(true); + return $reflectionClient->getValue($adapter); + } + public static function clientFromCachePool(\SplitIO\Component\Cache\Pool $cachePool) { $reflectionPool = new ReflectionClass('\SplitIO\Component\Cache\Pool'); @@ -23,4 +55,41 @@ public static function clientFromCachePool(\SplitIO\Component\Cache\Pool $cacheP $reflectionClient->setAccessible(true); return $reflectionClient->getValue($adapter); } + + public static function overrideLogger($logger) + { + $di = Context::getInstance(); + $reflection = new ReflectionClass('SplitIO\Component\Common\Context'); + $property = $reflection->getProperty('logger'); + $property->setAccessible(true); + $property->setValue($di, $logger); + } + + public static function resetIPAddress() + { + $di = Context::getInstance(); + $reflection = new ReflectionClass('SplitIO\Component\Common\Context'); + $property = $reflection->getProperty('ipAddress'); + $property->setAccessible(true); + $property->setValue($di, ""); + } + + public static function overrideTracker() + { + $di = Context::getInstance(); + $reflection = new ReflectionClass('SplitIO\Component\Common\Context'); + $property = $reflection->getProperty('factoryTracker'); + $property->setAccessible(true); + $property->setValue($di, array()); + } + + public static function resetContext() + { + $context = Context::getInstance(); + $reflection = new ReflectionClass($context); + $instance = $reflection->getProperty('instance'); + $instance->setAccessible(true); + $instance->setValue(null, null); + $instance->setAccessible(false); + } } diff --git a/tests/Suite/Redis/SafeRedisWrapperTest.php b/tests/Suite/Redis/SafeRedisWrapperTest.php index 473a7aa6..b60b9c8d 100644 --- a/tests/Suite/Redis/SafeRedisWrapperTest.php +++ b/tests/Suite/Redis/SafeRedisWrapperTest.php @@ -1,9 +1,6 @@ getMockBuilder('\Predis\Client') ->disableOriginalConstructor() - ->setMethods($cachePoolMethods) + ->addMethods($cachePoolMethods) + ->onlyMethods(array('getOptions')) ->getMock(); foreach ($cachePoolMethods as $method) { $predisMock->method($method) ->will($this->throwException(new \Exception())); } + $predisMock->method('getOptions') + ->will($this->throwException(new \Exception())); $predisMock->method('getOptions') ->willReturn(array()); @@ -46,5 +46,6 @@ public function testAllMethodsException() $this->assertEquals(array(), $safeRedisWrapper->getKeys("some")); $this->assertEquals(0, $safeRedisWrapper->rightPushQueue("some", "another")); $this->assertEquals(false, $safeRedisWrapper->expireKey("some", 12345)); + $this->assertEquals(array(), $safeRedisWrapper->sMembers("key")); } } diff --git a/tests/Suite/Sdk/ImpressionWrapperTest.php b/tests/Suite/Sdk/ImpressionWrapperTest.php index 10d45d83..c4e636fb 100644 --- a/tests/Suite/Sdk/ImpressionWrapperTest.php +++ b/tests/Suite/Sdk/ImpressionWrapperTest.php @@ -3,12 +3,10 @@ use SplitIO\Sdk\ImpressionListenerWrapper; use SplitIO\Sdk\Impressions\Impression; -use SplitIO\Component\Cache\SplitCache; use SplitIO\Grammar\Condition\Partition\TreatmentEnum; use SplitIO\Test\Suite\Sdk\Helpers\ListenerClient; use SplitIO\Test\Suite\Sdk\Helpers\ListenerClientWithException; -use SplitIO\Test\Suite\Sdk\Helpers\ListenerClientWrong; -use SplitIO\Component\Common\Di; +use SplitIO\Test\Suite\Redis\ReflectiveTools; use SplitIO\Test\Utils; @@ -42,7 +40,6 @@ public function testSendDataToClient() private function getFactoryClient($sdkConfig) { - Di::set(Di::KEY_FACTORY_TRACKER, false); $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array(); @@ -121,6 +118,7 @@ public function testClient() $impressionClient = new ListenerClient(); + ReflectiveTools::resetIPAddress(); $sdkConfig = array( 'log' => array('adapter' => 'stdout'), 'impressionListener' => $impressionClient, @@ -162,6 +160,7 @@ public function testClientWithEmptyIpAddress() $impressionClient3 = new ListenerClient(); + ReflectiveTools::resetIPAddress(); $sdkConfig = array( 'log' => array('adapter' => 'stdout'), 'impressionListener' => $impressionClient3, @@ -194,6 +193,7 @@ public function testClientWithEmptyStringIpAddress() $impressionClient4 = new ListenerClient(); + ReflectiveTools::resetIPAddress(); $sdkConfig = array( 'log' => array('adapter' => 'stdout'), 'impressionListener' => $impressionClient4, @@ -226,6 +226,7 @@ public function testClientErasingServer() $impressionClient4 = new ListenerClient(); + ReflectiveTools::resetIPAddress(); $sdkConfig = array( 'log' => array('adapter' => 'stdout'), 'impressionListener' => $impressionClient4, diff --git a/tests/Suite/Sdk/ImpressionsTest.php b/tests/Suite/Sdk/ImpressionsTest.php index 0ba34dc1..23eaec50 100644 --- a/tests/Suite/Sdk/ImpressionsTest.php +++ b/tests/Suite/Sdk/ImpressionsTest.php @@ -1,8 +1,6 @@ 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); @@ -24,14 +21,15 @@ public function testImpressionsAreAdded() ); //Initializing the SDK instance. - \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); - - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $redisClient = ReflectiveTools::clientFromFactory($factory); + $cachePool = ReflectiveTools::cacheFromFactory($factory); $redisClient->del(ImpressionCache::IMPRESSIONS_QUEUE_KEY); $queueMetadata = new QueueMetadataMessage(); - TreatmentImpression::log(new Impression( + $impressionCache = new ImpressionCache($cachePool); + $impressionCache->logImpressions(array(new Impression( 'someMatchingKey', 'someFeature', 'on', @@ -39,7 +37,7 @@ public function testImpressionsAreAdded() 123456, 321654, 'someBucketingKey' - ), $queueMetadata); + )), $queueMetadata); // Assert that the TTL is within a 10-second range (between it was set and retrieved). $ttl = $redisClient->ttl(ImpressionCache::IMPRESSIONS_QUEUE_KEY); @@ -63,7 +61,6 @@ public function testImpressionsAreAdded() public function testExpirationOnlyOccursOnce() { - Di::set(Di::KEY_FACTORY_TRACKER, false); $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); @@ -74,13 +71,15 @@ public function testExpirationOnlyOccursOnce() ); //Initializing the SDK instance. - \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); - - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $redisClient = ReflectiveTools::clientFromFactory($factory); + $cachePool = ReflectiveTools::cacheFromFactory($factory); + $redisClient->del(ImpressionCache::IMPRESSIONS_QUEUE_KEY); $queueMetadata = new QueueMetadataMessage(false); - TreatmentImpression::log(new Impression( + $impressionCache = new ImpressionCache($cachePool); + $impressionCache->logImpressions(array(new Impression( 'someMatchingKey', 'someFeature', 'on', @@ -88,11 +87,11 @@ public function testExpirationOnlyOccursOnce() 123456, 321654, 'someBucketingKey' - ), $queueMetadata); + )), $queueMetadata); sleep(3); - TreatmentImpression::log(new Impression( + $impressionCache->logImpressions(array(new Impression( 'someMatchingKey', 'someFeature', 'on', @@ -100,7 +99,7 @@ public function testExpirationOnlyOccursOnce() 123456, 321654, 'someBucketingKey' - ), $queueMetadata); + )), $queueMetadata); $ttl = $redisClient->ttl(ImpressionCache::IMPRESSIONS_QUEUE_KEY); // $ttl should be lower than or equalt the default impressions TTL minus 3 seconds, diff --git a/tests/Suite/Sdk/SdkClientTest.php b/tests/Suite/Sdk/SdkClientTest.php index 12c3dcfa..ddd27fed 100644 --- a/tests/Suite/Sdk/SdkClientTest.php +++ b/tests/Suite/Sdk/SdkClientTest.php @@ -2,24 +2,22 @@ namespace SplitIO\Test\Suite\Sdk; use \stdClass; -use SplitIO\Component\Common\Di; +use ReflectionClass; use SplitIO\Test\Suite\Redis\ReflectiveTools; +use SplitIO\Component\Cache\SegmentCache; +use SplitIO\Component\Cache\SplitCache; use SplitIO\Component\Cache\ImpressionCache; -use SplitIO\Component\Cache\Storage\Adapter; +use SplitIO\Component\Cache\EventsCache; use SplitIO\Component\Cache\Storage\Adapter\PRedis; use SplitIO\Component\Cache\Pool; -use SplitIO\Component\Cache\SegmentCache; -use SplitIO\Component\Cache\SplitCache; use SplitIO\Sdk\Client; - -use SplitIO\Test\Suite\Sdk\Helpers\CustomLogger; use SplitIO\Test\Utils; class SdkClientTest extends \PHPUnit\Framework\TestCase { public function testLocalClient() { - Di::set(Di::KEY_FACTORY_TRACKER, false); + ReflectiveTools::resetContext(); $options['splitFile'] = dirname(dirname(__DIR__)).'/files/.splits'; $splitFactory = \SplitIO\Sdk::factory('localhost', $options); $splitSdk = $splitFactory->client(); @@ -36,7 +34,7 @@ public function testLocalClient() public function testLocalClientYAML() { - Di::set(Di::KEY_FACTORY_TRACKER, false); + ReflectiveTools::resetContext(); $options['splitFile'] = dirname(dirname(__DIR__)).'/files/splits.yml'; $splitFactory = \SplitIO\Sdk::factory('localhost', $options); $splitSdk = $splitFactory->client(); @@ -207,9 +205,43 @@ private function validateLastImpression( $this->assertEquals($parsed['m']['n'], $machineName); } + public function testSplitManager() + { + ReflectiveTools::resetContext(); + $parameters = array( + 'scheme' => 'redis', + 'host' => REDIS_HOST, + 'port' => REDIS_PORT, + 'timeout' => 881, + ); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + ); + + //Populating the cache. + Utils\Utils::addSplitsInCache(file_get_contents(__DIR__."/files/splitChanges.json")); + + //Initializing the SDK instance. + $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $splitManager = $splitFactory->manager(); + + //Assertions + $split_view = $splitManager->split("flagsets_feature"); + $this->assertEquals("flagsets_feature", $split_view->getName()); + $this->assertEquals('off', $split_view->getDefaultTreatment()); + $this->assertEquals('["set_a","set_b","set_c"]', json_encode($split_view->getSets())); + + $split_views = $splitManager->splits(); + $this->assertEquals('off', $split_views["flagsets_feature"]->getDefaultTreatment()); + $this->assertEquals('["set_a","set_b","set_c"]', json_encode($split_views["flagsets_feature"]->getSets())); + } + public function testClient() { - Di::set(Di::KEY_FACTORY_TRACKER, false); + ReflectiveTools::resetContext(); //Testing version string $this->assertTrue(is_string(\SplitIO\version())); @@ -236,7 +268,7 @@ public function testClient() Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentEmployeesChanges.json")); Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentHumanBeignsChanges.json")); - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); //Assertions $this->assertEquals('on', $splitSdk->getTreatment('user1', 'sample_feature')); @@ -435,18 +467,15 @@ public function testClient() $this->assertEquals('{"size":15,"test":20}', $configs['on']); } - /** - * @depends testClient - */ public function testCustomLog() { - Di::set(Di::KEY_FACTORY_TRACKER, false); + ReflectiveTools::resetContext(); // create a log channel $log = $this ->getMockBuilder('Psr\Log\LoggerInterface') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); @@ -476,7 +505,7 @@ public function testCustomLog() public function testInvalidCacheAdapter() { - Di::set(Di::KEY_FACTORY_TRACKER, false); + ReflectiveTools::resetContext(); $this->expectException('\SplitIO\Exception\Exception'); $sdkConfig = array( @@ -490,13 +519,12 @@ public function testInvalidCacheAdapter() public function testCacheExceptionReturnsControl() { - Di::set(Di::KEY_FACTORY_TRACKER, false); - + ReflectiveTools::resetContext(); $log = $this ->getMockBuilder('Psr\Log\LoggerInterface') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); @@ -516,7 +544,7 @@ public function testCacheExceptionReturnsControl() $cachePool = $this ->getMockBuilder('\SplitIO\Component\Cache\Pool') ->disableOriginalConstructor() - ->setMethods($cachePoolMethods) + ->onlyMethods($cachePoolMethods) ->getMock(); foreach ($cachePoolMethods as $method) { @@ -524,15 +552,13 @@ public function testCacheExceptionReturnsControl() ->will($this->throwException(new \Exception())); } - Di::setCache($cachePool); - $treatment = $splitSdk->getTreatment('key1', 'feature1'); $this->assertEquals($treatment, 'control'); } public function testGetTreatmentsWithDistinctFeatures() { - Di::set(Di::KEY_FACTORY_TRACKER, false); + ReflectiveTools::resetContext(); //Testing version string $this->assertTrue(is_string(\SplitIO\version())); @@ -563,14 +589,13 @@ public function testGetTreatmentsWithDistinctFeatures() $this->assertEquals('control', $treatmentResult['invalid_feature']); //Check impressions generated - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); $this->validateLastImpression($redisClient, 'sample_feature', 'user1', 'on', 'NA', 'NA'); } public function testGetTreatmentsWithRepeteadedFeatures() { - Di::set(Di::KEY_FACTORY_TRACKER, false); - + ReflectiveTools::resetContext(); //Testing version string $this->assertTrue(is_string(\SplitIO\version())); @@ -602,15 +627,13 @@ public function testGetTreatmentsWithRepeteadedFeatures() $this->assertEquals('control', $treatmentResult['invalid_feature']); // Check impressions - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); $this->validateLastImpression($redisClient, 'sample_feature', 'user1', 'on', 'ip-1-2-3-4', '1.2.3.4'); } public function testGetTreatmentsWithRepeteadedAndNullFeatures() { - Di::set(Di::KEY_FACTORY_TRACKER, false); - Di::set('ipAddress', null); // unset ipaddress from previous test - + ReflectiveTools::resetIPAddress(); //Testing version string $this->assertTrue(is_string(\SplitIO\version())); @@ -619,7 +642,8 @@ public function testGetTreatmentsWithRepeteadedAndNullFeatures() $sdkConfig = array( 'log' => array('adapter' => 'stdout'), - 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options), + 'ipAddress' => '' ); //Initializing the SDK instance. @@ -641,36 +665,34 @@ public function testGetTreatmentsWithRepeteadedAndNullFeatures() $this->assertEquals('control', $treatmentResult['invalid_feature']); //Check impressions - $redisClient = ReflectiveTools::clientFromCachePool(Di::getCache()); + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); $this->validateLastImpression($redisClient, 'sample_feature', 'user1', 'on'); } public function testGetTreatmentsFetchesSplitsInOneCall() { + ReflectiveTools::resetIPAddress(); // Set redis-library client mock $predisMock = $this ->getMockBuilder('\Predis\Client') ->disableOriginalConstructor() - ->setMethods(array('get', 'mget', 'rpush', 'incr')) + ->addMethods(array('get', 'mget', 'rpush', 'incr')) ->getMock(); // Create an adapter and inject mock via reflection $adapter = new PRedis(array('parameters' => array())); - $adapterReflection = new \ReflectionClass($adapter); + $adapterReflection = new ReflectionClass($adapter); $clientProperty = $adapterReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($adapter, $predisMock); // Create a pool and inject the adapter via reflection $pool = new Pool(array('adapter' => array('name' => 'predis', 'options' => array('parameters' => array())))); - $poolReflection = new \ReflectionClass($pool); + $poolReflection = new ReflectionClass($pool); $adapterProperty = $poolReflection->getProperty('adapter'); $adapterProperty->setAccessible(true); $adapterProperty->setValue($pool, $adapter); - // use the newly created pool as cache - Di::setCache($pool); - // Assert that MGET is called with the 3 splits. $predisMock->expects($this->once()) ->method('mget') @@ -680,10 +702,265 @@ public function testGetTreatmentsFetchesSplitsInOneCall() $predisMock->expects($this->never()) ->method('get'); - $client = new Client(); + $eventCache = new EventsCache($pool); + $impressionCache = new ImpressionCache($pool); + $segmentCache = new SegmentCache($pool); + $splitCache = new SplitCache($pool); + + $client = new Client(array( + 'splitCache' => $splitCache, + 'segmentCache' => $segmentCache, + 'impressionCache' => $impressionCache, + 'eventCache' => $eventCache, + )); + $client->getTreatments('key1', array('split1', 'split2', 'split3')); } + public function testMultipleInstantiationNotOverrideIP() + { + ReflectiveTools::resetIPAddress(); + //Testing version string + $this->assertTrue(is_string(\SplitIO\version())); + + $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options), + 'ipAddress' => '1.2.3.4' + ); + + //Initializing the SDK instance. + $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $splitSdk = $splitFactory->client(); + + //Populating the cache. + Utils\Utils::addSplitsInCache(file_get_contents(__DIR__."/files/splitChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentEmployeesChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentHumanBeignsChanges.json")); + + $treatmentResult = $splitSdk->getTreatments('user1', array('sample_feature', 'invalid_feature', + 'sample_feature', 'sample_feature'), null); + + //Assertions + $this->assertEquals(2, count(array_keys($treatmentResult))); + + $this->assertEquals('on', $treatmentResult['sample_feature']); + $this->assertEquals('control', $treatmentResult['invalid_feature']); + + // Check impressions + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); + $this->validateLastImpression($redisClient, 'sample_feature', 'user1', 'on', 'ip-1-2-3-4', '1.2.3.4'); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options), + 'ipAddress' => '1.2.3.4.5' + ); + + //Initializing the SDK instance. + $splitFactory2 = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $splitSdk2 = $splitFactory2->client(); + + $treatmentResult2 = $splitSdk2->getTreatments('user1', array('sample_feature', 'invalid_feature', + 'sample_feature', 'sample_feature'), null); + + //Assertions + $this->assertEquals(2, count(array_keys($treatmentResult2))); + + $this->assertEquals('on', $treatmentResult2['sample_feature']); + $this->assertEquals('control', $treatmentResult2['invalid_feature']); + + // Check impressions + $redisClient = ReflectiveTools::clientFromFactory($splitFactory2); + $this->validateLastImpression($redisClient, 'sample_feature', 'user1', 'on', 'ip-1-2-3-4', '1.2.3.4'); + } + + public function testGetTreatmentsWithConfigByFlagSets() + { + ReflectiveTools::resetContext(); + $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + ); + + //Initializing the SDK instance. + $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $splitSdk = $splitFactory->client(); + + //Populating the cache. + Utils\Utils::addSplitsInCache(file_get_contents(__DIR__."/files/splitChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentEmployeesChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentHumanBeignsChanges.json")); + + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSets('user1', array('set_a', null, 'invalid-set', 'set_a', null, 'set_b'), null); + + //Assertions + $this->assertEquals(2, count(array_keys($treatmentResult))); + + $this->assertEquals('on', $treatmentResult['flagsets_feature']["treatment"]); + $this->assertEquals("{\"size\":15,\"test\":20}", $treatmentResult['flagsets_feature']["config"]); + $this->assertEquals('off', $treatmentResult['boolean_test']["treatment"]); + $this->assertNull($treatmentResult['boolean_test']["config"]); + + //Check impressions + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); + $this->validateLastImpression($redisClient, 'boolean_test', 'user1', 'off'); + $this->validateLastImpression($redisClient, 'flagsets_feature', 'user1', 'on'); + + // With Incorrect Values + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSets('user1', array(null, 123, ""=>"", "fake_name", "###", ["set"], ["set"=>"set"]), null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // Empty array + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSets('user1', array(), null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // null + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSets('user1', null, null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + } + + public function testGetTreatmentsByFlagSets() + { + ReflectiveTools::resetContext(); + $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + ); + + //Initializing the SDK instance. + $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $splitSdk = $splitFactory->client(); + + //Populating the cache. + Utils\Utils::addSplitsInCache(file_get_contents(__DIR__."/files/splitChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentEmployeesChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentHumanBeignsChanges.json")); + + $treatmentResult = $splitSdk->getTreatmentsByFlagSets('user1', array('set_a', null, 'invalid-set', 'set_a', null, 'set_b'), null); + + //Assertions + $this->assertEquals(2, count(array_keys($treatmentResult))); + + $this->assertEquals('on', $treatmentResult['flagsets_feature']); + $this->assertEquals('off', $treatmentResult['boolean_test']); + + //Check impressions + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); + $this->validateLastImpression($redisClient, 'boolean_test', 'user1', 'off'); + $this->validateLastImpression($redisClient, 'flagsets_feature', 'user1', 'on'); + + // With Incorrect Values + $treatmentResult = $splitSdk->getTreatmentsByFlagSets('user1', array(null, 123, "set"=>"set", "fake_name", "###", ["set"], ["set"=>"set"]), null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // Empty array + $treatmentResult = $splitSdk->getTreatmentsByFlagSets('user1', array(), null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // null + $treatmentResult = $splitSdk->getTreatmentsByFlagSets('user1', null, null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + } + + public function testGetTreatmentsWithConfigByFlagSet() + { + ReflectiveTools::resetContext(); + $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + ); + + //Initializing the SDK instance. + $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $splitSdk = $splitFactory->client(); + + //Populating the cache. + Utils\Utils::addSplitsInCache(file_get_contents(__DIR__."/files/splitChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentEmployeesChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentHumanBeignsChanges.json")); + + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSet('user1', 'set_a', null); + + //Assertions + $this->assertEquals(1, count(array_keys($treatmentResult))); + + $this->assertEquals('on', $treatmentResult['flagsets_feature']["treatment"]); + $this->assertEquals("{\"size\":15,\"test\":20}", $treatmentResult['flagsets_feature']["config"]); + + //Check impressions + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); + $this->validateLastImpression($redisClient, 'flagsets_feature', 'user1', 'on'); + + // With Incorrect Values + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSet('user1', 123, null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // Empty array + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSet('user1', array(), null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // null + $treatmentResult = $splitSdk->getTreatmentsWithConfigByFlagSet('user1', null, null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + } + + public function testGetTreatmentsByFlagSet() + { + ReflectiveTools::resetContext(); + $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); + $options = array('prefix' => TEST_PREFIX); + + $sdkConfig = array( + 'log' => array('adapter' => 'stdout'), + 'cache' => array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options) + ); + + //Initializing the SDK instance. + $splitFactory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $splitSdk = $splitFactory->client(); + + //Populating the cache. + Utils\Utils::addSplitsInCache(file_get_contents(__DIR__."/files/splitChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentEmployeesChanges.json")); + Utils\Utils::addSegmentsInCache(file_get_contents(__DIR__."/files/segmentHumanBeignsChanges.json")); + + $treatmentResult = $splitSdk->getTreatmentsByFlagSet('user1', 'set_a', null); + + //Assertions + $this->assertEquals(1, count(array_keys($treatmentResult))); + + $this->assertEquals('on', $treatmentResult['flagsets_feature']); + + //Check impressions + $redisClient = ReflectiveTools::clientFromFactory($splitFactory); + $this->validateLastImpression($redisClient, 'flagsets_feature', 'user1', 'on'); + + // With Incorrect Values + $treatmentResult = $splitSdk->getTreatmentsByFlagSet('user1', 123, null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // Empty array + $treatmentResult = $splitSdk->getTreatmentsByFlagSet('user1', array(), null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + + // null + $treatmentResult = $splitSdk->getTreatmentsByFlagSet('user1', null, null); + $this->assertEquals(0, count(array_keys($treatmentResult))); + } + public static function tearDownAfterClass(): void { Utils\Utils::cleanCache(); diff --git a/tests/Suite/Sdk/SdkReadOnlyTest.php b/tests/Suite/Sdk/SdkReadOnlyTest.php index 732635c1..5020c9af 100644 --- a/tests/Suite/Sdk/SdkReadOnlyTest.php +++ b/tests/Suite/Sdk/SdkReadOnlyTest.php @@ -2,14 +2,8 @@ namespace SplitIO\Test\Suite\Sdk; -use SplitIO\Component\Cache\SplitCache; use SplitIO\Component\Cache\Storage\Adapter\PRedis; -use SplitIO\Component\Common\Di; -use SplitIO\Test\Suite\Redis\PRedisReadOnlyMock; -use SplitIO\TreatmentImpression; -use SplitIO\Grammar\Condition\Partition\TreatmentEnum; -use SplitIO\Sdk\Impressions\Impression; -use SplitIO\Sdk\QueueMetadataMessage; +use SplitIO\Component\Common\Context; use SplitIO\Test\Utils; @@ -17,8 +11,6 @@ class SdkReadOnlyTest extends \PHPUnit\Framework\TestCase { public function testClient() { - Di::set(Di::KEY_FACTORY_TRACKER, false); - $parameters = array('scheme' => 'redis', 'host' => REDIS_HOST, 'port' => REDIS_PORT, 'timeout' => 881); $options = array('prefix' => TEST_PREFIX); $sdkConfig = array( @@ -35,14 +27,13 @@ public function testClient() //Instantiate PRedis Mocked Cache $predis = new PRedis(array('adapter' => 'predis', 'parameters' => $parameters, 'options' => $options)); - Di::set(Di::KEY_CACHE, new PRedisReadOnlyMock($predis)); //Initialize mock logger $logger = $this ->getMockBuilder('\SplitIO\Component\Log\Logger') ->disableOriginalConstructor() - ->setMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', - 'alert', 'notice', 'write', 'log')) + ->onlyMethods(array('warning', 'debug', 'error', 'info', 'critical', 'emergency', + 'alert', 'notice', 'log')) ->getMock(); $logger->expects($this->any()) @@ -54,13 +45,15 @@ public function testClient() $this->equalTo('The SPLIT definition for \'mockedPRedisInvalid\' has not been found') )); - Di::set(Di::KEY_LOG, $logger); + Context::setLogger($logger); $this->assertEquals('on', $splitSdk->getTreatment('valid', 'mockedPRedis')); $this->assertEquals('off', $splitSdk->getTreatment('invalid', 'mockedPRedis')); $this->assertEquals('control', $splitSdk->getTreatment('valid', 'mockedPRedisInvalid')); } + /* + @TODO FIND A WAY TO IMPLEMENT public function testException() { Di::set(Di::KEY_FACTORY_TRACKER, false); @@ -73,7 +66,7 @@ public function testException() ); //Initializing the SDK instance. - \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); + $factory = \SplitIO\Sdk::factory('asdqwe123456', $sdkConfig); //Populating the cache. Utils\Utils::addSplitsInCache(file_get_contents(__DIR__."/files/splitReadOnly.json")); @@ -115,9 +108,18 @@ public function testException() 'something' ); - TreatmentImpression::log($impression, new QueueMetadataMessage()); + $cachePool = ReflectiveTools::cacheFromFactory($factory); + $logger = $this + ->getMockBuilder('\SplitIO\Component\Log\Logger') + ->disableOriginalConstructor() + ->setMethods(array('warning', 'debug')) + ->getMock(); + $impressionCache = new ImpressionCache($cachePool); + $impressionCache->logImpressions(array($impression), new QueueMetadataMessage()); } + */ + public static function tearDownAfterClass(): void { Utils\Utils::cleanCache(); diff --git a/tests/Suite/Sdk/files/splitChanges.json b/tests/Suite/Sdk/files/splitChanges.json index 84548e58..59e8a287 100644 --- a/tests/Suite/Sdk/files/splitChanges.json +++ b/tests/Suite/Sdk/files/splitChanges.json @@ -10,6 +10,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -69,6 +70,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -128,6 +130,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -164,6 +167,7 @@ "status": "ACTIVE", "killed": true, "defaultTreatment": "defTreatment", + "sets": [], "configurations": { "off": "{\"size\":15,\"test\":20}", "defTreatment": "{\"size\":15,\"defTreatment\":true}" @@ -204,6 +208,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "configurations": { "on": "{\"size\":15,\"test\":20}" }, @@ -266,6 +271,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -305,6 +311,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -341,6 +348,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": ["set_b", "set_c"], "conditions": [ { "matcherGroup": { @@ -366,6 +374,70 @@ ] } ] + }, + { + "orgId": null, + "environment": null, + "trafficTypeId": null, + "trafficTypeName": null, + "name": "flagsets_feature", + "seed": -1222652054, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "sets": ["set_a", "set_b", "set_c"], + "configurations": { + "on": "{\"size\":15,\"test\":20}", + "of": "{\"size\":15,\"defTreatment\":true}" + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "whitelisted_user" + ] + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ] + }, + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ] + } + ] } ], "since": -1, diff --git a/tests/Suite/Sdk/files/splitReadOnly.json b/tests/Suite/Sdk/files/splitReadOnly.json index 748dd155..9f540ead 100644 --- a/tests/Suite/Sdk/files/splitReadOnly.json +++ b/tests/Suite/Sdk/files/splitReadOnly.json @@ -10,6 +10,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { diff --git a/tests/Suite/Sdk/files/splitil.json b/tests/Suite/Sdk/files/splitil.json index 0753555f..2b7f689b 100644 --- a/tests/Suite/Sdk/files/splitil.json +++ b/tests/Suite/Sdk/files/splitil.json @@ -10,6 +10,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { diff --git a/tests/Suite/TrafficAllocation/TrafficAllocationTest.php b/tests/Suite/TrafficAllocation/TrafficAllocationTest.php new file mode 100644 index 00000000..9aaac8c3 --- /dev/null +++ b/tests/Suite/TrafficAllocation/TrafficAllocationTest.php @@ -0,0 +1,73 @@ +shouldReceive([ + 'getBucket' => 1, + 'getTreatment' => 'on', + ]); + + $logger = LoggerFactory::setupLogger(array('adapter' => 'stdout', 'level' => 'error')); + Context::setLogger($logger); + + $rawSplit = array( + 'name' => 'test1', + 'algo' => 1, + 'killed' => false, + 'status' => 'ACTIVE', + 'defaultTreatment' => 'default', + 'seed' => -1222652054, + 'orgId' => null, + 'environment' => null, + 'trafficTypeId' => null, + 'trafficTypeName' => null, + 'conditions' => array( + array( + 'conditionType' => 'WHITELIST', + 'matcherGroup' => array( + 'combiner' => 'AND', + 'matchers' => array( + array( + 'matcherType' => 'ALL_KEYS', + 'negate' => false, + 'userDefinedSegmentMatcherData' => null, + 'whitelistMatcherData' => null + ) + ) + ), + 'partitions' => array( + array( + 'treatment' => 'on', + 'size' => 100 + ) + ), + 'label' => 'in segment all' + ) + ) + ); + + $rawSplit['trafficAllocation'] = 1; + $rawSplit['trafficAllocationSeed'] = -1; + $split4 = new Split($rawSplit); + $treatment4 = \SplitIO\Engine::getTreatment('testKey', null, $split4); + $this->assertEquals('on', $treatment4[\SplitIO\Engine::EVALUATION_RESULT_TREATMENT]); + + $rawSplit['trafficAllocation'] = 1; + $rawSplit['trafficAllocationSeed'] = -1; + $split4 = new Split($rawSplit); + $treatment4 = \SplitIO\Engine::getTreatment('testKey', null, $split4); + $this->assertEquals('on', $treatment4[\SplitIO\Engine::EVALUATION_RESULT_TREATMENT]); + } +}; \ No newline at end of file diff --git a/tests/Utils/Utils.php b/tests/Utils/Utils.php index e2be6ec3..df602407 100644 --- a/tests/Utils/Utils.php +++ b/tests/Utils/Utils.php @@ -7,6 +7,7 @@ public static function addSplitsInCache($splitChanges) { $splitKey = "SPLITIO.split."; $tillKey = "SPLITIO.splits.till"; + $flagSetKey = "SPLITIO.flagSet."; $predis = new \Predis\Client([ 'host' => REDIS_HOST, @@ -23,6 +24,11 @@ public static function addSplitsInCache($splitChanges) foreach ($splits as $split) { $splitName = $split['name']; $predis->set($splitKey . $splitName, json_encode($split)); + + $sets = $split['sets']; + foreach ($sets as $set) { + $predis->sadd($flagSetKey . $set, $splitName); + } } $till = -1; if (isset($splitChanges['till'])) { diff --git a/tests/files/algoSplits.json b/tests/files/algoSplits.json index 67dac317..37e78bba 100644 --- a/tests/files/algoSplits.json +++ b/tests/files/algoSplits.json @@ -11,6 +11,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -71,6 +72,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { @@ -108,6 +110,7 @@ "status": "ACTIVE", "killed": true, "defaultTreatment": "defTreatment", + "sets": [], "conditions": [ { "matcherGroup": { @@ -144,6 +147,7 @@ "status": "ACTIVE", "killed": false, "defaultTreatment": "off", + "sets": [], "conditions": [ { "matcherGroup": { diff --git a/tests/files/splitChanges.json b/tests/files/splitChanges.json index 35b44a35..30ab4981 100644 --- a/tests/files/splitChanges.json +++ b/tests/files/splitChanges.json @@ -9,6 +9,72 @@ "seed": 301711069, "status": "ACTIVE", "killed": false, + "sets": ["set_a", "set_b", "set_c"], + "configurations": { + "on": "{\"size\":15,\"test\":20}" + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "fake_user_id_6", + "fake_user_id_7999" + ] + } + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ] + }, + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "employees" + }, + "whitelistMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 80 + }, + { + "treatment": "control", + "size": 20 + } + ] + } + ] + }, + { + "orgId": "bf083ab0-b402-11e5-b7d5-024293b5d101", + "environment": "bf9d9ce0-b402-11e5-b7d5-024293b5d101", + "name": "sample_feature_2", + "trafficTypeId": "u", + "trafficTypeName": "User", + "seed": 301711069, + "status": "ACTIVE", + "killed": false, + "sets": ["set_a", "set_b", "set_c"], "configurations": { "on": "{\"size\":15,\"test\":20}" },