@nauth-toolkit/core 0.1.0 → 0.1.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 (184) hide show
  1. package/LICENSE +90 -0
  2. package/README.md +30 -0
  3. package/package.json +7 -2
  4. package/jest.config.js +0 -15
  5. package/jest.setup.ts +0 -6
  6. package/src/adapters/database-columns.ts +0 -165
  7. package/src/adapters/express.adapter.ts +0 -385
  8. package/src/adapters/fastify.adapter.ts +0 -416
  9. package/src/adapters/index.ts +0 -16
  10. package/src/adapters/storage.factory.ts +0 -143
  11. package/src/bootstrap.ts +0 -374
  12. package/src/dto/auth-challenge.dto.ts +0 -231
  13. package/src/dto/auth-response.dto.ts +0 -253
  14. package/src/dto/challenge-response.dto.ts +0 -234
  15. package/src/dto/change-password-request.dto.ts +0 -50
  16. package/src/dto/change-password-response.dto.ts +0 -29
  17. package/src/dto/change-password.dto.ts +0 -57
  18. package/src/dto/error-response.dto.ts +0 -136
  19. package/src/dto/get-available-methods.dto.ts +0 -55
  20. package/src/dto/get-challenge-data-response.dto.ts +0 -28
  21. package/src/dto/get-challenge-data.dto.ts +0 -69
  22. package/src/dto/get-client-info.dto.ts +0 -104
  23. package/src/dto/get-device-token-response.dto.ts +0 -25
  24. package/src/dto/get-events-by-type.dto.ts +0 -76
  25. package/src/dto/get-ip-address-response.dto.ts +0 -24
  26. package/src/dto/get-mfa-status.dto.ts +0 -94
  27. package/src/dto/get-risk-assessment-history.dto.ts +0 -39
  28. package/src/dto/get-session-id-response.dto.ts +0 -25
  29. package/src/dto/get-setup-data-response.dto.ts +0 -31
  30. package/src/dto/get-setup-data.dto.ts +0 -75
  31. package/src/dto/get-suspicious-activity.dto.ts +0 -42
  32. package/src/dto/get-user-agent-response.dto.ts +0 -23
  33. package/src/dto/get-user-auth-history.dto.ts +0 -95
  34. package/src/dto/get-user-by-email.dto.ts +0 -61
  35. package/src/dto/get-user-by-id.dto.ts +0 -46
  36. package/src/dto/get-user-devices.dto.ts +0 -53
  37. package/src/dto/get-user-response.dto.ts +0 -17
  38. package/src/dto/has-provider.dto.ts +0 -56
  39. package/src/dto/index.ts +0 -57
  40. package/src/dto/is-trusted-device-response.dto.ts +0 -34
  41. package/src/dto/list-providers-response.dto.ts +0 -23
  42. package/src/dto/login.dto.ts +0 -95
  43. package/src/dto/logout-all-response.dto.ts +0 -24
  44. package/src/dto/logout-all.dto.ts +0 -65
  45. package/src/dto/logout-response.dto.ts +0 -25
  46. package/src/dto/logout.dto.ts +0 -64
  47. package/src/dto/refresh-token.dto.ts +0 -36
  48. package/src/dto/remove-devices.dto.ts +0 -85
  49. package/src/dto/resend-code-response.dto.ts +0 -32
  50. package/src/dto/resend-code.dto.ts +0 -51
  51. package/src/dto/reset-password.dto.ts +0 -115
  52. package/src/dto/respond-challenge.dto.ts +0 -272
  53. package/src/dto/set-mfa-exemption.dto.ts +0 -112
  54. package/src/dto/set-must-change-password-response.dto.ts +0 -27
  55. package/src/dto/set-must-change-password.dto.ts +0 -46
  56. package/src/dto/set-preferred-method.dto.ts +0 -80
  57. package/src/dto/setup-mfa.dto.ts +0 -98
  58. package/src/dto/signup.dto.ts +0 -174
  59. package/src/dto/social-auth.dto.ts +0 -422
  60. package/src/dto/trust-device-response.dto.ts +0 -30
  61. package/src/dto/trust-device.dto.ts +0 -9
  62. package/src/dto/update-user-attributes-request.dto.ts +0 -51
  63. package/src/dto/user-response.dto.ts +0 -138
  64. package/src/dto/user-update.dto.ts +0 -222
  65. package/src/dto/verify-email.dto.ts +0 -313
  66. package/src/dto/verify-mfa-code.dto.ts +0 -103
  67. package/src/dto/verify-phone-by-sub.dto.ts +0 -78
  68. package/src/dto/verify-phone.dto.ts +0 -245
  69. package/src/entities/auth-audit.entity.ts +0 -232
  70. package/src/entities/challenge-session.entity.ts +0 -116
  71. package/src/entities/index.ts +0 -29
  72. package/src/entities/login-attempt.entity.ts +0 -64
  73. package/src/entities/mfa-device.entity.ts +0 -151
  74. package/src/entities/rate-limit.entity.ts +0 -44
  75. package/src/entities/session.entity.ts +0 -180
  76. package/src/entities/social-account.entity.ts +0 -96
  77. package/src/entities/storage-lock.entity.ts +0 -39
  78. package/src/entities/trusted-device.entity.ts +0 -112
  79. package/src/entities/user.entity.ts +0 -243
  80. package/src/entities/verification-token.entity.ts +0 -141
  81. package/src/enums/auth-audit-event-type.enum.ts +0 -360
  82. package/src/enums/error-codes.enum.ts +0 -420
  83. package/src/enums/mfa-method.enum.ts +0 -97
  84. package/src/enums/risk-factor.enum.ts +0 -111
  85. package/src/exceptions/nauth.exception.ts +0 -231
  86. package/src/handlers/auth.handler.ts +0 -260
  87. package/src/handlers/client-info.handler.ts +0 -101
  88. package/src/handlers/csrf.handler.ts +0 -156
  89. package/src/handlers/token-delivery.handler.ts +0 -118
  90. package/src/index.ts +0 -118
  91. package/src/interfaces/client-info.interface.ts +0 -85
  92. package/src/interfaces/config.interface.ts +0 -2135
  93. package/src/interfaces/entities.interface.ts +0 -226
  94. package/src/interfaces/index.ts +0 -15
  95. package/src/interfaces/logger.interface.ts +0 -283
  96. package/src/interfaces/mfa-provider.interface.ts +0 -154
  97. package/src/interfaces/oauth.interface.ts +0 -148
  98. package/src/interfaces/provider.interface.ts +0 -47
  99. package/src/interfaces/social-auth-provider.interface.ts +0 -131
  100. package/src/interfaces/storage-adapter.interface.ts +0 -82
  101. package/src/interfaces/template.interface.ts +0 -510
  102. package/src/interfaces/token-verifier.interface.ts +0 -110
  103. package/src/internal.ts +0 -178
  104. package/src/platform/interfaces.ts +0 -299
  105. package/src/schemas/auth-config.schema.ts +0 -646
  106. package/src/services/adaptive-mfa-decision.service.spec.ts +0 -1058
  107. package/src/services/adaptive-mfa-decision.service.ts +0 -457
  108. package/src/services/auth-audit.service.spec.ts +0 -675
  109. package/src/services/auth-audit.service.ts +0 -558
  110. package/src/services/auth-challenge-helper.service.spec.ts +0 -3227
  111. package/src/services/auth-challenge-helper.service.ts +0 -825
  112. package/src/services/auth-flow-context-builder.service.ts +0 -520
  113. package/src/services/auth-flow-rules.ts +0 -202
  114. package/src/services/auth-flow-state-definitions.ts +0 -190
  115. package/src/services/auth-flow-state-machine.service.ts +0 -207
  116. package/src/services/auth-flow-state-machine.types.ts +0 -316
  117. package/src/services/auth.service.spec.ts +0 -4195
  118. package/src/services/auth.service.ts +0 -3727
  119. package/src/services/challenge.service.spec.ts +0 -1363
  120. package/src/services/challenge.service.ts +0 -696
  121. package/src/services/client-info.service.spec.ts +0 -572
  122. package/src/services/client-info.service.ts +0 -374
  123. package/src/services/csrf.service.ts +0 -54
  124. package/src/services/email-verification.service.spec.ts +0 -1229
  125. package/src/services/email-verification.service.ts +0 -578
  126. package/src/services/geo-location.service.spec.ts +0 -603
  127. package/src/services/geo-location.service.ts +0 -599
  128. package/src/services/index.ts +0 -13
  129. package/src/services/jwt.service.spec.ts +0 -882
  130. package/src/services/jwt.service.ts +0 -621
  131. package/src/services/mfa-base.service.spec.ts +0 -246
  132. package/src/services/mfa-base.service.ts +0 -611
  133. package/src/services/mfa.service.spec.ts +0 -693
  134. package/src/services/mfa.service.ts +0 -960
  135. package/src/services/password.service.spec.ts +0 -166
  136. package/src/services/password.service.ts +0 -309
  137. package/src/services/phone-verification.service.spec.ts +0 -1120
  138. package/src/services/phone-verification.service.ts +0 -751
  139. package/src/services/risk-detection.service.spec.ts +0 -1292
  140. package/src/services/risk-detection.service.ts +0 -1012
  141. package/src/services/risk-scoring.service.spec.ts +0 -204
  142. package/src/services/risk-scoring.service.ts +0 -131
  143. package/src/services/session.service.spec.ts +0 -1293
  144. package/src/services/session.service.ts +0 -803
  145. package/src/services/social-account.service.spec.ts +0 -725
  146. package/src/services/social-auth-base.service.spec.ts +0 -418
  147. package/src/services/social-auth-base.service.ts +0 -581
  148. package/src/services/social-auth.service.spec.ts +0 -238
  149. package/src/services/social-auth.service.ts +0 -436
  150. package/src/services/social-provider-registry.service.spec.ts +0 -238
  151. package/src/services/social-provider-registry.service.ts +0 -122
  152. package/src/services/trusted-device.service.spec.ts +0 -505
  153. package/src/services/trusted-device.service.ts +0 -339
  154. package/src/storage/account-lockout-storage.service.spec.ts +0 -310
  155. package/src/storage/account-lockout-storage.service.ts +0 -89
  156. package/src/storage/index.ts +0 -3
  157. package/src/storage/memory-storage.adapter.ts +0 -443
  158. package/src/storage/rate-limit-storage.service.spec.ts +0 -247
  159. package/src/storage/rate-limit-storage.service.ts +0 -38
  160. package/src/templates/html-template.engine.spec.ts +0 -161
  161. package/src/templates/html-template.engine.ts +0 -688
  162. package/src/templates/index.ts +0 -7
  163. package/src/utils/common-passwords.spec.ts +0 -230
  164. package/src/utils/common-passwords.ts +0 -170
  165. package/src/utils/context-storage.ts +0 -188
  166. package/src/utils/cookie-names.util.ts +0 -67
  167. package/src/utils/cookies.util.ts +0 -94
  168. package/src/utils/index.ts +0 -12
  169. package/src/utils/ip-extractor.spec.ts +0 -330
  170. package/src/utils/ip-extractor.ts +0 -220
  171. package/src/utils/nauth-logger.spec.ts +0 -388
  172. package/src/utils/nauth-logger.ts +0 -215
  173. package/src/utils/pii-redactor.spec.ts +0 -130
  174. package/src/utils/pii-redactor.ts +0 -288
  175. package/src/utils/setup/get-repositories.ts +0 -140
  176. package/src/utils/setup/init-services.ts +0 -422
  177. package/src/utils/setup/init-social.ts +0 -189
  178. package/src/utils/setup/init-storage.ts +0 -94
  179. package/src/utils/setup/register-mfa.ts +0 -165
  180. package/src/utils/setup/run-nauth-migrations.ts +0 -61
  181. package/src/utils/token-delivery-policy.ts +0 -38
  182. package/src/validators/template.validator.ts +0 -219
  183. package/tsconfig.json +0 -37
  184. package/tsconfig.lint.json +0 -6
