Skip to content

Commit 5616735

Browse files
committed
test: add unit tests for feature flag functionality
1 parent 9504af5 commit 5616735

File tree

6 files changed

+557
-8
lines changed

6 files changed

+557
-8
lines changed

src/main/java/org/broadinstitute/consent/http/db/FeatureFlagDAO.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ public interface FeatureFlagDAO extends Transactional<FeatureFlagDAO> {
2626
* @param id The feature flag id
2727
* @return FeatureFlag or null if not found
2828
*/
29-
@SqlQuery(
30-
"SELECT id, value, create_date, update_date FROM feature_flag WHERE id = :id")
29+
@SqlQuery("SELECT id, value, create_date, update_date FROM feature_flag WHERE id = :id")
3130
FeatureFlag findById(@Bind("id") String id);
3231

3332
/**

src/main/java/org/broadinstitute/consent/http/service/FeatureFlagService.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public FeatureFlag getFeatureFlagById(String id) {
4141
}
4242

4343
/**
44-
* Get the value of a feature flag by id, or return null if not found
45-
* This is a helper method for other services
44+
* Get the value of a feature flag by id, or return null if not found This is a helper method for
45+
* other services
4646
*
4747
* @param id The feature flag id
4848
* @return The feature flag value, or null if not found
@@ -53,8 +53,8 @@ public String getFeatureFlagValue(String id) {
5353
}
5454

5555
/**
56-
* Get the value of a feature flag by id, or return a default value if not found
57-
* This is a helper method for other services
56+
* Get the value of a feature flag by id, or return a default value if not found This is a helper
57+
* method for other services
5858
*
5959
* @param id The feature flag id
6060
* @param defaultValue The default value to return if not found
@@ -66,8 +66,7 @@ public String getFeatureFlagValue(String id, String defaultValue) {
6666
}
6767

6868
/**
69-
* Check if a feature flag is enabled (value is "true")
70-
* This is a helper method for other services
69+
* Check if a feature flag is enabled (value is "true") This is a helper method for other services
7170
*
7271
* @param id The feature flag id
7372
* @return true if the flag exists and its value is "true", false otherwise

src/test/java/org/broadinstitute/consent/http/db/DAOTestHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public class DAOTestHelper extends AbstractTestHelper implements TestExecutionLi
6262
protected static AcknowledgementDAO acknowledgementDAO;
6363
protected static DraftDAO draftDAO;
6464
protected static DACAutomationRuleDAO dacAutomationRuleDAO;
65+
protected static FeatureFlagDAO featureFlagDAO;
6566
private static DropwizardTestSupport<ConsentConfiguration> testApp;
6667
// This is a test-only DAO class where we manage the deletion
6768
// of all records between test runs.
@@ -152,6 +153,7 @@ public void startUp() throws Exception {
152153
acknowledgementDAO = jdbi.onDemand(AcknowledgementDAO.class);
153154
draftDAO = jdbi.onDemand(DraftDAO.class);
154155
dacAutomationRuleDAO = jdbi.onDemand(DACAutomationRuleDAO.class);
156+
featureFlagDAO = jdbi.onDemand(FeatureFlagDAO.class);
155157
testingDAO = jdbi.onDemand(TestingDAO.class);
156158
}
157159

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package org.broadinstitute.consent.http.db;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNotNull;
6+
import static org.junit.jupiter.api.Assertions.assertNull;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
import java.util.List;
10+
import org.apache.commons.lang3.RandomStringUtils;
11+
import org.broadinstitute.consent.http.models.FeatureFlag;
12+
import org.junit.jupiter.api.Test;
13+
14+
class FeatureFlagDAOTest extends DAOTestHelper {
15+
16+
@Test
17+
void testInsertAndFindById() {
18+
String id = "test-feature-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
19+
String value = "test-value";
20+
featureFlagDAO.insert(id, value);
21+
22+
FeatureFlag flag = featureFlagDAO.findById(id);
23+
assertNotNull(flag);
24+
assertEquals(id, flag.getId());
25+
assertEquals(value, flag.getValue());
26+
assertNotNull(flag.getCreateDate());
27+
assertNotNull(flag.getUpdateDate());
28+
}
29+
30+
@Test
31+
void testFindById_NotFound() {
32+
String id = "non-existent-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
33+
FeatureFlag flag = featureFlagDAO.findById(id);
34+
assertNull(flag);
35+
}
36+
37+
@Test
38+
void testFindAll() {
39+
String id1 = "feature-1-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
40+
String id2 = "feature-2-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
41+
featureFlagDAO.insert(id1, "value1");
42+
featureFlagDAO.insert(id2, "value2");
43+
44+
List<FeatureFlag> flags = featureFlagDAO.findAll();
45+
assertNotNull(flags);
46+
assertTrue(flags.size() >= 2);
47+
assertTrue(flags.stream().anyMatch(f -> f.getId().equals(id1)));
48+
assertTrue(flags.stream().anyMatch(f -> f.getId().equals(id2)));
49+
}
50+
51+
@Test
52+
void testUpdate() {
53+
String id = "update-test-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
54+
String originalValue = "original";
55+
String updatedValue = "updated";
56+
57+
featureFlagDAO.insert(id, originalValue);
58+
FeatureFlag flag = featureFlagDAO.findById(id);
59+
assertEquals(originalValue, flag.getValue());
60+
61+
featureFlagDAO.update(id, updatedValue);
62+
FeatureFlag updatedFlag = featureFlagDAO.findById(id);
63+
assertEquals(updatedValue, updatedFlag.getValue());
64+
assertTrue(
65+
updatedFlag.getUpdateDate().isAfter(flag.getUpdateDate())
66+
|| updatedFlag.getUpdateDate().equals(flag.getUpdateDate()));
67+
}
68+
69+
@Test
70+
void testDeleteById() {
71+
String id = "delete-test-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
72+
featureFlagDAO.insert(id, "value");
73+
74+
assertTrue(featureFlagDAO.exists(id));
75+
featureFlagDAO.deleteById(id);
76+
assertFalse(featureFlagDAO.exists(id));
77+
assertNull(featureFlagDAO.findById(id));
78+
}
79+
80+
@Test
81+
void testExists() {
82+
String id = "exists-test-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
83+
assertFalse(featureFlagDAO.exists(id));
84+
85+
featureFlagDAO.insert(id, "value");
86+
assertTrue(featureFlagDAO.exists(id));
87+
88+
featureFlagDAO.deleteById(id);
89+
assertFalse(featureFlagDAO.exists(id));
90+
}
91+
92+
@Test
93+
void testMultipleOperations() {
94+
String id = "multi-test-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
95+
96+
// Insert
97+
featureFlagDAO.insert(id, "initial");
98+
assertTrue(featureFlagDAO.exists(id));
99+
100+
// Update multiple times
101+
featureFlagDAO.update(id, "updated1");
102+
assertEquals("updated1", featureFlagDAO.findById(id).getValue());
103+
104+
featureFlagDAO.update(id, "updated2");
105+
assertEquals("updated2", featureFlagDAO.findById(id).getValue());
106+
107+
// Delete
108+
featureFlagDAO.deleteById(id);
109+
assertFalse(featureFlagDAO.exists(id));
110+
}
111+
112+
@Test
113+
void testFindAllOrdering() {
114+
String id1 = "aaa-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
115+
String id2 = "zzz-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
116+
String id3 = "mmm-" + RandomStringUtils.secureStrong().nextAlphanumeric(10);
117+
118+
featureFlagDAO.insert(id2, "value2");
119+
featureFlagDAO.insert(id1, "value1");
120+
featureFlagDAO.insert(id3, "value3");
121+
122+
List<FeatureFlag> flags = featureFlagDAO.findAll();
123+
assertNotNull(flags);
124+
125+
// Verify ordering (should be alphabetical by id)
126+
int idx1 = -1, idx2 = -1, idx3 = -1;
127+
for (int i = 0; i < flags.size(); i++) {
128+
if (flags.get(i).getId().equals(id1)) idx1 = i;
129+
if (flags.get(i).getId().equals(id2)) idx2 = i;
130+
if (flags.get(i).getId().equals(id3)) idx3 = i;
131+
}
132+
assertTrue(idx1 < idx3);
133+
assertTrue(idx3 < idx2);
134+
}
135+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package org.broadinstitute.consent.http.resources;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNotNull;
5+
import static org.mockito.ArgumentMatchers.anyString;
6+
import static org.mockito.Mockito.doNothing;
7+
import static org.mockito.Mockito.doThrow;
8+
import static org.mockito.Mockito.mock;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
12+
import jakarta.ws.rs.NotFoundException;
13+
import jakarta.ws.rs.core.Response;
14+
import jakarta.ws.rs.core.UriBuilder;
15+
import jakarta.ws.rs.core.UriInfo;
16+
import java.net.URI;
17+
import java.util.List;
18+
import java.util.Map;
19+
import org.broadinstitute.consent.http.AbstractTestHelper;
20+
import org.broadinstitute.consent.http.models.AuthUser;
21+
import org.broadinstitute.consent.http.models.FeatureFlag;
22+
import org.broadinstitute.consent.http.service.FeatureFlagService;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.extension.ExtendWith;
26+
import org.mockito.Mock;
27+
import org.mockito.junit.jupiter.MockitoExtension;
28+
29+
@ExtendWith(MockitoExtension.class)
30+
class FeatureFlagResourceTest extends AbstractTestHelper {
31+
32+
@Mock private FeatureFlagService featureFlagService;
33+
34+
private FeatureFlagResource resource;
35+
private final AuthUser authUser = new AuthUser("[email protected]");
36+
37+
@BeforeEach
38+
void setUp() {
39+
resource = new FeatureFlagResource(featureFlagService);
40+
}
41+
42+
@Test
43+
void testGetAllFeatureFlags() {
44+
FeatureFlag flag1 = new FeatureFlag("feature1", "value1");
45+
FeatureFlag flag2 = new FeatureFlag("feature2", "value2");
46+
when(featureFlagService.getAllFeatureFlags()).thenReturn(List.of(flag1, flag2));
47+
48+
Response response = resource.getAllFeatureFlags();
49+
assertEquals(200, response.getStatus());
50+
assertNotNull(response.getEntity());
51+
verify(featureFlagService).getAllFeatureFlags();
52+
}
53+
54+
@Test
55+
void testGetAllFeatureFlags_Empty() {
56+
when(featureFlagService.getAllFeatureFlags()).thenReturn(List.of());
57+
58+
Response response = resource.getAllFeatureFlags();
59+
assertEquals(200, response.getStatus());
60+
verify(featureFlagService).getAllFeatureFlags();
61+
}
62+
63+
@Test
64+
void testGetFeatureFlagById_Found() {
65+
FeatureFlag flag = new FeatureFlag("test-feature", "test-value");
66+
when(featureFlagService.getFeatureFlagById("test-feature")).thenReturn(flag);
67+
68+
Response response = resource.getFeatureFlagById("test-feature");
69+
assertEquals(200, response.getStatus());
70+
assertNotNull(response.getEntity());
71+
verify(featureFlagService).getFeatureFlagById("test-feature");
72+
}
73+
74+
@Test
75+
void testGetFeatureFlagById_NotFound() {
76+
when(featureFlagService.getFeatureFlagById("non-existent"))
77+
.thenThrow(new NotFoundException("Feature flag with id 'non-existent' not found"));
78+
79+
Response response = resource.getFeatureFlagById("non-existent");
80+
assertEquals(404, response.getStatus());
81+
verify(featureFlagService).getFeatureFlagById("non-existent");
82+
}
83+
84+
@Test
85+
void testCreateOrUpdateFeatureFlag_Create() {
86+
UriInfo uriInfo = mock(UriInfo.class);
87+
UriBuilder uriBuilder = mock(UriBuilder.class);
88+
when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
89+
when(uriBuilder.path(anyString())).thenReturn(uriBuilder);
90+
when(uriBuilder.build()).thenReturn(URI.create("http://localhost/api/feature/new-feature"));
91+
92+
FeatureFlag createdFlag = new FeatureFlag("new-feature", "new-value");
93+
when(featureFlagService.exists("new-feature")).thenReturn(false);
94+
when(featureFlagService.createOrUpdateFeatureFlag("new-feature", "new-value"))
95+
.thenReturn(createdFlag);
96+
97+
Map<String, String> body = Map.of("value", "new-value");
98+
Response response = resource.createOrUpdateFeatureFlag(uriInfo, authUser, "new-feature", body);
99+
100+
assertEquals(201, response.getStatus());
101+
assertNotNull(response.getEntity());
102+
verify(featureFlagService).exists("new-feature");
103+
verify(featureFlagService).createOrUpdateFeatureFlag("new-feature", "new-value");
104+
}
105+
106+
@Test
107+
void testCreateOrUpdateFeatureFlag_Update() {
108+
UriInfo uriInfo = mock(UriInfo.class);
109+
FeatureFlag updatedFlag = new FeatureFlag("existing-feature", "updated-value");
110+
when(featureFlagService.exists("existing-feature")).thenReturn(true);
111+
when(featureFlagService.createOrUpdateFeatureFlag("existing-feature", "updated-value"))
112+
.thenReturn(updatedFlag);
113+
114+
Map<String, String> body = Map.of("value", "updated-value");
115+
Response response =
116+
resource.createOrUpdateFeatureFlag(uriInfo, authUser, "existing-feature", body);
117+
118+
assertEquals(200, response.getStatus());
119+
assertNotNull(response.getEntity());
120+
verify(featureFlagService).exists("existing-feature");
121+
verify(featureFlagService).createOrUpdateFeatureFlag("existing-feature", "updated-value");
122+
}
123+
124+
@Test
125+
void testCreateOrUpdateFeatureFlag_MissingValue() {
126+
UriInfo uriInfo = mock(UriInfo.class);
127+
Map<String, String> body = Map.of();
128+
129+
Response response = resource.createOrUpdateFeatureFlag(uriInfo, authUser, "test-id", body);
130+
131+
assertEquals(400, response.getStatus());
132+
assertNotNull(response.getEntity());
133+
}
134+
135+
@Test
136+
void testCreateOrUpdateFeatureFlag_ServiceError() {
137+
UriInfo uriInfo = mock(UriInfo.class);
138+
when(featureFlagService.exists("test-id")).thenReturn(false);
139+
when(featureFlagService.createOrUpdateFeatureFlag("test-id", "value"))
140+
.thenThrow(new RuntimeException("Database error"));
141+
142+
Map<String, String> body = Map.of("value", "value");
143+
Response response = resource.createOrUpdateFeatureFlag(uriInfo, authUser, "test-id", body);
144+
145+
assertEquals(500, response.getStatus());
146+
verify(featureFlagService).exists("test-id");
147+
verify(featureFlagService).createOrUpdateFeatureFlag("test-id", "value");
148+
}
149+
150+
@Test
151+
void testDeleteFeatureFlag_Success() {
152+
doNothing().when(featureFlagService).deleteFeatureFlag("test-id");
153+
154+
Response response = resource.deleteFeatureFlag(authUser, "test-id");
155+
156+
assertEquals(204, response.getStatus());
157+
verify(featureFlagService).deleteFeatureFlag("test-id");
158+
}
159+
160+
@Test
161+
void testDeleteFeatureFlag_NotFound() {
162+
doThrow(new NotFoundException("Feature flag with id 'non-existent' not found"))
163+
.when(featureFlagService)
164+
.deleteFeatureFlag("non-existent");
165+
166+
Response response = resource.deleteFeatureFlag(authUser, "non-existent");
167+
168+
assertEquals(404, response.getStatus());
169+
verify(featureFlagService).deleteFeatureFlag("non-existent");
170+
}
171+
172+
@Test
173+
void testDeleteFeatureFlag_ServiceError() {
174+
doThrow(new RuntimeException("Database error"))
175+
.when(featureFlagService)
176+
.deleteFeatureFlag("test-id");
177+
178+
Response response = resource.deleteFeatureFlag(authUser, "test-id");
179+
180+
assertEquals(500, response.getStatus());
181+
verify(featureFlagService).deleteFeatureFlag("test-id");
182+
}
183+
}

0 commit comments

Comments
 (0)