Skip to content

Commit 42602cc

Browse files
committed
test: add tests for request-code endpoint
- Covers success and failure scenarios - Validates request body and email format - Handles AuthService exceptions
1 parent 7165819 commit 42602cc

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import 'dart:convert';
2+
import 'dart:io';
3+
4+
import 'package:dart_frog/dart_frog.dart';
5+
import 'package:ht_api/src/services/auth_service.dart';
6+
import 'package:ht_shared/ht_shared.dart';
7+
import 'package:mocktail/mocktail.dart';
8+
import 'package:test/test.dart';
9+
10+
import '../../../../helpers/create_mock_request_context.dart';
11+
import '../../../../helpers/mock_classes.dart';
12+
// Import the actual route handler
13+
import '../../../../../routes/api/v1/auth/request-code.dart' as route;
14+
15+
void main() {
16+
group('POST /api/v1/auth/request-code', () {
17+
late MockAuthService mockAuthService;
18+
late MockRequest mockRequest;
19+
const validEmail = '[email protected]';
20+
final validRequestBody = jsonEncode({'email': validEmail});
21+
22+
setUp(() {
23+
mockAuthService = MockAuthService();
24+
mockRequest = MockRequest();
25+
26+
// Default stub for POST method
27+
when(() => mockRequest.method).thenReturn(HttpMethod.post);
28+
// Default stub for headers
29+
when(() => mockRequest.headers).thenReturn(
30+
{HttpHeaders.contentTypeHeader: ContentType.json.mimeType},
31+
);
32+
// Default stub for valid body
33+
when(() => mockRequest.body()).thenAnswer((_) async => validRequestBody);
34+
when(() => mockRequest.json()).thenAnswer(
35+
(_) async => jsonDecode(validRequestBody) as Map<String, dynamic>,
36+
);
37+
});
38+
39+
test('returns 202 Accepted on successful code request', () async {
40+
// Arrange
41+
when(() => mockAuthService.initiateEmailSignIn(validEmail))
42+
.thenAnswer((_) async {}); // Simulate successful void call
43+
44+
final context = createMockRequestContext(
45+
request: mockRequest,
46+
dependencies: {AuthService: mockAuthService},
47+
);
48+
49+
// Act
50+
final response = await route.onRequest(context);
51+
52+
// Assert
53+
expect(response.statusCode, equals(HttpStatus.accepted));
54+
verify(() => mockAuthService.initiateEmailSignIn(validEmail)).called(1);
55+
});
56+
57+
test('returns 405 Method Not Allowed for non-POST requests', () async {
58+
// Arrange
59+
when(() => mockRequest.method).thenReturn(HttpMethod.get);
60+
final context = createMockRequestContext(
61+
request: mockRequest,
62+
dependencies: {AuthService: mockAuthService},
63+
);
64+
65+
// Act
66+
final response = await route.onRequest(context);
67+
68+
// Assert
69+
expect(response.statusCode, equals(HttpStatus.methodNotAllowed));
70+
verifyNever(() => mockAuthService.initiateEmailSignIn(any()));
71+
});
72+
73+
test('throws InvalidInputException for invalid JSON body', () async {
74+
// Arrange
75+
when(() => mockRequest.json()).thenThrow(FormatException('Invalid JSON'));
76+
final context = createMockRequestContext(
77+
request: mockRequest,
78+
dependencies: {AuthService: mockAuthService},
79+
);
80+
81+
// Act & Assert
82+
expect(
83+
() => route.onRequest(context),
84+
throwsA(isA<InvalidInputException>().having(
85+
(e) => e.message,
86+
'message',
87+
'Invalid JSON format in request body.',
88+
)),
89+
);
90+
verifyNever(() => mockAuthService.initiateEmailSignIn(any()));
91+
});
92+
93+
test('throws InvalidInputException for non-object JSON body', () async {
94+
// Arrange
95+
when(() => mockRequest.json()).thenAnswer((_) async => []); // Array body
96+
final context = createMockRequestContext(
97+
request: mockRequest,
98+
dependencies: {AuthService: mockAuthService},
99+
);
100+
101+
// Act & Assert
102+
expect(
103+
() => route.onRequest(context),
104+
throwsA(isA<InvalidInputException>().having(
105+
(e) => e.message,
106+
'message',
107+
'Request body must be a JSON object.',
108+
)),
109+
);
110+
verifyNever(() => mockAuthService.initiateEmailSignIn(any()));
111+
});
112+
113+
114+
test('throws InvalidInputException for missing email field', () async {
115+
// Arrange
116+
when(() => mockRequest.json()).thenAnswer((_) async => <String, dynamic>{});
117+
final context = createMockRequestContext(
118+
request: mockRequest,
119+
dependencies: {AuthService: mockAuthService},
120+
);
121+
122+
// Act & Assert
123+
expect(
124+
() => route.onRequest(context),
125+
throwsA(isA<InvalidInputException>().having(
126+
(e) => e.message,
127+
'message',
128+
'Missing or empty "email" field in request body.',
129+
)),
130+
);
131+
verifyNever(() => mockAuthService.initiateEmailSignIn(any()));
132+
});
133+
134+
test('throws InvalidInputException for empty email field', () async {
135+
// Arrange
136+
when(() => mockRequest.json()).thenAnswer((_) async => {'email': ''});
137+
final context = createMockRequestContext(
138+
request: mockRequest,
139+
dependencies: {AuthService: mockAuthService},
140+
);
141+
142+
// Act & Assert
143+
expect(
144+
() => route.onRequest(context),
145+
throwsA(isA<InvalidInputException>().having(
146+
(e) => e.message,
147+
'message',
148+
'Missing or empty "email" field in request body.',
149+
)),
150+
);
151+
verifyNever(() => mockAuthService.initiateEmailSignIn(any()));
152+
});
153+
154+
155+
test('throws InvalidInputException for invalid email format', () async {
156+
// Arrange
157+
const invalidEmail = 'not-an-email';
158+
when(() => mockRequest.json()).thenAnswer((_) async => {'email': invalidEmail});
159+
final context = createMockRequestContext(
160+
request: mockRequest,
161+
dependencies: {AuthService: mockAuthService},
162+
);
163+
164+
// Act & Assert
165+
expect(
166+
() => route.onRequest(context),
167+
throwsA(isA<InvalidInputException>().having(
168+
(e) => e.message,
169+
'message',
170+
'Invalid email format provided.',
171+
)),
172+
);
173+
verifyNever(() => mockAuthService.initiateEmailSignIn(any()));
174+
});
175+
176+
177+
test('rethrows HtHttpException from AuthService', () async {
178+
// Arrange
179+
const exception = OperationFailedException('Email service down');
180+
when(() => mockAuthService.initiateEmailSignIn(validEmail))
181+
.thenThrow(exception);
182+
final context = createMockRequestContext(
183+
request: mockRequest,
184+
dependencies: {AuthService: mockAuthService},
185+
);
186+
187+
// Act & Assert
188+
expect(
189+
() => route.onRequest(context),
190+
throwsA(isA<OperationFailedException>()),
191+
);
192+
verify(() => mockAuthService.initiateEmailSignIn(validEmail)).called(1);
193+
});
194+
195+
test('throws OperationFailedException for unexpected errors', () async {
196+
// Arrange
197+
final exception = Exception('Unexpected');
198+
when(() => mockAuthService.initiateEmailSignIn(validEmail))
199+
.thenThrow(exception);
200+
final context = createMockRequestContext(
201+
request: mockRequest,
202+
dependencies: {AuthService: mockAuthService},
203+
);
204+
205+
// Act & Assert
206+
expect(
207+
() => route.onRequest(context),
208+
throwsA(isA<OperationFailedException>().having(
209+
(e) => e.message,
210+
'message',
211+
'An unexpected error occurred while requesting the sign-in code.',
212+
)),
213+
);
214+
verify(() => mockAuthService.initiateEmailSignIn(validEmail)).called(1);
215+
});
216+
});
217+
}

0 commit comments

Comments
 (0)