cachecontrol / hippie-swagger Goto Github PK
View Code? Open in Web Editor NEWAPI testing tool with automatic swagger assertions
License: ISC License
API testing tool with automatic swagger assertions
License: ISC License
hippie-swagger doesn't support swagger basePath.
I specified in my swagger file a basePath set to "/api/v1" and one path called "/users".
The following test doesn't work because hippie-swagger implementation doesn't interpret the swagger basePath when computing the REST service url:
it('works when server refuse content-type', function (done) {
hippie(app, swagger, options)
.post('/api/v1/users')
.send(JSON.parse(fs.readFileSync('user.json')))
.expectStatus(201)
.end(function (err, res, body) {
if (err) throw err;
done();
})
});
Given Swagger:
swagger: "2.0"
info:
version: ""
title: test
schemes:
- http
paths:
/fragment:
get:
produces:
- text/html
description: returns some html
responses:
200:
description: text/html response
schema:
type: string
HippieSwagger 3.2.0 chokes on the response, as it assumes all responses to always be JSON - but can't parse HTML as JSON. Can the JSON-parsing be disabled somehow?
Specification for Swagger 2.0 concerning mime-types in requests and responses:
https://swagger.io/docs/specification/2-0/mime-types/
var swaggerJson = require('swagger-pet-store.json'); // from http://petstore.swagger.io/v2/swagger.json
it('pet store order test', (done) => {
hippie(require(swaggerJson)
.get('http://petstore.swagger.io/v2/store/order/{orderId}')
.pathParams({
orderId: 2
})
.expectStatus(StatusCodes.Ok)
.end(function (err, res, body) {
if (err) throw err;
done();
});
});
// Throws: "Uncaught Error: can't resolve reference #/definitions/Order from id #"
Hi!
When processing an example swagger definition hippie-swagger complains of an undefined path that is defined:
'use strict'
var SwaggerParser = require('swagger-parser')
var parser = new SwaggerParser()
var hippie = require('hippie-swagger')
var expect = require('chai').expect
var path = require('path')
var dereferencedSwagger
describe('Example of', function () {
before(function (done) {
parser.dereference('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/petstore.json', function (err, api) {
if (err) return done(err)
dereferencedSwagger = api
done()
})
})
describe('correct usage', function () {
it('should allow to post to /pets', function (done) {
hippie(dereferencedSwagger)
.json()
.post('/pets')
.expectStatus(200)
.end(done)
})
})
})
The answer I receive is:
$ mocha index.js
Example of
correct usage
1) should allow to post to /pets
0 passing (225ms)
1 failing
1) Example of correct usage should allow to post to /pets:
Error: Swagger spec does not define path: /pets
at Client.middleware (node_modules/hippie-swagger/lib/middleware.js:15:13)
at next (node_modules/hippie/lib/hippie/client.js:456:5)
at Client.setup (node_modules/hippie/lib/hippie/client.js:457:5)
at Client.prepare (node_modules/hippie/lib/hippie/client.js:364:17)
at Client.end (node_modules/hippie/lib/hippie/client.js:382:24)
at Context.<anonymous> (index.js:32:8)
By the way, thanks for this awesome project!
hippie-swagger chokes when using recursive or circular references.
Versions:
node 7.4.0 or 6.9.2
hippie-swagger 2.1.0
ajv 4.11.2
json-stable-stringify 1.0.1
definition:
swagger: '2.0'
info:
description: navigation
basePath: /
produces:
- application/json
paths:
/v1/Navigation/getNavigationByIso/de-DE:
get:
description: gets the navigation tree by iso code
responses:
200:
description: OK
schema:
$ref: '#/definitions/navigation'
definitions:
navigation:
description: complete navigation tree
type: object
properties:
nodes:
type: array
items:
$ref: '#/definitions/navigationNode'
required:
- nodes
navigationNode:
description: a single navigation node including its children
type: object
properties:
subnodes:
description: zero or more child-nodes of this navigation node
type: array
items:
$ref: '#/definitions/navigationNode'
required:
- subnodes
test-code:
'use strict';
const SwaggerParser = require('swagger-parser');
const parser = new SwaggerParser();
const hippie = require('hippie-swagger');
const app = require('../../server/server.js');
let dereferencedSwagger;
const swaggerFile = 'swagger/navigation.yaml';
describe('Test of Navigation.yaml', function () {
before(function (done) {
parser.dereference(swaggerFile, function (err, api) {
if (err) return done(err);
dereferencedSwagger = api;
done();
});
});
describe('get HTTP-Statuscode 200', function () {
it('works when the request matches the swagger file', function (done) {
hippie(app, dereferencedSwagger)
.get('/v1/Navigation/getNavigationByIso/de-DE')
.expectStatus(200)
.end(done);
})
});
});
result:
1) Test of Navigation.yaml get HTTP-Statuscode 200 works when the request matches the swagger file:
TypeError: Converting circular structure to JSON
at stringify (node_modules/json-stable-stringify/index.js:49:23)
at stringify (node_modules/json-stable-stringify/index.js:57:29)
at stringify (node_modules/json-stable-stringify/index.js:57:29)
at stringify (node_modules/json-stable-stringify/index.js:57:29)
at stringify (node_modules/json-stable-stringify/index.js:57:29)
at stringify (node_modules/json-stable-stringify/index.js:57:29)
at stringify (node_modules/json-stable-stringify/index.js:57:29)
at module.exports (node_modules/json-stable-stringify/index.js:70:7)
at _addSchema (node_modules/hippie-swagger/node_modules/ajv/lib/ajv.js:276:19)
at Ajv.validate (node_modules/hippie-swagger/node_modules/ajv/lib/ajv.js:94:23)
at validateBody (node_modules/hippie-swagger/lib/response.js:20:14)
at Client.response (node_modules/hippie-swagger/lib/response.js:45:5)
at verify (node_modules/hippie/lib/hippie/client.js:476:5)
at statusCode (node_modules/hippie/lib/hippie/expect.js:23:5)
at verify (node_modules/hippie/lib/hippie/client.js:476:5)
at Client.verify (node_modules/hippie/lib/hippie/client.js:477:5)
at node_modules/hippie/lib/hippie/client.js:437:12
at Client.exports.json [as parse] (node_modules/hippie/lib/hippie/parsers.js:21:3)
at Request._callback (node_modules/hippie/lib/hippie/client.js:435:10)
at Request.self.callback (node_modules/hippie/node_modules/request/request.js:187:22)
at Request.<anonymous> (node_modules/hippie/node_modules/request/request.js:1044:10)
at IncomingMessage.<anonymous> (node_modules/hippie/node_modules/request/request.js:965:12)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickDomainCallback (internal/process/next_tick.js:122:9)
Documentation for SwaggerParser suggests to use "bundle" instead of "dereference", but hippie-swagger will not work if/when references are not fully resolved (not visible in the example above, but then hippie-swagger will not recognize path-params when using references for them).
Sorry for this basic question. How do you run the example?
Thanks for helping this newbie.
Hi,
Is there any current effort to update the Hoek dependency package?
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Moderate │ Prototype pollution │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ hoek │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in │ > 4.2.0 < 5.0.0 || >= 5.0.3 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ hippie-swagger [dev] │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ hippie-swagger > hippie > npm > npm-lifecycle > node-gyp > │
│ │ request > hawk > boom > hoek │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://nodesecurity.io/advisories/566 │
└───────────────┴──────────────────────────────────────────────────────────────┘
According to the specs the swagger
key has been replaced by openapi
https://swagger.io/specification/#schema
The line below is causing the issue.
hippie-swagger/lib/validate-swagger.js
Line 24 in fd60e34
One way of fixing this (not the most elegant) could be
function isSwagger (obj) {
var requiredKeys = [['openapi', 'swagger'], 'info', 'paths']
return requiredKeys.every(function (key) {
if (Array.isArray(key)) {
return key.some(function (alias) {
return obj.hasOwnProperty(alias)
})
}
return obj.hasOwnProperty(key)
})
}
I went ahead and proposed a pull request of that very changes #35
Noticed Hippie keeps trying to parse responses as JSON when I'm expecting a string back. How can I prevent Hippie from assuming the response is JSON?
My swagger.yml is using OpenAPI 3.0, however your example api.swagger.json is using 2.0, wonder if this has anything to do with it (i.e produces:
became content:
).
Test Code:
const app = require('../app');
const _ = require('lodash');
const { expect } = require('chai');
const request = require('supertest');
const SwaggerParser = require('swagger-parser');
const hippie = require('hippie-swagger');
const path = require('path');
const parser = new SwaggerParser();
const URLSafeBase64 = require('urlsafe-base64');
let dereferencedSwagger;
parser.dereference(path.join(__dirname, '../swagger/swagger.yml'), (err, api) => {
this.dereferencedSwagger = api;
if (err) throw err;
dereferencedSwagger = api;
return run();
});
…
describe('AVATAR SAVE', () => {
it('Should return 200 when provided empty avatar', (done) => {
//request(app)
let saveOptions = {
//validateResponseSchema: false,
// validateParameterSchema: false,
// errorOnExtraParameters: false,
// errorOnExtraHeaderParameters: false
};
hippie(app, dereferencedSwagger, saveOptions)
/* .parser(function(body, fn){
fn(null, 'ok');
}) */
.put('/api/v1/user/avatar')
.header('Authorization', `Bearer ${this.jwt.token}`)
.expectStatus(200)
.end((err, res) => {
console.log(res.body);
if (err) throw err;
done();
});
});
Output:
SAVING AND ACCESSING AVATAR
AVATAR SAVE
Should return 200 when provided empty avatar:
Uncaught SyntaxError: Unexpected token o in JSON at position 0
at JSON.parse (<anonymous>)
at Client.exports.json [as parse] (node_modules\hippie\lib\hippie\parsers.js:15:18)
at Request._callback (node_modules\hippie\lib\hippie\client.js:435:10)
at Request.self.callback (node_modules\request\request.js:185:22)
at Request.<anonymous> (node_modules\request\request.js:1157:10)
at IncomingMessage.<anonymous> (node_modules\request\request.js:1079:12)
at endReadableNT (_stream_readable.js:1081:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
Output with .parser(function(body, fn){ fn(null, 'ok'); })
uncommented:
SAVING AND ACCESSING AVATAR
AVATAR SAVE
Should return 200 when provided empty avatar:
Uncaught Error: Received non-empty response from /api/v1/user/avatar. Expected empty response body because no "schema" property was specified in swagger path.
at validateBody (node_modules\hippie-swagger\lib\response.js:27:11)
at Client.response (node_modules\hippie-swagger\lib\response.js:46:5)
at verify (node_modules\hippie\lib\hippie\client.js:476:5)
at statusCode (node_modules\hippie\lib\hippie\expect.js:23:5)
at verify (node_modules\hippie\lib\hippie\client.js:476:5)
at Client.verify (node_modules\hippie\lib\hippie\client.js:477:5)
at ..\node_modules\hippie\lib\hippie\client.js:437:12
at Client.parse (test\avatar.spec.js:212:13)
at Request._callback (node_modules\hippie\lib\hippie\client.js:435:10)
at Request.self.callback (node_modules\request\request.js:185:22)
at Request.<anonymous> (node_modules\request\request.js:1157:10)
at IncomingMessage.<anonymous> (node_modules\request\request.js:1079:12)
at endReadableNT (_stream_readable.js:1081:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
Swagger definition:
/api/v1/user/avatar:
put:
summary: Upsert the users avatar
tags:
- user
requestBody:
content:
application/json:
schema:
type: object
properties:
encodedAvatar:
$ref: '#/components/schemas/EncodedAvatar'
responses:
'200':
description: OK
content:
text/html:
schema:
type: string
Affected Versions: >= 3.1.0
HippieSwagger 3.1.0 brings an updated version of ajv, which in turn no longer accepts all "format"-values for datatypes.
Excerpt from a valid Swagger-file:
price:
type: number
format: float
description: Actual Price with Reduction included
x-example: 19.99
According to SwaggerSpecification, the beforementioned excerpt is valid (see https://swagger.io/specification/#data-types-13).
Specifically
OAS uses several known formats to define in fine detail the data type being used. However, to support documentation needs, the format property is an open string-valued property, and can have any value. Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification.
(emphasize mine)
Expected behaviour:
This is valid.
Actual behaviour:
Uncaught Error: unknown format "float" is used in schema at path "#/properties/price"
Background:
with HippieSwagger 3.0.0 an prior, the "bundled" ajv raised these as a warning.
Starting with 3.1.0 of HippieSwagger, ajv raises these as an error.
Solution:
either
Hippie swagger only supports Swagger 2.0
Related #37
Hi there, I'm getting the TypeError: Cannot read property 'hasOwnProperty' of null
error when calling parser.dereference().
I'm 'pretty sure' the file path is correct and its a valid swagger file - I can view it in the online swagger editor.
Plus the code in the library would throw an Error is no file was passed in (I tested this by passing a bogus file and it threw the Error opening file and another ENOENT error 'no such file or directory).
Seems that this library needs an object of the server/application to be passed to hippie(...)
function.
I'm able to use hippie-swagger
to create automated tests for an remote deployed API? If I can, how I can I do it?
Because as far I have analyzed the app
argument is highly coupled in to the code:
https://github.com/CacheControl/hippie-swagger/blob/master/lib/hippie-swagger.js#L24
Is there some reason that in order to use hippie swagger we need to install hippie separately?
ie I need to do both:
npm install hippie-swagger --save-dev
npm install hippie --save-dev
Why not just have it as a dependency?
Happy to fix it if someone agrees with me :)
As an engineer I expect the validateParamaterSchema
option to apply to required parameters as well.
Use case: I am testing an endpoint with 2 required query string parameters. I want to test that we do indeed get a bad response (non-200) due to bad parameters however, the test fails despite expectStatus(4xx)
with 'Missing required parameter in ...
error message. I expected that the option to bypass that check as it does for Invalid format for parameter ...
.
Solution?: Update docs or add an extra setting for the use case above.
Use Case:
In our Swagger Docs, we have an example of response 400
: invalid data. I'm unable to test this with Hippie-Swagger since sending invalid (or missing) data results in:
Error: Invalid format for parameter {body}, received: undefined. errors:data.thing should have required property 'name'
I'm investigating hippie-swagger for building out a backend automation framework. I'm trying to pass in the URL for an API, but get the error app.listen is not a function
.
Here's my code
const hippie = require('hippie-swagger');
const path = require('path');
const SwaggerParser = require('swagger-parser');
const baseApi = 'http://api.local:4000'
let dereferencedSwagger;
const swaggerPath = '../resources/payments.swagger.json';
function parseSwaggerDoc(swaggerDoc) {
const swaggerPath = path.join(__dirname, swaggerDoc);
return SwaggerParser.dereference(swaggerPath);
}
describe('Payments API PoC', () => {
before(async () => {
dereferencedSwagger = parseSwaggerDoc(swaggerPath);
})
describe('Payments Swagger', () => {
it('works when the request matches the swagger file', done => {
hippie(baseApi, dereferencedSwagger)
.get('/')
.pathParams({
tagId: 1
})
.expectStatus(200)
.end(done)
})
})
})
Test output:
Payments API PoC
Payments Swagger
1) works when the request matches the swagger file
0 passing (38ms)
1 failing
1) Payments API PoC
Payments Swagger
works when the request matches the swagger file:
TypeError: app.listen is not a function
at Client.app (node_modules/hippie/lib/hippie/client.js:412:30)
at new Client (node_modules/hippie/lib/hippie/client.js:39:17)
at Client (node_modules/hippie/lib/hippie/client.js:26:41)
at module.exports (node_modules/hippie-swagger/lib/hippie-swagger.js:34:13)
at Context.done (tests/payments_swagger.js:18:7)
hippie-swagger
doesn't validate query parameters from the given url.
'use strict';
var express = require('express');
var hippie = require('hippie-swagger');
var spec = {
'swagger': '2.0',
'info': {
'version': '1.0.0',
'title': 'Test App'
},
'paths': {
'/': {
'get': {
'description': 'description',
'parameters': [{
'name': 'x',
'in': 'query',
'required': true,
'type': 'number'
}]
}
}
}
};
var app = express();
app.get('/', function (req, res) {
res.end();
});
hippie(app, spec)
.get('/?x=1')
.expectStatus(200)
.end(function (err) {
if (err) throw err;
});
$ node test.js
/home/kostya/proj/swag/node_modules/hippie-swagger/lib/parameters.js:41
throw new Error('Missing required parameter in ' + param.in + ': ' + param.name)
^
Error: Missing required parameter in query: x
at throwMissingRequiredParameter (/home/kostya/proj/swag/node_modules/hippie-swagger/lib/parameters.js:41:9)
at validateRequiredParams (/home/kostya/proj/swag/node_modules/hippie-swagger/lib/parameters.js:73:9)
at /home/kostya/proj/swag/node_modules/hippie-swagger/lib/parameters.js:113:5
at Array.forEach (native)
at parameters (/home/kostya/proj/swag/node_modules/hippie-swagger/lib/parameters.js:112:23)
at Client.middleware (/home/kostya/proj/swag/node_modules/hippie-swagger/lib/middleware.js:37:13)
at next (/home/kostya/proj/swag/node_modules/hippie/lib/hippie/client.js:443:5)
at Client.setup (/home/kostya/proj/swag/node_modules/hippie/lib/hippie/client.js:444:5)
at Client.prepare (/home/kostya/proj/swag/node_modules/hippie/lib/hippie/client.js:351:17)
at Client.end (/home/kostya/proj/swag/node_modules/hippie/lib/hippie/client.js:369:24)
Validate query parameters x
from url.
It is possible for swagger to define common parameters (relative to a path) underneath the path definition and not just the operation, for example:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Test App"
},
"paths": {
"/foos/{fooId}": {
"parameters": [{
"name": "fooId",
"in": "path",
"description": "foo identifier",
"required": true,
"type": "string",
"pattern": "^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$"
}],
"get": {
"description": "Retrieving a foo",
"parameters": [{
"name": "fooId",
"in": "path",
"description": "foo identifier (duplicated)",
"required": true,
"type": "string",
"pattern": "^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$"
}],
"responses": {
"200": {
"description": "Successful response",
"schema": {
"$ref": "#/definitions/foo"
}
}
}
},
"post": {
"description": "Upload file example",
"consumes": [
"multipart/form-data"
],
"responses": {
"201": {
"description": "noop"
}
},
"parameters": [{
"in": "formData",
"name": "uploadedFile",
"type": "file",
"description": "file upload",
"required": false
}]
},
"delete": {
"description": "Deleting a foo",
"parameters": [],
"responses": {
"204": {
"description": "Deleted. No content"
}
}
}
}
}
}
The example above should share the following parameter with all requests in that path - i.e. the post
and get
operations should get the fooId
parameter if they are not defined.
{
"name": "fooId",
"in": "path",
"description": "foo identifier",
"required": true,
"type": "string",
"pattern": "^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$"
}
I have a P/R on the way to fix this with tests but wanted an issue to cross reference with for anyone searching in issues.
I need to upload files to a swagger api using a hippie-swagger test as a form, i did not see any coverage.. what could I be missing..?
I noticed this in the code as well
/**
* Asserts that request form data variables are in Swagger. Throws if not found.
* @param {Object} requestFormData - request form data(query string format) as a hash
* @param {Object} requestParams - hash of request parameters
*/
function handleFormData (requestFormData, specFormData) {
for (var qsKey in requestFormData) {
var found = false
specFormData.forEach(function (formSpecParameter) {
if (formSpecParameter.name === qsKey && formSpecParameter.type !== 'file') {
found = true
}
})
if (!found) {
throwParamNotFound(qsKey, 'formData')
}
}
}
I'd like to have a central (common) instance of the hippieSwaggerApp()
function, to make it cleaner for each of my tests to call it. As a workaround, within each Test Suite, I've duplicated the function:
import hippie from "hippie-swagger";
import { username, password } from "common/resources/httpAuth.json";
describe("Test Swagger examples for Foo", () => {
let dereferencedSwagger;
function myApp() {
return hippie(dereferencedSwagger).base(baseUrl).auth(username, password).json();
}
before(async () => {
dereferencedSwagger = await parseSwaggerDoc(mySwaggerDoc);
});
context("<Foo>", () => {
it("Returns 200, when sending a valid request", async () => {
await myApp()
.header("Auth-Type", "auth")
.header("Auth-Token", authToken)
.get(`my/route/{id}`)
.pathParams({
id: my.id,
})
.expectStatus(200)
.end();
});
However, it would be really nice to have a setup script which parses the Swagger Doc (as we have multiple APIs), and writes the dereferenced contents to a temp file. Allowing for:
// common/path/commonHippieApp.js
import hippie from "hippie-swagger";
import { username, password } from "common/resources/httpAuth.json";
export const commonHippieApp = (pathToSwaggerDoc, baseUrl) => {
return hippie(pathToSwaggerDoc).base(baseUrl).auth(username, password).json();
}
// service/path/myHippieApp.js
import commonHippieApp from "common/path/commonHippieApp";
const swaggerPath = "path/to/dereferencedSwagger.json"
export const myHippieApp = () => {
return commonHippieApp(swaggerPath, "http://some.base/uri");
}
// tests.spec.js
import myHippieApp from "service/path/myHippieApp";
describe("Test Swagger examples for Foo", () => {
context("<Foo>", () => {
it("Returns 200, when sending a valid request", async () => {
await myHippieApp()
.header("Auth-Type", "auth")
.header("Auth-Token", authToken)
.get(`my/route/{id}`)
.pathParams({
id: my.id,
})
.expectStatus(200)
.end();
});
Similarly with Hippie, there's a large number of security vulnerabilities with hippie-swagger (110). I'm unable to resolve these using npm audit fix
.
npm audit fix
up to date in 1.95s
fixed 0 of 110 vulnerabilities in 12488 scanned packages
110 vulnerabilities required manual review and could not be updated
Although these are low, it would be great to try and lower this number
Affected version: HippieSwagger 2.1.0
Specification:
parameters:
quantity:
name: quantity
in: formData
type: integer
format: int32
required: true
Code/Request:
hippieSwagger(app, dereferencedSwagger)
.form()
.post('/url')
.send({
quantity: 1
})
.expectStatus(200)
Result:
Error: Invalid format for parameter {quantity}, received: 1. errors:data should be integer
As far as i have seen, this happens because hippie-swagger decomposes the query-string for validating the parameters - meaning 1 (integer) has changed to "1" (string), thus failing in ajv.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.