@dangao/bun-server 1.0.0 → 1.0.3

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.
Files changed (200) hide show
  1. package/package.json +4 -2
  2. package/readme.md +163 -2
  3. package/src/auth/controller.ts +148 -0
  4. package/src/auth/decorators.ts +81 -0
  5. package/src/auth/index.ts +12 -0
  6. package/src/auth/jwt.ts +169 -0
  7. package/src/auth/oauth2.ts +244 -0
  8. package/src/auth/types.ts +248 -0
  9. package/src/cache/cache-module.ts +67 -0
  10. package/src/cache/decorators.ts +202 -0
  11. package/src/cache/index.ts +27 -0
  12. package/src/cache/service.ts +151 -0
  13. package/src/cache/types.ts +420 -0
  14. package/src/config/config-module.ts +76 -0
  15. package/src/config/index.ts +8 -0
  16. package/src/config/service.ts +93 -0
  17. package/src/config/types.ts +27 -0
  18. package/src/controller/controller.ts +251 -0
  19. package/src/controller/decorators.ts +84 -0
  20. package/src/controller/index.ts +7 -0
  21. package/src/controller/metadata.ts +27 -0
  22. package/src/controller/param-binder.ts +157 -0
  23. package/src/core/application.ts +233 -0
  24. package/src/core/context.ts +228 -0
  25. package/src/core/index.ts +4 -0
  26. package/src/core/server.ts +128 -0
  27. package/src/core/types.ts +2 -0
  28. package/src/database/connection-manager.ts +239 -0
  29. package/src/database/connection-pool.ts +322 -0
  30. package/src/database/database-extension.ts +62 -0
  31. package/src/database/database-module.ts +115 -0
  32. package/src/database/health-indicator.ts +51 -0
  33. package/src/database/index.ts +47 -0
  34. package/src/database/orm/decorators.ts +155 -0
  35. package/src/database/orm/drizzle-repository.ts +39 -0
  36. package/src/database/orm/index.ts +23 -0
  37. package/src/database/orm/repository-decorator.ts +39 -0
  38. package/src/database/orm/repository.ts +103 -0
  39. package/src/database/orm/service.ts +49 -0
  40. package/src/database/orm/transaction-decorator.ts +45 -0
  41. package/src/database/orm/transaction-interceptor.ts +243 -0
  42. package/src/database/orm/transaction-manager.ts +276 -0
  43. package/src/database/orm/transaction-types.ts +140 -0
  44. package/src/database/orm/types.ts +99 -0
  45. package/src/database/service.ts +221 -0
  46. package/src/database/types.ts +171 -0
  47. package/src/di/container.ts +398 -0
  48. package/src/di/decorators.ts +228 -0
  49. package/src/di/index.ts +4 -0
  50. package/src/di/module-registry.ts +188 -0
  51. package/src/di/module.ts +65 -0
  52. package/src/di/types.ts +67 -0
  53. package/src/error/error-codes.ts +222 -0
  54. package/src/error/filter.ts +43 -0
  55. package/src/error/handler.ts +66 -0
  56. package/src/error/http-exception.ts +115 -0
  57. package/src/error/i18n.ts +217 -0
  58. package/src/error/index.ts +16 -0
  59. package/src/extensions/index.ts +5 -0
  60. package/src/extensions/logger-extension.ts +31 -0
  61. package/src/extensions/logger-module.ts +69 -0
  62. package/src/extensions/types.ts +14 -0
  63. package/src/files/index.ts +5 -0
  64. package/src/files/static-middleware.ts +53 -0
  65. package/src/files/storage.ts +67 -0
  66. package/src/files/types.ts +33 -0
  67. package/src/files/upload-middleware.ts +45 -0
  68. package/src/health/controller.ts +76 -0
  69. package/src/health/health-module.ts +51 -0
  70. package/src/health/index.ts +12 -0
  71. package/src/health/types.ts +28 -0
  72. package/src/index.ts +270 -0
  73. package/src/metrics/collector.ts +209 -0
  74. package/src/metrics/controller.ts +40 -0
  75. package/src/metrics/index.ts +15 -0
  76. package/src/metrics/metrics-module.ts +58 -0
  77. package/src/metrics/middleware.ts +46 -0
  78. package/src/metrics/prometheus.ts +79 -0
  79. package/src/metrics/types.ts +103 -0
  80. package/src/middleware/builtin/cors.ts +60 -0
  81. package/src/middleware/builtin/error-handler.ts +90 -0
  82. package/src/middleware/builtin/file-upload.ts +42 -0
  83. package/src/middleware/builtin/index.ts +14 -0
  84. package/src/middleware/builtin/logger.ts +91 -0
  85. package/src/middleware/builtin/rate-limit.ts +252 -0
  86. package/src/middleware/builtin/static-file.ts +88 -0
  87. package/src/middleware/decorators.ts +91 -0
  88. package/src/middleware/index.ts +11 -0
  89. package/src/middleware/middleware.ts +13 -0
  90. package/src/middleware/pipeline.ts +93 -0
  91. package/src/queue/decorators.ts +110 -0
  92. package/src/queue/index.ts +26 -0
  93. package/src/queue/queue-module.ts +64 -0
  94. package/src/queue/service.ts +302 -0
  95. package/src/queue/types.ts +341 -0
  96. package/src/request/body-parser.ts +133 -0
  97. package/src/request/file-handler.ts +46 -0
  98. package/src/request/index.ts +5 -0
  99. package/src/request/request.ts +107 -0
  100. package/src/request/response.ts +150 -0
  101. package/src/router/decorators.ts +122 -0
  102. package/src/router/index.ts +6 -0
  103. package/src/router/registry.ts +98 -0
  104. package/src/router/route.ts +140 -0
  105. package/src/router/router.ts +241 -0
  106. package/src/router/types.ts +27 -0
  107. package/src/security/access-decision-manager.ts +34 -0
  108. package/src/security/authentication-manager.ts +47 -0
  109. package/src/security/context.ts +92 -0
  110. package/src/security/filter.ts +162 -0
  111. package/src/security/index.ts +8 -0
  112. package/src/security/providers/index.ts +3 -0
  113. package/src/security/providers/jwt-provider.ts +60 -0
  114. package/src/security/providers/oauth2-provider.ts +70 -0
  115. package/src/security/security-module.ts +145 -0
  116. package/src/security/types.ts +165 -0
  117. package/src/session/decorators.ts +45 -0
  118. package/src/session/index.ts +19 -0
  119. package/src/session/middleware.ts +143 -0
  120. package/src/session/service.ts +218 -0
  121. package/src/session/session-module.ts +69 -0
  122. package/src/session/types.ts +373 -0
  123. package/src/swagger/decorators.ts +133 -0
  124. package/src/swagger/generator.ts +234 -0
  125. package/src/swagger/index.ts +7 -0
  126. package/src/swagger/swagger-extension.ts +41 -0
  127. package/src/swagger/swagger-module.ts +83 -0
  128. package/src/swagger/types.ts +188 -0
  129. package/src/swagger/ui.ts +98 -0
  130. package/src/testing/harness.ts +96 -0
  131. package/src/validation/decorators.ts +95 -0
  132. package/src/validation/errors.ts +28 -0
  133. package/src/validation/index.ts +14 -0
  134. package/src/validation/types.ts +35 -0
  135. package/src/validation/validator.ts +63 -0
  136. package/src/websocket/decorators.ts +51 -0
  137. package/src/websocket/index.ts +12 -0
  138. package/src/websocket/registry.ts +133 -0
  139. package/tests/cache/cache-module.test.ts +212 -0
  140. package/tests/config/config-module.test.ts +151 -0
  141. package/tests/controller/controller.test.ts +189 -0
  142. package/tests/core/application.test.ts +57 -0
  143. package/tests/core/context-body.test.ts +44 -0
  144. package/tests/core/context.test.ts +86 -0
  145. package/tests/core/edge-cases.test.ts +432 -0
  146. package/tests/database/database-module.test.ts +385 -0
  147. package/tests/database/orm.test.ts +164 -0
  148. package/tests/database/postgres-mysql-integration.test.ts +395 -0
  149. package/tests/database/transaction.test.ts +238 -0
  150. package/tests/di/container.test.ts +264 -0
  151. package/tests/di/module.test.ts +128 -0
  152. package/tests/error/error-codes.test.ts +121 -0
  153. package/tests/error/error-handler.test.ts +68 -0
  154. package/tests/error/error-handling.test.ts +254 -0
  155. package/tests/error/http-exception.test.ts +37 -0
  156. package/tests/error/i18n-integration.test.ts +175 -0
  157. package/tests/extensions/logger-extension.test.ts +40 -0
  158. package/tests/files/static-middleware.test.ts +67 -0
  159. package/tests/files/upload-middleware.test.ts +43 -0
  160. package/tests/health/health-module.test.ts +116 -0
  161. package/tests/integration/application-router.test.ts +85 -0
  162. package/tests/integration/body-parsing.test.ts +88 -0
  163. package/tests/integration/cache-e2e.test.ts +114 -0
  164. package/tests/integration/oauth2-e2e.test.ts +615 -0
  165. package/tests/integration/session-e2e.test.ts +207 -0
  166. package/tests/metrics/metrics-module.test.ts +178 -0
  167. package/tests/middleware/builtin.test.ts +206 -0
  168. package/tests/middleware/file-upload.test.ts +41 -0
  169. package/tests/middleware/middleware.test.ts +120 -0
  170. package/tests/middleware/pipeline.test.ts +72 -0
  171. package/tests/middleware/rate-limit.test.ts +314 -0
  172. package/tests/middleware/static-file.test.ts +62 -0
  173. package/tests/perf/harness.test.ts +48 -0
  174. package/tests/perf/optimization.test.ts +183 -0
  175. package/tests/perf/regression.test.ts +120 -0
  176. package/tests/queue/queue-module.test.ts +217 -0
  177. package/tests/request/body-parser.test.ts +96 -0
  178. package/tests/request/response.test.ts +99 -0
  179. package/tests/router/decorators.test.ts +48 -0
  180. package/tests/router/registry.test.ts +51 -0
  181. package/tests/router/route.test.ts +71 -0
  182. package/tests/router/router-normalization.test.ts +106 -0
  183. package/tests/router/router.test.ts +133 -0
  184. package/tests/security/access-decision-manager.test.ts +84 -0
  185. package/tests/security/authentication-manager.test.ts +81 -0
  186. package/tests/security/context.test.ts +302 -0
  187. package/tests/security/filter.test.ts +225 -0
  188. package/tests/security/jwt-provider.test.ts +106 -0
  189. package/tests/security/oauth2-provider.test.ts +269 -0
  190. package/tests/security/security-module.test.ts +143 -0
  191. package/tests/session/session-module.test.ts +307 -0
  192. package/tests/stress/di-stress.test.ts +30 -0
  193. package/tests/swagger/decorators.test.ts +153 -0
  194. package/tests/swagger/generator.test.ts +202 -0
  195. package/tests/swagger/swagger-extension.test.ts +72 -0
  196. package/tests/swagger/swagger-module.test.ts +79 -0
  197. package/tests/utils/test-port.ts +10 -0
  198. package/tests/validation/controller-validation.test.ts +64 -0
  199. package/tests/validation/validation.test.ts +42 -0
  200. package/tests/websocket/gateway.test.ts +68 -0
