Skip to content

Commit 99be647

Browse files
chore: add shell scripts tests
1 parent e11a4b3 commit 99be647

3 files changed

Lines changed: 141 additions & 1 deletion

File tree

.github/workflows/pr.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,14 @@ jobs:
182182
github-token: ${{ secrets.GITHUB_TOKEN }}
183183
reporter: github-pr-check
184184
fail-on-error: true
185+
186+
test-shell:
187+
runs-on: ubuntu-22.04
188+
189+
steps:
190+
- uses: actions/checkout@v4
191+
with:
192+
persist-credentials: false
193+
194+
- name: Run shell tests
195+
run: yarn test-shell

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
"lint-llms": "python3 webapp/llms.py lint",
1616
"serve": "./entrypoint 0.0.0.0:${PORT}",
1717
"start": "yarn run build && concurrently --raw 'yarn run watch' 'yarn run serve'",
18-
"test": "yarn run lint-scss && yarn run lint-python && yarn run lint-html && yarn run lint-llms && yarn run test-python",
18+
"test": "yarn run lint-scss && yarn run lint-python && yarn run lint-html && yarn run lint-llms && yarn run test-python && yarn run test-shell",
1919
"test-python": "python3 -m unittest discover tests",
2020
"test-js": "jest --env=jsdom",
2121
"test-marketo": "python3 -m unittest tests.test_marketo",
2222
"test-kh": "python3 -m unittest tests.test_views.TestKnowledgeSections",
2323
"test-e2e": "playwright test",
24+
"test-shell": "for f in tests/shell/*.sh; do bash \"$f\" || exit 1; done",
2425
"percy-snapshot": "percy snapshot snapshots.js"
2526
},
2627
"dependencies": {

tests/shell/test-check-links.sh

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env bash
2+
# Smoke test for scripts/check-links.sh.
3+
#
4+
# Puts a fake `linkchecker` and a no-op `sleep` on PATH, so the real network and
5+
# the retry backoff are never hit. Run with: bash tests/shell/test-check-links.sh
6+
7+
set -uo pipefail
8+
9+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
11+
TARGET="${REPO_ROOT}/scripts/check-links.sh"
12+
13+
STUB_DIR="$(mktemp -d)"
14+
trap 'rm -rf "$STUB_DIR"' EXIT
15+
16+
# STUB_EXITS: space-separated exit code per invocation.
17+
# STUB_FAILS: space-separated group per invocation; comma-separated failing
18+
# URLs within a group, or "-" for none.
19+
cat > "${STUB_DIR}/linkchecker" <<'STUB'
20+
#!/usr/bin/env bash
21+
out=""
22+
prev=""
23+
for arg in "$@"; do
24+
[ "$prev" = "-F" ] && out="${arg#csv/}"
25+
prev="$arg"
26+
done
27+
28+
n=$(( $(cat "$STUB_STATE") + 1 ))
29+
echo "$n" > "$STUB_STATE"
30+
printf '%s\n' "$*" >> "$STUB_LOG"
31+
32+
read -ra exits <<< "$STUB_EXITS"
33+
read -ra groups <<< "$STUB_FAILS"
34+
code="${exits[$((n - 1))]:-0}"
35+
fails="${groups[$((n - 1))]:--}"
36+
37+
if [ -n "$out" ]; then
38+
{
39+
echo "# generated by fake linkchecker"
40+
echo "urlname;parentname;valid"
41+
if [ "$fails" != "-" ]; then
42+
IFS=',' read -ra urls <<< "$fails"
43+
for u in "${urls[@]}"; do echo "${u};https://example.com;False"; done
44+
fi
45+
echo "https://example.com/ok;https://example.com;True"
46+
} > "$out"
47+
fi
48+
49+
exit "$code"
50+
STUB
51+
chmod +x "${STUB_DIR}/linkchecker"
52+
53+
printf '#!/usr/bin/env bash\nexit 0\n' > "${STUB_DIR}/sleep"
54+
chmod +x "${STUB_DIR}/sleep"
55+
56+
export STUB_STATE="${STUB_DIR}/count"
57+
export STUB_LOG="${STUB_DIR}/log"
58+
59+
failures=0
60+
status=0
61+
output=""
62+
63+
run_case() {
64+
export STUB_EXITS="$1"
65+
export STUB_FAILS="$2"
66+
echo 0 > "$STUB_STATE"
67+
: > "$STUB_LOG"
68+
output="$(PATH="${STUB_DIR}:${PATH}" bash "$TARGET" https://example.com 2>&1)"
69+
status=$?
70+
}
71+
72+
check() {
73+
local name="$1" expected="$2" actual="$3"
74+
if [ "$expected" = "$actual" ]; then
75+
echo "ok - ${name}"
76+
else
77+
echo "FAIL - ${name}: expected '${expected}', got '${actual}'"
78+
printf '%s\n' "$output" | sed 's/^/ | /'
79+
failures=$((failures + 1))
80+
fi
81+
}
82+
83+
contains() {
84+
local name="$1" needle="$2"
85+
case "$output" in
86+
*"$needle"*) echo "ok - ${name}" ;;
87+
*)
88+
echo "FAIL - ${name}: output missing '${needle}'"
89+
printf '%s\n' "$output" | sed 's/^/ | /'
90+
failures=$((failures + 1))
91+
;;
92+
esac
93+
}
94+
95+
# 1. Clean run.
96+
run_case "0" "-"
97+
check "clean run exits 0" 0 "$status"
98+
contains "clean run reports success" "No broken links found."
99+
100+
# 2. Transient failure that clears on the first retry.
101+
run_case "1 0" "https://example.com/flaky -"
102+
check "transient failure exits 0" 0 "$status"
103+
contains "transient failure reports recovery" "resolved on attempt 1"
104+
105+
# 3. Persistent failure: initial run + 3 retries, then give up.
106+
run_case "1 1 1 1" "https://example.com/dead https://example.com/dead https://example.com/dead https://example.com/dead"
107+
check "persistent failure exits 1" 1 "$status"
108+
contains "persistent failure lists the URL" "https://example.com/dead"
109+
check "persistent failure uses all attempts" 4 "$(cat "$STUB_STATE")"
110+
111+
# 4. Unexpected linkchecker exit code is not treated as a broken link.
112+
run_case "3" "-"
113+
check "runtime error exits 2" 2 "$status"
114+
contains "runtime error is reported" "LinkChecker failed unexpectedly"
115+
116+
# 5. Duplicate failures are de-duplicated before the recheck.
117+
run_case "1 0" "https://example.com/a,https://example.com/a,https://example.com/b -"
118+
check "dedup run exits 0" 0 "$status"
119+
check "recheck receives 1 copy of /a" 1 "$(grep -c -o 'https://example.com/a' <<< "$(sed -n 2p "$STUB_LOG")")"
120+
contains "recheck reports 2 links" "rechecking 2 link(s)"
121+
122+
echo
123+
if [ "$failures" -eq 0 ]; then
124+
echo "All check-links.sh tests passed."
125+
else
126+
echo "${failures} test(s) failed."
127+
fi
128+
exit $(( failures > 0 ))

0 commit comments

Comments
 (0)