fix: e2e/integration test infrastructure and Makefile test targets
- Fix make test: was pointing to non-existent api/tests/, now runs unit tests correctly with --ignore=e2e --ignore=integration - Remove dead phase test targets (test-phase1..4, test-all-phases) that all referenced cd api && pytest tests/ (non-existent path) - Add .test_admin_pass file: reset_admin_password.py now writes a persistent test password file alongside .admin_initial_password; the API never deletes it (unlike .admin_initial_password which is consumed on first startup) - Update both integration/conftest.py and e2e/helpers/admin_password.py to read .test_admin_pass before .admin_initial_password — so tests work after make restart without needing PIC_ADMIN_PASS env var - Add AI collaboration rules to CLAUDE.md (auto-loaded every session) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -74,3 +74,14 @@ Config files for each service live under `config/<service>/`. Persistent data is
|
||||
## Testing
|
||||
|
||||
Tests live in `tests/` (28 files). Use mocking (`pytest-mock`) for external system calls. Integration tests in `test_integration.py` require Docker services running.
|
||||
|
||||
## AI Collaboration Rules (Claude Code)
|
||||
|
||||
These rules apply to every Claude Code session in this repo:
|
||||
|
||||
- **Read memory first** — load `/home/roof/.claude/projects/-home-roof/memory/MEMORY.md` and referenced files at session start.
|
||||
- **Dev machine context** — you are already on pic0 (192.168.31.51), the dev machine. Execute commands here directly; do not ask the user to run them.
|
||||
- **Use all available agents** — spawn specialized sub-agents (pic-remote, pic-qa, pic-architect, etc.) for tasks that match their description.
|
||||
- **make is the only interface** — never call docker/docker-compose directly. All container lifecycle operations go through `make start`, `make stop`, `make build`, `make logs`, etc.
|
||||
- **Test every new feature** — after implementing any change, run `make test` before considering the task done.
|
||||
- **Test before commit** — the pre-commit hook enforces this, but run `make test` manually first and fix all failures before staging files.
|
||||
|
||||
@@ -223,17 +223,18 @@ restore:
|
||||
# ── Tests ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
test:
|
||||
@echo "Running all tests..."
|
||||
pytest tests/ api/tests/
|
||||
@echo "Running unit tests..."
|
||||
python3 -m pytest tests/ --ignore=tests/e2e --ignore=tests/integration -q
|
||||
|
||||
test-all:
|
||||
python3 api/tests/run_tests.py
|
||||
test-all: test test-integration test-e2e-api test-e2e-ui
|
||||
@echo "All test suites complete."
|
||||
|
||||
test-unit:
|
||||
pytest tests/
|
||||
python3 -m pytest tests/ --ignore=tests/e2e --ignore=tests/integration -q
|
||||
|
||||
test-coverage:
|
||||
pytest tests/ api/tests/ --cov=api --cov-report=html --cov-report=term-missing --cov-fail-under=70 -v
|
||||
python3 -m pytest tests/ --ignore=tests/e2e --ignore=tests/integration \
|
||||
--cov=api --cov-report=html --cov-report=term-missing --cov-fail-under=70 -v
|
||||
|
||||
test-integration:
|
||||
@echo "Running full integration tests (requires running PIC stack)..."
|
||||
@@ -244,14 +245,15 @@ test-integration-readonly:
|
||||
PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/integration/test_live_api.py tests/integration/test_webui.py -v
|
||||
|
||||
test-api:
|
||||
cd api && python3 -m pytest tests/test_api_endpoints.py -v
|
||||
python3 -m pytest tests/test_api_endpoints.py -v
|
||||
|
||||
test-cli:
|
||||
cd api && python3 -m pytest tests/test_cli_tool.py -v
|
||||
python3 -m pytest tests/test_cli_tool.py -v
|
||||
|
||||
# ── E2E tests ─────────────────────────────────────────────────────────────────
|
||||
# Run `make test-e2e-deps` once to install dependencies, then use the other targets.
|
||||
# WG tests require wg-quick and run under sudo (passwordless sudo assumed on this host).
|
||||
# Admin password is read from data/api/.test_admin_pass (written by reset-test-admin-pass).
|
||||
# Override: make test-e2e-api PIC_ADMIN_PASS=mypassword
|
||||
|
||||
test-e2e-deps:
|
||||
sudo pip3 install --break-system-packages -r tests/e2e/requirements.txt
|
||||
@@ -282,20 +284,6 @@ show-admin-password:
|
||||
reset-admin-password:
|
||||
@sudo python3 scripts/reset_admin_password.py --generate
|
||||
|
||||
test-phase1:
|
||||
cd api && python3 -m pytest tests/test_network_manager.py tests/test_phase1_endpoints.py -v
|
||||
|
||||
test-phase2:
|
||||
cd api && python3 -m pytest tests/test_wireguard_manager.py tests/test_phase2_endpoints.py -v
|
||||
|
||||
test-phase3:
|
||||
cd api && python3 -m pytest tests/test_phase3_managers.py tests/test_phase3_endpoints.py -v
|
||||
|
||||
test-phase4:
|
||||
cd api && python3 -m pytest tests/test_phase4_routing.py tests/test_phase4_endpoints.py -v
|
||||
|
||||
test-all-phases:
|
||||
cd api && python3 -m pytest tests/ -v
|
||||
|
||||
# ── Network / peers ───────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'api'))
|
||||
|
||||
ROOT = os.path.join(os.path.dirname(__file__), '..')
|
||||
INIT_PW_FILE = os.path.normpath(os.path.join(ROOT, 'data', 'api', '.admin_initial_password'))
|
||||
TEST_PW_FILE = os.path.normpath(os.path.join(ROOT, 'data', 'api', '.test_admin_pass'))
|
||||
|
||||
|
||||
def _generate_password(length: int = 20) -> str:
|
||||
@@ -88,13 +89,19 @@ def main() -> None:
|
||||
|
||||
_set_password(password)
|
||||
|
||||
# Also update the initial password file so show-admin-password works
|
||||
# Write the initial password file (API reads it on first start, then deletes it)
|
||||
os.makedirs(os.path.dirname(INIT_PW_FILE), exist_ok=True)
|
||||
with open(INIT_PW_FILE, 'w') as f:
|
||||
f.write(password)
|
||||
|
||||
# Write the persistent test password file (never deleted by the API)
|
||||
with open(TEST_PW_FILE, 'w') as f:
|
||||
f.write(password)
|
||||
os.chmod(TEST_PW_FILE, 0o600)
|
||||
|
||||
_print_banner(password)
|
||||
print(f'\n Also saved to: {INIT_PW_FILE}')
|
||||
print(f' Test file: {TEST_PW_FILE} (persists across API restarts)')
|
||||
print(' Restart the API container for the change to take effect:')
|
||||
print(' docker restart cell-api')
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import os
|
||||
|
||||
|
||||
_DATA_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'data', 'api'))
|
||||
|
||||
|
||||
def resolve_admin_password() -> str:
|
||||
p = os.environ.get('PIC_ADMIN_PASS', '').strip()
|
||||
if p:
|
||||
return p
|
||||
candidate = os.path.normpath(
|
||||
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'data', 'api', '.admin_initial_password')
|
||||
)
|
||||
if os.path.exists(candidate):
|
||||
return open(candidate).read().strip()
|
||||
for fname in ('.test_admin_pass', '.admin_initial_password'):
|
||||
candidate = os.path.join(_DATA_DIR, fname)
|
||||
if os.path.exists(candidate):
|
||||
return open(candidate).read().strip()
|
||||
raise RuntimeError(
|
||||
"Admin password unknown. Set PIC_ADMIN_PASS env var or run: "
|
||||
"make reset-test-admin-pass PIC_TEST_ADMIN_PASS=<password>"
|
||||
|
||||
@@ -35,18 +35,19 @@ TEST_PEERS = (
|
||||
TEST_PEER_PASSWORD = 'IntegrationTest123!'
|
||||
|
||||
|
||||
_DATA_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'api'))
|
||||
|
||||
|
||||
def _resolve_admin_pass() -> str:
|
||||
if ADMIN_PASS:
|
||||
return ADMIN_PASS
|
||||
# Try reading from the initial password file (present on first run before bootstrap)
|
||||
candidate = os.path.join(
|
||||
os.path.dirname(__file__), '..', '..', 'data', 'api', '.admin_initial_password'
|
||||
)
|
||||
candidate = os.path.normpath(candidate)
|
||||
if os.path.exists(candidate):
|
||||
return open(candidate).read().strip()
|
||||
for fname in ('.test_admin_pass', '.admin_initial_password'):
|
||||
candidate = os.path.join(_DATA_DIR, fname)
|
||||
if os.path.exists(candidate):
|
||||
return open(candidate).read().strip()
|
||||
raise RuntimeError(
|
||||
"Admin password unknown. Set PIC_ADMIN_PASS env var or run make setup first."
|
||||
"Admin password unknown. Set PIC_ADMIN_PASS env var or run: "
|
||||
"make reset-test-admin-pass PIC_TEST_ADMIN_PASS=<password>"
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user