mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-23 19:37:46 +08:00
* feat: serveRawBlock implements ?format=block * feat: serveCar implements ?format=car * feat(gw): ?format= or Accept HTTP header - extracted file-like content type responses to separate .go files - Accept HTTP header with support for application/vnd.ipld.* types * fix: use .bin for raw block content-disposition .raw may be handled by something, depending on OS, and .bin seems to be universally "binary file" across all systems: https://en.wikipedia.org/wiki/List_of_filename_extensions_(A%E2%80%93E) * refactor: gateway_handler_unixfs.go - Moved UnixFS response handling to gateway_handler_unixfs*.go files. - Removed support for X-Ipfs-Gateway-Prefix (Closes #7702) * refactor: prefix cleanup and readable paths - removed dead code after X-Ipfs-Gateway-Prefix is gone (https://github.com/ipfs/go-ipfs/issues/7702) - escaped special characters in content paths returned with http.Error making them both safer and easier to reason about (e.g. when invisible whitespace Unicode is used)
536 lines
13 KiB
Bash
536 lines
13 KiB
Bash
# Test framework for go-ipfs
|
|
#
|
|
# Copyright (c) 2014 Christian Couder
|
|
# MIT Licensed; see the LICENSE file in this repository.
|
|
#
|
|
# We are using sharness (https://github.com/mlafeldt/sharness)
|
|
# which was extracted from the Git test framework.
|
|
|
|
# use the ipfs tool to test against
|
|
|
|
# add current directory to path, for ipfs tool.
|
|
if test "$MAKE_SKIP_PATH" != "1"; then
|
|
BIN=$(cd .. && echo `pwd`/bin)
|
|
BIN2=$(cd ../.. && echo `pwd`/cmd/ipfs)
|
|
PATH=${BIN2}:${BIN}:${PATH}
|
|
|
|
# assert the `ipfs` we're using is the right one.
|
|
if test `which ipfs` != ${BIN2}/ipfs; then
|
|
echo >&2 "Cannot find the tests' local ipfs tool."
|
|
echo >&2 "Please check test and ipfs tool installation."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# set sharness verbosity. we set the env var directly as
|
|
# it's too late to pass in --verbose, and --verbose is harder
|
|
# to pass through in some cases.
|
|
test "$TEST_VERBOSE" = 1 && verbose=t
|
|
test "$TEST_IMMEDIATE" = 1 && immediate=t
|
|
# source the common hashes first.
|
|
. lib/test-lib-hashes.sh
|
|
|
|
|
|
SHARNESS_LIB="lib/sharness/sharness.sh"
|
|
|
|
. "$SHARNESS_LIB" || {
|
|
echo >&2 "Cannot source: $SHARNESS_LIB"
|
|
echo >&2 "Please check Sharness installation."
|
|
exit 1
|
|
}
|
|
|
|
# Please put go-ipfs specific shell functions below
|
|
|
|
###
|
|
# BEGIN Check for pre-existing daemon being stuck
|
|
###
|
|
wait_prev_cleanup_tick_secs=1
|
|
wait_prev_cleanup_max_secs=5
|
|
cur_test_pwd="$(pwd)"
|
|
|
|
while true ; do
|
|
echo -n > stuck_cwd_list
|
|
|
|
lsof -c ipfs -Ffn 2>/dev/null | grep -A1 '^fcwd$' | grep '^n' | cut -b 2- | while read -r pwd_of_stuck ; do
|
|
case "$pwd_of_stuck" in
|
|
"$cur_test_pwd"*)
|
|
echo "$pwd_of_stuck" >> stuck_cwd_list
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
done
|
|
|
|
test -s stuck_cwd_list || break
|
|
|
|
test "$wait_prev_cleanup_max_secs" -le 0 && break
|
|
|
|
echo "Daemons still running, waiting for ${wait_prev_cleanup_max_secs}s"
|
|
sleep $wait_prev_cleanup_tick_secs
|
|
|
|
wait_prev_cleanup_max_secs="$(( $wait_prev_cleanup_max_secs - $wait_prev_cleanup_tick_secs ))"
|
|
done
|
|
|
|
if test -s stuck_cwd_list ; then
|
|
test_expect_success "ipfs daemon (s)seems to be running with CWDs of
|
|
$(cat stuck_cwd_list)
|
|
Almost certainly a leftover from a prior test, ABORTING" 'false'
|
|
|
|
test_done
|
|
fi
|
|
###
|
|
# END Check for pre-existing daemon being stuck
|
|
###
|
|
|
|
# Make sure the ipfs path is set, also set in test_init_ipfs but that
|
|
# is not always used.
|
|
export IPFS_PATH="$(pwd)/.ipfs"
|
|
# Ask programs to please not print ANSI codes
|
|
export TERM=dumb
|
|
|
|
TEST_OS="$(uname -s | tr '[a-z]' '[A-Z]')"
|
|
|
|
# grab + output options
|
|
test "$TEST_NO_FUSE" != 1 && test_set_prereq FUSE
|
|
test "$TEST_EXPENSIVE" = 1 && test_set_prereq EXPENSIVE
|
|
test "$TEST_NO_DOCKER" != 1 && type docker >/dev/null 2>&1 && groups | egrep "\bdocker\b" && test_set_prereq DOCKER
|
|
test "$TEST_NO_PLUGIN" != 1 && test "$TEST_OS" = "LINUX" && test_set_prereq PLUGIN
|
|
|
|
# this may not be available, skip a few dependent tests
|
|
type socat >/dev/null 2>&1 && test_set_prereq SOCAT
|
|
type unzip >/dev/null 2>&1 && test_set_prereq UNZIP
|
|
|
|
|
|
# Set a prereq as error messages are often different on Windows/Cygwin
|
|
expr "$TEST_OS" : "CYGWIN_NT" >/dev/null || test_set_prereq STD_ERR_MSG
|
|
|
|
if test "$TEST_VERBOSE" = 1; then
|
|
echo '# TEST_VERBOSE='"$TEST_VERBOSE"
|
|
echo '# TEST_NO_FUSE='"$TEST_NO_FUSE"
|
|
echo '# TEST_NO_PLUGIN='"$TEST_NO_PLUGIN"
|
|
echo '# TEST_EXPENSIVE='"$TEST_EXPENSIVE"
|
|
echo '# TEST_OS='"$TEST_OS"
|
|
fi
|
|
|
|
# source our generic test lib
|
|
. ../../ipfs-test-lib.sh
|
|
|
|
# source iptb lib
|
|
. ../lib/iptb-lib.sh
|
|
|
|
test_cmp_repeat_10_sec() {
|
|
for i in $(test_seq 1 100)
|
|
do
|
|
test_cmp "$1" "$2" >/dev/null && return
|
|
go-sleep 100ms
|
|
done
|
|
test_cmp "$1" "$2"
|
|
}
|
|
|
|
test_run_repeat_60_sec() {
|
|
for i in $(test_seq 1 600)
|
|
do
|
|
(test_eval_ "$1") && return
|
|
go-sleep 100ms
|
|
done
|
|
return 1 # failed
|
|
}
|
|
|
|
test_wait_output_n_lines_60_sec() {
|
|
for i in $(test_seq 1 600)
|
|
do
|
|
test $(cat "$1" | wc -l | tr -d " ") -ge $2 && return
|
|
go-sleep 100ms
|
|
done
|
|
actual=$(cat "$1" | wc -l | tr -d " ")
|
|
test_fsh "expected $2 lines of output. got $actual"
|
|
}
|
|
|
|
test_wait_open_tcp_port_10_sec() {
|
|
for i in $(test_seq 1 100)
|
|
do
|
|
# this is not a perfect check, but it's portable.
|
|
# cant count on ss. not installed everywhere.
|
|
# cant count on netstat using : or . as port delim. differ across platforms.
|
|
echo $(netstat -aln | egrep "^tcp.*LISTEN" | egrep "[.:]$1" | wc -l) -gt 0
|
|
if [ $(netstat -aln | egrep "^tcp.*LISTEN" | egrep "[.:]$1" | wc -l) -gt 0 ]; then
|
|
return 0
|
|
fi
|
|
go-sleep 100ms
|
|
done
|
|
return 1
|
|
}
|
|
|
|
|
|
# test_config_set helps us make sure _we really did set_ a config value.
|
|
# it sets it and then tests it. This became elaborate because ipfs config
|
|
# was setting really weird things and am not sure why.
|
|
test_config_set() {
|
|
|
|
# grab flags (like --bool in "ipfs config --bool")
|
|
test_cfg_flags="" # unset in case.
|
|
test "$#" = 3 && { test_cfg_flags=$1; shift; }
|
|
|
|
test_cfg_key=$1
|
|
test_cfg_val=$2
|
|
|
|
# when verbose, tell the user what config values are being set
|
|
test_cfg_cmd="ipfs config $test_cfg_flags \"$test_cfg_key\" \"$test_cfg_val\""
|
|
test "$TEST_VERBOSE" = 1 && echo "$test_cfg_cmd"
|
|
|
|
# ok try setting the config key/val pair.
|
|
ipfs config $test_cfg_flags "$test_cfg_key" "$test_cfg_val"
|
|
echo "$test_cfg_val" >cfg_set_expected
|
|
ipfs config "$test_cfg_key" >cfg_set_actual
|
|
test_cmp cfg_set_expected cfg_set_actual
|
|
}
|
|
|
|
test_init_ipfs() {
|
|
|
|
|
|
# we set the Addresses.API config variable.
|
|
# the cli client knows to use it, so only need to set.
|
|
# todo: in the future, use env?
|
|
|
|
test_expect_success "ipfs init succeeds" '
|
|
export IPFS_PATH="$(pwd)/.ipfs" &&
|
|
ipfs init --profile=test > /dev/null
|
|
'
|
|
|
|
test_expect_success "prepare config -- mounting" '
|
|
mkdir mountdir ipfs ipns &&
|
|
test_config_set Mounts.IPFS "$(pwd)/ipfs" &&
|
|
test_config_set Mounts.IPNS "$(pwd)/ipns" ||
|
|
test_fsh cat "\"$IPFS_PATH/config\""
|
|
'
|
|
|
|
}
|
|
|
|
test_config_ipfs_gateway_writable() {
|
|
test_expect_success "prepare config -- gateway writable" '
|
|
test_config_set --bool Gateway.Writable true ||
|
|
test_fsh cat "\"$IPFS_PATH/config\""
|
|
'
|
|
}
|
|
|
|
test_wait_for_file() {
|
|
loops=$1
|
|
delay=$2
|
|
file=$3
|
|
fwaitc=0
|
|
while ! test -f "$file"
|
|
do
|
|
if test $fwaitc -ge $loops
|
|
then
|
|
echo "Error: timed out waiting for file: $file"
|
|
return 1
|
|
fi
|
|
|
|
go-sleep $delay
|
|
fwaitc=`expr $fwaitc + 1`
|
|
done
|
|
}
|
|
|
|
test_set_address_vars() {
|
|
daemon_output="$1"
|
|
|
|
test_expect_success "set up address variables" '
|
|
API_MADDR=$(cat "$IPFS_PATH/api") &&
|
|
API_ADDR=$(convert_tcp_maddr $API_MADDR) &&
|
|
API_PORT=$(port_from_maddr $API_MADDR) &&
|
|
|
|
GWAY_MADDR=$(sed -n "s/^Gateway (.*) server listening on //p" "$daemon_output") &&
|
|
GWAY_ADDR=$(convert_tcp_maddr $GWAY_MADDR) &&
|
|
GWAY_PORT=$(port_from_maddr $GWAY_MADDR)
|
|
'
|
|
|
|
if ipfs swarm addrs local >/dev/null 2>&1; then
|
|
test_expect_success "get swarm addresses" '
|
|
ipfs swarm addrs local > addrs_out
|
|
'
|
|
|
|
test_expect_success "set swarm address vars" '
|
|
SWARM_MADDR=$(grep "127.0.0.1" addrs_out) &&
|
|
SWARM_PORT=$(port_from_maddr $SWARM_MADDR)
|
|
'
|
|
fi
|
|
}
|
|
|
|
test_launch_ipfs_daemon() {
|
|
|
|
args=("$@")
|
|
|
|
test "$TEST_ULIMIT_PRESET" != 1 && ulimit -n 2048
|
|
|
|
test_expect_success "'ipfs daemon' succeeds" '
|
|
ipfs daemon "${args[@]}" >actual_daemon 2>daemon_err &
|
|
IPFS_PID=$!
|
|
'
|
|
|
|
# wait for api file to show up
|
|
test_expect_success "api file shows up" '
|
|
test_wait_for_file 50 200ms "$IPFS_PATH/api"
|
|
'
|
|
|
|
test_set_address_vars actual_daemon
|
|
|
|
# we say the daemon is ready when the API server is ready.
|
|
test_expect_success "'ipfs daemon' is ready" '
|
|
pollEndpoint -host=$API_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout ||
|
|
test_fsh cat actual_daemon || test_fsh cat daemon_err || test_fsh cat poll_apierr || test_fsh cat poll_apiout
|
|
'
|
|
}
|
|
|
|
test_launch_ipfs_daemon_without_network() {
|
|
test_launch_ipfs_daemon --offline "$@"
|
|
}
|
|
|
|
do_umount() {
|
|
if [ "$(uname -s)" = "Linux" ]; then
|
|
fusermount -z -u "$1"
|
|
else
|
|
umount "$1"
|
|
fi
|
|
}
|
|
|
|
test_mount_ipfs() {
|
|
|
|
# make sure stuff is unmounted first.
|
|
test_expect_success FUSE "'ipfs mount' succeeds" '
|
|
do_umount "$(pwd)/ipfs" || true &&
|
|
do_umount "$(pwd)/ipns" || true &&
|
|
ipfs mount >actual
|
|
'
|
|
|
|
test_expect_success FUSE "'ipfs mount' output looks good" '
|
|
echo "IPFS mounted at: $(pwd)/ipfs" >expected &&
|
|
echo "IPNS mounted at: $(pwd)/ipns" >>expected &&
|
|
test_cmp expected actual
|
|
'
|
|
|
|
}
|
|
|
|
test_launch_ipfs_daemon_and_mount() {
|
|
|
|
test_init_ipfs
|
|
test_launch_ipfs_daemon
|
|
test_mount_ipfs
|
|
|
|
}
|
|
|
|
test_kill_repeat_10_sec() {
|
|
# try to shut down once + wait for graceful exit
|
|
kill $1
|
|
for i in $(test_seq 1 100)
|
|
do
|
|
go-sleep 100ms
|
|
! kill -0 $1 2>/dev/null && return
|
|
done
|
|
|
|
# if not, try once more, which will skip graceful exit
|
|
kill $1
|
|
go-sleep 1s
|
|
! kill -0 $1 2>/dev/null && return
|
|
|
|
# ok, no hope. kill it to prevent it messing with other tests
|
|
kill -9 $1 2>/dev/null
|
|
return 1
|
|
}
|
|
|
|
test_kill_ipfs_daemon() {
|
|
|
|
test_expect_success "'ipfs daemon' is still running" '
|
|
kill -0 $IPFS_PID
|
|
'
|
|
|
|
test_expect_success "'ipfs daemon' can be killed" '
|
|
test_kill_repeat_10_sec $IPFS_PID
|
|
'
|
|
}
|
|
|
|
test_curl_resp_http_code() {
|
|
curl -I "$1" >curl_output || {
|
|
echo "curl error with url: '$1'"
|
|
echo "curl output was:"
|
|
cat curl_output
|
|
return 1
|
|
}
|
|
shift &&
|
|
RESP=$(head -1 curl_output) &&
|
|
while test "$#" -gt 0
|
|
do
|
|
expr "$RESP" : "$1" >/dev/null && return
|
|
shift
|
|
done
|
|
echo "curl response didn't match!"
|
|
echo "curl response was: '$RESP'"
|
|
echo "curl output was:"
|
|
cat curl_output
|
|
return 1
|
|
}
|
|
|
|
test_must_be_empty() {
|
|
if test -s "$1"
|
|
then
|
|
echo "'$1' is not empty, it contains:"
|
|
cat "$1"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_should_contain() {
|
|
test "$#" = 2 || error "bug in the test script: not 2 parameters to test_should_contain"
|
|
if ! grep -q "$1" "$2"
|
|
then
|
|
echo "'$2' does not contain '$1', it contains:"
|
|
cat "$2"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_str_contains() {
|
|
find=$1
|
|
shift
|
|
echo "$@" | egrep "\b$find\b" >/dev/null
|
|
}
|
|
|
|
disk_usage() {
|
|
# normalize du across systems
|
|
case $(uname -s) in
|
|
Linux)
|
|
DU="du -sb"
|
|
M=1
|
|
;;
|
|
FreeBSD)
|
|
DU="du -s -A -B 1"
|
|
M=512
|
|
;;
|
|
Darwin | DragonFly | *)
|
|
DU="du -s"
|
|
M=512
|
|
;;
|
|
esac
|
|
expr $($DU "$1" | awk "{print \$1}") "*" "$M"
|
|
}
|
|
|
|
# output a file's permission in human readable format
|
|
generic_stat() {
|
|
# normalize stat across systems
|
|
case $(uname -s) in
|
|
Linux)
|
|
_STAT="stat -c %A"
|
|
;;
|
|
FreeBSD | Darwin | DragonFly)
|
|
_STAT="stat -f %Sp"
|
|
;;
|
|
*)
|
|
echo "unsupported OS" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
$_STAT "$1" || echo "failed" # Avoid returning nothing.
|
|
}
|
|
|
|
# output a file's permission in human readable format
|
|
file_size() {
|
|
case $(uname -s) in
|
|
Linux)
|
|
_STAT="stat --format=%s"
|
|
;;
|
|
FreeBSD | Darwin | DragonFly)
|
|
_STAT="stat -f%z"
|
|
;;
|
|
*)
|
|
echo "unsupported OS" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
$_STAT "$1"
|
|
}
|
|
|
|
# len 46: 2048-bit RSA keys, b58mh-encoded
|
|
# len 52: ED25519 keys, b58mh-encoded
|
|
# len 56: 2048-bit RSA keys, base36-encoded
|
|
# len 62: ED25519 keys, base36-encoded
|
|
test_check_peerid() {
|
|
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
|
|
test "$peeridlen" = "46" -o "$peeridlen" = "52" -o "$peeridlen" = "56" -o "$peeridlen" = "62" || {
|
|
echo "Bad peerid '$1' with len '$peeridlen'"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
test_check_rsa2048_b58mh_peerid() {
|
|
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
|
|
test "$peeridlen" = "46" || {
|
|
echo "Bad RSA2048 B58MH peerid '$1' with len '$peeridlen'"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
test_check_ed25519_b58mh_peerid() {
|
|
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
|
|
test "$peeridlen" = "52" || {
|
|
echo "Bad ED25519 B58MH peerid '$1' with len '$peeridlen'"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
test_check_rsa2048_base36_peerid() {
|
|
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
|
|
test "$peeridlen" = "56" || {
|
|
echo "Bad RSA2048 B36CID peerid '$1' with len '$peeridlen'"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
test_check_ed25519_base36_peerid() {
|
|
peeridlen=$(echo "$1" | tr -dC "[:alnum:]" | wc -c | tr -d " ") &&
|
|
test "$peeridlen" = "62" || {
|
|
echo "Bad ED25519 B36CID peerid '$1' with len '$peeridlen'"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
convert_tcp_maddr() {
|
|
echo $1 | awk -F'/' '{ printf "%s:%s", $3, $5 }'
|
|
}
|
|
|
|
port_from_maddr() {
|
|
echo $1 | awk -F'/' '{ print $NF }'
|
|
}
|
|
|
|
findprovs_empty() {
|
|
test_expect_success 'findprovs '$1' succeeds' '
|
|
ipfsi 1 dht findprovs -n 1 '$1' > findprovsOut
|
|
'
|
|
|
|
test_expect_success "findprovs $1 output is empty" '
|
|
test_must_be_empty findprovsOut
|
|
'
|
|
}
|
|
|
|
findprovs_expect() {
|
|
test_expect_success 'findprovs '$1' succeeds' '
|
|
ipfsi 1 dht findprovs -n 1 '$1' > findprovsOut &&
|
|
echo '$2' > expected
|
|
'
|
|
|
|
test_expect_success "findprovs $1 output looks good" '
|
|
test_cmp findprovsOut expected
|
|
'
|
|
}
|
|
|
|
purge_blockstore() {
|
|
ipfs pin ls --quiet --type=recursive | ipfs pin rm &>/dev/null
|
|
ipfs repo gc --silent &>/dev/null
|
|
|
|
test_expect_success "pinlist empty" '
|
|
[[ -z "$( ipfs pin ls )" ]]
|
|
'
|
|
test_expect_success "nothing left to gc" '
|
|
[[ -z "$( ipfs repo gc )" ]]
|
|
'
|
|
}
|
|
|