@umituz/react-native-auth 4.3.51 → 4.3.52
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.
- package/package.json +1 -1
- package/README.md +0 -425
- package/src/__tests__/services/AnonymousModeService.test.ts +0 -53
- package/src/__tests__/services/AuthCoreInitialization.test.ts +0 -45
- package/src/__tests__/services/AuthCoreOperations.test.ts +0 -71
- package/src/__tests__/utils/AuthDisplayNameValidation.test.ts +0 -44
- package/src/__tests__/utils/AuthEmailValidation.test.ts +0 -38
- package/src/__tests__/utils/AuthPasswordValidation.test.ts +0 -59
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.52",
|
|
4
4
|
"description": "Authentication service for React Native apps - Secure, type-safe, and production-ready. Provider-agnostic design with dependency injection, configurable validation, and comprehensive error handling.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
package/README.md
DELETED
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
# @umituz/react-native-auth
|
|
2
|
-
|
|
3
|
-
Authentication service for React Native applications with secure, type-safe, and production-ready implementation.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Strategy
|
|
8
|
-
|
|
9
|
-
**Purpose**: Provides comprehensive authentication solution for React Native apps with Domain-Driven Design architecture, supporting multiple authentication methods and providers.
|
|
10
|
-
|
|
11
|
-
**When to Use**:
|
|
12
|
-
- Building React Native apps requiring authentication
|
|
13
|
-
- Need multiple auth methods (email, social, anonymous)
|
|
14
|
-
- Want type-safe auth implementation
|
|
15
|
-
- Prefer DDD architecture
|
|
16
|
-
- Need production-ready auth solution
|
|
17
|
-
|
|
18
|
-
**Package Location**: `/src`
|
|
19
|
-
|
|
20
|
-
**Documentation**: See `/src/[layer]/README.md` for detailed documentation
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Core Features
|
|
25
|
-
|
|
26
|
-
### Authentication Methods
|
|
27
|
-
|
|
28
|
-
**SUPPORTED METHODS**:
|
|
29
|
-
- Email/Password authentication
|
|
30
|
-
- Google OAuth integration
|
|
31
|
-
- Apple Sign-In (iOS)
|
|
32
|
-
- Anonymous user sessions
|
|
33
|
-
- Account upgrade (anonymous → registered)
|
|
34
|
-
|
|
35
|
-
### Architecture
|
|
36
|
-
|
|
37
|
-
**DOMAIN-DRIVEN DESIGN LAYERS**:
|
|
38
|
-
- **Domain**: Core business logic and entities
|
|
39
|
-
- **Application**: Use cases and interfaces
|
|
40
|
-
- **Infrastructure**: External integrations
|
|
41
|
-
- **Presentation**: UI components and hooks
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Installation
|
|
46
|
-
|
|
47
|
-
### Package Installation
|
|
48
|
-
|
|
49
|
-
**NPM**:
|
|
50
|
-
```bash
|
|
51
|
-
npm install @umituz/react-native-auth
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**Yarn**:
|
|
55
|
-
```bash
|
|
56
|
-
yarn add @umituz/react-native-auth
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Peer Dependencies
|
|
60
|
-
|
|
61
|
-
**REQUIRED PACKAGES**:
|
|
62
|
-
- `firebase`: >= 11.0.0
|
|
63
|
-
- `react`: >= 18.2.0
|
|
64
|
-
- `react-native`: >= 0.74.0
|
|
65
|
-
- `@tanstack/react-query`: >= 5.0.0
|
|
66
|
-
- `zustand`: >= 4.0.0
|
|
67
|
-
|
|
68
|
-
**EXTERNAL DEPENDENCIES**:
|
|
69
|
-
- `@umituz/react-native-firebase` - Firebase integration
|
|
70
|
-
- `@umituz/react-native-design-system` - UI components
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Configuration
|
|
75
|
-
|
|
76
|
-
### Firebase Setup
|
|
77
|
-
|
|
78
|
-
**Rules**:
|
|
79
|
-
- MUST create Firebase project
|
|
80
|
-
- MUST enable Authentication
|
|
81
|
-
- MUST enable Firestore (for user documents)
|
|
82
|
-
- MUST configure OAuth providers
|
|
83
|
-
|
|
84
|
-
**MUST NOT**:
|
|
85
|
-
- Skip Firebase console setup
|
|
86
|
-
- Use production keys in development
|
|
87
|
-
- Forget to enable required providers
|
|
88
|
-
|
|
89
|
-
**Steps**:
|
|
90
|
-
1. Create Firebase project at console.firebase.google.com
|
|
91
|
-
2. Enable Authentication
|
|
92
|
-
3. Enable Google Sign-In
|
|
93
|
-
4. Enable Apple Sign-In (for iOS)
|
|
94
|
-
5. Enable Firestore
|
|
95
|
-
6. Download config files
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
## Layer Overview
|
|
100
|
-
|
|
101
|
-
### Domain Layer
|
|
102
|
-
|
|
103
|
-
**Location**: `src/domain/`
|
|
104
|
-
|
|
105
|
-
**Purpose**: Core business logic and entities
|
|
106
|
-
|
|
107
|
-
**CONTAINS**:
|
|
108
|
-
- `AuthUser` entity
|
|
109
|
-
- `UserProfile` entity
|
|
110
|
-
- `AuthConfig` value object
|
|
111
|
-
- `AuthError` hierarchy
|
|
112
|
-
|
|
113
|
-
**Documentation**: `src/domain/README.md`
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
### Application Layer
|
|
118
|
-
|
|
119
|
-
**Location**: `src/application/`
|
|
120
|
-
|
|
121
|
-
**Purpose**: Use cases and interfaces
|
|
122
|
-
|
|
123
|
-
**CONTAINS**:
|
|
124
|
-
- Authentication ports
|
|
125
|
-
- User profile ports
|
|
126
|
-
- Account management ports
|
|
127
|
-
|
|
128
|
-
**Documentation**: `src/application/README.md`
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
### Infrastructure Layer
|
|
133
|
-
|
|
134
|
-
**Location**: `src/infrastructure/`
|
|
135
|
-
|
|
136
|
-
**Purpose**: External integrations and implementations
|
|
137
|
-
|
|
138
|
-
**CONTAINS**:
|
|
139
|
-
- Firebase Auth service
|
|
140
|
-
- Firestore repositories
|
|
141
|
-
- Validation utilities
|
|
142
|
-
- Provider implementations
|
|
143
|
-
|
|
144
|
-
**Documentation**:
|
|
145
|
-
- `src/infrastructure/README.md`
|
|
146
|
-
- `src/infrastructure/services/README.md`
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
### Presentation Layer
|
|
151
|
-
|
|
152
|
-
**Location**: `src/presentation/`
|
|
153
|
-
|
|
154
|
-
**Purpose**: UI components and hooks
|
|
155
|
-
|
|
156
|
-
**CONTAINS**:
|
|
157
|
-
- React hooks for auth
|
|
158
|
-
- Pre-built components
|
|
159
|
-
- Screen components
|
|
160
|
-
- State management (Zustand)
|
|
161
|
-
|
|
162
|
-
**Documentation**:
|
|
163
|
-
- `src/presentation/README.md`
|
|
164
|
-
- `src/presentation/hooks/README.md`
|
|
165
|
-
- `src/presentation/components/README.md`
|
|
166
|
-
- `src/presentation/screens/README.md`
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## Usage Guidelines
|
|
171
|
-
|
|
172
|
-
### Authentication Hooks
|
|
173
|
-
|
|
174
|
-
**PRIMARY HOOK**: `useAuth`
|
|
175
|
-
**Location**: `src/presentation/hooks/useAuth.ts`
|
|
176
|
-
|
|
177
|
-
**When to Use**:
|
|
178
|
-
- Need authentication state
|
|
179
|
-
- Require user information
|
|
180
|
-
- Performing auth operations
|
|
181
|
-
- Checking auth status
|
|
182
|
-
|
|
183
|
-
**Import Path**:
|
|
184
|
-
```typescript
|
|
185
|
-
import { useAuth } from '@umituz/react-native-auth';
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**Rules**:
|
|
189
|
-
- MUST initialize AuthProvider before use
|
|
190
|
-
- MUST handle loading state
|
|
191
|
-
- MUST check auth readiness
|
|
192
|
-
- MUST handle errors appropriately
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
### Components
|
|
197
|
-
|
|
198
|
-
**AVAILABLE COMPONENTS**:
|
|
199
|
-
- `LoginForm` - Email/password login
|
|
200
|
-
- `RegisterForm` - User registration
|
|
201
|
-
- `SocialLoginButtons` - Google/Apple buttons
|
|
202
|
-
- `ProfileSection` - Profile display
|
|
203
|
-
- `AccountActions` - Account management
|
|
204
|
-
|
|
205
|
-
**Import Path**:
|
|
206
|
-
```typescript
|
|
207
|
-
import {
|
|
208
|
-
LoginForm,
|
|
209
|
-
RegisterForm,
|
|
210
|
-
SocialLoginButtons
|
|
211
|
-
} from '@umituz/react-native-auth';
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**Rules**:
|
|
215
|
-
- MUST follow component documentation
|
|
216
|
-
- MUST provide required props
|
|
217
|
-
- MUST handle events appropriately
|
|
218
|
-
- MUST NOT override internal logic
|
|
219
|
-
|
|
220
|
-
---
|
|
221
|
-
|
|
222
|
-
## Platform Support
|
|
223
|
-
|
|
224
|
-
### Supported Platforms
|
|
225
|
-
|
|
226
|
-
**iOS**: ✅ Full support
|
|
227
|
-
- All authentication methods
|
|
228
|
-
- Apple Sign-In available
|
|
229
|
-
- Google Sign-In available
|
|
230
|
-
|
|
231
|
-
**Android**: ✅ Full support
|
|
232
|
-
- All authentication methods (except Apple)
|
|
233
|
-
- Google Sign-In available
|
|
234
|
-
|
|
235
|
-
**Web**: ✅ Full support
|
|
236
|
-
- All authentication methods (except Apple)
|
|
237
|
-
- Google Sign-In available
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## Security Requirements
|
|
242
|
-
|
|
243
|
-
### Rules
|
|
244
|
-
|
|
245
|
-
**MUST**:
|
|
246
|
-
- Validate all inputs
|
|
247
|
-
- Use HTTPS for all operations
|
|
248
|
-
- Implement proper error handling
|
|
249
|
-
- Follow Firebase security best practices
|
|
250
|
-
- Use secure token storage
|
|
251
|
-
- Validate tokens server-side
|
|
252
|
-
|
|
253
|
-
**MUST NOT**:
|
|
254
|
-
- Store tokens in AsyncStorage
|
|
255
|
-
- Log passwords or tokens
|
|
256
|
-
- Expose sensitive data in errors
|
|
257
|
-
- Skip validation
|
|
258
|
-
- Use HTTP for auth operations
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
## Architecture Principles
|
|
263
|
-
|
|
264
|
-
### Domain-Driven Design
|
|
265
|
-
|
|
266
|
-
**PRINCIPLES**:
|
|
267
|
-
- Business logic in domain layer
|
|
268
|
-
- Infrastructure concerns isolated
|
|
269
|
-
- Presentation layer UI-focused
|
|
270
|
-
- Application layer orchestrates
|
|
271
|
-
|
|
272
|
-
**BENEFITS**:
|
|
273
|
-
- Testable business logic
|
|
274
|
-
- Swappable providers
|
|
275
|
-
- Clear separation of concerns
|
|
276
|
-
- Maintainable codebase
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
## Error Handling
|
|
281
|
-
|
|
282
|
-
### Strategy
|
|
283
|
-
|
|
284
|
-
**Purpose**: Comprehensive error handling throughout application.
|
|
285
|
-
|
|
286
|
-
**Rules**:
|
|
287
|
-
- MUST handle auth errors gracefully
|
|
288
|
-
- MUST show user-friendly messages
|
|
289
|
-
- MUST allow retry after failures
|
|
290
|
-
- MUST log errors for debugging
|
|
291
|
-
- MUST not expose sensitive data
|
|
292
|
-
|
|
293
|
-
**Error Hierarchy**:
|
|
294
|
-
- `AuthError` - Base error class
|
|
295
|
-
- `ValidationError` - Input validation errors
|
|
296
|
-
- `AuthenticationError` - Auth operation errors
|
|
297
|
-
- `NetworkError` - Network issues
|
|
298
|
-
|
|
299
|
-
---
|
|
300
|
-
|
|
301
|
-
## Validation Strategy
|
|
302
|
-
|
|
303
|
-
### Purpose
|
|
304
|
-
|
|
305
|
-
**Purpose**: Ensure data integrity and security.
|
|
306
|
-
|
|
307
|
-
**Rules**:
|
|
308
|
-
- MUST validate email format
|
|
309
|
-
- MUST validate password complexity
|
|
310
|
-
- MUST validate required fields
|
|
311
|
-
- MUST provide clear error messages
|
|
312
|
-
- MUST prevent invalid submissions
|
|
313
|
-
|
|
314
|
-
**Validation Location**: `src/infrastructure/utils/AuthValidation.ts`
|
|
315
|
-
|
|
316
|
-
---
|
|
317
|
-
|
|
318
|
-
## Migration Guide
|
|
319
|
-
|
|
320
|
-
### From Previous Versions
|
|
321
|
-
|
|
322
|
-
**Breaking Changes**:
|
|
323
|
-
- See changelog for details
|
|
324
|
-
- Follow migration steps
|
|
325
|
-
- Update component props
|
|
326
|
-
- Update hook usage
|
|
327
|
-
|
|
328
|
-
**Rules**:
|
|
329
|
-
- MUST read migration guide
|
|
330
|
-
- MUST test thoroughly after upgrade
|
|
331
|
-
- MUST update dependencies
|
|
332
|
-
- MUST check deprecated features
|
|
333
|
-
|
|
334
|
-
---
|
|
335
|
-
|
|
336
|
-
## Performance Considerations
|
|
337
|
-
|
|
338
|
-
### Optimization
|
|
339
|
-
|
|
340
|
-
**Rules**:
|
|
341
|
-
- MUST memoize expensive computations
|
|
342
|
-
- MUST minimize re-renders
|
|
343
|
-
- MUST optimize state updates
|
|
344
|
-
- MUST use efficient selectors
|
|
345
|
-
|
|
346
|
-
**Constraints**:
|
|
347
|
-
- Auth state single source of truth
|
|
348
|
-
- Minimal network requests
|
|
349
|
-
- Efficient validation checks
|
|
350
|
-
- Optimized component rendering
|
|
351
|
-
|
|
352
|
-
---
|
|
353
|
-
|
|
354
|
-
## Testing Strategy
|
|
355
|
-
|
|
356
|
-
### Unit Testing
|
|
357
|
-
|
|
358
|
-
**WHAT TO TEST**:
|
|
359
|
-
- Domain logic and entities
|
|
360
|
-
- Validation utilities
|
|
361
|
-
- Hook behavior
|
|
362
|
-
- Component rendering
|
|
363
|
-
|
|
364
|
-
**RULES**:
|
|
365
|
-
- MUST test auth operations
|
|
366
|
-
- MUST test validation
|
|
367
|
-
- MUST test error handling
|
|
368
|
-
- MUST mock Firebase dependencies
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
## Contributing
|
|
373
|
-
|
|
374
|
-
### Development Setup
|
|
375
|
-
|
|
376
|
-
**RULES**:
|
|
377
|
-
- MUST follow DDD principles
|
|
378
|
-
- MUST maintain type safety
|
|
379
|
-
- MUST update documentation
|
|
380
|
-
- MUST add tests for new features
|
|
381
|
-
- MUST follow existing patterns
|
|
382
|
-
|
|
383
|
-
**MUST NOT**:
|
|
384
|
-
- Break DDD layer boundaries
|
|
385
|
-
- Skip documentation
|
|
386
|
-
- Add code without tests
|
|
387
|
-
- Introduce breaking changes without major version bump
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## License
|
|
392
|
-
|
|
393
|
-
MIT License - See LICENSE file for details
|
|
394
|
-
|
|
395
|
-
---
|
|
396
|
-
|
|
397
|
-
## Support and Documentation
|
|
398
|
-
|
|
399
|
-
### Documentation Structure
|
|
400
|
-
|
|
401
|
-
**MAIN README**: This file
|
|
402
|
-
|
|
403
|
-
**LAYER DOCUMENTATION**:
|
|
404
|
-
- `src/domain/README.md` - Domain layer details
|
|
405
|
-
- `src/application/README.md` - Application layer details
|
|
406
|
-
- `src/infrastructure/README.md` - Infrastructure details
|
|
407
|
-
- `src/presentation/README.md` - Presentation layer details
|
|
408
|
-
|
|
409
|
-
**COMPONENT/HOOK DOCUMENTATION**:
|
|
410
|
-
- Each component has dedicated .md file
|
|
411
|
-
- Each hook has dedicated .md file
|
|
412
|
-
- Follows Strategy/Rules/Constraints format
|
|
413
|
-
|
|
414
|
-
### Getting Help
|
|
415
|
-
|
|
416
|
-
- Read documentation first
|
|
417
|
-
- Check existing issues
|
|
418
|
-
- Review examples in docs
|
|
419
|
-
- Follow AI agent guidelines
|
|
420
|
-
|
|
421
|
-
---
|
|
422
|
-
|
|
423
|
-
## Changelog
|
|
424
|
-
|
|
425
|
-
See CHANGELOG.md for version history and changes.
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { AnonymousModeService } from '../../infrastructure/services/AnonymousModeService';
|
|
2
|
-
import type { IStorageProvider } from '../../infrastructure/services/AuthPackage';
|
|
3
|
-
|
|
4
|
-
describe('AnonymousModeService', () => {
|
|
5
|
-
let anonymousModeService: AnonymousModeService;
|
|
6
|
-
let mockStorageProvider: jest.Mocked<IStorageProvider>;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
mockStorageProvider = { get: jest.fn(), set: jest.fn(), remove: jest.fn() };
|
|
10
|
-
anonymousModeService = new AnonymousModeService('@test_anonymous_mode');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
describe('configuration', () => {
|
|
14
|
-
it('should use storage keys correctly', () => {
|
|
15
|
-
const service = new AnonymousModeService('@custom');
|
|
16
|
-
expect(service.getIsAnonymousMode()).toBe(false);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
describe('persistence', () => {
|
|
21
|
-
it('should load state from storage', async () => {
|
|
22
|
-
mockStorageProvider.get.mockResolvedValue('true');
|
|
23
|
-
const result = await anonymousModeService.load(mockStorageProvider);
|
|
24
|
-
expect(result).toBe(true);
|
|
25
|
-
expect(anonymousModeService.getIsAnonymousMode()).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should handle missing storage state', async () => {
|
|
29
|
-
mockStorageProvider.get.mockResolvedValue(null);
|
|
30
|
-
expect(await anonymousModeService.load(mockStorageProvider)).toBe(false);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should save state to storage', async () => {
|
|
34
|
-
anonymousModeService.setAnonymousMode(true);
|
|
35
|
-
await anonymousModeService.save(mockStorageProvider);
|
|
36
|
-
expect(mockStorageProvider.set).toHaveBeenCalledWith('@test_anonymous_mode', 'true');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should clear state from storage', async () => {
|
|
40
|
-
await anonymousModeService.clear(mockStorageProvider);
|
|
41
|
-
expect(anonymousModeService.getIsAnonymousMode()).toBe(false);
|
|
42
|
-
expect(mockStorageProvider.remove).toHaveBeenCalledWith('@test_anonymous_mode');
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('auth integration', () => {
|
|
47
|
-
it('should enable anonymous mode', async () => {
|
|
48
|
-
mockStorageProvider.set.mockResolvedValue(undefined);
|
|
49
|
-
await anonymousModeService.enable(mockStorageProvider);
|
|
50
|
-
expect(anonymousModeService.getIsAnonymousMode()).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { AuthCoreService } from '../../../src/infrastructure/services/AuthCoreService';
|
|
2
|
-
import { DEFAULT_AUTH_CONFIG } from '../../../src/domain/value-objects/AuthConfig';
|
|
3
|
-
import type { IAuthProvider } from '../../../src/application/ports/IAuthProvider';
|
|
4
|
-
|
|
5
|
-
describe('AuthCoreInitialization', () => {
|
|
6
|
-
let authCoreService: AuthCoreService;
|
|
7
|
-
let mockProvider: jest.Mocked<IAuthProvider>;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
mockProvider = {
|
|
11
|
-
initialize: jest.fn(),
|
|
12
|
-
isInitialized: jest.fn().mockReturnValue(true),
|
|
13
|
-
signIn: jest.fn(),
|
|
14
|
-
signUp: jest.fn(),
|
|
15
|
-
signOut: jest.fn(),
|
|
16
|
-
getCurrentUser: jest.fn(),
|
|
17
|
-
onAuthStateChange: jest.fn().mockReturnValue(jest.fn()),
|
|
18
|
-
};
|
|
19
|
-
authCoreService = new AuthCoreService(DEFAULT_AUTH_CONFIG);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should initialize with provided config', () => {
|
|
23
|
-
const customConfig = { ...DEFAULT_AUTH_CONFIG, password: { ...DEFAULT_AUTH_CONFIG.password, minLength: 12 } };
|
|
24
|
-
const service = new AuthCoreService(customConfig);
|
|
25
|
-
expect(service.getConfig()).toEqual(customConfig);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should initialize with IAuthProvider', async () => {
|
|
29
|
-
await authCoreService.initialize(mockProvider);
|
|
30
|
-
expect(mockProvider.initialize).toHaveBeenCalled();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should throw error when no provider provided', async () => {
|
|
34
|
-
await expect(authCoreService.initialize(null as any)).rejects.toThrow();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should return false when not initialized', () => {
|
|
38
|
-
expect(authCoreService.isInitialized()).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should return true when initialized', async () => {
|
|
42
|
-
await authCoreService.initialize(mockProvider);
|
|
43
|
-
expect(authCoreService.isInitialized()).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { AuthCoreService } from '../../../src/infrastructure/services/AuthCoreService';
|
|
2
|
-
import { DEFAULT_AUTH_CONFIG } from '../../../src/domain/value-objects/AuthConfig';
|
|
3
|
-
import type { IAuthProvider } from '../../../src/application/ports/IAuthProvider';
|
|
4
|
-
import type { AuthUser } from '../../../src/domain/entities/AuthUser';
|
|
5
|
-
|
|
6
|
-
describe('AuthCoreOperations', () => {
|
|
7
|
-
let authCoreService: AuthCoreService;
|
|
8
|
-
let mockProvider: jest.Mocked<IAuthProvider>;
|
|
9
|
-
const mockUser: AuthUser = {
|
|
10
|
-
uid: 'test-uid',
|
|
11
|
-
email: 'test@example.com',
|
|
12
|
-
displayName: 'Test User',
|
|
13
|
-
isAnonymous: false,
|
|
14
|
-
emailVerified: false,
|
|
15
|
-
photoURL: null,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
beforeEach(async () => {
|
|
19
|
-
mockProvider = {
|
|
20
|
-
initialize: jest.fn(),
|
|
21
|
-
isInitialized: jest.fn().mockReturnValue(true),
|
|
22
|
-
signIn: jest.fn(),
|
|
23
|
-
signUp: jest.fn(),
|
|
24
|
-
signOut: jest.fn(),
|
|
25
|
-
getCurrentUser: jest.fn(),
|
|
26
|
-
onAuthStateChange: jest.fn().mockReturnValue(jest.fn()),
|
|
27
|
-
};
|
|
28
|
-
authCoreService = new AuthCoreService(DEFAULT_AUTH_CONFIG);
|
|
29
|
-
await authCoreService.initialize(mockProvider);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('Auth Flow', () => {
|
|
33
|
-
it('should sign up successfully', async () => {
|
|
34
|
-
mockProvider.signUp.mockResolvedValue(mockUser);
|
|
35
|
-
const result = await authCoreService.signUp({ email: 'test@example.com', password: 'password123' });
|
|
36
|
-
expect(result).toEqual(mockUser);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should sign in successfully', async () => {
|
|
40
|
-
mockProvider.signIn.mockResolvedValue(mockUser);
|
|
41
|
-
const result = await authCoreService.signIn({ email: 'test@example.com', password: 'password123' });
|
|
42
|
-
expect(result).toEqual(mockUser);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should sign out successfully', async () => {
|
|
46
|
-
await authCoreService.signOut();
|
|
47
|
-
expect(mockProvider.signOut).toHaveBeenCalled();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('User State', () => {
|
|
52
|
-
it('should return current user', () => {
|
|
53
|
-
mockProvider.getCurrentUser.mockReturnValue(mockUser);
|
|
54
|
-
expect(authCoreService.getCurrentUser()).toEqual(mockUser);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should return null when no user', () => {
|
|
58
|
-
mockProvider.getCurrentUser.mockReturnValue(null);
|
|
59
|
-
expect(authCoreService.getCurrentUser()).toBeNull();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should subscribe to auth state changes', () => {
|
|
63
|
-
const callback = jest.fn();
|
|
64
|
-
const mockCleanup = jest.fn();
|
|
65
|
-
mockProvider.onAuthStateChange.mockReturnValue(mockCleanup);
|
|
66
|
-
const cleanup = authCoreService.onAuthStateChange(callback);
|
|
67
|
-
expect(mockProvider.onAuthStateChange).toHaveBeenCalledWith(callback);
|
|
68
|
-
expect(cleanup).toBe(mockCleanup);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { validateDisplayName } from '../../../src/infrastructure/utils/AuthValidation';
|
|
2
|
-
|
|
3
|
-
describe('AuthDisplayNameValidation', () => {
|
|
4
|
-
describe('validateDisplayName', () => {
|
|
5
|
-
it('should reject empty name', () => {
|
|
6
|
-
const result = validateDisplayName('');
|
|
7
|
-
expect(result.isValid).toBe(false);
|
|
8
|
-
expect(result.error).toBe('Name is required');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('should reject whitespace-only name', () => {
|
|
12
|
-
const result = validateDisplayName(' ');
|
|
13
|
-
expect(result.isValid).toBe(false);
|
|
14
|
-
expect(result.error).toBe('Name is required');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should reject name that is too short', () => {
|
|
18
|
-
const result = validateDisplayName('A');
|
|
19
|
-
expect(result.isValid).toBe(false);
|
|
20
|
-
expect(result.error).toBe('Name must be at least 2 characters');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should accept name that meets minimum length', () => {
|
|
24
|
-
const result = validateDisplayName('Al');
|
|
25
|
-
expect(result.isValid).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should use custom minimum length', () => {
|
|
29
|
-
const result = validateDisplayName('Al', 3);
|
|
30
|
-
expect(result.isValid).toBe(false);
|
|
31
|
-
expect(result.error).toBe('Name must be at least 3 characters');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should trim whitespace', () => {
|
|
35
|
-
const result = validateDisplayName(' John Doe ');
|
|
36
|
-
expect(result.isValid).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should accept name with special characters', () => {
|
|
40
|
-
const result = validateDisplayName('John-O\'Connor');
|
|
41
|
-
expect(result.isValid).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { validateEmail } from '../../../src/infrastructure/utils/AuthValidation';
|
|
2
|
-
|
|
3
|
-
describe('AuthEmailValidation', () => {
|
|
4
|
-
describe('validateEmail', () => {
|
|
5
|
-
it('should reject empty email', () => {
|
|
6
|
-
const result = validateEmail('');
|
|
7
|
-
expect(result.isValid).toBe(false);
|
|
8
|
-
expect(result.error).toBe('Email is required');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('should reject whitespace-only email', () => {
|
|
12
|
-
const result = validateEmail(' ');
|
|
13
|
-
expect(result.isValid).toBe(false);
|
|
14
|
-
expect(result.error).toBe('Email is required');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should reject invalid email format', () => {
|
|
18
|
-
const result = validateEmail('invalid-email');
|
|
19
|
-
expect(result.isValid).toBe(false);
|
|
20
|
-
expect(result.error).toBe('Please enter a valid email address');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should accept valid email format', () => {
|
|
24
|
-
const result = validateEmail('test@example.com');
|
|
25
|
-
expect(result.isValid).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should accept valid email with subdomain', () => {
|
|
29
|
-
const result = validateEmail('test@mail.example.com');
|
|
30
|
-
expect(result.isValid).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should trim whitespace', () => {
|
|
34
|
-
const result = validateEmail(' test@example.com ');
|
|
35
|
-
expect(result.isValid).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
validatePasswordForLogin,
|
|
3
|
-
validatePasswordForRegister,
|
|
4
|
-
validatePasswordConfirmation,
|
|
5
|
-
} from '../../../src/infrastructure/utils/AuthValidation';
|
|
6
|
-
import { DEFAULT_PASSWORD_CONFIG } from '../../../src/domain/value-objects/AuthConfig';
|
|
7
|
-
|
|
8
|
-
describe('AuthPasswordValidation', () => {
|
|
9
|
-
describe('validatePasswordForLogin', () => {
|
|
10
|
-
it('should reject empty password', () => {
|
|
11
|
-
const result = validatePasswordForLogin('');
|
|
12
|
-
expect(result.isValid).toBe(false);
|
|
13
|
-
expect(result.error).toBe('Password is required');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('should accept any non-empty password', () => {
|
|
17
|
-
const result = validatePasswordForLogin('any');
|
|
18
|
-
expect(result.isValid).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('validatePasswordForRegister', () => {
|
|
23
|
-
const config = DEFAULT_PASSWORD_CONFIG;
|
|
24
|
-
|
|
25
|
-
it('should reject empty password', () => {
|
|
26
|
-
const result = validatePasswordForRegister('', config);
|
|
27
|
-
expect(result.isValid).toBe(false);
|
|
28
|
-
expect(result.requirements.hasMinLength).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should reject password that is too short', () => {
|
|
32
|
-
const result = validatePasswordForRegister('123', config);
|
|
33
|
-
expect(result.isValid).toBe(false);
|
|
34
|
-
expect(result.requirements.hasMinLength).toBe(false);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should accept password that meets length requirement', () => {
|
|
38
|
-
const result = validatePasswordForRegister('Password1!', config);
|
|
39
|
-
expect(result.isValid).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('validatePasswordConfirmation', () => {
|
|
44
|
-
it('should reject empty confirmation', () => {
|
|
45
|
-
const result = validatePasswordConfirmation('password', '');
|
|
46
|
-
expect(result.isValid).toBe(false);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should reject mismatched passwords', () => {
|
|
50
|
-
const result = validatePasswordConfirmation('password', 'different');
|
|
51
|
-
expect(result.isValid).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should accept matching passwords', () => {
|
|
55
|
-
const result = validatePasswordConfirmation('password', 'password');
|
|
56
|
-
expect(result.isValid).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
});
|