Skip to content

Commit

Permalink
add test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
helloyou2012 committed Mar 4, 2019
1 parent fdaf181 commit f189442
Show file tree
Hide file tree
Showing 8 changed files with 524 additions and 13 deletions.
7 changes: 4 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,13 @@ export default class Mapper {
// create route
const route = new Layer(path, methods, middlewares, {
...opts,
prefix: this.opts.prefix || '',
validator: this.validator,
end: opts.end === false ? opts.end : true,
sensitive: opts.sensitive || this.opts.sensitive || false,
strict: opts.strict || this.opts.strict || false,
prefix: this.opts.prefix || '',
validator: this.validator,
bodyparser: opts.bodyparser || this.opts.bodyparser
bodyparser: opts.bodyparser || this.opts.bodyparser,
throwParamsError: opts.throwParamsError || this.opts.throwParamsError
});

// add parameter middlewares
Expand Down
13 changes: 10 additions & 3 deletions src/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,16 @@ export default class Layer {
if (validate) {
const valid = validate(params);
ctx.paramsErrors = validate.errors;
if (!valid && opts.throwParamsError) {
const msgs = (ctx.paramsErrors || []).map(e => e.message);
ctx.throw(400, msgs.join('\n'));
if (!valid && opts.throwParamsError !== false) {
if (typeof opts.throwParamsError === 'function') {
opts.throwParamsError(ctx.paramsErrors);
} else {
const msgs = ctx.paramsErrors.map((e) => {
const key = e.dataPath.substring(1);
return `[${key}] ${e.message}`;
});
ctx.throw(400, msgs.join('\n'));
}
}
}
return params;
Expand Down
6 changes: 3 additions & 3 deletions src/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import File from 'formidable/lib/file';
import { assert, transformExtends, transformType, ref } from './utils';

const converts = {
'date': v => moment.utc(v, 'YYYY-MM-DD'),
'time': v => moment.utc(v, 'HH:mm:ssZ.SSS'),
'date-time': v => moment.utc(v)
'date': v => moment.utc(v, 'YYYY-MM-DD').toDate(),
'time': v => moment.utc(v, 'HH:mm:ssZ.SSS').toDate(),
'date-time': v => moment.utc(v).toDate()
};

export default class Validator {
Expand Down
6 changes: 3 additions & 3 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const should = chai.should();
describe('Mapper', () => {
it('creates new mapper with koa app', (done) => {
const mapper = new Mapper();
mapper.should.be.instanceOf(Mapper);
mapper.should.be.instanceof(Mapper);
done();
});

Expand Down Expand Up @@ -1035,7 +1035,7 @@ describe('Mapper', () => {
mapper.register.should.be.a('function');
mapper.register('/', ['GET', 'POST'], () => {});
app.use(mapper.routes());
mapper.stack.should.be.an.instanceOf(Array);
mapper.stack.should.be.an.instanceof(Array);
mapper.stack.should.have.property('length', 2);
mapper.stack[1].should.have.property('path', '/');
done();
Expand All @@ -1051,7 +1051,7 @@ describe('Mapper', () => {
mapper.redirect('/source', '/destination', 302);
app.use(mapper.routes());
mapper.stack.should.have.property('length', 2);
mapper.stack[1].should.be.instanceOf(Layer);
mapper.stack[1].should.be.instanceof(Layer);
mapper.stack[1].should.have.property('path', '/source');
done();
});
Expand Down
1 change: 0 additions & 1 deletion test/layer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ describe('Layer', () => {
ctx.body = ctx.user;
}]);
route.param('user', (id, ctx, next) => {
console.log('hello');
ctx.user = { name: 'alex' };
if (!id) {
ctx.status = 404;
Expand Down
207 changes: 207 additions & 0 deletions test/openapi.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/**
* Mapper tests
*/

import http from 'http';
import Koa from 'koa';
import qs from 'qs';
import request from 'supertest';
import { expect } from 'chai';
import Mapper from '../src/index';

describe('OpenAPI', () => {
describe('simple params', () => {
const app = new Koa();
const mapper = new Mapper();
mapper.get('/users/:id', {
name: 'user',
params: {
id: { type: 'number', schema: { minimum: 100 } },
type: { type: 'number', in: 'query' },
token: { type: 'string', in: 'header' },
sid: { type: 'string', in: 'cookie' }
}
}, (ctx) => {
ctx.body = ctx.params;
});
app.use(mapper.routes());
const client = request(http.createServer(app.callback()));

it('simple params: get params', (done) => {
client.get('/users/123?type=2')
.expect(200)
.end((err, res) => {
if (err) return done(err);
expect(res.body).to.have.property('id', 123);
expect(res.body).to.have.property('type', 2);
done();
});
});

it('simple params: get params from header and cookie', (done) => {
client.get('/users/123?type=2')
.set('token', 'hello')
.set('Cookie', ['sid=world'])
.expect(200)
.end((err, res) => {
if (err) return done(err);
expect(res.body).to.have.property('token', 'hello');
expect(res.body).to.have.property('sid', 'world');
done();
});
});

it('simple params: get params required error', (done) => {
client.get('/users/hello')
.expect(400)
.end((err, res) => {
if (err) return done(err);
expect(res.text).to.equal('[id] should be number');
done();
});
});

it('simple params: get params minimum error', (done) => {
client.get('/users/10')
.expect(400)
.end((err, res) => {
if (err) return done(err);
expect(res.text).to.equal('[id] should be >= 100');
done();
});
});

it('simple params: get params custom error', (done) => {
mapper.route('user').opts.throwParamsError = (errors) => {
expect(errors[0].message).to.equal('should be >= 100');
const err = new Error('custom error');
err.status = 400;
err.expose = true;
throw err;
};
client.get('/users/10')
.expect(400)
.end((err, res) => {
if (err) return done(err);
expect(res.text).to.equal('custom error');
done();
});
});
});

it('complex params', (done) => {
const app = new Koa();
const mapper = new Mapper();
mapper.get('/users/:id', {
name: 'user',
params: {
id: { type: 'number' },
info: { type: 'User', in: 'query' }
}
}, (ctx) => {
ctx.body = ctx.params;
});
mapper.define('Model', {
id: { type: 'number' }
});
mapper.define('User: Model', {
name: { type: 'string' },
roles: { type: 'array<string>' }
});
app.use(mapper.routes());
const info = {
id: 123,
name: 'test',
roles: ['hello', 'world']
};
request(http.createServer(app.callback()))
.get(`/users/123?${qs.stringify({ info })}`)
.expect(200)
.end((err, res) => {
if (err) return done(err);
expect(res.body).to.have.property('id', 123);
expect(res.body).to.have.deep.property('info', info);
done();
});
});

it('openapi json', (done) => {
const app = new Koa();
const mapper = new Mapper();
const info = {
title: 'users api',
version: '1.0.0'
};
const tags = [{
name: 'user',
description: 'user api'
}, {
name: 'simple',
description: 'simple api'
}];
const servers = [{
url: 'https://development.gigantic-server.com/v1',
description: 'Development server'
}, {
url: 'https://staging.gigantic-server.com/v1',
description: 'Staging server'
}, {
url: 'https://api.gigantic-server.com/v1',
description: 'Production server'
}];

mapper.info(info);
tags.forEach(tag => mapper.addTag(tag));
servers.forEach(server => mapper.addServer(server));

mapper.get('/users/:id', {
name: 'user',
tags: ['user'],
summary: 'get user infomation',
params: {
id: { type: 'number', schema: { minimum: 100 } },
type: { type: 'number', in: 'query' },
token: { type: 'string', in: 'header' },
sid: { type: 'string', in: 'cookie' }
}
}, (ctx) => {
ctx.body = ctx.params;
});
app.use(mapper.routes());
request(http.createServer(app.callback()))
.get('/openapi.json')
.expect(200)
.end((err, res) => {
if (err) return done(err);
expect(res.body).to.have.property('paths');
expect(res.body.paths).to.have.property('/openapi.json');
expect(res.body.paths).to.have.property('/users/{id}');
expect(res.body.info).to.deep.equal(info);
expect(res.body.tags).to.deep.equal(tags);
expect(res.body.servers).to.deep.equal(servers);
expect(res.body.paths['/users/{id}'].get).to.deep.equal({
parameters: [{
in: 'path',
name: 'id',
required: true,
schema: { minimum: 100, type: 'number' }
}, {
in: 'query',
name: 'type',
schema: { type: 'number' }
}, {
in: 'header',
name: 'token',
schema: { type: 'string' }
}, {
in: 'cookie',
name: 'sid',
schema: { type: 'string' }
}],
summary: 'get user infomation',
tags: ['user']
});
done();
});
});
});
100 changes: 100 additions & 0 deletions test/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { expect } from 'chai';
import * as utils from '../src/utils';

describe('utils', () => {
it('utils#toURI()', () => {
expect(utils.toURI('/hello')).to.equal('/hello');
expect(utils.toURI('/hello', null)).to.equal('/hello');
expect(utils.toURI('/hello', {})).to.equal('/hello');
expect(utils.toURI('/hello', 'a=b')).to.equal('/hello?a=b');
expect(utils.toURI('/hello', { a: 'b' })).to.equal('/hello?a=b');
expect(utils.toURI('/hello?test', 'a=b')).to.equal('/hello?test&a=b');
expect(utils.toURI('/hello?test', { a: 'b' })).to.equal('/hello?test&a=b');
});
it('utils#getMixType()', () => {
expect(utils.getMixType('file')).to.deep.equal({ type: 'object', file: true });
expect(utils.getMixType('date')).to.deep.equal({ type: 'string', format: 'date', convert: true });
expect(utils.getMixType('datetime')).to.deep.equal({ type: 'string', format: 'date-time', convert: true });
expect(utils.getMixType('number')).to.deep.equal({ type: 'number' });
expect(utils.getMixType('User')).to.deep.equal({ $ref: '#/components/schemas/User' });
expect(utils.getMixType('number|string')).to.deep.equal({
oneOf: [{ type: 'number' }, { type: 'string' }]
});
expect(utils.getMixType('number|User')).to.deep.equal({
oneOf: [{ type: 'number' }, { $ref: '#/components/schemas/User' }]
});
expect(utils.getMixType('Model|User')).to.deep.equal({
oneOf: [{ $ref: '#/components/schemas/Model' }, { $ref: '#/components/schemas/User' }]
});
expect(utils.getMixType('number&integer')).to.deep.equal({
allOf: [{ type: 'number' }, { type: 'integer' }]
});
expect(utils.getMixType('number&User')).to.deep.equal({
allOf: [{ type: 'number' }, { $ref: '#/components/schemas/User' }]
});
expect(utils.getMixType('Model&User')).to.deep.equal({
allOf: [{ $ref: '#/components/schemas/Model' }, { $ref: '#/components/schemas/User' }]
});
expect(() => utils.getMixType('number|string&date')).to.throw('& and | can only have one');
});
it('utils#transformExtends()', () => {
expect(utils.transformExtends('User')).to.deep.equal({
name: 'User',
parents: []
});
expect(utils.transformExtends('User:Model')).to.deep.equal({
name: 'User',
parents: [{ $ref: '#/components/schemas/Model' }]
});
expect(utils.transformExtends('User: Model, Test')).to.deep.equal({
name: 'User',
parents: [
{ $ref: '#/components/schemas/Model' },
{ $ref: '#/components/schemas/Test' }
]
});
});
it('utils#transformType()', () => {
expect(utils.transformType('')).to.deep.equal({});
expect(utils.transformType('number')).to.deep.equal({
type: 'number'
});
expect(utils.transformType('array')).to.deep.equal({
type: 'array'
});
expect(utils.transformType('array< >')).to.deep.equal({
type: 'array'
});
expect(utils.transformType('array<number>')).to.deep.equal({
type: 'array',
items: { type: 'number' }
});
expect(utils.transformType('array<number|string>')).to.deep.equal({
type: 'array',
items: {
oneOf: [{ type: 'number' }, { type: 'string' }]
}
});
expect(utils.transformType('array<User>')).to.deep.equal({
type: 'array',
items: {
$ref: '#/components/schemas/User'
}
});
});
it('utils#takeInOptions()', () => {
expect(utils.takeInOptions({
summary: 'test',
tags: 'test'
}, 'path')).to.deep.equal({ summary: 'test' });
expect(utils.takeInOptions({
summary: 'test',
tags: 'test'
}, 'method')).to.deep.equal({ summary: 'test', tags: 'test' });
expect(utils.takeInOptions({
summary: 'test',
tags: 'test',
test: 'hello'
}, 'method')).to.deep.equal({ summary: 'test', tags: 'test' });
});
});
Loading

0 comments on commit f189442

Please sign in to comment.