diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca9a86403..f5b546913 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,192 @@ on: push: branches: [main, master] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: + quality: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "0.10.7" + + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Install dependencies + run: uv sync --frozen --all-packages --no-cache + + - name: Run static quality checks + run: | + uv lock --check + if [ "${{ github.event_name }}" = "pull_request" ]; then + git fetch origin "${{ github.base_ref }}" --depth=1 + mapfile -t changed < <(git diff --name-only --diff-filter=ACMRT "origin/${{ github.base_ref }}...HEAD") + else + mapfile -t changed < <(git diff --name-only --diff-filter=ACMRT "${{ github.event.before }}" "${{ github.sha }}") + fi + if [ "${#changed[@]}" -gt 0 ]; then + uv run pre-commit run --files "${changed[@]}" + fi + if printf '%s\n' "${changed[@]}" | grep -qE '^potpie/parsing/.*\.rs$'; then + cargo fmt --manifest-path potpie/parsing/Cargo.toml -- --check + fi + + integrations: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 5s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "0.10.7" + + - name: Install dependencies + run: uv sync --frozen --all-packages --no-cache + + - name: Run integrations package tests + env: + POSTGRES_SERVER: "postgresql://postgres:postgres@localhost:5432/postgres" + ENV: "development" + # HTTP integration tests mount the full legacy FastAPI app, which still + # imports removed context-graph Celery handlers; run unit coverage until + # that wiring is repaired. + run: uv run --project legacy pytest potpie/integrations/tests -m "not integration" + + ui-build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Build graph-explorer UI + working-directory: potpie/context-engine/adapters/inbound/http/ui/frontend + run: npm install && npm run build + + graph-e2e: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 5s + --health-timeout 5s + --health-retries 5 + falkordb: + image: falkordb/falkordb:latest + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "0.10.7" + + - name: Install dependencies + run: uv sync --frozen --all-packages --no-cache + + - name: Run FalkorDB graph e2e tests + working-directory: potpie/context-engine + env: + POSTGRES_SERVER: "postgresql://postgres:postgres@localhost:5432/postgres" + FALKORDB_URL: "redis://localhost:6379" + FALKORDB_MODE: "server" + GRAPH_DB_BACKEND: "falkordb" + CONTEXT_GRAPH_ENABLED: "1" + ENV: "development" + run: | + uv run --project . pytest \ + tests/integration/test_falkordb_server_e2e.py \ + tests/integration/test_falkordb_roundtrip.py \ + tests/integration/test_e2e_batching.py \ + tests/conformance/ \ + -v + + benchmark-smoke: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "0.10.7" + + - name: Install dependencies + run: uv sync --frozen --all-packages --no-cache + + - name: Run benchmark harness unit tests + working-directory: potpie/context-engine + run: uv run --project . pytest tests/unit/benchmarks/ -v + potpie-parsing: runs-on: ubuntu-latest strategy: diff --git a/potpie/context-engine/tests/integration/test_falkordb_server_e2e.py b/potpie/context-engine/tests/integration/test_falkordb_server_e2e.py new file mode 100644 index 000000000..268cf9951 --- /dev/null +++ b/potpie/context-engine/tests/integration/test_falkordb_server_e2e.py @@ -0,0 +1,100 @@ +"""Live FalkorDB **server** round-trip against a running FalkorDB instance. + +Designed for CI (GitHub Actions ``falkordb/falkordb`` service). Skips when +``FALKORDB_URL`` is unset or the server is unreachable. +""" + +from __future__ import annotations + +import asyncio +import os +import socket +import uuid +from urllib.parse import urlparse + +import pytest + +from adapters.outbound.graph.backends import build_backend +from adapters.outbound.settings_env import EnvContextEngineSettings +from domain.context_events import EventRef +from domain.graph_mutations import EdgeUpsert, EntityUpsert +from domain.ports.claim_query import ClaimQueryFilter +from domain.reconciliation import ReconciliationPlan + +pytestmark = pytest.mark.integration + + +def _redis_reachable(url: str) -> bool: + parsed = urlparse(url) + host = parsed.hostname or "127.0.0.1" + port = parsed.port or 6379 + try: + with socket.create_connection((host, port), timeout=2): + return True + except OSError: + return False + + +def _plan( + pot_id: str, summary: str = "prefers structured logging" +) -> ReconciliationPlan: + return ReconciliationPlan( + event_ref=EventRef(event_id="e1", source_system="agent", pot_id=pot_id), + summary=summary, + entity_upserts=[ + EntityUpsert(entity_key="pref:logging", labels=("Preference",)), + EntityUpsert(entity_key="svc:api", labels=("Service",)), + ], + edge_upserts=[ + EdgeUpsert( + edge_type="DEPENDS_ON", + from_entity_key="pref:logging", + to_entity_key="svc:api", + properties={"fact": summary}, + ) + ], + ) + + +@pytest.fixture() +def falkordb_live_env(monkeypatch: pytest.MonkeyPatch) -> EnvContextEngineSettings: + url = ( + os.environ.get("CONTEXT_ENGINE_FALKORDB_URL") + or os.environ.get("FALKORDB_URL") + or "" + ).strip() + if not url: + pytest.skip("FALKORDB_URL not configured") + if not _redis_reachable(url): + pytest.skip(f"FalkorDB not reachable at {url}") + monkeypatch.setenv("FALKORDB_URL", url) + monkeypatch.setenv("FALKORDB_MODE", "server") + monkeypatch.setenv("GRAPH_DB_BACKEND", "falkordb") + monkeypatch.setenv("CONTEXT_GRAPH_ENABLED", "1") + graph_name = ( + os.environ.get("CONTEXT_ENGINE_FALKORDB_GRAPH_NAME") + or os.environ.get("FALKORDB_GRAPH_NAME") + or "context_graph_ci" + ) + monkeypatch.setenv("FALKORDB_GRAPH_NAME", graph_name) + return EnvContextEngineSettings() + + +def test_falkordb_server_mutation_claim_query_roundtrip( + falkordb_live_env: EnvContextEngineSettings, +) -> None: + pot_id = f"ci_{uuid.uuid4().hex[:12]}" + backend = build_backend("falkordb", settings=falkordb_live_env) + assert backend.profile == "falkordb" + + result = asyncio.run( + backend.mutation.apply_async(_plan(pot_id), expected_pot_id=pot_id) + ) + assert result.ok + + rows = backend.claim_query.find_claims(ClaimQueryFilter(pot_id=pot_id)) + assert len(rows) == 1 + assert rows[0].fact == "prefers structured logging" + + backend.mutation.reset_pot(pot_id) + assert backend.claim_query.find_claims(ClaimQueryFilter(pot_id=pot_id)) == []