@@ -0,0 +1,269 @@
1
+ import { describe, expect, test, beforeEach } from 'bun:test';
2
+ import { OAuth2AuthenticationProvider } from '../../src/security/providers/oauth2-provider';
3
+ import { OAuth2Service } from '../../src/auth/oauth2';
4
+ import { JWTUtil } from '../../src/auth/jwt';
5
+ import type { OAuth2TokenRequest } from '../../src/auth/types';
6
+
7
+ describe('OAuth2AuthenticationProvider', () => {
8
+ let jwtUtil: JWTUtil;
9
+ let oauth2Service: OAuth2Service;
10
+ let provider: OAuth2AuthenticationProvider;
11
+
12
+ beforeEach(() => {
13
+ jwtUtil = new JWTUtil({
14
+ secret: 'test-secret-key',
15
+ accessTokenExpiresIn: 3600,
16
+ });
17
+ oauth2Service = new OAuth2Service(jwtUtil, [
18
+ {
19
+ clientId: 'test-client',
20
+ clientSecret: 'test-secret',
21
+ redirectUris: ['http://localhost/callback'],
22
+ grantTypes: ['authorization_code'],
23
+ },
24
+ ]);
25
+ provider = new OAuth2AuthenticationProvider(oauth2Service, jwtUtil);
26
+ });
27
+
28
+ test('should support oauth2 and authorization_code types', () => {
29
+ expect(provider.supports('oauth2')).toBe(true);
30
+ expect(provider.supports('authorization_code')).toBe(true);
31
+ expect(provider.supports('OAUTH2')).toBe(true);
32
+ expect(provider.supports('AUTHORIZATION_CODE')).toBe(true);
33
+ expect(provider.supports('jwt')).toBe(false);
34
+ expect(provider.supports('bearer')).toBe(false);
35
+ });
36
+
37
+ test('should authenticate with valid authorization code', async () => {
38
+ // 生成授权码
39
+ const code = oauth2Service.generateAuthorizationCode(
40
+ 'test-client',
41
+ 'http://localhost/callback',
42
+ 'user-1',
43
+ 'read write',
44
+ );
45
+
46
+ const tokenRequest: OAuth2TokenRequest = {
47
+ code,
48
+ clientId: 'test-client',
49
+ clientSecret: 'test-secret',
50
+ redirectUri: 'http://localhost/callback',
51
+ grantType: 'authorization_code',
52
+ };
53
+
54
+ const authentication = await provider.authenticate({
55
+ principal: '',
56
+ credentials: tokenRequest,
57
+ type: 'oauth2',
58
+ });
59
+
60
+ expect(authentication).not.toBeNull();
61
+ expect(authentication?.authenticated).toBe(true);
62
+ expect(authentication?.principal.id).toBe('user-1');
63
+ expect(authentication?.principal.username).toBe('user-1'); // OAuth2Service 默认使用 userId 作为 username
64
+ expect(authentication?.credentials.type).toBe('oauth2');
65
+ expect(authentication?.credentials.data).toBeDefined();
66
+ expect(authentication?.credentials.data.accessToken).toBeDefined();
67
+ expect(authentication?.credentials.data.refreshToken).toBeDefined();
68
+ expect(authentication?.authorities).toEqual([]);
69
+ });
70
+
71
+ test('should return null for invalid grant type', async () => {
72
+ const tokenRequest: OAuth2TokenRequest = {
73
+ code: 'invalid-code',
74
+ clientId: 'test-client',
75
+ clientSecret: 'test-secret',
76
+ redirectUri: 'http://localhost/callback',
77
+ grantType: 'refresh_token', // 不支持的 grant type
78
+ };
79
+
80
+ const authentication = await provider.authenticate({
81
+ principal: '',
82
+ credentials: tokenRequest,
83
+ type: 'oauth2',
84
+ });
85
+
86
+ expect(authentication).toBeNull();
87
+ });
88
+
89
+ test('should return null for invalid authorization code', async () => {
90
+ const tokenRequest: OAuth2TokenRequest = {
91
+ code: 'invalid-code',
92
+ clientId: 'test-client',
93
+ clientSecret: 'test-secret',
94
+ redirectUri: 'http://localhost/callback',
95
+ grantType: 'authorization_code',
96
+ };
97
+
98
+ const authentication = await provider.authenticate({
99
+ principal: '',
100
+ credentials: tokenRequest,
101
+ type: 'oauth2',
102
+ });
103
+
104
+ expect(authentication).toBeNull();
105
+ });
106
+
107
+ test('should return null for invalid client credentials', async () => {
108
+ const code = oauth2Service.generateAuthorizationCode(
109
+ 'test-client',
110
+ 'http://localhost/callback',
111
+ 'user-1',
112
+ );
113
+
114
+ const tokenRequest: OAuth2TokenRequest = {
115
+ code,
116
+ clientId: 'test-client',
117
+ clientSecret: 'wrong-secret', // 错误的 secret
118
+ redirectUri: 'http://localhost/callback',
119
+ grantType: 'authorization_code',
120
+ };
121
+
122
+ const authentication = await provider.authenticate({
123
+ principal: '',
124
+ credentials: tokenRequest,
125
+ type: 'oauth2',
126
+ });
127
+
128
+ expect(authentication).toBeNull();
129
+ });
130
+
131
+ test('should return null for invalid redirect URI', async () => {
132
+ const code = oauth2Service.generateAuthorizationCode(
133
+ 'test-client',
134
+ 'http://localhost/callback',
135
+ 'user-1',
136
+ );
137
+
138
+ const tokenRequest: OAuth2TokenRequest = {
139
+ code,
140
+ clientId: 'test-client',
141
+ clientSecret: 'test-secret',
142
+ redirectUri: 'http://localhost/wrong-callback', // 错误的 redirect URI
143
+ grantType: 'authorization_code',
144
+ };
145
+
146
+ const authentication = await provider.authenticate({
147
+ principal: '',
148
+ credentials: tokenRequest,
149
+ type: 'oauth2',
150
+ });
151
+
152
+ expect(authentication).toBeNull();
153
+ });
154
+
155
+ test('should return null for expired authorization code', async () => {
156
+ // 创建一个快速过期的 OAuth2Service
157
+ const expiredOAuth2Service = new OAuth2Service(jwtUtil, [
158
+ {
159
+ clientId: 'test-client',
160
+ clientSecret: 'test-secret',
161
+ redirectUris: ['http://localhost/callback'],
162
+ grantTypes: ['authorization_code'],
163
+ },
164
+ ], {
165
+ expiresIn: 0.1, // 0.1 秒过期
166
+ });
167
+ const expiredProvider = new OAuth2AuthenticationProvider(
168
+ expiredOAuth2Service,
169
+ jwtUtil,
170
+ );
171
+
172
+ const code = expiredOAuth2Service.generateAuthorizationCode(
173
+ 'test-client',
174
+ 'http://localhost/callback',
175
+ 'user-1',
176
+ );
177
+
178
+ // 等待授权码过期
179
+ await new Promise((resolve) => setTimeout(resolve, 200));
180
+
181
+ const tokenRequest: OAuth2TokenRequest = {
182
+ code,
183
+ clientId: 'test-client',
184
+ clientSecret: 'test-secret',
185
+ redirectUri: 'http://localhost/callback',
186
+ grantType: 'authorization_code',
187
+ };
188
+
189
+ const authentication = await expiredProvider.authenticate({
190
+ principal: '',
191
+ credentials: tokenRequest,
192
+ type: 'oauth2',
193
+ });
194
+
195
+ expect(authentication).toBeNull();
196
+ });
197
+
198
+ test('should return null for missing credentials', async () => {
199
+ const authentication = await provider.authenticate({
200
+ principal: '',
201
+ credentials: null as any,
202
+ type: 'oauth2',
203
+ });
204
+
205
+ expect(authentication).toBeNull();
206
+ });
207
+
208
+ test('should return null for invalid credentials format', async () => {
209
+ const authentication = await provider.authenticate({
210
+ principal: '',
211
+ credentials: 'invalid-string' as any,
212
+ type: 'oauth2',
213
+ });
214
+
215
+ expect(authentication).toBeNull();
216
+ });
217
+
218
+ test('should include user roles when available', async () => {
219
+ // 创建带用户提供者的 OAuth2Service
220
+ const oauth2ServiceWithUser = new OAuth2Service(
221
+ jwtUtil,
222
+ [
223
+ {
224
+ clientId: 'test-client',
225
+ clientSecret: 'test-secret',
226
+ redirectUris: ['http://localhost/callback'],
227
+ grantTypes: ['authorization_code'],
228
+ },
229
+ ],
230
+ {},
231
+ async (userId: string) => {
232
+ return {
233
+ id: userId,
234
+ username: 'testuser',
235
+ roles: ['admin', 'user'],
236
+ };
237
+ },
238
+ );
239
+ const providerWithUser = new OAuth2AuthenticationProvider(
240
+ oauth2ServiceWithUser,
241
+ jwtUtil,
242
+ );
243
+
244
+ const code = oauth2ServiceWithUser.generateAuthorizationCode(
245
+ 'test-client',
246
+ 'http://localhost/callback',
247
+ 'user-1',
248
+ );
249
+
250
+ const tokenRequest: OAuth2TokenRequest = {
251
+ code,
252
+ clientId: 'test-client',
253
+ clientSecret: 'test-secret',
254
+ redirectUri: 'http://localhost/callback',
255
+ grantType: 'authorization_code',
256
+ };
257
+
258
+ const authentication = await providerWithUser.authenticate({
259
+ principal: '',
260
+ credentials: tokenRequest,
261
+ type: 'oauth2',
262
+ });
263
+
264
+ expect(authentication).not.toBeNull();
265
+ expect(authentication?.principal.roles).toEqual(['admin', 'user']);
266
+ expect(authentication?.authorities).toEqual(['admin', 'user']);
267
+ });
268
+ });
269
+
@@ -0,0 +1,143 @@
1
+ import { describe, expect, test, beforeEach } from 'bun:test';
2
+ import { SecurityModule } from '../../src/security/security-module';
3
+ import { MODULE_METADATA_KEY } from '../../src/di/module';
4
+ import { JWT_UTIL_TOKEN, OAUTH2_SERVICE_TOKEN } from '../../src/auth/controller';
5
+ import { AuthenticationManager } from '../../src/security/authentication-manager';
6
+ import 'reflect-metadata';
7
+
8
+ describe('SecurityModule', () => {
9
+ beforeEach(() => {
10
+ // 清除模块元数据
11
+ Reflect.deleteMetadata(MODULE_METADATA_KEY, SecurityModule);
12
+ });
13
+
14
+ test('should create module with forRoot', () => {
15
+ const module = SecurityModule.forRoot({
16
+ jwt: {
17
+ secret: 'test-secret',
18
+ accessTokenExpiresIn: 3600,
19
+ },
20
+ });
21
+
22
+ expect(module).toBe(SecurityModule);
23
+ });
24
+
25
+ test('should register JWT and OAuth2 services', () => {
26
+ SecurityModule.forRoot({
27
+ jwt: {
28
+ secret: 'test-secret',
29
+ accessTokenExpiresIn: 3600,
30
+ },
31
+ oauth2Clients: [
32
+ {
33
+ clientId: 'test-client',
34
+ clientSecret: 'test-secret',
35
+ redirectUris: ['http://localhost/callback'],
36
+ grantTypes: ['authorization_code'],
37
+ },
38
+ ],
39
+ });
40
+
41
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule);
42
+ expect(metadata).toBeDefined();
43
+ expect(metadata.providers).toBeDefined();
44
+
45
+ const jwtProvider = metadata.providers.find(
46
+ (p: any) => p.provide === JWT_UTIL_TOKEN,
47
+ );
48
+ expect(jwtProvider).toBeDefined();
49
+
50
+ const oauth2Provider = metadata.providers.find(
51
+ (p: any) => p.provide === OAUTH2_SERVICE_TOKEN,
52
+ );
53
+ expect(oauth2Provider).toBeDefined();
54
+
55
+ const authManagerProvider = metadata.providers.find(
56
+ (p: any) => p.provide === AuthenticationManager,
57
+ );
58
+ expect(authManagerProvider).toBeDefined();
59
+ });
60
+
61
+ test('should register security filter middleware', () => {
62
+ SecurityModule.forRoot({
63
+ jwt: {
64
+ secret: 'test-secret',
65
+ accessTokenExpiresIn: 3600,
66
+ },
67
+ });
68
+
69
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule);
70
+ expect(metadata.middlewares).toBeDefined();
71
+ expect(metadata.middlewares.length).toBeGreaterThan(0);
72
+ });
73
+
74
+ test('should register OAuth2 controller when enabled', () => {
75
+ SecurityModule.forRoot({
76
+ jwt: {
77
+ secret: 'test-secret',
78
+ accessTokenExpiresIn: 3600,
79
+ },
80
+ enableOAuth2Endpoints: true,
81
+ });
82
+
83
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule);
84
+ expect(metadata.controllers).toBeDefined();
85
+ expect(metadata.controllers.length).toBeGreaterThan(0);
86
+ });
87
+
88
+ test('should not register OAuth2 controller when disabled', () => {
89
+ SecurityModule.forRoot({
90
+ jwt: {
91
+ secret: 'test-secret',
92
+ accessTokenExpiresIn: 3600,
93
+ },
94
+ enableOAuth2Endpoints: false,
95
+ });
96
+
97
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule);
98
+ expect(metadata.controllers).toBeDefined();
99
+ expect(metadata.controllers.length).toBe(0);
100
+ });
101
+
102
+ test('should export services', () => {
103
+ SecurityModule.forRoot({
104
+ jwt: {
105
+ secret: 'test-secret',
106
+ accessTokenExpiresIn: 3600,
107
+ },
108
+ });
109
+
110
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule);
111
+ expect(metadata.exports).toBeDefined();
112
+ expect(metadata.exports).toContain(JWT_UTIL_TOKEN);
113
+ expect(metadata.exports).toContain(OAUTH2_SERVICE_TOKEN);
114
+ expect(metadata.exports).toContain(AuthenticationManager);
115
+ });
116
+
117
+ test('should use custom exclude paths', () => {
118
+ SecurityModule.forRoot({
119
+ jwt: {
120
+ secret: 'test-secret',
121
+ accessTokenExpiresIn: 3600,
122
+ },
123
+ excludePaths: ['/public', '/health'],
124
+ });
125
+
126
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule);
127
+ expect(metadata).toBeDefined();
128
+ });
129
+
130
+ test('should use custom OAuth2 prefix', () => {
131
+ SecurityModule.forRoot({
132
+ jwt: {
133
+ secret: 'test-secret',
134
+ accessTokenExpiresIn: 3600,
135
+ },
136
+ oauth2Prefix: '/auth',
137
+ });
138
+
139
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule);
140
+ expect(metadata).toBeDefined();
141
+ });
142
+ });
143
+