Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8ef481a11 |
@@ -33,11 +33,11 @@ jobs:
|
||||
|
||||
- name: Typecheck
|
||||
run: |
|
||||
pnpm --filter @groombook/api typecheck
|
||||
pnpm run typecheck
|
||||
pnpm --filter @groombook/db typecheck
|
||||
|
||||
- name: Lint
|
||||
run: pnpm --filter @groombook/api lint
|
||||
run: pnpm run lint
|
||||
|
||||
test:
|
||||
name: Test
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm --filter @groombook/api test
|
||||
run: pnpm run test
|
||||
|
||||
docker:
|
||||
name: Build & Push Docker Images
|
||||
|
||||
@@ -147,8 +147,6 @@ Expected: one row, `role = 'groomer'`. If zero rows return, the request hit the
|
||||
| TC-API-3.19b | Get pet profile summary — customer cross-tenant blocked (GRO-2013) | Sign in as `uat-customer@groombook.dev`; reuse the customer's sessionId from TC-API-3.19a; `GET /api/pets/{otherClientPetId}/profile-summary` for a pet owned by a different client (`c0000002-...` or any non-customer pet) | 403 Forbidden (owner-bypass requires session.clientId === pet.clientId) |
|
||||
| TC-API-3.19c | Get pet profile summary — customer without portal session header | Same as TC-API-3.19a but omit the `X-Impersonation-Session-Id` header | 403 Forbidden (no owner-bypass without valid portal session) |
|
||||
| TC-API-3.19d | Get pet profile summary — owner-bypass writes audit row (GRO-2063) | Same setup as TC-API-3.19a (sign in as `uat-customer@groombook.dev`, establish a portal session for the customer's own clientId, call `GET /api/pets/{ownPetId}/profile-summary` with `X-Impersonation-Session-Id: {sessionId}` and a 200 OK response). Then call `GET /api/impersonation/sessions/{sessionId}/audit-log` and confirm there is exactly one entry with `action === "read_profile_summary"`, `pageVisited` matching the profile-summary path, and `metadata` containing `petId` and `actorStaffId` for the customer. Repeat TC-API-3.19b (cross-tenant attempt) and confirm NO new `read_profile_summary` row was written for the cross-tenant attempt. | 200 OK on the profile-summary call AND an audit log entry is present with the correct shape (defense-in-depth audit row; bypass attempts against other clients must NOT log) |
|
||||
| TC-UAT-2 | Groomer accesses linked pet profile summary (GRO-2100) | Sign in as `uat-groomer@groombook.dev`; `GET /api/pets/c0000001-0000-0000-0000-000000000002/profile-summary` (UAT Pup Alpha — linked via deterministic completed appointment `a0000001-0000-0000-0000-000000000001`, service `b0000001-…-0001` "Bath & Brush", `startTime` ~7 days ago) | 200 OK, `recentGroomingHistory[]` non-empty (>=1 entry), `visitCount >= 1`, `upcomingAppointment` null (the seeded appointment is in the past) |
|
||||
| TC-UAT-3 | Groomer blocked from unlinked pet profile summary (GRO-2100) | Sign in as `uat-groomer@groombook.dev`; `GET /api/pets/c0000001-0000-0000-0000-000000000003/profile-summary` (UAT Pup Beta — intentionally UNLINKED; no appointment row references this pet's clientId+groomerId combo) | 403 Forbidden (RBAC `groomer` role lacks the appointment-linkage grant for this pet). NOTE: if 404 is returned instead of 403, file a separate RBAC defect (not against the seed) — see GRO-2100 verification note |
|
||||
| TC-API-3.29 | Get pet profile summary — unknown UUID returns 404 (GRO-2014) | GET /api/pets/00000000-0000-0000-0000-000000000001/profile-summary while authenticated (any role) | 404 Not Found with body `{"error":"Not found"}` (was empty-body 500 in GRO-2014) |
|
||||
| TC-API-3.30 | Get pet profile summary — malformed UUID returns 404 (GRO-2014) | GET /api/pets/not-a-uuid/profile-summary while authenticated | 404 Not Found with body `{"error":"Not found"}` (was empty-body 500 in GRO-2014 — Postgres uuid cast failure) |
|
||||
| TC-API-3.31 | Get pet profile summary — never empty-body 500 (GRO-2014) | GET /api/pets/{anyId}/profile-summary across the test sweep | No response has status 500 with an empty body. Any 500 must include a JSON body `{"error":"Internal Server Error"}` |
|
||||
|
||||
@@ -131,20 +131,6 @@ function makeAppointment(overrides: Record<string, unknown> = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function makeService(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
id: "service-1",
|
||||
name: "Full Groom",
|
||||
description: null,
|
||||
basePriceCents: 6000,
|
||||
durationMinutes: 120,
|
||||
active: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function makeSession(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
id: "sess-owner",
|
||||
@@ -164,7 +150,6 @@ function makeSession(overrides: Record<string, unknown> = {}) {
|
||||
|
||||
let petsTable: Record<string, unknown>[];
|
||||
let appointmentsTable: Record<string, unknown>[];
|
||||
let servicesTable: Record<string, unknown>[];
|
||||
let sessionsTable: Record<string, unknown>[];
|
||||
|
||||
// selectQueue: queries resolve in FIFO order. Each .from(table) result
|
||||
@@ -198,7 +183,6 @@ function enqueueThrow(table: string, message: string) {
|
||||
function resetMock() {
|
||||
petsTable = [makePet()];
|
||||
appointmentsTable = [makeAppointment()];
|
||||
servicesTable = [makeService()];
|
||||
sessionsTable = [makeSession()];
|
||||
selectQueue = [];
|
||||
insertCapture = [];
|
||||
|
||||
Reference in New Issue
Block a user