Skip to content

Commit 8a77b60

Browse files
derrickstoleeNeil Kainga
andcommitted
t5798: test parallel POST failure handling
Parallel requests need to preserve the sequential path's behavior for configuration boundaries, authentication, throttling, cache fallback, corrupt responses, and request headers. Extend the protocol test server with targeted failure modes. Verify that parallel POST refreshes authentication, honors Retry-After, falls back from cache 404 responses only when permitted, retries a one-time corrupt pack, and reports permanent corruption as an index-pack failure. Also cover absent and invalid thread configuration, cookie-enabled sequential fallback, configured headers, multiple participating workers, and a timeout-protected child-pipe stress case. Helped-by: GPT-5.6 Sol Co-authored-by: Neil Kainga <t-neilkainga@microsoft.com> Signed-off-by: Neil Kainga <t-neilkainga@microsoft.com> Signed-off-by: Derrick Stolee <stolee@gmail.com>
1 parent bbf1212 commit 8a77b60

2 files changed

Lines changed: 291 additions & 5 deletions

File tree

‎t/helper/test-gvfs-protocol.c‎

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,14 @@ static int mayhem_try_auth(struct req *req, enum worker_result *wr_out)
309309
{
310310
*wr_out = WR_OK;
311311

312+
if (string_list_has_string(&mayhem_list, "http_401_1") &&
313+
mayhem_child == 0) {
314+
logmayhem("http_401_1");
315+
*wr_out = send_http_error(1, 401, "Unauthorized", -1,
316+
WR_MAYHEM);
317+
return 1;
318+
}
319+
312320
if (string_list_has_string(&mayhem_list, "http_401")) {
313321
struct string_list_item *item;
314322
int has_auth = 0;
@@ -906,7 +914,28 @@ static enum worker_result send_packfile_from_buffer(const struct strbuf *packfil
906914
goto done;
907915
}
908916

909-
if (write_in_full(1, packfile->buf, packfile->len) < 0) {
917+
if ((string_list_has_string(&mayhem_list, "bad_post_pack_sha") ||
918+
(string_list_has_string(&mayhem_list, "bad_post_pack_sha_1") &&
919+
mayhem_child == 0)) &&
920+
packfile->len) {
921+
char byte = packfile->buf[packfile->len - 1] ^ 0xff;
922+
923+
logmayhem("bad_post_pack_sha%s",
924+
string_list_has_string(&mayhem_list,
925+
"bad_post_pack_sha_1") ?
926+
"_1" : "");
927+
if (write_in_full(1, packfile->buf, packfile->len - 1) < 0 ||
928+
write_in_full(1, &byte, 1) < 0) {
929+
logerror("unable to write corrupt response body");
930+
wr = WR_IO_ERROR;
931+
goto done;
932+
}
933+
if (string_list_has_string(&mayhem_list,
934+
"bad_post_pack_sha_1")) {
935+
wr = WR_MAYHEM | WR_HANGUP;
936+
goto done;
937+
}
938+
} else if (write_in_full(1, packfile->buf, packfile->len) < 0) {
910939
logerror("unable to write response content body");
911940
wr = WR_IO_ERROR;
912941
goto done;
@@ -1555,15 +1584,14 @@ static enum worker_result req__read(struct req *req, int fd)
15551584
done:
15561585

15571586
/*
1558-
* Log the X-Session-Id header if present (for testing purposes).
1587+
* Log selected test headers if present.
15591588
*/
15601589
{
15611590
struct string_list_item *item;
15621591
for_each_string_list_item(item, &req->header_list) {
1563-
if (starts_with(item->string, "X-Session-Id:")) {
1592+
if (starts_with(item->string, "X-Session-Id:") ||
1593+
starts_with(item->string, "X-Test-Header:"))
15641594
loginfo("Received header: %s", item->string);
1565-
break;
1566-
}
15671595
}
15681596
}
15691597

@@ -1600,6 +1628,11 @@ static enum worker_result dispatch(struct req *req)
16001628
enum worker_result wr;
16011629

16021630
if (strstr(req->uri_base.buf, MY_SERVER_TYPE__CACHE)) {
1631+
if (string_list_has_string(&mayhem_list, "cache_http_404")) {
1632+
logmayhem("cache_http_404");
1633+
return send_http_error(1, 404, "Not Found", -1,
1634+
WR_MAYHEM);
1635+
}
16031636
if (string_list_has_string(&mayhem_list, "cache_http_503")) {
16041637
logmayhem("cache_http_503");
16051638
return send_http_error(1, 503, "Service Unavailable", 2,

‎t/t5798-gvfs-helper-post-threads.sh‎

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and to exercise both code paths in do__http_post__fetch_oidset().
1212

1313
. "$TEST_DIRECTORY"/lib-gvfs-helper.sh
1414

15+
test_lazy_prereq TIMEOUT '
16+
type timeout >/dev/null 2>&1
17+
'
18+
1519
# Helper: POST a set of OIDs and verify we get the expected packfiles.
1620
#
1721
do_post_blobs () {
@@ -98,6 +102,89 @@ verify_parallel_post_workers () {
98102
test "$nr_workers" -gt 1
99103
}
100104

105+
do_post_corrupt_pack () {
106+
test_must_fail \
107+
git -C "$REPO_T1" gvfs-helper \
108+
--cache-server=disable \
109+
--remote=origin \
110+
--no-progress \
111+
post \
112+
--block-size=2 \
113+
--max-retries=0 \
114+
<"$OIDS_BLOBS_FILE" >OUT.output 2>OUT.stderr &&
115+
116+
test_grep "error: post: index-pack failed" OUT.stderr
117+
}
118+
119+
for value in unset 0 negative
120+
do
121+
test_expect_success "postThreads=$value uses sequential mode" '
122+
test_when_finished "per_test_cleanup" &&
123+
start_gvfs_protocol_server &&
124+
if test "'$value'" = unset
125+
then
126+
git -C "$REPO_T1" config --unset-all \
127+
gvfs.postThreads || :
128+
elif test "'$value'" = negative
129+
then
130+
git -C "$REPO_T1" config gvfs.postThreads -1
131+
else
132+
git -C "$REPO_T1" config gvfs.postThreads 0
133+
fi &&
134+
135+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
136+
export GIT_TRACE2_EVENT &&
137+
138+
do_post_blobs_small_blocks &&
139+
140+
stop_gvfs_protocol_server &&
141+
test_trace2_data gvfs-helper post/fetch_mode 1 \
142+
<"trace-$test_count.txt"
143+
'
144+
done
145+
146+
test_expect_success 'malformed postThreads is rejected' '
147+
test_when_finished "git -C \"$REPO_T1\" config --unset-all \
148+
gvfs.postThreads" &&
149+
git -C "$REPO_T1" config gvfs.postThreads invalid &&
150+
151+
test_must_fail git -C "$REPO_T1" gvfs-helper \
152+
--cache-server=disable \
153+
--remote=origin \
154+
--no-progress \
155+
post \
156+
<"$OIDS_BLOBS_FILE" >OUT.output 2>OUT.stderr &&
157+
test_grep "bad numeric config value" OUT.stderr
158+
'
159+
160+
test_expect_success PTHREADS 'cookie configuration uses sequential POST' '
161+
test_when_finished "per_test_cleanup" &&
162+
test_when_finished "rm -f cookies" &&
163+
>"cookies" &&
164+
start_gvfs_protocol_server &&
165+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
166+
167+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
168+
export GIT_TRACE2_EVENT &&
169+
170+
git -C "$REPO_T1" \
171+
-c http.cookieFile="$(pwd)/cookies" \
172+
-c http.saveCookies=true \
173+
gvfs-helper \
174+
--cache-server=disable \
175+
--remote=origin \
176+
--no-progress \
177+
post \
178+
--block-size=2 \
179+
<"$OIDS_BLOBS_FILE" >OUT.output 2>OUT.stderr &&
180+
181+
test_must_be_empty OUT.stderr &&
182+
verify_objects_in_shared_cache "$OIDS_BLOBS_FILE" &&
183+
stop_gvfs_protocol_server &&
184+
test_trace2_data gvfs-helper post/fetch_mode 1 \
185+
<"trace-$test_count.txt"
186+
'
187+
101188
for threads in 1 4
102189
do
103190
if test "$threads" = "1"
@@ -186,4 +273,170 @@ do
186273
'
187274
done
188275

276+
test_expect_success PTHREADS,TIMEOUT 'parallel POST does not deadlock' '
277+
test_when_finished "per_test_cleanup" &&
278+
start_gvfs_protocol_server &&
279+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
280+
281+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
282+
export GIT_TRACE2_EVENT &&
283+
284+
timeout 30 git -C "$REPO_T1" gvfs-helper \
285+
--cache-server=disable \
286+
--remote=origin \
287+
--no-progress \
288+
post \
289+
--block-size=2 \
290+
<"$OIDS_BLOBS_FILE" >OUT.output 2>OUT.stderr &&
291+
292+
test_must_be_empty OUT.stderr &&
293+
verify_objects_in_shared_cache "$OIDS_BLOBS_FILE" &&
294+
stop_gvfs_protocol_server &&
295+
test_trace2_data gvfs-helper post/fetch_mode 4 \
296+
<"trace-$test_count.txt"
297+
'
298+
299+
test_expect_success PTHREADS 'parallel POST reports index-pack failure' '
300+
test_when_finished "per_test_cleanup" &&
301+
start_gvfs_protocol_server_with_mayhem bad_post_pack_sha &&
302+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
303+
304+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
305+
export GIT_TRACE2_EVENT &&
306+
307+
do_post_corrupt_pack &&
308+
309+
stop_gvfs_protocol_server &&
310+
test_grep "bad_post_pack_sha" "$SERVER_LOG" &&
311+
test_trace2_data gvfs-helper post/fetch_mode 4 \
312+
<"trace-$test_count.txt"
313+
'
314+
315+
test_expect_success PTHREADS 'parallel POST retries a corrupt pack' '
316+
test_when_finished "per_test_cleanup" &&
317+
start_gvfs_protocol_server_with_mayhem bad_post_pack_sha_1 &&
318+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
319+
320+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
321+
export GIT_TRACE2_EVENT &&
322+
323+
do_post_blobs_small_blocks &&
324+
325+
stop_gvfs_protocol_server &&
326+
test_grep "bad_post_pack_sha_1" "$SERVER_LOG" &&
327+
test_trace2_data gvfs-helper post/fetch_mode 4 \
328+
<"trace-$test_count.txt"
329+
'
330+
331+
test_expect_success PTHREADS 'parallel POST retries a transient HTTP error' '
332+
test_when_finished "per_test_cleanup" &&
333+
start_gvfs_protocol_server_with_mayhem http_429_1 &&
334+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
335+
336+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
337+
export GIT_TRACE2_EVENT &&
338+
339+
do_post_blobs_small_blocks &&
340+
341+
stop_gvfs_protocol_server &&
342+
test_grep "http_429_1" "$SERVER_LOG" &&
343+
test_trace2_data gvfs-helper post/fetch_mode 4 \
344+
<"trace-$test_count.txt"
345+
'
346+
347+
test_expect_success PTHREADS 'parallel POST retries authentication' '
348+
test_when_finished "per_test_cleanup" &&
349+
start_gvfs_protocol_server_with_mayhem http_401_1 &&
350+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
351+
352+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
353+
export GIT_TRACE2_EVENT &&
354+
355+
do_post_blobs_small_blocks &&
356+
357+
stop_gvfs_protocol_server &&
358+
test_grep "http_401_1" "$SERVER_LOG" &&
359+
test_trace2_data gvfs-helper post/fetch_mode 4 \
360+
<"trace-$test_count.txt" &&
361+
test_trace2_data gvfs-helper post/auth_retry 1 \
362+
<"trace-$test_count.txt"
363+
'
364+
365+
test_expect_success PTHREADS 'parallel POST falls back after cache 404' '
366+
test_when_finished "per_test_cleanup" &&
367+
start_gvfs_protocol_server_with_mayhem cache_http_404 &&
368+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
369+
370+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
371+
export GIT_TRACE2_EVENT &&
372+
373+
git -C "$REPO_T1" gvfs-helper \
374+
--cache-server=trust \
375+
--remote=origin \
376+
--fallback \
377+
--no-progress \
378+
post \
379+
--block-size=2 \
380+
<"$OIDS_BLOBS_FILE" >OUT.output 2>OUT.stderr &&
381+
382+
test_must_be_empty OUT.stderr &&
383+
verify_objects_in_shared_cache "$OIDS_BLOBS_FILE" &&
384+
stop_gvfs_protocol_server &&
385+
test_grep "cache_http_404" "$SERVER_LOG" &&
386+
test_trace2_data gvfs-helper post/fetch_mode 4 \
387+
<"trace-$test_count.txt"
388+
'
389+
390+
test_expect_success PTHREADS 'parallel POST honors --no-fallback' '
391+
test_when_finished "per_test_cleanup" &&
392+
start_gvfs_protocol_server_with_mayhem cache_http_404 &&
393+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
394+
395+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
396+
export GIT_TRACE2_EVENT &&
397+
398+
test_must_fail \
399+
git -C "$REPO_T1" gvfs-helper \
400+
--cache-server=trust \
401+
--remote=origin \
402+
--no-fallback \
403+
--no-progress \
404+
post \
405+
--block-size=2 \
406+
<"$OIDS_BLOBS_FILE" >OUT.output 2>OUT.stderr &&
407+
408+
test_grep "error: post: (http:404)" OUT.stderr &&
409+
stop_gvfs_protocol_server &&
410+
test_grep "cache_http_404" "$SERVER_LOG" &&
411+
test_trace2_data gvfs-helper post/fetch_mode 4 \
412+
<"trace-$test_count.txt"
413+
'
414+
415+
test_expect_success PTHREADS 'parallel POST preserves configured headers' '
416+
test_when_finished "per_test_cleanup" &&
417+
start_gvfs_protocol_server &&
418+
git -C "$REPO_T1" config gvfs.postThreads 4 &&
419+
420+
GIT_TRACE2_EVENT="$(pwd)/trace-$test_count.txt" &&
421+
export GIT_TRACE2_EVENT &&
422+
423+
git -C "$REPO_T1" \
424+
-c http.extraHeader="X-Test-Header: parallel" \
425+
-c gvfs.sessionkey=test.id \
426+
-c test.id=parallel-session \
427+
gvfs-helper \
428+
--cache-server=disable \
429+
--remote=origin \
430+
--no-progress \
431+
post \
432+
--block-size=2 \
433+
<"$OIDS_BLOBS_FILE" >OUT.output 2>OUT.stderr &&
434+
435+
test_must_be_empty OUT.stderr &&
436+
stop_gvfs_protocol_server &&
437+
test_grep "X-Test-Header: parallel" "$SERVER_LOG" &&
438+
test_grep "X-Session-Id:.*parallel-session:.*-P" "$SERVER_LOG" &&
439+
verify_parallel_post_workers "trace-$test_count.txt"
440+
'
441+
189442
test_done

0 commit comments

Comments
 (0)