opinionated-machine 6.19.1 → 6.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1511,6 +1511,39 @@ it('streams chat completions', async () => {
|
|
|
1511
1511
|
})
|
|
1512
1512
|
```
|
|
1513
1513
|
|
|
1514
|
+
#### Asserting documented error responses with `bodyForStatus`
|
|
1515
|
+
|
|
1516
|
+
When a contract declares `responseBodySchemasByStatusCode` for non-2xx responses (the shape the handler emits via `sse.respond(status, body)` before streaming starts), `injectSSE` / `injectPayloadSSE` expose a typed `bodyForStatus(status)` accessor:
|
|
1517
|
+
|
|
1518
|
+
```ts
|
|
1519
|
+
import { buildSseContract } from '@lokalise/api-contracts'
|
|
1520
|
+
import { z } from 'zod'
|
|
1521
|
+
import { injectSSE } from 'opinionated-machine'
|
|
1522
|
+
|
|
1523
|
+
const streamContract = buildSseContract({
|
|
1524
|
+
method: 'get',
|
|
1525
|
+
pathResolver: () => '/api/stream',
|
|
1526
|
+
requestQuerySchema: z.object({}),
|
|
1527
|
+
requestHeaderSchema: z.object({}),
|
|
1528
|
+
responseBodySchemasByStatusCode: {
|
|
1529
|
+
401: z.object({ message: z.string() }),
|
|
1530
|
+
404: z.object({ resourceId: z.string() }),
|
|
1531
|
+
},
|
|
1532
|
+
serverSentEventSchemas: { message: z.object({ text: z.string() }) },
|
|
1533
|
+
})
|
|
1534
|
+
|
|
1535
|
+
it('returns the documented 401 body when unauthenticated', async () => {
|
|
1536
|
+
const { bodyForStatus } = injectSSE(app, streamContract, {})
|
|
1537
|
+
|
|
1538
|
+
// `body` is typed as `{ message: string }` — the 401 schema.
|
|
1539
|
+
// TS rejects status codes the contract doesn't declare, e.g. bodyForStatus(500).
|
|
1540
|
+
const body = await bodyForStatus(401)
|
|
1541
|
+
expect(body.message).toBe('Unauthorized')
|
|
1542
|
+
})
|
|
1543
|
+
```
|
|
1544
|
+
|
|
1545
|
+
`bodyForStatus(status)` awaits the response, asserts the actual status matches, JSON-parses the body, and runs it through the Zod schema declared for that status. It throws — with the offending status and a truncated body snippet — if the status doesn't match, the contract declares no schema for that status, the body isn't valid JSON, or Zod parsing fails. The raw `closed` promise is still exposed for callers that want to read `body: string` directly.
|
|
1546
|
+
|
|
1514
1547
|
### SSESessionSpy API
|
|
1515
1548
|
|
|
1516
1549
|
The `connectionSpy` is available when `isTestMode: true` is passed to `asSSEControllerClass`:
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import type { SSEContractDefinition } from '@lokalise/api-contracts';
|
|
1
|
+
import type { HttpStatusCode, SSEContractDefinition } from '@lokalise/api-contracts';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
import type { AnyFastifyInstance } from './AnyFastifyInstance.ts';
|
|
4
|
-
import type { InjectPayloadSSEOptions, InjectSSEOptions, InjectSSEResult } from './sseTestTypes.ts';
|
|
4
|
+
import type { InjectPayloadSSEOptions, InjectSSEOptions, InjectSSEResult, SSEResponse } from './sseTestTypes.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Build a `bodyForStatus` accessor bound to one inject call. The closure
|
|
7
|
+
* captures the contract's schemas map so the resulting helper knows which
|
|
8
|
+
* schemas to parse against; at the type level the caller is constrained to
|
|
9
|
+
* status codes the contract actually declares.
|
|
10
|
+
*
|
|
11
|
+
* @internal Exported only for unit testing — not part of the public API
|
|
12
|
+
* (the testing barrel re-exports `injectSSE`/`injectPayloadSSE` by name).
|
|
13
|
+
*/
|
|
14
|
+
export declare function bindBodyForStatus<Schemas extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined>(contract: {
|
|
15
|
+
responseBodySchemasByStatusCode?: Schemas;
|
|
16
|
+
}, closed: Promise<SSEResponse>): InjectSSEResult<Schemas>['bodyForStatus'];
|
|
5
17
|
/**
|
|
6
18
|
* Inject a GET SSE request using a contract definition.
|
|
7
19
|
*
|
|
@@ -21,7 +33,7 @@ import type { InjectPayloadSSEOptions, InjectSSEOptions, InjectSSEResult } from
|
|
|
21
33
|
* const events = parseSSEEvents(result.body)
|
|
22
34
|
* ```
|
|
23
35
|
*/
|
|
24
|
-
export declare function injectSSE<Contract extends SSEContractDefinition<'get', z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny, undefined, Record<string, z.ZodTypeAny
|
|
36
|
+
export declare function injectSSE<Contract extends SSEContractDefinition<'get', z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny, undefined, Record<string, z.ZodTypeAny>, Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined>>(app: AnyFastifyInstance, contract: Contract, options?: InjectSSEOptions<Contract>): InjectSSEResult<Contract['responseBodySchemasByStatusCode']>;
|
|
25
37
|
/**
|
|
26
38
|
* Inject a POST/PUT/PATCH SSE request using a contract definition.
|
|
27
39
|
*
|
|
@@ -49,4 +61,4 @@ export declare function injectSSE<Contract extends SSEContractDefinition<'get',
|
|
|
49
61
|
* )
|
|
50
62
|
* ```
|
|
51
63
|
*/
|
|
52
|
-
export declare function injectPayloadSSE<Contract extends SSEContractDefinition<'post' | 'put' | 'patch', z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny, Record<string, z.ZodTypeAny
|
|
64
|
+
export declare function injectPayloadSSE<Contract extends SSEContractDefinition<'post' | 'put' | 'patch', z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny, Record<string, z.ZodTypeAny>, Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined>>(app: AnyFastifyInstance, contract: Contract, options: InjectPayloadSSEOptions<Contract>): InjectSSEResult<Contract['responseBodySchemasByStatusCode']>;
|
|
@@ -1,3 +1,54 @@
|
|
|
1
|
+
/** Truncate a long body string for error messages. */
|
|
2
|
+
const BODY_TRUNCATE_LIMIT = 500;
|
|
3
|
+
const truncateBody = (body) => {
|
|
4
|
+
if (body.length <= BODY_TRUNCATE_LIMIT) {
|
|
5
|
+
return body;
|
|
6
|
+
}
|
|
7
|
+
// Step back one unit if the cut would split a surrogate pair, so the
|
|
8
|
+
// snippet never ends in a lone (invalid) surrogate.
|
|
9
|
+
const lastCode = body.charCodeAt(BODY_TRUNCATE_LIMIT - 1);
|
|
10
|
+
const end = lastCode >= 0xd800 && lastCode <= 0xdbff ? BODY_TRUNCATE_LIMIT - 1 : BODY_TRUNCATE_LIMIT;
|
|
11
|
+
return `${body.slice(0, end)}…`;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Build a `bodyForStatus` accessor bound to one inject call. The closure
|
|
15
|
+
* captures the contract's schemas map so the resulting helper knows which
|
|
16
|
+
* schemas to parse against; at the type level the caller is constrained to
|
|
17
|
+
* status codes the contract actually declares.
|
|
18
|
+
*
|
|
19
|
+
* @internal Exported only for unit testing — not part of the public API
|
|
20
|
+
* (the testing barrel re-exports `injectSSE`/`injectPayloadSSE` by name).
|
|
21
|
+
*/
|
|
22
|
+
export function bindBodyForStatus(contract, closed) {
|
|
23
|
+
// A generic arrow function can't be assigned directly to the generic
|
|
24
|
+
// method signature, so the whole closure is cast once. Keep this
|
|
25
|
+
// implementation in sync with `InjectSSEResult['bodyForStatus']`.
|
|
26
|
+
return (async (statusCode) => {
|
|
27
|
+
const res = await closed;
|
|
28
|
+
const expected = statusCode;
|
|
29
|
+
if (res.statusCode !== expected) {
|
|
30
|
+
throw new Error(`bodyForStatus(${expected}) — actual status ${res.statusCode}, body: ${truncateBody(res.body)}`);
|
|
31
|
+
}
|
|
32
|
+
// Widen the generic schemas map to a concrete type so it can be indexed.
|
|
33
|
+
const schemas = contract.responseBodySchemasByStatusCode;
|
|
34
|
+
const schema = schemas?.[expected];
|
|
35
|
+
if (!schema) {
|
|
36
|
+
throw new Error(`bodyForStatus(${expected}) — no response body schema declared for status ${expected} in contract.responseBodySchemasByStatusCode`);
|
|
37
|
+
}
|
|
38
|
+
let parsedJson;
|
|
39
|
+
try {
|
|
40
|
+
parsedJson = JSON.parse(res.body);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw new Error(`bodyForStatus(${expected}) — body is not valid JSON: ${err.message}; body: ${truncateBody(res.body)}`);
|
|
44
|
+
}
|
|
45
|
+
const parsed = schema.safeParse(parsedJson);
|
|
46
|
+
if (!parsed.success) {
|
|
47
|
+
throw new Error(`bodyForStatus(${expected}) — body does not match the declared schema: ${parsed.error.message}; body: ${truncateBody(res.body)}`);
|
|
48
|
+
}
|
|
49
|
+
return parsed.data;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
1
52
|
/**
|
|
2
53
|
* Build query string from query params object.
|
|
3
54
|
* @internal
|
|
@@ -62,7 +113,7 @@ export function injectSSE(app, contract, options) {
|
|
|
62
113
|
headers: res.headers,
|
|
63
114
|
body: res.body,
|
|
64
115
|
}));
|
|
65
|
-
return { closed };
|
|
116
|
+
return { closed, bodyForStatus: bindBodyForStatus(contract, closed) };
|
|
66
117
|
}
|
|
67
118
|
/**
|
|
68
119
|
* Inject a POST/PUT/PATCH SSE request using a contract definition.
|
|
@@ -109,6 +160,6 @@ export function injectPayloadSSE(app, contract, options) {
|
|
|
109
160
|
headers: res.headers,
|
|
110
161
|
body: res.body,
|
|
111
162
|
}));
|
|
112
|
-
return { closed };
|
|
163
|
+
return { closed, bodyForStatus: bindBodyForStatus(contract, closed) };
|
|
113
164
|
}
|
|
114
165
|
//# sourceMappingURL=sseInjectHelpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sseInjectHelpers.js","sourceRoot":"","sources":["../../../lib/testing/sseInjectHelpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sseInjectHelpers.js","sourceRoot":"","sources":["../../../lib/testing/sseInjectHelpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import type { AnySSEContractDefinition } from '@lokalise/api-contracts';
|
|
1
|
+
import type { AnySSEContractDefinition, HttpStatusCode } from '@lokalise/api-contracts';
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
import type { ParsedSSEEvent } from '../sse/sseParser.ts';
|
|
4
4
|
/** Safely infer the output type of an optional Zod schema property. */
|
|
5
5
|
type InferOptionalSchema<T, Fallback = unknown> = NonNullable<T> extends z.ZodTypeAny ? z.infer<NonNullable<T>> : Fallback;
|
|
6
|
+
/**
|
|
7
|
+
* Status codes that the given schemas-map declares.
|
|
8
|
+
* Resolves to `never` when the map is `undefined`, so `bodyForStatus` is
|
|
9
|
+
* uncallable for contracts that declare no response body schemas at all.
|
|
10
|
+
*/
|
|
11
|
+
export type DeclaredResponseStatus<Schemas extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined> = Schemas extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> ? keyof Schemas & HttpStatusCode : never;
|
|
12
|
+
/** Type of the parsed response body for a declared status. */
|
|
13
|
+
export type DeclaredResponseBody<Schemas extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined, Status extends DeclaredResponseStatus<Schemas>> = Schemas extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> ? Status extends keyof Schemas ? Schemas[Status] extends z.ZodTypeAny ? z.infer<Schemas[Status]> : never : never : never;
|
|
6
14
|
/**
|
|
7
15
|
* Represents an active SSE test connection (inject-based).
|
|
8
16
|
*
|
|
@@ -82,12 +90,45 @@ export type SSEResponse = {
|
|
|
82
90
|
* Note: Fastify's inject() waits for the full response, so these helpers
|
|
83
91
|
* work best for streaming that completes (OpenAI-style). For long-lived
|
|
84
92
|
* SSE connections, use `SSEHttpClient` with a real HTTP server instead.
|
|
93
|
+
*
|
|
94
|
+
* When the contract declares `responseBodySchemasByStatusCode`, the result
|
|
95
|
+
* exposes `bodyForStatus(status)` — a typed accessor that parses the response
|
|
96
|
+
* body against the contract's schema for that status. TS rejects status codes
|
|
97
|
+
* the contract doesn't declare.
|
|
85
98
|
*/
|
|
86
|
-
export type InjectSSEResult = {
|
|
99
|
+
export type InjectSSEResult<Schemas extends Partial<Record<HttpStatusCode, z.ZodTypeAny>> | undefined = undefined> = {
|
|
87
100
|
/**
|
|
88
101
|
* Resolves when the response completes with the full SSE body.
|
|
89
102
|
* Parse the body with `parseSSEEvents()` to get individual events.
|
|
90
103
|
*/
|
|
91
104
|
closed: Promise<SSEResponse>;
|
|
105
|
+
/**
|
|
106
|
+
* Awaits the response, asserts the status code matches, parses the body
|
|
107
|
+
* against the contract's schema for that status, and returns the parsed
|
|
108
|
+
* object. Useful for asserting on documented error response shapes.
|
|
109
|
+
*
|
|
110
|
+
* Intended for non-streaming responses emitted via `sse.respond(status, body)`
|
|
111
|
+
* before streaming starts (auth failures, validation errors, not-found).
|
|
112
|
+
* Calling it for a status served by an actual SSE stream fails at the
|
|
113
|
+
* JSON-parse step, since a stream body is `text/event-stream`, not JSON.
|
|
114
|
+
*
|
|
115
|
+
* Throws (with the offending status and a truncated body snippet) if:
|
|
116
|
+
* - the actual status code doesn't match the expected one;
|
|
117
|
+
* - the contract declares no schema for that status;
|
|
118
|
+
* - the body isn't valid JSON;
|
|
119
|
+
* - the body doesn't match the declared Zod schema.
|
|
120
|
+
*
|
|
121
|
+
* At the type level, `statusCode` is constrained to the keys of the
|
|
122
|
+
* contract's `responseBodySchemasByStatusCode`. Contracts without any
|
|
123
|
+
* declared schemas can't call this method (`statusCode: never`).
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* const { bodyForStatus } = injectSSE(app, contract, { headers })
|
|
128
|
+
* const error = await bodyForStatus(401) // typed as z.infer<401-schema>
|
|
129
|
+
* expect(error.message).toBe('Unauthorized')
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
bodyForStatus<Status extends DeclaredResponseStatus<Schemas>>(statusCode: Status): Promise<DeclaredResponseBody<Schemas, Status>>;
|
|
92
133
|
};
|
|
93
134
|
export {};
|