@hazeljs/core 0.2.0-beta.8 → 0.2.0-beta.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -690,4 +690,548 @@ describe('Decorators', () => {
690
690
  expect(routes[0].method).toBe('POST');
691
691
  });
692
692
  });
693
+ // -------------------------------------------------------------------------
694
+ // @Req() parameter decorator
695
+ // -------------------------------------------------------------------------
696
+ describe('Req', () => {
697
+ it('should register request injection type', () => {
698
+ class TestController {
699
+ handleRequest(req) {
700
+ return req;
701
+ }
702
+ }
703
+ __decorate([
704
+ __param(0, (0, decorators_1.Req)()),
705
+ __metadata("design:type", Function),
706
+ __metadata("design:paramtypes", [Object]),
707
+ __metadata("design:returntype", void 0)
708
+ ], TestController.prototype, "handleRequest", null);
709
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'handleRequest');
710
+ expect(injections[0].type).toBe('request');
711
+ });
712
+ it('should throw error when used outside a method parameter', () => {
713
+ expect(() => {
714
+ const decorator = (0, decorators_1.Req)();
715
+ decorator({}, undefined, 0);
716
+ }).toThrow('Req decorator must be used on a method parameter');
717
+ });
718
+ });
719
+ // -------------------------------------------------------------------------
720
+ // @Headers() parameter decorator
721
+ // -------------------------------------------------------------------------
722
+ describe('Headers', () => {
723
+ it('should register headers injection with a specific header name', () => {
724
+ class TestController {
725
+ getAuth(auth) {
726
+ return auth;
727
+ }
728
+ }
729
+ __decorate([
730
+ __param(0, (0, decorators_1.Headers)('authorization')),
731
+ __metadata("design:type", Function),
732
+ __metadata("design:paramtypes", [String]),
733
+ __metadata("design:returntype", void 0)
734
+ ], TestController.prototype, "getAuth", null);
735
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'getAuth');
736
+ expect(injections[0].type).toBe('headers');
737
+ expect(injections[0].name).toBe('authorization');
738
+ });
739
+ it('should register headers injection without a name (all headers)', () => {
740
+ class TestController {
741
+ getAllHeaders(headers) {
742
+ return headers;
743
+ }
744
+ }
745
+ __decorate([
746
+ __param(0, (0, decorators_1.Headers)()),
747
+ __metadata("design:type", Function),
748
+ __metadata("design:paramtypes", [Object]),
749
+ __metadata("design:returntype", void 0)
750
+ ], TestController.prototype, "getAllHeaders", null);
751
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'getAllHeaders');
752
+ expect(injections[0].type).toBe('headers');
753
+ expect(injections[0].name).toBeUndefined();
754
+ });
755
+ it('should throw error when used outside a method parameter', () => {
756
+ expect(() => {
757
+ const decorator = (0, decorators_1.Headers)();
758
+ decorator({}, undefined, 0);
759
+ }).toThrow('Headers decorator must be used on a method parameter');
760
+ });
761
+ });
762
+ // -------------------------------------------------------------------------
763
+ // @HttpCode() method decorator
764
+ // -------------------------------------------------------------------------
765
+ describe('HttpCode', () => {
766
+ it('should store the status code in metadata', () => {
767
+ class TestController {
768
+ create() {
769
+ return { id: 1 };
770
+ }
771
+ }
772
+ __decorate([
773
+ (0, decorators_1.HttpCode)(201),
774
+ __metadata("design:type", Function),
775
+ __metadata("design:paramtypes", []),
776
+ __metadata("design:returntype", void 0)
777
+ ], TestController.prototype, "create", null);
778
+ const code = Reflect.getMetadata('hazel:http-code', TestController.prototype, 'create');
779
+ expect(code).toBe(201);
780
+ });
781
+ it('should allow 204 for no-content responses', () => {
782
+ class TestController {
783
+ remove() {
784
+ return undefined;
785
+ }
786
+ }
787
+ __decorate([
788
+ (0, decorators_1.HttpCode)(204),
789
+ __metadata("design:type", Function),
790
+ __metadata("design:paramtypes", []),
791
+ __metadata("design:returntype", void 0)
792
+ ], TestController.prototype, "remove", null);
793
+ const code = Reflect.getMetadata('hazel:http-code', TestController.prototype, 'remove');
794
+ expect(code).toBe(204);
795
+ });
796
+ });
797
+ // -------------------------------------------------------------------------
798
+ // @Header() method decorator (custom response headers)
799
+ // -------------------------------------------------------------------------
800
+ describe('Header', () => {
801
+ it('should store a single custom response header', () => {
802
+ class TestController {
803
+ getVersion() {
804
+ return {};
805
+ }
806
+ }
807
+ __decorate([
808
+ (0, decorators_1.Header)('X-Version', '1.0'),
809
+ __metadata("design:type", Function),
810
+ __metadata("design:paramtypes", []),
811
+ __metadata("design:returntype", void 0)
812
+ ], TestController.prototype, "getVersion", null);
813
+ const headers = Reflect.getMetadata('hazel:headers', TestController.prototype, 'getVersion');
814
+ expect(headers).toHaveLength(1);
815
+ expect(headers[0]).toEqual({ name: 'X-Version', value: '1.0' });
816
+ });
817
+ it('should accumulate multiple @Header decorators on the same method', () => {
818
+ class TestController {
819
+ getData() {
820
+ return {};
821
+ }
822
+ }
823
+ __decorate([
824
+ (0, decorators_1.Header)('X-First', 'a'),
825
+ (0, decorators_1.Header)('X-Second', 'b'),
826
+ __metadata("design:type", Function),
827
+ __metadata("design:paramtypes", []),
828
+ __metadata("design:returntype", void 0)
829
+ ], TestController.prototype, "getData", null);
830
+ const headers = Reflect.getMetadata('hazel:headers', TestController.prototype, 'getData');
831
+ expect(headers).toHaveLength(2);
832
+ expect(headers.map((h) => h.name)).toContain('X-First');
833
+ expect(headers.map((h) => h.name)).toContain('X-Second');
834
+ });
835
+ });
836
+ // -------------------------------------------------------------------------
837
+ // @Redirect() method decorator
838
+ // -------------------------------------------------------------------------
839
+ describe('Redirect', () => {
840
+ it('should store redirect url with default 302 status', () => {
841
+ class TestController {
842
+ goSomewhere() {
843
+ return undefined;
844
+ }
845
+ }
846
+ __decorate([
847
+ (0, decorators_1.Redirect)('/new-location'),
848
+ __metadata("design:type", Function),
849
+ __metadata("design:paramtypes", []),
850
+ __metadata("design:returntype", void 0)
851
+ ], TestController.prototype, "goSomewhere", null);
852
+ const meta = Reflect.getMetadata('hazel:redirect', TestController.prototype, 'goSomewhere');
853
+ expect(meta).toEqual({ url: '/new-location', statusCode: 302 });
854
+ });
855
+ it('should store redirect url with custom status code', () => {
856
+ class TestController {
857
+ movedPermanently() {
858
+ return undefined;
859
+ }
860
+ }
861
+ __decorate([
862
+ (0, decorators_1.Redirect)('/permanent', 301),
863
+ __metadata("design:type", Function),
864
+ __metadata("design:paramtypes", []),
865
+ __metadata("design:returntype", void 0)
866
+ ], TestController.prototype, "movedPermanently", null);
867
+ const meta = Reflect.getMetadata('hazel:redirect', TestController.prototype, 'movedPermanently');
868
+ expect(meta).toEqual({ url: '/permanent', statusCode: 301 });
869
+ });
870
+ });
871
+ // -------------------------------------------------------------------------
872
+ // @AITask() method decorator
873
+ // -------------------------------------------------------------------------
874
+ describe('AITask', () => {
875
+ it('should store AI task options in metadata', () => {
876
+ const options = {
877
+ name: 'summarise',
878
+ prompt: 'Summarise the following text: {{input}}',
879
+ provider: 'openai',
880
+ model: 'gpt-4o',
881
+ outputType: 'string',
882
+ };
883
+ class TestController {
884
+ summarise() {
885
+ return undefined;
886
+ }
887
+ }
888
+ __decorate([
889
+ (0, decorators_1.AITask)(options),
890
+ __metadata("design:type", Function),
891
+ __metadata("design:paramtypes", []),
892
+ __metadata("design:returntype", void 0)
893
+ ], TestController.prototype, "summarise", null);
894
+ const meta = Reflect.getMetadata('hazel:ai-task', TestController.prototype, 'summarise');
895
+ expect(meta).toEqual(options);
896
+ });
897
+ });
898
+ // -------------------------------------------------------------------------
899
+ // @UsePipes() class-level metadata verification
900
+ // -------------------------------------------------------------------------
901
+ describe('UsePipes class-level metadata', () => {
902
+ it('should store pipe metadata on the class itself when used as class decorator', () => {
903
+ class TrimPipe {
904
+ transform(value) {
905
+ return value.trim();
906
+ }
907
+ }
908
+ let TestController = class TestController {
909
+ };
910
+ TestController = __decorate([
911
+ (0, decorators_1.UsePipes)(TrimPipe)
912
+ ], TestController);
913
+ const pipeMeta = Reflect.getMetadata('hazel:pipe', TestController);
914
+ expect(pipeMeta).toBeDefined();
915
+ expect(Array.isArray(pipeMeta)).toBe(true);
916
+ expect(pipeMeta.length).toBeGreaterThan(0);
917
+ });
918
+ });
919
+ // -------------------------------------------------------------------------
920
+ // @UseGuards() class-level metadata verification
921
+ // -------------------------------------------------------------------------
922
+ describe('UseGuards class-level metadata', () => {
923
+ it('should store guard metadata on the class when used as class decorator', () => {
924
+ class AuthGuard {
925
+ canActivate() {
926
+ return true;
927
+ }
928
+ }
929
+ let TestController = class TestController {
930
+ };
931
+ TestController = __decorate([
932
+ (0, decorators_1.UseGuards)(AuthGuard)
933
+ ], TestController);
934
+ const guardMeta = Reflect.getMetadata('hazel:guards', TestController);
935
+ expect(guardMeta).toBeDefined();
936
+ expect(guardMeta).toContain(AuthGuard);
937
+ });
938
+ it('should store guard metadata on the method when used as method decorator', () => {
939
+ class RoleGuard {
940
+ canActivate() {
941
+ return true;
942
+ }
943
+ }
944
+ class TestController {
945
+ getProtected() {
946
+ return {};
947
+ }
948
+ }
949
+ __decorate([
950
+ (0, decorators_1.UseGuards)(RoleGuard),
951
+ (0, decorators_1.Get)('/protected'),
952
+ __metadata("design:type", Function),
953
+ __metadata("design:paramtypes", []),
954
+ __metadata("design:returntype", void 0)
955
+ ], TestController.prototype, "getProtected", null);
956
+ const guardMeta = Reflect.getMetadata('hazel:guards', TestController.prototype, 'getProtected');
957
+ expect(guardMeta).toBeDefined();
958
+ expect(guardMeta).toContain(RoleGuard);
959
+ });
960
+ });
961
+ describe('Ip', () => {
962
+ it('should register ip injection type', () => {
963
+ class TestController {
964
+ get(ip) {
965
+ return { ip };
966
+ }
967
+ }
968
+ __decorate([
969
+ (0, decorators_1.Get)('/'),
970
+ __param(0, (0, decorators_1.Ip)()),
971
+ __metadata("design:type", Function),
972
+ __metadata("design:paramtypes", [String]),
973
+ __metadata("design:returntype", void 0)
974
+ ], TestController.prototype, "get", null);
975
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
976
+ expect(injections[0]).toEqual({ type: 'ip' });
977
+ });
978
+ it('should throw when used outside a method parameter', () => {
979
+ class Ctrl {
980
+ get() { }
981
+ }
982
+ expect(() => (0, decorators_1.Ip)()(Ctrl.prototype, undefined, 0)).toThrow('Ip decorator must be used on a method parameter');
983
+ });
984
+ });
985
+ describe('Host', () => {
986
+ it('should register host injection type', () => {
987
+ class TestController {
988
+ get(host) {
989
+ return { host };
990
+ }
991
+ }
992
+ __decorate([
993
+ (0, decorators_1.Get)('/'),
994
+ __param(0, (0, decorators_1.Host)()),
995
+ __metadata("design:type", Function),
996
+ __metadata("design:paramtypes", [String]),
997
+ __metadata("design:returntype", void 0)
998
+ ], TestController.prototype, "get", null);
999
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
1000
+ expect(injections[0]).toEqual({ type: 'host' });
1001
+ });
1002
+ it('should throw when used outside a method parameter', () => {
1003
+ class Ctrl {
1004
+ get() { }
1005
+ }
1006
+ expect(() => (0, decorators_1.Host)()(Ctrl.prototype, undefined, 0)).toThrow('Host decorator must be used on a method parameter');
1007
+ });
1008
+ });
1009
+ describe('Public', () => {
1010
+ it('should set public metadata on class', () => {
1011
+ let TestController = class TestController {
1012
+ };
1013
+ TestController = __decorate([
1014
+ (0, decorators_1.Public)()
1015
+ ], TestController);
1016
+ expect(Reflect.getMetadata('hazel:public', TestController)).toBe(true);
1017
+ });
1018
+ it('should set public metadata on method', () => {
1019
+ class TestController {
1020
+ login() {
1021
+ return {};
1022
+ }
1023
+ }
1024
+ __decorate([
1025
+ (0, decorators_1.Public)(),
1026
+ (0, decorators_1.Get)('/login'),
1027
+ __metadata("design:type", Function),
1028
+ __metadata("design:paramtypes", []),
1029
+ __metadata("design:returntype", void 0)
1030
+ ], TestController.prototype, "login", null);
1031
+ expect(Reflect.getMetadata('hazel:public', TestController.prototype, 'login')).toBe(true);
1032
+ });
1033
+ });
1034
+ describe('SkipAuth', () => {
1035
+ it('should be an alias for Public', () => {
1036
+ expect(decorators_1.SkipAuth).toBe(decorators_1.Public);
1037
+ });
1038
+ });
1039
+ describe('Timeout', () => {
1040
+ it('should store timeout in metadata', () => {
1041
+ class TestController {
1042
+ get() {
1043
+ return {};
1044
+ }
1045
+ }
1046
+ __decorate([
1047
+ (0, decorators_1.Timeout)(5000),
1048
+ (0, decorators_1.Get)('/'),
1049
+ __metadata("design:type", Function),
1050
+ __metadata("design:paramtypes", []),
1051
+ __metadata("design:returntype", void 0)
1052
+ ], TestController.prototype, "get", null);
1053
+ expect(Reflect.getMetadata('hazel:timeout', TestController.prototype, 'get')).toBe(5000);
1054
+ });
1055
+ });
1056
+ describe('Optional', () => {
1057
+ it('should add parameter index to optional indices', () => {
1058
+ class TestController {
1059
+ get(q) {
1060
+ return { q };
1061
+ }
1062
+ }
1063
+ __decorate([
1064
+ (0, decorators_1.Get)('/'),
1065
+ __param(0, (0, decorators_1.Optional)()),
1066
+ __param(0, (0, decorators_1.Query)('q')),
1067
+ __metadata("design:type", Function),
1068
+ __metadata("design:paramtypes", [String]),
1069
+ __metadata("design:returntype", void 0)
1070
+ ], TestController.prototype, "get", null);
1071
+ const indices = Reflect.getMetadata('hazel:optional-indices', TestController, 'get');
1072
+ expect(indices).toContain(0);
1073
+ });
1074
+ it('should throw when used outside a method parameter', () => {
1075
+ class Ctrl {
1076
+ get() { }
1077
+ }
1078
+ expect(() => (0, decorators_1.Optional)()(Ctrl.prototype, undefined, 0)).toThrow('Optional decorator must be used on a method parameter');
1079
+ });
1080
+ });
1081
+ describe('Session', () => {
1082
+ it('should register session injection type', () => {
1083
+ class TestController {
1084
+ get(session) {
1085
+ return { session };
1086
+ }
1087
+ }
1088
+ __decorate([
1089
+ (0, decorators_1.Get)('/'),
1090
+ __param(0, (0, decorators_1.Session)()),
1091
+ __metadata("design:type", Function),
1092
+ __metadata("design:paramtypes", [Object]),
1093
+ __metadata("design:returntype", void 0)
1094
+ ], TestController.prototype, "get", null);
1095
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
1096
+ expect(injections[0]).toEqual({ type: 'session' });
1097
+ });
1098
+ it('should throw when used outside a method parameter', () => {
1099
+ class Ctrl {
1100
+ get() { }
1101
+ }
1102
+ expect(() => (0, decorators_1.Session)()(Ctrl.prototype, undefined, 0)).toThrow('Session decorator must be used on a method parameter');
1103
+ });
1104
+ });
1105
+ describe('Retry', () => {
1106
+ it('should store retry options and add RetryInterceptor to route', () => {
1107
+ class TestController {
1108
+ get() {
1109
+ return {};
1110
+ }
1111
+ }
1112
+ __decorate([
1113
+ (0, decorators_1.Retry)({ count: 3, delay: 100 }),
1114
+ (0, decorators_1.Get)('/'),
1115
+ __metadata("design:type", Function),
1116
+ __metadata("design:paramtypes", []),
1117
+ __metadata("design:returntype", void 0)
1118
+ ], TestController.prototype, "get", null);
1119
+ expect(Reflect.getMetadata('hazel:retry', TestController.prototype, 'get')).toEqual({
1120
+ count: 3,
1121
+ delay: 100,
1122
+ });
1123
+ const routes = Reflect.getMetadata('hazel:routes', TestController);
1124
+ const route = routes.find((r) => r.propertyKey === 'get');
1125
+ expect(route?.interceptors?.[0]?.type?.name).toBe('RetryInterceptor');
1126
+ });
1127
+ });
1128
+ describe('ApiTags', () => {
1129
+ it('should set api tags on class', () => {
1130
+ let TestController = class TestController {
1131
+ };
1132
+ TestController = __decorate([
1133
+ (0, decorators_1.ApiTags)('users', 'admin')
1134
+ ], TestController);
1135
+ expect(Reflect.getMetadata('hazel:api:tags', TestController)).toEqual(['users', 'admin']);
1136
+ });
1137
+ it('should set api tags on method', () => {
1138
+ class TestController {
1139
+ login() {
1140
+ return {};
1141
+ }
1142
+ }
1143
+ __decorate([
1144
+ (0, decorators_1.ApiTags)('auth'),
1145
+ (0, decorators_1.Get)('/login'),
1146
+ __metadata("design:type", Function),
1147
+ __metadata("design:paramtypes", []),
1148
+ __metadata("design:returntype", void 0)
1149
+ ], TestController.prototype, "login", null);
1150
+ expect(Reflect.getMetadata('hazel:api:tags', TestController.prototype, 'login')).toEqual(['auth']);
1151
+ });
1152
+ });
1153
+ describe('ApiOperation', () => {
1154
+ it('should store operation options when given object', () => {
1155
+ class TestController {
1156
+ get() {
1157
+ return {};
1158
+ }
1159
+ }
1160
+ __decorate([
1161
+ (0, decorators_1.ApiOperation)({ summary: 'Get user', description: 'Returns a user', operationId: 'getUser' }),
1162
+ (0, decorators_1.Get)('/'),
1163
+ __metadata("design:type", Function),
1164
+ __metadata("design:paramtypes", []),
1165
+ __metadata("design:returntype", void 0)
1166
+ ], TestController.prototype, "get", null);
1167
+ expect(Reflect.getMetadata('hazel:api:operation', TestController.prototype, 'get')).toEqual({
1168
+ summary: 'Get user',
1169
+ description: 'Returns a user',
1170
+ operationId: 'getUser',
1171
+ });
1172
+ });
1173
+ it('should accept string as summary', () => {
1174
+ class TestController {
1175
+ get() {
1176
+ return {};
1177
+ }
1178
+ }
1179
+ __decorate([
1180
+ (0, decorators_1.ApiOperation)('List users'),
1181
+ (0, decorators_1.Get)('/'),
1182
+ __metadata("design:type", Function),
1183
+ __metadata("design:paramtypes", []),
1184
+ __metadata("design:returntype", void 0)
1185
+ ], TestController.prototype, "get", null);
1186
+ expect(Reflect.getMetadata('hazel:api:operation', TestController.prototype, 'get')).toEqual({
1187
+ summary: 'List users',
1188
+ });
1189
+ });
1190
+ });
1191
+ describe('SetMetadata and getMetadata', () => {
1192
+ it('should set and get class-level metadata', () => {
1193
+ let AdminController = class AdminController {
1194
+ };
1195
+ AdminController = __decorate([
1196
+ (0, decorators_1.SetMetadata)('roles', ['admin'])
1197
+ ], AdminController);
1198
+ expect((0, decorators_1.getMetadata)('roles', AdminController)).toEqual(['admin']);
1199
+ expect(Reflect.getMetadata(`${decorators_1.CUSTOM_METADATA_PREFIX}roles`, AdminController)).toEqual(['admin']);
1200
+ });
1201
+ it('should set and get method-level metadata', () => {
1202
+ class TestController {
1203
+ get() {
1204
+ return {};
1205
+ }
1206
+ }
1207
+ __decorate([
1208
+ (0, decorators_1.SetMetadata)('roles', ['user']),
1209
+ (0, decorators_1.Get)('/'),
1210
+ __metadata("design:type", Function),
1211
+ __metadata("design:paramtypes", []),
1212
+ __metadata("design:returntype", void 0)
1213
+ ], TestController.prototype, "get", null);
1214
+ expect((0, decorators_1.getMetadata)('roles', TestController.prototype, 'get')).toEqual(['user']);
1215
+ });
1216
+ });
1217
+ describe('createParamDecorator', () => {
1218
+ it('should register custom inject metadata', () => {
1219
+ const MyParam = (0, decorators_1.createParamDecorator)((_req, ctx) => ctx.query?.foo);
1220
+ class TestController {
1221
+ get(foo) {
1222
+ return { foo };
1223
+ }
1224
+ }
1225
+ __decorate([
1226
+ (0, decorators_1.Get)('/'),
1227
+ __param(0, MyParam),
1228
+ __metadata("design:type", Function),
1229
+ __metadata("design:paramtypes", [String]),
1230
+ __metadata("design:returntype", void 0)
1231
+ ], TestController.prototype, "get", null);
1232
+ const injections = Reflect.getMetadata('hazel:inject', TestController, 'get');
1233
+ expect(injections).toBeDefined();
1234
+ expect(injections[0]).toEqual({ type: 'custom', resolve: expect.any(Function) });
1235
+ });
1236
+ });
693
1237
  });
