Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit d41f59e

Browse files
committed
Added a TestFeatureStore which helps programatically flip flag values on or off with a public API.
1 parent 1f6dc9b commit d41f59e

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.launchdarkly.client;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
5+
/**
6+
* A decorated {@link InMemoryFeatureStore} which provides functionality to create (or override) "on" or "off" feature flags for all users.
7+
*
8+
* Using this store is useful for testing purposes when you want to have runtime support for turning specific features "on" or "off".
9+
*
10+
*/
11+
public class TestFeatureStore extends InMemoryFeatureStore {
12+
13+
private AtomicInteger version = new AtomicInteger(0);
14+
15+
/**
16+
* Turns a feature, identified by key, "on" for every user. If the feature rules already exist in the store then it will override it to be "on" for every {@link LDUser}.
17+
* If the feature rule is not currently in the store, it will create one that is "on" for every {@link LDUser}.
18+
*
19+
* @param key the key of the feature flag to be "on".
20+
*/
21+
public void turnFeatureOn(String key) {
22+
writeFeatureRep(key, new Variation.Builder<>(true, 100).build());
23+
}
24+
25+
/**
26+
* Turns a feature, identified by key, "off" for every user. If the feature rules already exists in the store then it will override it to be "off" for every {@link LDUser}.
27+
* If the feature rule is not currently in the store, it will create one that is "off" for every {@link LDUser}.
28+
*
29+
* @param key the key of the feature flag to be "off".
30+
*/
31+
public void turnFeatureOff(String key) {
32+
writeFeatureRep(key, new Variation.Builder<>(false, 100).build());
33+
}
34+
35+
private void writeFeatureRep(final String key, final Variation<Boolean> variation) {
36+
FeatureRep<Boolean> newFeature = new FeatureRep.Builder<Boolean>(String.format("test-%s", key), key)
37+
.variation(variation).version(version.incrementAndGet()).build();
38+
upsert(key, newFeature);
39+
}
40+
}

src/test/java/com/launchdarkly/client/LDClientTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
import org.junit.Test;
66

77
import java.io.IOException;
8+
import java.io.ObjectInput;
9+
import java.util.concurrent.ExecutionException;
810
import java.util.concurrent.Future;
911
import java.util.concurrent.TimeUnit;
1012
import java.util.concurrent.TimeoutException;
1113

1214
import static org.easymock.EasyMock.anyObject;
1315
import static org.easymock.EasyMock.expect;
1416
import static org.junit.Assert.assertEquals;
17+
import static org.junit.Assert.assertFalse;
1518
import static org.junit.Assert.assertTrue;
1619

1720
public class LDClientTest extends EasyMockSupport {
@@ -45,6 +48,76 @@ public void testOffline() throws IOException {
4548
verifyAll();
4649
}
4750

51+
@Test
52+
public void testTestFeatureStoreFlagOn() throws IOException, InterruptedException, ExecutionException, TimeoutException {
53+
TestFeatureStore testFeatureStore = new TestFeatureStore();
54+
LDConfig config = new LDConfig.Builder()
55+
.startWaitMillis(10L)
56+
.stream(false)
57+
.featureStore(testFeatureStore)
58+
.build();
59+
60+
expect(initFuture.get(10L, TimeUnit.MILLISECONDS)).andReturn(new Object());
61+
expect(pollingProcessor.start()).andReturn(initFuture);
62+
expect(pollingProcessor.initialized()).andReturn(true).times(1);
63+
expect(eventProcessor.sendEvent(anyObject(Event.class))).andReturn(true);
64+
replayAll();
65+
66+
client = createMockClient(config);
67+
testFeatureStore.turnFeatureOn("key");
68+
assertTrue("Test flag should be on, but was not.", client.toggle("key", new LDUser("user"), false));
69+
70+
verifyAll();
71+
}
72+
73+
@Test
74+
public void testTestFeatureStoreFlagOff() throws IOException, InterruptedException, ExecutionException, TimeoutException {
75+
TestFeatureStore testFeatureStore = new TestFeatureStore();
76+
LDConfig config = new LDConfig.Builder()
77+
.startWaitMillis(10L)
78+
.stream(false)
79+
.featureStore(testFeatureStore)
80+
.build();
81+
82+
expect(initFuture.get(10L, TimeUnit.MILLISECONDS)).andReturn(new Object());
83+
expect(pollingProcessor.start()).andReturn(initFuture);
84+
expect(pollingProcessor.initialized()).andReturn(true).times(1);
85+
expect(eventProcessor.sendEvent(anyObject(Event.class))).andReturn(true);
86+
replayAll();
87+
88+
client = createMockClient(config);
89+
testFeatureStore.turnFeatureOff("key");
90+
assertFalse("Test flag should be off, but was on (the default).", client.toggle("key", new LDUser("user"), true));
91+
92+
verifyAll();
93+
}
94+
95+
@Test
96+
public void testTestFeatureStoreFlagOnThenOff() throws IOException, InterruptedException, ExecutionException, TimeoutException {
97+
TestFeatureStore testFeatureStore = new TestFeatureStore();
98+
LDConfig config = new LDConfig.Builder()
99+
.startWaitMillis(10L)
100+
.stream(false)
101+
.featureStore(testFeatureStore)
102+
.build();
103+
104+
expect(initFuture.get(10L, TimeUnit.MILLISECONDS)).andReturn(new Object());
105+
expect(pollingProcessor.start()).andReturn(initFuture);
106+
expect(pollingProcessor.initialized()).andReturn(true).times(2);
107+
expect(eventProcessor.sendEvent(anyObject(Event.class))).andReturn(true).times(2);
108+
replayAll();
109+
110+
client = createMockClient(config);
111+
112+
testFeatureStore.turnFeatureOn("key");
113+
assertTrue("Test flag should be on, but was not.", client.toggle("key", new LDUser("user"), false));
114+
115+
testFeatureStore.turnFeatureOff("key");
116+
assertFalse("Test flag should be off, but was on (the default).", client.toggle("key", new LDUser("user"), true));
117+
118+
verifyAll();
119+
}
120+
48121
@Test
49122
public void testUseLdd() throws IOException {
50123
LDConfig config = new LDConfig.Builder()

0 commit comments

Comments
 (0)