# ============================================================================
# dev-framework Makefile
# ============================================================================
# Entry point for running tests, linting, and validation.
#
# Usage:
#   make test          Run all tests (bats + pytest)
#   make test-bats     Run only bats (bash) tests
#   make test-shell    Run only the shell regression scripts (test/*.sh)
#   make test-python   Run only pytest (Python) tests
#   make lint          Run shellcheck + python syntax check
#   make smoke         Quick validation: syntax + compilation + core tests
#   make ci            Full CI pipeline (lint + test)
# ============================================================================

.PHONY: test test-bats test-shell test-python lint smoke ci check-deps

BATS := test/bats/bin/bats
BATS_FILES := $(wildcard test/*.bats)

# The shell regression root. Like BATS_FILES this is a wildcard, not a list: a
# script added to test/ must run without anyone editing this file. Named whole
# for the same reason the Python roots are -- see the test-python recipe.
#
# This root existed for months with nothing running it. test_framework_content.sh
# was failing on main against a heading that had been reorganised away, and no
# lane read the result. A root that is not named is a root that does not exist.
SHELL_TEST_FILES := $(wildcard test/*.sh)

# Both maintained Python test roots. Each runs as its own pytest process, in
# this order — see the test-python recipe for why.
PYTHON_TEST_ROOTS := tests scripts/tests

# Tests that need a network, a container runtime, or real credentials do not
# belong in a lane that must pass on every PR. They run in
# .github/workflows/external-compatibility.yml instead.
PR_SAFE_SELECTOR := not requires_network and not requires_container and not requires_credentials

# ── Prerequisites check ────────────────────────────────────────────────────

check-deps:
	@command -v python3 >/dev/null 2>&1 || { echo "ERROR: python3 required"; exit 1; }
	@command -v bash >/dev/null 2>&1 || { echo "ERROR: bash required"; exit 1; }
	@test -x $(BATS) || { echo "ERROR: bats-core not found. Run: git submodule update --init --recursive"; exit 1; }

# ── Test targets ───────────────────────────────────────────────────────────

test: check-deps test-bats test-shell test-python
	@echo ""
	@echo "All tests passed."

test-bats: check-deps
	@echo "Running bats tests..."
	@$(BATS) $(BATS_FILES)

# An empty root fails loudly. A `for` loop over an empty wildcard is a no-op that
# reports success, which is the same "absence is invisible" failure that let this
# root go unwired in the first place -- a green lane proving nothing.
test-shell: check-deps
	@echo "Running shell regression tests..."
	@test -n "$(SHELL_TEST_FILES)" || { echo "ERROR: no test/*.sh found -- the shell root is empty"; exit 1; }
	@for f in $(SHELL_TEST_FILES); do echo "  -> $$f"; bash "$$f" || exit 1; done

# Each root is named whole — never an allowlist, a glob, or a -k filter. A test
# file added to either directory must run without anyone editing this recipe.
#
# The two roots run as separate processes rather than one combined invocation:
# a combined run has produced order-dependent failures (conftest and sys.path
# state leaking between roots), and a lane that fails for reasons unrelated to
# the change under review stops being read.
#
# PYTHONPATH is not optional. An editable install elsewhere on the interpreter's
# path satisfies `import shipteam` from a different checkout entirely, and the
# suite then describes code that is not the code under test.
test-python: check-deps
	@echo "Running Python tests..."
	@PYTHONPATH="$$PWD/src" python3 -m pytest tests -m "$(PR_SAFE_SELECTOR)" --strict-markers -v --tb=short
	@PYTHONPATH="$$PWD/src" python3 -m pytest scripts/tests -m "$(PR_SAFE_SELECTOR)" --strict-markers -v --tb=short

# ── Lint targets ───────────────────────────────────────────────────────────

lint:
	@echo "Checking shell syntax..."
	@for f in scripts/*.sh; do bash -n "$$f" || exit 1; done
	@echo "  All shell scripts OK"
	@echo "Checking Python compilation..."
	@for f in .claude/hooks/*.py; do python3 -m py_compile "$$f" || exit 1; done
	@echo "  All Python hooks OK"
	@if command -v shellcheck >/dev/null 2>&1; then \
		echo "Running shellcheck..."; \
		shellcheck scripts/*.sh || true; \
	else \
		echo "  shellcheck not installed (skipping)"; \
	fi

# ── Smoke test (fast validation) ──────────────────────────────────────────

smoke: check-deps lint
	@echo "Running core tests..."
	@$(BATS) test/safety_gate.bats test/framework_config.bats test/template_instantiation.bats
	@echo ""
	@echo "Smoke test passed."

# ── CI target (full pipeline) ─────────────────────────────────────────────

ci: lint test
	@echo ""
	@echo "CI pipeline passed."
