Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: example-value-or-externalValue too broad JsonPath expressions #899

Merged
merged 10 commits into from
Jan 14, 2020
221 changes: 188 additions & 33 deletions src/rulesets/oas/__tests__/example-value-or-externalValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,82 @@ describe('example-value-or-externalValue', () => {
});

test('validate if just externalValue', async () => {
const results = await s.run({ examples: { first: { externalValue: 'value' } } });
const results = await s.run({ components: { examples: { first: { externalValue: 'value' } } } });
expect(results.length).toEqual(0);
});

test('validate if just value', async () => {
const results = await s.run({ examples: { first: { value: 'value' } } });
const results = await s.run({ components: { examples: { first: { value: 'value' } } } });
expect(results.length).toEqual(0);
});

test('validate if example on top level', async () => {
const results = await s.run({ examples: { first: { value: 'value', externalValue: 'value' } } });
expect(results.length).toEqual(0);
});

test('validate if examples properties in examples', async () => {
const results = await s.run({
components: {
examples: {
first: {
value: {
examples: {
a: 'b',
},
},
},
second: {
value: {
components: {
examples: {
value: 'value',
externalValue: 'value',
},
},
},
},
third: {
value: {
examples: {
a: 'b',
},
},
},
},
},
});
expect(results.length).toEqual(0);
});

test('multiple examples - validate all value or externalValue', async () => {
const results = await s.run({
examples: {
first: { value: 'value1' },
second: { externalValue: 'external-value2' },
third: { value: 'value3' },
components: {
examples: {
first: { value: 'value1' },
second: { externalValue: 'external-value2' },
third: { value: 'value3' },
},
},
});
expect(results.length).toEqual(0);
});

test('return warnings if missing externalValue and value', async () => {
const results = await s.run({ examples: { first: {} } });
const results = await s.run({ components: { examples: { first: {} } } });
expect(results).toEqual([
{
code: 'example-value-or-externalValue',
message: 'Example should have either a `value` or `externalValue` field.',
path: ['examples', 'first'],
path: ['components', 'examples', 'first'],
range: {
end: {
character: 15,
line: 2,
character: 17,
line: 3,
},
start: {
character: 12,
line: 2,
character: 14,
line: 3,
},
},
severity: DiagnosticSeverity.Warning,
Expand All @@ -56,20 +97,108 @@ describe('example-value-or-externalValue', () => {

test('multiple examples - return warnings if missing externalValue and value in one', async () => {
const results = await s.run({
examples: { first: { value: 'value1' }, second: { externalValue: 'external-value2' }, third: {} },
components: { examples: { first: { value: 'value1' }, second: { externalValue: 'external-value2' }, third: {} } },
});
expect(results).toEqual([
{
code: 'example-value-or-externalValue',
message: 'Example should have either a `value` or `externalValue` field.',
path: ['examples', 'third'],
path: ['components', 'examples', 'third'],
range: {
end: {
character: 15,
character: 17,
line: 9,
},
start: {
character: 14,
line: 9,
},
},
severity: DiagnosticSeverity.Warning,
},
]);
});

test('return warnings if both externalValue and value', async () => {
const results = await s.run({
components: { examples: { first: { externalValue: 'externalValue', value: 'value' } } },
});
expect(results).toEqual([
{
code: 'example-value-or-externalValue',
message: 'Example should have either a `value` or `externalValue` field.',
path: ['components', 'examples', 'first'],
range: {
end: {
character: 24,
line: 5,
},
start: {
character: 14,
line: 3,
},
},
severity: DiagnosticSeverity.Warning,
},
]);
});

test('multiple examples - return warnings if both externalValue and value in one (in components)', async () => {
const results = await s.run({
components: {
examples: {
first: { value: 'value1' },
second: { externalValue: 'external-value2', value: 'value2' },
third: { externalValue: 'external-value3' },
},
},
});
expect(results).toEqual([
{
code: 'example-value-or-externalValue',
message: 'Example should have either a `value` or `externalValue` field.',
path: ['components', 'examples', 'second'],
range: {
end: {
character: 25,
line: 8,
},
start: {
character: 12,
character: 15,
line: 6,
},
},
severity: DiagnosticSeverity.Warning,
},
]);
});

test('multiple examples - return warnings if both externalValue and value in one (in headers)', async () => {
const results = await s.run({
components: {
headers: {
headerName: {
examples: {
first: { value: 'value1' },
second: { externalValue: 'external-value2', value: 'value2' },
third: { externalValue: 'external-value3' },
},
},
},
},
});
expect(results).toEqual([
{
code: 'example-value-or-externalValue',
message: 'Example should have either a `value` or `externalValue` field.',
path: ['components', 'headers', 'headerName', 'examples', 'second'],
range: {
end: {
character: 29,
line: 10,
},
start: {
character: 19,
line: 8,
},
},
Expand All @@ -78,49 +207,75 @@ describe('example-value-or-externalValue', () => {
]);
});

test('return warnings if both externalValue and value', async () => {
const results = await s.run({ examples: { first: { externalValue: 'externalValue', value: 'value' } } });
test('multiple examples - return warnings if both externalValue and value in one (in parameters)', async () => {
const results = await s.run({
components: {
parameters: {
parameterName: {
examples: {
first: { value: 'value1' },
second: { externalValue: 'external-value2', value: 'value2' },
third: { externalValue: 'external-value3' },
},
},
},
},
});
expect(results).toEqual([
{
code: 'example-value-or-externalValue',
message: 'Example should have either a `value` or `externalValue` field.',
path: ['examples', 'first'],
path: ['components', 'parameters', 'parameterName', 'examples', 'second'],
range: {
end: {
character: 22,
line: 4,
character: 29,
line: 10,
},
start: {
character: 12,
line: 2,
character: 19,
line: 8,
},
},
severity: DiagnosticSeverity.Warning,
},
]);
});

test('multiple examples - return warnings if both externalValue and value in one', async () => {
test('multiple examples - return warnings if both externalValue and value in one (in content)', async () => {
const results = await s.run({
examples: {
first: { value: 'value1' },
second: { externalValue: 'external-value2', value: 'value2' },
third: { externalValue: 'external-value3' },
paths: {
'/path': {
get: {
responses: {
200: {
content: {
'application/json': {
examples: {
first: { value: 'value1' },
second: { externalValue: 'external-value2', value: 'value2' },
third: { externalValue: 'external-value3' },
},
},
},
},
},
},
},
},
});
expect(results).toEqual([
{
code: 'example-value-or-externalValue',
message: 'Example should have either a `value` or `externalValue` field.',
path: ['examples', 'second'],
path: ['paths', '/path', 'get', 'responses', '200', 'content', 'application/json', 'examples', 'second'],
range: {
end: {
character: 23,
line: 7,
character: 37,
line: 14,
},
start: {
character: 13,
line: 5,
character: 27,
line: 12,
},
},
severity: DiagnosticSeverity.Warning,
Expand Down
10 changes: 9 additions & 1 deletion src/rulesets/oas/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,15 @@
"description": "Example should have either a `value` or `externalValue` field.",
"recommended": true,
"type": "style",
"given": "$..examples.*",
"given": [
m-mohr marked this conversation as resolved.
Show resolved Hide resolved
"$.components.examples.*",
"$.paths..content.*.examples.*",
"$.components..content.*.examples.*",
"$.paths..parameters.*.examples.*",
"$.components..parameters.*.examples.*",
"$.paths..headers.*.examples.*",
"$.components..headers.*.examples.*"
],
"then": {
"function": "xor",
"functionOptions": {
Expand Down