Skip to content

Commit 04f5c20

Browse files
[FSSDK-9022] Dev Containers and bug bash (#269)
* Add bug bash autoloader * Initial version of decide tests * Fix autoloads; DRY print to console * Refactor and clean using * Refactor; Fix array outputs * Better output in linux env * Add notification on decide test * Output log to ensure logx impression sent * Verify error is logged for invalid flag * Add devcontainer config * Install dependencies in devcontainer * Update decide instructions * Add postCreateCommand shell postCreateCommand * Fix path on postCreateCommand * Finish devcontainer config * Refine decide.php * Renamed files & their refs * Instruction update * Add DecideAll skeleton * Initial DecideAll test * Finish DecideAll test; Decide fixes * DecideForKeys skeleton * Add decide for keys; update other decide classes * Comment DecideForKeys test calls * Track event test class skeleton * Update attributes in decide tests * Add track event tests * DRY $onTrackEvent and use in negative test * Event Key instead of "name" * Forced Decision skeleton * Add PHP documentation links * Initial attempt forced decision tests * Remove sensitive info * Added second part for forced decisions - not necessary, just a advanced addition. And Optimizely config. * Delete ForcedDecision.php removing this file, duplicate * Rename ForcedDecisionPart2.php to ForcedDecision.php * Update ForcedDecision.php * Update OptiConfig.php * Update EventBuilder.php SDK minor release bump * Update EventBuilderTest.php release version bump in the test to 3.10.0 * Update EventBuilder.php-revert * Update EventBuilderTest.php - revert --------- Co-authored-by: Mike Chu <[email protected]> Co-authored-by: Mike Chu <[email protected]>
1 parent e197aa4 commit 04f5c20

File tree

8 files changed

+1041
-0
lines changed

8 files changed

+1041
-0
lines changed

.devcontainer/devcontainer.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/php
3+
{
4+
"name": "PHP",
5+
6+
"remoteEnv": {
7+
"SDK_ROOT": "/workspaces/php-sdk",
8+
"XDEBUG_CONFIG": "log_level=0",
9+
},
10+
11+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
12+
"image": "mcr.microsoft.com/devcontainers/php:0-8.2",
13+
14+
// Features to add to the dev container. More info: https://containers.dev/features.
15+
// "features": {},
16+
17+
"postStartCommand": "composer install",
18+
19+
// Configure tool-specific properties.
20+
// "customizations": {},
21+
22+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
23+
"forwardPorts": [
24+
8080
25+
],
26+
"customizations": {
27+
"vscode": {
28+
"extensions": [
29+
"bmewburn.vscode-intelephense-client",
30+
"xdebug.php-debug",
31+
"DEVSENSE.composer-php-vscode"
32+
]
33+
}
34+
}
35+
36+
// Use 'postCreateCommand' to run commands after the container is created.
37+
// "postCreateCommand": "sudo chmod a+x \"$(pwd)\" && sudo rm -rf /var/www/html && sudo ln -s \"$(pwd)\" /var/www/html"
38+
39+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
40+
// "remoteUser": "root"
41+
}

bug-bash/Decide.php

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
namespace Optimizely\BugBash;
4+
5+
require_once '../vendor/autoload.php';
6+
require_once '../bug-bash/_bug-bash-autoload.php';
7+
8+
use Monolog\Logger;
9+
use Optimizely\Decide\OptimizelyDecideOption;
10+
use Optimizely\Logger\DefaultLogger;
11+
use Optimizely\Notification\NotificationType;
12+
use Optimizely\Optimizely;
13+
use Optimizely\OptimizelyFactory;
14+
use Optimizely\OptimizelyUserContext;
15+
16+
// 1. Change this SDK key to your project's SDK Key
17+
const SDK_KEY = '<your-sdk-key>';
18+
19+
// 2. Change this to your flag key
20+
const FLAG_KEY = '<your-flag-key>';
21+
22+
// 3. Uncomment each scenario 1 by 1 modifying the contents of the method
23+
// to test additional scenarios.
24+
25+
$test = new DecideTests();
26+
$test->verifyDecisionProperties();
27+
// $test->testWithVariationsOfDecideOptions();
28+
// $test->verifyLogsImpressionsEventsDispatched();
29+
// $test->verifyResultsPageInYourProjectShowsImpressionEvent();
30+
// $test->verifyDecisionListenerWasCalled();
31+
// $test->verifyAnInvalidFlagKeyIsHandledCorrectly();
32+
33+
// 4. Change the current folder into the bug-bash directory
34+
// cd bug-bash/
35+
36+
// 5. Run the following command to execute the uncommented tests above:
37+
// php Decide.php
38+
39+
// https://docs.developers.optimizely.com/feature-experimentation/docs/decide-methods-php
40+
class DecideTests
41+
{
42+
// verify decision return properties with default DecideOptions
43+
public function verifyDecisionProperties(): void
44+
{
45+
$decision = $this->userContext->decide(FLAG_KEY);
46+
47+
$this->printDecision($decision, "Check that the following decision properties are expected for user $this->userId");
48+
}
49+
50+
// test decide w all options: DISABLE_DECISION_EVENT, ENABLED_FLAGS_ONLY, IGNORE_USER_PROFILE_SERVICE, INCLUDE_REASONS, EXCLUDE_VARIABLES (will need to add variables)
51+
public function testWithVariationsOfDecideOptions(): void
52+
{
53+
$options = [
54+
OptimizelyDecideOption::INCLUDE_REASONS,
55+
// OptimizelyDecideOption::DISABLE_DECISION_EVENT,
56+
// OptimizelyDecideOption::ENABLED_FLAGS_ONLY, // ⬅️ Disable some of your flags
57+
// OptimizelyDecideOption::IGNORE_USER_PROFILE_SERVICE,
58+
// OptimizelyDecideOption::EXCLUDE_VARIABLES,
59+
];
60+
61+
$decision = $this->userContext->decide(FLAG_KEY, $options);
62+
63+
$this->printDecision($decision, 'Modify the OptimizelyDecideOptions and check the decision variables expected');
64+
}
65+
66+
// verify in logs that impression event of this decision was dispatched
67+
public function verifyLogsImpressionsEventsDispatched(): void
68+
{
69+
// 💡️ Create a new flag with an A/B Test eg "product_version"
70+
$featureFlagKey = 'product_version';
71+
$logger = new DefaultLogger(Logger::DEBUG);
72+
$localOptimizelyClient = new Optimizely(datafile: null, logger: $logger, sdkKey: SDK_KEY);
73+
$localUserContext = $localOptimizelyClient->createUserContext($this->userId);
74+
75+
// review the DEBUG output, ensuring you see an impression log
76+
// "Dispatching impression event to URL https://logx.optimizely.com/v1/events with params..."
77+
$localUserContext->decide($featureFlagKey);
78+
}
79+
80+
// verify on Results page that impression even was created
81+
public function verifyResultsPageInYourProjectShowsImpressionEvent(): void
82+
{
83+
print "Go to your project's results page and verify decisions events are showing (5 min delay)";
84+
}
85+
86+
// verify that decision listener contains correct information
87+
public function verifyDecisionListenerWasCalled(): void
88+
{
89+
// Check that this was called during the...
90+
$onDecision = function ($type, $userId, $attributes, $decisionInfo) {
91+
print ">>> [$this->outputTag] OnDecision:
92+
type: $type,
93+
userId: $userId,
94+
attributes: " . print_r($attributes, true) . "
95+
decisionInfo: " . print_r($decisionInfo, true) . "\r\n";
96+
};
97+
$this->optimizelyClient->notificationCenter->addNotificationListener(
98+
NotificationType::DECISION,
99+
$onDecision
100+
);
101+
102+
// ...decide.
103+
$this->userContext->decide(FLAG_KEY);
104+
}
105+
106+
// verify that invalid flag key is handled correctly
107+
public function verifyAnInvalidFlagKeyIsHandledCorrectly(): void
108+
{
109+
$logger = new DefaultLogger(Logger::ERROR);
110+
$localOptimizelyClient = new Optimizely(datafile: null, logger: $logger, sdkKey: SDK_KEY);
111+
$userId = 'user-' . mt_rand(10, 99);
112+
$localUserContext = $localOptimizelyClient->createUserContext($userId);
113+
114+
// ensure you see an error -- Optimizely.ERROR: FeatureFlag Key "a_key_not_in_the_project" is not in datafile.
115+
$localUserContext->decide("a_key_not_in_the_project");
116+
}
117+
118+
private Optimizely $optimizelyClient;
119+
private string $userId;
120+
private ?OptimizelyUserContext $userContext;
121+
private string $outputTag = "Decide";
122+
123+
public function __construct()
124+
{
125+
$this->optimizelyClient = OptimizelyFactory::createDefaultInstance(SDK_KEY);
126+
127+
$this->userId = 'user-' . mt_rand(10, 99);
128+
$attributes = ['age' => 25, 'country' => 'canada', 'abandoned_cart' => false];
129+
$this->userContext = $this->optimizelyClient->createUserContext($this->userId, $attributes);
130+
}
131+
132+
private function printDecision($decision, $message): void
133+
{
134+
$enabled = $decision->getEnabled() ? "true" : "false";
135+
136+
print ">>> [$this->outputTag] $message:
137+
enabled: $enabled,
138+
flagKey: {$decision->getFlagKey()},
139+
ruleKey: {$decision->getRuleKey()},
140+
variationKey: {$decision->getVariationKey()},
141+
variables: " . print_r($decision->getVariables(), true) . ",
142+
reasons: " . print_r($decision->getReasons(), true) . "\r\n";
143+
}
144+
}

bug-bash/DecideAll.php

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
namespace Optimizely\BugBash;
4+
5+
require_once '../vendor/autoload.php';
6+
require_once '../bug-bash/_bug-bash-autoload.php';
7+
8+
use Monolog\Logger;
9+
use Optimizely\Decide\OptimizelyDecideOption;
10+
use Optimizely\Logger\DefaultLogger;
11+
use Optimizely\Notification\NotificationType;
12+
use Optimizely\Optimizely;
13+
use Optimizely\OptimizelyFactory;
14+
use Optimizely\OptimizelyUserContext;
15+
16+
// 1. Change this SDK key to your project's SDK Key
17+
const SDK_KEY = '<your-sdk-key>';
18+
19+
// 2. Create additional flag keys in your project (2+)
20+
21+
// 3. Uncomment each scenario 1 by 1 modifying the contents of the method
22+
// to test additional scenarios.
23+
24+
$test = new DecideAllTests();
25+
$test->verifyDecisionProperties();
26+
// $test->testWithVariousCombinationsOfOptions();
27+
// $test->verifyLogImpressionEventDispatched();
28+
// $test->verifyResultsPageShowsImpressionEvents();
29+
// $test->verifyDecisionListenerContainsCorrectInformation();
30+
31+
// 4. Change the current folder into the bug-bash directory if you're not already there:
32+
// cd bug-bash/
33+
34+
// 5. Run the following command to execute the uncommented tests above:
35+
// php DecideAll.php
36+
37+
// https://docs.developers.optimizely.com/feature-experimentation/docs/decide-methods-php
38+
class DecideAllTests
39+
{
40+
// verify decide all returns properties without specifying default options
41+
public function verifyDecisionProperties(): void
42+
{
43+
$decision = $this->userContext->decideAll();
44+
45+
$this->printDecisions($decision, "Check that all of the decisions' multiple properties are expected for user `$this->userId`");
46+
}
47+
48+
// test with all and variations/combinations of options
49+
public function testWithVariousCombinationsOfOptions(): void
50+
{
51+
$options = [
52+
OptimizelyDecideOption::INCLUDE_REASONS,
53+
// OptimizelyDecideOption::DISABLE_DECISION_EVENT,
54+
// OptimizelyDecideOption::ENABLED_FLAGS_ONLY, // ⬅️ Disable some of your flags
55+
// OptimizelyDecideOption::IGNORE_USER_PROFILE_SERVICE,
56+
OptimizelyDecideOption::EXCLUDE_VARIABLES,
57+
];
58+
59+
$decisions = $this->userContext->decideAll($options);
60+
61+
$this->printDecisions($decisions, "Check that all of your flags' decisions respected the options passed.");
62+
}
63+
64+
// verify in logs that impression event of this decision was dispatched
65+
public function verifyLogImpressionEventDispatched(): void
66+
{
67+
// 💡️ Be sure you have >=1 of your project's flags has an EXPERIMENT type
68+
$logger = new DefaultLogger(Logger::DEBUG);
69+
$localOptimizelyClient = new Optimizely(datafile: null, logger: $logger, sdkKey: SDK_KEY);
70+
$localUserContext = $localOptimizelyClient->createUserContext($this->userId);
71+
72+
// review the DEBUG output, ensuring you see an impression log for each *EXPERIMENT* with a message like
73+
// "Dispatching impression event to URL https://logx.optimizely.com/v1/events with params..."
74+
// ⚠️ Rollout flag types should not dispatch and impression event
75+
$localUserContext->decideAll();
76+
}
77+
78+
// verify on Results page that impression events was created
79+
public function verifyResultsPageShowsImpressionEvents(): void
80+
{
81+
print "After about 5-10 minutes, go to your project's results page and verify decisions events are showing.";
82+
}
83+
84+
// verify that decision listener contains correct information
85+
public function verifyDecisionListenerContainsCorrectInformation(): void
86+
{
87+
// Check that this was called for each of your project flag keys
88+
$onDecision = function ($type, $userId, $attributes, $decisionInfo) {
89+
print ">>> [$this->outputTag] OnDecision:
90+
type: $type,
91+
userId: $userId,
92+
attributes: " . print_r($attributes, true) . "
93+
decisionInfo: " . print_r($decisionInfo, true) . "\r\n";
94+
};
95+
$this->optimizelyClient->notificationCenter->addNotificationListener(
96+
NotificationType::DECISION,
97+
$onDecision
98+
);
99+
100+
$this->userContext->decideAll();
101+
}
102+
103+
private Optimizely $optimizelyClient;
104+
private string $userId;
105+
private ?OptimizelyUserContext $userContext;
106+
private string $outputTag = "Decide All";
107+
108+
public function __construct()
109+
{
110+
$this->optimizelyClient = OptimizelyFactory::createDefaultInstance(SDK_KEY);
111+
112+
$this->userId = 'user-' . mt_rand(10, 99);
113+
$attributes = ['country' => 'nederland', 'age' => 43, 'is_return_visitor' => true];
114+
$this->userContext = $this->optimizelyClient->createUserContext($this->userId, $attributes);
115+
}
116+
117+
private function printDecisions($decisions, $message): void
118+
{
119+
$count = 0;
120+
foreach ($decisions as $decision) {
121+
$enabled = $decision->getEnabled() ? "true" : "false";
122+
123+
print ">>> [$this->outputTag #$count] $message:
124+
enabled: $enabled,
125+
flagKey: {$decision->getFlagKey()},
126+
ruleKey: {$decision->getRuleKey()},
127+
variationKey: {$decision->getVariationKey()},
128+
variables: " . print_r($decision->getVariables(), true) . ",
129+
reasons: " . print_r($decision->getReasons(), true) . "\r\n";
130+
131+
$count++;
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)