Skip to content

Commit b59de98

Browse files
committed
test: add sign-out route tests
- Tests successful sign-out - Tests method not allowed - Tests unauthorized user - Tests auth service exceptions - Tests unexpected errors
1 parent 42602cc commit b59de98

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import 'dart:io';
2+
3+
import 'package:dart_frog/dart_frog.dart';
4+
import 'package:ht_api/src/services/auth_service.dart';
5+
import 'package:ht_shared/ht_shared.dart';
6+
import 'package:mocktail/mocktail.dart';
7+
import 'package:test/test.dart';
8+
9+
import '../../../../helpers/create_mock_request_context.dart';
10+
import '../../../../helpers/mock_classes.dart';
11+
// Import the actual route handler
12+
import '../../../../../routes/api/v1/auth/sign-out.dart' as route;
13+
14+
void main() {
15+
group('POST /api/v1/auth/sign-out', () {
16+
late MockAuthService mockAuthService;
17+
late MockRequest mockRequest;
18+
19+
// Define a sample authenticated user
20+
final testUser = User(
21+
id: 'user-789',
22+
23+
isAnonymous: false,
24+
);
25+
26+
setUp(() {
27+
mockAuthService = MockAuthService();
28+
mockRequest = MockRequest();
29+
30+
// Default stub for POST method
31+
when(() => mockRequest.method).thenReturn(HttpMethod.post);
32+
// Default stub for headers (authentication handled by middleware context)
33+
when(() => mockRequest.headers).thenReturn({});
34+
// Sign-out doesn't typically have a body
35+
when(() => mockRequest.body()).thenAnswer((_) async => '');
36+
when(() => mockRequest.json()).thenAnswer((_) async => <String, dynamic>{});
37+
});
38+
39+
test('returns 204 No Content on successful sign-out', () async {
40+
// Arrange
41+
when(() => mockAuthService.performSignOut(userId: testUser.id))
42+
.thenAnswer((_) async {}); // Simulate successful void call
43+
44+
final context = createMockRequestContext(
45+
request: mockRequest,
46+
dependencies: {
47+
AuthService: mockAuthService,
48+
// Provide the authenticated user via context
49+
User: testUser,
50+
},
51+
);
52+
53+
// Act
54+
final response = await route.onRequest(context);
55+
56+
// Assert
57+
expect(response.statusCode, equals(HttpStatus.noContent));
58+
// Verify the service method was called correctly
59+
verify(() => mockAuthService.performSignOut(userId: testUser.id)).called(1);
60+
});
61+
62+
test('returns 405 Method Not Allowed for non-POST requests', () async {
63+
// Arrange
64+
when(() => mockRequest.method).thenReturn(HttpMethod.delete); // Test DELETE
65+
final context = createMockRequestContext(
66+
request: mockRequest,
67+
dependencies: {
68+
AuthService: mockAuthService,
69+
User: testUser, // User presence doesn't matter for method check
70+
},
71+
);
72+
73+
// Act
74+
final response = await route.onRequest(context);
75+
76+
// Assert
77+
expect(response.statusCode, equals(HttpStatus.methodNotAllowed));
78+
verifyNever(() => mockAuthService.performSignOut(userId: any(named: 'userId')));
79+
});
80+
81+
test('throws UnauthorizedException if user is null in context', () async {
82+
// Arrange
83+
// This scenario assumes the requireAuthentication middleware somehow failed
84+
// or wasn't applied.
85+
final context = createMockRequestContext(
86+
request: mockRequest,
87+
dependencies: {
88+
AuthService: mockAuthService,
89+
// Explicitly provide null User
90+
User: null,
91+
},
92+
);
93+
94+
// Act & Assert
95+
// Expect the handler to throw the exception
96+
expect(
97+
() => route.onRequest(context),
98+
throwsA(isA<UnauthorizedException>().having(
99+
(e) => e.message,
100+
'message',
101+
'Authentication required to sign out.',
102+
)),
103+
);
104+
verifyNever(() => mockAuthService.performSignOut(userId: any(named: 'userId')));
105+
});
106+
107+
test('rethrows HtHttpException from AuthService', () async {
108+
// Arrange
109+
// Example: Maybe performSignOut throws if token invalidation fails
110+
const exception = OperationFailedException('Token revocation failed');
111+
when(() => mockAuthService.performSignOut(userId: testUser.id))
112+
.thenThrow(exception);
113+
114+
final context = createMockRequestContext(
115+
request: mockRequest,
116+
dependencies: {
117+
AuthService: mockAuthService,
118+
User: testUser,
119+
},
120+
);
121+
122+
// Act & Assert
123+
expect(
124+
() => route.onRequest(context),
125+
throwsA(isA<OperationFailedException>()),
126+
);
127+
verify(() => mockAuthService.performSignOut(userId: testUser.id)).called(1);
128+
});
129+
130+
test('throws OperationFailedException for unexpected errors', () async {
131+
// Arrange
132+
final exception = Exception('Unexpected');
133+
when(() => mockAuthService.performSignOut(userId: testUser.id))
134+
.thenThrow(exception);
135+
136+
final context = createMockRequestContext(
137+
request: mockRequest,
138+
dependencies: {
139+
AuthService: mockAuthService,
140+
User: testUser,
141+
},
142+
);
143+
144+
// Act & Assert
145+
expect(
146+
() => route.onRequest(context),
147+
throwsA(isA<OperationFailedException>().having(
148+
(e) => e.message,
149+
'message',
150+
'An unexpected error occurred during sign-out.',
151+
)),
152+
);
153+
verify(() => mockAuthService.performSignOut(userId: testUser.id)).called(1);
154+
});
155+
});
156+
}

0 commit comments

Comments
 (0)