japa-openapi-assertions 0.1.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 +240 -0
- package/build/coverage.d.ts +53 -0
- package/build/coverage.d.ts.map +1 -0
- package/build/coverage.js +123 -0
- package/build/coverage.js.map +1 -0
- package/build/index.d.ts +33 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +42 -0
- package/build/index.js.map +1 -0
- package/build/openapi_assertions.d.ts +49 -0
- package/build/openapi_assertions.d.ts.map +1 -0
- package/build/openapi_assertions.js +114 -0
- package/build/openapi_assertions.js.map +1 -0
- package/build/path_matcher.d.ts +18 -0
- package/build/path_matcher.d.ts.map +1 -0
- package/build/path_matcher.js +89 -0
- package/build/path_matcher.js.map +1 -0
- package/build/response_validator.d.ts +10 -0
- package/build/response_validator.d.ts.map +1 -0
- package/build/response_validator.js +173 -0
- package/build/response_validator.js.map +1 -0
- package/build/schema_builder.d.ts +22 -0
- package/build/schema_builder.d.ts.map +1 -0
- package/build/schema_builder.js +215 -0
- package/build/schema_builder.js.map +1 -0
- package/build/types.d.ts +126 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# @drew-daniels/japa-openapi-assertions
|
|
2
|
+
|
|
3
|
+
OpenAPI 3.1 assertions plugin for [Japa](https://japa.dev) test framework. Validate your API responses against OpenAPI specifications with full OpenAPI 3.1 support.
|
|
4
|
+
|
|
5
|
+
## Why This Fork?
|
|
6
|
+
|
|
7
|
+
This package is a fork of [`@japa/openapi-assertions`](https://japa.dev/docs/plugins/openapi-assertions) created to add **OpenAPI 3.1 support**. The original package uses [`api-contract-validator`](https://github.com/PayU/api-contract-validator) under the hood, which only supports OpenAPI 3.0.
|
|
8
|
+
|
|
9
|
+
OpenAPI 3.1 introduced significant changes that break compatibility with OpenAPI 3.0 tooling. This fork replaces the underlying validation engine with [AJV](https://ajv.js.org/) configured for [JSON Schema Draft 2020-12](https://json-schema.org/draft/2020-12/json-schema-core.html), providing native support for OpenAPI 3.1 specifications.
|
|
10
|
+
|
|
11
|
+
## OpenAPI 3.0 vs 3.1: Key Differences
|
|
12
|
+
|
|
13
|
+
OpenAPI 3.1 aligns fully with JSON Schema Draft 2020-12, which introduced several breaking changes from the modified JSON Schema subset used in OpenAPI 3.0:
|
|
14
|
+
|
|
15
|
+
### Nullable Types
|
|
16
|
+
|
|
17
|
+
The `nullable` keyword was removed. Use JSON Schema type arrays instead:
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
# OpenAPI 3.0
|
|
21
|
+
type: string
|
|
22
|
+
nullable: true
|
|
23
|
+
|
|
24
|
+
# OpenAPI 3.1
|
|
25
|
+
type: ["string", "null"]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Exclusive Min/Max
|
|
29
|
+
|
|
30
|
+
Boolean modifiers changed to numeric values:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
# OpenAPI 3.0
|
|
34
|
+
minimum: 0
|
|
35
|
+
exclusiveMinimum: true
|
|
36
|
+
|
|
37
|
+
# OpenAPI 3.1
|
|
38
|
+
exclusiveMinimum: 0
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### $ref Behavior
|
|
42
|
+
|
|
43
|
+
OpenAPI 3.1 allows sibling keywords alongside `$ref`, eliminating the need for `allOf` workarounds:
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
# OpenAPI 3.0 (workaround required)
|
|
47
|
+
allOf:
|
|
48
|
+
- $ref: '#/components/schemas/Pet'
|
|
49
|
+
- description: A pet with extra info
|
|
50
|
+
|
|
51
|
+
# OpenAPI 3.1 (direct usage)
|
|
52
|
+
$ref: '#/components/schemas/Pet'
|
|
53
|
+
description: A pet with extra info
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Examples
|
|
57
|
+
|
|
58
|
+
The `example` keyword is deprecated in favor of `examples` (array format):
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
# OpenAPI 3.0
|
|
62
|
+
example: "Fluffy"
|
|
63
|
+
|
|
64
|
+
# OpenAPI 3.1
|
|
65
|
+
examples:
|
|
66
|
+
- "Fluffy"
|
|
67
|
+
- "Buddy"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For a complete list of changes, see the [OpenAPI Initiative migration guide](https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0).
|
|
71
|
+
|
|
72
|
+
## Backward Compatibility
|
|
73
|
+
|
|
74
|
+
**This package is designed for OpenAPI 3.1 specifications.** It may work with some OpenAPI 3.0 specs, but compatibility is not guaranteed due to the fundamental differences in how schemas are validated:
|
|
75
|
+
|
|
76
|
+
| Feature | OpenAPI 3.0 Spec | This Package |
|
|
77
|
+
|---------|------------------|--------------|
|
|
78
|
+
| `nullable: true` | Supported in 3.0 | Not supported - use `type: ["string", "null"]` |
|
|
79
|
+
| `type: ["string", "null"]` | Not valid in 3.0 | Fully supported |
|
|
80
|
+
| `exclusiveMinimum: true` | Supported in 3.0 | Not supported - use numeric value |
|
|
81
|
+
| `$ref` with siblings | Not valid in 3.0 | Fully supported |
|
|
82
|
+
|
|
83
|
+
If you need OpenAPI 3.0 support, use the original [`@japa/openapi-assertions`](https://www.npmjs.com/package/@japa/openapi-assertions) package.
|
|
84
|
+
|
|
85
|
+
## Installation
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install @drew-daniels/japa-openapi-assertions
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Peer Dependencies
|
|
92
|
+
|
|
93
|
+
This plugin requires:
|
|
94
|
+
- `@japa/assert` ^4.0.0
|
|
95
|
+
- `@japa/runner` ^3.0.0 || ^4.0.0 || ^5.0.0
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install @japa/assert @japa/runner
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Setup
|
|
102
|
+
|
|
103
|
+
Configure the plugin in your Japa configuration file:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// tests/bootstrap.ts
|
|
107
|
+
import { assert } from '@japa/assert'
|
|
108
|
+
import { apiClient } from '@japa/api-client'
|
|
109
|
+
import { openapi } from '@drew-daniels/japa-openapi-assertions'
|
|
110
|
+
|
|
111
|
+
export const plugins = [
|
|
112
|
+
assert(),
|
|
113
|
+
openapi({
|
|
114
|
+
schemas: [new URL('../openapi.json', import.meta.url)],
|
|
115
|
+
reportCoverage: true,
|
|
116
|
+
}),
|
|
117
|
+
apiClient(),
|
|
118
|
+
]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Usage
|
|
122
|
+
|
|
123
|
+
Use the `isValidApiResponse` assertion method to validate responses against your OpenAPI specification:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { test } from '@japa/runner'
|
|
127
|
+
|
|
128
|
+
test.group('Pets API', () => {
|
|
129
|
+
test('list all pets', async ({ client, assert }) => {
|
|
130
|
+
const response = await client.get('/pets')
|
|
131
|
+
|
|
132
|
+
response.assertStatus(200)
|
|
133
|
+
assert.isValidApiResponse(response)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('get a single pet', async ({ client, assert }) => {
|
|
137
|
+
const response = await client.get('/pets/123')
|
|
138
|
+
|
|
139
|
+
response.assertStatus(200)
|
|
140
|
+
assert.isValidApiResponse(response)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('create a pet', async ({ client, assert }) => {
|
|
144
|
+
const response = await client
|
|
145
|
+
.post('/pets')
|
|
146
|
+
.json({ name: 'Fluffy', tag: 'cat' })
|
|
147
|
+
|
|
148
|
+
response.assertStatus(201)
|
|
149
|
+
assert.isValidApiResponse(response)
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Validation Behavior
|
|
155
|
+
|
|
156
|
+
The plugin validates:
|
|
157
|
+
|
|
158
|
+
- **Path matching**: Ensures the request path exists in your OpenAPI spec (supports parameterized paths like `/pets/{petId}`)
|
|
159
|
+
- **HTTP method**: Validates the method is defined for the matched path
|
|
160
|
+
- **Status code**: Checks that a response schema exists for the returned status
|
|
161
|
+
- **Response body**: Validates the response body against the JSON schema defined in your spec
|
|
162
|
+
|
|
163
|
+
If validation fails, a detailed `AssertionError` is thrown with information about what didn't match.
|
|
164
|
+
|
|
165
|
+
## Configuration Options
|
|
166
|
+
|
|
167
|
+
| Option | Type | Description |
|
|
168
|
+
|--------|------|-------------|
|
|
169
|
+
| `schemas` | `(string \| URL)[]` | **Required.** Paths to OpenAPI specification files (JSON format) |
|
|
170
|
+
| `reportCoverage` | `boolean` | Print coverage report to console on process exit |
|
|
171
|
+
| `exportCoverage` | `boolean` | Export coverage data to `coverage.json` on process exit |
|
|
172
|
+
|
|
173
|
+
### Coverage Reporting
|
|
174
|
+
|
|
175
|
+
Enable coverage tracking to see which API endpoints have been tested:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
openapi({
|
|
179
|
+
schemas: ['./openapi.json'],
|
|
180
|
+
reportCoverage: true,
|
|
181
|
+
exportCoverage: true,
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The coverage report shows:
|
|
186
|
+
- Total endpoints defined in your spec
|
|
187
|
+
- Number of endpoints tested
|
|
188
|
+
- List of untested endpoints
|
|
189
|
+
- Coverage percentage
|
|
190
|
+
|
|
191
|
+
## Supported HTTP Clients
|
|
192
|
+
|
|
193
|
+
The plugin automatically detects and parses responses from popular HTTP client libraries:
|
|
194
|
+
|
|
195
|
+
| Library | Support |
|
|
196
|
+
|---------|---------|
|
|
197
|
+
| [@japa/api-client](https://japa.dev/docs/plugins/api-client) | Full support |
|
|
198
|
+
| [Axios](https://axios-http.com/) | Full support |
|
|
199
|
+
| [Supertest](https://github.com/ladjs/supertest) | Full support |
|
|
200
|
+
|
|
201
|
+
### Response Format Detection
|
|
202
|
+
|
|
203
|
+
The plugin inspects the response object to determine which format to use:
|
|
204
|
+
|
|
205
|
+
- **Japa api-client**: Detected by `request` and `statusCode` properties
|
|
206
|
+
- **Axios**: Detected by `data`, `config`, and `status` properties
|
|
207
|
+
- **Supertest**: Detected by `body`, `req`, and `statusCode` properties
|
|
208
|
+
|
|
209
|
+
## Requirements
|
|
210
|
+
|
|
211
|
+
- Node.js >= 20.6.0
|
|
212
|
+
- OpenAPI 3.1.x specification in JSON format
|
|
213
|
+
|
|
214
|
+
## Technical Details
|
|
215
|
+
|
|
216
|
+
This package uses:
|
|
217
|
+
- [AJV](https://ajv.js.org/) with JSON Schema 2020-12 support for schema validation
|
|
218
|
+
- [ajv-formats](https://github.com/ajv-validator/ajv-formats) for format validation (email, uri, date-time, etc.)
|
|
219
|
+
- [@seriousme/openapi-schema-validator](https://github.com/seriousme/openapi-schema-validator) for OpenAPI document validation
|
|
220
|
+
|
|
221
|
+
All `$ref` pointers are resolved locally before validation, supporting references like `$ref: '#/components/schemas/Pet'`.
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT
|
|
226
|
+
|
|
227
|
+
## Contributing
|
|
228
|
+
|
|
229
|
+
Issues and pull requests are welcome at [github.com/drew-daniels/japa-openapi-assertions](https://github.com/drew-daniels/japa-openapi-assertions).
|
|
230
|
+
|
|
231
|
+
## Credits
|
|
232
|
+
|
|
233
|
+
This package is a fork of the original [@japa/openapi-assertions](https://japa.dev/docs/plugins/openapi-assertions) by the Japa team.
|
|
234
|
+
|
|
235
|
+
## Further Reading
|
|
236
|
+
|
|
237
|
+
- [OpenAPI 3.1 Migration Guide](https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0)
|
|
238
|
+
- [Upgrading from OpenAPI 3.0 to 3.1](https://learn.openapis.org/upgrading/v3.0-to-v3.1.html)
|
|
239
|
+
- [JSON Schema Draft 2020-12](https://json-schema.org/draft/2020-12/json-schema-core.html)
|
|
240
|
+
- [Japa Testing Framework](https://japa.dev/)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CoverageEntry } from './types.js';
|
|
2
|
+
interface CoverageRecord {
|
|
3
|
+
route: string;
|
|
4
|
+
method: string;
|
|
5
|
+
status: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Tracks API endpoint coverage during test runs.
|
|
9
|
+
*/
|
|
10
|
+
export declare class CoverageTracker {
|
|
11
|
+
private allEndpoints;
|
|
12
|
+
private covered;
|
|
13
|
+
private reportOnExit;
|
|
14
|
+
private exportOnExit;
|
|
15
|
+
private exitHandlerRegistered;
|
|
16
|
+
/**
|
|
17
|
+
* Register all endpoints from the OpenAPI spec for coverage tracking.
|
|
18
|
+
*/
|
|
19
|
+
registerEndpoints(entries: CoverageEntry[]): void;
|
|
20
|
+
/**
|
|
21
|
+
* Record that an endpoint was covered during testing.
|
|
22
|
+
*/
|
|
23
|
+
recordCoverage(route: string, method: string, status: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Get list of uncovered endpoints.
|
|
26
|
+
*/
|
|
27
|
+
getUncovered(): CoverageRecord[];
|
|
28
|
+
/**
|
|
29
|
+
* Get coverage statistics.
|
|
30
|
+
*/
|
|
31
|
+
getStats(): {
|
|
32
|
+
total: number;
|
|
33
|
+
covered: number;
|
|
34
|
+
percentage: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Enable reporting on process exit.
|
|
38
|
+
*/
|
|
39
|
+
enableReporting(report: boolean, exportFile: boolean): void;
|
|
40
|
+
/**
|
|
41
|
+
* Print coverage report to console.
|
|
42
|
+
*/
|
|
43
|
+
report(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Export coverage data to JSON file.
|
|
46
|
+
*/
|
|
47
|
+
export(filePath?: string): void;
|
|
48
|
+
private makeKey;
|
|
49
|
+
private registerExitHandler;
|
|
50
|
+
}
|
|
51
|
+
export declare const coverageTracker: CoverageTracker;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,qBAAqB,CAAiB;IAE9C;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI;IAYjD;;OAEG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAKnE;;OAEG;IACH,YAAY,IAAI,cAAc,EAAE;IAMhC;;OAEG;IACH,QAAQ,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAQlE;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI;IAS3D;;OAEG;IACH,MAAM,IAAI,IAAI;IAuCd;;OAEG;IACH,MAAM,CAAC,QAAQ,GAAE,MAAwB,GAAG,IAAI;IAYhD,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,mBAAmB;CAY5B;AAGD,eAAO,MAAM,eAAe,iBAAwB,CAAA"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
/**
|
|
4
|
+
* Tracks API endpoint coverage during test runs.
|
|
5
|
+
*/
|
|
6
|
+
export class CoverageTracker {
|
|
7
|
+
allEndpoints = [];
|
|
8
|
+
covered = new Set();
|
|
9
|
+
reportOnExit = false;
|
|
10
|
+
exportOnExit = false;
|
|
11
|
+
exitHandlerRegistered = false;
|
|
12
|
+
/**
|
|
13
|
+
* Register all endpoints from the OpenAPI spec for coverage tracking.
|
|
14
|
+
*/
|
|
15
|
+
registerEndpoints(entries) {
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
for (const status of entry.statuses) {
|
|
18
|
+
this.allEndpoints.push({
|
|
19
|
+
route: entry.route,
|
|
20
|
+
method: entry.method,
|
|
21
|
+
status,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Record that an endpoint was covered during testing.
|
|
28
|
+
*/
|
|
29
|
+
recordCoverage(route, method, status) {
|
|
30
|
+
const key = this.makeKey(route, method, status);
|
|
31
|
+
this.covered.add(key);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get list of uncovered endpoints.
|
|
35
|
+
*/
|
|
36
|
+
getUncovered() {
|
|
37
|
+
return this.allEndpoints.filter((e) => !this.covered.has(this.makeKey(e.route, e.method, e.status)));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get coverage statistics.
|
|
41
|
+
*/
|
|
42
|
+
getStats() {
|
|
43
|
+
const total = this.allEndpoints.length;
|
|
44
|
+
const covered = this.covered.size;
|
|
45
|
+
const percentage = total > 0 ? Math.round((covered / total) * 100) : 100;
|
|
46
|
+
return { total, covered, percentage };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Enable reporting on process exit.
|
|
50
|
+
*/
|
|
51
|
+
enableReporting(report, exportFile) {
|
|
52
|
+
this.reportOnExit = report;
|
|
53
|
+
this.exportOnExit = exportFile;
|
|
54
|
+
if ((report || exportFile) && !this.exitHandlerRegistered) {
|
|
55
|
+
this.registerExitHandler();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Print coverage report to console.
|
|
60
|
+
*/
|
|
61
|
+
report() {
|
|
62
|
+
const uncovered = this.getUncovered();
|
|
63
|
+
const stats = this.getStats();
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(chalk.bold('API Coverage Report'));
|
|
66
|
+
console.log(chalk.dim('─'.repeat(50)));
|
|
67
|
+
if (uncovered.length === 0) {
|
|
68
|
+
console.log(chalk.green('✓ All endpoints covered!'));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log(chalk.yellow(`Uncovered endpoints (${uncovered.length}):`));
|
|
72
|
+
console.log('');
|
|
73
|
+
// Group by route for cleaner output
|
|
74
|
+
const byRoute = new Map();
|
|
75
|
+
for (const endpoint of uncovered) {
|
|
76
|
+
const existing = byRoute.get(endpoint.route) || [];
|
|
77
|
+
existing.push(endpoint);
|
|
78
|
+
byRoute.set(endpoint.route, existing);
|
|
79
|
+
}
|
|
80
|
+
for (const [route, endpoints] of byRoute) {
|
|
81
|
+
console.log(chalk.dim(` ${route}`));
|
|
82
|
+
for (const ep of endpoints) {
|
|
83
|
+
console.log(` ${chalk.cyan(ep.method.padEnd(7))} ${chalk.dim(ep.status)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log(chalk.dim('─'.repeat(50)));
|
|
89
|
+
console.log(`Coverage: ${stats.covered}/${stats.total} endpoints `
|
|
90
|
+
+ `(${chalk.bold(stats.percentage + '%')})`);
|
|
91
|
+
console.log('');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Export coverage data to JSON file.
|
|
95
|
+
*/
|
|
96
|
+
export(filePath = 'coverage.json') {
|
|
97
|
+
const uncovered = this.getUncovered();
|
|
98
|
+
const data = uncovered.map((e) => ({
|
|
99
|
+
route: e.route,
|
|
100
|
+
method: e.method,
|
|
101
|
+
statuses: e.status,
|
|
102
|
+
}));
|
|
103
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
104
|
+
console.log(chalk.dim(`Coverage data exported to ${filePath}`));
|
|
105
|
+
}
|
|
106
|
+
makeKey(route, method, status) {
|
|
107
|
+
return `${method.toUpperCase()}:${route}:${status}`;
|
|
108
|
+
}
|
|
109
|
+
registerExitHandler() {
|
|
110
|
+
this.exitHandlerRegistered = true;
|
|
111
|
+
process.on('beforeExit', () => {
|
|
112
|
+
if (this.reportOnExit) {
|
|
113
|
+
this.report();
|
|
114
|
+
}
|
|
115
|
+
if (this.exportOnExit) {
|
|
116
|
+
this.export();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Singleton instance for global coverage tracking
|
|
122
|
+
export const coverageTracker = new CoverageTracker();
|
|
123
|
+
//# sourceMappingURL=coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"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"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PluginConfig } from './types.js';
|
|
2
|
+
export { OpenApiAssertions } from './openapi_assertions.js';
|
|
3
|
+
export type { PluginConfig } from './types.js';
|
|
4
|
+
declare module '@japa/assert' {
|
|
5
|
+
interface Assert {
|
|
6
|
+
isValidApiResponse(response: unknown): void;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* OpenAPI assertions plugin for Japa.
|
|
11
|
+
*
|
|
12
|
+
* This plugin validates HTTP responses against OpenAPI 3.0/3.1 specifications.
|
|
13
|
+
* It's a drop-in replacement for @japa/openapi-assertions with full OpenAPI 3.1 support.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { openapi } from '@drew-daniels/japa-openapi-assertions'
|
|
18
|
+
*
|
|
19
|
+
* export const plugins = [
|
|
20
|
+
* assert(),
|
|
21
|
+
* openapi({
|
|
22
|
+
* schemas: ['./openapi.json'],
|
|
23
|
+
* reportCoverage: true,
|
|
24
|
+
* }),
|
|
25
|
+
* apiClient(),
|
|
26
|
+
* ]
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @param options - Plugin configuration
|
|
30
|
+
* @returns Japa plugin function
|
|
31
|
+
*/
|
|
32
|
+
export declare function openapi(options: PluginConfig): () => void;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,MAAM;QACd,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAA;KAC5C;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,YAAY,cAc5C"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Assert } from '@japa/assert';
|
|
2
|
+
import { OpenApiAssertions } from './openapi_assertions.js';
|
|
3
|
+
// Re-export types and classes
|
|
4
|
+
export { OpenApiAssertions } from './openapi_assertions.js';
|
|
5
|
+
/**
|
|
6
|
+
* OpenAPI assertions plugin for Japa.
|
|
7
|
+
*
|
|
8
|
+
* This plugin validates HTTP responses against OpenAPI 3.0/3.1 specifications.
|
|
9
|
+
* It's a drop-in replacement for @japa/openapi-assertions with full OpenAPI 3.1 support.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { openapi } from '@drew-daniels/japa-openapi-assertions'
|
|
14
|
+
*
|
|
15
|
+
* export const plugins = [
|
|
16
|
+
* assert(),
|
|
17
|
+
* openapi({
|
|
18
|
+
* schemas: ['./openapi.json'],
|
|
19
|
+
* reportCoverage: true,
|
|
20
|
+
* }),
|
|
21
|
+
* apiClient(),
|
|
22
|
+
* ]
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param options - Plugin configuration
|
|
26
|
+
* @returns Japa plugin function
|
|
27
|
+
*/
|
|
28
|
+
export function openapi(options) {
|
|
29
|
+
// Register specs immediately (async loading happens in background)
|
|
30
|
+
OpenApiAssertions.registerSpecs(options.schemas, {
|
|
31
|
+
reportCoverage: options.reportCoverage,
|
|
32
|
+
exportCoverage: options.exportCoverage,
|
|
33
|
+
});
|
|
34
|
+
// Return the Japa plugin function
|
|
35
|
+
return function japaOpenapiPlugin() {
|
|
36
|
+
// Register the assertion macro
|
|
37
|
+
Assert.macro('isValidApiResponse', function (response) {
|
|
38
|
+
return new OpenApiAssertions().isValidResponse(response);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAG3D,8BAA8B;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAU3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,OAAO,CAAC,OAAqB;IAC3C,mEAAmE;IACnE,iBAAiB,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE;QAC/C,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC,CAAA;IAEF,kCAAkC;IAClC,OAAO,SAAS,iBAAiB;QAC/B,+BAA+B;QAC/B,MAAM,CAAC,KAAK,CAAC,oBAAoB,EAAE,UAAwB,QAAiB;YAC1E,OAAO,IAAI,iBAAiB,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAA;QAC1D,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
interface RegisterOptions {
|
|
2
|
+
reportCoverage?: boolean;
|
|
3
|
+
exportCoverage?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* OpenAPI assertions for validating HTTP responses against OpenAPI specifications.
|
|
7
|
+
*
|
|
8
|
+
* This is a drop-in replacement for @japa/openapi-assertions with OpenAPI 3.1 support.
|
|
9
|
+
*/
|
|
10
|
+
export declare class OpenApiAssertions {
|
|
11
|
+
/**
|
|
12
|
+
* Compiled validators from registered specs.
|
|
13
|
+
*/
|
|
14
|
+
private static validators;
|
|
15
|
+
/**
|
|
16
|
+
* Flag indicating if specs have been registered.
|
|
17
|
+
*/
|
|
18
|
+
private static hasRegistered;
|
|
19
|
+
/**
|
|
20
|
+
* Coverage options.
|
|
21
|
+
*/
|
|
22
|
+
private static coverageOptions;
|
|
23
|
+
/**
|
|
24
|
+
* Register OpenAPI specs to validate responses against.
|
|
25
|
+
* This method is SYNCHRONOUS to match the original @japa/openapi-assertions API.
|
|
26
|
+
*
|
|
27
|
+
* @param schemaPathsOrURLs - Paths or URLs to OpenAPI spec files
|
|
28
|
+
* @param options - Coverage reporting options
|
|
29
|
+
*/
|
|
30
|
+
static registerSpecs(schemaPathsOrURLs: (string | URL)[], options?: RegisterOptions): void;
|
|
31
|
+
/**
|
|
32
|
+
* Reset the assertions state (useful for testing).
|
|
33
|
+
*/
|
|
34
|
+
static reset(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get validators, throwing if not registered.
|
|
37
|
+
*/
|
|
38
|
+
private static getValidators;
|
|
39
|
+
/**
|
|
40
|
+
* Validate that a response matches the OpenAPI specification.
|
|
41
|
+
* This method is SYNCHRONOUS to match the original @japa/openapi-assertions API.
|
|
42
|
+
*
|
|
43
|
+
* @param response - HTTP response object from axios, supertest, or Japa api-client
|
|
44
|
+
* @throws AssertionError if the response does not match the spec
|
|
45
|
+
*/
|
|
46
|
+
isValidResponse(response: unknown): void;
|
|
47
|
+
}
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=openapi_assertions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi_assertions.d.ts","sourceRoot":"","sources":["../src/openapi_assertions.ts"],"names":[],"mappings":"AAQA,UAAU,eAAe;IACvB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkC;IAE3D;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa,CAAiB;IAE7C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe,CAAsB;IAEpD;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAClB,iBAAiB,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,EACnC,OAAO,GAAE,eAAoB,GAC5B,IAAI;IAuBP;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI,IAAI;IAMpB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAU5B;;;;;;OAMG;IACH,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;CAsDzC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { AssertionError } from 'node:assert';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { buildValidatorsSync } from './schema_builder.js';
|
|
4
|
+
import { validateResponse, parseResponse } from './response_validator.js';
|
|
5
|
+
import { matchPath } from './path_matcher.js';
|
|
6
|
+
import { coverageTracker } from './coverage.js';
|
|
7
|
+
/**
|
|
8
|
+
* OpenAPI assertions for validating HTTP responses against OpenAPI specifications.
|
|
9
|
+
*
|
|
10
|
+
* This is a drop-in replacement for @japa/openapi-assertions with OpenAPI 3.1 support.
|
|
11
|
+
*/
|
|
12
|
+
export class OpenApiAssertions {
|
|
13
|
+
/**
|
|
14
|
+
* Compiled validators from registered specs.
|
|
15
|
+
*/
|
|
16
|
+
static validators = null;
|
|
17
|
+
/**
|
|
18
|
+
* Flag indicating if specs have been registered.
|
|
19
|
+
*/
|
|
20
|
+
static hasRegistered = false;
|
|
21
|
+
/**
|
|
22
|
+
* Coverage options.
|
|
23
|
+
*/
|
|
24
|
+
static coverageOptions = {};
|
|
25
|
+
/**
|
|
26
|
+
* Register OpenAPI specs to validate responses against.
|
|
27
|
+
* This method is SYNCHRONOUS to match the original @japa/openapi-assertions API.
|
|
28
|
+
*
|
|
29
|
+
* @param schemaPathsOrURLs - Paths or URLs to OpenAPI spec files
|
|
30
|
+
* @param options - Coverage reporting options
|
|
31
|
+
*/
|
|
32
|
+
static registerSpecs(schemaPathsOrURLs, options = {}) {
|
|
33
|
+
this.hasRegistered = true;
|
|
34
|
+
this.coverageOptions = options;
|
|
35
|
+
// Convert URLs to file paths
|
|
36
|
+
const paths = schemaPathsOrURLs.map((p) => p instanceof URL ? fileURLToPath(p) : p);
|
|
37
|
+
// Build validators synchronously
|
|
38
|
+
const result = buildValidatorsSync(paths);
|
|
39
|
+
this.validators = result.validators;
|
|
40
|
+
// Register coverage entries
|
|
41
|
+
if (options.reportCoverage || options.exportCoverage) {
|
|
42
|
+
coverageTracker.registerEndpoints(result.coverageEntries);
|
|
43
|
+
coverageTracker.enableReporting(options.reportCoverage ?? false, options.exportCoverage ?? false);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Reset the assertions state (useful for testing).
|
|
48
|
+
*/
|
|
49
|
+
static reset() {
|
|
50
|
+
this.validators = null;
|
|
51
|
+
this.hasRegistered = false;
|
|
52
|
+
this.coverageOptions = {};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get validators, throwing if not registered.
|
|
56
|
+
*/
|
|
57
|
+
static getValidators() {
|
|
58
|
+
if (!this.validators) {
|
|
59
|
+
throw new Error('Cannot validate responses without defining API schemas. '
|
|
60
|
+
+ 'Please configure the plugin with schemas.');
|
|
61
|
+
}
|
|
62
|
+
return this.validators;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Validate that a response matches the OpenAPI specification.
|
|
66
|
+
* This method is SYNCHRONOUS to match the original @japa/openapi-assertions API.
|
|
67
|
+
*
|
|
68
|
+
* @param response - HTTP response object from axios, supertest, or Japa api-client
|
|
69
|
+
* @throws AssertionError if the response does not match the spec
|
|
70
|
+
*/
|
|
71
|
+
isValidResponse(response) {
|
|
72
|
+
if (!OpenApiAssertions.hasRegistered) {
|
|
73
|
+
throw new Error('Cannot validate responses without defining API schemas. '
|
|
74
|
+
+ 'Please configure the plugin with schemas.');
|
|
75
|
+
}
|
|
76
|
+
const validators = OpenApiAssertions.getValidators();
|
|
77
|
+
const result = validateResponse(response, validators);
|
|
78
|
+
// Record coverage if enabled
|
|
79
|
+
if (OpenApiAssertions.coverageOptions.reportCoverage
|
|
80
|
+
|| OpenApiAssertions.coverageOptions.exportCoverage) {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = parseResponse(response);
|
|
83
|
+
const specPaths = Object.keys(validators);
|
|
84
|
+
const pathMatch = matchPath(parsed.path, specPaths);
|
|
85
|
+
if (pathMatch) {
|
|
86
|
+
coverageTracker.recordCoverage(pathMatch.matched, parsed.method, String(parsed.status));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Ignore coverage tracking errors
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!result.valid) {
|
|
94
|
+
const errorDetails = result.errors
|
|
95
|
+
?.map((e) => {
|
|
96
|
+
let msg = e.path ? `${e.path}: ${e.message}` : e.message;
|
|
97
|
+
if (e.expected !== undefined) {
|
|
98
|
+
msg += ` (expected: ${JSON.stringify(e.expected)})`;
|
|
99
|
+
}
|
|
100
|
+
if (e.actual !== undefined) {
|
|
101
|
+
msg += ` (actual: ${JSON.stringify(e.actual)})`;
|
|
102
|
+
}
|
|
103
|
+
return msg;
|
|
104
|
+
})
|
|
105
|
+
.join('\n ');
|
|
106
|
+
throw new AssertionError({
|
|
107
|
+
message: `Response does not match API schema:\n ${errorDetails}`,
|
|
108
|
+
actual: result.errors,
|
|
109
|
+
expected: 'valid response matching OpenAPI schema',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=openapi_assertions.js.map
|