Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/repository-json-schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"devDependencies": {
"@loopback/build": "^0.6.5",
"@loopback/testlab": "^0.10.4",
"@types/node": "^10.1.1"
"@types/node": "^10.1.1",
"ajv": "^6.5.0"
},
"files": [
"README.md",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '../..';
import {expect} from '@loopback/testlab';
import {MetadataInspector} from '@loopback/context';
import * as Ajv from 'ajv';

describe('build-schema', () => {
describe('modelToJsonSchema', () => {
Expand All @@ -25,6 +26,7 @@ describe('build-schema', () => {

const jsonSchema = modelToJsonSchema(TestModel);
expect(jsonSchema.properties).to.not.have.keys(['nul', 'undef']);
expectValidJsonSchema(jsonSchema);
});

it('does not convert properties that have not been decorated', () => {
Expand All @@ -42,11 +44,13 @@ describe('build-schema', () => {
const noPropJson = modelToJsonSchema(NoPropertyMeta);
const onePropJson = modelToJsonSchema(OnePropertyDecorated);
expect(noPropJson).to.not.have.key('properties');
expectValidJsonSchema(noPropJson);
expect(onePropJson.properties).to.deepEqual({
foo: {
type: 'string',
},
});
expectValidJsonSchema(onePropJson);
});

it('does not convert models that have not been decorated with @model()', () => {
Expand All @@ -56,8 +60,12 @@ describe('build-schema', () => {
bar: number;
}

expect(modelToJsonSchema(Empty)).to.eql({});
expect(modelToJsonSchema(NoModelMeta)).to.eql({});
const emptyJson = modelToJsonSchema(Empty);
const noModelMetaJson = modelToJsonSchema(NoModelMeta);
expect(emptyJson).to.eql({});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems redundant. Won't expectValidJsonSchema check if it's {} or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it is redundant. On the other hand though, what if someday they decide {} is not a valid JSON Schema? In that case, we'd want this to test to fail while still maintaining that emptyJson is an empty object and that {} is not a valid JSON Schema

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlikely, but I'm ok with the redundancy.

expectValidJsonSchema(emptyJson);
expect(noModelMetaJson).to.eql({});
expectValidJsonSchema(noModelMetaJson);
});

it('infers "title" property from constructor name', () => {
Expand All @@ -68,6 +76,7 @@ describe('build-schema', () => {

const jsonSchema = modelToJsonSchema(TestModel);
expect(jsonSchema.title).to.eql('TestModel');
expectValidJsonSchema(jsonSchema);
});

it('overrides "title" property if explicitly given', () => {
Expand All @@ -78,6 +87,7 @@ describe('build-schema', () => {

const jsonSchema = modelToJsonSchema(TestModel);
expect(jsonSchema.title).to.eql('NewName');
expectValidJsonSchema(jsonSchema);
});

it('retains "description" properties from top-level metadata', () => {
Expand All @@ -91,6 +101,7 @@ describe('build-schema', () => {

const jsonSchema = modelToJsonSchema(TestModel);
expect(jsonSchema.description).to.eql(topMeta.description);
expectValidJsonSchema(jsonSchema);
});

it('properly converts string, number, and boolean properties', () => {
Expand All @@ -113,6 +124,7 @@ describe('build-schema', () => {
type: 'boolean',
},
});
expectValidJsonSchema(jsonSchema);
});

it('properly converts object properties', () => {
Expand All @@ -127,6 +139,7 @@ describe('build-schema', () => {
type: 'object',
},
});
expectValidJsonSchema(jsonSchema);
});

context('with custom type properties', () => {
Expand All @@ -147,6 +160,7 @@ describe('build-schema', () => {
},
});
expect(jsonSchema).to.not.have.key('definitions');
expectValidJsonSchema(jsonSchema);
});

it('properly converts decorated custom type properties', () => {
Expand Down Expand Up @@ -176,6 +190,7 @@ describe('build-schema', () => {
},
},
});
expectValidJsonSchema(jsonSchema);
});

it('creates definitions only at the root level of the schema', () => {
Expand Down Expand Up @@ -223,6 +238,7 @@ describe('build-schema', () => {
},
},
});
expectValidJsonSchema(jsonSchema);
});
});

Expand All @@ -241,6 +257,7 @@ describe('build-schema', () => {
},
},
});
expectValidJsonSchema(jsonSchema);
});

it('properly converts custom type arrays properties', () => {
Expand All @@ -262,6 +279,7 @@ describe('build-schema', () => {
},
},
});
expectValidJsonSchema(jsonSchema);
});

