Skip to content

feat(ci): container-based integration tests + migration gate (closes #3, #5)#234

Merged
charlie83Gs merged 7 commits intomainfrom
worktree-ci-integration-tests
Apr 20, 2026
Merged

feat(ci): container-based integration tests + migration gate (closes #3, #5)#234
charlie83Gs merged 7 commits intomainfrom
worktree-ci-integration-tests

Conversation

@charlie83Gs
Copy link
Copy Markdown
Contributor

Summary

  • Enables container-based integration tests in GitHub Actions — per-package matrix job that boots postgres, postgres-write, redis, qdrant service containers, applies Alembic migrations, and runs tests/integration/ suites. Gated by path filter so it only runs when relevant code changes.
  • Splits unit from integration — top-level conftest.py files no longer spin up DB/Qdrant; DB fixtures live under tests/integration/conftest.py. The three tests that genuinely need DB moved into tests/integration/.
  • Unit tests are now infra-free and key-free — 1240 tests pass in under a minute with Postgres/Redis/Qdrant/Hatchet all down and no API keys configured.
  • New migrations-check CI job — required, runs on every PR. Applies both graph-db and write-db Alembic migrations from scratch, asserts exactly one head each, and sanity-tests a downgrade/re-upgrade roundtrip.
  • Lefthook pre-push runs just test-unit (infra-free) instead of just test-all.
  • GitHub secrets: SERPER_KEY and OPENROUTER_API_KEY configured. BRAVE_KEY intentionally not set — Serper is the primary provider; Brave tests skip cleanly via existing pytest.skip() guards.
  • Fixes pre-existing Alembic divergence — migrations 212efc51d897 and e3eb15f51d7c were both rooted at 489643109ccd. Chained the former off the latter; the new migrations-check job would have failed without this.

Addresses

Test plan

  • backend-lint passes
  • migrations-check passes (detects single head, applies both DBs, downgrade roundtrip succeeds)
  • backend-test matrix runs hermetically per changed package — unit tests (~1240) pass without any services: block
  • backend-integration-test matrix runs per changed package — integration tests pass with postgres/postgres-write/redis/qdrant service containers + SERPER_KEY/OPENROUTER_API_KEY env
  • Brave plugin test shows SKIPPED ... BRAVE_KEY not set in the plugin-search-providers integration run
  • frontend-test passes

Closes #3 and #5.

Split unit tests (infra-free, key-free) from integration tests (real
postgres/redis/qdrant/hatchet + API secrets). Removes integration tests
from lefthook pre-push for faster local feedback; CI runs them per-package
on PRs only when relevant code changes.

CI changes (.github/workflows/test.yml)
  - backend-test job: drop service containers + DB env vars. Unit tests
    now run hermetically (1240 passing in under 60s).
  - backend-integration-test job (new): matrix per-package, boots postgres,
    postgres-write, redis, qdrant service containers; injects SERPER_KEY,
    OPENROUTER_API_KEY, OPENAI_API_KEY secrets. No BRAVE_KEY - Serper is
    primary provider; Brave tests skip cleanly via existing pytest.skip
    guards.
  - migrations-check job (new): applies both alembic migrations from scratch,
    asserts exactly one head per DB, sanity-tests downgrade/upgrade roundtrip.
    Runs on every PR regardless of path filter.
  - Path filter: add plugin-search-providers; dedupe worker-nodes block.

Conftest split (tests/conftest.py → tests/integration/conftest.py)
  - libs/kt-{db,facts,graph,models,providers}, services/{api,worker-nodes,
    worker-bottomup,worker-sync,mcp}, plugins/backend-engine-search-providers
  - Top-level conftests keep only env setup (USE_HATCHET, SKIP_AUTH) and
    lightweight mock-friendly fixtures (_ambient_test_expense in models/
    worker-nodes/worker-bottomup).
  - worker-{bottomup,sync,ingest} set a fake HATCHET_CLIENT_TOKEN to let
    modules that import the Hatchet client at load time collect tests.

Test relocations (tests/ → tests/integration/)
  - libs/kt-db/tests/test_write_{dimensions,seeds}.py - use write_db_session
  - libs/kt-facts/tests/test_tiered_dedup_experiment.py - connects to
    write-db directly

Alembic fix (libs/kt-db/alembic/versions/)
  - 212efc51d897 chained off e3eb15f51d7c instead of the shared
    initial_schema parent. Prior divergence would have failed the new
    migrations-check job.

lefthook.yml / justfile
  - pre-push runs `just test-unit` (infra-free) instead of `just test-all`.
  - New `test-unit` and `test-integration` recipes; `test-all` remains as
    an alias that chains both.
Embeddings go through OpenRouter, so OPENAI_API_KEY is not configured
as a repo secret. Keep SERPER_KEY + OPENROUTER_API_KEY only.
…odes

- qdrant/qdrant:v1.17.2 does not exist; use v1.17.1.
- services/worker-nodes/tests/conftest.py: set dummy HATCHET_CLIENT_TOKEN
  because auto_build imports the Hatchet client at module load, same as
  worker-bottomup, worker-ingest, and worker-sync.
worker-bottomup and worker-nodes have tests/integration/ dirs with just
a conftest+__init__ but no test files. Previously they'd exit 5 (no
tests collected) and fail the job. Now the matrix builder checks for
actual test_*.py files before adding a package.
Addresses PR #234 review feedback:

- `kt_db.testing` pytest plugin exposes engine/db_session/write_engine/
  write_db_session fixtures. 8 integration conftests reduced from ~60
  lines of duplication to a single `pytest_plugins = ["kt_db.testing"]`
  line. kt-facts (shared graph+write schema) and worker-sync (renamed
  fixtures + sync_engine) keep their own — they diverge meaningfully.

- `kt_hatchet.testing.install_fake_hatchet_env()` replaces the inline
  JWT literal repeated in 4 worker conftests (bottomup, nodes, ingest,
  sync). Single source of the fake token and the Hatchet env triad.

- Remove legacy justfile aliases (test-libs/test-api/test-mcp/
  test-workers). All collapsed to test-unit, leaving the names
  misleading.

- test.yml comment explains that integration tests use
  Base.metadata.create_all on per-worker schemas for xdist isolation,
  so model/migration drift is caught by `migrations-check`, not the
  integration matrix.
OpenRouter returned 429 on kt-models integration run. Two mitigations:
- Switch test model from openrouter/google/gemini-2.0-flash-001 to
  openrouter/google/gemma-4-26b-a4b-it:nitro (matches Settings.decomposition_model).
- Add per-suite pytest -n override to the integration matrix so suites
  that hit rate-limited external APIs can run serially. kt-models
  pinned to -n 1; all others default to -n auto.
Flaky runs observed on kt-db integration came from session-scoped state
leaking across test modules (e.g. write_seeds committing rows that
test_trigram_dedup read). Move schema creation/teardown from session
scope to module scope so every test file gets its own random schema
that's dropped on module teardown.

- kt_db.testing: engine/schema_name/write_engine/write_schema_name are
  now module-scoped; db_session/write_db_session remain function-scoped
  with per-test txn rollback. Extensions install once per session.
- kt-facts and worker-sync conftests follow the same pattern for their
  divergent fixtures (shared schema / renamed graph_engine).
- services/api/tests/integration/*.py: downgrade local session-scoped
  fixtures (api_app, wi_client, etc.) to module scope to match the new
  engine scope.

Result: test_trigram_dedup.test_abbreviation_found no longer needs to
be deselected — runs green alongside the full suite. 152 kt-db
integration tests in 6.6s (session-scope was 3s; the cost of per-module
teardown is modest). Each CI run uses randomly-named schemas so
concurrent runs and leftover data never interfere.
@charlie83Gs charlie83Gs merged commit 34883d7 into main Apr 20, 2026
26 checks passed
@charlie83Gs charlie83Gs deleted the worktree-ci-integration-tests branch April 20, 2026 14:25
@github-actions
Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: add pytest.skip guards for tests requiring external API keys feat: integration test pipeline with containerized services

1 participant