Skip to content

Commit

Permalink
fix(rest): add tests for request validation per media type
Browse files Browse the repository at this point in the history
See #1494
  • Loading branch information
raymondfeng committed Nov 26, 2018
1 parent 5187f9b commit 630d392
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 8 deletions.
64 changes: 62 additions & 2 deletions packages/rest/test/acceptance/validation/validation.acceptance.ts
Expand Up @@ -42,8 +42,14 @@ describe('Validation at REST level', () => {

const PRODUCT_SPEC = jsonToSchemaObject(getJsonSchema(Product));

// Add a schema that requires `description`
const PRODUCT_SPEC_WITH_DESCRIPTION = jsonToSchemaObject(
getJsonSchema(Product),
);
PRODUCT_SPEC_WITH_DESCRIPTION.required!.push('description');

// This is the standard use case that most LB4 applications should use.
// The request body specification is infered from a decorated model class.
// The request body specification is inferred from a decorated model class.
context('for request body specified via model definition', () => {
class ProductController {
@post('/products')
Expand Down Expand Up @@ -87,7 +93,7 @@ describe('Validation at REST level', () => {
});
});

// A request body schema can be provied explicitly by the user
// A request body schema can be provided explicitly by the user
// as an inlined content[type].schema property.
context('for fully-specified request body', () => {
class ProductControllerWithFullSchema {
Expand All @@ -111,6 +117,37 @@ describe('Validation at REST level', () => {
serverRejectsRequestWithMissingRequiredValues());
});

context('for different schemas per media type', () => {
let spec = aBodySpec(PRODUCT_SPEC, {}, 'application/json');
spec = aBodySpec(
PRODUCT_SPEC_WITH_DESCRIPTION,
spec,
'application/x-www-form-urlencoded',
);
class ProductControllerWithFullSchema {
@post('/products')
async create(
@requestBody(spec) data: object,
// ^^^^^^
// use "object" instead of "Product" to verify the situation when
// body schema cannot be inferred from the argument type
): Promise<Product> {
return new Product(data);
}
}

before(() => givenAnAppAndAClient(ProductControllerWithFullSchema));
after(() => app.stop());

it('accepts valid values for json', () => serverAcceptsValidRequestBody());

it('accepts valid values for urlencoded', () =>
serverAcceptsValidRequestBodyForUrlencoded());

it('rejects missing required properties for urlencoded', () =>
serverRejectsMissingDescriptionForUrlencoded());
});

// A request body schema can be provided explicitly by the user as a reference
// to a schema shared in the global `components.schemas` object.
context('for request body specified via a reference', () => {
Expand Down Expand Up @@ -156,6 +193,29 @@ describe('Validation at REST level', () => {
.expect(200, DATA);
}

async function serverAcceptsValidRequestBodyForUrlencoded() {
const DATA =
'name=Pencil&price=10&description=An optional description of a pencil';
await client
.post('/products')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(DATA)
.expect(200, {
name: 'Pencil',
description: 'An optional description of a pencil',
price: 10,
});
}

async function serverRejectsMissingDescriptionForUrlencoded() {
const DATA = 'name=Pencil&price=10';
await client
.post('/products')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(DATA)
.expect(422);
}

async function serverRejectsRequestWithMissingRequiredValues() {
await client
.post('/products')
Expand Down
20 changes: 14 additions & 6 deletions packages/rest/test/helpers.ts
Expand Up @@ -31,15 +31,23 @@ export function createUnexpectedHttpErrorLogger(
};
}

/**
* Create an OpenAPI request body spec with the given content
* @param schema The schema object
* @param options Other attributes for the spec
* @param mediaType Optional media type, default to `application/json`
*/
export function aBodySpec(
schema: SchemaObject | ReferenceObject,
options?: Partial<RequestBodyObject>,
options: Partial<RequestBodyObject> = {},
mediaType: string = 'application/json',
): RequestBodyObject {
return Object.assign({}, options, {
content: {
'application/json': {
schema: schema,
},
const spec = Object.assign({}, options);
spec.content = spec.content || {};
Object.assign(spec.content, {
[mediaType]: {
schema: schema,
},
});
return spec as RequestBodyObject;
}

0 comments on commit 630d392

Please sign in to comment.