Skip to content

Commit 987531b

Browse files
committed
fixup! write test
1 parent 1a954f8 commit 987531b

File tree

2 files changed

+236
-1
lines changed

2 files changed

+236
-1
lines changed

openfeature-provider-local/src/main/java/com/spotify/confidence/GrpcWasmFlagLogger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ private List<WriteFlagLogsRequest> createFlagAssignedChunks(WriteFlagLogsRequest
109109
WriteFlagLogsRequest.newBuilder()
110110
.addAllFlagAssigned(request.getFlagAssignedList().subList(i, end));
111111

112-
// Include metadata only in the first chunk
112+
// Include telemetry and resolve info only in the first chunk
113113
if (i == 0) {
114114
if (request.hasTelemetryData()) {
115115
chunkBuilder.setTelemetryData(request.getTelemetryData());
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package com.spotify.confidence;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.never;
8+
import static org.mockito.Mockito.times;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
12+
import com.spotify.confidence.shaded.flags.admin.v1.ClientResolveInfo;
13+
import com.spotify.confidence.shaded.flags.admin.v1.FlagResolveInfo;
14+
import com.spotify.confidence.shaded.flags.resolver.v1.InternalFlagLoggerServiceGrpc;
15+
import com.spotify.confidence.shaded.flags.resolver.v1.TelemetryData;
16+
import com.spotify.confidence.shaded.flags.resolver.v1.WriteFlagLogsRequest;
17+
import com.spotify.confidence.shaded.flags.resolver.v1.WriteFlagLogsResponse;
18+
import com.spotify.confidence.shaded.flags.resolver.v1.events.ClientInfo;
19+
import com.spotify.confidence.shaded.flags.resolver.v1.events.FlagAssigned;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import org.junit.jupiter.api.Test;
23+
import org.mockito.ArgumentCaptor;
24+
25+
class GrpcWasmFlagLoggerTest {
26+
27+
@Test
28+
void testEmptyRequest_shouldSkip() {
29+
// Given
30+
final var mockStub =
31+
mock(InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceBlockingStub.class);
32+
final var logger = createLoggerWithMockStub(mockStub);
33+
final var emptyRequest = WriteFlagLogsRequest.newBuilder().build();
34+
35+
// When
36+
logger.write(emptyRequest);
37+
38+
// Then
39+
verify(mockStub, never()).writeFlagLogs(any());
40+
logger.shutdown();
41+
}
42+
43+
@Test
44+
void testSmallRequest_shouldSendAsIs() {
45+
// Given
46+
final var mockStub =
47+
mock(InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceBlockingStub.class);
48+
when(mockStub.writeFlagLogs(any())).thenReturn(WriteFlagLogsResponse.getDefaultInstance());
49+
final var logger = createLoggerWithMockStub(mockStub);
50+
51+
final var request =
52+
WriteFlagLogsRequest.newBuilder()
53+
.addAllFlagAssigned(createFlagAssignedList(100))
54+
.setTelemetryData(TelemetryData.newBuilder().setDroppedEvents(5).build())
55+
.addClientResolveInfo(
56+
ClientResolveInfo.newBuilder().setClient("clients/test-client").build())
57+
.addFlagResolveInfo(FlagResolveInfo.newBuilder().setFlag("flags/test-flag").build())
58+
.build();
59+
60+
final ArgumentCaptor<WriteFlagLogsRequest> captor =
61+
ArgumentCaptor.forClass(WriteFlagLogsRequest.class);
62+
63+
// When
64+
logger.write(request);
65+
66+
// Then
67+
verify(mockStub, times(1)).writeFlagLogs(captor.capture());
68+
69+
final WriteFlagLogsRequest sentRequest = captor.getValue();
70+
assertEquals(100, sentRequest.getFlagAssignedCount());
71+
assertEquals(5, sentRequest.getTelemetryData().getDroppedEvents());
72+
assertEquals(1, sentRequest.getClientResolveInfoCount());
73+
assertEquals(1, sentRequest.getFlagResolveInfoCount());
74+
75+
logger.shutdown();
76+
}
77+
78+
@Test
79+
void testLargeRequest_shouldChunkWithMetadataInFirstChunkOnly() {
80+
// Given
81+
final var mockStub =
82+
mock(InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceBlockingStub.class);
83+
when(mockStub.writeFlagLogs(any())).thenReturn(WriteFlagLogsResponse.getDefaultInstance());
84+
final var logger = createLoggerWithMockStub(mockStub);
85+
86+
final int totalFlags = 2500; // Will create 3 chunks: 1000, 1000, 500
87+
final var request =
88+
WriteFlagLogsRequest.newBuilder()
89+
.addAllFlagAssigned(createFlagAssignedList(totalFlags))
90+
.setTelemetryData(TelemetryData.newBuilder().setDroppedEvents(10).build())
91+
.addClientResolveInfo(
92+
ClientResolveInfo.newBuilder().setClient("clients/test-client").build())
93+
.addFlagResolveInfo(FlagResolveInfo.newBuilder().setFlag("flags/test-flag").build())
94+
.build();
95+
96+
final ArgumentCaptor<WriteFlagLogsRequest> captor =
97+
ArgumentCaptor.forClass(WriteFlagLogsRequest.class);
98+
99+
// When
100+
logger.write(request);
101+
102+
// Then
103+
verify(mockStub, times(3)).writeFlagLogs(captor.capture());
104+
105+
final List<WriteFlagLogsRequest> sentRequests = captor.getAllValues();
106+
assertEquals(3, sentRequests.size());
107+
108+
// First chunk: 1000 flag_assigned + metadata
109+
final WriteFlagLogsRequest firstChunk = sentRequests.get(0);
110+
assertEquals(1000, firstChunk.getFlagAssignedCount());
111+
assertTrue(firstChunk.hasTelemetryData());
112+
assertEquals(10, firstChunk.getTelemetryData().getDroppedEvents());
113+
assertEquals(1, firstChunk.getClientResolveInfoCount());
114+
assertEquals("clients/test-client", firstChunk.getClientResolveInfo(0).getClient());
115+
assertEquals(1, firstChunk.getFlagResolveInfoCount());
116+
assertEquals("flags/test-flag", firstChunk.getFlagResolveInfo(0).getFlag());
117+
118+
// Second chunk: 1000 flag_assigned only, no metadata
119+
final WriteFlagLogsRequest secondChunk = sentRequests.get(1);
120+
assertEquals(1000, secondChunk.getFlagAssignedCount());
121+
assertEquals(false, secondChunk.hasTelemetryData());
122+
assertEquals(0, secondChunk.getClientResolveInfoCount());
123+
assertEquals(0, secondChunk.getFlagResolveInfoCount());
124+
125+
// Third chunk: 500 flag_assigned only, no metadata
126+
final WriteFlagLogsRequest thirdChunk = sentRequests.get(2);
127+
assertEquals(500, thirdChunk.getFlagAssignedCount());
128+
assertEquals(false, thirdChunk.hasTelemetryData());
129+
assertEquals(0, thirdChunk.getClientResolveInfoCount());
130+
assertEquals(0, thirdChunk.getFlagResolveInfoCount());
131+
132+
logger.shutdown();
133+
}
134+
135+
@Test
136+
void testExactlyAtChunkBoundary_shouldCreateTwoChunks() {
137+
// Given
138+
final var mockStub =
139+
mock(InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceBlockingStub.class);
140+
when(mockStub.writeFlagLogs(any())).thenReturn(WriteFlagLogsResponse.getDefaultInstance());
141+
final var logger = createLoggerWithMockStub(mockStub);
142+
143+
final int totalFlags = 2000; // Exactly 2 chunks of 1000
144+
final var request =
145+
WriteFlagLogsRequest.newBuilder()
146+
.addAllFlagAssigned(createFlagAssignedList(totalFlags))
147+
.setTelemetryData(TelemetryData.newBuilder().setDroppedEvents(7).build())
148+
.build();
149+
150+
final ArgumentCaptor<WriteFlagLogsRequest> captor =
151+
ArgumentCaptor.forClass(WriteFlagLogsRequest.class);
152+
153+
// When
154+
logger.write(request);
155+
156+
// Then
157+
verify(mockStub, times(2)).writeFlagLogs(captor.capture());
158+
159+
final List<WriteFlagLogsRequest> sentRequests = captor.getAllValues();
160+
assertEquals(2, sentRequests.size());
161+
162+
// First chunk with metadata
163+
assertEquals(1000, sentRequests.get(0).getFlagAssignedCount());
164+
assertTrue(sentRequests.get(0).hasTelemetryData());
165+
166+
// Second chunk without metadata
167+
assertEquals(1000, sentRequests.get(1).getFlagAssignedCount());
168+
assertEquals(false, sentRequests.get(1).hasTelemetryData());
169+
170+
logger.shutdown();
171+
}
172+
173+
@Test
174+
void testOnlyMetadata_noFlagAssigned_shouldSendAsIs() {
175+
// Given
176+
final var mockStub =
177+
mock(InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceBlockingStub.class);
178+
when(mockStub.writeFlagLogs(any())).thenReturn(WriteFlagLogsResponse.getDefaultInstance());
179+
final var logger = createLoggerWithMockStub(mockStub);
180+
181+
final var request =
182+
WriteFlagLogsRequest.newBuilder()
183+
.setTelemetryData(TelemetryData.newBuilder().setDroppedEvents(3).build())
184+
.addClientResolveInfo(
185+
ClientResolveInfo.newBuilder().setClient("clients/test-client").build())
186+
.build();
187+
188+
final ArgumentCaptor<WriteFlagLogsRequest> captor =
189+
ArgumentCaptor.forClass(WriteFlagLogsRequest.class);
190+
191+
// When
192+
logger.write(request);
193+
194+
// Then
195+
verify(mockStub, times(1)).writeFlagLogs(captor.capture());
196+
197+
final WriteFlagLogsRequest sentRequest = captor.getValue();
198+
assertEquals(0, sentRequest.getFlagAssignedCount());
199+
assertTrue(sentRequest.hasTelemetryData());
200+
assertEquals(1, sentRequest.getClientResolveInfoCount());
201+
202+
logger.shutdown();
203+
}
204+
205+
// Helper methods
206+
207+
private List<FlagAssigned> createFlagAssignedList(int count) {
208+
final List<FlagAssigned> list = new ArrayList<>();
209+
for (int i = 0; i < count; i++) {
210+
list.add(
211+
FlagAssigned.newBuilder()
212+
.setResolveId("resolve-" + i)
213+
.setClientInfo(
214+
ClientInfo.newBuilder()
215+
.setClient("clients/test-client")
216+
.setClientCredential("clients/test-client/credentials/cred-1")
217+
.build())
218+
.addFlags(
219+
FlagAssigned.AppliedFlag.newBuilder()
220+
.setFlag("flags/test-flag-" + i)
221+
.setTargetingKey("user-" + i)
222+
.setAssignmentId("assignment-" + i)
223+
.build())
224+
.build());
225+
}
226+
return list;
227+
}
228+
229+
private GrpcWasmFlagLogger createLoggerWithMockStub(
230+
InternalFlagLoggerServiceGrpc.InternalFlagLoggerServiceBlockingStub mockStub) {
231+
// Create logger with synchronous test writer
232+
return new GrpcWasmFlagLogger(
233+
new ApiSecret("test-client-id", "test-client-secret"), mockStub::writeFlagLogs);
234+
}
235+
}

0 commit comments

Comments
 (0)