From 1f011238773feced2dce0ecfe44a17d470ec1dbd Mon Sep 17 00:00:00 2001 From: Amachua Date: Wed, 3 Feb 2021 15:35:05 +0100 Subject: [PATCH] feat(oas): add rule to detect duplicated entry in enum (#1485) --- docs/reference/openapi-rules.md | 38 ++++++ .../oas/__tests__/duplicated-entry-in-enum.ts | 117 ++++++++++++++++++ src/rulesets/oas/index.json | 17 +++ .../duplicated-entry-in-enum.oas.scenario | 33 +++++ .../enabled-rules-amount.oas3.scenario | 2 +- 5 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/rulesets/oas/__tests__/duplicated-entry-in-enum.ts create mode 100644 test-harness/scenarios/duplicated-entry-in-enum.oas.scenario diff --git a/docs/reference/openapi-rules.md b/docs/reference/openapi-rules.md index db84c37d3..fbb9fbfec 100644 --- a/docs/reference/openapi-rules.md +++ b/docs/reference/openapi-rules.md @@ -411,6 +411,44 @@ TheBadModel: - 8 ``` +### duplicated-entry-in-enum + +Each value of an `enum` must be different from one another. + +**Recommended:** Yes + +**Good Example** + +```yaml +TheGoodModel: + type: object + properties: + number_of_connectors: + type: integer + description: The number of extension points. + enum: + - 1 + - 2 + - 4 + - 8 +``` + +**Bad Example** + +```yaml +TheBadModel: + type: object + properties: + number_of_connectors: + type: integer + description: The number of extension points. + enum: + - 1 + - 2 + - 3 + - 2 +``` + ## OpenAPI v2.0-only These rules will only apply to OpenAPI v2.0 documents. diff --git a/src/rulesets/oas/__tests__/duplicated-entry-in-enum.ts b/src/rulesets/oas/__tests__/duplicated-entry-in-enum.ts new file mode 100644 index 000000000..32c15f9fc --- /dev/null +++ b/src/rulesets/oas/__tests__/duplicated-entry-in-enum.ts @@ -0,0 +1,117 @@ +import { DiagnosticSeverity } from '@stoplight/types'; +import * as ruleset from '../index.json'; +import { RuleType, Spectral } from '../../../spectral'; + +describe('duplicated-entry-in-enum', () => { + const s = new Spectral(); + s.setRules({ + 'duplicated-entry-in-enum': Object.assign(ruleset.rules['duplicated-entry-in-enum'], { + recommended: true, + type: RuleType[ruleset.rules['duplicated-entry-in-enum'].type], + }), + }); + + describe('oas2', () => { + test('does not report anything for empty object', async () => { + const results = await s.run({ + swagger: '2.0', + }); + + expect(results).toEqual([]); + }); + + test('does not report anything when the model valid', async () => { + const doc = { + swagger: '2.0', + definitions: { + Test: { + type: 'integer', + enum: [1, 2, 3], + }, + }, + }; + + const results = await s.run(doc); + + expect(results).toEqual([]); + }); + + test('identifies enum with duplicated entries', async () => { + const doc = { + swagger: '2.0', + definitions: { + Test: { + type: 'integer', + enum: [1, 2, 3, 4, 5, 2], + }, + }, + }; + + const results = await s.run(doc); + + expect(results).toEqual([ + { + code: 'duplicated-entry-in-enum', + message: `A duplicated entry in the enum was found. Error: \`enum\` property should not have duplicate items (items ## 1 and 5 are identical)`, + path: ['definitions', 'Test', 'enum'], + range: expect.any(Object), + severity: DiagnosticSeverity.Warning, + }, + ]); + }); + }); + + describe('oas3', () => { + test('does not report anything for empty object', async () => { + const results = await s.run({ + openapi: '3.0.0', + }); + + expect(results).toEqual([]); + }); + + test('does not report anything when the model is valid', async () => { + const doc = { + openapi: '3.0.0', + components: { + schemas: { + Test: { + type: 'integer', + enum: [1, 2, 3], + }, + }, + }, + }; + + const results = await s.run(doc); + + expect(results).toEqual([]); + }); + + test('identifies enum with duplicated entries', async () => { + const doc = { + openapi: '3.0.0', + components: { + schemas: { + Test: { + type: 'integer', + enum: [1, 2, 3, 4, 5, 2], + }, + }, + }, + }; + + const results = await s.run(doc); + + expect(results).toEqual([ + { + code: 'duplicated-entry-in-enum', + message: `A duplicated entry in the enum was found. Error: \`enum\` property should not have duplicate items (items ## 1 and 5 are identical)`, + path: ['components', 'schemas', 'Test', 'enum'], + range: expect.any(Object), + severity: DiagnosticSeverity.Warning, + }, + ]); + }); + }); +}); diff --git a/src/rulesets/oas/index.json b/src/rulesets/oas/index.json index d258d57cd..8f7ad55ff 100644 --- a/src/rulesets/oas/index.json +++ b/src/rulesets/oas/index.json @@ -95,6 +95,23 @@ } ] }, + "duplicated-entry-in-enum": { + "description": "Enum values should not have duplicate entry.", + "type": "validation", + "severity": 1, + "recommended": true, + "message": "A duplicated entry in the enum was found. Error: {{error}}", + "given": "$..enum", + "then": { + "function": "schema", + "functionOptions": { + "schema": { + "type": "array", + "uniqueItems": true + } + } + } + }, "info-contact": { "description": "Info object should contain `contact` object.", "recommended": true, diff --git a/test-harness/scenarios/duplicated-entry-in-enum.oas.scenario b/test-harness/scenarios/duplicated-entry-in-enum.oas.scenario new file mode 100644 index 000000000..fb0775e91 --- /dev/null +++ b/test-harness/scenarios/duplicated-entry-in-enum.oas.scenario @@ -0,0 +1,33 @@ +====test==== +Identify enum values that does not respect the specified type +====asset:ruleset==== +extends: [[spectral:oas, off]] + +rules: + duplicated-entry-in-enum: error +====document==== +openapi: 3.0.2 +components: + schemas: + a_model: + type: object + properties: + id: + type: integer + description: Unique asset identifier + enum: + - 1 + - 2 + - 3 + - 4 + - 5 + - 2 +====command==== +{bin} lint {document} --ruleset {asset:ruleset} +====stdout==== +OpenAPI 3.x detected + +{document} + 10:16 error duplicated-entry-in-enum A duplicated entry in the enum was found. Error: `enum` property should not have duplicate items (items ## 1 and 5 are identical) components.schemas.a_model.properties.id.enum + +✖ 1 problem (1 error, 0 warnings, 0 infos, 0 hints) diff --git a/test-harness/scenarios/enabled-rules-amount.oas3.scenario b/test-harness/scenarios/enabled-rules-amount.oas3.scenario index 431ab1064..bb41e1f5a 100644 --- a/test-harness/scenarios/enabled-rules-amount.oas3.scenario +++ b/test-harness/scenarios/enabled-rules-amount.oas3.scenario @@ -24,7 +24,7 @@ components: ====command==== {bin} lint {document} --ruleset ./rulesets/parameter-description.oas3.yaml -v ====stdout==== -Found 55 rules (1 enabled) +Found 56 rules (1 enabled) Linting {document} OpenAPI 3.x detected