it('supports explicit primitive type decoration via strings', () => {
Expand All @@ -287,6 +305,7 @@ describe('build-schema', () => {
type: 'number',
},
});
expectValidJsonSchema(jsonSchema);
});

it('maps "required" keyword to the schema appropriately', () => {
Expand All @@ -301,6 +320,7 @@ describe('build-schema', () => {

const jsonSchema = modelToJsonSchema(TestModel);
expect(jsonSchema.required).to.deepEqual(['propTwo']);
expectValidJsonSchema(jsonSchema);
});

it('errors out when explicit type decoration is not primitive', () => {
Expand Down Expand Up @@ -335,6 +355,18 @@ describe('build-schema', () => {
}).to.throw(/type is defined as an array/);
});
});

function expectValidJsonSchema(schema: JsonSchema) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider exporting this as a util function from this package?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 This means that we'd have to have @loopback/testlab and ajv as dependencies for @loopback/repository. Looking at https://bundlephobia.com/result?p=ajv@6.5.0, would you consider ajv to be sizable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that big imo but I'm ok not exporting this as a utility function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shimks I think @virkt25 was suggesting to export expectValidJsonSchema from repository-json-schema, not testlab package?

At one hand, a readily-available helper for verifying JSON Schema may be helpful outside of this module tests too. On the other hand, exporting a test helper from a non-test-helper module is tricky as we may need to move dev-dependencies to regular dependencies. As for testlab, it should not become a kitchen-sink for everything testing related.

In the light of what I wrote above, I think we should not be exporting this utility function.

const ajv = new Ajv();
const validate = ajv.compile(
require('ajv/lib/refs/json-schema-draft-06.json'),
);
const isValid = validate(schema);
const result = isValid
? 'JSON Schema is valid'
: ajv.errorsText(validate.errors!);
expect(result).to.equal('JSON Schema is valid');
}
});

describe('getjsonSchema', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/repository-json-schema
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {expect} from '@loopback/testlab';
import {isComplexType, stringTypeToWrapper, metaToJsonProperty} from '../..';

Expand Down
63 changes: 63 additions & 0 deletions packages/repository-json-schema/test/unit/json-schema.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/repository-json-schema
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {JsonSchema} from '../../src';

describe('JSON Schema type', () => {
describe('JsonSchema interface', () => {
/**
* The classes below are declared as tests for the Interfaces.
* The TS Compiler will complain if an interface changes in a way
* inconsistent with the JSON Schema definition.
*
* Inspired by https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/json-schema/json-schema-tests.ts
*/

// tslint:disable-next-line:no-unused-variable
const testSchema: JsonSchema = {
$id: 'test',
$ref: 'test/sub',
$schema: 'http://json-schema.org/schema#',
title: 'test',
description: 'test description',
default: 10,
multipleOf: 5,
maximum: 4,
exclusiveMaximum: 20,
minimum: 5,
exclusiveMinimum: 5,
maxLength: 7,
minLength: 2,
pattern: 'test pattern',
additionalItems: true,
items: [
{
type: 'string',
},
],
maxItems: 5,
minItems: 4,
uniqueItems: true,
maxProperties: 5,
minProperties: 12,
required: ['foo', 'bar'],
additionalProperties: true,
definitions: {foo: {type: 'number'}},
properties: {bar: {type: 'string'}},
dependencies: {baz: {type: 'boolean'}},
enum: ['foo', 23],
type: 'string',
allOf: [{type: 'string'}],
anyOf: [{type: 'number'}],
oneOf: [{type: 'boolean'}],
not: {type: 'array'},
const: 'test',
contains: {type: 'string'},
examples: [4],
propertyNames: {enum: ['foo', 'bar']},
format: 'email',
};
});
});