Codync
免费开源的 1:1 Grok Bot 与 Muse 替代方案。可在 iPhone、Mac 或 Linux 桌面上将 Claude 代码、Codex、Cursor 以及 40 多个编码代理作为持久机器人使用。支持群组聊天、线程、应用。
AI 翻译
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Live demo: test-case-management-rabinavidans-projects.vercel.app · Repo: rabinavidan/test-case-management
A full-stack test case management platform built with FastAPI microservices, Vanilla JS, PostgreSQL, and Redis. Designed to demonstrate cutting-edge engineering practices — microservice decomposition, event-driven async, real-time WebSocket collaboration, and AI-powered test generation, failure triage, and flaky-test detection (via Anthropic's Claude Haiku). Backed by five independent, feature-equivalent test automation stacks — Python/pytest, TypeScript/Playwright, Java/REST Assured, Java/Playwright, and Cucumber/Gherkin BDD — see Test Architecture below.
Note for reviewers: two of the five test suites are written in Java —
java-tests/(JUnit 5 + REST Assured, black-box API tests) andjava-e2e/(JUnit 5 + Playwright Java, browser E2E). Both run standalone with Maven (mvn test) and are feature-equivalent to the Python and TypeScript suites. A fifth,e2e-bdd/(Cucumber.js + Playwright), covers the same core flows as Gherkin feature files, with a companion Azure DevOps pipeline (azure-pipelines.yml) alongside the GitHub Actions workflows — see Test Architecture.
Beyond LLM-powered product features, this repo is a working example of agentic AI running in production CI/CD — three independent AI agents, deliberately split across two model providers by cost/capability trade-off, each with its own failure-handling and test coverage — plus a fourth, authoring-time agent set that writes and self-heals the test suite itself, and deterministic guardrails around all of them (flaky-test detection, CI failure triage that names whose bug a red test is, and an assertion guard):
| Agent | What it does | Model | Where |
|---|---|---|---|
| PR Steward | Runs autonomously on every PR event (open, review, @claude comment) — diagnoses CI failures, pushes fixes, resolves review threads, following repo-specific conventions defined in its own skill file. Opt-in: skipped unless the ENABLE_CLAUDE_STEWARD repository variable is true, so it never spends API tokens by default | Claude (Anthropic) | .github/workflows/claude-pr-steward.yml · .claude/skills/steward/SKILL.md |
| Coverage-Gap Agent | Diffs a PR's changed source files against its test files; for any gap, prompts an LLM for concrete, specific test-case suggestions and posts them as a PR comment | Gemini (free tier) | scripts/coverage_gap_agent.py |
| Flaky-Test Detector | Parses CI rerun results to distinguish "needed a retry" from a real failure, and maintains a single tracking GitHub issue across runs | Deterministic (no LLM) | scripts/flake_report.py |
| CI Failure Triage | Answers "whose bug is this red test?" for the repo's own CI: labels every failing pytest and Playwright test as product (→ Developers), test-code (→ Automation / QA) or infra (→ DevOps), or unknown (→ human), from the error signature, the phase it failed in (fixture setup vs test body) and whether the exception came from a test file or product code. The table lands in the job summary of test.yml and pw-ts.yml and never gates the build. A companion Claude Code skill tells agents (the PR Steward included) how to confirm each verdict and what's off-limits per category: never loosen an assertion over a product bug, never change product code for a broken test, at most one rerun for infra |
Provider gateway. AI Test Generation and AI Failure Triage both call through one router
(api/ai_gateway.py) instead of each hand-rolling its own client — which provider/model
actually runs (Anthropic, Ollama, or Groq) is an AI_PROVIDER/AI_MODEL environment variable, not a code change.
Every call, success or failure, is logged (model, provider, tokens in/out, latency, outcome) for the KPI
dashboard's AI Call Latency/Cost cards, scored by scripts/ai_call_metrics.py.
Deliberately monolith-only this milestone — the microservices services/ai/ variant, the Coverage-Gap Agent's
Gemini client, and the Test Plan Reviewer's Ollama client keep their own, for now.
Why this matters more than "calls an LLM API":
failure-triage skill, which turns that verdict into what they may and may not change. The
model's judgment is spent on the unknown cases and on confirming the evidence, not on a first guess.playwright-test MCP server, root
.mcp.json), never unattended in a workflow; everything they produce is an ordinary *.spec.ts
file that still goes through human review and the existing pw-ts.yml pipeline like any other change.| Building block | Implemented in | Proof it works |
|---|---|---|
| LLMs | Provider gateway (api/ai_gateway.py) — Anthropic / Ollama / Groq by env var, per-call token + latency telemetry | tests/unit/test_ai_gateway.py, tests/api/test_ai_generate.py |
| RAG | Retrieval-grounded test generation (api/retrieval.py) and the triage agent's get_similar_test_cases tool, over a pluggable embedding model (api/embeddings.py: hashed vector · Ollama nomic-embed-text · Voyage). Each vector records the model that made it; a provider switch re-embeds stale rows on read, and a provider outage falls back to the hash vector for the whole request — never mixing models | tests/api/test_retrieval.py; retrieval eval (evals/retrieval_eval.py) — nomic-embed-text R@1 1.00 vs 0.56 for the hashed vector on 18 paraphrased queries |
| Tool use | Agentic Failure Triage (api/triage_agent.py) over a provider-pluggable tool loop (complete_with_tools(): Anthropic · Ollama) · Playwright MCP agents (.claude/agents/) · PR Steward | tests/api/test_triage_agentic.py — scripted multi-turn tool loop, iteration cap, out-of-scope arguments rejected; M10 trajectory eval — qwen2.5:3b tool-selection accuracy 0.93 vs 0.00 for 1.5B |
| Evaluations | evals/ — N-runs-per-case quality + variance, LLM-as-judge, baselines with regression thresholds, prompt versioning, CI job; a labelled triage-verdict eval (evals/triage_verdict_eval.py) that separates abstaining from confidently wrong; a trajectory eval (evals/triage_trajectory_eval.py) that scores which tools the agent called before deciding, gated in CI against a committed baseline | tests/unit/test_evals_*.py, tests/unit/test_triage_verdicts.py, tests/unit/test_triage_trajectory_eval.py (a lazy agent fails the gate), .github/workflows/eval-harness.yml |
| Agent skills | Repo-specific procedures Claude Code agents load on demand: steward (CI/review conventions, what counts as a small fix) and failure-triage (product vs test-code vs infra, and what each category allows), in ; agent definitions for the Playwright trio in |
Reliability guards (failure modes AI-in-QA agents actually hit, see docs/ai-roadmap.md):
| Failure mode | Guard | Proof |
|---|---|---|
| Agent can't tell a product bug from a flaky test or a broken environment | Triage records a structured verdict per failing case (product_bug · flaky · environment · unknown) through a record_verdict tool, after checking get_run_environment_status (env health + the same case on other environments). A deterministic heuristic cross-checks it; disagreement → Needs human review | tests/api/test_triage_agentic.py; evals/triage_verdict_eval.py on 16 labelled cases: precision 1.00, 0 confident errors, 4 honest abstentions |
| A red CI test gets "fixed" by the wrong owner — an assertion loosened to hide a product bug, product code bent around a broken locator, a real failure rerun until green | scripts/failure_classifier.py labels every CI failure product / test-code / infra / unknown with its owner in the job summary; the failure-triage skill makes agents confirm the verdict and forbids the wrong-owner fix per category | tests/unit/test_failure_classifier.py — 23 cases on real pytest and Playwright report shapes, incl. a stack line app.ts:503:7 not misread as an HTTP 503 |
| A self-heal fixes the selector but changes what the test verifies | Assertion Guard CI (scripts/assertion_guard.py) blocks a PR that removes/comments out an expect(), adds skip/fixme, loosens (toBeTruthy…) or inverts (.not) a matcher; changed expected values are listed for a human. Override only by a human: assertion-change-approved label or in-file reason | tests/unit/test_assertion_guard.py; back-tested on the repo's spec history — 1 flag in 19 spec-changing commits, a genuine assertion inversion |
Roadmap: docs/ai-roadmap.md tracks the next milestones that close the remaining gaps
honestly — M7 agentic tool-use triage ✅, M8 a real embedding model behind RAG ✅, M10 trajectory evals that score
which tools an agent chose to call ✅, M9 in-app human review of AI drafts feeding the eval dataset ✅.
See docs/interview-prep/agentic-ai-test-engineering.md
for the deeper case study behind the Eval Harness and Test Plan Reviewer — what they're for, the real failures
and dependency conflicts hit building them, and the honest gaps not to oversell.
App in action:
The public, unauthenticated view (visiting the app with no session) is built for a recruiter or hiring manager evaluating this repo, not only as a test-management product demo. Everything below is generated directly from this repository's own code, tests, and CI — not a static marketing page.
pytest --collect-only,
playwright test --list, @Test counts), never invented numbers.services/README.md and k8s/README.md, including a
real concurrency trade-off found through load-testing rather than assumed.Demo buttons never create data for an unauthenticated visitor: they navigate to the existing flagship demo project (or a curated welcome state if none exists yet) instead of seeding a new record on every visit.
Screenshots of this guest-facing view aren't included yet. The gallery above predates this redesign and still reflects the signed-in product views; capturing accurate new screenshots needs a real browser session with the Tailwind CDN reachable, which this repo's own dev/CI environments have.
?page=&page_size=&search=)| Feature | Tech | Endpoint / File |
|---|---|---|
| AI Test Generation | Anthropic Claude Haiku (claude-haiku-4-5-20251001) | POST /api/suites/{id}/testcases/generate |
| AI Failure Triage | Claude Haiku summarizes a run's failed/skipped results into a plain-English root-cause guess | POST /api/runs/{id}/triage |
| Flaky Test Detection | Flags test cases whose pass/fail results flip-flop across runs (no AI needed — deterministic pattern matching) | GET /api/suites/{id}/flaky-tests |
| CSV Export | One row per test case with its most recent run status across the suite; UTF-8 BOM for non-ASCII titles and a formula-injection guard for values opened in Excel/Sheets | GET /api/suites/{id}/export/csv |
| Real-time Collaboration | WebSocket + Redis Pub/Sub | WS /ws/runs/{run_id} |
| Analytics Dashboard | Chart.js 4 (pass-rate trend line, suite coverage bars) | GET /api/projects/{id}/analytics |
| Microservice Architecture | 6 services · Docker Compose · Redis + Kafka events | services/ + docker-compose.microservices.yml |
| Structured Logging | Middleware logging every HTTP request with status and latency_ms; microservice mode also threads a correlation request_id across every service | api/main.py · services/common/request_id.py |
| Paginated API | Envelope {items, total, page, page_size, total_pages} | GET /api/projects |
| Environments | Four-stage deployment pipeline (staging → regression → preprod → prod), each pinned to its own Kubernetes node — see k8s/ for the kustomize manifests and the in-app dashboard for live (simulated) node/pod health. Runs can be tagged with the environment they executed against. (monolith only) |
Two deployment modes are supported. The public URL surface (/api/*, /ws/*) is identical in both.
Browser (Vanilla JS SPA)
│ HTTP / REST + WebSocket
▼
┌─────────────────────────────────────────────────────────┐
│ Gateway :8000 (httpx proxy · static file serving) │
└──┬──────────┬──────────┬──────────────────────────┬─────┘
│ │ │ │
▼ ▼ ▼ ▼
Auth:8001 Projects:8002 Runs:8003 AI:8004
JWT login CRUD + stats Runs + results Claude Haiku
users analytics WebSocket test generation
demo seed Redis pub/sub
│ │
└──────┬───────┘
▼
PostgreSQL 16 (one shared instance, table-prefixed per service)
├─ auth_users
├─ projects_projects, projects_test_suites, projects_test_cases
└─ runs_test_runs, runs_test_results
Redis 7
└─ channel: runs.completed (async event pub/sub)
Kafka 3.7 (single-node KRaft, no ZooKeeper) ──► Worker
├─ topic: alert.triggered — runs publishes on every failed result
└─ topic: alerts.triggered.dlq — malformed, or retries-exhausted, messages
Worker persists valid alerts into
runs_alerts, deduped per (run_id, testcase_id)
Key design decisions:
role claim — non-auth services verify tokens locally (no auth round-trip per request)run.completed events; degrades gracefully if Redis is downalert.triggered events on test failure; both the runs producer and the worker consumer
no-op (never fail the request) if the broker is unreachable — the same graceful-degradation shape as Redis/health/live never checks a dependency (a DB outage must not
restart every replica at once), /health/ready checks the service's database and returns 503 so
Kubernetes pulls the pod out of rotation instead. The port-less worker uses a heartbeat-file exec
probe — see services/README.mdBrowser (SPA)
│ HTTP / REST + WebSocket
▼
FastAPI (api/main.py) — single process
├─ JWT auth middleware
├─ ConnectionManager (WebSocket broadcast)
├─ /api/suites/{id}/testcases/generate ──► Anthropic Claude Haiku API
└─ SQLAlchemy ORM
├─ PostgreSQL (Neon · production)
└─ SQLite (/tmp · local dev / Vercel)
cp .env.example .env # set JWT_SECRET_KEY and ANTHROPIC_API_KEY
docker compose -f docker-compose.microservices.yml up --build
# open http://localhost:8000
pip install -r requirements.txt
uvicorn api.main:app --reload
# open http://localhost:8000
cp .env.example .env
docker compose up --build
# open http://localhost:8000
| Variable | Default | Description |
|---|---|---|
DATABASE_URL | SQLite /tmp/testflow.db | Postgres URL for production |
JWT_SECRET_KEY | change-me-in-production | HS256 signing secret |
ANTHROPIC_API_KEY | (empty) | Required for AI test generation |
EMBEDDING_PROVIDER | hash | Retrieval embeddings: hash (no dependencies), ollama (local model), or voyage (hosted) |
EMBEDDING_MODEL | provider default | nomic-embed-text for Ollama, voyage-3-lite for Voyage |
VOYAGE_API_KEY | (empty) | Required when EMBEDDING_PROVIDER=voyage |
REDIS_URL | redis://localhost:6379 | Used by Runs service (microservice mode) |
All endpoints are identical regardless of deployment mode (monolith or microservices),
except /api/environments, which is monolith-only for now.
POST /api/auth/register
POST /api/auth/login
GET /api/auth/me
GET /api/environments # Staging/regression/preprod/prod health (monolith only)
POST /api/contact # Contact Us form (public) — saves + emails the site owner
GET /api/logs # Log Center (admin only) — filter by level/source, search
POST /api/logs/client # Reports a browser-side error (public, no auth)
GET /api/projects?page=1&page_size=50&search=
POST /api/projects
DELETE /api/projects/{id}
GET /api/projects/{id}/stats
GET /api/projects/{id}/analytics
GET /api/projects/{id}/suites
POST /api/projects/{id}/suites
GET /api/suites/{id}/testcases
POST /api/suites/{id}/testcases
POST /api/suites/{id}/testcases/generate # AI generation (→ AI service)
POST /api/suites/{id}/testcases/generate/save # Bulk save AI results
POST /api/suites/{id}/runs # Body: {name, environment_key?} — tags the run with its environment
GET /api/suites/{id}/runs
GET /api/runs/{id}
PUT /api/runs/{id}/results/{testcase_id}
POST /api/runs/{id}/triage # AI failure triage — plain-English root-cause guess
POST /api/runs/{id}/triage?agentic=true # Agentic triage — Claude calls read-only tools first; returns tool_calls trace
GET /api/suites/{id}/flaky-tests # Flags test cases with repeated pass/fail flips
WS /ws/runs/{run_id} # Real-time result updates
Each microservice (auth · projects · runs · ai — on its own port, not through the gateway) also exposes the health endpoints its Kubernetes probes use:
GET /health/live # Liveness — process is serving; never checks a dependency
GET /health/ready # Readiness — checks the service's DB; 503 + {"checks": {...}} when down
GET /health # Same as /health/live (kept for existing callers)
Five independent, feature-equivalent automation stacks drive the same app — same core flows, same public
/api/* surface — each targeting a different hiring context on purpose (see the breakdown below):
TestFlow (api/main.py + static/)
▲
┌──────────────┬─────────────┼─────────────┬──────────────┐
│ │ │ │ │
┌───────▼──────┐┌──────▼───────┐┌────▼────────┐┌───▼──────────┐┌──▼───────────┐
│ Python ││ TypeScript ││ Python ││ Java ││ Java │
│ pytest ││ Playwright ││ Playwright ││ REST Assured││ Playwright │
├───────────────┤├──────────────┤├─────────────┤├──────────────┤├──────────────┤
│ tests/unit ││ e2e/tests/ ││ tests/e2e/ ││ java-tests/ ││ java-e2e/ │
│ tests/api ││ *.spec.ts ││ *.py ││ *.java ││ *.java │
│ tests/contract││ ││ ││ ││ │
│ tests/services││ ││ ││ ││ │
├───────────────┤├──────────────┤├─────────────┤├──────────────┤├──────────────┤
│ in-process ││ real browser ││ real browser││ black-box ││ real browser │
│ TestClient + ││ + real HTTP ││ + real HTTP ││ HTTP only — ││ + real HTTP │
│ throwaway DB ││ ││ ││ no shortcuts ││ │
└───────┬───────┘└──────┬───────┘└──────┬──────┘└──────┬───────┘└──────┬───────┘
│ │ │ │ │
└────────────────┴───────────────┴───────────────┴───────────────┘
Allure report (every run, every stack)
707 pytest tests, 92 Playwright TS specs (plus a 19-spec fully-offline mocked layer — see below), 35 REST Assured tests, a JUnit 5/Playwright Java E2E suite, and a Cucumber/Gherkin BDD suite — see the full breakdown below.
Coverage: ~90% line coverage of api/, services/, and shared/ from the pytest suite alone (unit + API +
contract + services), measured with pytest-cov and enforced at an 85% floor in CI (.github/workflows/test.yml)
— a PR that drops coverage below that fails the build. This doesn't count the additional exercise from the
Playwright/REST Assured E2E suites, which run against a live deployment rather than in-process.
| Type | What it checks | Where |
|---|---|---|
| Unit | Pure functions (JWT/hash logic) — no DB, no HTTP, no I/O | tests/unit/ (pytest) |
| API / integration | Real FastAPI app + real (throwaway, per-test) SQLite DB, via TestClient | tests/api/ (pytest) |
| Contract | Real responses validated against the app's own live OpenAPI schema — property-based edge cases, not just hand-picked examples | tests/contract/ (pytest + Schemathesis) · e2e/tests/contract.spec.ts (Playwright + ajv + fast-check) |
| Microservices | Each of the 6 services/ (auth, projects, runs, worker, ai, gateway) tested in isolation — auth, CRUD, inter-service HTTP calls, graceful degradation when a downstream service, Redis, or Kafka is unreachable | tests/services/ (pytest) |
| Kafka producer/consumer | services/runs' alert.triggered producer and services/worker's consumer of it, unit-level with KafkaProducer/KafkaConsumer monkeypatched (no live broker needed) — publish success, broker-unreachable degradation, malformed-message and retries-exhausted routing to the dead-letter topic, transient-failure retry-then-succeed, idempotent persistence | tests/services/test_kafka_producer.py, tests/services/test_kafka_consumer.py (pytest) |
| E2E / browser | Full user flows through the real UI in a real browser, against a running instance of the app | tests/e2e/ (pytest + Playwright) · e2e/tests/*.spec.ts (Playwright + TypeScript) · java-e2e/ (JUnit 5 + Playwright Java) |
| Serverless mocked E2E | Every backend call intercepted with page.route() against typed fixtures (e2e/mocks/) — no FastAPI process, no database, served by a ~40-line static file server instead of uvicorn. A separate contract-drift check validates those fixtures against the live OpenAPI schema (ajv) so a mocked test can't quietly keep passing against a stale backend contract | e2e/tests/mocked-serverless-*.spec.ts, e2e/tests/mock-contract-drift.spec.ts (Playwright + TypeScript) |
707 pytest tests total (347 unit + 177 API + 38 contract operations + 145 services), plus 92 Playwright TS E2E specs (plus a 19-spec fully-offline mocked layer — see below), 35 JUnit 5/REST Assured API tests, a JUnit 5/Playwright-Java E2E suite, and a Cucumber/Gherkin BDD suite — five independent automation stacks (Python, TypeScript, two in Java, and Cucumber) against the same app. See the breakdown below for how each stack is built.
This project deliberately maintains three independent, feature-equivalent browser-automation stacks against the
same app — Playwright + TypeScript, Playwright + pytest (Python), and Playwright + Java (JUnit 5) — rather than
picking one. All three drive the same UI through a Page Object Model and cover the same core user flows (auth,
projects, suites, test cases, runs); the TypeScript and Python stacks run in CI on every PR, and stay in parity with
each other as new coverage is added (e.g. login.spec.ts / test_login_e2e.py, the sidebar pass-rate bar). A
fourth stack, JUnit 5 + REST Assured (java-tests/), covers the same API surface again as pure black-box HTTP
tests — see below.
| Playwright · TypeScript | Playwright · Python (pytest) | Playwright · Java (JUnit 5) | |
|---|---|---|---|
| Location | e2e/ | tests/e2e/ | java-e2e/ |
| Run with | npm test (@playwright/test runner) | pytest tests/e2e -m regression (pytest-playwright) | mvn test |
| Page Object Model | e2e/pages/*.page.ts | tests/e2e/pages/*_page.py | java-e2e/.../pages/*.java |
| Auth strategy | fixture-based token injection (fixtures/auth.fixture.ts) + a dedicated UI modal spec (login.spec.ts) | page-object login flow (test_login_e2e.py) | token injection (BaseTest.signInAs) + a dedicated UI modal spec (LoginTest) |
| Structured step logging | logger.ts (log.step/action/assert) | logger.py (PWLogger) | — |
| Failure diagnostics | screenshot + video + trace, only-on-failure (playwright.config.ts) | screenshot + video + trace, only-on-failure (pytest.ini addopts) | screenshot + video + trace, only-on-failure (FailureArtifactsExtension, details) |
| CI workflow | .github/workflows/pw-ts.yml — every PR / push to main touching e2e/, api/, static/ | .github/workflows/test.yml, pw-scheduled.yml, pw-regression.yml | .github/workflows/java-e2e-tests.yml — every PR / push to touching , , |
The TypeScript and Python stacks get equal billing below — same depth of detail, same structure — since each
targets a different hiring context (Playwright/TypeScript roles vs. pytest/Python roles) and both are meant to
stand on their own. The Java stacks (both java-e2e/ and java-tests/) target a third hiring context —
JUnit/Playwright/REST Assured roles — the same way; see their own READMEs for the same level of detail.
Every stack above deliberately minimizes dependency on live, shared, or unstable environments — not as one feature, but as a strategy applied consistently across layers:
tests/api/, tests/contract/,
and tests/services/ each get a real SQLite database created fresh and torn down per test (per-pytest-xdist-worker
path, so parallel workers never race the same file — see tests/api/conftest.py). No test ever depends on
another test's leftover state or a pre-seeded shared DB.tests/api/test_ai_generate.py
mocks anthropic.Anthropic itself — the actual third-party client — rather than a wrapper this repo wrote
around it, so the test still exercises this app's own request-building and response-parsing code, not just a
fake of it.tests/services/test_events_resilience.py asserts the app degrades correctly when Redis or Kafka is
unreachable, and tests/services/test_kafka_consumer.py covers malformed-message and retries-exhausted
routing to a dead-letter topic.e2e/mocks/
intercepts every backend call with Playwright's page.route() against typed fixtures, so the entire E2E suite
runs with no FastAPI process and no database at all (e2e/tests/mocked-serverless-*.spec.ts, 19 specs) — see
the Serverless mocked E2E row above. The risk a hand-maintained mock always carries is silent drift from
what the real backend actually returns; e2e/tests/mock-contract-drift.spec.ts validates every fixture in
e2e/mocks/factories.ts against the live OpenAPI schema (ajv) in CI, so a mock that stops matching reality
fails the build instead of quietly staying "green" against a contract that no longer exists.The Python side is further split into the classic pyramid — narrow and fast at the bottom, broad and slow at the
top — as five physically separate pytest layers under tests/.
tests/
├── conftest.py # shared failure-logging hook + auto layer-marking (unit/api/contract/services/e2e)
├── unit/ # 347 tests — pure functions, no DB/HTTP/I-O (~4s total)
├── api/ # 177 tests — FastAPI TestClient against an in-memory DB (~95s total)
│ └── conftest.py # per-test SQLite engine + admin/executor auth fixtures
├── contract/ # 1 property-based suite (38 operations) — Schemathesis vs. the OpenAPI schema (~10s)
├── services/ # 145 tests — each services/ microservice in isolation, via TestClient (~11s total)
│ ├── conftest.py # per-service SQLite engine + JWT minting
│ ├── test_kafka_producer.py # services/runs' alert.triggered producer, KafkaProducer monkeypatched
│ └── test_kafka_consumer.py # services/worker's consumer of it — parsing, retry, dead-letter routing
└── e2e/ # 44 tests — Playwright browser + deployed-instance API (minutes; needs a running app)
└── pages/ # Page Object Model — locators isolated from test logic
Engineering practices this demonstrates:
app.dependency_overrides — so they verify actual SQLAlchemy behavior, not a stubbed-out fake, while staying hermetic and parallelizable.pytest-xdist -n auto runs tests/unit/api/contract/services across worker processes — ~125s → ~35s locally on 4 cores — safe because every DB-backed fixture derives a worker-unique SQLite path from PYTEST_XDIST_WORKER (see tests/api/conftest.py), so parallel workers never race the same file. The TypeScript E2E suite (e2e/) shards across a CI matrix the same way, merging per-shard blob reports back into one report (.github/workflows/pw-ts.yml). The Java suites (java-tests/, java-e2e/) deliberately do not run parallel yet — enabling JUnit 5 concurrent test classes reproducibly exposed a real backend race (concurrent DELETE + GET on a cascading delete returning a stale 200), tracked as issue #214 rather than silently shipped or hidden.tests/api/test_ai_generate.py monkeypatches anthropic.Anthropic so AI-generation tests are deterministic and free, without ever faking the FastAPI/Pydantic layers around it.tests/unit/test_auth_tokens.py and through real HTTP status codes in tests/api/test_auth.py — a failure in the unit layer localizes to the algorithm; a failure only in the API layer points at the wiring (dependency injection, route guards) instead.pytest_collection_modifyitems hook in the root conftest.py tags every test with unit/api/contract/services/e2e from its file path, so pytest -m api works regardless of which paths you point pytest at — no per-test @pytest.mark upkeep.pip install -r requirements.txt -r requirements-test.txt
pytest tests/unit -v # unit layer only — milliseconds, no setup
pytest tests/unit tests/api tests/contract tests/services -v # what CI runs on every PR
pytest tests/services -v # microservices layer only
pytest tests/e2e/test_e2e.py --base-url=https://your-app.vercel.app -v # browser E2E
pytest tests/ -m regression --base-url=https://your-app.vercel.app -v # full regression suite
pytest tests/unit tests/api --alluredir=allure-results # write Allure results
allure generate allure-results --clean -o allure-report && allure open allure-report
The TypeScript side (e2e/) is a single, flat spec layer — every spec drives the real browser against a running
instance of the app, backed by a shared Page Object Model and an auth fixture.
e2e/
├── fixtures/auth.fixture.ts # authToken / authedRequest — one login, reused by every spec
├── pages/ # BasePage + one *.page.ts per screen (POM, data-testid locators)
├── mocks/ # typed fixtures + page.route() helpers for the serverless specs
├── tests/ # login · projects · suites · testcases · runs · sidebar-progress-bar ·
│ # contract · api · mocked-serverless-* · mock-contract-drift
├── global-setup.ts # registers the e2e user once, saves the auth token to disk
├── global-teardown.ts # deletes leftover test projects by name prefix
├── serve-static.py # backend-free static server for the mocked-serverless specs
├── playwright.config.ts # real-backend suite (globalSetup logs in a real user)
└── playwright.mocked.config.ts # serverless suite — no globalSetup, no FastAPI/DB
Engineering practices this demonstrates:
fixtures/auth.fixture.ts extends Playwright's base test with an authToken fixture read once from global-setup.ts — specs inject it via localStorage/Authorization header instead of repeating a login flow.login.spec.ts still exercises the real sign-in modal end-to-end (render, success, invalid credentials) as its own unauthenticated spec, so the fixture's shortcut never leaves the actual login UI untested.pages/*.page.ts): every page extends a shared BasePage, locators use the data-testid strategy exclusively, and test.step() annotates each action for readable traces.--project=webkit) rather than slowing down the default run.on-failure only — full repro evidence without paying the cost on green runs.logger.ts mirrors the Python suite's PWLogger output format 1:1, so both stacks read the same way in CI logs.allure-playwright reporter is registered in playwright.config.ts (alongside a JSON reporter CI's job summary parses — no plain HTML report); every run produces a full Allure report (steps, attachments, history), generated in CI (pw-ts.yml) and uploaded as a build artifact.contract.spec.ts validates real responses against the app's own /openapi.json with ajv, and property-tests extreme path-param values with fast-check — the TypeScript counterpart to the Python side's Schemathesis suite. It caught a real bug of its own: a datetime fix on the Python side had silently dropped format: date-time from the generated schema instead of preserving it.pw-ts.yml parses the JSON reporter output into a pass/fail/flaky job-summary table on every run.mocked-serverless-*.spec.ts intercept every backend call with against typed fixtures () instead of hitting a real FastAPI process, so CRUD flows, AI-feature error paths (503, malformed JSON), and edge-case responses (401 mid-session, 429, partial data) run fast and deterministically in a separate, lighter CI job () with no at all. The catch with any mocked layer — it can't notice the real backend's response shape drifting out from under it — is covered by , which fetches the live and validates every fixture against it; a failure there names exactly which fixture and field broke. See for the full design.cd e2e && npm install
npx playwright install chromium firefox
npm test # headless, chromium + firefox
npm run test:headed # headed chromium, for debugging
BASE_URL=https://your-app.vercel.app npm test # against staging
npm run test:mocked # serverless mocked layer (no backend needed)
npm run allure:report # generate + open the Allure report
See e2e/README.md for the full breakdown.
Authoring aid, not a CI stack. Playwright's official agents
(planner/generator/healer) are wired up as Claude Code subagents (.claude/agents/, root .mcp.json) to explore
the running app and draft/self-heal specs for this suite — see
e2e/README.md#playwright-agents. Everything they produce is an ordinary
*.spec.ts file reviewed and run through pw-ts.yml like any other change; they don't run unattended in CI.
The Java side (java-tests/) is a fourth, independent stack — black-box HTTP tests against a
running instance of the app, written the way a Java QA engineer would test any deployed
service, with no in-process shortcuts. It targets a different hiring context again (JUnit/REST
Assured roles), rounding out the project's automation coverage across Python, TypeScript and Java.
java-tests/
├── pom.xml
└── src/test/java/com/testflow/api/
├── support/ # REST Assured config, bootstrap-admin auth, unique naming
├── AuthApiTest.java # login/me/token validation
├── ProjectsApiTest.java # CRUD, pagination envelope, admin-only writes
├── SuitesApiTest.java # CRUD, 404s, admin-only writes
├── TestCasesApiTest.java # CRUD, defaults, the null-vs-omitted-field regression
├── RunsApiTest.java # run creation, pending-result seeding, auto-completion
└── UsersApiTest.java # admin user management, self-delete guard
Engineering practices this demonstrates:
TestClient, no dependency overrides — so it exercises the exact same public
contract a real API consumer (or the other two stacks) would hit, monolith or microservices.POST /api/auth/register only
ever succeeds for the very first user on a given database. AuthSupport uses the same
register-then-login fallback and the same fixed bootstrap credentials as
e2e/global-setup.ts, so this suite, the TypeScript suite, and a human
using the app can all run against one live instance without racing to become the first user.TestData.uniqueName), so the suite is safe to run repeatedly against a persistent database
(SQLite/Postgres), not just a throwaway one.TestCasesApiTest re-asserts the
explicit-null-doesn't-clear-a-non-nullable-field fix documented in api/main.py's
update_testcase, so the same real bug the contract suite caught stays covered here too.allure-junit5 + allure-rest-assured, in the same format the
Python and TypeScript stacks already produce.# app must be running first (see Quick start above)
cd java-tests
mvn test # against http://localhost:8000
mvn test -DbaseUrl=https://your-app.vercel.app # against a deployed instance
mvn test
allure generate target/allure-results --clean -o target/allure-report && allure open target/allure-report
See java-tests/README.md for the full breakdown.
The fourth stack (java-e2e/) is a Java browser-automation suite — JUnit 5 + Playwright-for-Java
driving the real UI in a real browser, the Java counterpart to e2e/ and tests/e2e/. It covers
the same core flows (sign-in modal, projects, suites, test cases, a run) through a Java Page
Object Model, rather than the API layer java-tests/ covers.
java-e2e/
├── pom.xml
└── src/test/java/com/testflow/e2e/
├── support/ # Playwright lifecycle, bootstrap-admin auth, unique naming
├── pages/ # Page Object Model — mirrors e2e/pages/*.ts
├── LoginTest.java # sign-in modal: render, valid/invalid login
├── ProjectsTest.java # create/delete a project through the UI
├── SuitesTest.java # create a suite inside a project
├── TestCasesTest.java # create a test case
└── RunsTest.java # start a run, mark results, summary counts
Engineering practices this demonstrates:
pages/*.java mirror e2e/pages/*.ts locator-for-locator (data-testid selectors, the same
modal/form flows), so a bug caught by one stack's page object has an equivalent check here.ApiClient (a plain JDK HttpClient, no extra dependency) rather than the UI, the same
"drive setup through the API, assert through the UI" pattern e2e/tests/*.spec.ts uses via
Playwright's request fixture — keeps each spec focused on the one flow it's testing.java-tests/ and e2e/global-setup.ts — see
java-e2e/README.md.allure-junit5, in the same format the other three stacks produce.# app must be running first (see Quick start above)
cd java-e2e
mvn test # headless, against http://localhost:8000
mvn test -Dheaded=true # headed Chromium, for debugging
mvn test -DbaseUrl=https://your-app.vercel.app # against a deployed instance
allure generate target/allure-results --clean -o target/allure-report && allure open target/allure-report
See java-e2e/README.md for the full breakdown.
The fifth stack (e2e-bdd/) is a behavior-driven layer over the same app — Cucumber.js + Playwright, feature
files in Gherkin instead of hand-written test() blocks. It covers the same sign-in and project-lifecycle
flows as e2e/, expressed as scenarios a non-engineer stakeholder can read and review directly.
e2e-bdd/
├── features/
│ ├── login.feature # Sign-in modal — Scenario Outline for invalid-credentials cases
│ └── project_management.feature # Create / delete a project
├── pages/ # Small, purpose-built Page Object Model (see e2e-bdd/README.md for why
│ # it doesn't import e2e/pages directly)
├── step-definitions/ # Given/When/Then bindings + Before/After hooks
└── scripts/cucumber-json-to-junit.js # Converts Cucumber's JSON report to JUnit XML
Engineering practices this demonstrates:
Given a project named "Legacy Suite" already exists / When I delete the project named "Legacy Suite"), with a
Scenario Outline + Examples table driving the invalid-login cases from data instead of copy-pasted
scenarios.java-e2e/'s own POM, ported from but not
importing e2e/pages); e2e-bdd/ follows the same rule — for a concrete reason, not just convention, see
e2e-bdd/README.md.cucumber-json-to-junit.js is a small, dependency-free converter (the
cucumber-js team dropped the built-in JUnit formatter years ago) so results render natively in whichever CI
system is consuming them — GitHub Actions' dorny/test-reporter and Azure Pipelines' PublishTestResults@2
both read the same JUnit file it produces.bdd-cucumber.yml (GitHub
Actions) and azure-pipelines.yml (Azure DevOps) both start the app and run the exact
same npm test against it — the pipeline definition is the only thing that differs.cd e2e-bdd && npm install
npx playwright install chromium
npm test # all scenarios
npm run test:smoke # @smoke only
npm run report:junit # reports/cucumber-report.json -> reports/cucumber-junit.xml
See e2e-bdd/README.md for the full breakdown.
.github/workflows/)| Workflow | Trigger | What runs |
|---|---|---|
test.yml | every PR + push to main | ruff check . (lint, blocking); tests/unit + tests/api + tests/contract + tests/services (blocking); tests/infra/ — renders every k8s/overlays/* with kubectl kustomize and runs terraform fmt/init/validate (blocking); tests/e2e/test_e2e.py on push to main only (non-blocking) |
pw-scheduled.yml | weekly cron | tests/e2e/test_e2e.py + tests/e2e/test_users_e2e.py against the live deployment |
pw-regression.yml | manual dispatch | full -m regression suite across all layers against a chosen target URL |
pw-ts.yml | every PR + push to main touching e2e/, api/, static/; daily cron | full e2e/tests/*.spec.ts suite (incl. mock-contract-drift.spec.ts, which needs the real backend this job starts), Allure report uploaded as an artifact |
pw-mocked-e2e.yml | every PR + push to main touching e2e/tests/mocked-serverless-*.spec.ts, e2e/mocks/, static/ | the fully offline mocked-serverless-*.spec.ts suite — no pip install, no FastAPI, no database, just npm ci + a static file server — Allure report uploaded as an artifact |
java-api-tests.yml | every PR + push to main touching java-tests/, api/, shared/ | starts the app locally, runs the full java-tests/ JUnit suite, Allure report uploaded as an artifact |
java-e2e-tests.yml | every PR + push to main touching java-e2e/, api/, static/ | starts the app locally, installs Playwright's Chromium, runs the full java-e2e/ JUnit suite, Allure report uploaded as an artifact |
Also included: azure-pipelines.yml — an Azure DevOps YAML pipeline running the
pytest, Cucumber, and serverless mocked/service-virtualized Playwright suites, alongside the GitHub Actions
workflows above (which remain this repo's actual CI, since it's hosted on GitHub). See the comment at the top
of that file for why it's here.
.
├── api/ # Monolith (FastAPI single-process)
│ ├── main.py # All routes, WebSocket, AI generation, middleware
│ ├── models.py # SQLAlchemy ORM models
│ ├── schemas.py # Monolith-only Pydantic schemas + re-exports from shared/
│ └── database.py # DB engine + session factory
│
├── shared/ # Pydantic schemas shared by api/ and every services/*
│ └── schemas.py # see its docstring for what was unified and why
│
├── services/ # Microservice architecture
│ ├── gateway/ # :8000 HTTP proxy + WebSocket bridge + SPA files
│ ├── auth/ # :8001 JWT login · register · user management
│ ├── projects/ # :8002 Projects · suites · test cases · analytics
│ ├── runs/ # :8003 Test runs · results · WebSocket · Redis + Kafka events
│ ├── ai/ # :8004 Claude Haiku AI test case generation
│ ├── worker/ # Consumes runs' alert.triggered Kafka topic into runs_alerts
│ ├── common/ # Shared request-ID correlation, JWT helpers across services
│ └── README.md # Microservice architecture deep-dive
│
├── static/
│ ├── index.html # SPA shell (Chart.js CDN included)
│ └── app.js # All UI logic — hash routing, WebSocket, Chart.js
│
├── tests/ # Python pytest suite, layered
│ ├── unit/ # pure JWT/hash logic — no DB, no HTTP
│ ├── api/ # FastAPI TestClient integration tests
│ ├── contract/ # Schemathesis property tests vs. the OpenAPI schema
│ ├── services/ # per-microservice TestClient tests (services/ coverage)
│ ├── infra/ # kustomize render + terraform fmt/init/validate (no cloud calls)
│ └── e2e/ # Playwright browser + deployed-instance API tests
│ └── pages/ # page objects for the browser E2E specs
├── e2e/ # Playwright TypeScript E2E tests, real-backend + serverless mocked
├── e2e-bdd/ # Cucumber.js + Playwright BDD suite — e2e-bdd/README.md
├── java-tests/ # JUnit 5 + REST Assured black-box API tests
├── java-e2e/ # JUnit 5 + Playwright Java browser E2E tests
├── agents/ # LangChain critic/drafter pipeline + LangGraph Playwright-trio orchestrator — agents/README.md
├── evals/ # AI eval harness — golden datasets, LLM-as-judge scoring — evals/README.md
├── scripts/ # CI-support agents: flaky-test tracker, coverage-gap bot, heal/AI-call metrics
├── docs/ # Architecture write-ups, interview prep, agent-governance notes, screenshots
├── specs/ # Playwright agents' test plans + accessibility-tree context artifacts
├── k8s/ # Kustomize manifests, 4 environment overlays — k8s/README.md
├── terraform/ # GCP infra-as-code, never applied from CI — terraform/README.md
├── deploy/gcp/ # Deployment tooling for the terraform/k8s stack above
├── loadtests/ # Locust scenarios — monolith vs. microservices — loadtests/README.md
├── Dockerfile # Monolith container
├── docker-compose.yml # Monolith mode (app + Postgres)
├── docker-compose.microservices.yml # Microservice mode (6 services + Postgres + Redis + Kafka)
├── requirements.txt
└── vercel.json # Vercel serverless deployment (monolith)
The app deploys automatically to Vercel on every push to main via GitHub Actions.
Each pull request gets its own preview URL.
Set DATABASE_URL (Neon Postgres), JWT_SECRET_KEY, and ANTHROPIC_API_KEY in Vercel environment variables.
Use docker-compose.microservices.yml with a Postgres 16 instance, Redis 7, and Kafka 3.7 (the worker
service and the broker are both optional at runtime — runs' Kafka producer and worker's consumer both
degrade gracefully if the broker is unreachable). The gateway container is the only one that needs to be
publicly exposed.
Point your orchestrator's health checks at each service's /health/live (restart) and
/health/ready (route traffic) — k8s/base/ already wires them as startup/liveness/readiness probes.
Every service is also Datadog-ready (APM via ddtrace-run, JSON logs carrying dd.trace_id, unified
service tagging), switched off until you set DD_TRACE_ENABLED=true and run an Agent — see
docs/datadog.md.
See CONTRIBUTING.md for local setup, the test/lint/coverage
gates a PR needs to pass, and repo conventions. See SECURITY.md
to report a vulnerability, and CHANGELOG.md for notable changes.
Copyright (c) 2026 Rabin Avidan. All rights reserved. This repository is public for
portfolio and evaluation purposes only — see LICENSE for details.
| Deterministic (no LLM) + Claude Code skill |
scripts/failure_classifier.py · .claude/skills/failure-triage/SKILL.md |
| Playwright Test Agents | Planner/generator/healer trio: explores the running app in a real browser, drafts a numbered test plan, generates Playwright specs from it, and debugs/fixes failing ones — an authoring aid, not a CI job. The planner saves a shared accessibility-tree context artifact (specs/*.context.json) the generator and healer read instead of re-exploring the same flow from scratch. The healer classifies each failure as safe-to-auto-fix (locator/timing drift) or a suspected real defect (behavior change) — the latter is never silently skipped, only escalated — and every healing session is logged to heal-outcomes/heal_outcomes.jsonl, scored by scripts/heal_metrics.py (heal-success-rate, false-heal-rate) | Claude (Sonnet, via Claude Code) | .claude/agents/playwright-test-*.md · e2e/README.md#playwright-agents |
| AI Test Generation | Generates test cases from a plain-English feature description; can optionally run "grounded" (grounded: true), retrieving the suite's nearest existing test cases by embedding similarity and passing them as "don't duplicate these" context (monolith only — see api/retrieval.py). The embedding model is pluggable (M8): EMBEDDING_PROVIDER=hash (default) · ollama (nomic-embed-text) · voyage | Claude Haiku (or Ollama/Groq — see below) | POST /api/suites/{id}/testcases/generate |
| AI Failure Triage | Summarizes a run's failed/skipped results into a root-cause hypothesis | Claude Haiku (or Ollama/Groq — see below) | POST /api/runs/{id}/triage |
| Agentic Failure Triage (M7) | With ?agentic=true, triage becomes a bounded Anthropic tool-use loop: the model can call scoped, read-only tools — get_test_case_history, get_similar_test_cases (RAG), get_suite_flaky_tests — before diagnosing, and the response carries the full tool trace for human review. Iteration cap, suite-scoped tool arguments, tool errors fed back to the model, single-shot fallback for non-Anthropic providers | Claude Haiku (tool use) | api/triage_agent.py · complete_with_tools() in api/ai_gateway.py |
| Eval Harness | Runs the AI Test Generation and AI Failure Triage prompts N times per case against a local model, scoring output quality and run-to-run consistency; wired into CI (informational) against a real model | Ollama (local, no API key) | evals/ · .github/workflows/eval-harness.yml |
| Test Plan Reviewer | A genuine two-step agent pipeline built with LangChain (prompt | llm | parser, LCEL) — a critic step finds test-coverage gaps for a feature, a drafter step writes test cases to fill them, in the same schema AI Test Generation uses | Ollama (local, no API key) | agents/ |
| Pipeline Orchestrator | An explicit LangGraph multi-agent orchestrator for the Playwright planner→generator→healer trio — an authoring graph with a real interrupt()-based human-approval gate before generation and before a spec is marked ready to commit, plus a separate classify→fix→retry healer graph sharing the interactive healer's escalate-on-behavior-change guardrail. A headless illustration of the orchestration pattern (see docs/agent-governance.md), not a replacement for the real browser-driven Playwright Test Agents above | Ollama (local, no API key) | agents/pipeline_orchestrator.py · docs/agent-governance.md |
docs/agent-governance.md
documents why (the checkpoints an orchestrator would remove are exactly where a human should be looking) and
what evidence would change that call, plus scripts/agent_telemetry.py's unified
view over the healer's and AI gateway's separate telemetry logs..claude/skills/.claude/agents/tests/unit/test_failure_classifier.py, tests/unit/test_flaky_test_ci_config.py, tests/unit/test_pr_steward_workflow.py |
| Human-in-the-loop | LangGraph interrupt() approval gates (agents/pipeline_orchestrator.py), healer escalate-don't-skip rule, AI drafts reviewed in-app — accept / edit / reject-with-reason, rejections fed into the next generation prompt and exported as eval cases (M9, api/ai_review.py), triage tool trace shown in the UI, triage verdicts flagged Needs human review when the agent and the heuristic disagree, and the assertion guard's assertion-change-approved sign-off | tests/unit/test_pipeline_orchestrator.py, tests/api/test_triage_agentic.py, tests/unit/test_assertion_guard.py, tests/api/test_ai_review.py, tests/api/test_ai_review_loop.py |
GET /api/environments · k8s/overlays/* |
| Contact Us | Public form (footer, no login required) — topic/email/phone/message, saved to the DB and emailed to the site owner via Resend or SMTP (configurable, see .env.example) | POST /api/contact |
| Log Center | Admin-only dashboard over every request/response, unhandled server exception, and reported browser-side error — filter by level/source, free-text search. A global window.onerror/unhandledrejection hook reports client errors automatically. Self-pruned at 5,000 rows. | GET /api/logs · POST /api/logs/client |
| Java API | Black-box HTTP tests against a running instance — no in-process shortcuts, same public /api/* surface as every other stack | java-tests/ (JUnit 5 + REST Assured) |
| BDD / Gherkin | Stakeholder-readable Given/When/Then feature files over the same core flows (sign-in, project lifecycle), driven by Cucumber.js + Playwright | e2e-bdd/ (Cucumber.js + Playwright + TypeScript) |
| Regression | Cross-layer tag (-m regression) for a scheduled full-suite run against a live deployment | pytest.ini marker, run by pw-regression.yml / pw-scheduled.yml |
| Reporting | Allure report (history, retries, step-by-step detail) generated from every run in CI | allure-pytest (Python) · allure-playwright (TypeScript) · allure-junit5 (Java) |
| Coverage | Line coverage of api/, services/, shared/ — ~90%, gated at an 85% floor | pytest-cov (.coveragerc), reported in the CI job summary and as a coverage.json artifact |
| Scalability / load | Throughput and latency under increasing concurrency against both live deployments — the same Locust scenarios against the SQLite monolith and the Postgres/Redis microservices stack, quantifying issue #214's backend race on one and finding a new one (#219) on the other, not just pass/fail | loadtests/ (Locust), manual/monthly via .github/workflows/loadtest-sqlite.yml + .github/workflows/loadtest-microservices.yml |
| Microservices boot smoke test | Actually builds and boots docker-compose.microservices.yml (Postgres + Redis + Kafka + all 6 services) and drives a real CRUD flow through the gateway — the one thing tests/services/'s in-process TestClient coverage can't catch. Found (and fixed) issue #217: every service crash-looped on the documented docker compose up --build, undetected because nothing else in CI ever builds these images | .github/workflows/microservices-smoke.yml, every PR/push touching services/, shared/, or the compose file |
mainjava-e2e/api/static/tests/contract/test_openapi_contract.pydatetimeUTCDatetimeshared/schemas.pySQLite-INTEGEROverflowErrorapi/main.pynullPUT /api/testcases/{tc_id}api/main.pyservices/runs/kafka_events.py publishes an alert.triggered event whenever a test result is recorded as fail; services/worker/kafka_consumer.py consumes it into a new runs_alerts table, deduplicated per (run_id, testcase_id). tests/services/test_kafka_producer.py and test_kafka_consumer.py cover the producer and consumer independently — KafkaProducer/KafkaConsumer monkeypatched rather than relying on "no broker in this environment" (unlike Redis, a Kafka client's connection-refused path isn't fast enough to lean on that) — including broker-unreachable graceful degradation, malformed-message and retries-exhausted dead-letter routing, and transient-failure retry-then-succeed.tests/services/ had zero prior coverage of services/ (the 5-service microservices deployment) and immediately found a critical, previously-undetected bug: identical JWT-padding math ("=" * (4 - len(s) % 4) % 4, wrong operator precedence) duplicated across five files, crashing every authenticated request across the entire microservices deployment with a raw TypeError — fixed in all five. It also caught the gateway proxy silently misrouting /api/suites/{id}/runs to the projects service instead of runs (fixed in services/gateway/main.py), and directly validates the services README's claim that events "degrade gracefully if Redis is down" by calling the publisher with no Redis reachable — true by construction in this environment, not asserted from documentation.tests/services/ imports each service's FastAPI app directly with TestClient — real code, but never through a built Docker image or the real gateway proxy. That gap let docker-compose.microservices.yml ship with every one of its 5 services crash-looping on the documented docker compose up --build (isolated per-service build contexts and bare-module CMDs couldn't resolve the services.common/shared/relative imports the application code actually uses) — issue #217, found while starting the scalability-profiling work below. Fixed by building every service from the repo root and running it via its full dotted module path, and closed the actual gap with .github/workflows/microservices-smoke.yml, which builds and boots the real compose file and runs a live CRUD flow through the gateway on every relevant PR.loadtests/locustfile.py ran unmodified against the Postgres/Redis microservices stack: issue #214's race never reproduced in 906 samples there (vs. 8.3% under SQLite at the same concurrency) — a real, measured difference in how the two backends handle contention, not an assumption — but the same run surfaced a different real bug the monolith can't have: issue #219, the async worker container's queue-driven TestResult population occasionally losing a race against a client's immediate mark-result call. See loadtests/README.md for the full comparison — trading one concurrency bug for a different one, not a strict win.tests/e2e/pages/): locators, data-testid selection strategy, and modal/toast helpers live in page classes, never inline in test bodies.tests/e2e/logger.py (PWLogger) prints a step/action/assert trace for every test, so a CI log reads like a script, not a wall of framework noise.allure-pytest (wired via --alluredir) turns every test run into a browsable Allure report — history, retries, timeline and step-by-step detail — generated in CI (test.yml) and uploaded as a build artifact.page.route()e2e/mocks/factories.tspw-mocked-e2e.ymlpip installmock-contract-drift.spec.ts/openapi.jsonbdd-cucumber.yml |
every PR + push to main touching e2e-bdd/, api/, static/ |
starts the app locally, runs the full e2e-bdd/ Cucumber suite, JSON + JUnit reports uploaded as an artifact |