name: Tests concurrency: group: bm-ci-${{ github.workflow }}-${{ github.repository }}-${{ github.head_ref || github.ref }} cancel-in-progress: true on: # Trigger: PR branch pushes already publish commit statuses that show up on the PR. # Why: running the full matrix on both push and pull_request doubles CI time for the # exact same branch head commit. # Outcome: each branch push runs the test suite once, including PR updates. push: env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" # Branch builds (PRs arrive as push events — this workflow has no # pull_request trigger) select only impacted tests from the cached testmon # baseline (branch cache falling back to main's full-run recording). Pushes # to main run the full suite with --testmon-noselect to refresh the baseline. BASIC_MEMORY_TESTMON_FLAGS: ${{ github.ref_name == 'main' && '--testmon-noselect' || '--testmon --testmon-forceselect' }} jobs: changes: # Docs/workflow-only changes skip the entire test matrix while the workflow # still concludes successfully, so the BM Bossbot gate (workflow_run on # Tests success) keeps firing and the PR stays mergeable. name: Detect code changes runs-on: ubuntu-latest outputs: code: ${{ steps.filter.outputs.code }} steps: - uses: actions/checkout@v6 - id: filter uses: dorny/paths-filter@v3 with: # Tests only runs on push events; for branch pushes compare against # main (merge-base), for main pushes dorny diffs the push range. base: main filters: | code: - 'src/**' - 'tests/**' - 'test-int/**' - 'alembic/**' - 'pyproject.toml' - 'uv.lock' - 'justfile' - '.github/workflows/test.yml' static-checks: needs: changes if: needs.changes.outputs.code == 'true' name: Static Checks (Python 3.12) timeout-minutes: 20 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: true - name: Set up Python 3.12 uses: actions/setup-python@v6 with: python-version: "3.12" cache: "pip" - name: Install uv run: | pip install uv - uses: extractions/setup-just@v4 - name: Create virtual env run: | uv venv - name: Install dependencies run: | uv pip install -e ".[dev]" - name: Run type checks run: | just typecheck - name: Run linting run: | just lint test-sqlite-unit: needs: changes if: needs.changes.outputs.code == 'true' name: Test SQLite Unit (${{ matrix.os }}, Python ${{ matrix.python-version }}) timeout-minutes: 45 strategy: fail-fast: false matrix: include: - os: ubuntu-latest python-version: "3.12" - os: ubuntu-latest python-version: "3.13" # Python 3.14 unit tests are the longest full-suite slice; keep this # one on GitHub-hosted runners after Depot terminated it mid-suite. - os: ubuntu-latest python-version: "3.14" - os: windows-latest python-version: "3.12" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 with: submodules: true - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install uv run: | pip install uv - uses: extractions/setup-just@v4 - name: Cache pytest-testmon results uses: actions/cache@v4 with: path: | .testmondata .testmondata-shm .testmondata-wal key: ${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}-${{ github.ref_name }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}-${{ github.ref_name }}- ${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}-main- ${{ runner.os }}-testmon-sqlite-unit-py${{ matrix.python-version }}- - name: Create virtual env run: | uv venv - name: Install dependencies run: | uv pip install -e ".[dev]" - name: Run tests run: | just test-unit-sqlite test-sqlite-integration: needs: changes if: needs.changes.outputs.code == 'true' name: Test SQLite Integration (${{ matrix.os }}, Python ${{ matrix.python-version }}) timeout-minutes: 45 strategy: fail-fast: false matrix: include: - os: ubuntu-latest python-version: "3.12" - os: ubuntu-latest python-version: "3.13" - os: ubuntu-latest python-version: "3.14" - os: windows-latest python-version: "3.12" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 with: submodules: true - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install uv run: | pip install uv - uses: extractions/setup-just@v4 - name: Cache pytest-testmon results uses: actions/cache@v4 with: path: | .testmondata .testmondata-shm .testmondata-wal key: ${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}-${{ github.ref_name }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}-${{ github.ref_name }}- ${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}-main- ${{ runner.os }}-testmon-sqlite-integration-py${{ matrix.python-version }}- - name: Create virtual env run: | uv venv - name: Install dependencies run: | uv pip install -e ".[dev]" - name: Run tests run: | just test-int-sqlite test-postgres-unit: needs: changes if: needs.changes.outputs.code == 'true' name: Test Postgres Unit (Python ${{ matrix.python-version }}, shard ${{ matrix.group }}/3) timeout-minutes: 60 strategy: fail-fast: false matrix: # Shard the largest suite across parallel jobs: each shard is a full job # with its own Postgres service running 1/3 of the collection. # Postgres runs on the latest Python only — the SQLite matrix carries # Python-version coverage; Postgres carries backend coverage. group: [1, 2, 3] python-version: ["3.14"] runs-on: ubuntu-latest services: postgres: image: pgvector/pgvector:pg16 env: POSTGRES_USER: basic_memory_user POSTGRES_PASSWORD: dev_password POSTGRES_DB: basic_memory_test ports: - 5432:5432 options: >- --health-cmd "pg_isready -U basic_memory_user -d basic_memory_test" --health-interval 10s --health-timeout 5s --health-retries 5 env: BASIC_MEMORY_TEST_POSTGRES_URL: postgresql://basic_memory_user:dev_password@127.0.0.1:5432/basic_memory_test steps: - uses: actions/checkout@v6 with: submodules: true - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install uv run: | pip install uv - uses: extractions/setup-just@v4 - name: Cache pytest-testmon results uses: actions/cache@v4 with: path: | .testmondata .testmondata-shm .testmondata-wal key: ${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-g${{ matrix.group }}-${{ github.ref_name }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-g${{ matrix.group }}-${{ github.ref_name }}- ${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-g${{ matrix.group }}-main- ${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}-main- ${{ runner.os }}-testmon-postgres-unit-py${{ matrix.python-version }}- - name: Create virtual env run: | uv venv - name: Install dependencies run: | uv pip install -e ".[dev]" - name: Run tests run: | BASIC_MEMORY_PYTEST_SPLIT_FLAGS="--splits 3 --group ${{ matrix.group }}" just test-unit-postgres test-postgres-integration: needs: changes if: needs.changes.outputs.code == 'true' name: Test Postgres Integration (Python ${{ matrix.python-version }}) timeout-minutes: 45 strategy: fail-fast: false matrix: # Latest Python only: SQLite carries version coverage, Postgres carries # backend coverage. python-version: ["3.14"] runs-on: ubuntu-latest services: postgres: image: pgvector/pgvector:pg16 env: POSTGRES_USER: basic_memory_user POSTGRES_PASSWORD: dev_password POSTGRES_DB: basic_memory_test ports: - 5432:5432 options: >- --health-cmd "pg_isready -U basic_memory_user -d basic_memory_test" --health-interval 10s --health-timeout 5s --health-retries 5 env: BASIC_MEMORY_TEST_POSTGRES_URL: postgresql://basic_memory_user:dev_password@127.0.0.1:5432/basic_memory_test steps: - uses: actions/checkout@v6 with: submodules: true - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install uv run: | pip install uv - uses: extractions/setup-just@v4 - name: Cache pytest-testmon results uses: actions/cache@v4 with: path: | .testmondata .testmondata-shm .testmondata-wal key: ${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}-${{ github.ref_name }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}-${{ github.ref_name }}- ${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}-main- ${{ runner.os }}-testmon-postgres-integration-py${{ matrix.python-version }}- - name: Create virtual env run: | uv venv - name: Install dependencies run: | uv pip install -e ".[dev]" - name: Run tests run: | just test-int-postgres test-semantic: needs: changes if: needs.changes.outputs.code == 'true' name: Test Semantic (Python 3.12) timeout-minutes: 45 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: true - name: Set up Python 3.12 uses: actions/setup-python@v6 with: python-version: "3.12" cache: "pip" - name: Install uv run: | pip install uv - uses: extractions/setup-just@v4 - name: Cache pytest-testmon results uses: actions/cache@v4 with: path: | .testmondata .testmondata-shm .testmondata-wal key: ${{ runner.os }}-testmon-semantic-py3.12-${{ github.ref_name }}-${{ github.run_id }} restore-keys: | ${{ runner.os }}-testmon-semantic-py3.12-${{ github.ref_name }}- ${{ runner.os }}-testmon-semantic-py3.12-main- ${{ runner.os }}-testmon-semantic-py3.12- - name: Create virtual env run: | uv venv - name: Install dependencies run: | uv pip install -e ".[dev]" - name: Run tests run: | just test-semantic