@@ -679,4 +679,132 @@ describe('HazelApp', () => {
679
679
  expect(app).toBeDefined();
680
680
  });
681
681
  });
682
+ // -------------------------------------------------------------------------
683
+ // getRouter
684
+ // -------------------------------------------------------------------------
685
+ describe('getRouter', () => {
686
+ let app;
687
+ beforeEach(() => {
688
+ class AppModule {
689
+ }
690
+ Reflect.defineMetadata('hazel:module', {}, AppModule);
691
+ app = new hazel_app_1.HazelApp(AppModule);
692
+ });
693
+ it('should return the internal Router instance', () => {
694
+ const router = app.getRouter();
695
+ expect(router).toBeDefined();
696
+ expect(typeof router.registerController).toBe('function');
697
+ });
698
+ });
699
+ // -------------------------------------------------------------------------
700
+ // registerShutdownHandler
701
+ // -------------------------------------------------------------------------
702
+ describe('registerShutdownHandler', () => {
703
+ let app;
704
+ beforeEach(() => {
705
+ class AppModule {
706
+ }
707
+ Reflect.defineMetadata('hazel:module', {}, AppModule);
708
+ app = new hazel_app_1.HazelApp(AppModule);
709
+ });
710
+ it('should register a shutdown handler without throwing', () => {
711
+ expect(() => {
712
+ app.registerShutdownHandler({
713
+ name: 'db',
714
+ handler: async () => { },
715
+ });
716
+ }).not.toThrow();
717
+ });
718
+ it('should register a shutdown handler with a timeout', () => {
719
+ expect(() => {
720
+ app.registerShutdownHandler({
721
+ name: 'cache',
722
+ handler: async () => { },
723
+ timeout: 5000,
724
+ });
725
+ }).not.toThrow();
726
+ });
727
+ });
728
+ // -------------------------------------------------------------------------
729
+ // registerHealthCheck
730
+ // -------------------------------------------------------------------------
731
+ describe('registerHealthCheck', () => {
732
+ let app;
733
+ beforeEach(() => {
734
+ class AppModule {
735
+ }
736
+ Reflect.defineMetadata('hazel:module', {}, AppModule);
737
+ app = new hazel_app_1.HazelApp(AppModule);
738
+ });
739
+ it('should register a health check without throwing', () => {
740
+ expect(() => {
741
+ app.registerHealthCheck({
742
+ name: 'db',
743
+ check: async () => ({ status: 'healthy' }),
744
+ });
745
+ }).not.toThrow();
746
+ });
747
+ it('should register a critical health check with a timeout', () => {
748
+ expect(() => {
749
+ app.registerHealthCheck({
750
+ name: 'redis',
751
+ check: async () => ({ status: 'healthy', message: 'connected' }),
752
+ critical: true,
753
+ timeout: 3000,
754
+ });
755
+ }).not.toThrow();
756
+ });
757
+ });
758
+ // -------------------------------------------------------------------------
759
+ // addEarlyHandler
760
+ // -------------------------------------------------------------------------
761
+ describe('addEarlyHandler', () => {
762
+ let app;
763
+ beforeEach(() => {
764
+ class AppModule {
765
+ }
766
+ Reflect.defineMetadata('hazel:module', {}, AppModule);
767
+ app = new hazel_app_1.HazelApp(AppModule);
768
+ });
769
+ it('should register an early handler without throwing', () => {
770
+ const handler = jest.fn().mockResolvedValue(false);
771
+ expect(() => {
772
+ app.addEarlyHandler('/internal', handler);
773
+ }).not.toThrow();
774
+ });
775
+ it('should register multiple early handlers', () => {
776
+ const h1 = jest.fn().mockResolvedValue(false);
777
+ const h2 = jest.fn().mockResolvedValue(false);
778
+ expect(() => {
779
+ app.addEarlyHandler('/a', h1);
780
+ app.addEarlyHandler('/b', h2);
781
+ }).not.toThrow();
782
+ });
783
+ });
784
+ // -------------------------------------------------------------------------
785
+ // addProxyHandler
786
+ // -------------------------------------------------------------------------
787
+ describe('addProxyHandler', () => {
788
+ let app;
789
+ beforeEach(() => {
790
+ class AppModule {
791
+ }
792
+ Reflect.defineMetadata('hazel:module', {}, AppModule);
793
+ app = new hazel_app_1.HazelApp(AppModule);
794
+ });
795
+ it('should register a proxy handler without throwing', () => {
796
+ const handler = jest.fn().mockResolvedValue(false);
797
+ expect(() => {
798
+ app.addProxyHandler('/api', handler);
799
+ }).not.toThrow();
800
+ });
801
+ it('should register multiple proxy handlers for different prefixes', () => {
802
+ const h1 = jest.fn().mockResolvedValue(false);
803
+ const h2 = jest.fn().mockResolvedValue(false);
804
+ expect(() => {
805
+ app.addProxyHandler('/service-a', h1);
806
+ app.addProxyHandler('/service-b', h2);
807
+ }).not.toThrow();
808
+ });
809
+ });
682
810
  });