This repository has been archived on 2026-07-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
files/tests/files-smoke.sh
T
2026-07-04 20:44:28 +02:00

348 lines
11 KiB
Bash
Executable File

#!/bin/sh
# POSIX smoke suite for the files agent tool.
# req: files/001 files/002 files/003 files/004 files/005 files/006 files/007 files/008 files/009 files/010 files/011 files/012 files/013 files/014
set -u
bin=${FILES_BIN:-./zig-out/bin/files}
fail() {
printf 'files-smoke: %s\n' "$*" >&2
exit 1
}
assert_empty() {
file=$1
label=$2
[ ! -s "$file" ] || fail "$label not empty: $(cat "$file")"
}
assert_contains() {
file=$1
text=$2
label=$3
grep -F "$text" "$file" >/dev/null 2>&1 || fail "$label missing: $text"
}
assert_not_contains() {
file=$1
text=$2
label=$3
if grep -F "$text" "$file" >/dev/null 2>&1; then
fail "$label unexpectedly contains: $text"
fi
}
assert_status() {
got=$1
want=$2
label=$3
[ "$got" -eq "$want" ] || fail "$label exit $got, want $want"
}
assert_same_file() {
want=$1
got=$2
label=$3
cmp -s "$want" "$got" || fail "$label output mismatch"
}
assert_unchanged_tree() {
dir=$1
before=$2
label=$3
find "$dir" -print | sort > "$sorted"
assert_same_file "$before" "$sorted" "$label"
}
assert_runtime_failure() {
got=$1
label=$2
[ "$got" -ne 0 ] || fail "$label exit was zero"
[ "$got" -ne 2 ] || fail "$label used usage exit 2 for runtime failure"
}
assert_help_contract() {
file=$1
label=$2
assert_contains "$file" "usage: files [PATH...]" "$label"
assert_contains "$file" "stdout:" "$label"
assert_contains "$file" "One path per line" "$label"
assert_contains "$file" "exit status:" "$label"
assert_contains "$file" "paths only" "$label"
assert_contains "$file" "No filtering, sorting, metadata" "$label"
assert_contains "$file" "No deletion, exec, callbacks, network, hidden writes, config, or expression language" "$label"
}
write_tree_listing_expected() {
prefix=$1
{
printf '%s\n' "${prefix}--dash"
printf '%s\n' "${prefix}a/name with space"
printf '%s\n' "${prefix}a/one"
if [ "$have_symlink" = yes ]; then
printf '%s\n' "${prefix}linkdir"
printf '%s\n' "${prefix}linkfile"
fi
printf '%s\n' "${prefix}two"
} > "$expected"
}
[ -x "$bin" ] || fail "executable not found: $bin"
case $bin in
/*) abs_bin=$bin ;;
*) abs_bin=$(pwd -P)/$bin ;;
esac
i=0
base=${TMPDIR:-/tmp}/files-smoke.$$
while :; do
work=$base.$i
if mkdir "$work" 2>/dev/null; then
break
fi
i=$((i + 1))
[ "$i" -lt 50 ] || fail "could not create temp directory under ${TMPDIR:-/tmp}"
done
trap 'rm -rf "$work"' EXIT HUP INT TERM
out=$work/out
err=$work/err
expected=$work/expected
sorted=$work/sorted
tree=$work/tree
mkdir -p "$tree/a" "$tree/empty" || fail "mkdir tree"
printf 'dash\n' > "$tree/--dash" || fail "write dash"
printf 'one\n' > "$tree/a/one" || fail "write one"
printf 'space\n' > "$tree/a/name with space" || fail "write space"
printf 'two\n' > "$tree/two" || fail "write two"
if ln -s a "$tree/linkdir" 2>/dev/null && ln -s two "$tree/linkfile" 2>/dev/null; then
have_symlink=yes
else
have_symlink=no
fi
"$bin" --help > "$out" 2> "$err"
assert_status $? 0 "--help"
assert_empty "$err" "--help stderr"
assert_help_contract "$out" "help"
"$bin" -h > "$out" 2> "$err"
assert_status $? 0 "-h"
assert_empty "$err" "-h stderr"
assert_help_contract "$out" "-h help"
help_roots=$work/help-roots
mkdir -p "$help_roots" || fail "mkdir help roots"
printf 'help\n' > "$help_roots/--help" || fail "write --help root"
printf 'short\n' > "$help_roots/-h" || fail "write -h root"
printf 'extra\n' > "$help_roots/extra" || fail "write extra root"
(
cd "$help_roots" || exit 1
"$abs_bin" --help extra > "$out" 2> "$err"
)
assert_status $? 0 "--help as path root"
assert_empty "$err" "--help as path root stderr"
{
printf '%s\n' "--help"
printf '%s\n' "extra"
} > "$expected"
assert_same_file "$expected" "$out" "--help as path root"
(
cd "$help_roots" || exit 1
"$abs_bin" -h extra > "$out" 2> "$err"
)
assert_status $? 0 "-h as path root"
assert_empty "$err" "-h as path root stderr"
{
printf '%s\n' "-h"
printf '%s\n' "extra"
} > "$expected"
assert_same_file "$expected" "$out" "-h as path root"
"$bin" "$tree" > "$out" 2> "$err"
assert_status $? 0 "directory listing"
assert_empty "$err" "directory listing stderr"
sort "$out" > "$sorted"
write_tree_listing_expected "$tree/"
sort "$expected" > "$expected.sorted"
mv "$expected.sorted" "$expected"
assert_same_file "$expected" "$sorted" "directory listing"
assert_not_contains "$out" "$tree/linkdir/one" "symlink traversal"
(
cd "$tree" || exit 1
"$abs_bin" . > "$out" 2> "$err"
)
assert_status $? 0 "default dot listing"
assert_empty "$err" "default dot stderr"
sort "$out" > "$sorted"
write_tree_listing_expected ""
sort "$expected" > "$expected.sorted"
mv "$expected.sorted" "$expected"
assert_same_file "$expected" "$sorted" "default dot"
(
cd "$tree" || exit 1
"$abs_bin" > "$out" 2> "$err"
)
assert_status $? 0 "no-arg default listing"
assert_empty "$err" "no-arg default stderr"
sort "$out" > "$sorted"
write_tree_listing_expected ""
sort "$expected" > "$expected.sorted"
mv "$expected.sorted" "$expected"
assert_same_file "$expected" "$sorted" "no-arg default"
"$bin" "$tree/empty" > "$out" 2> "$err"
assert_status $? 0 "empty directory"
assert_empty "$out" "empty directory stdout"
assert_empty "$err" "empty directory stderr"
"$bin" "$tree/two" > "$out" 2> "$err"
assert_status $? 0 "file input"
assert_empty "$err" "file input stderr"
printf '%s\n' "$tree/two" > "$expected"
assert_same_file "$expected" "$out" "file input"
single=$work/single
mkdir -p "$single" || fail "mkdir single"
printf 'single\n' > "$single/only" || fail "write single"
"$bin" "$tree/two" "$single" "$tree/--dash" > "$out" 2> "$err"
assert_status $? 0 "multiple roots"
assert_empty "$err" "multiple roots stderr"
{
printf '%s\n' "$tree/two"
printf '%s\n' "$single/only"
printf '%s\n' "$tree/--dash"
} > "$expected"
assert_same_file "$expected" "$out" "multiple roots"
"$bin" "$tree/two" "$tree/two" "$single" "$single" > "$out" 2> "$err"
assert_status $? 0 "duplicate roots"
assert_empty "$err" "duplicate roots stderr"
{
printf '%s\n' "$tree/two"
printf '%s\n' "$tree/two"
printf '%s\n' "$single/only"
printf '%s\n' "$single/only"
} > "$expected"
assert_same_file "$expected" "$out" "duplicate roots"
"$bin" "$single/" "$tree/two" > "$out" 2> "$err"
assert_status $? 0 "trailing slash root"
assert_empty "$err" "trailing slash root stderr"
{
printf '%s\n' "$single/only"
printf '%s\n' "$tree/two"
} > "$expected"
assert_same_file "$expected" "$out" "trailing slash root"
assert_not_contains "$out" "$single//only" "trailing slash double slash"
if [ "$have_symlink" = yes ]; then
"$bin" "$tree/linkfile" "$tree/linkdir" > "$out" 2> "$err"
assert_status $? 0 "symlink roots"
assert_empty "$err" "symlink roots stderr"
{
printf '%s\n' "$tree/linkfile"
printf '%s\n' "$tree/linkdir"
} > "$expected"
assert_same_file "$expected" "$out" "symlink roots"
assert_not_contains "$out" "$tree/linkdir/one" "symlink root traversal"
"$bin" "$tree/linkdir" "$single" > "$out" 2> "$err"
assert_status $? 0 "symlink then directory root"
assert_empty "$err" "symlink then directory root stderr"
{
printf '%s\n' "$tree/linkdir"
printf '%s\n' "$single/only"
} > "$expected"
assert_same_file "$expected" "$out" "symlink then directory root"
assert_not_contains "$out" "$tree/linkdir/one" "symlink then directory traversal"
fi
later_missing=$work/later-missing
if "$bin" "$tree/two" "$later_missing" > "$out" 2> "$err"; then
fail "missing later root unexpectedly succeeded"
else
status=$?
fi
assert_runtime_failure "$status" "missing later root"
printf '%s\n' "$tree/two" > "$expected"
assert_same_file "$expected" "$out" "missing later root partial stdout"
assert_contains "$err" "files: $later_missing:" "missing later root stderr"
if "$bin" "" > "$out" 2> "$err"; then
fail "empty path unexpectedly succeeded"
else
status=$?
fi
assert_runtime_failure "$status" "empty path"
assert_empty "$out" "empty path stdout"
assert_contains "$err" "files: empty PATH" "empty path stderr"
missing=$work/missing
if "$bin" "$missing" > "$out" 2> "$err"; then
fail "missing path unexpectedly succeeded"
else
status=$?
fi
assert_runtime_failure "$status" "missing path"
assert_empty "$out" "missing path stdout"
assert_contains "$err" "files: $missing:" "missing path stderr"
restricted=$work/restricted
mkdir -p "$restricted/blocked" || fail "mkdir restricted"
printf 'hidden\n' > "$restricted/blocked/file" || fail "write restricted file"
chmod 000 "$restricted/blocked" || fail "chmod restricted"
if "$bin" "$restricted" > "$out" 2> "$err"; then
status=0
else
status=$?
fi
chmod 700 "$restricted/blocked" || fail "restore restricted permissions"
if [ "$status" -eq 0 ]; then
if [ ! -r "$restricted/blocked" ] || [ ! -x "$restricted/blocked" ]; then
fail "unreadable directory unexpectedly succeeded"
fi
else
assert_runtime_failure "$status" "unreadable directory"
assert_empty "$out" "unreadable directory stdout"
assert_contains "$err" "files: $restricted:" "unreadable directory stderr"
fi
big=$work/big
mkdir -p "$big" || fail "mkdir big"
n=1
while [ "$n" -le 1000 ]; do
d=$big/d$((n % 10))
mkdir -p "$d" || fail "mkdir big dir $n"
: > "$d/$n" || fail "write big $n"
n=$((n + 1))
done
"$bin" "$big" > "$out" 2> "$err"
assert_status $? 0 "generated tree listing"
assert_empty "$err" "generated tree stderr"
count=$(wc -l < "$out" | tr -d ' ')
[ "$count" -eq 1000 ] || fail "generated tree count $count, want 1000"
{ "$bin" "$big" | head -n 1 > /dev/null; } 2> "$err"
assert_empty "$err" "pipeline stderr"
{ "$bin" "$big" "$work/pipe-missing" | head -n 1 > /dev/null; } 2> "$err"
assert_empty "$err" "multi-root pipeline stderr"
no_mutation=$work/no-mutation
mkdir -p "$no_mutation/home" "$no_mutation/cache" "$no_mutation/target/sub" || fail "mkdir no-mutation"
printf 'kept\n' > "$no_mutation/target/sub/file" || fail "write no-mutation file"
find "$no_mutation" -print | sort > "$expected"
HOME=$no_mutation/home XDG_CACHE_HOME=$no_mutation/cache "$bin" "$no_mutation/target" > "$out" 2> "$err"
assert_status $? 0 "no-mutation listing"
assert_empty "$err" "no-mutation listing stderr"
assert_unchanged_tree "$no_mutation" "$expected" "no-mutation listing"
HOME=$no_mutation/home XDG_CACHE_HOME=$no_mutation/cache "$bin" --help > "$out" 2> "$err"
assert_status $? 0 "no-mutation help"
assert_empty "$err" "no-mutation help stderr"
assert_unchanged_tree "$no_mutation" "$expected" "no-mutation help"
printf 'files-smoke: ok\n' >&2