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

Commit

Permalink
Merge pull request #132 from launchdarkly/eb/ch45321/polling-tests
Browse files Browse the repository at this point in the history
add polling mode tests and end-to-end streaming test
  • Loading branch information
eli-darkly authored Aug 2, 2019
2 parents fd5dcda + 1a8f40f commit 37c456e
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 35 deletions.
11 changes: 0 additions & 11 deletions src/main/java/com/launchdarkly/client/FeatureFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
class FeatureFlag implements VersionedData {
private final static Logger logger = LoggerFactory.getLogger(FeatureFlag.class);

private static final Type mapType = new TypeToken<Map<String, FeatureFlag>>() {
}.getType();

private String key;
private int version;
private boolean on;
Expand All @@ -35,14 +32,6 @@ class FeatureFlag implements VersionedData {
private Long debugEventsUntilDate;
private boolean deleted;

static FeatureFlag fromJson(LDConfig config, String json) {
return config.gson.fromJson(json, FeatureFlag.class);
}

static Map<String, FeatureFlag> fromJsonMap(LDConfig config, String json) {
return config.gson.fromJson(json, mapType);
}

// We need this so Gson doesn't complain in certain java environments that restrict unsafe allocation
FeatureFlag() {}

Expand Down
16 changes: 3 additions & 13 deletions src/main/java/com/launchdarkly/client/FeatureRequestor.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,14 @@ static class AllData {
this.config = config;
}

Map<String, FeatureFlag> getAllFlags() throws IOException, HttpErrorException {
String body = get(GET_LATEST_FLAGS_PATH);
return FeatureFlag.fromJsonMap(config, body);
}

FeatureFlag getFlag(String featureKey) throws IOException, HttpErrorException {
String body = get(GET_LATEST_FLAGS_PATH + "/" + featureKey);
return FeatureFlag.fromJson(config, body);
}

Map<String, Segment> getAllSegments() throws IOException, HttpErrorException {
String body = get(GET_LATEST_SEGMENTS_PATH);
return Segment.fromJsonMap(config, body);
return config.gson.fromJson(body, FeatureFlag.class);
}

Segment getSegment(String segmentKey) throws IOException, HttpErrorException {
String body = get(GET_LATEST_SEGMENTS_PATH + "/" + segmentKey);
return Segment.fromJson(config, body);
return config.gson.fromJson(body, Segment.class);
}

AllData getAllData() throws IOException, HttpErrorException {
Expand All @@ -71,7 +61,7 @@ AllData getAllData() throws IOException, HttpErrorException {

private String get(String path) throws IOException, HttpErrorException {
Request request = getRequestBuilder(sdkKey)
.url(config.baseURI.toString() + path)
.url(config.baseURI.resolve(path).toURL())
.get()
.build();

Expand Down
11 changes: 0 additions & 11 deletions src/main/java/com/launchdarkly/client/Segment.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
import com.google.gson.reflect.TypeToken;

class Segment implements VersionedData {

private static final Type mapType = new TypeToken<Map<String, Segment>>() { }.getType();

private String key;
private List<String> included;
private List<String> excluded;
Expand All @@ -20,14 +17,6 @@ class Segment implements VersionedData {
private int version;
private boolean deleted;

static Segment fromJson(LDConfig config, String json) {
return config.gson.fromJson(json, Segment.class);
}

static Map<String, Segment> fromJsonMap(LDConfig config, String json) {
return config.gson.fromJson(json, mapType);
}

// We need this so Gson doesn't complain in certain java environments that restrict unsafe allocation
Segment() {}

Expand Down
166 changes: 166 additions & 0 deletions src/test/java/com/launchdarkly/client/FeatureRequestorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.launchdarkly.client;

import org.junit.Assert;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

import static com.launchdarkly.client.TestHttpUtil.baseConfig;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;

public class FeatureRequestorTest {
private static final String sdkKey = "sdk-key";
private static final String flag1Key = "flag1";
private static final String flag1Json = "{\"key\":\"" + flag1Key + "\"}";
private static final String flagsJson = "{\"" + flag1Key + "\":" + flag1Json + "}";
private static final String segment1Key = "segment1";
private static final String segment1Json = "{\"key\":\"" + segment1Key + "\"}";
private static final String segmentsJson = "{\"" + segment1Key + "\":" + segment1Json + "}";
private static final String allDataJson = "{\"flags\":" + flagsJson + ",\"segments\":" + segmentsJson + "}";

@Test
public void requestAllData() throws Exception {
MockResponse resp = new MockResponse();
resp.setHeader("Content-Type", "application/json");
resp.setBody(allDataJson);

try (MockWebServer server = TestHttpUtil.makeStartedServer(resp)) {
FeatureRequestor r = new FeatureRequestor(sdkKey, basePollingConfig(server).build());

FeatureRequestor.AllData data = r.getAllData();

RecordedRequest req = server.takeRequest();
assertEquals("/sdk/latest-all", req.getPath());
verifyHeaders(req);

assertNotNull(data);
assertNotNull(data.flags);
assertNotNull(data.segments);
assertEquals(1, data.flags.size());
assertEquals(1, data.flags.size());
verifyFlag(data.flags.get(flag1Key), flag1Key);
verifySegment(data.segments.get(segment1Key), segment1Key);
}
}

@Test
public void requestFlag() throws Exception {
MockResponse resp = new MockResponse();
resp.setHeader("Content-Type", "application/json");
resp.setBody(flag1Json);

try (MockWebServer server = TestHttpUtil.makeStartedServer(resp)) {
FeatureRequestor r = new FeatureRequestor(sdkKey, basePollingConfig(server).build());

FeatureFlag flag = r.getFlag(flag1Key);

RecordedRequest req = server.takeRequest();
assertEquals("/sdk/latest-flags/" + flag1Key, req.getPath());
verifyHeaders(req);

verifyFlag(flag, flag1Key);
}
}

@Test
public void requestSegment() throws Exception {
MockResponse resp = new MockResponse();
resp.setHeader("Content-Type", "application/json");
resp.setBody(segment1Json);

try (MockWebServer server = TestHttpUtil.makeStartedServer(resp)) {
FeatureRequestor r = new FeatureRequestor(sdkKey, basePollingConfig(server).build());

Segment segment = r.getSegment(segment1Key);

RecordedRequest req = server.takeRequest();
assertEquals("/sdk/latest-segments/" + segment1Key, req.getPath());
verifyHeaders(req);

verifySegment(segment, segment1Key);
}
}

@Test
public void requestFlagNotFound() throws Exception {
MockResponse notFoundResp = new MockResponse().setResponseCode(404);

try (MockWebServer server = TestHttpUtil.makeStartedServer(notFoundResp)) {
FeatureRequestor r = new FeatureRequestor(sdkKey, basePollingConfig(server).build());

try {
r.getFlag(flag1Key);
Assert.fail("expected exception");
} catch (HttpErrorException e) {
assertEquals(404, e.getStatus());
}
}
}

@Test
public void requestSegmentNotFound() throws Exception {
MockResponse notFoundResp = new MockResponse().setResponseCode(404);

try (MockWebServer server = TestHttpUtil.makeStartedServer(notFoundResp)) {
FeatureRequestor r = new FeatureRequestor(sdkKey, basePollingConfig(server).build());

try {
r.getSegment(segment1Key);
Assert.fail("expected exception");
} catch (HttpErrorException e) {
assertEquals(404, e.getStatus());
}
}
}

@Test
public void requestsAreCached() throws Exception {
MockResponse cacheableResp = new MockResponse();
cacheableResp.setHeader("Content-Type", "application/json");
cacheableResp.setHeader("ETag", "aaa");
cacheableResp.setHeader("Cache-Control", "max-age=1000");
cacheableResp.setBody(flag1Json);

try (MockWebServer server = TestHttpUtil.makeStartedServer(cacheableResp)) {
FeatureRequestor r = new FeatureRequestor(sdkKey, basePollingConfig(server).build());

FeatureFlag flag1a = r.getFlag(flag1Key);

RecordedRequest req1 = server.takeRequest();
assertEquals("/sdk/latest-flags/" + flag1Key, req1.getPath());
verifyHeaders(req1);

verifyFlag(flag1a, flag1Key);

FeatureFlag flag1b = r.getFlag(flag1Key);
verifyFlag(flag1b, flag1Key);
assertNull(server.takeRequest(0, TimeUnit.SECONDS)); // there was no second request, due to the cache hit
}
}

private LDConfig.Builder basePollingConfig(MockWebServer server) {
return baseConfig(server)
.stream(false);
}

private void verifyHeaders(RecordedRequest req) {
assertEquals(sdkKey, req.getHeader("Authorization"));
assertEquals("JavaClient/" + LDClient.CLIENT_VERSION, req.getHeader("User-Agent"));
}

private void verifyFlag(FeatureFlag flag, String key) {
assertNotNull(flag);
assertEquals(key, flag.getKey());
}

private void verifySegment(Segment segment, String key) {
assertNotNull(segment);
assertEquals(key, segment.getKey());
}
}
116 changes: 116 additions & 0 deletions src/test/java/com/launchdarkly/client/LDClientEndToEndTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.launchdarkly.client;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import org.junit.Assert;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

import static com.launchdarkly.client.TestHttpUtil.baseConfig;
import static com.launchdarkly.client.TestHttpUtil.makeStartedServer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import okhttp3.mockwebserver.SocketPolicy;

public class LDClientEndToEndTest {
private static final Gson gson = new Gson();
private static final String sdkKey = "sdk-key";
private static final String flagKey = "flag1";
private static final FeatureFlag flag = new FeatureFlagBuilder(flagKey)
.offVariation(0).variations(new JsonPrimitive(true))
.build();
private static final LDUser user = new LDUser("user-key");

@Test
public void clientStartsInPollingMode() throws Exception {
MockResponse resp = new MockResponse()
.setHeader("Content-Type", "application/json")
.setBody(makeAllDataJson());

try (MockWebServer server = makeStartedServer(resp)) {
LDConfig config = baseConfig(server)
.stream(false)
.sendEvents(false)
.build();

try (LDClient client = new LDClient(sdkKey, config)) {
assertTrue(client.initialized());
assertTrue(client.boolVariation(flagKey, user, false));
}
}
}

@Test
public void clientFailsInPollingModeWith401Error() throws Exception {
MockResponse resp = new MockResponse().setResponseCode(401);

try (MockWebServer server = makeStartedServer(resp)) {
LDConfig config = baseConfig(server)
.stream(false)
.sendEvents(false)
.build();

try (LDClient client = new LDClient(sdkKey, config)) {
assertFalse(client.initialized());
assertFalse(client.boolVariation(flagKey, user, false));
}
}
}

@Test
public void clientStartsInStreamingMode() throws Exception {
String eventData = "event: put\n" +
"data: {\"data\":" + makeAllDataJson() + "}\n\n";

MockResponse resp = new MockResponse()
.setHeader("Content-Type", "text/event-stream")
.setChunkedBody(eventData, 1000)
.setSocketPolicy(SocketPolicy.KEEP_OPEN);

try (MockWebServer server = makeStartedServer(resp)) {
LDConfig config = baseConfig(server)
.sendEvents(false)
.build();

try (LDClient client = new LDClient(sdkKey, config)) {
assertTrue(client.initialized());
assertTrue(client.boolVariation(flagKey, user, false));
}
}
}

@Test
public void clientFailsInStreamingModeWith401Error() throws Exception {
MockResponse resp = new MockResponse().setResponseCode(401);

try (MockWebServer server = makeStartedServer(resp)) {
LDConfig config = baseConfig(server)
.sendEvents(false)
.build();

try (LDClient client = new LDClient(sdkKey, config)) {
assertFalse(client.initialized());
assertFalse(client.boolVariation(flagKey, user, false));
}
}
}

public String makeAllDataJson() {
JsonObject flagsData = new JsonObject();
flagsData.add(flagKey, gson.toJsonTree(flag));
JsonObject allData = new JsonObject();
allData.add("flags", flagsData);
allData.add("segments", new JsonObject());
return gson.toJson(allData);
}
}
Loading

0 comments on commit 37c456e

Please sign in to comment.