Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Version 1.1.0 brings major improvements to usability, troubleshooting, and devel
- **Documentation**: Full changelog in `CHANGELOG.md`

### 🔧 Engineering Quality
- **Test Suite**: 14 integration tests ensure reliability
- **Test Suite**: 25 integration tests cover versioning, config precedence, mount flags, and host-side validation guardrails
- **CI/CD Pipeline**: Automated testing on every change
- **Better Code Organization**: Modular, maintainable codebase

Expand Down
13 changes: 9 additions & 4 deletions TECHNICAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,19 @@ Location: `tests/integration_tests.sh`
7. Completion files
8. Configuration files
9. Config file loading
10. Config priority (3 locations)
10. Config priority
11. SSH mount dry-run wiring
12. Wrapper metadata rebuild checks
13. GitHub mount dry-run wiring
14. Container path validation (`CODEX_YOLO_HOME`, `CODEX_YOLO_WORKDIR`)
15. `--gh` prerequisite failures (missing `gh`, missing auth, missing `~/.copilot`)

**Running Tests**:
```bash
./tests/integration_tests.sh
```

Expected output: All tests pass (14/14)
Expected output: 25 total tests. On hosts without Docker, the Docker-dependent smoke tests are skipped while the host-side validation tests still run.

### Manual Testing Checklist

Expand Down Expand Up @@ -425,7 +430,7 @@ Container enables passwordless sudo:
- Improved code organization
- Integration test infrastructure

**Impact**: +1,800 lines of code, +186% files, 14 automated tests
**Impact**: +1,800 lines of code, +186% files, 25 automated tests

### v1.0.2 (Previous)

Expand All @@ -434,5 +439,5 @@ Basic functionality with auto-update and Docker containerization.
---

**Document Version**: 1.1.0
**Last Updated**: 2026-01-31
**Last Updated**: 2026-03-24
**Maintained By**: Development Team
237 changes: 237 additions & 0 deletions tests/integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,52 @@ log_info() {
echo -e " $*"
}

capture_command() {
local __output_var="$1"
local __status_var="$2"
shift 2

local captured_output
local captured_status
if captured_output=$("$@" 2>&1); then
captured_status=0
else
captured_status=$?
fi

printf -v "${__output_var}" '%s' "${captured_output}"
printf -v "${__status_var}" '%s' "${captured_status}"
}

create_fake_docker() {
local fake_bin="$1"

cat > "${fake_bin}/docker" <<'TESTEOF'
#!/bin/bash
case "${1:-}" in
info)
exit 0
;;
buildx)
if [[ "${2:-}" == "version" ]]; then
exit 0
fi
;;
image)
if [[ "${2:-}" == "inspect" ]]; then
exit 1
fi
;;
build|run)
exit 0
;;
esac
exit 0
TESTEOF

chmod +x "${fake_bin}/docker"
}

echo "=== codex_yolo Test Suite ==="
echo ""

Expand Down Expand Up @@ -384,6 +430,197 @@ else
log_skip "Docker not available, skipping --gh flag test"
fi

# Test 21: Reject relative CODEX_YOLO_HOME
log_test "Reject relative CODEX_YOLO_HOME"
fake_bin=$(mktemp -d)
original_path="${PATH}"

cleanup_test_21() {
rm -rf "${fake_bin}"
export PATH="${original_path}"
unset CODEX_SKIP_UPDATE_CHECK
unset CODEX_SKIP_VERSION_CHECK
unset CODEX_YOLO_HOME
}
trap cleanup_test_21 EXIT

create_fake_docker "${fake_bin}"
export PATH="${fake_bin}:${PATH}"
export CODEX_SKIP_UPDATE_CHECK=1
export CODEX_SKIP_VERSION_CHECK=1
export CODEX_YOLO_HOME="relative/home"

capture_command output status "${CODEX_YOLO_SH}"

cleanup_test_21
trap - EXIT

if [[ "${status}" -ne 0 ]] && echo "${output}" | grep -q "CODEX_YOLO_HOME must be an absolute path"; then
log_pass "Relative CODEX_YOLO_HOME is rejected before running Docker"
else
log_fail "Relative CODEX_YOLO_HOME was not rejected as expected"
log_info "Status: ${status}"
log_info "Output: ${output}"
fi

# Test 22: Reject relative CODEX_YOLO_WORKDIR
log_test "Reject relative CODEX_YOLO_WORKDIR"
fake_bin=$(mktemp -d)
original_path="${PATH}"

cleanup_test_22() {
rm -rf "${fake_bin}"
export PATH="${original_path}"
unset CODEX_SKIP_UPDATE_CHECK
unset CODEX_SKIP_VERSION_CHECK
unset CODEX_YOLO_WORKDIR
}
trap cleanup_test_22 EXIT

create_fake_docker "${fake_bin}"
export PATH="${fake_bin}:${PATH}"
export CODEX_SKIP_UPDATE_CHECK=1
export CODEX_SKIP_VERSION_CHECK=1
export CODEX_YOLO_WORKDIR="workspace"

