Skip to content

Commit ce0f26d

Browse files
authored
prepare 3.5.5 release (#125)
1 parent a10ffac commit ce0f26d

File tree

4 files changed

+263
-179
lines changed

4 files changed

+263
-179
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ jobs:
138138
echo "extension=apcu.so" | sudo tee -a /usr/local/etc/php/conf.d/apcu.ini;
139139
echo "apc.enable_cli = 1" | sudo tee -a /usr/local/etc/php/conf.d/apcu.ini
140140
- run: composer update --no-progress
141-
- run: vendor/bin/phpunit --log-junit ~/phpunit/junit.xml integration-tests/LDDFeatureRequesterTest.php
141+
- run: vendor/bin/phpunit --log-junit ~/phpunit/junit.xml integration-tests/CachedRedisFeatureRequesterTest.php
142142
- store_test_results:
143143
path: ~/phpunit
144144
- store_artifacts:
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
<?php
2+
namespace LaunchDarkly\Tests;
3+
4+
use LaunchDarkly\LDClient;
5+
use LaunchDarkly\LDUser;
6+
use LaunchDarkly\LDUserBuilder;
7+
use LaunchDarkly\LDDFeatureRequester;
8+
use LaunchDarkly\Integrations\Redis;
9+
use LaunchDarkly\ApcLDDFeatureRequester;
10+
use Predis\Client;
11+
use LaunchDarkly\ApcuLDDFeatureRequester;
12+
13+
/**
14+
* These tests use the LaunchDarkly Redis integration along with the optional caching behavior.
15+
* They are meant to be run in a PHP environment that has APCu installed. They will also test the
16+
* deprecated APC caching mode.
17+
*/
18+
class CachedRedisFeatureRequesterTest extends \PHPUnit_Framework_TestCase
19+
{
20+
const CACHE_SECONDS = 60;
21+
22+
public function testGetUncached()
23+
{
24+
$redis = self::makeRedisClient();
25+
26+
$client = self::makeLDClient([
27+
'feature_requester' => Redis::featureRequester()
28+
]);
29+
$user = self::makeUser();
30+
31+
$redis->del("launchdarkly:features");
32+
$this->assertEquals("jim", $client->variation('foo', $user, 'jim'));
33+
$redis->hset("launchdarkly:features", 'foo', self::genFeature("foo", "bar"));
34+
$this->assertEquals("bar", $client->variation('foo', $user, 'jim'));
35+
}
36+
37+
public function testGetApc()
38+
{
39+
if (!extension_loaded('apc')) {
40+
self::markTestSkipped('Install `apc` extension to run this test.');
41+
}
42+
43+
$this->doCachedGetTest(
44+
[
45+
// Redis::featureRequester() will always use APCu for caching, rather than APC. The only
46+
// way to specify APC is to use the deprecated ApcLDDFeatureRequester class.
47+
'feature_requester_class' => ApcLDDFeatureRequester::class,
48+
'apc_expiration' => static::CACHE_SECONDS
49+
],
50+
function ($cacheKey) {
51+
apc_delete($cacheKey);
52+
});
53+
}
54+
55+
public function testGetApcu()
56+
{
57+
if (!extension_loaded('apcu')) {
58+
self::markTestSkipped('Install `apcu` extension to run this test.');
59+
}
60+
61+
$this->doCachedGetTest(
62+
[
63+
'feature_requester' => Redis::featureRequester([ 'apc_expiration' => static::CACHE_SECONDS ])
64+
],
65+
function ($cacheKey) {
66+
apcu_delete($cacheKey);
67+
});
68+
}
69+
70+
private function doCachedGetTest($options, $clearFn)
71+
{
72+
$featureKey = 'fiz';
73+
$firstValue = 'first';
74+
$secondValue = 'second';
75+
$defaultValue = 'default';
76+
77+
$redis = self::makeRedisClient();
78+
79+
$client = self::makeLDClient($options);
80+
$user = self::makeUser();
81+
82+
$redis->del('launchdarkly:features');
83+
$clearFn("launchdarkly:features:{$featureKey}");
84+
85+
$this->assertEquals($defaultValue, $client->variation($featureKey, $user, $defaultValue));
86+
$redis->hset('launchdarkly:features', $featureKey, self::genFeature($featureKey, $firstValue));
87+
$this->assertEquals($firstValue, $client->variation($featureKey, $user, $defaultValue));
88+
89+
# cached value so not updated
90+
$redis->hset('launchdarkly:features', $featureKey, self::genFeature($featureKey, $secondValue));
91+
$this->assertEquals($firstValue, $client->variation($featureKey, $user, $defaultValue));
92+
93+
$clearFn("launchdarkly:features:{$featureKey}");
94+
95+
# cache has been cleared, should get new value from Redis
96+
$this->assertEquals($secondValue, $client->variation($featureKey, $user, $defaultValue));
97+
}
98+
99+
public function testGetAllWithoutFeatures()
100+
{
101+
$redis = self::makeRedisClient();
102+
$redis->flushall();
103+
104+
$client = self::makeLDClient([ 'feature_requester_class' => LDDFeatureRequester::class ]);
105+
$user = self::makeUser();
106+
$allFlags = $client->allFlags($user);
107+
108+
$this->assertEquals(array(), $allFlags);
109+
}
110+
111+
public function testGetAllUncached()
112+
{
113+
$featureKey = 'foo';
114+
$featureValue = 'bar';
115+
116+
$redis = self::makeRedisClient();
117+
118+
$client = self::makeLDClient([ 'feature_requester' => Redis::featureRequester() ]);
119+
$user = self::makeUser();
120+
121+
$redis->hset('launchdarkly:features', $featureKey, self::genFeature($featureKey, $featureValue));
122+
123+
$allFlags = $client->allFlags($user);
124+
125+
$this->assertInternalType('array', $allFlags);
126+
$this->assertArrayHasKey($featureKey, $allFlags);
127+
$this->assertEquals($featureValue, $allFlags[$featureKey]);
128+
}
129+
130+
public function testGetAllApc()
131+
{
132+
if (!extension_loaded('apc')) {
133+
self::markTestSkipped('Install `apc` extension to run this test.');
134+
}
135+
136+
$this->doCachedGetAllTest(
137+
[
138+
'feature_requester_class' => ApcLDDFeatureRequester::class,
139+
'apc_expiration' => static::CACHE_SECONDS
140+
],
141+
function ($cacheKey) {
142+
apc_delete($cacheKey);
143+
});
144+
}
145+
146+
public function testGetAllApcu()
147+
{
148+
if (!extension_loaded('apcu')) {
149+
self::markTestSkipped('Install `apcu` extension to run this test.');
150+
}
151+
152+
$this->doCachedGetAllTest(
153+
[
154+
'feature_requester' => Redis::featureRequester([ 'apc_expiration' => static::CACHE_SECONDS ])
155+
],
156+
function ($cacheKey) {
157+
apcu_delete($cacheKey);
158+
});
159+
}
160+
161+
private function doCachedGetAllTest($options, $clearFn)
162+
{
163+
$featureKey = 'foo';
164+
$firstValue = 'first';
165+
$secondValue = 'second';
166+
167+
$redis = self::makeRedisClient();
168+
169+
$client = self::makeLDClient($options);
170+
$user = self::makeUser();
171+
172+
$redis->hset('launchdarkly:features', $featureKey, self::genFeature($featureKey, $firstValue));
173+
$clearFn('launchdarkly:features:$all');
174+
175+
$allFlags = $client->allFlags($user);
176+
$this->assertInternalType('array', $allFlags);
177+
$this->assertArrayHasKey($featureKey, $allFlags);
178+
$this->assertEquals($firstValue, $allFlags[$featureKey]);
179+
180+
$redis->hset('launchdarkly:features', $featureKey, self::genFeature($featureKey, $secondValue));
181+
182+
# should still return cached value
183+
$allFlags = $client->allFlags($user);
184+
$this->assertArrayHasKey($featureKey, $allFlags);
185+
$this->assertEquals($firstValue, $allFlags[$featureKey]);
186+
187+
$clearFn('launchdarkly:features:$all');
188+
189+
# cache has been cleared, should get new value from Redis
190+
$allFlags = $client->allFlags($user);
191+
$this->assertArrayHasKey($featureKey, $allFlags);
192+
$this->assertEquals($secondValue, $allFlags[$featureKey]);
193+
}
194+
195+
private static function makeLDClient($options)
196+
{
197+
$options['send_events'] = false;
198+
return new LDClient('BOGUS_API_KEY', $options);
199+
}
200+
201+
private static function makeRedisClient()
202+
{
203+
return new Client(array('scheme' => 'tcp', 'host' => 'localhost', 'port' => 6379));
204+
}
205+
206+
private static function makeUser()
207+
{
208+
$builder = new LDUserBuilder('userkey');
209+
return $builder->build();
210+
}
211+
212+
private static function genFeature($key, $val)
213+
{
214+
$data = [
215+
'name' => 'Feature ' . $key,
216+
'key' => $key,
217+
'kind' => 'flag',
218+
'salt' => 'Zm9v',
219+
'on' => true,
220+
'variations' => [
221+
$val,
222+
false,
223+
],
224+
'version' => 4,
225+
'prerequisites' => [],
226+
'targets' => [
227+
[
228+
'values' => [
229+
$val,
230+
],
231+
'variation' => 0,
232+
],
233+
[
234+
'values' => [
235+
false,
236+
],
237+
'variation' => 1,
238+
],
239+
],
240+
'rules' => [],
241+
'fallthrough' => [
242+
'rollout' => [
243+
'variations' => [
244+
[
245+
'variation' => 0,
246+
'weight' => 95000,
247+
],
248+
[
249+
'variation' => 1,
250+
'weight' => 5000,
251+
],
252+
],
253+
],
254+
],
255+
'offVariation' => null,
256+
'deleted' => false,
257+
];
258+
259+
return \json_encode($data);
260+
}
261+
}

0 commit comments

Comments
 (0)