diff --git a/__tests__/utils/isValidKey.test.ts b/__tests__/utils/isValidKey.test.ts new file mode 100644 index 0000000..f51e242 --- /dev/null +++ b/__tests__/utils/isValidKey.test.ts @@ -0,0 +1,105 @@ +import { Chance } from 'chance'; +import { isValidKey } from '../../src/utils/isValidKey'; +import { firstOnlyCharacters, middleOnlyCharacters, reservedCharacters } from '../../src/constants/characterSets'; + +describe('`isValidKey`', () => { + let chance: Chance.Chance; + + beforeEach(() => { + chance = new Chance(); + }); + + it('should throw errors that correctly reference the decorator name and key', () => { + const decoratorName = chance.string(); + const key = chance.integer(); + + try { + isValidKey(decoratorName, key); + } catch (error) { + expect(error.message).toContain( + `@${decoratorName} can only be applied to specific keys. Expected key ${key.toString()}`, + ); + } + }); + + it('should throw an error if the key is not a string', () => { + const key = chance.pickone([Symbol(), chance.integer()]); + + try { + isValidKey('some-decorator', key); + } catch (error) { + expect(error.message).toContain( + `Expected key ${key.toString()} to be of type string, but received ${typeof key}.`, + ); + } + }); + + it('should throw an error if the key is an empty string', () => { + const key = ''; + + try { + isValidKey('some-decorator', key); + } catch (error) { + expect(error.message).toContain('Expected key to be non-empty.'); + } + }); + + it('should throw an error if the key contains reserved characters', () => { + const key = chance.pickone([...reservedCharacters]); + + try { + isValidKey('some-decorator', key); + } + catch (error) { + expect(error.message).toContain( + `Expected key ${key} to not contain any reserved characters, but found ${key}.`, + ); + } + }); + + it('should throw an error if the key contains first only characters after the first character', () => { + const firstOnlyCharacter = chance.pickone([...firstOnlyCharacters]); + const key = `${chance.guid()}${firstOnlyCharacter}`; + + try { + isValidKey('some-decorator', key); + } catch (error) { + expect(error.message).toContain( + `Expected key ${key} to not contain the characters ${firstOnlyCharacter}, except as the first character.`, + ); + } + }); + + it('should allow first only characters as the first character', () => { + const firstOnlyCharacter = chance.pickone([...firstOnlyCharacters]); + const key = `${firstOnlyCharacter}${chance.guid()}`; + + expect(isValidKey('some-decorator', key)).toBe(true); + }); + + it('should throw an error if the key contains middle only characters as the first or last character', () => { + const middleOnlyCharacter = chance.pickone([...middleOnlyCharacters]); + const key = `${middleOnlyCharacter}${chance.guid()}${middleOnlyCharacter}`; + + try { + isValidKey('some-decorator', key); + } catch (error) { + expect(error.message).toContain( + `Expected key ${key} to not contain the characters ${middleOnlyCharacter}, ${middleOnlyCharacter} as the first or last character.`, + ); + } + }); + + it('should allow middle only characters in the middle of the key', () => { + const middleOnlyCharacter = chance.pickone([...middleOnlyCharacters]); + const key = `${chance.guid()}${middleOnlyCharacter}${chance.guid()}`; + + expect(isValidKey('some-decorator', key)).toBe(true); + }); + + it('should return true if the key is valid', () => { + const key = chance.guid(); + + expect(isValidKey('some-decorator', key)).toBe(true); + }); +}); diff --git a/src/constants/characterSets.ts b/src/constants/characterSets.ts index a9a0481..e69b9f3 100644 --- a/src/constants/characterSets.ts +++ b/src/constants/characterSets.ts @@ -32,9 +32,9 @@ export const reservedCharacters: ReadonlySet = new Set([ '\u001F', ]); -export const firstCharacterOnly: ReadonlySet = new Set(['\u0040']); +export const firstOnlyCharacters: ReadonlySet = new Set(['\u0040']); -export const notFirstOrLastCharacter: ReadonlySet = new Set([ +export const middleOnlyCharacters: ReadonlySet = new Set([ '\u002D', '\u005F', '\u0020', diff --git a/src/utils/isValidKey.ts b/src/utils/isValidKey.ts index 1ebb66c..10fda9e 100644 --- a/src/utils/isValidKey.ts +++ b/src/utils/isValidKey.ts @@ -1,6 +1,6 @@ import { - firstCharacterOnly, - notFirstOrLastCharacter, + firstOnlyCharacters, + middleOnlyCharacters, reservedCharacters, } from '../constants/characterSets'; @@ -32,7 +32,7 @@ export const isValidKey = ( } const firstCharacterViolations = charactersInKey.filter( - (character, index) => firstCharacterOnly.has(character) && index !== 0, + (character, index) => firstOnlyCharacters.has(character) && index !== 0, ); if (firstCharacterViolations.length > 0) { throw new Error( @@ -42,7 +42,7 @@ export const isValidKey = ( const firstOrLastCharacterViolations = charactersInKey.filter( (character, index) => - notFirstOrLastCharacter.has(character) && + middleOnlyCharacters.has(character) && (index === 0 || index === charactersInKey.length - 1), ); if (firstOrLastCharacterViolations.length > 0) {