From 249489cbd0e7546eacb81095d383fe80442efe86 Mon Sep 17 00:00:00 2001 From: Arnav Naval Date: Thu, 30 Apr 2026 23:34:26 -0500 Subject: [PATCH] [arnav] add backend pytest CI workflow and local test runner script --- .github/workflows/test.yml | 44 ++++++++++++++++++++++++++ scripts/test.sh | 64 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100755 scripts/test.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..df7f98de --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,44 @@ +name: Backend tests + +# Runs the backend pytest suite (mirrors `bash scripts/test.sh`) on every PR +# and every push to main. Tests are fully self-contained — they stub the +# Anthropic API and the Claude Code CLI — so no secrets or services are +# required. + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: tests-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + pytest: + name: pytest + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + cache: 'pip' + cache-dependency-path: | + backend/requirements.txt + backend/requirements-dev.txt + + - name: Install backend deps + run: | + python -m pip install --upgrade pip + python -m pip install \ + -r backend/requirements.txt \ + -r backend/requirements-dev.txt + + - name: Run pytest + run: python -m pytest backend/tests/ -v diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000..24124c64 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# Run the backend pytest suite. Creates backend/.venv if missing, installs +# requirements.txt + requirements-dev.txt the first time (or whenever +# pytest isn't importable), then invokes pytest. +# +# Usage: +# bash scripts/test.sh # run everything (~30s; 500 stress iters) +# bash scripts/test.sh --quick # cap DISCONNECT_STRESS_N at 20 for fast iteration +# bash scripts/test.sh -k disconnect # forward any pytest args +# bash scripts/test.sh backend/tests/test_analytics.py -v +# +# Env: +# DISCONNECT_STRESS_N Override the stress-iteration count for +# test_disconnect_resilience.py (default 500; +# --quick sets it to 20). + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" +BACKEND_DIR="$PROJECT_ROOT/backend" +VENV_DIR="$BACKEND_DIR/.venv" +PYTHON_BIN="$VENV_DIR/bin/python" + +# --- Parse our one wrapper flag, then forward the rest to pytest ---------- +QUICK=0 +PYTEST_ARGS=() +for arg in "$@"; do + case "$arg" in + --quick) QUICK=1 ;; + *) PYTEST_ARGS+=("$arg") ;; + esac +done + +# --- Ensure venv exists --------------------------------------------------- +if [[ ! -x "$PYTHON_BIN" ]]; then + echo "==> Creating venv at $VENV_DIR" + python3 -m venv "$VENV_DIR" +fi + +# --- Ensure pytest is installed (idempotent: only re-installs if missing) - +if ! "$PYTHON_BIN" -c "import pytest, pytest_asyncio" >/dev/null 2>&1; then + echo "==> Installing backend deps + dev deps into $VENV_DIR" + "$PYTHON_BIN" -m pip install --upgrade pip >/dev/null + "$PYTHON_BIN" -m pip install \ + -r "$BACKEND_DIR/requirements.txt" \ + -r "$BACKEND_DIR/requirements-dev.txt" +fi + +# --- Default target = backend/tests/ if caller didn't pass any path ------- +if [[ ${#PYTEST_ARGS[@]} -eq 0 ]]; then + PYTEST_ARGS=(backend/tests/ -v) +fi + +# --- --quick: dial the disconnect stress loop way down -------------------- +if [[ "$QUICK" -eq 1 ]]; then + export DISCONNECT_STRESS_N="${DISCONNECT_STRESS_N:-20}" + echo "==> --quick: DISCONNECT_STRESS_N=$DISCONNECT_STRESS_N" +fi + +# Run from project root so `from backend.apps...` imports resolve. +cd "$PROJECT_ROOT" +echo "==> Running: pytest ${PYTEST_ARGS[*]}" +exec "$PYTHON_BIN" -m pytest "${PYTEST_ARGS[@]}"