Skip to content

Commit 3752ac5

Browse files
Merge pull request #1009 from Adamsonoladipupo/test/919-tenants-response-schema
Test/919 tenants response schema
2 parents 2bf9b3c + 0896636 commit 3752ac5

3 files changed

Lines changed: 1032 additions & 0 deletions

File tree

docs/tenants-api.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,46 @@ Invalid requests return `400` before route logic runs:
8383
```
8484

8585
Unknown JSON fields are rejected.
86+
87+
## Schema Stability Tests
88+
89+
`tests/schema/tenants.test.ts` contains snapshot tests that guard against
90+
accidental response-schema drift on both endpoints. Run them with:
91+
92+
```bash
93+
npx jest --testPathPattern="tests/schema/tenants" --no-coverage
94+
```
95+
96+
### What is covered
97+
98+
| Group | Count | Purpose |
99+
|---|---:|---|
100+
| POST 201 success envelope shape | 8 | Exact top-level keys, `data` field types, optional-field omission |
101+
| POST full-response snapshot | 2 | `toMatchSnapshot` lock on the complete stabilized response |
102+
| POST 401 error envelope | 3 | Top-level keys, `UNAUTHORIZED` code, no details array |
103+
| POST 400 validation error | 5 | `VALIDATION_ERROR` code, details array shape, `body.name` field path, strict-mode unknown-field rejection, snapshot |
104+
| PATCH 200 success envelope shape | 6 | Same as POST checks plus `data.id` ↔ URL param echo |
105+
| PATCH full-response snapshot | 2 | `toMatchSnapshot` lock on the complete stabilized PATCH response |
106+
| PATCH 401 error envelope | 1 | `UNAUTHORIZED` code and envelope keys |
107+
| PATCH 400 validation error | 4 | Empty body, bad `tenantId` param, details shape, snapshot |
108+
| 500 error propagation | 2 | Repository errors surface as `INTERNAL_SERVER_ERROR` envelopes |
109+
| Cross-endpoint envelope invariants | 6 | Parameterized: every scenario carries `success`, `requestId`, `timestamp`, and `data`/`error` |
110+
111+
**Total: 38 tests, 6 snapshots.**
112+
113+
### Snapshot strategy
114+
115+
Variable fields (`timestamp`, `createdAt`, `updatedAt`) are replaced with
116+
`<TIMESTAMP>` / `<CREATED_AT>` / `<UPDATED_AT>` placeholders before snapshotting
117+
so the stored snapshots are reproducible across runs at different wall-clock
118+
times. The stable snapshots live in
119+
`tests/schema/__snapshots__/tenants.test.ts.snap`.
120+
121+
To update snapshots after an intentional schema change:
122+
123+
```bash
124+
npx jest --testPathPattern="tests/schema/tenants" --updateSnapshot
125+
```
126+
127+
Review the diff carefully before committing updated snapshots — any change
128+
represents a visible API contract change for clients.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`PATCH /api/tenants/:tenantId — Response Schema Stability 400 validation error — error envelope shape matches the patch validation error envelope snapshot: patch-tenant-validation-error 1`] = `
4+
{
5+
"error": {
6+
"code": "VALIDATION_ERROR",
7+
"details": [
8+
{
9+
"code": "CUSTOM",
10+
"field": "body",
11+
"message": "At least one tenant field must be provided",
12+
},
13+
],
14+
"message": "Request validation failed",
15+
},
16+
"requestId": "req-schema-patch-invalid-snap",
17+
"success": false,
18+
"timestamp": "<TIMESTAMP>",
19+
}
20+
`;
21+
22+
exports[`PATCH /api/tenants/:tenantId — Response Schema Stability snapshot — full PATCH /api/tenants/:tenantId response matches the stable full update-tenant response snapshot: patch-tenant-full-response 1`] = `
23+
{
24+
"data": {
25+
"contactEmail": "stadium@grantfox.test",
26+
"createdAt": "<CREATED_AT>",
27+
"createdBy": "dev-1",
28+
"id": "tenant-abc-001",
29+
"metadata": {
30+
"campaign": "fwc26",
31+
},
32+
"name": "GrantFox Stadium Ops",
33+
"plan": "enterprise",
34+
"slug": "grantfox-ops",
35+
"updatedAt": "<UPDATED_AT>",
36+
},
37+
"requestId": "req-schema-update-snap",
38+
"success": true,
39+
"timestamp": "<TIMESTAMP>",
40+
}
41+
`;
42+
43+
exports[`PATCH /api/tenants/:tenantId — Response Schema Stability snapshot — full PATCH /api/tenants/:tenantId response matches the top-level shape snapshot: patch-tenant-shape 1`] = `
44+
{
45+
"dataKeys": [
46+
"createdAt",
47+
"createdBy",
48+
"id",
49+
"name",
50+
"plan",
51+
"slug",
52+
"updatedAt",
53+
],
54+
"successValue": true,
55+
"topLevelKeys": [
56+
"data",
57+
"requestId",
58+
"success",
59+
"timestamp",
60+
],
61+
}
62+
`;
63+
64+
exports[`POST /api/tenants — Response Schema Stability 400 validation error — error envelope shape matches the validation error envelope snapshot: post-tenant-validation-error 1`] = `
65+
{
66+
"error": {
67+
"code": "VALIDATION_ERROR",
68+
"details": [
69+
{
70+
"code": "INVALID_TYPE",
71+
"field": "body.name",
72+
"message": "Invalid input: expected string, received undefined",
73+
},
74+
{
75+
"code": "INVALID_FORMAT",
76+
"field": "body.contactEmail",
77+
"message": "contactEmail must be a valid email address",
78+
},
79+
],
80+
"message": "Request validation failed",
81+
},
82+
"requestId": "req-schema-invalid-snap",
83+
"success": false,
84+
"timestamp": "<TIMESTAMP>",
85+
}
86+
`;
87+
88+
exports[`POST /api/tenants — Response Schema Stability snapshot — full POST /api/tenants response matches the stable full create-tenant response snapshot: post-tenant-full-response 1`] = `
89+
{
90+
"data": {
91+
"contactEmail": "ops@grantfox.test",
92+
"createdAt": "<CREATED_AT>",
93+
"createdBy": "dev-1",
94+
"id": "ten_fixture_001",
95+
"metadata": {
96+
"campaign": "fwc26",
97+
"priority": 1,
98+
},
99+
"name": "GrantFox Ops",
100+
"plan": "growth",
101+
"slug": "grantfox-ops",
102+
"updatedAt": "<UPDATED_AT>",
103+
},
104+
"requestId": "req-schema-snapshot",
105+
"success": true,
106+
"timestamp": "<TIMESTAMP>",
107+
}
108+
`;
109+
110+
exports[`POST /api/tenants — Response Schema Stability snapshot — full POST /api/tenants response matches the top-level shape snapshot: post-tenant-shape 1`] = `
111+
{
112+
"dataKeys": [
113+
"createdAt",
114+
"createdBy",
115+
"id",
116+
"name",
117+
"plan",
118+
"slug",
119+
"updatedAt",
120+
],
121+
"successValue": true,
122+
"topLevelKeys": [
123+
"data",
124+
"requestId",
125+
"success",
126+
"timestamp",
127+
],
128+
}
129+
`;

0 commit comments

Comments
 (0)