Add the RepoFoundry foundation: config parsing, HTTP helper, prompts, tests
create-project.sh validates config.env and .env (parsed, never sourced), checks the required tools, asks for the project details and prints a summary. It makes no network call and no change on disk yet. - config.env.example and .env.example hold placeholders only - tokens go through a private curl config file, never the command line - tests run in private directories with a stub curl; shellcheck and shfmt are part of tests/run-tests.sh Task: MIL-001#1 Task: MIL-001#2 Task: MIL-001#3 Task: MIL-001#4 Task: MIL-001#5 Task: MIL-001#6 Closes #3 Closes #4 Closes #5 Closes #6 Closes #7 Closes #8 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+162
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env bash
|
||||
# lib.sh - tiny test helpers for the RepoFoundry tests (sourced, not run).
|
||||
#
|
||||
# Each test file defines functions named test_*; run-tests.sh calls them.
|
||||
# The helpers run create-project.sh in separate bash processes with a private
|
||||
# work directory and stub tools on PATH, so a test never touches the network,
|
||||
# the real .env or the repository.
|
||||
#
|
||||
# Requires: bash 4.4 or later.
|
||||
|
||||
# shellcheck disable=SC2016,SC2034 # stub and snippet text is literal on purpose; OUT, ERR and STATUS are read by the test files
|
||||
REPO_ROOT="$(cd "${BASH_SOURCE[0]%/*}/.." && pwd)"
|
||||
readonly REPO_ROOT
|
||||
readonly SCRIPT="$REPO_ROOT/create-project.sh"
|
||||
|
||||
# Distinctive fake credentials; the tests search all output for them.
|
||||
readonly FAKE_GITEA_TOKEN="giteaFAKEtoken1234567890"
|
||||
readonly FAKE_GITHUB_PAT="ghpFAKEtoken1234567890"
|
||||
|
||||
TESTS_RUN=0
|
||||
TESTS_FAILED=0
|
||||
CURRENT_TEST=""
|
||||
WORK=""
|
||||
OUT=""
|
||||
ERR=""
|
||||
STATUS=0
|
||||
|
||||
fail() {
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
printf 'FAIL %s: %s\n' "$CURRENT_TEST" "$1"
|
||||
}
|
||||
|
||||
check() {
|
||||
TESTS_RUN=$((TESTS_RUN + 1))
|
||||
}
|
||||
|
||||
assert_eq() {
|
||||
check
|
||||
if [[ $2 != "$3" ]]; then
|
||||
fail "$1: expected '$2', got '$3'"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_status() {
|
||||
assert_eq "$1 (exit status)" "$2" "$3"
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
check
|
||||
if [[ $2 != *"$3"* ]]; then
|
||||
fail "$1: output does not contain '$3'"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_contains() {
|
||||
check
|
||||
if [[ $2 == *"$3"* ]]; then
|
||||
fail "$1: output contains '$3' but must not"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_file_exists() {
|
||||
check
|
||||
if [[ ! -e $2 ]]; then
|
||||
fail "$1: '$2' does not exist"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_file_missing() {
|
||||
check
|
||||
if [[ -e $2 ]]; then
|
||||
fail "$1: '$2' exists but must not"
|
||||
fi
|
||||
}
|
||||
|
||||
# new_workdir: create a private work directory with a stub bin directory.
|
||||
new_workdir() {
|
||||
WORK="$(mktemp -d "${TMPDIR:-/tmp}/repofoundry-test.XXXXXX")"
|
||||
mkdir -p "$WORK/bin" "$WORK/tmp"
|
||||
}
|
||||
|
||||
# remove_workdir: delete the work directory without a recursive rm: files
|
||||
# first, then the now empty directories from the bottom up.
|
||||
remove_workdir() {
|
||||
if [[ -n $WORK && -d $WORK ]]; then
|
||||
find "$WORK" -type f -delete
|
||||
find "$WORK" -depth -type d -exec rmdir {} +
|
||||
fi
|
||||
WORK=""
|
||||
}
|
||||
|
||||
# write_fixtures: valid config.env and .env in the work directory.
|
||||
write_fixtures() {
|
||||
cat >"$WORK/config.env" <<EOF
|
||||
# test configuration
|
||||
GITHUB_API_URL=https://api.github.com
|
||||
GITHUB_WEB_URL=https://github.com
|
||||
GITEA_URL=https://git.example.test/
|
||||
GITEA_API_URL=https://git.example.test/api/v1
|
||||
EOF
|
||||
cat >"$WORK/.env" <<EOF
|
||||
GITHUB_PAT=$FAKE_GITHUB_PAT
|
||||
GITHUB_USER=octo-user
|
||||
GITEA_TOKEN=$FAKE_GITEA_TOKEN
|
||||
EOF
|
||||
}
|
||||
|
||||
# write_stub NAME BODY: install an executable stub tool in the work bin.
|
||||
write_stub() {
|
||||
printf '#!/usr/bin/env bash\n%s\n' "$2" >"$WORK/bin/$1"
|
||||
chmod +x "$WORK/bin/$1"
|
||||
}
|
||||
|
||||
# write_curl_stub: a curl that records its arguments and configuration and
|
||||
# answers with STUB_CURL_STATUS (default 200) and body STUB_CURL_BODY.
|
||||
write_curl_stub() {
|
||||
write_stub curl '
|
||||
printf "%s\n" "$@" >>"$STUB_DIR/curl.args"
|
||||
out="" cfg=""
|
||||
while (($# > 0)); do
|
||||
case "$1" in
|
||||
--output) out="$2"; shift 2 ;;
|
||||
--config) cfg="$2"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
if [[ -n $cfg ]]; then cat "$cfg" >>"$STUB_DIR/curl.config"; fi
|
||||
body="${STUB_CURL_BODY:-}"
|
||||
if [[ -z $body ]]; then body="{\"ok\":true}"; fi
|
||||
if [[ -n $out ]]; then printf "%s" "$body" >"$out"; fi
|
||||
printf "%s" "${STUB_CURL_STATUS:-200}"
|
||||
exit "${STUB_CURL_EXIT:-0}"'
|
||||
}
|
||||
|
||||
# run_cli STDIN ARGS...: run create-project.sh with answers from STDIN (a
|
||||
# string). Sets OUT, ERR and STATUS.
|
||||
run_cli() {
|
||||
local input="$1"
|
||||
shift
|
||||
STATUS=0
|
||||
PATH="$WORK/bin:$PATH" STUB_DIR="$WORK" TMPDIR="$WORK/tmp" \
|
||||
"$BASH" "$SCRIPT" "$@" <<<"$input" >"$WORK/out.txt" 2>"$WORK/err.txt" ||
|
||||
STATUS=$?
|
||||
OUT="$(cat "$WORK/out.txt")"
|
||||
ERR="$(cat "$WORK/err.txt")"
|
||||
}
|
||||
|
||||
# run_lib INPUT CODE: source create-project.sh and run CODE in a fresh bash,
|
||||
# so that single functions can be tested. Sets OUT, ERR and STATUS.
|
||||
run_lib() {
|
||||
local input="$1"
|
||||
STATUS=0
|
||||
{
|
||||
printf '#!/usr/bin/env bash\nsource "%s"\n' "$SCRIPT"
|
||||
printf '%s\n' "$2"
|
||||
} >"$WORK/snippet.sh"
|
||||
PATH="$WORK/bin:$PATH" STUB_DIR="$WORK" TMPDIR="$WORK/tmp" \
|
||||
"$BASH" "$WORK/snippet.sh" <<<"$input" >"$WORK/out.txt" 2>"$WORK/err.txt" ||
|
||||
STATUS=$?
|
||||
OUT="$(cat "$WORK/out.txt")"
|
||||
ERR="$(cat "$WORK/err.txt")"
|
||||
}
|
||||
Reference in New Issue
Block a user