test: add tests for CommentService and CommentsProvider.createComment

CommentService tests (10 tests):
- Successful comment creation
- Auth failure when no session
- Network error handling
- 401 response handling
- Invalid responses (null data, missing uri, empty uri)
- Server error handling
- Nested reply request format

CommentsProvider.createComment tests (12 tests):
- Validation: empty content, whitespace-only, exceeds limit
- Emoji counting with grapheme clusters
- Error states: no post loaded, no CommentService
- Top-level comment (reply to post)
- Nested comment (reply to comment)
- Content trimming
- Refresh after success
- Exception propagation
- Boundary testing at max length

All 52 tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

+447 -8
test/providers/comments_provider_test.dart
···
import 'package:coves_flutter/providers/auth_provider.dart';
import 'package:coves_flutter/providers/comments_provider.dart';
import 'package:coves_flutter/providers/vote_provider.dart';
+
import 'package:coves_flutter/services/api_exceptions.dart';
+
import 'package:coves_flutter/services/comment_service.dart';
import 'package:coves_flutter/services/coves_api_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
···
import 'comments_provider_test.mocks.dart';
// Generate mocks for dependencies
-
@GenerateMocks([AuthProvider, CovesApiService, VoteProvider])
+
@GenerateMocks([AuthProvider, CovesApiService, VoteProvider, CommentService])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('CommentsProvider', () {
+
const testPostUri = 'at://did:plc:test/social.coves.post.record/123';
+
const testPostCid = 'test-post-cid';
+
late CommentsProvider commentsProvider;
late MockAuthProvider mockAuthProvider;
late MockCovesApiService mockApiService;
···
});
group('loadComments', () {
-
const testPostUri = 'at://did:plc:test/social.coves.post.record/123';
-
test('should load comments successfully', () async {
final mockComments = [
_createMockThreadComment('comment1'),
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
),
).thenAnswer((_) async => secondResponse);
-
await commentsProvider.loadComments(postUri: testPostUri);
+
await commentsProvider.loadComments(postUri: testPostUri, postCid: testPostCid);
expect(commentsProvider.comments.length, 2);
expect(commentsProvider.comments[0].comment.uri, 'comment1');
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
// Load different post
const differentPostUri =
'at://did:plc:test/social.coves.post.record/456';
+
const differentPostCid = 'different-post-cid';
final secondResponse = CommentsResponse(
post: {},
comments: [_createMockThreadComment('comment2')],
···
await commentsProvider.loadComments(
postUri: differentPostUri,
+
postCid: differentPostCid,
refresh: true,
);
···
// Start first load
final firstFuture = commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
// Try to load again while still loading - should schedule a refresh
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
expect(commentsProvider.currentTimeNotifier.value, null);
await commentsProvider.loadComments(
-
postUri: 'at://did:plc:test/social.coves.post.record/123',
+
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
).thenAnswer((_) async => response);
await commentsProvider.loadComments(
-
postUri: 'at://did:plc:test/social.coves.post.record/123',
+
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
).thenAnswer((_) async => response);
await commentsProvider.loadComments(
-
postUri: 'at://did:plc:test/social.coves.post.record/123',
+
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
});
final loadFuture = commentsProvider.loadComments(
-
postUri: 'at://did:plc:test/social.coves.post.record/123',
+
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
// Load first page (refresh)
await commentsProvider.loadComments(
postUri: testPostUri,
+
postCid: testPostCid,
refresh: true,
);
···
);
},
);
+
});
+
+
group('createComment', () {
+
late MockCommentService mockCommentService;
+
late CommentsProvider providerWithCommentService;
+
+
setUp(() {
+
mockCommentService = MockCommentService();
+
+
// Setup mock API service for loadComments
+
final mockResponse = CommentsResponse(
+
post: {},
+
comments: [_createMockThreadComment('comment1')],
+
);
+
when(
+
mockApiService.getComments(
+
postUri: anyNamed('postUri'),
+
sort: anyNamed('sort'),
+
timeframe: anyNamed('timeframe'),
+
depth: anyNamed('depth'),
+
limit: anyNamed('limit'),
+
cursor: anyNamed('cursor'),
+
),
+
).thenAnswer((_) async => mockResponse);
+
+
providerWithCommentService = CommentsProvider(
+
mockAuthProvider,
+
apiService: mockApiService,
+
voteProvider: mockVoteProvider,
+
commentService: mockCommentService,
+
);
+
});
+
+
tearDown(() {
+
providerWithCommentService.dispose();
+
});
+
+
test('should throw ValidationException for empty content', () async {
+
// First load comments to set up post context
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
expect(
+
() => providerWithCommentService.createComment(content: ''),
+
throwsA(
+
isA<ValidationException>().having(
+
(e) => e.message,
+
'message',
+
contains('empty'),
+
),
+
),
+
);
+
});
+
+
test('should throw ValidationException for whitespace-only content', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
expect(
+
() => providerWithCommentService.createComment(content: ' \n\t '),
+
throwsA(isA<ValidationException>()),
+
);
+
});
+
+
test('should throw ValidationException for content exceeding limit', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
// Create a string longer than 10000 characters
+
final longContent = 'a' * 10001;
+
+
expect(
+
() => providerWithCommentService.createComment(content: longContent),
+
throwsA(
+
isA<ValidationException>().having(
+
(e) => e.message,
+
'message',
+
contains('too long'),
+
),
+
),
+
);
+
});
+
+
test('should count emoji correctly in character limit', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
// Each emoji should count as 1 character, not 2-4 bytes
+
// 9999 'a' chars + 1 emoji = 10000 chars (should pass)
+
final contentAtLimit = '${'a' * 9999}😀';
+
+
when(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: anyNamed('content'),
+
),
+
).thenAnswer(
+
(_) async => const CreateCommentResponse(
+
uri: 'at://did:plc:test/comment/abc',
+
cid: 'cid123',
+
),
+
);
+
+
// This should NOT throw
+
await providerWithCommentService.createComment(content: contentAtLimit);
+
+
verify(
+
mockCommentService.createComment(
+
rootUri: testPostUri,
+
rootCid: testPostCid,
+
parentUri: testPostUri,
+
parentCid: testPostCid,
+
content: contentAtLimit,
+
),
+
).called(1);
+
});
+
+
test('should throw ApiException when no post loaded', () async {
+
// Don't call loadComments first - no post context
+
+
expect(
+
() => providerWithCommentService.createComment(
+
content: 'Test comment',
+
),
+
throwsA(
+
isA<ApiException>().having(
+
(e) => e.message,
+
'message',
+
contains('No post loaded'),
+
),
+
),
+
);
+
});
+
+
test('should throw ApiException when no CommentService', () async {
+
// Create provider without CommentService
+
final providerWithoutService = CommentsProvider(
+
mockAuthProvider,
+
apiService: mockApiService,
+
voteProvider: mockVoteProvider,
+
);
+
+
await providerWithoutService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
expect(
+
() => providerWithoutService.createComment(content: 'Test comment'),
+
throwsA(
+
isA<ApiException>().having(
+
(e) => e.message,
+
'message',
+
contains('CommentService not available'),
+
),
+
),
+
);
+
+
providerWithoutService.dispose();
+
});
+
+
test('should create top-level comment (reply to post)', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
when(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: anyNamed('content'),
+
),
+
).thenAnswer(
+
(_) async => const CreateCommentResponse(
+
uri: 'at://did:plc:test/comment/abc',
+
cid: 'cid123',
+
),
+
);
+
+
await providerWithCommentService.createComment(
+
content: 'This is a test comment',
+
);
+
+
// Verify the comment service was called with correct parameters
+
// Root and parent should both be the post for top-level comments
+
verify(
+
mockCommentService.createComment(
+
rootUri: testPostUri,
+
rootCid: testPostCid,
+
parentUri: testPostUri,
+
parentCid: testPostCid,
+
content: 'This is a test comment',
+
),
+
).called(1);
+
});
+
+
test('should create nested comment (reply to comment)', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
when(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: anyNamed('content'),
+
),
+
).thenAnswer(
+
(_) async => const CreateCommentResponse(
+
uri: 'at://did:plc:test/comment/reply1',
+
cid: 'cidReply',
+
),
+
);
+
+
// Create a parent comment to reply to
+
final parentComment = _createMockThreadComment('parent-comment');
+
+
await providerWithCommentService.createComment(
+
content: 'This is a nested reply',
+
parentComment: parentComment,
+
);
+
+
// Root should still be the post, but parent should be the comment
+
verify(
+
mockCommentService.createComment(
+
rootUri: testPostUri,
+
rootCid: testPostCid,
+
parentUri: 'parent-comment',
+
parentCid: 'cid-parent-comment',
+
content: 'This is a nested reply',
+
),
+
).called(1);
+
});
+
+
test('should trim content before sending', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
when(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: anyNamed('content'),
+
),
+
).thenAnswer(
+
(_) async => const CreateCommentResponse(
+
uri: 'at://did:plc:test/comment/abc',
+
cid: 'cid123',
+
),
+
);
+
+
await providerWithCommentService.createComment(
+
content: ' Hello world! ',
+
);
+
+
// Verify trimmed content was sent
+
verify(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: 'Hello world!',
+
),
+
).called(1);
+
});
+
+
test('should refresh comments after successful creation', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
when(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: anyNamed('content'),
+
),
+
).thenAnswer(
+
(_) async => const CreateCommentResponse(
+
uri: 'at://did:plc:test/comment/abc',
+
cid: 'cid123',
+
),
+
);
+
+
await providerWithCommentService.createComment(
+
content: 'Test comment',
+
);
+
+
// Should have called getComments twice - once for initial load,
+
// once for refresh after comment creation
+
verify(
+
mockApiService.getComments(
+
postUri: anyNamed('postUri'),
+
sort: anyNamed('sort'),
+
timeframe: anyNamed('timeframe'),
+
depth: anyNamed('depth'),
+
limit: anyNamed('limit'),
+
cursor: anyNamed('cursor'),
+
),
+
).called(2);
+
});
+
+
test('should rethrow exception from CommentService', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
when(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: anyNamed('content'),
+
),
+
).thenThrow(ApiException('Network error'));
+
+
expect(
+
() => providerWithCommentService.createComment(
+
content: 'Test comment',
+
),
+
throwsA(
+
isA<ApiException>().having(
+
(e) => e.message,
+
'message',
+
contains('Network error'),
+
),
+
),
+
);
+
});
+
+
test('should accept content at exactly max length', () async {
+
await providerWithCommentService.loadComments(
+
postUri: testPostUri,
+
postCid: testPostCid,
+
refresh: true,
+
);
+
+
final contentAtLimit = 'a' * CommentsProvider.maxCommentLength;
+
+
when(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: anyNamed('content'),
+
),
+
).thenAnswer(
+
(_) async => const CreateCommentResponse(
+
uri: 'at://did:plc:test/comment/abc',
+
cid: 'cid123',
+
),
+
);
+
+
// Should not throw
+
await providerWithCommentService.createComment(content: contentAtLimit);
+
+
verify(
+
mockCommentService.createComment(
+
rootUri: anyNamed('rootUri'),
+
rootCid: anyNamed('rootCid'),
+
parentUri: anyNamed('parentUri'),
+
parentCid: anyNamed('parentCid'),
+
content: contentAtLimit,
+
),
+
).called(1);
+
});
});
});
+91 -44
test/providers/comments_provider_test.mocks.dart
···
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
-
import 'dart:async' as _i5;
-
import 'dart:ui' as _i6;
+
import 'dart:async' as _i6;
+
import 'dart:ui' as _i7;
import 'package:coves_flutter/models/comment.dart' as _i3;
import 'package:coves_flutter/models/post.dart' as _i2;
-
import 'package:coves_flutter/providers/auth_provider.dart' as _i4;
-
import 'package:coves_flutter/providers/vote_provider.dart' as _i8;
-
import 'package:coves_flutter/services/coves_api_service.dart' as _i7;
+
import 'package:coves_flutter/providers/auth_provider.dart' as _i5;
+
import 'package:coves_flutter/providers/vote_provider.dart' as _i9;
+
import 'package:coves_flutter/services/comment_service.dart' as _i4;
+
import 'package:coves_flutter/services/coves_api_service.dart' as _i8;
import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: type=lint
···
class _FakeCommentsResponse_1 extends _i1.SmartFake
implements _i3.CommentsResponse {
_FakeCommentsResponse_1(Object parent, Invocation parentInvocation)
+
: super(parent, parentInvocation);
+
}
+
+
class _FakeCreateCommentResponse_2 extends _i1.SmartFake
+
implements _i4.CreateCommentResponse {
+
_FakeCreateCommentResponse_2(Object parent, Invocation parentInvocation)
: super(parent, parentInvocation);
}
/// A class which mocks [AuthProvider].
///
/// See the documentation for Mockito's code generation for more information.
-
class MockAuthProvider extends _i1.Mock implements _i4.AuthProvider {
+
class MockAuthProvider extends _i1.Mock implements _i5.AuthProvider {
MockAuthProvider() {
_i1.throwOnMissingStub(this);
}
···
as bool);
@override
-
_i5.Future<String?> getAccessToken() =>
+
_i6.Future<String?> getAccessToken() =>
(super.noSuchMethod(
Invocation.method(#getAccessToken, []),
-
returnValue: _i5.Future<String?>.value(),
+
returnValue: _i6.Future<String?>.value(),
)
-
as _i5.Future<String?>);
+
as _i6.Future<String?>);
@override
-
_i5.Future<void> initialize() =>
+
_i6.Future<void> initialize() =>
(super.noSuchMethod(
Invocation.method(#initialize, []),
-
returnValue: _i5.Future<void>.value(),
-
returnValueForMissingStub: _i5.Future<void>.value(),
+
returnValue: _i6.Future<void>.value(),
+
returnValueForMissingStub: _i6.Future<void>.value(),
)
-
as _i5.Future<void>);
+
as _i6.Future<void>);
@override
-
_i5.Future<void> signIn(String? handle) =>
+
_i6.Future<void> signIn(String? handle) =>
(super.noSuchMethod(
Invocation.method(#signIn, [handle]),
-
returnValue: _i5.Future<void>.value(),
-
returnValueForMissingStub: _i5.Future<void>.value(),
+
returnValue: _i6.Future<void>.value(),
+
returnValueForMissingStub: _i6.Future<void>.value(),
)
-
as _i5.Future<void>);
+
as _i6.Future<void>);
@override
-
_i5.Future<void> signOut() =>
+
_i6.Future<void> signOut() =>
(super.noSuchMethod(
Invocation.method(#signOut, []),
-
returnValue: _i5.Future<void>.value(),
-
returnValueForMissingStub: _i5.Future<void>.value(),
+
returnValue: _i6.Future<void>.value(),
+
returnValueForMissingStub: _i6.Future<void>.value(),
)
-
as _i5.Future<void>);
+
as _i6.Future<void>);
@override
-
_i5.Future<bool> refreshToken() =>
+
_i6.Future<bool> refreshToken() =>
(super.noSuchMethod(
Invocation.method(#refreshToken, []),
-
returnValue: _i5.Future<bool>.value(false),
+
returnValue: _i6.Future<bool>.value(false),
)
-
as _i5.Future<bool>);
+
as _i6.Future<bool>);
@override
void clearError() => super.noSuchMethod(
···
);
@override
-
void addListener(_i6.VoidCallback? listener) => super.noSuchMethod(
+
void addListener(_i7.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
-
void removeListener(_i6.VoidCallback? listener) => super.noSuchMethod(
+
void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
···
/// A class which mocks [CovesApiService].
///
/// See the documentation for Mockito's code generation for more information.
-
class MockCovesApiService extends _i1.Mock implements _i7.CovesApiService {
+
class MockCovesApiService extends _i1.Mock implements _i8.CovesApiService {
MockCovesApiService() {
_i1.throwOnMissingStub(this);
}
@override
-
_i5.Future<_i2.TimelineResponse> getTimeline({
+
_i6.Future<_i2.TimelineResponse> getTimeline({
String? sort = 'hot',
String? timeframe,
int? limit = 15,
···
#limit: limit,
#cursor: cursor,
}),
-
returnValue: _i5.Future<_i2.TimelineResponse>.value(
+
returnValue: _i6.Future<_i2.TimelineResponse>.value(
_FakeTimelineResponse_0(
this,
Invocation.method(#getTimeline, [], {
···
),
),
)
-
as _i5.Future<_i2.TimelineResponse>);
+
as _i6.Future<_i2.TimelineResponse>);
@override
-
_i5.Future<_i2.TimelineResponse> getDiscover({
+
_i6.Future<_i2.TimelineResponse> getDiscover({
String? sort = 'hot',
String? timeframe,
int? limit = 15,
···
#limit: limit,
#cursor: cursor,
}),
-
returnValue: _i5.Future<_i2.TimelineResponse>.value(
+
returnValue: _i6.Future<_i2.TimelineResponse>.value(
_FakeTimelineResponse_0(
this,
Invocation.method(#getDiscover, [], {
···
),
),
)
-
as _i5.Future<_i2.TimelineResponse>);
+
as _i6.Future<_i2.TimelineResponse>);
@override
-
_i5.Future<_i3.CommentsResponse> getComments({
+
_i6.Future<_i3.CommentsResponse> getComments({
required String? postUri,
String? sort = 'hot',
String? timeframe,
···
#limit: limit,
#cursor: cursor,
}),
-
returnValue: _i5.Future<_i3.CommentsResponse>.value(
+
returnValue: _i6.Future<_i3.CommentsResponse>.value(
_FakeCommentsResponse_1(
this,
Invocation.method(#getComments, [], {
···
),
),
)
-
as _i5.Future<_i3.CommentsResponse>);
+
as _i6.Future<_i3.CommentsResponse>);
@override
void dispose() => super.noSuchMethod(
···
/// A class which mocks [VoteProvider].
///
/// See the documentation for Mockito's code generation for more information.
-
class MockVoteProvider extends _i1.Mock implements _i8.VoteProvider {
+
class MockVoteProvider extends _i1.Mock implements _i9.VoteProvider {
MockVoteProvider() {
_i1.throwOnMissingStub(this);
}
···
);
@override
-
_i8.VoteState? getVoteState(String? postUri) =>
+
_i9.VoteState? getVoteState(String? postUri) =>
(super.noSuchMethod(Invocation.method(#getVoteState, [postUri]))
-
as _i8.VoteState?);
+
as _i9.VoteState?);
@override
bool isLiked(String? postUri) =>
···
as int);
@override
-
_i5.Future<bool> toggleVote({
+
_i6.Future<bool> toggleVote({
required String? postUri,
required String? postCid,
String? direction = 'up',
···
#postCid: postCid,
#direction: direction,
}),
-
returnValue: _i5.Future<bool>.value(false),
+
returnValue: _i6.Future<bool>.value(false),
)
-
as _i5.Future<bool>);
+
as _i6.Future<bool>);
@override
void setInitialVoteState({
···
);
@override
-
void addListener(_i6.VoidCallback? listener) => super.noSuchMethod(
+
void addListener(_i7.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null,
);
@override
-
void removeListener(_i6.VoidCallback? listener) => super.noSuchMethod(
+
void removeListener(_i7.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null,
);
···
returnValueForMissingStub: null,
);
}
+
+
/// A class which mocks [CommentService].
+
///
+
/// See the documentation for Mockito's code generation for more information.
+
class MockCommentService extends _i1.Mock implements _i4.CommentService {
+
MockCommentService() {
+
_i1.throwOnMissingStub(this);
+
}
+
+
@override
+
_i6.Future<_i4.CreateCommentResponse> createComment({
+
required String? rootUri,
+
required String? rootCid,
+
required String? parentUri,
+
required String? parentCid,
+
required String? content,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(#createComment, [], {
+
#rootUri: rootUri,
+
#rootCid: rootCid,
+
#parentUri: parentUri,
+
#parentCid: parentCid,
+
#content: content,
+
}),
+
returnValue: _i6.Future<_i4.CreateCommentResponse>.value(
+
_FakeCreateCommentResponse_2(
+
this,
+
Invocation.method(#createComment, [], {
+
#rootUri: rootUri,
+
#rootCid: rootCid,
+
#parentUri: parentUri,
+
#parentCid: parentCid,
+
#content: content,
+
}),
+
),
+
),
+
)
+
as _i6.Future<_i4.CreateCommentResponse>);
+
}
+357
test/services/comment_service_test.dart
···
+
import 'package:coves_flutter/models/coves_session.dart';
+
import 'package:coves_flutter/services/api_exceptions.dart';
+
import 'package:coves_flutter/services/comment_service.dart';
+
import 'package:dio/dio.dart';
+
import 'package:flutter_test/flutter_test.dart';
+
import 'package:mockito/annotations.dart';
+
import 'package:mockito/mockito.dart';
+
+
import 'comment_service_test.mocks.dart';
+
+
@GenerateMocks([Dio])
+
void main() {
+
group('CommentService', () {
+
group('CreateCommentResponse', () {
+
test('should create response with uri and cid', () {
+
const response = CreateCommentResponse(
+
uri: 'at://did:plc:test/social.coves.community.comment/123',
+
cid: 'bafy123',
+
);
+
+
expect(
+
response.uri,
+
'at://did:plc:test/social.coves.community.comment/123',
+
);
+
expect(response.cid, 'bafy123');
+
});
+
});
+
+
group('createComment', () {
+
late MockDio mockDio;
+
late CommentService commentService;
+
late CovesSession testSession;
+
+
setUp(() {
+
mockDio = MockDio();
+
testSession = CovesSession(
+
token: 'test-token',
+
did: 'did:plc:test',
+
sessionId: 'test-session-id',
+
handle: 'test.user',
+
);
+
+
// Setup default interceptors behavior
+
when(mockDio.interceptors).thenReturn(Interceptors());
+
+
commentService = CommentService(
+
sessionGetter: () async => testSession,
+
tokenRefresher: () async => true,
+
signOutHandler: () async {},
+
dio: mockDio,
+
);
+
});
+
+
test('should create comment successfully', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenAnswer(
+
(_) async => Response(
+
requestOptions: RequestOptions(path: ''),
+
statusCode: 200,
+
data: {
+
'uri': 'at://did:plc:test/social.coves.community.comment/abc123',
+
'cid': 'bafy123',
+
},
+
),
+
);
+
+
final response = await commentService.createComment(
+
rootUri: 'at://did:plc:author/social.coves.post.record/post123',
+
rootCid: 'rootCid123',
+
parentUri: 'at://did:plc:author/social.coves.post.record/post123',
+
parentCid: 'parentCid123',
+
content: 'This is a test comment',
+
);
+
+
expect(
+
response.uri,
+
'at://did:plc:test/social.coves.community.comment/abc123',
+
);
+
expect(response.cid, 'bafy123');
+
+
verify(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: {
+
'reply': {
+
'root': {
+
'uri': 'at://did:plc:author/social.coves.post.record/post123',
+
'cid': 'rootCid123',
+
},
+
'parent': {
+
'uri': 'at://did:plc:author/social.coves.post.record/post123',
+
'cid': 'parentCid123',
+
},
+
},
+
'content': 'This is a test comment',
+
},
+
),
+
).called(1);
+
});
+
+
test('should throw AuthenticationException when no session', () async {
+
final serviceWithoutSession = CommentService(
+
sessionGetter: () async => null,
+
tokenRefresher: () async => true,
+
signOutHandler: () async {},
+
dio: mockDio,
+
);
+
+
expect(
+
() => serviceWithoutSession.createComment(
+
rootUri: 'at://did:plc:author/post/123',
+
rootCid: 'rootCid',
+
parentUri: 'at://did:plc:author/post/123',
+
parentCid: 'parentCid',
+
content: 'Test comment',
+
),
+
throwsA(isA<AuthenticationException>()),
+
);
+
});
+
+
test('should throw ApiException on network error', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenThrow(
+
DioException(
+
requestOptions: RequestOptions(path: ''),
+
type: DioExceptionType.connectionError,
+
message: 'Connection failed',
+
),
+
);
+
+
expect(
+
() => commentService.createComment(
+
rootUri: 'at://did:plc:author/post/123',
+
rootCid: 'rootCid',
+
parentUri: 'at://did:plc:author/post/123',
+
parentCid: 'parentCid',
+
content: 'Test comment',
+
),
+
throwsA(isA<ApiException>()),
+
);
+
});
+
+
test('should throw AuthenticationException on 401 response', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenThrow(
+
DioException(
+
requestOptions: RequestOptions(path: ''),
+
type: DioExceptionType.badResponse,
+
response: Response(
+
requestOptions: RequestOptions(path: ''),
+
statusCode: 401,
+
data: {'error': 'Unauthorized'},
+
),
+
),
+
);
+
+
expect(
+
() => commentService.createComment(
+
rootUri: 'at://did:plc:author/post/123',
+
rootCid: 'rootCid',
+
parentUri: 'at://did:plc:author/post/123',
+
parentCid: 'parentCid',
+
content: 'Test comment',
+
),
+
throwsA(isA<AuthenticationException>()),
+
);
+
});
+
+
test('should throw ApiException on invalid response (null data)', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenAnswer(
+
(_) async => Response(
+
requestOptions: RequestOptions(path: ''),
+
statusCode: 200,
+
data: null,
+
),
+
);
+
+
expect(
+
() => commentService.createComment(
+
rootUri: 'at://did:plc:author/post/123',
+
rootCid: 'rootCid',
+
parentUri: 'at://did:plc:author/post/123',
+
parentCid: 'parentCid',
+
content: 'Test comment',
+
),
+
throwsA(
+
isA<ApiException>().having(
+
(e) => e.message,
+
'message',
+
contains('no data'),
+
),
+
),
+
);
+
});
+
+
test('should throw ApiException on invalid response (missing uri)', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenAnswer(
+
(_) async => Response(
+
requestOptions: RequestOptions(path: ''),
+
statusCode: 200,
+
data: {'cid': 'bafy123'},
+
),
+
);
+
+
expect(
+
() => commentService.createComment(
+
rootUri: 'at://did:plc:author/post/123',
+
rootCid: 'rootCid',
+
parentUri: 'at://did:plc:author/post/123',
+
parentCid: 'parentCid',
+
content: 'Test comment',
+
),
+
throwsA(
+
isA<ApiException>().having(
+
(e) => e.message,
+
'message',
+
contains('missing uri'),
+
),
+
),
+
);
+
});
+
+
test('should throw ApiException on invalid response (empty uri)', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenAnswer(
+
(_) async => Response(
+
requestOptions: RequestOptions(path: ''),
+
statusCode: 200,
+
data: {'uri': '', 'cid': 'bafy123'},
+
),
+
);
+
+
expect(
+
() => commentService.createComment(
+
rootUri: 'at://did:plc:author/post/123',
+
rootCid: 'rootCid',
+
parentUri: 'at://did:plc:author/post/123',
+
parentCid: 'parentCid',
+
content: 'Test comment',
+
),
+
throwsA(
+
isA<ApiException>().having(
+
(e) => e.message,
+
'message',
+
contains('missing uri'),
+
),
+
),
+
);
+
});
+
+
test('should throw ApiException on server error', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenThrow(
+
DioException(
+
requestOptions: RequestOptions(path: ''),
+
type: DioExceptionType.badResponse,
+
response: Response(
+
requestOptions: RequestOptions(path: ''),
+
statusCode: 500,
+
data: {'error': 'Internal server error'},
+
),
+
message: 'Internal server error',
+
),
+
);
+
+
expect(
+
() => commentService.createComment(
+
rootUri: 'at://did:plc:author/post/123',
+
rootCid: 'rootCid',
+
parentUri: 'at://did:plc:author/post/123',
+
parentCid: 'parentCid',
+
content: 'Test comment',
+
),
+
throwsA(isA<ApiException>()),
+
);
+
});
+
+
test('should send correct parent for nested reply', () async {
+
when(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: anyNamed('data'),
+
),
+
).thenAnswer(
+
(_) async => Response(
+
requestOptions: RequestOptions(path: ''),
+
statusCode: 200,
+
data: {
+
'uri': 'at://did:plc:test/social.coves.community.comment/reply1',
+
'cid': 'bafyReply',
+
},
+
),
+
);
+
+
await commentService.createComment(
+
rootUri: 'at://did:plc:author/social.coves.post.record/post123',
+
rootCid: 'postCid',
+
parentUri:
+
'at://did:plc:commenter/social.coves.community.comment/comment1',
+
parentCid: 'commentCid',
+
content: 'This is a nested reply',
+
);
+
+
verify(
+
mockDio.post<Map<String, dynamic>>(
+
'/xrpc/social.coves.community.comment.create',
+
data: {
+
'reply': {
+
'root': {
+
'uri': 'at://did:plc:author/social.coves.post.record/post123',
+
'cid': 'postCid',
+
},
+
'parent': {
+
'uri':
+
'at://did:plc:commenter/social.coves.community.comment/'
+
'comment1',
+
'cid': 'commentCid',
+
},
+
},
+
'content': 'This is a nested reply',
+
},
+
),
+
).called(1);
+
});
+
});
+
});
+
}
+806
test/services/comment_service_test.mocks.dart
···
+
// Mocks generated by Mockito 5.4.6 from annotations
+
// in coves_flutter/test/services/comment_service_test.dart.
+
// Do not manually edit this file.
+
+
// ignore_for_file: no_leading_underscores_for_library_prefixes
+
import 'dart:async' as _i8;
+
+
import 'package:dio/src/adapter.dart' as _i4;
+
import 'package:dio/src/cancel_token.dart' as _i9;
+
import 'package:dio/src/dio.dart' as _i7;
+
import 'package:dio/src/dio_mixin.dart' as _i3;
+
import 'package:dio/src/options.dart' as _i2;
+
import 'package:dio/src/response.dart' as _i6;
+
import 'package:dio/src/transformer.dart' as _i5;
+
import 'package:mockito/mockito.dart' as _i1;
+
+
// ignore_for_file: type=lint
+
// ignore_for_file: avoid_redundant_argument_values
+
// ignore_for_file: avoid_setters_without_getters
+
// ignore_for_file: comment_references
+
// ignore_for_file: deprecated_member_use
+
// ignore_for_file: deprecated_member_use_from_same_package
+
// ignore_for_file: implementation_imports
+
// ignore_for_file: invalid_use_of_visible_for_testing_member
+
// ignore_for_file: must_be_immutable
+
// ignore_for_file: prefer_const_constructors
+
// ignore_for_file: unnecessary_parenthesis
+
// ignore_for_file: camel_case_types
+
// ignore_for_file: subtype_of_sealed_class
+
// ignore_for_file: invalid_use_of_internal_member
+
+
class _FakeBaseOptions_0 extends _i1.SmartFake implements _i2.BaseOptions {
+
_FakeBaseOptions_0(Object parent, Invocation parentInvocation)
+
: super(parent, parentInvocation);
+
}
+
+
class _FakeInterceptors_1 extends _i1.SmartFake implements _i3.Interceptors {
+
_FakeInterceptors_1(Object parent, Invocation parentInvocation)
+
: super(parent, parentInvocation);
+
}
+
+
class _FakeHttpClientAdapter_2 extends _i1.SmartFake
+
implements _i4.HttpClientAdapter {
+
_FakeHttpClientAdapter_2(Object parent, Invocation parentInvocation)
+
: super(parent, parentInvocation);
+
}
+
+
class _FakeTransformer_3 extends _i1.SmartFake implements _i5.Transformer {
+
_FakeTransformer_3(Object parent, Invocation parentInvocation)
+
: super(parent, parentInvocation);
+
}
+
+
class _FakeResponse_4<T1> extends _i1.SmartFake implements _i6.Response<T1> {
+
_FakeResponse_4(Object parent, Invocation parentInvocation)
+
: super(parent, parentInvocation);
+
}
+
+
class _FakeDio_5 extends _i1.SmartFake implements _i7.Dio {
+
_FakeDio_5(Object parent, Invocation parentInvocation)
+
: super(parent, parentInvocation);
+
}
+
+
/// A class which mocks [Dio].
+
///
+
/// See the documentation for Mockito's code generation for more information.
+
class MockDio extends _i1.Mock implements _i7.Dio {
+
MockDio() {
+
_i1.throwOnMissingStub(this);
+
}
+
+
@override
+
_i2.BaseOptions get options =>
+
(super.noSuchMethod(
+
Invocation.getter(#options),
+
returnValue: _FakeBaseOptions_0(this, Invocation.getter(#options)),
+
)
+
as _i2.BaseOptions);
+
+
@override
+
_i3.Interceptors get interceptors =>
+
(super.noSuchMethod(
+
Invocation.getter(#interceptors),
+
returnValue: _FakeInterceptors_1(
+
this,
+
Invocation.getter(#interceptors),
+
),
+
)
+
as _i3.Interceptors);
+
+
@override
+
_i4.HttpClientAdapter get httpClientAdapter =>
+
(super.noSuchMethod(
+
Invocation.getter(#httpClientAdapter),
+
returnValue: _FakeHttpClientAdapter_2(
+
this,
+
Invocation.getter(#httpClientAdapter),
+
),
+
)
+
as _i4.HttpClientAdapter);
+
+
@override
+
_i5.Transformer get transformer =>
+
(super.noSuchMethod(
+
Invocation.getter(#transformer),
+
returnValue: _FakeTransformer_3(
+
this,
+
Invocation.getter(#transformer),
+
),
+
)
+
as _i5.Transformer);
+
+
@override
+
set options(_i2.BaseOptions? value) => super.noSuchMethod(
+
Invocation.setter(#options, value),
+
returnValueForMissingStub: null,
+
);
+
+
@override
+
set httpClientAdapter(_i4.HttpClientAdapter? value) => super.noSuchMethod(
+
Invocation.setter(#httpClientAdapter, value),
+
returnValueForMissingStub: null,
+
);
+
+
@override
+
set transformer(_i5.Transformer? value) => super.noSuchMethod(
+
Invocation.setter(#transformer, value),
+
returnValueForMissingStub: null,
+
);
+
+
@override
+
void close({bool? force = false}) => super.noSuchMethod(
+
Invocation.method(#close, [], {#force: force}),
+
returnValueForMissingStub: null,
+
);
+
+
@override
+
_i8.Future<_i6.Response<T>> head<T>(
+
String? path, {
+
Object? data,
+
Map<String, dynamic>? queryParameters,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#head,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#head,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> headUri<T>(
+
Uri? uri, {
+
Object? data,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#headUri,
+
[uri],
+
{#data: data, #options: options, #cancelToken: cancelToken},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#headUri,
+
[uri],
+
{#data: data, #options: options, #cancelToken: cancelToken},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> get<T>(
+
String? path, {
+
Object? data,
+
Map<String, dynamic>? queryParameters,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#get,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#get,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> getUri<T>(
+
Uri? uri, {
+
Object? data,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#getUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#getUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> post<T>(
+
String? path, {
+
Object? data,
+
Map<String, dynamic>? queryParameters,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#post,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#post,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> postUri<T>(
+
Uri? uri, {
+
Object? data,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#postUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#postUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> put<T>(
+
String? path, {
+
Object? data,
+
Map<String, dynamic>? queryParameters,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#put,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#put,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> putUri<T>(
+
Uri? uri, {
+
Object? data,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#putUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#putUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> patch<T>(
+
String? path, {
+
Object? data,
+
Map<String, dynamic>? queryParameters,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#patch,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#patch,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> patchUri<T>(
+
Uri? uri, {
+
Object? data,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#patchUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#patchUri,
+
[uri],
+
{
+
#data: data,
+
#options: options,
+
#cancelToken: cancelToken,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> delete<T>(
+
String? path, {
+
Object? data,
+
Map<String, dynamic>? queryParameters,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#delete,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#delete,
+
[path],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#options: options,
+
#cancelToken: cancelToken,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> deleteUri<T>(
+
Uri? uri, {
+
Object? data,
+
_i2.Options? options,
+
_i9.CancelToken? cancelToken,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#deleteUri,
+
[uri],
+
{#data: data, #options: options, #cancelToken: cancelToken},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#deleteUri,
+
[uri],
+
{#data: data, #options: options, #cancelToken: cancelToken},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<dynamic>> download(
+
String? urlPath,
+
dynamic savePath, {
+
_i2.ProgressCallback? onReceiveProgress,
+
Map<String, dynamic>? queryParameters,
+
_i9.CancelToken? cancelToken,
+
bool? deleteOnError = true,
+
_i2.FileAccessMode? fileAccessMode = _i2.FileAccessMode.write,
+
String? lengthHeader = 'content-length',
+
Object? data,
+
_i2.Options? options,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#download,
+
[urlPath, savePath],
+
{
+
#onReceiveProgress: onReceiveProgress,
+
#queryParameters: queryParameters,
+
#cancelToken: cancelToken,
+
#deleteOnError: deleteOnError,
+
#fileAccessMode: fileAccessMode,
+
#lengthHeader: lengthHeader,
+
#data: data,
+
#options: options,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<dynamic>>.value(
+
_FakeResponse_4<dynamic>(
+
this,
+
Invocation.method(
+
#download,
+
[urlPath, savePath],
+
{
+
#onReceiveProgress: onReceiveProgress,
+
#queryParameters: queryParameters,
+
#cancelToken: cancelToken,
+
#deleteOnError: deleteOnError,
+
#fileAccessMode: fileAccessMode,
+
#lengthHeader: lengthHeader,
+
#data: data,
+
#options: options,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<dynamic>>);
+
+
@override
+
_i8.Future<_i6.Response<dynamic>> downloadUri(
+
Uri? uri,
+
dynamic savePath, {
+
_i2.ProgressCallback? onReceiveProgress,
+
_i9.CancelToken? cancelToken,
+
bool? deleteOnError = true,
+
_i2.FileAccessMode? fileAccessMode = _i2.FileAccessMode.write,
+
String? lengthHeader = 'content-length',
+
Object? data,
+
_i2.Options? options,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#downloadUri,
+
[uri, savePath],
+
{
+
#onReceiveProgress: onReceiveProgress,
+
#cancelToken: cancelToken,
+
#deleteOnError: deleteOnError,
+
#fileAccessMode: fileAccessMode,
+
#lengthHeader: lengthHeader,
+
#data: data,
+
#options: options,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<dynamic>>.value(
+
_FakeResponse_4<dynamic>(
+
this,
+
Invocation.method(
+
#downloadUri,
+
[uri, savePath],
+
{
+
#onReceiveProgress: onReceiveProgress,
+
#cancelToken: cancelToken,
+
#deleteOnError: deleteOnError,
+
#fileAccessMode: fileAccessMode,
+
#lengthHeader: lengthHeader,
+
#data: data,
+
#options: options,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<dynamic>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> request<T>(
+
String? url, {
+
Object? data,
+
Map<String, dynamic>? queryParameters,
+
_i9.CancelToken? cancelToken,
+
_i2.Options? options,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#request,
+
[url],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#cancelToken: cancelToken,
+
#options: options,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#request,
+
[url],
+
{
+
#data: data,
+
#queryParameters: queryParameters,
+
#cancelToken: cancelToken,
+
#options: options,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> requestUri<T>(
+
Uri? uri, {
+
Object? data,
+
_i9.CancelToken? cancelToken,
+
_i2.Options? options,
+
_i2.ProgressCallback? onSendProgress,
+
_i2.ProgressCallback? onReceiveProgress,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(
+
#requestUri,
+
[uri],
+
{
+
#data: data,
+
#cancelToken: cancelToken,
+
#options: options,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(
+
#requestUri,
+
[uri],
+
{
+
#data: data,
+
#cancelToken: cancelToken,
+
#options: options,
+
#onSendProgress: onSendProgress,
+
#onReceiveProgress: onReceiveProgress,
+
},
+
),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i8.Future<_i6.Response<T>> fetch<T>(_i2.RequestOptions? requestOptions) =>
+
(super.noSuchMethod(
+
Invocation.method(#fetch, [requestOptions]),
+
returnValue: _i8.Future<_i6.Response<T>>.value(
+
_FakeResponse_4<T>(
+
this,
+
Invocation.method(#fetch, [requestOptions]),
+
),
+
),
+
)
+
as _i8.Future<_i6.Response<T>>);
+
+
@override
+
_i7.Dio clone({
+
_i2.BaseOptions? options,
+
_i3.Interceptors? interceptors,
+
_i4.HttpClientAdapter? httpClientAdapter,
+
_i5.Transformer? transformer,
+
}) =>
+
(super.noSuchMethod(
+
Invocation.method(#clone, [], {
+
#options: options,
+
#interceptors: interceptors,
+
#httpClientAdapter: httpClientAdapter,
+
#transformer: transformer,
+
}),
+
returnValue: _FakeDio_5(
+
this,
+
Invocation.method(#clone, [], {
+
#options: options,
+
#interceptors: interceptors,
+
#httpClientAdapter: httpClientAdapter,
+
#transformer: transformer,
+
}),
+
),
+
)
+
as _i7.Dio);
+
}