|
source "${0:A:h}/../s3.zsh" |
|
|
|
typeset -g AWS_CALL='' |
|
typeset -ga AWS_CALLS=() |
|
typeset -gA EXISTING |
|
typeset -gi AWS_EXIT=0 |
|
typeset -gi HEAD_EXIT=1 |
|
aws() { |
|
if [[ $1 == s3api && $2 == head-object ]]; then |
|
[[ -n ${EXISTING[$6]-} ]] && return 0 |
|
return $HEAD_EXIT |
|
fi |
|
AWS_CALL="$@" |
|
AWS_CALLS+=("$*") |
|
return $AWS_EXIT |
|
} |
|
|
|
tmp=$(command mktemp -d -t s3.test.XXXXXX) |
|
tmp=${tmp:A} |
|
trap 'command rm -rf -- "$tmp"' EXIT |
|
|
|
print -r -- pixel > "$tmp/pic.png" |
|
print -r -- pixel > "$tmp/b.png" |
|
print -r -- pixel > "$tmp/c.png" |
|
command mkdir -p "$tmp/deep/dir" |
|
print -r -- pixel > "$tmp/deep/dir/img.png" |
|
|
|
esc=$'\e' |
|
out='' err='' ret=0 |
|
run() { |
|
"$@" >$tmp/out 2>$tmp/err |
|
ret=$? |
|
out=$(<$tmp/out) |
|
err=$(<$tmp/err) |
|
} |
|
|
|
plan 76 |
|
|
|
# --- help: -h / --help ------------------------------------------------- |
|
|
|
run s3 -h |
|
is $ret 0 'help(-h): exits 0' |
|
is "$out" "usage: s3 upload|up|u FILE... [--force] [aws-flags...] |
|
s3 delete|del|d FILE|URL... [aws-flags...]" 'help(-h): usage on stdout' |
|
is "$err" '' 'help(-h): nothing on stderr' |
|
|
|
run s3 --help |
|
is $ret 0 'help(--help): exits 0' |
|
is "$out" "usage: s3 upload|up|u FILE... [--force] [aws-flags...] |
|
s3 delete|del|d FILE|URL... [aws-flags...]" 'help(--help): usage on stdout' |
|
is "$err" '' 'help(--help): nothing on stderr' |
|
|
|
# --- invalid usage ------------------------------------------------------- |
|
|
|
run s3 |
|
is $ret 2 'no args: exits 2' |
|
like "$err" '*unknown command:*' 'no args: stderr names unknown command' |
|
like "$out" '*usage: s3 upload*' 'no args: usage on stdout' |
|
run s3 frobnicate |
|
is $ret 2 'bad verb: exits 2' |
|
like "$err" '*unknown command: frobnicate*' 'bad verb: stderr names it' |
|
like "$out" '*usage: s3 upload*' 'bad verb: usage on stdout' |
|
|
|
run s3 upload |
|
is $ret 2 'upload no arg: exits 2' |
|
is "$err" "usage: s3 upload|up|u FILE... [--force] [aws-flags...] |
|
s3 delete|del|d FILE|URL... [aws-flags...]" 'upload no arg: usage on stderr' |
|
is "$out" '' 'upload no arg: nothing on stdout' |
|
|
|
run s3 delete |
|
is $ret 2 'delete no arg: exits 2' |
|
is "$err" "usage: s3 upload|up|u FILE... [--force] [aws-flags...] |
|
s3 delete|del|d FILE|URL... [aws-flags...]" 'delete no arg: usage on stderr' |
|
is "$out" '' 'delete no arg: nothing on stdout' |
|
|
|
# --- upload: missing local file ------------------------------------------ |
|
|
|
run s3 upload "$tmp/nope.png" |
|
is $ret 1 'upload missing file: exits 1' |
|
like "$err" "*no such file: $tmp/nope.png*" 'upload missing file: stderr names it' |
|
is "$out" '' 'upload missing file: nothing on stdout' |
|
|
|
# --- upload: success ------------------------------------------------------ |
|
|
|
AWS_EXIT=0 |
|
run s3 upload "$tmp/pic.png" |
|
is $ret 0 'upload success: exits 0' |
|
is "$out" 'https://bp-articles.s3.eu-west-1.amazonaws.com/pic.png' \ |
|
'upload success: exact URL on stdout' |
|
like "$err" '*uploading*' 'upload success: progress line on stderr' |
|
like "$err" '*done*' 'upload success: done line on stderr' |
|
like "$AWS_CALL" '*s3 cp*' 'upload success: calls aws s3 cp' |
|
like "$AWS_CALL" '*s3://bp-articles/pic.png*' 'upload success: correct s3 uri' |
|
like "$AWS_CALL" "*--cache-control*public, max-age=31536000, immutable*" \ |
|
'upload success: cache-control header' |
|
|
|
run s3 upload "$tmp/deep/dir/img.png" |
|
is "$out" 'https://bp-articles.s3.eu-west-1.amazonaws.com/img.png' \ |
|
'upload nested path: key uses basename only' |
|
|
|
run s3 upload "$tmp/pic.png" --dryrun |
|
like "$AWS_CALL" '*--dryrun*' 'upload: extra flags passed through to aws' |
|
|
|
run s3 up "$tmp/pic.png" |
|
is "$out" 'https://bp-articles.s3.eu-west-1.amazonaws.com/pic.png' \ |
|
'up alias: behaves identically to upload' |
|
|
|
# --- upload: clobber protection ------------------------------------------ |
|
|
|
HEAD_EXIT=0 |
|
AWS_CALL='' |
|
run s3 upload "$tmp/pic.png" |
|
is $ret 1 'clobber: refuses when key exists' |
|
like "$err" '*pic.png already exists*use --force*' 'clobber: refusal message' |
|
is "$out" '' 'clobber: nothing on stdout' |
|
is "$AWS_CALL" '' 'clobber: aws cp never invoked' |
|
|
|
run s3 upload "$tmp/pic.png" --force |
|
is $ret 0 'clobber: --force overrides refusal' |
|
is "$out" 'https://bp-articles.s3.eu-west-1.amazonaws.com/pic.png' \ |
|
'clobber: --force still returns correct url' |
|
unlike "$AWS_CALL" '*--force*' 'clobber: --force stripped, not passed to aws' |
|
|
|
HEAD_EXIT=1 |
|
|
|
# --- upload: multiple files ------------------------------------------ |
|
|
|
AWS_CALLS=() |
|
run s3 upload "$tmp/pic.png" "$tmp/b.png" |
|
is $ret 0 'multi-upload: exits 0 when all succeed' |
|
like "$out" '*/pic.png*' 'multi-upload: first url on stdout' |
|
like "$out" '*/b.png*' 'multi-upload: second url on stdout' |
|
is ${#AWS_CALLS} 2 'multi-upload: both files actually uploaded' |
|
|
|
AWS_CALLS=() |
|
run s3 upload "$tmp/pic.png" "$tmp/nope.png" "$tmp/b.png" |
|
is $ret 1 'multi-upload: nonzero exit if any file missing' |
|
like "$err" "*no such file: $tmp/nope.png*" 'multi-upload: missing file named on stderr' |
|
is ${#AWS_CALLS} 2 'multi-upload: the two valid files still uploaded' |
|
|
|
AWS_CALLS=() |
|
EXISTING=(b.png 1) |
|
run s3 upload "$tmp/pic.png" "$tmp/b.png" "$tmp/c.png" |
|
is $ret 1 'multi-upload: nonzero exit if any file clobbers' |
|
like "$err" '*b.png already exists*' 'multi-upload: clobber names the right file' |
|
is ${#AWS_CALLS} 2 'multi-upload: non-conflicting files still uploaded' |
|
|
|
AWS_CALLS=() |
|
run s3 upload "$tmp/pic.png" "$tmp/b.png" --force |
|
is $ret 0 'multi-upload: --force applies to whole batch' |
|
is ${#AWS_CALLS} 2 'multi-upload: --force uploaded all files despite conflict' |
|
EXISTING=() |
|
|
|
run s3 upload --force |
|
is $ret 2 'multi-upload: flag with no files is a usage error' |
|
|
|
# --- upload: aws failure ---------------------------------------------- |
|
|
|
AWS_EXIT=1 |
|
run s3 upload "$tmp/pic.png" |
|
is $ret 1 'upload aws failure: exits 1' |
|
like "$err" "*upload failed: $tmp/pic.png*" 'upload aws failure: stderr message' |
|
is "$out" '' 'upload aws failure: nothing on stdout' |
|
AWS_EXIT=0 |
|
|
|
# --- delete: key resolution ------------------------------------------ |
|
|
|
run s3 delete pic.png |
|
like "$AWS_CALL" '*s3://bp-articles/pic.png*' \ |
|
'delete by bare filename: correct key' |
|
|
|
run s3 del pic.png |
|
like "$AWS_CALL" '*s3://bp-articles/pic.png*' \ |
|
'del alias: behaves identically to delete' |
|
|
|
run s3 delete /some/other/dir/pic.png |
|
like "$AWS_CALL" '*s3://bp-articles/pic.png*' \ |
|
'delete by filepath: basename-only key' |
|
|
|
run s3 delete https://bp-articles.s3.eu-west-1.amazonaws.com/pic.png |
|
like "$AWS_CALL" '*s3://bp-articles/pic.png*' \ |
|
'delete by url: prefix-stripped key' |
|
|
|
# --- delete: success / failure ----------------------------------------- |
|
|
|
AWS_EXIT=0 |
|
run s3 delete pic.png |
|
is $ret 0 'delete success: exits 0' |
|
is "$out" '' 'delete success: nothing on stdout' |
|
like "$err" '*done*' 'delete success: done line on stderr' |
|
|
|
AWS_EXIT=1 |
|
run s3 delete pic.png |
|
is $ret 1 'delete aws failure: exits 1' |
|
like "$err" '*delete failed: pic.png*' 'delete aws failure: stderr message' |
|
AWS_EXIT=0 |
|
|
|
run s3 delete pic.png --dryrun |
|
like "$AWS_CALL" '*--dryrun*' 'delete: extra flags passed through to aws' |
|
|
|
# --- delete: multiple assets ------------------------------------------ |
|
|
|
AWS_CALLS=() |
|
run s3 delete pic.png b.png |
|
is $ret 0 'multi-delete: exits 0 when all succeed' |
|
is ${#AWS_CALLS} 2 'multi-delete: both keys deleted' |
|
like "$err" '*2 asset\(s\)*' 'multi-delete: header counts assets' |
|
AWS_CALLS=() |
|
run s3 delete pic.png https://bp-articles.s3.eu-west-1.amazonaws.com/c.png |
|
is ${#AWS_CALLS} 2 'multi-delete: mixed filename and url both resolve' |
|
like "${AWS_CALLS[2]}" '*s3://bp-articles/c.png*' \ |
|
'multi-delete: url arg resolved to key' |
|
|
|
AWS_EXIT=1 |
|
run s3 delete pic.png b.png |
|
is $ret 1 'multi-delete: nonzero exit if any delete fails' |
|
AWS_EXIT=0 |
|
|
|
run s3 delete --dryrun |
|
is $ret 2 'multi-delete: flags with no assets is a usage error' |
|
|
|
# --- color gating -------------------------------------------------------- |
|
|
|
NO_COLOR=1 run s3 upload "$tmp/pic.png" |
|
unlike "$err" "*${esc}\[*" 'NO_COLOR=1: no escape codes on stderr' |
|
|
|
FORCE_COLOR=1 run s3 upload "$tmp/pic.png" |
|
like "$err" "*${esc}\[*" 'FORCE_COLOR=1: escape codes present on stderr' |
|
|
|
NO_COLOR=1 FORCE_COLOR=1 run s3 upload "$tmp/pic.png" |
|
unlike "$err" "*${esc}\[*" 'NO_COLOR beats FORCE_COLOR: no escape codes' |
|
|
|
# --- config overrides ---------------------------------------------------- |
|
|
|
run s3 upload "$tmp/pic.png" |
|
is "$out" 'https://bp-articles.s3.eu-west-1.amazonaws.com/pic.png' \ |
|
'default bucket/region reflected in url' |
|
|
|
S3_BUCKET=custom-bucket S3_REGION=us-east-1 run s3 upload "$tmp/pic.png" |
|
is "$out" 'https://custom-bucket.s3.us-east-1.amazonaws.com/pic.png' \ |
|
'custom S3_BUCKET/S3_REGION reflected in url' |
|
|
|
finish |