test-gvfs-prococol, t5799: tests for gvfs-helper

Create t/helper/test-gvfs-protocol.c and t/t5799-gvfs-helper.sh
to test gvfs-helper.

Create t/helper/test-gvfs-protocol.c as a stand-alone web server that
speaks the GVFS Protocol [1] and serves loose objects and packfiles
to clients.  It is borrows heavily from the code in daemon.c.
It includes a "mayhem" mode to cause various network and HTTP errors
to test the retry/recovery ability of gvfs-helper.

Create t/t5799-gvfs-helper.sh to test gvfs-helper.

[1] https://github.com/microsoft/VFSForGit/blob/master/Protocol.md

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
This commit is contained in:
Jeff Hostetler 2019-10-25 17:10:25 -04:00 коммит произвёл Victoria Dye
Родитель 876d4ba28e
Коммит 09c102523a
5 изменённых файлов: 2759 добавлений и 7 удалений

Просмотреть файл

@ -1633,6 +1633,7 @@ endif
BASIC_CFLAGS += $(CURL_CFLAGS)
PROGRAM_OBJS += gvfs-helper.o
TEST_PROGRAMS_NEED_X += test-gvfs-protocol
REMOTE_CURL_PRIMARY = git-remote-http$X
REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X

Просмотреть файл

