-
-
Notifications
You must be signed in to change notification settings - Fork 242
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
383 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { typedEnum } from '../typedEnum'; | ||
|
||
const defaultReportingThreshold = 3; | ||
|
||
function runTypedEnum(targetVal: any, reportingThreshold: any) { | ||
return typedEnum( | ||
targetVal, | ||
{ reportingThreshold }, | ||
{ given: ['$'] }, | ||
{ given: null, original: null, resolved: {} as any }, | ||
); | ||
} | ||
|
||
describe('typedEnum', () => { | ||
describe('parameters validation', () => { | ||
test.each([1, { a: 1 }, 'nope', undefined])('is undefined when the enum is not an array (%s)', enumContent => { | ||
const schema = { | ||
type: 'integer', | ||
enum: enumContent, | ||
}; | ||
|
||
expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
test('is undefined when the enum contains no value', () => { | ||
const schema = { | ||
type: 'integer', | ||
enum: [], | ||
}; | ||
|
||
expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined(); | ||
}); | ||
|
||
describe('basic', () => { | ||
test('is undefined when all enum values respect the type', () => { | ||
const schema = { | ||
type: 'integer', | ||
enum: [123, 456], | ||
}; | ||
|
||
expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined(); | ||
}); | ||
|
||
test('is undefined when all enum values respect the type', () => { | ||
const schema = { | ||
type: 'integer', | ||
enum: [123, 456], | ||
}; | ||
|
||
expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined(); | ||
}); | ||
|
||
test.each([undefined])('is undefined when type is "%s"', (typeValue: unknown) => { | ||
const schema = { | ||
type: typeValue, | ||
enum: [123, 456], | ||
}; | ||
|
||
expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined(); | ||
}); | ||
|
||
test('identifies enum values which do not respect the type', () => { | ||
const schema = { | ||
type: 'integer', | ||
enum: [123, 'a string!', 456, 'and another one!'], | ||
}; | ||
|
||
expect(runTypedEnum(schema, defaultReportingThreshold)).toEqual([ | ||
{ | ||
message: 'Enum value `a string!` does not respect the specified type `integer`.', | ||
path: ['$', 'enum', 1], | ||
}, | ||
{ | ||
message: 'Enum value `and another one!` does not respect the specified type `integer`.', | ||
path: ['$', 'enum', 3], | ||
}, | ||
]); | ||
}); | ||
}); | ||
|
||
describe('types', () => { | ||
const testCases: Array<[string, unknown[], unknown]> = [ | ||
['string', ['Hello', 'world!'], 12], | ||
['number', [-2147483648, 17.13], 'Hello'], | ||
['integer', [-2147483648, 17], 12.3], | ||
['boolean', [true, false], 1], | ||
]; | ||
|
||
test.each(testCases)( | ||
'does not report anything when all the definitions are valid for type "%s"', | ||
async (type: string, valids: unknown[], invalid: unknown) => { | ||
const schema = { | ||
type, | ||
enum: valids, | ||
}; | ||
|
||
expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined(); | ||
}, | ||
); | ||
|
||
test.each(testCases)( | ||
'identifies enum value which does not respect the type "%s"', | ||
async (type: string, valids: unknown[], invalid: unknown) => { | ||
const schema = { | ||
type, | ||
enum: [valids[0], invalid], | ||
}; | ||
|
||
const results = runTypedEnum(schema, defaultReportingThreshold); | ||
|
||
expect(results[0].message).toContain(`value \`${invalid}\``); | ||
}, | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { IFunction, IFunctionResult, IRule, RuleFunction } from '../types'; | ||
import { schema } from './schema'; | ||
|
||
export type TypedEnumRule = IRule<RuleFunction.TYPED_ENUM>; | ||
|
||
export const typedEnum: IFunction = (targetVal, opts, paths, otherValues): void | IFunctionResult[] => { | ||
const { enum: enumValues, ...initialSchema } = targetVal; | ||
|
||
if (!Array.isArray(enumValues)) { | ||
return; | ||
} | ||
|
||
const innerSchema = { type: initialSchema.type, enum: initialSchema.enum }; | ||
const schemaObject = { schema: innerSchema }; | ||
|
||
const incorrectValues: Array<{ index: number; val: unknown }> = []; | ||
|
||
enumValues.forEach((val, index) => { | ||
const res = schema(val, schemaObject, paths, otherValues); | ||
|
||
if (res !== undefined && res.length !== 0) { | ||
incorrectValues.push({ index, val }); | ||
} | ||
}); | ||
|
||
if (incorrectValues.length === 0) { | ||
return; | ||
} | ||
|
||
const { type } = initialSchema; | ||
|
||
const rootPath = paths.target ?? paths.given; | ||
|
||
return incorrectValues.map(bad => { | ||
return { | ||
message: `Enum value \`${bad.val}\` does not respect the specified type \`${type}\`.`, | ||
path: [...rootPath, 'enum', bad.index], | ||
}; | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { DiagnosticSeverity } from '@stoplight/types'; | ||
import { typedEnum } from '../../../functions/typedEnum'; | ||
import { RuleType, Spectral } from '../../../index'; | ||
import { rules } from '../index.json'; | ||
|
||
describe('typed-enum', () => { | ||
const s = new Spectral(); | ||
s.setFunctions({ typedEnum }); | ||
s.setRules({ | ||
'typed-enum': Object.assign(rules['typed-enum'], { | ||
recommended: true, | ||
type: RuleType[rules['typed-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 values which do not respect the type', async () => { | ||
const doc = { | ||
swagger: '2.0', | ||
definitions: { | ||
Test: { | ||
type: 'integer', | ||
enum: [1, 'a string!', 3, 'and another one!'], | ||
}, | ||
}, | ||
}; | ||
|
||
const results = await s.run(doc); | ||
|
||
expect(results).toEqual([ | ||
{ | ||
code: 'typed-enum', | ||
message: 'Enum value `a string!` does not respect the specified type `integer`.', | ||
path: ['definitions', 'Test', 'enum', '1'], | ||
range: expect.any(Object), | ||
severity: DiagnosticSeverity.Warning, | ||
}, | ||
{ | ||
code: 'typed-enum', | ||
message: 'Enum value `and another one!` does not respect the specified type `integer`.', | ||
path: ['definitions', 'Test', 'enum', '3'], | ||
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 values which do not respect the type', async () => { | ||
const doc = { | ||
openapi: '3.0.0', | ||
components: { | ||
schemas: { | ||
Test: { | ||
type: 'integer', | ||
enum: [1, 'a string!', 3, 'and another one!'], | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const results = await s.run(doc); | ||
|
||
expect(results).toEqual([ | ||
{ | ||
code: 'typed-enum', | ||
message: 'Enum value `a string!` does not respect the specified type `integer`.', | ||
path: ['components', 'schemas', 'Test', 'enum', '1'], | ||
range: expect.any(Object), | ||
severity: DiagnosticSeverity.Warning, | ||
}, | ||
{ | ||
code: 'typed-enum', | ||
message: 'Enum value `and another one!` does not respect the specified type `integer`.', | ||
path: ['components', 'schemas', 'Test', 'enum', '3'], | ||
range: expect.any(Object), | ||
severity: DiagnosticSeverity.Warning, | ||
}, | ||
]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.