diff --git a/test/generators/php/constrainer/ConstantConstrainer.spec.ts b/test/generators/php/constrainer/ConstantConstrainer.spec.ts new file mode 100644 index 0000000000..71202cdf05 --- /dev/null +++ b/test/generators/php/constrainer/ConstantConstrainer.spec.ts @@ -0,0 +1,19 @@ +import { defaultConstantConstraints } from '../../../../src/generators/php/constrainer/ConstantConstrainer'; + +const mockConstantContext = { + constrainedMetaModel: {} as any +}; + +describe('defaultConstantConstraints', () => { + it('should return a function that returns undefined', () => { + const constantConstraintsFunction = defaultConstantConstraints(); + const result = constantConstraintsFunction(mockConstantContext); + expect(result).toBeUndefined(); + }); + + it('should return a ConstantConstraint type', () => { + const constantConstraintsFunction = defaultConstantConstraints(); + const result = constantConstraintsFunction; + expect(typeof result).toBe('function'); + }); +}); diff --git a/test/generators/php/constrainer/EnumConstrainer.spec.ts b/test/generators/php/constrainer/EnumConstrainer.spec.ts new file mode 100644 index 0000000000..6295e5407b --- /dev/null +++ b/test/generators/php/constrainer/EnumConstrainer.spec.ts @@ -0,0 +1,167 @@ +import { PhpDefaultConstraints } from '../../../../src/generators/php/PhpConstrainer'; +import { EnumModel } from '../../../../src/models/MetaModel'; +import { + ConstrainedEnumModel, + ConstrainedEnumValueModel +} from '../../../../src'; +import { + defaultEnumKeyConstraints, + ModelEnumKeyConstraints +} from '../../../../src/generators/php/constrainer/EnumConstrainer'; + +describe('Php EnumConstrainer', () => { + const enumModel = new EnumModel('test', undefined, {}, []); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + {}, + '', + [] + ); + + describe('enum keys', () => { + test('should never render special chars', () => { + const constrainedKey = PhpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '%' + }); + expect(constrainedKey).toEqual('PERCENT'); + }); + + test('should not render number as start char', () => { + const constrainedKey = PhpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '1' + }); + expect(constrainedKey).toEqual('NUMBER_1'); + }); + + test('should not contain duplicate keys', () => { + const existingConstrainedEnumValueModel = new ConstrainedEnumValueModel( + 'EMPTY', + 'return', + 'return' + ); + const constrainedEnumModel = new ConstrainedEnumModel( + 'test', + undefined, + {}, + '', + [existingConstrainedEnumValueModel] + ); + const constrainedKey = PhpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('RESERVED_EMPTY'); + }); + + test('should never contain empty keys', () => { + const constrainedKey = PhpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: '' + }); + expect(constrainedKey).toEqual('RESERVED_EMPTY'); + }); + + test('should use constant naming format', () => { + const constrainedKey = PhpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'some weird_value!"#2' + }); + expect(constrainedKey).toEqual( + 'SOME_WEIRD_VALUE_EXCLAMATION_QUOTATION_HASH_2' + ); + }); + + test('should never render reserved keywords', () => { + const constrainedKey = PhpDefaultConstraints.enumKey({ + enumModel, + constrainedEnumModel, + enumKey: 'return' + }); + expect(constrainedKey).toEqual('RESERVED_RETURN'); + }); + }); + + describe('enum values', () => { + test('should render string values', () => { + const constrainedValue = PhpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 'string value' + }); + expect(constrainedValue).toEqual('"string value"'); + }); + test('should render boolean values', () => { + const constrainedBooleanValue = PhpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: true + }); + expect(constrainedBooleanValue).toEqual('"true"'); + }); + + test('should render numbers', () => { + const constrainedNumberValue = PhpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: 123 + }); + expect(constrainedNumberValue).toEqual(123); + }); + + test('should render object values', () => { + const constrainedObjectValue = PhpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: { test: 'test' } + }); + expect(constrainedObjectValue).toEqual('"{\\"test\\":\\"test\\"}"'); + }); + + test('should render unknown value', () => { + const constrainedUnknownValue = PhpDefaultConstraints.enumValue({ + enumModel, + constrainedEnumModel, + enumValue: undefined + }); + expect(constrainedUnknownValue).toEqual(`"undefined"`); + }); + }); + + describe('custom constraints', () => { + test('should be able to overwrite all hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite specific hooks for enum key', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultEnumKeyConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ enumModel, constrainedEnumModel, enumKey: '' }); + + expect(mockedConstraintCallbacks.NAMING_FORMATTER).toHaveBeenCalled(); + }); + }); +}); diff --git a/test/generators/php/constrainer/ModelNameConstrainer.spec.ts b/test/generators/php/constrainer/ModelNameConstrainer.spec.ts new file mode 100644 index 0000000000..5eb4e22de5 --- /dev/null +++ b/test/generators/php/constrainer/ModelNameConstrainer.spec.ts @@ -0,0 +1,75 @@ +import { + DefaultModelNameConstraints, + defaultModelNameConstraints, + ModelNameConstraints +} from '../../../../src/generators/php/constrainer/ModelNameConstrainer'; + +describe('PHP ModelNameConstrainer', () => { + const PhpDefaultConstraints: ModelNameConstraints = + DefaultModelNameConstraints; + + test('should replace special characters with underscores', () => { + const constrainedName = PhpDefaultConstraints.NO_SPECIAL_CHAR('name%$test'); + expect(constrainedName).toEqual('name_percent_dollar_test'); + }); + + test('should handle number start characters', () => { + const constrainedName = PhpDefaultConstraints.NO_NUMBER_START_CHAR('1Test'); + expect(constrainedName).toEqual('number_1Test'); + }); + + test('should handle empty value by default', () => { + const constrainedName = PhpDefaultConstraints.NO_EMPTY_VALUE(''); + expect(constrainedName).toEqual('empty'); + }); + + test('should convert to PascalCase by default', () => { + const constrainedName = PhpDefaultConstraints.NAMING_FORMATTER('test_name'); + expect(constrainedName).toEqual('TestName'); + }); + + test('should handle reserved PHP keywords', () => { + const constrainedName = + PhpDefaultConstraints.NO_RESERVED_KEYWORDS('return'); + expect(constrainedName).toEqual('reserved_return'); + }); + + describe('Custom PHP constraints', () => { + test('should be able to overwrite all hooks for PHP', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultModelNameConstraints( + mockedConstraintCallbacks + ); + constrainFunction({ modelName: '' }); + // Expect all callbacks to be called + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + + test('should be able to overwrite one hook for PHP', () => { + // All but NAMING_FORMATTER, as we customize that + const spies = [ + jest.spyOn(DefaultModelNameConstraints, 'NO_SPECIAL_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_NUMBER_START_CHAR'), + jest.spyOn(DefaultModelNameConstraints, 'NO_EMPTY_VALUE'), + jest.spyOn(DefaultModelNameConstraints, 'NO_RESERVED_KEYWORDS') + ]; + const jestCallback = jest.fn().mockReturnValue(''); + const constrainFunction = defaultModelNameConstraints({ + NAMING_FORMATTER: jestCallback + }); + const constrainedValue = constrainFunction({ modelName: '' }); + expect(constrainedValue).toEqual(''); + for (const jestMockCallback of spies) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + }); +}); diff --git a/test/generators/php/constrainer/PropertyKeyConstrainer.spec.ts b/test/generators/php/constrainer/PropertyKeyConstrainer.spec.ts new file mode 100644 index 0000000000..3547b6f40c --- /dev/null +++ b/test/generators/php/constrainer/PropertyKeyConstrainer.spec.ts @@ -0,0 +1,179 @@ +import { PhpDefaultConstraints } from '../../../../src/generators/php/PhpConstrainer'; +import { + ConstrainedObjectModel, + ObjectModel, + ConstrainedObjectPropertyModel, + ObjectPropertyModel +} from '../../../../src'; +import { + defaultPropertyKeyConstraints, + PropertyKeyConstraintOptions +} from '../../../../src/generators/php/constrainer/PropertyKeyConstrainer'; + +describe('PropertyKeyConstrainer for PHP', () => { + const objectModel = new ObjectModel('test', undefined, {}, {}); + const constrainedObjectModel = new ConstrainedObjectModel( + 'test', + undefined, + {}, + '', + {} + ); + + const constrainPropertyName = (propertyName: string) => { + const objectPropertyModel = new ObjectPropertyModel( + propertyName, + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + return PhpDefaultConstraints.propertyKey({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + }; + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should never render special chars for PHP', () => { + const constrainedKey = constrainPropertyName('%'); + expect(constrainedKey).toEqual('percent'); + }); + + test('should not render number as start char for PHP', () => { + const constrainedKey = constrainPropertyName('1'); + expect(constrainedKey).toEqual('number_1'); + }); + + test('should never contain empty name for PHP', () => { + const constrainedKey = constrainPropertyName(''); + expect(constrainedKey).toEqual('reservedEmpty'); + }); + + test('should use camelCase naming format for PHP', () => { + const constrainedKey = constrainPropertyName('some weird_value!"#2'); + expect(constrainedKey).toEqual('someWeirdValueExclamationQuotationHash_2'); + }); + + test('should never render reserved keywords for PHP', () => { + const constrainedKey = constrainPropertyName('return'); + expect(constrainedKey).toEqual('reservedReturn'); + }); + + describe('custom constraints for PHP', () => { + test('should be able to overwrite all hooks for PHP', () => { + const mockedConstraintCallbacks: Partial = { + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + for (const jestMockCallback of Object.values(mockedConstraintCallbacks)) { + expect(jestMockCallback).toHaveBeenCalled(); + } + }); + test('should be able to overwrite NO_SPECIAL_CHAR hook for PHP', () => { + const mockedConstraintCallbacks: Partial = { + NO_SPECIAL_CHAR: jest.fn().mockImplementation((value: string) => { + return value.replace(/[@#$%^&*]/g, ''); + }), + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_EMPTY_VALUE: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + + const result = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(result).toEqual(''); + expect(mockedConstraintCallbacks.NO_SPECIAL_CHAR).toHaveBeenCalled(); + }); + + test('should be able to overwrite NO_EMPTY_VALUE hook for PHP', () => { + const mockedConstraintCallbacks: Partial = { + NO_EMPTY_VALUE: jest.fn().mockImplementation((value: string) => { + return value.trim(); + }), + NAMING_FORMATTER: jest.fn().mockReturnValue(''), + NO_SPECIAL_CHAR: jest.fn().mockReturnValue(''), + NO_NUMBER_START_CHAR: jest.fn().mockReturnValue(''), + NO_RESERVED_KEYWORDS: jest.fn().mockReturnValue('') + }; + + const constrainFunction = defaultPropertyKeyConstraints( + mockedConstraintCallbacks + ); + + const objectPropertyModel = new ObjectPropertyModel( + '', + false, + objectModel + ); + const constrainedObjectPropertyModel = new ConstrainedObjectPropertyModel( + '', + '', + objectPropertyModel.required, + constrainedObjectModel + ); + + const result = constrainFunction({ + constrainedObjectModel, + objectModel, + objectPropertyModel, + constrainedObjectPropertyModel + }); + expect(result).toEqual(''); + expect(mockedConstraintCallbacks.NO_EMPTY_VALUE).toHaveBeenCalled(); + }); + }); +});