@ -1028,6 +1028,20 @@ set(wrapper_scripts
set(wrapper_test_scripts
test-fake-ssh test-tool)
if(CURL_FOUND)
list(APPEND wrapper_test_scripts test-gvfs-protocol)
add_executable(test-gvfs-protocol ${CMAKE_SOURCE_DIR}/t/helper/test-gvfs-protocol.c)
target_link_libraries(test-gvfs-protocol common-main)
if(MSVC)
set_target_properties(test-gvfs-protocol
PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/t/helper)
set_target_properties(test-gvfs-protocol
PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/t/helper)
endif()
endif()
foreach(script ${wrapper_scripts})
file(STRINGS ${CMAKE_SOURCE_DIR}/wrap-for-bin.sh content NEWLINE_CONSUME)

Просмотреть файл

@ -1880,6 +1880,8 @@ static void install_loose(struct gh__request_params *params,
/*
* We expect a loose object when we do a GET -or- when we
* do a POST with only 1 object.
*
* Note that this content type is singular, not plural.
*/
if (strcmp(status->content_type.buf,
"application/x-git-loose-object")) {
@ -2114,7 +2116,9 @@ static void do_throttle_spin(struct gh__request_params *params,
strbuf_addstr(&region, gh__server_type_label[params->server_type]);
trace2_region_enter("gvfs-helper", region.buf, NULL);
progress = start_progress(progress_msg, duration);
if (gh__cmd_opts.show_progress)
progress = start_progress(progress_msg, duration);
while (now < end) {
display_progress(progress, (now - begin));
@ -2122,6 +2126,7 @@ static void do_throttle_spin(struct gh__request_params *params,
now = time(NULL);
}
display_progress(progress, duration);
stop_progress(&progress);
@ -2676,13 +2681,15 @@ static void do__http_post__gvfs_objects(struct gh__response_status *status,
params.headers = curl_slist_append(params.headers,
"Content-Type: application/json");
/*
* We really always want a packfile. But if the payload only
* requests 1 OID, the server will send us a single loose
* objects instead. (Apparently the server ignores us when we
* only send application/x-git-packfile and does it anyway.)
* If our POST contains more than one object, we want the
* server to send us a packfile. We DO NOT want the non-standard
* concatenated loose object format, so we DO NOT send:
* "Accept: application/x-git-loose-objects" (plural)
*
* So to make it clear to my future self, go ahead and add
* an accept header for loose objects and own it.
* However, if the payload only requests 1 OID, the server
* will send us a single loose object instead of a packfile,
* so we ACK that and send:
* "Accept: application/x-git-loose-object" (singular)
*/
params.headers = curl_slist_append(params.headers,
"Accept: application/x-git-packfile");

Разница между файлами не показана из-за своего большого размера Загрузить разницу

974
t/t5799-gvfs-helper.sh Executable file
Просмотреть файл

@ -0,0 +1,974 @@
#!/bin/sh
test_description='test gvfs-helper and GVFS Protocol'
. ./test-lib.sh
# Set the port for t/helper/test-gvfs-protocol.exe from either the
# environment or from the test number of this shell script.
#
test_set_port GIT_TEST_GVFS_PROTOCOL_PORT
# Setup the following repos:
#
# repo_src:
# A normal, no-magic, fully-populated clone of something.
# No GVFS (aka VFS4G). No Scalar. No partial-clone.
# This will be used by "t/helper/test-gvfs-protocol.exe"
# to serve objects.
#
# repo_t1:
# An empty repo with no contents nor commits. That is,
# everything is missing. For the tests based on this repo,
# we don't care why it is missing objects (or if we could
# actually use it). We are only testing explicit object
# fetching using gvfs-helper.exe in isolation.
#
REPO_SRC="$PWD"/repo_src
REPO_T1="$PWD"/repo_t1
# Setup some loopback URLs where test-gvfs-protocol.exe will be
# listening. We will spawn it directly inside the repo_src directory,
# so we don't need any of the directory mapping or configuration
# machinery found in "git-daemon.exe" or "git-http-backend.exe".
#
# This lets us use the "uri-base" part of the URL (prior to the REST
# API "/gvfs/<token>") to control how our mock server responds. For
# example, only the origin (main Git) server supports "/gvfs/config".
#
# For example, this means that if we add a remote containing $ORIGIN_URL,
# it will work with gvfs-helper, but not for fetch (without some mapping
# tricks).
#
HOST_PORT=127.0.0.1:$GIT_TEST_GVFS_PROTOCOL_PORT
ORIGIN_URL=http://$HOST_PORT/servertype/origin
CACHE_URL=http://$HOST_PORT/servertype/cache
SHARED_CACHE_T1="$PWD"/shared_cache_t1
# The pid-file is created by test-gvfs-protocol.exe when it starts.
# The server will shut down if/when we delete it. (This is a little
# easier than killing it by PID.)
#
PID_FILE="$PWD"/pid-file.pid
SERVER_LOG="$PWD"/OUT.server.log
PATH="$GIT_BUILD_DIR/t/helper/:$PATH" && export PATH
OIDS_FILE="$PWD"/oid_list.txt
OIDS_CT_FILE="$PWD"/oid_ct_list.txt
OIDS_BLOBS_FILE="$PWD"/oids_blobs_file.txt
OID_ONE_BLOB_FILE="$PWD"/oid_one_blob_file.txt
# Get a list of available OIDs in repo_src so that we can try to fetch
# them and so that we don't have to hard-code a list of known OIDs.
# This doesn't need to be a complete list -- just enough to drive some
# representative tests.
#
# Optionally require that we find a minimum number of OIDs.
#
get_list_of_oids () {
git -C "$REPO_SRC" rev-list --objects HEAD | sed 's/ .*//' | sort >"$OIDS_FILE"
if test $# -eq 1
then
actual_nr=$(( $(wc -l <"$OIDS_FILE") ))
if test $actual_nr -lt $1
then
echo "get_list_of_oids: insufficient data. Need $1 OIDs."
return 1
fi
fi
return 0
}
get_list_of_blobs_oids () {
git -C "$REPO_SRC" ls-tree HEAD | grep ' blob ' | awk "{print \$3}" | sort >"$OIDS_BLOBS_FILE"
head -1 <"$OIDS_BLOBS_FILE" >"$OID_ONE_BLOB_FILE"
}
get_list_of_commit_and_tree_oids () {
git -C "$REPO_SRC" cat-file --batch-check --batch-all-objects | awk "/commit|tree/ {print \$1}" | sort >"$OIDS_CT_FILE"
if test $# -eq 1
then
actual_nr=$(( $(wc -l <"$OIDS_CT_FILE") ))
if test $actual_nr -lt $1
then
echo "get_list_of_commit_and_tree_oids: insufficient data. Need $1 OIDs."
return 1
fi
fi
return 0
}
test_expect_success 'setup repos' '
test_create_repo "$REPO_SRC" &&
git -C "$REPO_SRC" branch -M main &&
#
# test_commit_bulk() does magic to create a packfile containing
# the new commits.
#
test_commit_bulk -C "$REPO_SRC" --filename="batch_a.%s.t" 9 &&
cp "$REPO_SRC"/.git/refs/heads/main m1.branch &&
test_commit_bulk -C "$REPO_SRC" --filename="batch_b.%s.t" 9 &&
cp "$REPO_SRC"/.git/refs/heads/main m2.branch &&
#
# test_commit() creates commits, trees, tags, and blobs and leave
# them loose.
#
test_commit -C "$REPO_SRC" file1.txt &&
test_commit -C "$REPO_SRC" file2.txt &&
test_commit -C "$REPO_SRC" file3.txt &&
test_commit -C "$REPO_SRC" file4.txt &&
test_commit -C "$REPO_SRC" file5.txt &&
test_commit -C "$REPO_SRC" file6.txt &&
test_commit -C "$REPO_SRC" file7.txt &&
test_commit -C "$REPO_SRC" file8.txt &&
test_commit -C "$REPO_SRC" file9.txt &&
cp "$REPO_SRC"/.git/refs/heads/main m3.branch &&
#
# gvfs-helper.exe writes downloaded objects to a shared-cache directory
# rather than the ODB inside the .git directory.
#
mkdir "$SHARED_CACHE_T1" &&
mkdir "$SHARED_CACHE_T1/pack" &&
mkdir "$SHARED_CACHE_T1/info" &&
#
# setup repo_t1 and point all of the gvfs.* values to repo_src.
#
test_create_repo "$REPO_T1" &&
git -C "$REPO_T1" branch -M main &&
git -C "$REPO_T1" remote add origin $ORIGIN_URL &&
git -C "$REPO_T1" config --local gvfs.cache-server $CACHE_URL &&
git -C "$REPO_T1" config --local gvfs.sharedCache "$SHARED_CACHE_T1" &&
echo "$SHARED_CACHE_T1" >> "$REPO_T1"/.git/objects/info/alternates &&
#
#
#
cat <<-EOF >creds.txt &&
username=x
password=y
EOF
cat <<-EOF >creds.sh &&
#!/bin/sh
cat "$PWD"/creds.txt
EOF
chmod 755 creds.sh &&
git -C "$REPO_T1" config --local credential.helper "!f() { cat \"$PWD\"/creds.txt; }; f" &&
#
# Create some test data sets.
#
get_list_of_oids 30 &&
get_list_of_commit_and_tree_oids 30 &&
get_list_of_blobs_oids
'
stop_gvfs_protocol_server () {
if ! test -f "$PID_FILE"
then
return 0
fi
#
# The server will shutdown automatically when we delete the pid-file.
#
rm -f "$PID_FILE"
#
# Give it a few seconds to shutdown (mainly to completely release the
# port before the next test start another instance and it attempts to
# bind to it).
#
for k in 0 1 2 3 4
do
if grep -q "Starting graceful shutdown" "$SERVER_LOG"
then
return 0
fi
sleep 1
done
echo "stop_gvfs_protocol_server: timeout waiting for server shutdown"
return 1
}
start_gvfs_protocol_server () {
#
# Launch our server into the background in repo_src.
#
(
cd "$REPO_SRC"
test-gvfs-protocol --verbose \
--listen=127.0.0.1 \
--port=$GIT_TEST_GVFS_PROTOCOL_PORT \
--reuseaddr \
--pid-file="$PID_FILE" \
2>"$SERVER_LOG" &
)
#
# Give it a few seconds to get started.
#
for k in 0 1 2 3 4
do
if test -f "$PID_FILE"
then
return 0
fi
sleep 1
done
echo "start_gvfs_protocol_server: timeout waiting for server startup"
return 1
}
start_gvfs_protocol_server_with_mayhem () {
if test $# -lt 1
then
echo "start_gvfs_protocol_server_with_mayhem: need mayhem args"
return 1
fi
mayhem=""
for k in $*
do
mayhem="$mayhem --mayhem=$k"
done
#
# Launch our server into the background in repo_src.
#
(
cd "$REPO_SRC"
test-gvfs-protocol --verbose \
--listen=127.0.0.1 \
--port=$GIT_TEST_GVFS_PROTOCOL_PORT \
--reuseaddr \
--pid-file="$PID_FILE" \
$mayhem \
2>"$SERVER_LOG" &
)
#
# Give it a few seconds to get started.
#
for k in 0 1 2 3 4
do
if test -f "$PID_FILE"
then
return 0
fi
sleep 1
done
echo "start_gvfs_protocol_server: timeout waiting for server startup"
return 1
}
# Verify the number of connections from the client.
#
# If keep-alive is working, a series of successful sequential requests to the
# same server should use the same TCP connection, so a simple multi-get would
# only have one connection.
#
# On the other hand, an auto-retry after a network error (mayhem) will have
# more than one for a single object request.
#
# TODO This may generate false alarm when we get to complicated tests, so
# TODO we might only want to use it for basic tests.
#
verify_connection_count () {
if test $# -eq 1
then
expected_nr=$1
else
expected_nr=1
fi
actual_nr=$(( $(grep "Connection from" "$SERVER_LOG" | wc -l) ))
if test $actual_nr -ne $expected_nr
then
echo "verify_keep_live: expected $expected_nr; actual $actual_nr"
return 1
fi
return 0
}
# Verify that the set of requested objects are present in
# the shared-cache and that there is no corruption. We use
# cat-file to hide whether the object is packed or loose in
# the test repo.
#
# Usage: <pathname_to_file_of_oids>
#
verify_objects_in_shared_cache () {
#
# See if any of the objects are missing from repo_t1.
#
git -C "$REPO_T1" cat-file --batch-check <"$1" >OUT.bc_actual || return 1
grep -q " missing" OUT.bc_actual && return 1
#
# See if any of the objects have different sizes or types than repo_src.
#
git -C "$REPO_SRC" cat-file --batch-check <"$1" >OUT.bc_expect || return 1
test_cmp OUT.bc_expect OUT.bc_actual || return 1
#
# See if any of the objects are corrupt in repo_t1. This fully
# reconstructs the objects and verifies the hash and therefore
# detects corruption not found by the earlier "batch-check" step.
#
git -C "$REPO_T1" cat-file --batch <"$1" >OUT.b_actual || return 1
#
# TODO move the shared-cache directory (and/or the
# TODO .git/objects/info/alternates and temporarily unset
# TODO gvfs.sharedCache) and repeat the first "batch-check"
# TODO and make sure that they are ALL missing.
#
return 0
}
verify_received_packfile_count () {
if test $# -eq 1
then
expected_nr=$1
else
expected_nr=1
fi
actual_nr=$(( $(grep "packfile " OUT.output | wc -l) ))
if test $actual_nr -ne $expected_nr
then
echo "verify_received_packfile_count: expected $expected_nr; actual $actual_nr"
return 1
fi
return 0
}
per_test_cleanup () {
stop_gvfs_protocol_server
rm -rf "$SHARED_CACHE_T1"/[0-9a-f][0-9a-f]/
rm -rf "$SHARED_CACHE_T1"/info/*
rm -rf "$SHARED_CACHE_T1"/pack/*
rm -rf OUT.*
return 0
}
#################################################################
# Basic tests to confirm the happy path works.
#################################################################
test_expect_success 'basic: GET origin multi-get no-auth' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# Connect to the origin server (w/o auth) and make a series of
# single-object GET requests.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
<"$OIDS_FILE" >OUT.output &&
# Stop the server to prevent the verification steps from faulting-in
# any missing objects.
#
stop_gvfs_protocol_server &&
# gvfs-helper prints a "loose <oid>" message for each received object.
# Verify that gvfs-helper received each of the requested objects.
#
sed "s/loose //" <OUT.output | sort >OUT.actual &&
test_cmp "$OIDS_FILE" OUT.actual &&
verify_objects_in_shared_cache "$OIDS_FILE" &&
verify_connection_count 1
'
test_expect_success 'basic: GET cache-server multi-get trust-mode' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# Connect to the cache-server and make a series of
# single-object GET requests.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=trust \
--remote=origin \
get \
<"$OIDS_FILE" >OUT.output &&
# Stop the server to prevent the verification steps from faulting-in
# any missing objects.
#
stop_gvfs_protocol_server &&
# gvfs-helper prints a "loose <oid>" message for each received object.
# Verify that gvfs-helper received each of the requested objects.
#
sed "s/loose //" <OUT.output | sort >OUT.actual &&
test_cmp "$OIDS_FILE" OUT.actual &&
verify_objects_in_shared_cache "$OIDS_FILE" &&
verify_connection_count 1
'
test_expect_success 'basic: GET gvfs/config' '
# test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# Connect to the cache-server and make a series of
# single-object GET requests.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
config \
<"$OIDS_FILE" >OUT.output &&
# Stop the server to prevent the verification steps from faulting-in
# any missing objects.
#
stop_gvfs_protocol_server &&
# The cache-server URL should be listed in the gvfs/config output.
# We confirm this before assuming error-mode will work.
#
grep -q "$CACHE_URL" OUT.output
'
test_expect_success 'basic: GET cache-server multi-get error-mode' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# Connect to the cache-server and make a series of
# single-object GET requests.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=error \
--remote=origin \
get \
<"$OIDS_FILE" >OUT.output &&
# Stop the server to prevent the verification steps from faulting-in
# any missing objects.
#
stop_gvfs_protocol_server &&
# gvfs-helper prints a "loose <oid>" message for each received object.
# Verify that gvfs-helper received each of the requested objects.
#
sed "s/loose //" <OUT.output | sort >OUT.actual &&
test_cmp "$OIDS_FILE" OUT.actual &&
verify_objects_in_shared_cache "$OIDS_FILE" &&
# Technically, we have 1 connection to the origin server
# for the "gvfs/config" request and 1 to cache server to
# get the objects, but because we are using the same port
# for both, keep-alive will handle it. So 1 connection.
#
verify_connection_count 1
'
# The GVFS Protocol POST verb behaves like GET for non-commit objects
# (in that it just returns the requested object), but for commit
# objects POST *also* returns all trees referenced by the commit.
#
# The goal of this test is to confirm that gvfs-helper can send us
# a packfile at all. So, this test only passes blobs to not blur
# the issue.
#
test_expect_success 'basic: POST origin blobs' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# Connect to the origin server (w/o auth) and make
# multi-object POST request.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
--no-progress \
post \
<"$OIDS_BLOBS_FILE" >OUT.output &&
# Stop the server to prevent the verification steps from faulting-in
# any missing objects.
#
stop_gvfs_protocol_server &&
# gvfs-helper prints a "packfile <path>" message for each received
# packfile. We verify the number of expected packfile(s) and we
# individually verify that each requested object is present in the
# shared cache (and index-pack already verified the integrity of
# the packfile), so we do not bother to run "git verify-pack -v"
# and do an exact matchup here.
#
verify_received_packfile_count 1 &&
verify_objects_in_shared_cache "$OIDS_BLOBS_FILE" &&
verify_connection_count 1
'
#################################################################
# Tests to see how gvfs-helper responds to network problems.
#
# We use small --max-retry value because of exponential backoff.
#
# These mayhem tests are interested in how gvfs-helper gracefully
# retries when there is a network error. And verify that it gives
# up gracefully too.
#################################################################
mayhem_observed__close__connections () {
if $(grep -q "transient" OUT.stderr)
then
# Transient errors should retry.
# 1 for initial request + 2 retries.
#
verify_connection_count 3
return $?
elif $(grep -q "hard_fail" OUT.stderr)
then
# Hard errors should not retry.
#
verify_connection_count 1
return $?
else
error "mayhem_observed__close: unexpected mayhem-induced error type"
return 1
fi
}
mayhem_observed__close () {
# Expected error codes for mayhem events:
# close_read
# close_write
# close_no_write
#
# CURLE_PARTIAL_FILE 18
# CURLE_GOT_NOTHING 52
# CURLE_SEND_ERROR 55
# CURLE_RECV_ERROR 56
#
# I don't want to pin it down to an exact error for each because there may
# be races here because of network buffering.
#
# Also, It is unclear which of these network errors should be transient
# (with retry) and which should be a hard-fail (without retry). I'm only
# going to verify the connection counts based upon what type of error
# gvfs-helper claimed it to be.
#
if $(grep -q "error: get: (curl:18)" OUT.stderr) ||
$(grep -q "error: get: (curl:52)" OUT.stderr) ||
$(grep -q "error: get: (curl:55)" OUT.stderr) ||
$(grep -q "error: get: (curl:56)" OUT.stderr)
then
mayhem_observed__close__connections
return $?
else
echo "mayhem_observed__close: unexpected mayhem-induced error"
return 1
fi
}
test_expect_success 'curl-error: no server' '
test_when_finished "per_test_cleanup" &&
# Try to do a multi-get without a server.
#
# Use small max-retry value because of exponential backoff,
# but yet do exercise retry some.
#
test_must_fail \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OIDS_FILE" >OUT.output 2>OUT.stderr &&
# CURLE_COULDNT_CONNECT 7
grep -q "error: get: (curl:7)" OUT.stderr
'
test_expect_success 'curl-error: close socket while reading request' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem close_read &&
test_must_fail \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OIDS_FILE" >OUT.output 2>OUT.stderr &&
stop_gvfs_protocol_server &&
mayhem_observed__close
'
test_expect_success 'curl-error: close socket while writing response' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem close_write &&
test_must_fail \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OIDS_FILE" >OUT.output 2>OUT.stderr &&
stop_gvfs_protocol_server &&
mayhem_observed__close
'
test_expect_success 'curl-error: close socket before writing response' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem close_no_write &&
test_must_fail \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OIDS_FILE" >OUT.output 2>OUT.stderr &&
stop_gvfs_protocol_server &&
mayhem_observed__close
'
#################################################################
# Tests to confirm that gvfs-helper does silently recover when
# a retry succeeds.
#
# Note: I'm only to do this for 1 of the close_* mayhem events.
#################################################################
test_expect_success 'successful retry after curl-error: origin get' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem close_read_1 &&
# Connect to the origin server (w/o auth).
# Make a single-object GET request.
# Confirm that it succeeds without error.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OID_ONE_BLOB_FILE" >OUT.output &&
stop_gvfs_protocol_server &&
# gvfs-helper prints a "loose <oid>" message for each received object.
# Verify that gvfs-helper received each of the requested objects.
#
sed "s/loose //" <OUT.output | sort >OUT.actual &&
test_cmp "$OID_ONE_BLOB_FILE" OUT.actual &&
verify_objects_in_shared_cache "$OID_ONE_BLOB_FILE" &&
verify_connection_count 2
'
#################################################################
# Tests to see how gvfs-helper responds to HTTP errors/problems.
#
#################################################################
# See "enum gh__error_code" in gvfs-helper.c
#
GH__ERROR_CODE__HTTP_404=4
GH__ERROR_CODE__HTTP_429=5
GH__ERROR_CODE__HTTP_503=6
test_expect_success 'http-error: 503 Service Unavailable (with retry)' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem http_503 &&
test_expect_code $GH__ERROR_CODE__HTTP_503 \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OIDS_FILE" >OUT.output 2>OUT.stderr &&
stop_gvfs_protocol_server &&
grep -q "error: get: (http:503)" OUT.stderr &&
verify_connection_count 3
'
test_expect_success 'http-error: 429 Service Unavailable (with retry)' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem http_429 &&
test_expect_code $GH__ERROR_CODE__HTTP_429 \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OIDS_FILE" >OUT.output 2>OUT.stderr &&
stop_gvfs_protocol_server &&
grep -q "error: get: (http:429)" OUT.stderr &&
verify_connection_count 3
'
test_expect_success 'http-error: 404 Not Found (no retry)' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem http_404 &&
test_expect_code $GH__ERROR_CODE__HTTP_404 \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OID_ONE_BLOB_FILE" >OUT.output 2>OUT.stderr &&
stop_gvfs_protocol_server &&
grep -q "error: get: (http:404)" OUT.stderr &&
verify_connection_count 1
'
#################################################################
# Tests to confirm that gvfs-helper does silently recover when an
# HTTP request succeeds after a failure.
#
#################################################################
test_expect_success 'successful retry after http-error: origin get' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem http_429_1 &&
# Connect to the origin server (w/o auth).
# Make a single-object GET request.
# Confirm that it succeeds without error.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OID_ONE_BLOB_FILE" >OUT.output &&
stop_gvfs_protocol_server &&
# gvfs-helper prints a "loose <oid>" message for each received object.
# Verify that gvfs-helper received each of the requested objects.
#
sed "s/loose //" <OUT.output | sort >OUT.actual &&
test_cmp "$OID_ONE_BLOB_FILE" OUT.actual &&
verify_objects_in_shared_cache "$OID_ONE_BLOB_FILE" &&
verify_connection_count 2
'
#################################################################
# Test HTTP Auth
#
#################################################################
test_expect_success 'HTTP GET Auth on Origin Server' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem http_401 &&
# Force server to require auth.
# Connect to the origin server without auth.
# Make a single-object GET request.
# Confirm that it gets a 401 and then retries with auth.
#
GIT_CONFIG_NOSYSTEM=1 \
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
--max-retries=2 \
<"$OID_ONE_BLOB_FILE" >OUT.output &&
stop_gvfs_protocol_server &&
# gvfs-helper prints a "loose <oid>" message for each received object.
# Verify that gvfs-helper received each of the requested objects.
#
sed "s/loose //" <OUT.output | sort >OUT.actual &&
test_cmp "$OID_ONE_BLOB_FILE" OUT.actual &&
verify_objects_in_shared_cache "$OID_ONE_BLOB_FILE" &&
verify_connection_count 2
'
test_expect_success 'HTTP POST Auth on Origin Server' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem http_401 &&
# Connect to the origin server and make multi-object POST
# request and verify that it automatically handles the 401.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
--no-progress \
post \
<"$OIDS_BLOBS_FILE" >OUT.output &&
# Stop the server to prevent the verification steps from faulting-in
# any missing objects.
#
stop_gvfs_protocol_server &&
# gvfs-helper prints a "packfile <path>" message for each received
# packfile. We verify the number of expected packfile(s) and we
# individually verify that each requested object is present in the
# shared cache (and index-pack already verified the integrity of
# the packfile), so we do not bother to run "git verify-pack -v"
# and do an exact matchup here.
#
verify_received_packfile_count 1 &&
verify_objects_in_shared_cache "$OIDS_BLOBS_FILE" &&
verify_connection_count 2
'
test_expect_success 'HTTP GET Auth on Cache Server' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server_with_mayhem http_401 &&
# Try auth to cache-server. Note that gvfs-helper *ALWAYS* sends
# creds to cache-servers, so we will never see the "400 Bad Request"
# response. And we are using "trust" mode, so we only expect 1
# connection to the server.
#
GIT_CONFIG_NOSYSTEM=1 \
git -C "$REPO_T1" gvfs-helper \
--cache-server=trust \
--remote=origin \
get \
--max-retries=2 \
<"$OID_ONE_BLOB_FILE" >OUT.output &&
stop_gvfs_protocol_server &&
# gvfs-helper prints a "loose <oid>" message for each received object.
# Verify that gvfs-helper received each of the requested objects.
#
sed "s/loose //" <OUT.output | sort >OUT.actual &&
test_cmp "$OID_ONE_BLOB_FILE" OUT.actual &&
verify_objects_in_shared_cache "$OID_ONE_BLOB_FILE" &&
verify_connection_count 1
'
#################################################################
# Integration tests with Git.exe
#
# Now that we have confirmed that gvfs-helper works in isolation,
# run a series of tests using random Git commands that fault-in
# objects as needed.
#
# At this point, I'm going to stop verifying the shape of the ODB
# (loose vs packfiles) and the number of connections required to
# get them. The tests from here on are to verify that objects are
# magically fetched whenever required.
#################################################################
test_expect_success 'integration: explicit commit/trees, implicit blobs: log file' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# We have a very empty repo. Seed it with all of the commits
# and trees. The purpose of this test is to demand-load the
# needed blobs only, so we prefetch the commits and trees.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
<"$OIDS_CT_FILE" >OUT.output &&
# Confirm that we do not have the blobs locally.
# With gvfs-helper turned off, we should fail.
#
test_must_fail \
git -C "$REPO_T1" -c core.useGVFSHelper=false \
log $(cat m3.brach) -- file9.txt \
>OUT.output 2>OUT.stderr &&
# Turn on gvfs-helper and retry. This should implicitly fetch
# any needed blobs.
#
git -C "$REPO_T1" -c core.useGVFSHelper=true \
log $(cat m3.branch) -- file9.txt \
>OUT.output 2>OUT.stderr &&
# Verify that gvfs-helper wrote the fetched the blobs to the
# local ODB, such that a second attempt with gvfs-helper
# turned off should succeed.
#
git -C "$REPO_T1" -c core.useGVFSHelper=false \
log $(cat m3.branch) -- file9.txt \
>OUT.output 2>OUT.stderr
'
test_expect_success 'integration: explicit commit/trees, implicit blobs: diff 2 commits' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# We have a very empty repo. Seed it with all of the commits
# and trees. The purpose of this test is to demand-load the
# needed blobs only, so we prefetch the commits and trees.
#
git -C "$REPO_T1" gvfs-helper \
--cache-server=disable \
--remote=origin \
get \
<"$OIDS_CT_FILE" >OUT.output &&
# Confirm that we do not have the blobs locally.
# With gvfs-helper turned off, we should fail.
#
test_must_fail \
git -C "$REPO_T1" -c core.useGVFSHelper=false \
diff $(cat m1.branch)..$(cat m3.branch) \
>OUT.output 2>OUT.stderr &&
# Turn on gvfs-helper and retry. This should implicitly fetch
# any needed blobs.
#
git -C "$REPO_T1" -c core.useGVFSHelper=true \
diff $(cat m1.branch)..$(cat m3.branch) \
>OUT.output 2>OUT.stderr &&
# Verify that gvfs-helper wrote the fetched the blobs to the
# local ODB, such that a second attempt with gvfs-helper
# turned off should succeed.
#
git -C "$REPO_T1" -c core.useGVFSHelper=false \
diff $(cat m1.branch)..$(cat m3.branch) \
>OUT.output 2>OUT.stderr
'
test_expect_success 'integration: fully implicit: diff 2 commits' '
test_when_finished "per_test_cleanup" &&
start_gvfs_protocol_server &&
# Implicitly demand-load everything without any pre-seeding.
#
git -C "$REPO_T1" -c core.useGVFSHelper=true \
diff $(cat m1.branch)..$(cat m3.branch) \
>OUT.output 2>OUT.stderr
'
test_done