Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
nulltoken committed Jan 25, 2020
1 parent 3dbdb2c commit 5288ca0
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 161 deletions.
1 change: 1 addition & 0 deletions src/cli/services/linter/utils/getRuleset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ async function loadRulesets(cwd: string, rulesetFiles: string[]): Promise<IRules
return {
functions: {},
rules: {},
exceptions: {},
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/meta/ruleset.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@
"required": ["rules"]
}
]
}
}
6 changes: 6 additions & 0 deletions src/rulesets/__tests__/reader.jest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ describe('Rulesets reader', () => {
return expect(
readRuleset(path.join(__dirname, './__fixtures__/inheritanceRulesets/my-ruleset.json')),
).resolves.toStrictEqual({
exceptions: {},
functions: {},
rules: {
'contact-name-matches-stoplight': {
Expand Down Expand Up @@ -417,6 +418,7 @@ describe('Rulesets reader', () => {
return expect(
readRuleset(path.join(__dirname, './__fixtures__/inheritanceRulesets/my-ruleset-recommended.json')),
).resolves.toStrictEqual({
exceptions: {},
functions: {},
rules: {
'contact-name-matches-stoplight': {
Expand Down Expand Up @@ -468,6 +470,7 @@ describe('Rulesets reader', () => {
return expect(
readRuleset(path.join(__dirname, './__fixtures__/inheritanceRulesets/ruleset-c.json')),
).resolves.toStrictEqual({
exceptions: {},
functions: {},
rules: {
'contact-name-matches-stoplight': {
Expand Down Expand Up @@ -645,11 +648,13 @@ describe('Rulesets reader', () => {
return expect(readRuleset(rulesetWithMissingFunctions)).resolves.toEqual({
rules: {},
functions: {},
exceptions: {},
});
});

it('should handle ruleset with circular extensions', () => {
return expect(readRuleset(fooExtendsBarRuleset)).resolves.toEqual({
exceptions: {},
functions: {},
rules: {
'bar-rule': {
Expand All @@ -674,6 +679,7 @@ describe('Rulesets reader', () => {

it('should handle ruleset that extends itself', () => {
return expect(readRuleset(selfExtendingRuleset)).resolves.toEqual({
exceptions: {},
functions: {},
rules: {
'foo-rule': {
Expand Down
147 changes: 32 additions & 115 deletions src/rulesets/__tests__/validation.test.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,47 @@
import { JSONSchema7 } from 'json-schema';
import { IRulesetFile } from '../../types/ruleset';
import { assertValidAndNormalizeRuleset, decorateIFunctionWithSchemaValidation, ValidationError } from '../validation';
import { assertValidRuleset, decorateIFunctionWithSchemaValidation, ValidationError } from '../validation';
const invalidRuleset = require('./__fixtures__/invalid-ruleset.json');
const validRuleset = require('./__fixtures__/valid-flat-ruleset.json');

describe('Ruleset Validation', () => {
it('given primitive type should throw', () => {
expect(assertValidAndNormalizeRuleset.bind(null, null)).toThrow('Provided ruleset is not an object');
expect(assertValidAndNormalizeRuleset.bind(null, 2)).toThrow('Provided ruleset is not an object');
expect(assertValidAndNormalizeRuleset.bind(null, 'true')).toThrow('Provided ruleset is not an object');
expect(assertValidRuleset.bind(null, null)).toThrow('Provided ruleset is not an object');
expect(assertValidRuleset.bind(null, 2)).toThrow('Provided ruleset is not an object');
expect(assertValidRuleset.bind(null, 'true')).toThrow('Provided ruleset is not an object');
});

it('given object with no rules and no extends properties should throw', () => {
expect(assertValidAndNormalizeRuleset.bind(null, {})).toThrow('Ruleset must have rules or extends property');
expect(assertValidAndNormalizeRuleset.bind(null, { rule: {} })).toThrow(
'Ruleset must have rules or extends property',
);
expect(assertValidRuleset.bind(null, {})).toThrow('Ruleset must have rules or extends property');
expect(assertValidRuleset.bind(null, { rule: {} })).toThrow('Ruleset must have rules or extends property');
});

it('given object with extends property only should emit no errors', () => {
expect(assertValidAndNormalizeRuleset.bind(null, { extends: [] })).not.toThrow();
expect(assertValidRuleset.bind(null, { extends: [] })).not.toThrow();
});

it('given object with rules property only should emit no errors', () => {
expect(assertValidAndNormalizeRuleset.bind(null, { rules: {} })).not.toThrow();
expect(assertValidRuleset.bind(null, { rules: {} })).not.toThrow();
});

it('given invalid ruleset should throw', () => {
expect(assertValidAndNormalizeRuleset.bind(null, invalidRuleset)).toThrow(ValidationError);
expect(assertValidRuleset.bind(null, invalidRuleset)).toThrow(ValidationError);
});

it('given valid ruleset should emit no errors', () => {
expect(assertValidAndNormalizeRuleset.bind(null, validRuleset)).not.toThrow();
expect(assertValidRuleset.bind(null, validRuleset)).not.toThrow();
});

it.each(['error', 'warn', 'info', 'hint', 'off'])('recognizes human-readable %s severity', severity => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
rules: {
rule: severity,
},
}),
).not.toThrow();

expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
rules: {
rule: {
given: '$.info',
Expand All @@ -60,15 +57,15 @@ describe('Ruleset Validation', () => {

it('recognizes array-ish syntax', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
rules: {
rule: ['off'],
},
}),
).not.toThrow();

expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
rules: {
rule: [1],
},
Expand All @@ -78,7 +75,7 @@ describe('Ruleset Validation', () => {

it('recognizes invalid array-ish syntax', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
rules: {
rule: ['off', 2],
},
Expand All @@ -88,7 +85,7 @@ describe('Ruleset Validation', () => {

it('recognizes valid array-ish extends syntax', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
extends: [['foo', 'off'], 'test'],
rules: {},
}),
Expand All @@ -97,7 +94,7 @@ describe('Ruleset Validation', () => {

it('recognizes string extends syntax', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
extends: 'foo',
rules: {},
}),
Expand All @@ -106,7 +103,7 @@ describe('Ruleset Validation', () => {

it('recognizes invalid array-ish extends syntax', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
extends: [['foo', 'test']],
rules: {},
}),
Expand All @@ -115,7 +112,7 @@ describe('Ruleset Validation', () => {

it('recognizes valid ruleset formats syntax', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
formats: ['oas3'],
rules: {},
}),
Expand All @@ -124,7 +121,7 @@ describe('Ruleset Validation', () => {

it.each([[2, 'a'], 2, ['']])('recognizes invalid ruleset formats syntax', formats => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
formats,
rules: {},
}),
Expand All @@ -133,7 +130,7 @@ describe('Ruleset Validation', () => {

it('recognizes valid rule formats syntax', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
formats: ['d'],
rules: {
rule: {
Expand All @@ -150,7 +147,7 @@ describe('Ruleset Validation', () => {

it.each([[2, 'a'], 2, ['']])('recognizes invalid rule formats syntax', formats => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
rules: {
given: '$.info',
then: {
Expand All @@ -164,7 +161,7 @@ describe('Ruleset Validation', () => {

it('recognizes functions directory', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functionsDir: 'baz',
rules: {},
}),
Expand All @@ -173,7 +170,7 @@ describe('Ruleset Validation', () => {

it('recognizes invalid functions directory', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functionsDir: 2,
rules: {},
}),
Expand All @@ -182,7 +179,7 @@ describe('Ruleset Validation', () => {

it('recognizes valid array of functions with names only', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functions: ['foo', 'bar'],
rules: {},
}),
Expand All @@ -191,7 +188,7 @@ describe('Ruleset Validation', () => {

it('recognizes valid array of functions with object only', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functions: [
['foo', {}],
['baz', {}],
Expand All @@ -203,7 +200,7 @@ describe('Ruleset Validation', () => {

it('recognizes valid array of functions with both names and objects only', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functions: ['falsy', ['foo', {}], ['baz', {}], 'truthy'],
rules: {},
}),
Expand All @@ -212,7 +209,7 @@ describe('Ruleset Validation', () => {

it('recognizes valid schema functions', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functions: [['d', { type: 'object' }]],
rules: {},
}),
Expand All @@ -221,7 +218,7 @@ describe('Ruleset Validation', () => {

it('recognizes invalid functions', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functions: 3,
rules: {},
}),
Expand All @@ -230,7 +227,7 @@ describe('Ruleset Validation', () => {

it('recognizes invalid schema functions', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functions: ['d', { typo: 'a' }],
rules: {},
}),
Expand All @@ -239,16 +236,14 @@ describe('Ruleset Validation', () => {

it('recognizes invalid functions options', () => {
expect(
assertValidAndNormalizeRuleset.bind(null, {
assertValidRuleset.bind(null, {
functions: [3, 'd'],
rules: {},
}),
).toThrow(ValidationError);
});

describe('Exceptions validation', () => {
const dummyRuleseturi = '.';

const rulesetsWithInvalidExceptStructures = [
{ extends: ['foo'], except: '' },
{ extends: ['foo'], except: {} },
Expand All @@ -260,87 +255,9 @@ describe('Ruleset Validation', () => {

it.each(rulesetsWithInvalidExceptStructures)('throws when defined "except" do not match schema', async r => {
expect(() => {
assertValidAndNormalizeRuleset(r, dummyRuleseturi);
assertValidRuleset(r);
}).toThrow(ValidationError);
});

const rulesetsWithInvalidExceptKeys = [
{ extends: ['foo'], except: { where: ['one'] } },
{ extends: ['foo'], except: { '123.yaml': ['one'] } },
{ extends: ['foo'], except: { '../123.yaml': ['one'] } },
{ extends: ['foo'], except: { '##where': ['one'] } },
{ extends: ['foo'], except: { '#where': ['one'] } },
{ extends: ['foo'], except: { '#': ['one'] } },
{ extends: ['foo'], except: { '#/where': ['one'] } },
{ extends: ['foo'], except: { '../123.yaml#where': ['one'] } },
];

it.each(rulesetsWithInvalidExceptKeys)(
'throws when defined "except" keys are not valid uris (including fragment)',
async r => {
expect(() => {
assertValidAndNormalizeRuleset(r, dummyRuleseturi);
}).toThrow('is not a valid uri');
},
);

const assertLocationNormalization = (location: string, rulesetUri: string, expected: string) => {
const r: IRulesetFile = { extends: ['foo'] };
r.except = {};
r.except[location] = ['a'];
const res = assertValidAndNormalizeRuleset(r, rulesetUri);
expect(res).not.toBeNull();
expect(res).not.toBeUndefined();
expect(Object.keys(res.except!)).toHaveLength(1);
expect(Object.keys(res.except!)[0]).toEqual(expected);
};

const relativeLocations: Array<[string, string, string]> = [
['./ruleset.yaml', 'one.yaml#', 'one.yaml#'],
['./ruleset.yaml', 'one.yaml#/', 'one.yaml#/'],
['./ruleset.yaml', 'one.yaml#/toto', 'one.yaml#/toto'],
['./ruleset.yaml', 'down/one.yaml#/toto', 'down/one.yaml#/toto'],
['./ruleset.yaml', '../one.yaml#/toto', '../one.yaml#/toto'],
['../ruleset.yaml', 'one.yaml#', '../one.yaml#'],
['../ruleset.yaml', 'one.yaml#/', '../one.yaml#/'],
['../ruleset.yaml', 'one.yaml#/toto', '../one.yaml#/toto'],
['../ruleset.yaml', 'down/one.yaml#/toto', '../down/one.yaml#/toto'],
['../ruleset.yaml', '../one.yaml#/toto', '../../one.yaml#/toto'],
['https://dot.com/r/ruleset.yaml', 'one.yaml#', 'https://dot.com/r/one.yaml#'],
['https://dot.com/r/ruleset.yaml', 'one.yaml#/', 'https://dot.com/r/one.yaml#/'],
['https://dot.com/r/ruleset.yaml', 'one.yaml#/toto', 'https://dot.com/r/one.yaml#/toto'],
['https://dot.com/r/ruleset.yaml', 'down/one.yaml#/toto', 'https://dot.com/r/down/one.yaml#/toto'],
['https://dot.com/r/ruleset.yaml', '../one.yaml#/toto', 'https://dot.com/one.yaml#/toto'],
];

it.each(relativeLocations)(
'normalize keys, given "%s" as the ruleset uri and "%s" and the relative location',
(rulesetUri: string, location: string, expected: string) => {
assertLocationNormalization(location, rulesetUri, expected);
},
);

const absoluteLocations: Array<[string, string, string]> = [
['./ruleset.yaml', 'https://dot.com/one.yaml#/toto', 'https://dot.com/one.yaml#/toto'],
['../ruleset.yaml', 'https://dot.com/one.yaml#/toto', 'https://dot.com/one.yaml#/toto'],
['https://dot.com/r/ruleset.yaml', 'https://dot.com/one.yaml#/toto', 'https://dot.com/one.yaml#/toto'],
['./ruleset.yaml', '/local/one.yaml#/toto', '/local/one.yaml#/toto'],
['../ruleset.yaml', '/local/one.yaml#/toto', '/local/one.yaml#/toto'],
['https://dot.com/r/ruleset.yaml', '/local/one.yaml#/toto', '/local/one.yaml#/toto'],
['./ruleset.yaml', 'c:/one.yaml#/toto', 'c:/one.yaml#/toto'],
['../ruleset.yaml', 'c:/one.yaml#/toto', 'c:/one.yaml#/toto'],
['https://dot.com/r/ruleset.yaml', 'c:/one.yaml#/toto', 'c:/one.yaml#/toto'],
['./ruleset.yaml', 'c:\\one.yaml#/toto', 'c:\\one.yaml#/toto'],
['../ruleset.yaml', 'c:\\one.yaml#/toto', 'c:\\one.yaml#/toto'],
['https://dot.com/r/ruleset.yaml', 'c:\\one.yaml#/toto', 'c:\\one.yaml#/toto'],
];

it.each(absoluteLocations)(
'normalize keys, given "%s" as the ruleset uri and "%s" and the absolute location',
(rulesetUri: string, location: string, expected: string) => {
assertLocationNormalization(location, rulesetUri, expected);
},
);
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/rulesets/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './mergers';
export { readRuleset } from './reader';
export { assertValidAndNormalizeRuleset } from './validation';
export { assertValidRuleset } from './validation';
Loading

0 comments on commit 5288ca0

Please sign in to comment.