Skip to content

Commit 95e149e

Browse files
fix(gofeatureflag): fix unreachable code when flag not found (#1679)
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
1 parent fed5f39 commit 95e149e

2 files changed

Lines changed: 107 additions & 4 deletions

File tree

providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/service/EvaluationService.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ public <T> ProviderEvaluation<T> getEvaluation(
6868

6969
val goffResp = evaluator.evaluate(flagKey, defaultValue, evaluationContext);
7070

71+
// Check for FLAG_NOT_FOUND error first, before general error handling
72+
if (goffResp.getErrorCode() != null
73+
&& ErrorCode.FLAG_NOT_FOUND.name().equalsIgnoreCase(goffResp.getErrorCode())) {
74+
throw new FlagNotFoundError("Flag " + flagKey + " was not found in your configuration");
75+
}
76+
7177
// If we have an error code, we return the error directly.
7278
if (goffResp.getErrorCode() != null && !goffResp.getErrorCode().isEmpty()) {
7379
return ProviderEvaluation.<T>builder()
@@ -88,10 +94,6 @@ public <T> ProviderEvaluation<T> getEvaluation(
8894
.build();
8995
}
9096

91-
if (ErrorCode.FLAG_NOT_FOUND.name().equalsIgnoreCase(goffResp.getErrorCode())) {
92-
throw new FlagNotFoundError("Flag " + flagKey + " was not found in your configuration");
93-
}
94-
9597
// Convert the value received from the API.
9698
T flagValue = convertValue(goffResp.getValue(), expectedType);
9799

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package dev.openfeature.contrib.providers.gofeatureflag.service;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.ArgumentMatchers.anyString;
7+
import static org.mockito.Mockito.mock;
8+
import static org.mockito.Mockito.when;
9+
10+
import dev.openfeature.contrib.providers.gofeatureflag.bean.GoFeatureFlagResponse;
11+
import dev.openfeature.contrib.providers.gofeatureflag.evaluator.IEvaluator;
12+
import dev.openfeature.sdk.ErrorCode;
13+
import dev.openfeature.sdk.EvaluationContext;
14+
import dev.openfeature.sdk.ImmutableContext;
15+
import dev.openfeature.sdk.ProviderEvaluation;
16+
import dev.openfeature.sdk.Reason;
17+
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
18+
import org.junit.jupiter.api.BeforeEach;
19+
import org.junit.jupiter.api.DisplayName;
20+
import org.junit.jupiter.api.Test;
21+
22+
@DisplayName("EvaluationService tests")
23+
class EvaluationServiceTest {
24+
private IEvaluator mockEvaluator;
25+
private EvaluationService evaluationService;
26+
private EvaluationContext evaluationContext;
27+
28+
@BeforeEach
29+
void setUp() {
30+
mockEvaluator = mock(IEvaluator.class);
31+
evaluationService = new EvaluationService(mockEvaluator);
32+
evaluationContext = new ImmutableContext("test-targeting-key");
33+
}
34+
35+
@DisplayName("Should throw FlagNotFoundError when flag is not found")
36+
@Test
37+
void shouldThrowFlagNotFoundErrorWhenFlagIsNotFound() {
38+
// Given: evaluator returns a response with FLAG_NOT_FOUND error code
39+
GoFeatureFlagResponse response = new GoFeatureFlagResponse();
40+
response.setErrorCode(ErrorCode.FLAG_NOT_FOUND.name());
41+
response.setErrorDetails("Flag test-flag was not found in your configuration");
42+
response.setValue(false);
43+
44+
when(mockEvaluator.evaluate(anyString(), any(), any(EvaluationContext.class)))
45+
.thenReturn(response);
46+
47+
// When/Then: getEvaluation should throw FlagNotFoundError
48+
FlagNotFoundError exception = assertThrows(
49+
FlagNotFoundError.class,
50+
() -> evaluationService.getEvaluation("test-flag", false, evaluationContext, Boolean.class));
51+
52+
assertEquals("Flag test-flag was not found in your configuration", exception.getMessage());
53+
}
54+
55+
@DisplayName("Should return error response for other error codes")
56+
@Test
57+
void shouldReturnErrorResponseForOtherErrorCodes() {
58+
// Given: evaluator returns a response with a different error code
59+
GoFeatureFlagResponse response = new GoFeatureFlagResponse();
60+
response.setErrorCode(ErrorCode.GENERAL.name());
61+
response.setErrorDetails("Some other error occurred");
62+
response.setValue(false);
63+
64+
when(mockEvaluator.evaluate(anyString(), any(), any(EvaluationContext.class)))
65+
.thenReturn(response);
66+
67+
// When: getEvaluation is called
68+
ProviderEvaluation<Boolean> result =
69+
evaluationService.getEvaluation("test-flag", false, evaluationContext, Boolean.class);
70+
71+
// Then: should return error response, not throw exception
72+
assertEquals(ErrorCode.GENERAL, result.getErrorCode());
73+
assertEquals("Some other error occurred", result.getErrorMessage());
74+
assertEquals(Reason.ERROR.name(), result.getReason());
75+
assertEquals(false, result.getValue());
76+
}
77+
78+
@DisplayName("Should handle successful evaluation")
79+
@Test
80+
void shouldHandleSuccessfulEvaluation() {
81+
// Given: evaluator returns a successful response
82+
GoFeatureFlagResponse response = new GoFeatureFlagResponse();
83+
response.setValue(true);
84+
response.setReason(Reason.TARGETING_MATCH.name());
85+
response.setVariationType("enabled");
86+
response.setErrorCode(null);
87+
88+
when(mockEvaluator.evaluate(anyString(), any(), any(EvaluationContext.class)))
89+
.thenReturn(response);
90+
91+
// When: getEvaluation is called
92+
ProviderEvaluation<Boolean> result =
93+
evaluationService.getEvaluation("test-flag", false, evaluationContext, Boolean.class);
94+
95+
// Then: should return successful evaluation
96+
assertEquals(true, result.getValue());
97+
assertEquals(Reason.TARGETING_MATCH.name(), result.getReason());
98+
assertEquals("enabled", result.getVariant());
99+
assertEquals(null, result.getErrorCode());
100+
}
101+
}

0 commit comments

Comments
 (0)