@@ -1,572 +0,0 @@
1
- import { ClientInfoService } from './client-info.service';
2
- import { ClientInfo } from '../interfaces/client-info.interface';
3
- import { ContextStorage } from '../utils/context-storage';
4
-
5
- /**
6
- * Client Info Service Unit Tests
7
- *
8
- * Tests the client information extraction and retrieval functionality.
9
- * Uses ContextStorage (AsyncLocalStorage) for platform-agnostic context management.
10
- *
11
- * Covers:
12
- * - All service methods (get, getIpAddress, getUserAgent, getDeviceToken, getDeviceId)
13
- * - ClientInfo retrieval from context
14
- * - Default values when no context exists
15
- * - All optional ClientInfo fields
16
- * - Edge cases (empty strings, undefined values, complete objects)
17
- * - ContextStorage integration
18
- */
19
- describe('ClientInfoService', () => {
20
- let service: ClientInfoService;
21
-
22
- const mockClientInfo: ClientInfo = {
23
- ipAddress: '192.168.1.100',
24
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
25
- deviceToken: 'device-token-123',
26
- deviceName: 'My Laptop',
27
- deviceType: 'desktop',
28
- ipCountry: 'US',
29
- ipCity: 'New York',
30
- platform: 'Windows',
31
- browser: 'Chrome',
32
- sessionId: 123,
33
- };
34
-
35
- beforeEach(() => {
36
- service = new ClientInfoService();
37
- // Clear any existing context before each test
38
- ContextStorage.clear();
39
- });
40
-
41
- afterEach(() => {
42
- // Ensure context is cleared after each test
43
- ContextStorage.clear();
44
- });
45
-
46
- // ============================================================================
47
- // Service Initialization
48
- // ============================================================================
49
-
50
- it('should be defined', () => {
51
- expect(service).toBeDefined();
52
- });
53
-
54
- // ============================================================================
55
- // get() Method
56
- // ============================================================================
57
-
58
- describe('get()', () => {
59
- it('should return client info from context when available', () => {
60
- ContextStorage.run(() => {
61
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
62
-
63
- const result = service.get();
64
-
65
- expect(result).toEqual(mockClientInfo);
66
- expect(result.ipAddress).toBe('192.168.1.100');
67
- expect(result.userAgent).toBe('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
68
- expect(result.deviceToken).toBe('device-token-123');
69
- });
70
- });
71
-
72
- it('should return default values when no client info in context', () => {
73
- // Call outside ContextStorage.run() - no context
74
- const result = service.get();
75
-
76
- expect(result).toEqual({
77
- ipAddress: 'unknown',
78
- userAgent: 'unknown',
79
- });
80
- });
81
-
82
- it('should return default values when context returns null', () => {
83
- ContextStorage.run(() => {
84
- ContextStorage.set('CLIENT_INFO', null);
85
-
86
- const result = service.get();
87
-
88
- expect(result).toEqual({
89
- ipAddress: 'unknown',
90
- userAgent: 'unknown',
91
- });
92
- });
93
- });
94
-
95
- it('should return default values when context returns undefined', () => {
96
- ContextStorage.run(() => {
97
- ContextStorage.set('CLIENT_INFO', undefined);
98
-
99
- const result = service.get();
100
-
101
- expect(result).toEqual({
102
- ipAddress: 'unknown',
103
- userAgent: 'unknown',
104
- });
105
- });
106
- });
107
-
108
- it('should handle partial client info from context', () => {
109
- const partialClientInfo: ClientInfo = {
110
- ipAddress: '10.0.0.1',
111
- userAgent: 'Test Agent',
112
- };
113
-
114
- ContextStorage.run(() => {
115
- ContextStorage.set('CLIENT_INFO', partialClientInfo);
116
-
117
- const result = service.get();
118
-
119
- expect(result).toEqual(partialClientInfo);
120
- expect(result.ipAddress).toBe('10.0.0.1');
121
- expect(result.userAgent).toBe('Test Agent');
122
- });
123
- });
124
-
125
- it('should handle client info with all optional fields', () => {
126
- ContextStorage.run(() => {
127
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
128
-
129
- const result = service.get();
130
-
131
- expect(result).toEqual(mockClientInfo);
132
- expect(result.deviceToken).toBe('device-token-123');
133
- expect(result.deviceName).toBe('My Laptop');
134
- expect(result.deviceType).toBe('desktop');
135
- expect(result.ipCountry).toBe('US');
136
- expect(result.ipCity).toBe('New York');
137
- expect(result.platform).toBe('Windows');
138
- expect(result.browser).toBe('Chrome');
139
- expect(result.sessionId).toBe(123);
140
- });
141
- });
142
-
143
- it('should handle client info with empty strings', () => {
144
- const clientInfoWithEmptyStrings: ClientInfo = {
145
- ipAddress: '',
146
- userAgent: '',
147
- };
148
-
149
- ContextStorage.run(() => {
150
- ContextStorage.set('CLIENT_INFO', clientInfoWithEmptyStrings);
151
-
152
- const result = service.get();
153
-
154
- expect(result.ipAddress).toBe('');
155
- expect(result.userAgent).toBe('');
156
- });
157
- });
158
-
159
- it('should handle client info with only required fields', () => {
160
- const minimalClientInfo: ClientInfo = {
161
- ipAddress: '192.168.1.1',
162
- userAgent: 'Mozilla/5.0',
163
- };
164
-
165
- ContextStorage.run(() => {
166
- ContextStorage.set('CLIENT_INFO', minimalClientInfo);
167
-
168
- const result = service.get();
169
-
170
- expect(result).toEqual(minimalClientInfo);
171
- expect(result.deviceToken).toBeUndefined();
172
- expect(result.deviceName).toBeUndefined();
173
- expect(result.deviceType).toBeUndefined();
174
- });
175
- });
176
- });
177
-
178
- // ============================================================================
179
- // getIpAddress() Method
180
- // ============================================================================
181
-
182
- describe('getIpAddress()', () => {
183
- it('should return IP address from client info', () => {
184
- ContextStorage.run(() => {
185
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
186
-
187
- const result = service.getIpAddress();
188
-
189
- expect(result.ipAddress).toBe('192.168.1.100');
190
- });
191
- });
192
-
193
- it('should return "unknown" when no client info in context', () => {
194
- // Call outside ContextStorage.run() - no context
195
- const result = service.getIpAddress();
196
-
197
- expect(result.ipAddress).toBe('unknown');
198
- });
199
-
200
- it('should return IP address from partial client info', () => {
201
- const partialClientInfo: ClientInfo = {
202
- ipAddress: '10.0.0.1',
203
- userAgent: 'Test',
204
- };
205
-
206
- ContextStorage.run(() => {
207
- ContextStorage.set('CLIENT_INFO', partialClientInfo);
208
-
209
- const result = service.getIpAddress();
210
-
211
- expect(result.ipAddress).toBe('10.0.0.1');
212
- });
213
- });
214
-
215
- it('should return empty string when IP address is empty', () => {
216
- const clientInfoWithEmptyIp: ClientInfo = {
217
- ipAddress: '',
218
- userAgent: 'Test',
219
- };
220
-
221
- ContextStorage.run(() => {
222
- ContextStorage.set('CLIENT_INFO', clientInfoWithEmptyIp);
223
-
224
- const result = service.getIpAddress();
225
-
226
- expect(result.ipAddress).toBe('');
227
- });
228
- });
229
- });
230
-
231
- // ============================================================================
232
- // getUserAgent() Method
233
- // ============================================================================
234
-
235
- describe('getUserAgent()', () => {
236
- it('should return user agent from client info', () => {
237
- ContextStorage.run(() => {
238
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
239
-
240
- const result = service.getUserAgent();
241
-
242
- expect(result.userAgent).toBe('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
243
- });
244
- });
245
-
246
- it('should return "unknown" when no client info in context', () => {
247
- // Call outside ContextStorage.run() - no context
248
- const result = service.getUserAgent();
249
-
250
- expect(result.userAgent).toBe('unknown');
251
- });
252
-
253
- it('should return user agent from partial client info', () => {
254
- const partialClientInfo: ClientInfo = {
255
- ipAddress: '10.0.0.1',
256
- userAgent: 'Test Agent',
257
- };
258
-
259
- ContextStorage.run(() => {
260
- ContextStorage.set('CLIENT_INFO', partialClientInfo);
261
-
262
- const result = service.getUserAgent();
263
-
264
- expect(result.userAgent).toBe('Test Agent');
265
- });
266
- });
267
-
268
- it('should return empty string when user agent is empty', () => {
269
- const clientInfoWithEmptyUA: ClientInfo = {
270
- ipAddress: '10.0.0.1',
271
- userAgent: '',
272
- };
273
-
274
- ContextStorage.run(() => {
275
- ContextStorage.set('CLIENT_INFO', clientInfoWithEmptyUA);
276
-
277
- const result = service.getUserAgent();
278
-
279
- expect(result.userAgent).toBe('');
280
- });
281
- });
282
- });
283
-
284
- // ============================================================================
285
- // getDeviceToken() Method
286
- // ============================================================================
287
-
288
- describe('getDeviceToken()', () => {
289
- it('should return device token from client info when available', () => {
290
- ContextStorage.run(() => {
291
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
292
-
293
- const result = service.getDeviceToken();
294
-
295
- expect(result.deviceToken).toBe('device-token-123');
296
- });
297
- });
298
-
299
- it('should return undefined when device token is not provided', () => {
300
- const clientInfoWithoutDeviceToken: ClientInfo = {
301
- ipAddress: '192.168.1.100',
302
- userAgent: 'Mozilla/5.0',
303
- };
304
-
305
- ContextStorage.run(() => {
306
- ContextStorage.set('CLIENT_INFO', clientInfoWithoutDeviceToken);
307
-
308
- const result = service.getDeviceToken();
309
-
310
- expect(result.deviceToken).toBeUndefined();
311
- });
312
- });
313
-
314
- it('should return undefined when no client info in context', () => {
315
- // Call outside ContextStorage.run() - no context
316
- const result = service.getDeviceToken();
317
-
318
- expect(result.deviceToken).toBeUndefined();
319
- });
320
-
321
- it('should return device token when only device token is provided', () => {
322
- const clientInfoWithDeviceToken: ClientInfo = {
323
- ipAddress: '10.0.0.1',
324
- userAgent: 'Test',
325
- deviceToken: 'token-456',
326
- };
327
-
328
- ContextStorage.run(() => {
329
- ContextStorage.set('CLIENT_INFO', clientInfoWithDeviceToken);
330
-
331
- const result = service.getDeviceToken();
332
-
333
- expect(result.deviceToken).toBe('token-456');
334
- });
335
- });
336
- });
337
-
338
- // ============================================================================
339
- // getDeviceId() Method (Deprecated)
340
- // ============================================================================
341
-
342
- describe('getDeviceId()', () => {
343
- it('should return undefined (deprecated method)', () => {
344
- ContextStorage.run(() => {
345
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
346
-
347
- const result = service.getDeviceId();
348
-
349
- // Method always returns undefined (deprecated)
350
- expect(result).toBeUndefined();
351
- });
352
- });
353
-
354
- it('should return undefined even when device token is provided', () => {
355
- ContextStorage.run(() => {
356
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
357
-
358
- const result = service.getDeviceId();
359
-
360
- // Method always returns undefined regardless of context
361
- expect(result).toBeUndefined();
362
- });
363
- });
364
-
365
- it('should return undefined when no client info in context', () => {
366
- // Call outside ContextStorage.run() - no context
367
- const result = service.getDeviceId();
368
-
369
- expect(result).toBeUndefined();
370
- });
371
- });
372
-
373
- // ============================================================================
374
- // Integration with Different Request Scenarios
375
- // ============================================================================
376
-
377
- describe('Integration with different request scenarios', () => {
378
- it('should handle mobile user agent', () => {
379
- const mobileClientInfo: ClientInfo = {
380
- ipAddress: '10.0.0.1',
381
- userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X)',
382
- deviceType: 'mobile',
383
- platform: 'iOS',
384
- browser: 'Safari',
385
- };
386
-
387
- ContextStorage.run(() => {
388
- ContextStorage.set('CLIENT_INFO', mobileClientInfo);
389
-
390
- const result = service.get();
391
-
392
- expect(result.ipAddress).toBe('10.0.0.1');
393
- expect(result.userAgent).toContain('iPhone');
394
- expect(result.deviceType).toBe('mobile');
395
- expect(result.platform).toBe('iOS');
396
- expect(result.browser).toBe('Safari');
397
- });
398
- });
399
-
400
- it('should handle API client without user agent', () => {
401
- const apiClientInfo: ClientInfo = {
402
- ipAddress: '10.0.0.1',
403
- userAgent: '',
404
- };
405
-
406
- ContextStorage.run(() => {
407
- ContextStorage.set('CLIENT_INFO', apiClientInfo);
408
-
409
- const result = service.get();
410
-
411
- expect(result.ipAddress).toBe('10.0.0.1');
412
- expect(result.userAgent).toBe('');
413
- });
414
- });
415
-
416
- it('should handle IPv6 addresses', () => {
417
- const ipv6ClientInfo: ClientInfo = {
418
- ipAddress: '2001:db8::1',
419
- userAgent: 'curl/7.68.0',
420
- };
421
-
422
- ContextStorage.run(() => {
423
- ContextStorage.set('CLIENT_INFO', ipv6ClientInfo);
424
-
425
- const result = service.get();
426
-
427
- expect(result.ipAddress).toBe('2001:db8::1');
428
- expect(result.userAgent).toBe('curl/7.68.0');
429
- });
430
- });
431
-
432
- it('should handle tablet device type', () => {
433
- const tabletClientInfo: ClientInfo = {
434
- ipAddress: '10.0.0.1',
435
- userAgent: 'Mozilla/5.0 (iPad; CPU OS 14_0 like Mac OS X)',
436
- deviceType: 'tablet',
437
- platform: 'iOS',
438
- browser: 'Safari',
439
- };
440
-
441
- ContextStorage.run(() => {
442
- ContextStorage.set('CLIENT_INFO', tabletClientInfo);
443
-
444
- const result = service.get();
445
-
446
- expect(result.deviceType).toBe('tablet');
447
- expect(result.platform).toBe('iOS');
448
- });
449
- });
450
-
451
- it('should handle client info with geolocation data', () => {
452
- const clientInfoWithGeo: ClientInfo = {
453
- ipAddress: '10.0.0.1',
454
- userAgent: 'Mozilla/5.0',
455
- ipCountry: 'CA',
456
- ipCity: 'Toronto',
457
- };
458
-
459
- ContextStorage.run(() => {
460
- ContextStorage.set('CLIENT_INFO', clientInfoWithGeo);
461
-
462
- const result = service.get();
463
-
464
- expect(result.ipCountry).toBe('CA');
465
- expect(result.ipCity).toBe('Toronto');
466
- });
467
- });
468
-
469
- it('should handle client info with session ID', () => {
470
- const clientInfoWithSession: ClientInfo = {
471
- ipAddress: '10.0.0.1',
472
- userAgent: 'Mozilla/5.0',
473
- sessionId: 456,
474
- };
475
-
476
- ContextStorage.run(() => {
477
- ContextStorage.set('CLIENT_INFO', clientInfoWithSession);
478
-
479
- const result = service.get();
480
-
481
- expect(result.sessionId).toBe(456);
482
- });
483
- });
484
-
485
- it('should handle complete client info with all fields', () => {
486
- const completeClientInfo: ClientInfo = {
487
- ipAddress: '192.168.1.100',
488
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
489
- deviceToken: 'device-token-789',
490
- deviceName: 'Work Desktop',
491
- deviceType: 'desktop',
492
- ipCountry: 'GB',
493
- ipCity: 'London',
494
- platform: 'Windows',
495
- browser: 'Firefox',
496
- sessionId: 789,
497
- };
498
-
499
- ContextStorage.run(() => {
500
- ContextStorage.set('CLIENT_INFO', completeClientInfo);
501
-
502
- const result = service.get();
503
-
504
- expect(result).toEqual(completeClientInfo);
505
- expect(result.ipAddress).toBe('192.168.1.100');
506
- expect(result.deviceToken).toBe('device-token-789');
507
- expect(result.deviceName).toBe('Work Desktop');
508
- expect(result.deviceType).toBe('desktop');
509
- expect(result.ipCountry).toBe('GB');
510
- expect(result.ipCity).toBe('London');
511
- expect(result.platform).toBe('Windows');
512
- expect(result.browser).toBe('Firefox');
513
- expect(result.sessionId).toBe(789);
514
- });
515
- });
516
- });
517
-
518
- // ============================================================================
519
- // ContextStorage Integration
520
- // ============================================================================
521
-
522
- describe('ContextStorage integration', () => {
523
- it('should work across async boundaries', async () => {
524
- const result = await ContextStorage.run(async () => {
525
- ContextStorage.set('CLIENT_INFO', mockClientInfo);
526
-
527
- // Simulate async operation
528
- await new Promise((resolve) => setTimeout(resolve, 10));
529
-
530
- return service.get();
531
- });
532
-
533
- expect(result).toEqual(mockClientInfo);
534
- });
535
-
536
- it('should isolate context per run() call', () => {
537
- const firstContext = {
538
- ipAddress: '10.0.0.1',
539
- userAgent: 'First',
540
- };
541
-
542
- const secondContext = {
543
- ipAddress: '10.0.0.2',
544
- userAgent: 'Second',
545
- };
546
-
547
- const result1 = ContextStorage.run(() => {
548
- ContextStorage.set('CLIENT_INFO', firstContext);
549
- return service.get();
550
- });
551
-
552
- const result2 = ContextStorage.run(() => {
553
- ContextStorage.set('CLIENT_INFO', secondContext);
554
- return service.get();
555
- });
556
-
557
- expect(result1.ipAddress).toBe('10.0.0.1');
558
- expect(result2.ipAddress).toBe('10.0.0.2');
559
- expect(result1).not.toEqual(result2);
560
- });
561
-
562
- it('should return default values when called outside context', () => {
563
- // No ContextStorage.run() - should return defaults
564
- const result = service.get();
565
-
566
- expect(result).toEqual({
567
- ipAddress: 'unknown',
568
- userAgent: 'unknown',
569
- });
570
- });
571
- });
572
- });