Skip to content

Feat/contract tests - #419

Merged
Xhristin3 merged 1 commit into
XStreamRollz:mainfrom
Ibk-Bless:feat/contract-tests
Jul 25, 2026
Merged

Feat/contract tests#419
Xhristin3 merged 1 commit into
XStreamRollz:mainfrom
Ibk-Bless:feat/contract-tests

Conversation

@Ibk-Bless

@Ibk-Bless Ibk-Bless commented Jul 17, 2026

Copy link
Copy Markdown

Summary

Adds tests/contracts/ (@xstreamroll/contract-tests) as a lightweight, custom alternative to Pact — no broker or provider-state infrastructure needed, built entirely on tooling already in the repo (Jest, supertest — already a root devDependency — and zod, already used across api/app).
Each Contract object in tests/contracts/src/*.contract.ts describes one SDK/API interaction: the request the SDK sends, and a zod schema for the response it's entitled to expect.
The schemas are pinned to @xstreamroll/types at compile time via a small typed() helper — if a schema stops matching the shared types, the package fails to build, before any test even runs.
Two suites import the same Contract objects, so they can't independently drift the way the plain TS interfaces they're checking used to:
Provider verification — api/src/contract-provider.spec.ts boots the real controllers/services behind supertest (in-memory repositories instead of Postgres, no DB needed) and asserts every actual HTTP response satisfies its contract.
Consumer verification — xstreamroll-sdk/tests/contract.consumer.test.ts asserts the SDK sends the request a contract describes, and correctly parses a schema-valid response.
Both run as part of each package's normal npm run test:cov, so a contract violation fails CI the same way any other test failure would — no new CI job needed, just install/build/lint/typecheck steps for the new workspace package.
A real finding from building this

A stream id that doesn't exist returns 403, not 404 — StreamOwnershipGuard rejects before the controller's not-found branch is ever reachable (the ownership query returns false the same way for "doesn't exist" and "not yours"). The contract now documents that as intentional, pinned-down behavior instead of leaving it as an implicit assumption.

Coverage

create-stream: provider yes, consumer no (SDK doesn't implement this call yet)
list-streams: provider yes, consumer no (SDK doesn't implement this call yet)
get-stream-by-id: provider yes, consumer yes
get-stream-by-id-not-found: provider yes, consumer no (SDK doesn't implement this call yet)
update-stream: provider yes, consumer no (SDK doesn't implement this call yet)
register: provider yes, consumer yes
login: provider yes, consumer yes
See tests/contracts/README.md for the full design rationale (including why a custom suite over Pact) and the update workflow for future API changes.

Test plan

tests/contracts: npm run build / npm run typecheck / npm run lint all pass
api: full Jest suite (126 tests, including 8 new provider-verification cases) passes; npx tsc --noEmit and npm run build (Nest build) pass
xstreamroll-sdk: full Jest suite (44 tests, including 3 new consumer-verification cases) passes; npx tsc --noEmit passes
Root aggregate npm run lint (--max-warnings=0) has the same 39 pre-existing warnings as main — no new warnings introduced
Sanity-checked the compile-time schema pinning actually works: temporarily added a field to a contract's request body that doesn't exist on CreateUserDto and confirmed tsc fails the build; reverted
Closes #399

@Ibk-Bless
Ibk-Bless force-pushed the feat/contract-tests branch from 453e643 to 91d01a7 Compare July 17, 2026 11:23

Copy link
Copy Markdown
Contributor

Really good addition, @Ibk-Bless — contract tests are a huge help in catching API drift between services before it becomes a runtime problem. CI is all green. Merging! 🚀

Copy link
Copy Markdown
Contributor

Hey @Ibk-Bless, contract tests are a great addition and CI is green ✅!

The branch has conflicts with main after several PRs landed recently. The conflicts are in api/src/streams/streams.controller.spec.ts, streams.controller.ts, xstreamroll-sdk/package.json, and xstreamroll-sdk/src/types.ts — a rebase should resolve them:

git fetch origin
git rebase origin/main
# resolve conflicts, then
git push --force-with-lease

Once updated, this is good to merge!

@Ibk-Bless
Ibk-Bless force-pushed the feat/contract-tests branch 2 times, most recently from f5ff318 to d6a2e46 Compare July 22, 2026 10:11
@Ibk-Bless

Copy link
Copy Markdown
Author

@Xhristin3 conflict resolved, please review

@Ibk-Bless
Ibk-Bless force-pushed the feat/contract-tests branch from d6a2e46 to f2f4f2a Compare July 22, 2026 15:51
@Xhristin3

Copy link
Copy Markdown
Contributor

@Ibk-Bless please resolve conflicts

Adds tests/contracts/ (@xstreamroll/contract-tests) as a lightweight,
custom alternative to Pact — no broker or provider-state infra needed,
built entirely on tooling already in the repo (Jest, supertest, zod).

Each Contract object in tests/contracts/src/*.contract.ts describes one
SDK/API interaction: the request the SDK sends and a zod schema for the
response it's entitled to expect. The schemas are pinned to
@xstreamroll/types at compile time via a small typed<T>() helper, so a
schema that stops matching the shared types fails to build before any
test runs.

Two suites import the same Contract objects and can't independently
drift the way the plain interfaces they're checking used to:

- api/src/contract-provider.spec.ts boots the real controllers/services
  behind supertest (in-memory repositories instead of Postgres, no DB
  needed) and asserts each actual HTTP response satisfies its contract.
- xstreamroll-sdk/__tests__/contract.consumer.test.ts asserts the SDK
  sends the request a contract describes and correctly parses a
  schema-valid response.

Both run as part of each package's normal `npm run test:cov`, so a
contract violation fails CI the same way any other test failure would
— no new CI job needed, just build/lint/typecheck steps for the new
workspace package (see .github/workflows/ci.yml).

One real finding from building this: a stream id that doesn't exist
returns 403, not 404, because StreamOwnershipGuard rejects before the
controller's not-found branch is reachable. The contract now documents
that as intentional behavior instead of leaving it implicit.

Closes XStreamRollz#399
@Ibk-Bless
Ibk-Bless force-pushed the feat/contract-tests branch from f2f4f2a to 2c1f91b Compare July 23, 2026 15:10
@Ibk-Bless

Copy link
Copy Markdown
Author

@Xhristin3 conflict has been resolved

@Ibk-Bless

Copy link
Copy Markdown
Author

@Xhristin3 please review

@Xhristin3
Xhristin3 merged commit 0080125 into XStreamRollz:main Jul 25, 2026
2 checks passed

Copy link
Copy Markdown
Contributor

Nice contract coverage @Ibk-Bless, thanks! Merged.

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.

testing: No contract tests between API and SDK — breaking changes go undetected

2 participants