#!/usr/bin/env bash # End-to-end tests for generate-env.sh. # # Each test pipes a crafted stdin to generate-env.sh and inspects the output # .env file. Input lines are counted to match each variable's prompt exactly. TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(dirname "$TESTS_DIR")" SCRIPT="$ROOT_DIR/generate-env.sh" source "$TESTS_DIR/framework.sh" TMPDIR_SELF="$(mktemp -d)" trap 'rm -rf "$TMPDIR_SELF"' EXIT # run_gen # Runs generate-env.sh, feeding as stdin. # Suppresses all terminal output. Removes first so the # overwrite guard never triggers. run_gen() { local input="$1" local in_file="$2" local out_file="$3" rm -f "$out_file" # -o must come before the positional input-file argument so getopts sees it printf "%s" "$input" | bash "$SCRIPT" -o "$out_file" "$in_file" 2>/dev/null } # --------------------------------------------------------------------------- suite "e2e: accept all defaults" # --------------------------------------------------------------------------- # Fixture: three optional vars with defaults, no required fields IN="$TMPDIR_SELF/defaults.env.example" OUT="$TMPDIR_SELF/defaults.env" cat > "$IN" << 'EOF' # App name APP_NAME=MyApp # Port # @type number PORT=3000 # Debug mode # @type bool DEBUG=false EOF # Input: three Enter presses (accept all defaults) run_gen $'\n\n\n' "$IN" "$OUT" assert_file_exists "$OUT" "output file created" assert_file_contains "APP_NAME=MyApp" "$OUT" "string default accepted" assert_file_contains "PORT=3000" "$OUT" "number default accepted" assert_file_contains "DEBUG=false" "$OUT" "bool default accepted" # --------------------------------------------------------------------------- suite "e2e: override defaults" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/override.env.example" OUT="$TMPDIR_SELF/override.env" cat > "$IN" << 'EOF' APP_NAME=MyApp PORT=3000 EOF # Override both run_gen $'CustomApp\n8080\n' "$IN" "$OUT" assert_file_contains "APP_NAME=CustomApp" "$OUT" "string default overridden" assert_file_contains "PORT=8080" "$OUT" "number default overridden" # --------------------------------------------------------------------------- suite "e2e: required fields must be filled" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/required.env.example" OUT="$TMPDIR_SELF/required.env" cat > "$IN" << 'EOF' # @required MUST_HAVE= EOF # First send empty (should re-prompt), then a value run_gen $'\nfilled\n' "$IN" "$OUT" assert_file_contains "MUST_HAVE=filled" "$OUT" "required field filled on retry" # --------------------------------------------------------------------------- suite "e2e: bool values" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/bool.env.example" OUT="$TMPDIR_SELF/bool.env" cat > "$IN" << 'EOF' # @type bool FLAG_A=false # @type bool FLAG_B=true EOF # y for FLAG_A (-> true), Enter for FLAG_B (-> default true) run_gen $'y\n\n' "$IN" "$OUT" assert_file_contains "FLAG_A=true" "$OUT" "y -> true" assert_file_contains "FLAG_B=true" "$OUT" "empty accepts true default" # --------------------------------------------------------------------------- suite "e2e: enum with numeric choice" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/enum.env.example" OUT="$TMPDIR_SELF/enum.env" cat > "$IN" << 'EOF' # @type enum:development,staging,production # @required APP_ENV=development EOF # Choose option 3 (production) run_gen $'3\n' "$IN" "$OUT" assert_file_contains "APP_ENV=production" "$OUT" "enum numeric choice 3 -> production" # --------------------------------------------------------------------------- suite "e2e: enum with string choice" # --------------------------------------------------------------------------- OUT="$TMPDIR_SELF/enum_str.env" run_gen $'staging\n' "$IN" "$OUT" assert_file_contains "APP_ENV=staging" "$OUT" "enum string choice -> staging" # --------------------------------------------------------------------------- suite "e2e: enum accepts default" # --------------------------------------------------------------------------- OUT="$TMPDIR_SELF/enum_default.env" run_gen $'\n' "$IN" "$OUT" assert_file_contains "APP_ENV=development" "$OUT" "enum empty input uses default" # --------------------------------------------------------------------------- suite "e2e: secret with confirmation" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/secret.env.example" OUT="$TMPDIR_SELF/secret.env" cat > "$IN" << 'EOF' # @type secret # @required JWT_SECRET= EOF # Enter value twice (entry + confirm) run_gen $'supersecret\nsupersecret\n' "$IN" "$OUT" assert_file_contains "JWT_SECRET=supersecret" "$OUT" "secret stored after confirmation" # --------------------------------------------------------------------------- suite "e2e: secret uses default on empty input" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/secret_default.env.example" OUT="$TMPDIR_SELF/secret_default.env" cat > "$IN" << 'EOF' # @type secret JWT_SECRET=existing_token EOF # Empty input -> keep default run_gen $'\n' "$IN" "$OUT" assert_file_contains "JWT_SECRET=existing_token" "$OUT" "secret empty input keeps default" # --------------------------------------------------------------------------- suite "e2e: -o flag sets output path" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/flag.env.example" CUSTOM_OUT="$TMPDIR_SELF/custom_output.env" cat > "$IN" << 'EOF' KEY=val EOF rm -f "$CUSTOM_OUT" printf "\n" | bash "$SCRIPT" -o "$CUSTOM_OUT" "$IN" 2>/dev/null assert_file_exists "$CUSTOM_OUT" "custom output path created via -o flag" assert_file_contains "KEY=val" "$CUSTOM_OUT" "content written to custom path" # --------------------------------------------------------------------------- suite "e2e: empty optional field written as KEY=" # --------------------------------------------------------------------------- IN="$TMPDIR_SELF/optional.env.example" OUT="$TMPDIR_SELF/optional.env" cat > "$IN" << 'EOF' # Optional field with no default # @type email ADMIN_EMAIL= EOF # Leave empty run_gen $'\n' "$IN" "$OUT" assert_file_contains "ADMIN_EMAIL=" "$OUT" "optional empty field written as KEY=" assert_file_not_contains "ADMIN_EMAIL=x" "$OUT" "no stray value" # --------------------------------------------------------------------------- suite "e2e: full example.env.example" # --------------------------------------------------------------------------- # # Variable order and input count: # 1. APP_NAME (string, required, default "MyApp") → "\n" accept default # 2. APP_ENV (enum:…, required, default "development") → "\n" accept default (1) # 3. PORT (number, optional, default "3000") → "\n" accept default # 4. DEBUG (bool, optional, default "false") → "n\n" explicit n # 5. JWT_SECRET(secret, required, no default) → "tok\ntok\n" entry+confirm # 6. DATABASE_URL(url, required, has default) → "\n" accept default # 7. ADMIN_EMAIL (email, optional, no default) → "\n" leave empty OUT="$TMPDIR_SELF/full.env" INPUT=$'\n\n\nn\ntok\ntok\n\n\n' run_gen "$INPUT" "$ROOT_DIR/example.env.example" "$OUT" assert_file_exists "$OUT" "output file created" assert_file_contains "APP_NAME=MyApp" "$OUT" "APP_NAME default" assert_file_contains "APP_ENV=development" "$OUT" "APP_ENV default" assert_file_contains "PORT=3000" "$OUT" "PORT default" assert_file_contains "DEBUG=false" "$OUT" "DEBUG=false (n)" assert_file_contains "JWT_SECRET=tok" "$OUT" "JWT_SECRET value" assert_file_contains "DATABASE_URL=postgres://localhost:5432/myapp" "$OUT" "DATABASE_URL default" assert_file_contains "ADMIN_EMAIL=" "$OUT" "ADMIN_EMAIL empty" assert_file_contains "# Generated by generate-env" "$OUT" "header present" print_summary