diff --git a/src/Client/Client.php b/src/Client/Client.php index 2df51b2..933c8e4 100644 --- a/src/Client/Client.php +++ b/src/Client/Client.php @@ -13,7 +13,7 @@ use const JSON_THROW_ON_ERROR; class Client { - protected const VERSION = '1.0.3'; + protected const VERSION = '1.0.4'; private HTTPClient $httpClient; private string $url; private array $query; diff --git a/src/Context/Context.php b/src/Context/Context.php index d70888a..a9e5c0a 100644 --- a/src/Context/Context.php +++ b/src/Context/Context.php @@ -250,7 +250,7 @@ private function getAssignment(string $experimentName): Assignment { $assignment->audienceMismatch = !$result; } - if (isset($experiment->data->audienceStrict) && !empty($assignment->audienceMismatch)) { + if (isset($experiment->data->audienceStrict) && $experiment->data->audienceStrict === true && !empty($assignment->audienceMismatch)) { $assignment->variant = 0; } else if (empty($experiment->data->fullOnVariant) && $uid = $this->units[$experiment->data->unitType] ?? null) { diff --git a/src/Http/HTTPClient.php b/src/Http/HTTPClient.php index 2968bca..d306949 100644 --- a/src/Http/HTTPClient.php +++ b/src/Http/HTTPClient.php @@ -45,7 +45,7 @@ class HTTPClient { public int $retries = 5; public int $timeout = 3000; - private function setupRequest(string $url, array $query = [], array $headers = [], string $type = 'GET', string $data = null): void { + private function setupRequest(string $url, array $query = [], array $headers = [], string $type = 'GET', ?string $data = null): void { $this->curlInit(); $flatHeaders = []; foreach ($headers as $header => $value) { diff --git a/tests/Context/ContextTest.php b/tests/Context/ContextTest.php index 78bbe92..c79a6b6 100644 --- a/tests/Context/ContextTest.php +++ b/tests/Context/ContextTest.php @@ -1140,4 +1140,42 @@ public function testRefreshClearsAssignmentCacheForExperimentIdChange(): void { self::assertSame(3, $context->getPendingCount()); } + + public function testPeekTreatmentReturnsAssignedVariantOnAudienceMismatchWithExplicitFalseStrictMode(): void { + $context = $this->createReadyContext('audience_false_strict_context.json'); + self::assertSame(1, $context->peekTreatment("exp_test_ab")); + } + + public function testGetTreatmentWithMatchingAudienceAndExplicitFalseStrictMode(): void { + $context = $this->createReadyContext('audience_false_strict_context.json'); + $context->setAttribute('age', 21); + + self::assertSame(1, $context->getTreatment("exp_test_ab")); + self::assertSame(1, $context->getPendingCount()); + + $context->publish(); + self::assertArrayHasKey(0, $this->eventHandler->submitted); + self::assertSame('21', $this->eventHandler->submitted[0]->attributes[0]->value); + self::assertSame('exp_test_ab', $this->eventHandler->submitted[0]->exposures[0]->name); + self::assertFalse($this->eventHandler->submitted[0]->exposures[0]->audienceMismatch); + } + + public function testPeekVariableValueReturnsAssignedVariantOnAudienceMismatchWithExplicitFalseStrictMode(): void { + $context = $this->createReadyContext('audience_false_strict_context.json'); + self::assertSame("large", $context->peekVariableValue("banner.size", "small")); + } + + public function testGetVariableValueWithMatchingAudienceAndExplicitFalseStrictMode(): void { + $context = $this->createReadyContext('audience_false_strict_context.json'); + $context->setAttribute('age', 21); + + self::assertSame("large", $context->getVariableValue("banner.size", "small")); + self::assertSame(1, $context->getPendingCount()); + + $context->publish(); + self::assertArrayHasKey(0, $this->eventHandler->submitted); + self::assertSame('21', $this->eventHandler->submitted[0]->attributes[0]->value); + self::assertSame('exp_test_ab', $this->eventHandler->submitted[0]->exposures[0]->name); + self::assertFalse($this->eventHandler->submitted[0]->exposures[0]->audienceMismatch); + } } diff --git a/tests/Fixtures/json/audience_false_strict_context.json b/tests/Fixtures/json/audience_false_strict_context.json new file mode 100644 index 0000000..21f3e82 --- /dev/null +++ b/tests/Fixtures/json/audience_false_strict_context.json @@ -0,0 +1,165 @@ +{ + "experiments":[ + { + "id":1, + "name":"exp_test_ab", + "iteration":1, + "unitType":"session_id", + "seedHi":3603515, + "seedLo":233373850, + "split":[ + 0.5, + 0.5 + ], + "trafficSeedHi":449867249, + "trafficSeedLo":455443629, + "trafficSplit":[ + 0.0, + 1.0 + ], + "fullOnVariant":0, + "applications":[ + { + "name":"website" + } + ], + "variants":[ + { + "name":"A", + "config":null + }, + { + "name":"B", + "config":"{\"banner.border\":1,\"banner.size\":\"large\"}" + } + ], + "audienceStrict": false, + "audience": "{\"filter\":[{\"gte\":[{\"var\":\"age\"},{\"value\":20}]}]}" + }, + { + "id":2, + "name":"exp_test_abc", + "iteration":1, + "unitType":"session_id", + "seedHi":55006150, + "seedLo":47189152, + "split":[ + 0.34, + 0.33, + 0.33 + ], + "trafficSeedHi":705671872, + "trafficSeedLo":212903484, + "trafficSplit":[ + 0.0, + 1.0 + ], + "fullOnVariant":0, + "applications":[ + { + "name":"website" + } + ], + "variants":[ + { + "name":"A", + "config":null + }, + { + "name":"B", + "config":"{\"button.color\":\"blue\"}" + }, + { + "name":"C", + "config":"{\"button.color\":\"red\"}" + } + ], + "audience": "" + }, + { + "id":3, + "name":"exp_test_not_eligible", + "iteration":1, + "unitType":"user_id", + "seedHi":503266407, + "seedLo":144942754, + "split":[ + 0.34, + 0.33, + 0.33 + ], + "trafficSeedHi":87768905, + "trafficSeedLo":511357582, + "trafficSplit":[ + 0.99, + 0.01 + ], + "fullOnVariant":0, + "applications":[ + { + "name":"website" + } + ], + "variants":[ + { + "name":"A", + "config":null + }, + { + "name":"B", + "config":"{\"card.width\":\"80%\"}" + }, + { + "name":"C", + "config":"{\"card.width\":\"75%\"}" + } + ], + "audience": "{}" + }, + { + "id":4, + "name":"exp_test_fullon", + "iteration":1, + "unitType":"session_id", + "seedHi":856061641, + "seedLo":990838475, + "split":[ + 0.25, + 0.25, + 0.25, + 0.25 + ], + "trafficSeedHi":360868579, + "trafficSeedLo":330937933, + "trafficSplit":[ + 0.0, + 1.0 + ], + "fullOnVariant":2, + "applications":[ + { + "name":"website" + } + ], + "variants":[ + { + "name":"A", + "config":null + }, + { + "name":"B", + "config":"{\"submit.color\":\"red\",\"submit.shape\":\"circle\"}" + }, + { + "name":"C", + "config":"{\"submit.color\":\"blue\",\"submit.shape\":\"rect\"}" + }, + { + "name":"D", + "config":"{\"submit.color\":\"green\",\"submit.shape\":\"square\"}" + } + ], + "audience": "null" + } + ] +}