1
0
Fork 0
salty-dog/test/rule/TestSchemaRule.ts

236 lines
5.7 KiB
TypeScript

import { expect } from 'chai';
import { LogLevel, NullLogger } from 'noicejs';
import { stub } from 'sinon';
import { friendlyError, SchemaRule } from '../../src/rule/SchemaRule';
import { VisitorContext } from '../../src/visitor/VisitorContext';
import { describeLeaks, itLeaks } from '../helpers/async';
/* eslint-disable @typescript-eslint/unbound-method */
const TEST_NAME = 'test-rule';
describeLeaks('schema rule', async () => {
itLeaks('should pick items from the scope', async () => {
const ctx = new VisitorContext({
logger: NullLogger.global,
schemaOptions: {
coerce: false,
defaults: false,
mutate: false,
},
});
const data = {
foo: 3,
};
const rule = new SchemaRule({
check: {},
desc: '',
level: LogLevel.Info,
name: 'foo',
select: '$.foo',
tags: [],
});
const results = await rule.pick(ctx, data);
expect(results).to.deep.equal([data.foo]);
});
itLeaks('should pick no items', async () => {
const ctx = new VisitorContext({
logger: NullLogger.global,
schemaOptions: {
coerce: false,
defaults: false,
mutate: false,
},
});
const data = {
bar: 3,
};
const rule = new SchemaRule({
check: {},
desc: '',
level: LogLevel.Info,
name: 'foo',
select: '$.foo',
tags: [],
});
const results = await rule.pick(ctx, data);
expect(results).to.deep.equal([]);
});
itLeaks('should filter out items', async () => {
const ctx = new VisitorContext({
logger: NullLogger.global,
schemaOptions: {
coerce: false,
defaults: false,
mutate: false,
},
});
const data = {
foo: 3,
};
const rule = new SchemaRule({
check: {},
desc: '',
filter: {
properties: {
foo: {
type: 'number',
},
},
type: 'object',
},
level: LogLevel.Info,
name: 'foo',
select: '$.foo',
tags: [],
});
const results = await rule.visit(ctx, data);
expect(results.errors.length).to.equal(0);
});
itLeaks('should pick items from the root', async () => {
const ctx = new VisitorContext({
logger: NullLogger.global,
schemaOptions: {
coerce: false,
defaults: false,
mutate: false,
},
});
const rule = new SchemaRule({
check: undefined,
desc: TEST_NAME,
level: LogLevel.Info,
name: TEST_NAME,
select: '$.foo',
tags: [],
});
const results = await rule.pick(ctx, {
foo: [Math.random(), Math.random(), Math.random()],
});
expect(Array.isArray(results)).to.equal(true);
});
itLeaks('should visit selected items', async () => {
const ctx = new VisitorContext({
logger: NullLogger.global,
schemaOptions: {
coerce: false,
defaults: false,
mutate: false,
},
});
const check = {};
const checkSpy = stub().returns(true);
const filter = {};
const filterSpy = stub().returns(true);
ctx.compile = stub().onFirstCall().returns(checkSpy).onSecondCall().returns(filterSpy);
const rule = new SchemaRule({
check,
desc: TEST_NAME,
filter,
level: LogLevel.Info,
name: TEST_NAME,
select: '$.foo',
tags: [],
});
const data = {};
await rule.visit(ctx, data);
expect(filterSpy, 'filter spy should have been called with data').to.have.callCount(1).and.been.calledWithExactly(data);
expect(checkSpy, 'check spy should have been called with data').to.have.callCount(1).and.been.calledWithExactly(data);
});
itLeaks('should skip filtered items', async () => {
const ctx = new VisitorContext({
logger: NullLogger.global,
schemaOptions: {
coerce: false,
defaults: false,
mutate: false,
},
});
const checkSpy = stub().throws(new Error('check spy error'));
const filterSpy = stub().returns(false);
ctx.compile = stub().onFirstCall().returns(checkSpy).onSecondCall().returns(filterSpy);
const rule = new SchemaRule({
check: undefined,
desc: TEST_NAME,
filter: {},
level: LogLevel.Info,
name: TEST_NAME,
select: '$.foo',
tags: [],
});
const data = {};
await rule.visit(ctx, data);
expect(filterSpy, 'filter spy should have been called with data').to.have.callCount(1).and.been.calledWithExactly(data);
expect(checkSpy, 'check spy should not have been called').to.have.callCount(0);
});
});
function createErrorContext() {
const rule = new SchemaRule({
check: {},
desc: TEST_NAME,
level: LogLevel.Info,
name: TEST_NAME,
select: '',
tags: [TEST_NAME],
});
const ctx = new VisitorContext({
logger: NullLogger.global,
schemaOptions: {
coerce: false,
defaults: false,
mutate: false,
},
});
ctx.visitData = {
itemIndex: 0,
rule,
};
return { ctx, rule };
}
describe('friendly errors', () => {
it('should have a message', () => {
const { ctx } = createErrorContext();
const err = friendlyError(ctx, {
dataPath: 'test-path',
keyword: TEST_NAME,
params: { /* ? */ },
schemaPath: 'test-path',
});
expect(err.msg).to.include(TEST_NAME);
});
it('should handle errors with an existing message', () => {
const { ctx } = createErrorContext();
const TEST_MESSAGE = 'test-message';
const err = friendlyError(ctx, {
dataPath: 'test-path',
keyword: TEST_NAME,
message: TEST_MESSAGE,
params: { /* ? */ },
schemaPath: 'test-path',
});
expect(err.msg).to.include(TEST_MESSAGE);
});
});