Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 0e236bb

Browse files
Merge pull request #221 from apiaryio/unified-api
Implements unified result structure
2 parents 96f4092 + 7c67e51 commit 0e236bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1015
-1628
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v8.12.0

CONTRIBUTING.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Contributing
2+
3+
Thank you for deciding to contribute to Gavel.js. Please read the guidelines below to ensure the smoothest developer's experience during the involvement.
4+
5+
## Workflow
6+
7+
1. Clone the repository.
8+
2. Install dependencies:
9+
10+
```bash
11+
npm install
12+
```
13+
14+
3. Use the correct NodeJS version:
15+
16+
```bash
17+
nvm use
18+
```
19+
20+
4. Create a branch for a feature or a bugfix.
21+
5. Follow the [Conventional Commit Messages](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#user-content--git-commit-guidelines) for commit messages.
22+
6. Issue a pull request and undergo a code review.
23+
7. Upon approval, merge the pull request.

README.md

Lines changed: 240 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,249 @@
1-
# Gavel.js — Validator of HTTP Transactions
1+
<p align="center">
2+
<a href="https://badge.fury.io/js/gavel" target="_blank">
3+
<img src="https://badge.fury.io/js/gavel.svg" alt="npm version" />
4+
</a>
5+
<a href="https://travis-ci.org/apiaryio/gavel.js" target="_blank">
6+
<img src="https://travis-ci.org/apiaryio/gavel.js.svg?branch=master" alt="Build Status" />
7+
</a>
8+
<a href="https://ci.appveyor.com/project/Apiary/gavel-js/branch/master" target="_blank">
9+
<img src="https://ci.appveyor.com/api/projects/status/0cpnaoakhs8q58tn/branch/master?svg=true" alt="Build Status" />
10+
</a>
11+
<a href="https://coveralls.io/r/apiaryio/gavel.js?branch=master" target="_blank">
12+
<img src="https://coveralls.io/repos/apiaryio/gavel.js/badge.svg?branch=master" alt="Coverage Status" />
13+
</a>
14+
<a href="https://snyk.io/test/npm/gavel" target="_blank">
15+
<img src="https://snyk.io/test/npm/gavel/badge.svg" alt="Known Vulnerabilities" />
16+
</a>
17+
</p>
218

3-
[![npm version](https://badge.fury.io/js/gavel.svg)](https://badge.fury.io/js/gavel)
4-
[![Build Status](https://travis-ci.org/apiaryio/gavel.js.svg?branch=master)](https://travis-ci.org/apiaryio/gavel.js)
5-
[![Build status](https://ci.appveyor.com/api/projects/status/0cpnaoakhs8q58tn/branch/master?svg=true)](https://ci.appveyor.com/project/Apiary/gavel-js/branch/master)
6-
[![Coverage Status](https://coveralls.io/repos/apiaryio/gavel.js/badge.svg?branch=master)](https://coveralls.io/r/apiaryio/gavel.js?branch=master)
7-
[![Known Vulnerabilities](https://snyk.io/test/npm/gavel/badge.svg)](https://snyk.io/test/npm/gavel)
19+
<br />
820

9-
![Gavel.js - Validator of HTTP Transactions](https://raw.github.com/apiaryio/gavel/master/img/gavel.png?v=1)
21+
<p align="center">
22+
<img src="https://raw.githubusercontent.com/apiaryio/gavel/master/img/gavel.png?v=1" alt="Gavel logo" />
23+
</p>
1024

11-
Gavel detects important differences between actual and expected HTTP transactions (HTTP request and response pairs). Gavel also decides whether the actual HTTP transaction is valid or not.
25+
<h1 align="center">Gavel</h1>
1226

13-
## Installation
27+
<p align="center">Gavel tells you whether an actual HTTP message is valid against an expected HTTP message.</p>
1428

15-
```sh
16-
$ npm install gavel
29+
## Install
30+
31+
```bash
32+
npm install gavel
33+
```
34+
35+
## Usage
36+
37+
### CLI
38+
39+
```bash
40+
# (Optional) Record HTTP messages
41+
curl -s --trace - http://httpbin.org/ip | curl-trace-parser > expected
42+
curl -s --trace - http://httpbin.org/ip | curl-trace-parser > actual
43+
44+
# Perform the validation
45+
cat actual | gavel expected
46+
```
47+
48+
> **Gavel CLI is not supported on Windows**. Example above uses [`curl-trace-parser`](https://github.com/apiaryio/curl-trace-parser).
49+
50+
### NodeJS
51+
52+
```js
53+
const gavel = require('gavel');
54+
55+
// Define HTTP messages
56+
const expected = {
57+
statusCode: 200,
58+
headers: {
59+
'Content-Type': 'application/json'
60+
}
61+
};
62+
63+
const actual = {
64+
statusCode: 404,
65+
headers: {
66+
'Content-Type': 'application/json'
67+
}
68+
};
69+
70+
// Perform the validation
71+
const result = gavel.validate(expected, actual);
72+
```
73+
74+
The code above would return the following validation `result`:
75+
76+
```js
77+
{
78+
valid: false,
79+
fields: {
80+
statusCode: {
81+
valid: false,
82+
kind: 'text',
83+
values: {
84+
expected: '200',
85+
actual: '404'
86+
},
87+
errors: [
88+
{
89+
message: `Expected status code '200', but got '404'.`,
90+
values: {
91+
expected: '200',
92+
actual: '404'
93+
}
94+
}
95+
]
96+
},
97+
headers: {
98+
valid: true,
99+
kind: 'json',
100+
values: {
101+
expected: {
102+
'Content-Type': 'application/json'
103+
},
104+
actual: {
105+
'Content-Type': 'application/json'
106+
}
107+
},
108+
errors: []
109+
}
110+
}
111+
}
112+
```
113+
114+
### Usage with JSON Schema
115+
116+
> When a parsable JSON body is expected without an explicit schema the [default schema](https://github.com/apiaryio/gavel-spec/blob/master/features/expectations/bodyJsonExample.feature) is inferred.
117+
118+
You can describe the body expectations using [JSON Schema](https://json-schema.org/) by providing a valid schema to the `bodySchema` property of the expected HTTP message:
119+
120+
```js
121+
const gavel = require('gavel');
122+
123+
const expected = {
124+
bodySchema: {
125+
type: 'object',
126+
properties: {
127+
fruits: {
128+
type: 'array',
129+
items: {
130+
type: 'string'
131+
}
132+
}
133+
}
134+
}
135+
};
136+
137+
const actual = {
138+
body: JSON.stringify({
139+
fruits: ['apple', 'banana', 2]
140+
})
141+
};
142+
143+
const result = gavel.validate(expected, actual);
144+
```
145+
146+
The validation `result` against the given JSON Schema will look as follows:
147+
148+
```js
149+
{
150+
valid: false,
151+
fields: {
152+
body: {
153+
valid: false,
154+
kind: 'json',
155+
values: {
156+
actual: "{\"fruits\":[\"apple\",\"banana\",2]}"
157+
},
158+
errors: [
159+
{
160+
message: `At '/fruits/2' Invalid type: number (expected string)`,
161+
location: {
162+
pointer: '/fruits/2'
163+
}
164+
}
165+
]
166+
}
167+
}
168+
}
169+
```
170+
171+
> Note that JSON schema Draft-05+ are not currently supported. [Follow the support progress](https://github.com/apiaryio/gavel.js/issues/90).
172+
173+
## Examples
174+
175+
Take a look at the [Gherkin](https://cucumber.io/docs/gherkin/) specification, which describes on examples how validation of each field behaves:
176+
177+
- [`method`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/method)
178+
- [`statusCode`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/statusCode)
179+
- [`headers`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/headers)
180+
- [`body`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/body)
181+
- [`bodySchema`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/bodySchema)
182+
183+
## Type definitions
184+
185+
Type definitions below are described using [TypeScript](https://www.typescriptlang.org/) syntax.
186+
187+
### Input
188+
189+
> Gavel makes no assumptions over the validity of a given HTTP message according to the HTTP specification (RFCs [2616](https://www.ietf.org/rfc/rfc2616.txt), [7540](https://httpwg.org/specs/rfc7540.html)) and will accept any input matching the input type definition. Gavel will throw an exception when given malformed input data.
190+
191+
Both expected and actual HTTP messages (no matter request or response) inherit from a single `HttpMessage` interface:
192+
193+
```ts
194+
interface HttpMessage {
195+
method?: string;
196+
statusCode?: number;
197+
headers?: Record<string> | string;
198+
body?: string;
199+
bodySchema?: Object | string;
200+
}
17201
```
18202

19-
## Documentation
203+
### Output
204+
205+
```ts
206+
// Field kind describes the type of a field's values
207+
// subjected to the end comparison.
208+
enum ValidationKind {
209+
null // non-comparable data (validation didn't happen)
210+
text // compared as text
211+
json // compared as JSON
212+
}
213+
214+
interface ValidationResult {
215+
valid: boolean // validity of the actual message
216+
fields: {
217+
[fieldName: string]: {
218+
valid: boolean // validity of a single field
219+
kind: ValidationKind
220+
values: { // end compared values (coerced, normalized)
221+
actual: any
222+
expected: any
223+
}
224+
errors: FieldError[]
225+
}
226+
}
227+
}
228+
229+
interface FieldError {
230+
message: string
231+
location?: { // kind-specific additional information
232+
// kind: json
233+
pointer?: string
234+
property?: string[]
235+
}
236+
values?: {
237+
expected: any
238+
actual: any
239+
}
240+
}
241+
```
242+
243+
## API
244+
245+
- `validate(expected: HttpMessage, actual: HttpMessage): ValidationResult`
20246

21-
Gavel.js is a JavaScript implementation of the [Gavel behavior specification](https://www.relishapp.com/apiary/gavel/) ([repository](https://github.com/apiaryio/gavel-spec)):
247+
## License
22248

23-
- [Gavel.js-specific documentation](https://www.relishapp.com/apiary/gavel/docs/javascript/)
24-
- [CLI documentation](https://www.relishapp.com/apiary/gavel/docs/command-line-interface/)
249+
[MIT](LICENSE)

bin/gavel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ process.stdin.on('end', function() {
3333
const requestResult = gavel.validate(expectedRequest, realRequest);
3434
const responseResult = gavel.validate(expectedResponse, realResponse);
3535

36-
if (requestResult.isValid && responseResult.isValid) {
36+
if (requestResult.valid && responseResult.valid) {
3737
process.exit(0);
3838
} else {
3939
process.exit(1);

lib/units/isValid.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ function isValidField({ errors }) {
1010

1111
/**
1212
* Returns a boolean indicating the given validation result as valid.
13-
* @param {Object<string, any>} validationResult
13+
* @param {Object<string, any>} fields
1414
* @returns {boolean}
1515
*/
16-
function isValidResult(validationResult) {
17-
return Object.values(validationResult.fields).every(isValidField);
16+
function isValidResult(fields) {
17+
return Object.values(fields).every(isValidField);
1818
}
1919

2020
module.exports = {

0 commit comments

Comments
 (0)