capture_command output status "${CODEX_YOLO_SH}"

cleanup_test_22
trap - EXIT

if [[ "${status}" -ne 0 ]] && echo "${output}" | grep -q "CODEX_YOLO_WORKDIR must be an absolute path"; then
log_pass "Relative CODEX_YOLO_WORKDIR is rejected before running Docker"
else
log_fail "Relative CODEX_YOLO_WORKDIR was not rejected as expected"
log_info "Status: ${status}"
log_info "Output: ${output}"
fi

# Test 23: --gh requires host gh binary
log_test "--gh requires host gh binary"
fake_bin=$(mktemp -d)
original_path="${PATH}"
host_bash="$(command -v bash)"

cleanup_test_23() {
export PATH="${original_path}"
rm -rf "${fake_bin}"
unset CODEX_DRY_RUN
unset CODEX_SKIP_UPDATE_CHECK
unset CODEX_SKIP_VERSION_CHECK
}
trap cleanup_test_23 EXIT

create_fake_docker "${fake_bin}"
for required_tool in dirname id tr uname; do
ln -s "$(command -v "${required_tool}")" "${fake_bin}/${required_tool}"
done
export PATH="${fake_bin}"
export CODEX_DRY_RUN=1
export CODEX_SKIP_UPDATE_CHECK=1
export CODEX_SKIP_VERSION_CHECK=1

capture_command output status "${host_bash}" "${CODEX_YOLO_SH}" --gh

cleanup_test_23
trap - EXIT

if [[ "${status}" -ne 0 ]] && echo "${output}" | grep -q -- "--gh requires GitHub CLI (gh) installed on the host" && echo "${output}" | grep -q "gh auth login"; then
log_pass "--gh fails fast when gh is unavailable on the host"
else
log_fail "--gh did not report the missing host gh prerequisite"
log_info "Status: ${status}"
log_info "Output: ${output}"
fi

# Test 24: --gh requires authenticated host gh session
log_test "--gh requires authenticated host gh session"
fake_bin=$(mktemp -d)
original_path="${PATH}"

cleanup_test_24() {
rm -rf "${fake_bin}"
export PATH="${original_path}"
unset CODEX_DRY_RUN
unset CODEX_SKIP_UPDATE_CHECK
unset CODEX_SKIP_VERSION_CHECK
}
trap cleanup_test_24 EXIT

create_fake_docker "${fake_bin}"
cat > "${fake_bin}/gh" <<'TESTEOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "auth" ]] && [[ "${2:-}" == "status" ]]; then
exit 1
fi
exit 0
TESTEOF
chmod +x "${fake_bin}/gh"

export PATH="${fake_bin}:${PATH}"
export CODEX_DRY_RUN=1
export CODEX_SKIP_UPDATE_CHECK=1
export CODEX_SKIP_VERSION_CHECK=1

capture_command output status "${CODEX_YOLO_SH}" --gh

cleanup_test_24
trap - EXIT

if [[ "${status}" -ne 0 ]] && echo "${output}" | grep -q -- "--gh requires host GitHub authentication" && echo "${output}" | grep -q "gh auth login"; then
log_pass "--gh fails fast when host gh auth is missing"
else
log_fail "--gh did not report the missing host gh auth prerequisite"
log_info "Status: ${status}"
log_info "Output: ${output}"
fi

# Test 25: --gh requires host Copilot state directory
log_test "--gh requires host ~/.copilot state"
fake_bin=$(mktemp -d)
test_home=$(mktemp -d)
original_path="${PATH}"
original_home="${HOME}"

cleanup_test_25() {
rm -rf "${fake_bin}" "${test_home}"
export PATH="${original_path}"
export HOME="${original_home}"
unset CODEX_DRY_RUN
unset CODEX_SKIP_UPDATE_CHECK
unset CODEX_SKIP_VERSION_CHECK
}
trap cleanup_test_25 EXIT

create_fake_docker "${fake_bin}"
cat > "${fake_bin}/gh" <<'TESTEOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "auth" ]] && [[ "${2:-}" == "status" ]]; then
exit 0
fi
exit 0
TESTEOF
chmod +x "${fake_bin}/gh"

export PATH="${fake_bin}:${PATH}"
export HOME="${test_home}"
export CODEX_DRY_RUN=1
export CODEX_SKIP_UPDATE_CHECK=1
export CODEX_SKIP_VERSION_CHECK=1

capture_command output status "${CODEX_YOLO_SH}" --gh

cleanup_test_25
trap - EXIT

if [[ "${status}" -ne 0 ]] && echo "${output}" | grep -q -- "--gh enabled but .*\\.copilot does not exist or is not a directory" && echo "${output}" | grep -q "Ensure host Copilot data exists"; then
log_pass "--gh fails fast when host Copilot state is missing"
else
log_fail "--gh did not report the missing ~/.copilot prerequisite"
log_info "Status: ${status}"
log_info "Output: ${output}"
fi

# Summary
echo ""
echo "=== Test Summary ==="
Expand Down
Loading