Skip to content

Commit 1286461

Browse files
committed
test(client): assert factory call count for direct consume, abort, and 303
Lock in the reproducibility contracts that were previously only asserted in prose: - factoryInvokedOnceOnDirectConsumption: direct consumption (no retry/redirect decorator) subscribes the lazy delegate and runs the factory exactly once. - abortBeforeSubscriptionInvokesFactoryOnce: aborting a never-sent request runs the factory exactly once to release the produced body, and never regenerates. - seeOtherRedirectDropsBody now asserts the factory is called exactly once: the 303 hop drops the body (aborting the duplicator) instead of regenerating it, addressing the missing-assertion review note. Co-authored-by: Isaac
1 parent f30c82c commit 1286461

1 file changed

Lines changed: 44 additions & 2 deletions

File tree

core/src/test/java/com/linecorp/armeria/client/ReproducibleHttpRequestClientTest.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.assertj.core.api.Assertions.assertThat;
2020
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2121

22+
import java.util.concurrent.CompletionException;
2223
import java.util.concurrent.atomic.AtomicInteger;
2324
import java.util.function.Supplier;
2425

@@ -145,6 +146,41 @@ void factoryNotInvokedEagerly() {
145146
req.abort();
146147
}
147148

149+
@Test
150+
void factoryInvokedOnceOnDirectConsumption() {
151+
final AtomicInteger bodyCalls = new AtomicInteger();
152+
final RequestHeaders headers = RequestHeaders.of(HttpMethod.POST, "/upload");
153+
final Supplier<StreamMessage<? extends HttpObject>> bodyFactory = () -> {
154+
bodyCalls.incrementAndGet();
155+
return StreamMessage.of(HttpData.ofUtf8("hello-body"));
156+
};
157+
158+
// Consuming the request directly (no retry/redirect decorator) subscribes the lazy delegate,
159+
// which invokes the factory exactly once to produce the single body.
160+
final HttpRequest req = HttpRequest.reproducible(headers, bodyFactory);
161+
assertThat(req.aggregate().join().contentUtf8()).isEqualTo("hello-body");
162+
assertThat(bodyCalls).hasValue(1);
163+
}
164+
165+
@Test
166+
void abortBeforeSubscriptionInvokesFactoryOnce() {
167+
final AtomicInteger bodyCalls = new AtomicInteger();
168+
final RequestHeaders headers = RequestHeaders.of(HttpMethod.POST, "/upload");
169+
final Supplier<StreamMessage<? extends HttpObject>> bodyFactory = () -> {
170+
bodyCalls.incrementAndGet();
171+
return StreamMessage.of(HttpData.ofUtf8("hello-body"));
172+
};
173+
174+
// Aborting a directly-consumed request that was never sent subscribes an aborting subscriber,
175+
// which runs the factory once so the produced body can be released. It does not regenerate on a
176+
// later subscribe (a stream permits only one subscription), so the factory runs at most once.
177+
final HttpRequest req = HttpRequest.reproducible(headers, bodyFactory);
178+
req.abort();
179+
// abort() propagates asynchronously; join() blocks until completion before we assert the count.
180+
assertThatThrownBy(() -> req.whenComplete().join()).isInstanceOf(CompletionException.class);
181+
assertThat(bodyCalls).hasValue(1);
182+
}
183+
148184
@Test
149185
void retryRegeneratesBody() {
150186
final AtomicInteger bodyCalls = new AtomicInteger();
@@ -272,11 +308,14 @@ void followsRedirectRegeneratingBody() {
272308

273309
@Test
274310
void seeOtherRedirectDropsBody() {
311+
final AtomicInteger bodyCalls = new AtomicInteger();
275312
final RequestHeaders headers =
276313
RequestHeaders.of(HttpMethod.POST, "/see-other",
277314
HttpHeaderNames.CONTENT_TYPE, MediaType.PLAIN_TEXT_UTF_8);
278-
final Supplier<StreamMessage<? extends HttpObject>> bodyFactory =
279-
() -> StreamMessage.of(HttpData.ofUtf8("see-other-body"));
315+
final Supplier<StreamMessage<? extends HttpObject>> bodyFactory = () -> {
316+
bodyCalls.incrementAndGet();
317+
return StreamMessage.of(HttpData.ofUtf8("see-other-body"));
318+
};
280319

281320
final WebClient client =
282321
WebClient.builder(server.httpUri())
@@ -291,6 +330,9 @@ void seeOtherRedirectDropsBody() {
291330
// and must not throw.
292331
assertThat(res.status()).isEqualTo(HttpStatus.OK);
293332
assertThat(res.contentUtf8()).isEqualTo("GET:");
333+
// The factory is invoked once for the initial POST attempt; the SEE_OTHER hop drops the body
334+
// (aborting the duplicator) instead of regenerating it, so it is never called again.
335+
assertThat(bodyCalls).hasValue(1);
294336
}
295337

296338
@Test

0 commit comments

Comments
 (0)