···
1
+
import 'package:coves_flutter/models/comment.dart';
2
+
import 'package:coves_flutter/services/coves_api_service.dart';
3
+
import 'package:dio/dio.dart';
4
+
import 'package:flutter_test/flutter_test.dart';
5
+
import 'package:http_mock_adapter/http_mock_adapter.dart';
8
+
TestWidgetsFlutterBinding.ensureInitialized();
10
+
group('CovesApiService - getComments', () {
12
+
late DioAdapter dioAdapter;
13
+
late CovesApiService apiService;
16
+
dio = Dio(BaseOptions(baseUrl: 'https://api.test.coves.social'));
17
+
dioAdapter = DioAdapter(dio: dio);
18
+
apiService = CovesApiService(
20
+
tokenGetter: () async => 'test-token',
25
+
apiService.dispose();
28
+
test('should successfully fetch comments', () async {
29
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
31
+
final mockResponse = {
32
+
'post': {'uri': postUri},
33
+
'cursor': 'next-cursor',
37
+
'uri': 'at://did:plc:test/comment/1',
39
+
'content': 'Test comment 1',
40
+
'createdAt': '2025-01-01T12:00:00Z',
41
+
'indexedAt': '2025-01-01T12:00:00Z',
43
+
'did': 'did:plc:author1',
44
+
'handle': 'user1.test',
45
+
'displayName': 'User One',
61
+
'uri': 'at://did:plc:test/comment/2',
63
+
'content': 'Test comment 2',
64
+
'createdAt': '2025-01-01T13:00:00Z',
65
+
'indexedAt': '2025-01-01T13:00:00Z',
67
+
'did': 'did:plc:author2',
68
+
'handle': 'user2.test',
86
+
'/xrpc/social.coves.community.comment.getComments',
87
+
(server) => server.reply(200, mockResponse),
96
+
final response = await apiService.getComments(postUri: postUri);
98
+
expect(response, isA<CommentsResponse>());
99
+
expect(response.comments.length, 2);
100
+
expect(response.cursor, 'next-cursor');
101
+
expect(response.comments[0].comment.uri, 'at://did:plc:test/comment/1');
102
+
expect(response.comments[0].comment.content, 'Test comment 1');
103
+
expect(response.comments[1].comment.uri, 'at://did:plc:test/comment/2');
106
+
test('should handle empty comments response', () async {
107
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
109
+
final mockResponse = {
110
+
'post': {'uri': postUri},
116
+
'/xrpc/social.coves.community.comment.getComments',
117
+
(server) => server.reply(200, mockResponse),
126
+
final response = await apiService.getComments(postUri: postUri);
128
+
expect(response.comments, isEmpty);
129
+
expect(response.cursor, null);
132
+
test('should handle null comments array', () async {
133
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
135
+
final mockResponse = {
136
+
'post': {'uri': postUri},
142
+
'/xrpc/social.coves.community.comment.getComments',
143
+
(server) => server.reply(200, mockResponse),
152
+
final response = await apiService.getComments(postUri: postUri);
154
+
expect(response.comments, isEmpty);
157
+
test('should fetch comments with custom sort option', () async {
158
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
160
+
final mockResponse = {
161
+
'post': {'uri': postUri},
166
+
'uri': 'at://did:plc:test/comment/1',
168
+
'content': 'Newest comment',
169
+
'createdAt': '2025-01-01T15:00:00Z',
170
+
'indexedAt': '2025-01-01T15:00:00Z',
172
+
'did': 'did:plc:author',
173
+
'handle': 'user.test',
191
+
'/xrpc/social.coves.community.comment.getComments',
192
+
(server) => server.reply(200, mockResponse),
201
+
final response = await apiService.getComments(
206
+
expect(response.comments.length, 1);
207
+
expect(response.comments[0].comment.content, 'Newest comment');
210
+
test('should fetch comments with timeframe', () async {
211
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
213
+
final mockResponse = {
214
+
'post': {'uri': postUri},
220
+
'/xrpc/social.coves.community.comment.getComments',
221
+
(server) => server.reply(200, mockResponse),
225
+
'timeframe': 'week',
231
+
final response = await apiService.getComments(
237
+
expect(response, isA<CommentsResponse>());
240
+
test('should fetch comments with cursor for pagination', () async {
241
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
242
+
const cursor = 'pagination-cursor-123';
244
+
final mockResponse = {
245
+
'post': {'uri': postUri},
246
+
'cursor': 'next-cursor-456',
250
+
'uri': 'at://did:plc:test/comment/10',
252
+
'content': 'Paginated comment',
253
+
'createdAt': '2025-01-01T12:00:00Z',
254
+
'indexedAt': '2025-01-01T12:00:00Z',
256
+
'did': 'did:plc:author',
257
+
'handle': 'user.test',
275
+
'/xrpc/social.coves.community.comment.getComments',
276
+
(server) => server.reply(200, mockResponse),
286
+
final response = await apiService.getComments(
291
+
expect(response.comments.length, 1);
292
+
expect(response.cursor, 'next-cursor-456');
295
+
test('should fetch comments with custom depth and limit', () async {
296
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
298
+
final mockResponse = {
299
+
'post': {'uri': postUri},
305
+
'/xrpc/social.coves.community.comment.getComments',
306
+
(server) => server.reply(200, mockResponse),
315
+
final response = await apiService.getComments(
321
+
expect(response, isA<CommentsResponse>());
324
+
test('should handle 404 error', () async {
325
+
const postUri = 'at://did:plc:test/social.coves.post.record/nonexistent';
328
+
'/xrpc/social.coves.community.comment.getComments',
329
+
(server) => server.reply(404, {
330
+
'error': 'NotFoundError',
331
+
'message': 'Post not found',
342
+
() => apiService.getComments(postUri: postUri),
343
+
throwsA(isA<Exception>()),
347
+
test('should handle 500 internal server error', () async {
348
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
351
+
'/xrpc/social.coves.community.comment.getComments',
352
+
(server) => server.reply(500, {
353
+
'error': 'InternalServerError',
354
+
'message': 'Database connection failed',
365
+
() => apiService.getComments(postUri: postUri),
366
+
throwsA(isA<Exception>()),
370
+
test('should handle network timeout', () async {
371
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
374
+
'/xrpc/social.coves.community.comment.getComments',
375
+
(server) => server.throws(
377
+
DioException.connectionTimeout(
378
+
timeout: const Duration(seconds: 30),
379
+
requestOptions: RequestOptions(path: ''),
391
+
() => apiService.getComments(postUri: postUri),
392
+
throwsA(isA<DioException>()),
396
+
test('should handle network connection error', () async {
397
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
400
+
'/xrpc/social.coves.community.comment.getComments',
401
+
(server) => server.throws(
403
+
DioException.connectionError(
404
+
reason: 'Connection refused',
405
+
requestOptions: RequestOptions(path: ''),
417
+
() => apiService.getComments(postUri: postUri),
418
+
throwsA(isA<DioException>()),
422
+
test('should handle invalid JSON response', () async {
423
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
426
+
'/xrpc/social.coves.community.comment.getComments',
427
+
(server) => server.reply(200, 'invalid json string'),
437
+
() => apiService.getComments(postUri: postUri),
438
+
throwsA(isA<Exception>()),
442
+
test('should handle malformed JSON with missing required fields', () async {
443
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
445
+
final mockResponse = {
446
+
'post': {'uri': postUri},
450
+
'uri': 'at://did:plc:test/comment/1',
451
+
// Missing required 'cid' field
453
+
'createdAt': '2025-01-01T12:00:00Z',
454
+
'indexedAt': '2025-01-01T12:00:00Z',
456
+
'did': 'did:plc:author',
457
+
'handle': 'user.test',
475
+
'/xrpc/social.coves.community.comment.getComments',
476
+
(server) => server.reply(200, mockResponse),
486
+
() => apiService.getComments(postUri: postUri),
487
+
throwsA(isA<Exception>()),
491
+
test('should handle comments with nested replies', () async {
492
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
494
+
final mockResponse = {
495
+
'post': {'uri': postUri},
500
+
'uri': 'at://did:plc:test/comment/1',
502
+
'content': 'Parent comment',
503
+
'createdAt': '2025-01-01T12:00:00Z',
504
+
'indexedAt': '2025-01-01T12:00:00Z',
506
+
'did': 'did:plc:author1',
507
+
'handle': 'user1.test',
522
+
'uri': 'at://did:plc:test/comment/2',
524
+
'content': 'Reply comment',
525
+
'createdAt': '2025-01-01T13:00:00Z',
526
+
'indexedAt': '2025-01-01T13:00:00Z',
528
+
'did': 'did:plc:author2',
529
+
'handle': 'user2.test',
536
+
'uri': 'at://did:plc:test/comment/1',
554
+
'/xrpc/social.coves.community.comment.getComments',
555
+
(server) => server.reply(200, mockResponse),
564
+
final response = await apiService.getComments(postUri: postUri);
566
+
expect(response.comments.length, 1);
567
+
expect(response.comments[0].comment.content, 'Parent comment');
568
+
expect(response.comments[0].replies, isNotNull);
569
+
expect(response.comments[0].replies!.length, 1);
570
+
expect(response.comments[0].replies![0].comment.content, 'Reply comment');
573
+
test('should handle comments with viewer state', () async {
574
+
const postUri = 'at://did:plc:test/social.coves.post.record/123';
576
+
final mockResponse = {
577
+
'post': {'uri': postUri},
582
+
'uri': 'at://did:plc:test/comment/1',
584
+
'content': 'Voted comment',
585
+
'createdAt': '2025-01-01T12:00:00Z',
586
+
'indexedAt': '2025-01-01T12:00:00Z',
588
+
'did': 'did:plc:author',
589
+
'handle': 'user.test',
610
+
'/xrpc/social.coves.community.comment.getComments',
611
+
(server) => server.reply(200, mockResponse),
620
+
final response = await apiService.getComments(postUri: postUri);
622
+
expect(response.comments.length, 1);
623
+
expect(response.comments[0].comment.viewer, isNotNull);
624
+
expect(response.comments[0].comment.viewer!.vote, 'upvote');