Testing
VETA has nine test suites. Each addresses a distinct failure class: unit suites cover logic primitives, integration covers service boundaries, E2E covers UI composition, smoke covers deployment plumbing, the synthetic probe covers public reachability, and performance covers throughput regressions. The taxonomy below is organised by failure class rather than by tooling.
At a glance
Section titled “At a glance”| Suite | What it catches | Where it runs | Speed | | --- | --- | --- | --- | | Backend unit | Pure-logic bugs in algos, OMS, FIX, schemas | Pre-commit, CI | <30s | | Frontend unit | Component + Redux + hook regressions | Pre-commit, CI | ~20s | | Integration (Testcontainers) | Service-boundary contract breaks | CI | ~5min | | Playwright E2E | UI flows that span the whole stack | CI | ~3min | | Electron E2E | Desktop-app-specific behaviour (IPC, contextBridge) | CI | ~1min | | Smoke | Service reachability through production-equivalent ingress | CI testcontainers gate | ~90s | | Visual anomalies | DOM overflows + a11y violations (non-gating) | CI (informational) | ~1min | | Synthetic probe | Live deployment is reachable from the internet | Every 60s from edge server | continuous | | Performance | Throughput regressions, latency p99 drift | On demand | varies | | k6 load testing | Sustained throughput under realistic order flow scenarios | On demand | varies | | Continuous load generator | Always-on synthetic trade flow on the production server | Production only | continuous |
The pre-commit hook (.husky/pre-commit) runs lint + type-check + unit tests + integration in seven steps. Integration auto-skips when services aren't running locally so the hook stays fast for tight edit loops. Smoke runs in CI under the testcontainers gate rather than at pre-commit.
How the suites fit together
Section titled “How the suites fit together”graph TD UNIT["Unit<br/>(Deno, Vitest)<br/>pure functions, no I/O"]:::fast INT["Integration<br/>(.tc.test.ts)<br/>real Postgres and Redpanda<br/>per test, via Testcontainers"]:::mid SMOKE["Smoke<br/>(.tc.test.ts)<br/>HTTP health and pipeline checks<br/>against an ephemeral stack"]:::mid PW["Playwright E2E<br/>real browser, real frontend,<br/>mocked or real backend"]:::mid EL["Electron E2E<br/>Playwright launched against<br/>the packaged Electron binary"]:::mid PROBE["Synthetic probe<br/>internet → edge server → server Traefik<br/>the path real users take"]:::slow
UNIT --> INT --> SMOKE --> PW --> EL --> PROBE
classDef fast fill:#dcfce7,stroke:#16a34a,color:#000 classDef mid fill:#fef3c7,stroke:#d97706,color:#000 classDef slow fill:#fecaca,stroke:#dc2626,color:#000Lower entries depend on more of the system being real. The position of a failing suite localises the fault: a probe failure with green unit tests indicates a deployment or ingress regression; a unit failure with a green probe indicates a regression not yet shipped.
Where each suite runs across the dev lifecycle
Section titled “Where each suite runs across the dev lifecycle”graph LR subgraph LOCAL["Local pre-commit hook"] L1["lint + type-check"] L2["unit (backend + frontend)"] L3["(smoke runs in CI only)"] L4["integration<br/>(auto-skip if stack offline)"] end
subgraph PR["Pull request CI"] P1["lint + type-check"] P2["unit"] P3["integration<br/>(Testcontainers)"] P4["Playwright E2E"] P5["Electron E2E"] P6["visual-anomalies<br/>(non-gating)"] P7["screenshots"] end
subgraph MAIN["On merge to main"] M1["all PR checks"] M2["coverage badges<br/>auto-committed"] M3["container images<br/>published to GHCR"] end
subgraph DEPLOY["Homelab deploy (post-push)"] D1["systemd auto-pull<br/>(every 5 min)"] D2["docker compose up -d"] D3["docker compose healthchecks<br/>(gates rollout)"] D4["frontend deploy-gate smoke<br/>(post-deploy probe)"] end
subgraph PROD["Live production"] R1["synthetic probe<br/>(every 60s from edge server)"] end
LOCAL --> PR --> MAIN --> DEPLOY --> PROD
classDef local fill:#dcfce7,stroke:#16a34a,color:#000 classDef ci fill:#fef3c7,stroke:#d97706,color:#000 classDef deploy fill:#fecaca,stroke:#dc2626,color:#000 classDef prod fill:#e0e7ff,stroke:#4f46e5,color:#000
class L1,L2,L3,L4 local class P1,P2,P3,P4,P5,P6,P7,M1,M2,M3 ci class D1,D2,D3,D4 deploy class R1 prodEach suite has a defined trigger. The pre-commit hook is the first line; integration auto-skips locally to keep the hook fast, but runs in CI without the auto-skip. Smoke runs in CI behind the testcontainers gate rather than at pre-commit. The post-deploy probe is the final gate before users see new code: a probe failure pages the operator.
Where to start
Section titled “Where to start”Coverage
Section titled “Coverage”Coverage is generated on every push to main and surfaced as badges in the README:
- Frontend: V8 provider via Vitest. Reports
text-summary,lcov, andjson-summary. Thresholds: 80% statements, branches, functions, lines, enforced by vitest.config.ts. - Backend: Deno's native
--coverageflag. Exported as lcov viadeno coverage --lcov > coverage.lcov. - Badge:
docs/badges/coverage.jsonis auto-committed by the CI job that runs coverage.
To run coverage locally:
# backenddeno task test:coverage
# frontendcd frontend && npm run test:coverageCI pipeline shape
Section titled “CI pipeline shape”graph LR LT["lint-and-test"] --> INT["integration<br/>(Testcontainers)"] LT --> DB["docker-base"] --> DS["docker-services"] FE["frontend"] --> PW["playwright-ui"] FE --> SS["screenshots"] FE --> VA["visual-anomalies<br/>(non-gating)"] FE --> EL["electron"]Playwright, screenshots, Electron, and visual-anomalies run in parallel with the integration job rather than after it. The synthetic probe and load tests are not part of the merge pipeline; they run continuously and on demand respectively.
See CI / CD for the full GitHub Actions workflow.