UNPKG

@cmmv/core

Version:

CMMV core module for contract and application management

336 lines (335 loc) 18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); require("reflect-metadata"); const vitest_1 = require("vitest"); const contract_decorator_1 = require("../../decorators/contract.decorator"); (0, vitest_1.describe)('Contract Decorator', () => { (0, vitest_1.it)('should apply default metadata when no options are provided', () => { let TestClass = class TestClass { }; TestClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)() ], TestClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTRACT_WATERMARK, TestClass)).toBe(true); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_NAME_METADATA, TestClass)).toBe('DefaultContract'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.PROTO_PATH_METADATA, TestClass)).toBe('contract.proto'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.PROTO_PACKAGE_METADATA, TestClass)).toBe(''); }); (0, vitest_1.it)('should apply provided metadata options', () => { const options = { controllerName: 'CustomContract', protoPath: 'custom.proto', protoPackage: 'custom.package', directMessage: true, generateController: false, auth: false, controllerCustomPath: 'custom/path', }; let TestClass = class TestClass { }; TestClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], TestClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTRACT_WATERMARK, TestClass)).toBe(true); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_NAME_METADATA, TestClass)).toBe('CustomContract'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.PROTO_PATH_METADATA, TestClass)).toBe('custom.proto'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.PROTO_PACKAGE_METADATA, TestClass)).toBe('custom.package'); }); }); (0, vitest_1.describe)('ContractField Decorator', () => { (0, vitest_1.it)('should apply field metadata to class properties', () => { const options = { protoType: 'string', protoRepeated: true, defaultValue: 'default', index: true, unique: false, }; let TestClass = class TestClass { }; tslib_1.__decorate([ (0, contract_decorator_1.ContractField)(options), tslib_1.__metadata("design:type", String) ], TestClass.prototype, "testField", void 0); TestClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)() ], TestClass); const contract = new TestClass(); const target = contract.constructor || contract; const prototype = target.prototype || contract.prototype; const fieldMetadata = Reflect.getMetadata(contract_decorator_1.FIELD_METADATA, prototype).find((field) => field.propertyKey === 'testField'); (0, vitest_1.expect)(fieldMetadata.protoType).toBe('string'); (0, vitest_1.expect)(fieldMetadata.protoRepeated).toBe(true); (0, vitest_1.expect)(fieldMetadata.defaultValue).toBe('default'); (0, vitest_1.expect)(fieldMetadata.index).toBe(true); (0, vitest_1.expect)(fieldMetadata.unique).toBe(false); }); (0, vitest_1.it)('should handle multiple fields on the same class', () => { class TestClass { } tslib_1.__decorate([ (0, contract_decorator_1.ContractField)({ protoType: 'string' }), tslib_1.__metadata("design:type", String) ], TestClass.prototype, "field1", void 0); tslib_1.__decorate([ (0, contract_decorator_1.ContractField)({ protoType: 'int32', defaultValue: 42 }), tslib_1.__metadata("design:type", Number) ], TestClass.prototype, "field2", void 0); const contract = new TestClass(); const target = contract.constructor || contract; const prototype = target.prototype || contract.prototype; const field1Metadata = Reflect.getMetadata(contract_decorator_1.FIELD_METADATA, prototype).find((field) => field.propertyKey === 'field1'); const field2Metadata = Reflect.getMetadata(contract_decorator_1.FIELD_METADATA, prototype).find((field) => field.propertyKey === 'field2'); (0, vitest_1.expect)(field1Metadata.protoType).toBe('string'); (0, vitest_1.expect)(field2Metadata.protoType).toBe('int32'); (0, vitest_1.expect)(field2Metadata.defaultValue).toBe(42); }); }); (0, vitest_1.describe)('Contract Decorator Additional Tests', () => { (0, vitest_1.it)('should apply auth and cache metadata when provided', () => { const options = { controllerName: 'AuthContract', protoPath: 'auth.proto', protoPackage: 'auth.package', auth: true, cache: { key: 'authCacheKey', ttl: 3600 }, }; let AuthClass = class AuthClass { }; AuthClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], AuthClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_NAME_METADATA, AuthClass)).toBe('AuthContract'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.PROTO_PATH_METADATA, AuthClass)).toBe('auth.proto'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.PROTO_PACKAGE_METADATA, AuthClass)).toBe('auth.package'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.AUTH_METADATA, AuthClass)).toBe(true); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_CACHE, AuthClass)).toEqual({ key: 'authCacheKey', ttl: 3600, }); }); (0, vitest_1.it)('should handle contract imports and custom paths', () => { const options = { controllerName: 'ImportContract', protoPath: 'import.proto', imports: ['import1', 'import2'], controllerCustomPath: 'custom/import/path', }; let ImportClass = class ImportClass { }; ImportClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], ImportClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_NAME_METADATA, ImportClass)).toBe('ImportContract'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_IMPORTS, ImportClass)).toEqual([ 'import1', 'import2', ]); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_CUSTOM_PATH_METADATA, ImportClass)).toBe('custom/import/path'); }); }); (0, vitest_1.describe)('ContractField Decorator Additional Tests', () => { (0, vitest_1.it)('should apply transform and validation options correctly', () => { const options = { protoType: 'string', transform: (value) => value.toUpperCase(), validations: [{ type: 'required', message: 'Field is required' }], }; let TransformClass = class TransformClass { }; tslib_1.__decorate([ (0, contract_decorator_1.ContractField)(options), tslib_1.__metadata("design:type", String) ], TransformClass.prototype, "name", void 0); TransformClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)() ], TransformClass); const contract = new TransformClass(); const target = contract.constructor || contract; const prototype = target.prototype || contract.prototype; const fieldMetadata = Reflect.getMetadata(contract_decorator_1.FIELD_METADATA, prototype).find((field) => field.propertyKey === 'name'); (0, vitest_1.expect)(fieldMetadata.protoType).toBe('string'); (0, vitest_1.expect)(typeof fieldMetadata.transform).toBe('function'); (0, vitest_1.expect)(fieldMetadata.validations).toEqual([ { type: 'required', message: 'Field is required' }, ]); }); (0, vitest_1.it)('should apply nullable and exclude options', () => { const options1 = { protoType: 'string', nullable: true }; const options2 = { protoType: 'int32', exclude: true }; let NullableExcludeClass = class NullableExcludeClass { }; tslib_1.__decorate([ (0, contract_decorator_1.ContractField)(options1), tslib_1.__metadata("design:type", String) ], NullableExcludeClass.prototype, "optionalField", void 0); tslib_1.__decorate([ (0, contract_decorator_1.ContractField)(options2), tslib_1.__metadata("design:type", Number) ], NullableExcludeClass.prototype, "excludedField", void 0); NullableExcludeClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)() ], NullableExcludeClass); const contract = new NullableExcludeClass(); const target = contract.constructor || contract; const prototype = target.prototype || contract.prototype; const field1Metadata = Reflect.getMetadata(contract_decorator_1.FIELD_METADATA, prototype).find((field) => field.propertyKey === 'optionalField'); const field2Metadata = Reflect.getMetadata(contract_decorator_1.FIELD_METADATA, prototype).find((field) => field.propertyKey === 'excludedField'); (0, vitest_1.expect)(field1Metadata.protoType).toBe('string'); (0, vitest_1.expect)(field1Metadata.nullable).toBe(true); (0, vitest_1.expect)(field2Metadata.protoType).toBe('int32'); (0, vitest_1.expect)(field2Metadata.exclude).toBe(true); }); (0, vitest_1.it)('should handle toClassOnly option correctly', () => { const options = { protoType: 'string', toClassOnly: true }; let ClassOnlyFieldClass = class ClassOnlyFieldClass { }; tslib_1.__decorate([ (0, contract_decorator_1.ContractField)(options), tslib_1.__metadata("design:type", String) ], ClassOnlyFieldClass.prototype, "classOnlyField", void 0); ClassOnlyFieldClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)() ], ClassOnlyFieldClass); const contract = new ClassOnlyFieldClass(); const target = contract.constructor || contract; const prototype = target.prototype || contract.prototype; const fieldMetadata = Reflect.getMetadata(contract_decorator_1.FIELD_METADATA, prototype).find((field) => field.propertyKey === 'classOnlyField'); (0, vitest_1.expect)(fieldMetadata.protoType).toBe('string'); (0, vitest_1.expect)(fieldMetadata.toClassOnly).toBe(true); }); (0, vitest_1.describe)('Contract Decorator with ViewForm and ViewPage', () => { (0, vitest_1.it)('should apply viewForm and viewPage metadata when provided', () => { class MockViewForm { } class MockViewPage { } const options = { controllerName: 'ViewContract', protoPath: 'view.proto', viewForm: MockViewForm, viewPage: MockViewPage, }; let ViewClass = class ViewClass { }; ViewClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], ViewClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_NAME_METADATA, ViewClass)).toBe('ViewContract'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.PROTO_PATH_METADATA, ViewClass)).toBe('view.proto'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWFORM, ViewClass)).toBe(MockViewForm); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWPAGE, ViewClass)).toBe(MockViewPage); }); (0, vitest_1.it)('should apply default metadata when viewForm and viewPage are not provided', () => { let DefaultViewClass = class DefaultViewClass { }; DefaultViewClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)() ], DefaultViewClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWFORM, DefaultViewClass)).toBe(null); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWPAGE, DefaultViewClass)).toBe(null); }); }); (0, vitest_1.describe)('Contract Decorator Extended Tests for ViewForm and ViewPage', () => { (0, vitest_1.it)('should allow dynamic assignment of viewForm and viewPage', () => { class DynamicViewForm { } class DynamicViewPage { } const options = { controllerName: 'DynamicViewContract', protoPath: 'dynamic.proto', viewForm: DynamicViewForm, viewPage: DynamicViewPage, }; let DynamicViewClass = class DynamicViewClass { }; DynamicViewClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], DynamicViewClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWFORM, DynamicViewClass)).toBe(DynamicViewForm); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWPAGE, DynamicViewClass)).toBe(DynamicViewPage); }); (0, vitest_1.it)('should handle null values for viewForm and viewPage', () => { const options = { controllerName: 'NullViewContract', protoPath: 'nullview.proto', viewForm: null, viewPage: null, }; let NullViewClass = class NullViewClass { }; NullViewClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], NullViewClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWFORM, NullViewClass)).toBe(null); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWPAGE, NullViewClass)).toBe(null); }); (0, vitest_1.it)('should work with custom metadata alongside viewForm and viewPage', () => { class CustomViewForm { } class CustomViewPage { } const options = { controllerName: 'CustomViewContract', protoPath: 'customview.proto', viewForm: CustomViewForm, viewPage: CustomViewPage, imports: ['import1', 'import2'], cache: { key: 'customCache', ttl: 600 }, }; let CustomViewClass = class CustomViewClass { }; CustomViewClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], CustomViewClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_NAME_METADATA, CustomViewClass)).toBe('CustomViewContract'); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWFORM, CustomViewClass)).toBe(CustomViewForm); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWPAGE, CustomViewClass)).toBe(CustomViewPage); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_IMPORTS, CustomViewClass)).toEqual(['import1', 'import2']); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_CACHE, CustomViewClass)).toEqual({ key: 'customCache', ttl: 600 }); }); (0, vitest_1.it)('should override default viewForm and viewPage with provided values', () => { class OverriddenViewForm { } class OverriddenViewPage { } const options = { controllerName: 'contracttest', protoPath: '', viewForm: OverriddenViewForm, viewPage: OverriddenViewPage, }; let OverriddenViewClass = class OverriddenViewClass { }; OverriddenViewClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(options) ], OverriddenViewClass); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWFORM, OverriddenViewClass)).toBe(OverriddenViewForm); (0, vitest_1.expect)(Reflect.getMetadata(contract_decorator_1.CONTROLLER_VIEWPAGE, OverriddenViewClass)).toBe(OverriddenViewPage); }); (0, vitest_1.it)('should throw error if viewForm or viewPage are invalid types', () => { const invalidOptions1 = { viewForm: 'invalidViewForm', // Invalid type }; const invalidOptions2 = { viewPage: 123, // Invalid type }; (0, vitest_1.expect)(() => { let InvalidViewFormClass = class InvalidViewFormClass { }; InvalidViewFormClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(invalidOptions1) ], InvalidViewFormClass); }).toThrowError(); (0, vitest_1.expect)(() => { let InvalidViewPageClass = class InvalidViewPageClass { }; InvalidViewPageClass = tslib_1.__decorate([ (0, contract_decorator_1.Contract)(invalidOptions2) ], InvalidViewPageClass); }).toThrowError(); }); }); });