TF-3719 Write unit test for _updateEmailCache in ThreadRepositoryImplement and getChanges in ThreadAPI
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -6,14 +6,19 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/filter/filter.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/state.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/unsigned_int.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/query/query_email_response.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:model/extensions/account_id_extensions.dart';
|
||||
import 'package:model/extensions/email_id_extensions.dart';
|
||||
import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart';
|
||||
import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/search_email.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart';
|
||||
@@ -49,12 +54,17 @@ class MockQueryEmailResponse extends QueryEmailResponse {
|
||||
void main() {
|
||||
final baseOption = BaseOptions(method: 'POST');
|
||||
final dio = Dio(baseOption)..options.baseUrl = 'http://domain.com/jmap';
|
||||
final dioAdapterHeaders = <String, dynamic>{
|
||||
'accept': 'application/json;jmapVersion=rfc-8621',
|
||||
};
|
||||
final dioAdapter = DioAdapter(dio: dio);
|
||||
final httpClient = HttpClient(dio);
|
||||
final threadApi = ThreadAPI(httpClient);
|
||||
|
||||
final sessionState = State('some-session-state');
|
||||
final state = State('some-state');
|
||||
final sinceState = State('since-state');
|
||||
final newState = State('new-state');
|
||||
final filter = EmailFilterCondition(text: 'some-text');
|
||||
final queryState = State('some-query-state');
|
||||
|
||||
@@ -423,5 +433,347 @@ void main() {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('getChanges:', () {
|
||||
int emailSize = 10;
|
||||
|
||||
final createdEmailIds = List.generate(
|
||||
emailSize,
|
||||
(index) => EmailId(Id('created_$index')),
|
||||
);
|
||||
|
||||
final updatedEmailIds = List.generate(
|
||||
emailSize,
|
||||
(index) => EmailId(Id('updated_$index')),
|
||||
);
|
||||
|
||||
final destroyedEmailIds = List.generate(
|
||||
emailSize,
|
||||
(index) => EmailId(Id('destroyed_$index')),
|
||||
);
|
||||
|
||||
final createdEmails = List.generate(
|
||||
emailSize,
|
||||
(index) => Email(
|
||||
id: EmailId(Id('created_$index')),
|
||||
subject: 'Subject $index',
|
||||
preview: 'Preview $index',
|
||||
),
|
||||
);
|
||||
|
||||
final updatedEmails = List.generate(
|
||||
emailSize,
|
||||
(index) => Email(
|
||||
id: EmailId(Id('updated_$index')),
|
||||
keywords: {
|
||||
KeyWordIdentifier.emailSeen: true,
|
||||
},
|
||||
mailboxIds: {
|
||||
MailboxId(Id('inbox-id')): true,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final defaultCreatedProperties = Properties({
|
||||
'subject',
|
||||
'preview',
|
||||
});
|
||||
|
||||
final defaultUpdatedProperties = Properties({
|
||||
'mailboxIds',
|
||||
'keywords',
|
||||
});
|
||||
|
||||
Map<String, dynamic> generateRequest({
|
||||
Properties? propertiesCreated,
|
||||
Properties? propertiesUpdated,
|
||||
}) =>
|
||||
{
|
||||
"using": [
|
||||
"urn:ietf:params:jmap:core",
|
||||
"urn:ietf:params:jmap:mail",
|
||||
"urn:apache:james:params:jmap:mail:shares"
|
||||
],
|
||||
"methodCalls": [
|
||||
[
|
||||
"Email/changes",
|
||||
{
|
||||
"accountId": AccountFixtures.aliceAccountId.asString,
|
||||
"sinceState": sinceState.value,
|
||||
},
|
||||
"c0"
|
||||
],
|
||||
if (propertiesUpdated != null)
|
||||
[
|
||||
"Email/get",
|
||||
{
|
||||
"accountId": AccountFixtures.aliceAccountId.asString,
|
||||
"#ids": {
|
||||
"resultOf": "c0",
|
||||
"name": "Email/changes",
|
||||
"path": "/updated/*"
|
||||
},
|
||||
"properties": propertiesUpdated.value.toList()
|
||||
},
|
||||
"c1"
|
||||
],
|
||||
if (propertiesCreated != null)
|
||||
[
|
||||
"Email/get",
|
||||
{
|
||||
"accountId": AccountFixtures.aliceAccountId.asString,
|
||||
"#ids": {
|
||||
"resultOf": "c0",
|
||||
"name": "Email/changes",
|
||||
"path": "/created/*"
|
||||
},
|
||||
"properties": propertiesCreated.value.toList()
|
||||
},
|
||||
if (propertiesUpdated != null) "c2" else "c1"
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
Map<String, dynamic> generateResponse({
|
||||
Properties? propertiesCreated,
|
||||
Properties? propertiesUpdated,
|
||||
bool existNotFoundCreatedEmails = false,
|
||||
bool existNotFoundUpdatedEmails = false,
|
||||
}) =>
|
||||
{
|
||||
"sessionState": sessionState.value,
|
||||
"methodResponses": [
|
||||
[
|
||||
"Email/changes",
|
||||
{
|
||||
"accountId": AccountFixtures.aliceAccountId.asString,
|
||||
"oldState": sinceState.value,
|
||||
"newState": newState.value,
|
||||
"hasMoreChanges": false,
|
||||
"created": createdEmailIds
|
||||
.map((emailId) => emailId.id.value)
|
||||
.toList(),
|
||||
"updated": updatedEmailIds
|
||||
.map((emailId) => emailId.id.value)
|
||||
.toList(),
|
||||
"destroyed": destroyedEmailIds
|
||||
.map((emailId) => emailId.id.value)
|
||||
.toList(),
|
||||
},
|
||||
"c0"
|
||||
],
|
||||
if (propertiesUpdated != null)
|
||||
[
|
||||
"Email/get",
|
||||
{
|
||||
"accountId": AccountFixtures.aliceAccountId.asString,
|
||||
"state": newState.value,
|
||||
"list": existNotFoundUpdatedEmails
|
||||
? updatedEmails.map((email) => email.toJson()).toList().sublist(0, 5)
|
||||
: updatedEmails.map((email) => email.toJson()).toList(),
|
||||
"notFound": existNotFoundUpdatedEmails
|
||||
? updatedEmailIds.map((emailId) => emailId.id.value).toList().sublist(5, 10)
|
||||
: [],
|
||||
},
|
||||
"c1"
|
||||
],
|
||||
if (propertiesCreated != null)
|
||||
[
|
||||
"Email/get",
|
||||
{
|
||||
"accountId": AccountFixtures.aliceAccountId.asString,
|
||||
"state": newState.value,
|
||||
"list": existNotFoundUpdatedEmails
|
||||
? createdEmails.map((email) => email.toJson()).toList().sublist(0, 5)
|
||||
: createdEmails.map((email) => email.toJson()).toList(),
|
||||
"notFound": existNotFoundUpdatedEmails
|
||||
? createdEmailIds.map((emailId) => emailId.id.value).toList().sublist(5, 10)
|
||||
: [],
|
||||
},
|
||||
if (propertiesUpdated != null) "c2" else "c1"
|
||||
]
|
||||
]
|
||||
};
|
||||
|
||||
test(
|
||||
'should return created, updated, destroyed emails '
|
||||
'when has both created and updated properties '
|
||||
'and response Email/get has list', () async {
|
||||
// arrange
|
||||
dioAdapter.onPost(
|
||||
'',
|
||||
(server) => server.reply(
|
||||
200,
|
||||
generateResponse(
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
propertiesUpdated: defaultUpdatedProperties,
|
||||
),
|
||||
),
|
||||
data: generateRequest(
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
propertiesUpdated: defaultUpdatedProperties,
|
||||
),
|
||||
headers: dioAdapterHeaders..addAll({'content-length': 1245}),
|
||||
);
|
||||
|
||||
// act
|
||||
final result = await threadApi.getChanges(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
sinceState,
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
propertiesUpdated: defaultUpdatedProperties,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(
|
||||
result,
|
||||
equals(
|
||||
EmailChangeResponse(
|
||||
updated: updatedEmails,
|
||||
created: createdEmails,
|
||||
destroyed: destroyedEmailIds,
|
||||
newStateChanges: newState,
|
||||
newStateEmail: newState,
|
||||
hasMoreChanges: false,
|
||||
updatedProperties: defaultUpdatedProperties,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return created, updated, destroyed emails '
|
||||
'when has both created and updated properties '
|
||||
'and response Email/get has list and not found emails', () async {
|
||||
// arrange
|
||||
dioAdapter.onPost(
|
||||
'',
|
||||
(server) => server.reply(
|
||||
200,
|
||||
generateResponse(
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
propertiesUpdated: defaultUpdatedProperties,
|
||||
existNotFoundCreatedEmails: true,
|
||||
existNotFoundUpdatedEmails: true,
|
||||
),
|
||||
),
|
||||
data: generateRequest(
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
propertiesUpdated: defaultUpdatedProperties,
|
||||
),
|
||||
headers: dioAdapterHeaders..addAll({'content-length': 1245}),
|
||||
);
|
||||
|
||||
// act
|
||||
final result = await threadApi.getChanges(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
sinceState,
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
propertiesUpdated: defaultUpdatedProperties,
|
||||
);
|
||||
|
||||
// assert
|
||||
final newDestroyedEmailIds = destroyedEmailIds
|
||||
+ updatedEmailIds.sublist(5, 10)
|
||||
+ createdEmailIds.sublist(5, 10);
|
||||
|
||||
final newUpdatedEmails = updatedEmails.sublist(0, 5);
|
||||
final newCreatedEmails = createdEmails.sublist(0, 5);
|
||||
|
||||
expect(
|
||||
result,
|
||||
equals(
|
||||
EmailChangeResponse(
|
||||
updated: newUpdatedEmails,
|
||||
created: newCreatedEmails,
|
||||
destroyed: newDestroyedEmailIds,
|
||||
newStateChanges: newState,
|
||||
newStateEmail: newState,
|
||||
hasMoreChanges: false,
|
||||
updatedProperties: defaultUpdatedProperties,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return created, destroyed emails '
|
||||
'when only has created properties '
|
||||
'and response Email/get has list', () async {
|
||||
// arrange
|
||||
dioAdapter.onPost(
|
||||
'',
|
||||
(server) => server.reply(
|
||||
200,
|
||||
generateResponse(
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
),
|
||||
),
|
||||
data: generateRequest(
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
),
|
||||
headers: dioAdapterHeaders..addAll({'content-length': 798}),
|
||||
);
|
||||
|
||||
// act
|
||||
final result = await threadApi.getChanges(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
sinceState,
|
||||
propertiesCreated: defaultCreatedProperties,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(
|
||||
result,
|
||||
equals(
|
||||
EmailChangeResponse(
|
||||
created: createdEmails,
|
||||
destroyed: destroyedEmailIds,
|
||||
newStateChanges: newState,
|
||||
newStateEmail: newState,
|
||||
hasMoreChanges: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'should only return destroyed emails '
|
||||
'when both created, updated properties are absent '
|
||||
'and response Email/changes has destroyed list', () async {
|
||||
// arrange
|
||||
dioAdapter.onPost(
|
||||
'',
|
||||
(server) => server.reply(
|
||||
200,
|
||||
generateResponse(),
|
||||
),
|
||||
data: generateRequest(),
|
||||
headers: dioAdapterHeaders..addAll({'content-length': 355}),
|
||||
);
|
||||
|
||||
// act
|
||||
final result = await threadApi.getChanges(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
sinceState,
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(
|
||||
result,
|
||||
equals(
|
||||
EmailChangeResponse(
|
||||
destroyed: destroyedEmailIds,
|
||||
newStateChanges: newState,
|
||||
hasMoreChanges: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart';
|
||||
|
||||
import '../../../../fixtures/account_fixtures.dart';
|
||||
import '../../../../fixtures/session_fixtures.dart';
|
||||
@@ -531,4 +532,480 @@ void main() {
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
group('The functions regard to updateEmailCache:', () {
|
||||
test(
|
||||
'when local cache is empty should fetch from network '
|
||||
'and network has mail not found '
|
||||
'should delete mail not found in cache', () async {
|
||||
// Arrange
|
||||
when(threadDataSource.getAllEmailCache(
|
||||
any,
|
||||
any,
|
||||
filterOption: anyNamed('filterOption'),
|
||||
inMailboxId: anyNamed('inMailboxId'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
)).thenAnswer((_) => Future.value([]));
|
||||
|
||||
when(stateDataSource.getState(any, any, any))
|
||||
.thenAnswer((_) => Future.value(null));
|
||||
|
||||
final networkEmails = List.generate(
|
||||
ThreadConstants.defaultLimit.value.toInt(),
|
||||
(index) => Email(id: EmailId(Id('network_$index'))),
|
||||
);
|
||||
|
||||
final networkNotFoundEmailIds = List.generate(
|
||||
5,
|
||||
(index) => EmailId(Id('network_not_found_$index')),
|
||||
);
|
||||
|
||||
when(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
position: anyNamed('position'),
|
||||
filter: anyNamed('filter'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
properties: anyNamed('properties'),
|
||||
)).thenAnswer((_) => Future.value(EmailsResponse(
|
||||
emailList: networkEmails,
|
||||
notFoundEmailIds: networkNotFoundEmailIds,
|
||||
state: State('network_state'),
|
||||
)));
|
||||
|
||||
// Act
|
||||
final responses = await threadRepository
|
||||
.getAllEmail(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
)
|
||||
.toList();
|
||||
|
||||
// Assert
|
||||
expect(responses.length, 2);
|
||||
expect(responses[0].emailList, networkEmails);
|
||||
expect(responses[0].notFoundEmailIds, networkNotFoundEmailIds);
|
||||
verify(threadDataSource.update(
|
||||
any,
|
||||
any,
|
||||
created: networkEmails,
|
||||
destroyed: networkNotFoundEmailIds,
|
||||
));
|
||||
verify(stateDataSource.saveState(
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
));
|
||||
});
|
||||
|
||||
test(
|
||||
'when filter option is not "all" '
|
||||
'and no need getLatestChanges '
|
||||
'then fetch first page of all messages for this folder '
|
||||
'and network has mail not found '
|
||||
'should delete mail not found in cache', () async {
|
||||
// Arrange
|
||||
final mailboxId = MailboxId(Id('mailbox_id'));
|
||||
final localEmails = List.generate(
|
||||
10,
|
||||
(index) => Email(id: EmailId(Id('local_$index'))),
|
||||
);
|
||||
when(threadDataSource.getAllEmailCache(
|
||||
any,
|
||||
any,
|
||||
filterOption: anyNamed('filterOption'),
|
||||
inMailboxId: anyNamed('inMailboxId'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
)).thenAnswer((_) => Future.value(localEmails));
|
||||
|
||||
when(stateDataSource.getState(any, any, any))
|
||||
.thenAnswer((_) => Future.value(State('local_state')));
|
||||
|
||||
final networkEmails = List.generate(
|
||||
ThreadConstants.defaultLimit.value.toInt(),
|
||||
(index) => Email(id: EmailId(Id('network_$index'))),
|
||||
);
|
||||
|
||||
final networkNotFoundEmailIds = List.generate(
|
||||
5,
|
||||
(index) => EmailId(Id('network_not_found_$index')),
|
||||
);
|
||||
|
||||
when(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
limit: anyNamed('limit'),
|
||||
position: anyNamed('position'),
|
||||
sort: anyNamed('sort'),
|
||||
filter: anyNamed('filter'),
|
||||
properties: anyNamed('properties'),
|
||||
)).thenAnswer((_) => Future.value(EmailsResponse(
|
||||
emailList: networkEmails,
|
||||
notFoundEmailIds: networkNotFoundEmailIds,
|
||||
state: State('network_state'),
|
||||
)));
|
||||
|
||||
// Act
|
||||
final responses = await threadRepository
|
||||
.getAllEmail(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
emailFilter: EmailFilter(
|
||||
filterOption: FilterMessageOption.unread,
|
||||
mailboxId: mailboxId,
|
||||
),
|
||||
getLatestChanges: false,
|
||||
)
|
||||
.toList();
|
||||
|
||||
// Assert
|
||||
expect(responses.length, 2);
|
||||
verify(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
limit: anyNamed('limit'),
|
||||
filter: anyNamed('filter'),
|
||||
)).called(2); // Once for initial fetch, once for first page
|
||||
verify(threadDataSource.update(
|
||||
any,
|
||||
any,
|
||||
created: networkEmails,
|
||||
destroyed: networkNotFoundEmailIds,
|
||||
)).called(2);
|
||||
verifyNever(stateDataSource.saveState(
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
));
|
||||
});
|
||||
|
||||
test(
|
||||
'when local has mail in cache and getLatestChanges is true '
|
||||
'and email change has destroyed mail '
|
||||
'should delete destroyed mail in cache', () async {
|
||||
// Arrange
|
||||
final localEmails = List.generate(
|
||||
ThreadConstants.defaultLimit.value.toInt(),
|
||||
(index) => Email(id: EmailId(Id('local_$index'))),
|
||||
);
|
||||
when(threadDataSource.getAllEmailCache(
|
||||
any,
|
||||
any,
|
||||
filterOption: anyNamed('filterOption'),
|
||||
inMailboxId: anyNamed('inMailboxId'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
)).thenAnswer((_) => Future.value(localEmails));
|
||||
|
||||
when(stateDataSource.getState(any, any, any))
|
||||
.thenAnswer((_) => Future.value(State('local_state')));
|
||||
|
||||
final changedEmails = List.generate(
|
||||
5,
|
||||
(index) => Email(id: EmailId(Id('changed_$index'))),
|
||||
);
|
||||
|
||||
final destroyedEmailIds = List.generate(
|
||||
5,
|
||||
(index) => EmailId(Id('destroyed_mail_$index')),
|
||||
);
|
||||
when(threadDataSource.getChanges(
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
propertiesCreated: anyNamed('propertiesCreated'),
|
||||
propertiesUpdated: anyNamed('propertiesUpdated'),
|
||||
)).thenAnswer((_) => Future.value(EmailChangeResponse(
|
||||
hasMoreChanges: false,
|
||||
created: changedEmails,
|
||||
destroyed: destroyedEmailIds,
|
||||
newStateEmail: State('new_state'))));
|
||||
|
||||
// Act
|
||||
final responses = await threadRepository
|
||||
.getAllEmail(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
getLatestChanges: true,
|
||||
)
|
||||
.toList();
|
||||
|
||||
// Assert
|
||||
expect(responses.length, 2);
|
||||
verifyNever(threadDataSource.getAllEmail(any, any));
|
||||
verify(threadDataSource.getChanges(
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
propertiesCreated: anyNamed('propertiesCreated'),
|
||||
propertiesUpdated: anyNamed('propertiesUpdated'),
|
||||
));
|
||||
verify(threadDataSource.update(
|
||||
any,
|
||||
any,
|
||||
created: changedEmails,
|
||||
destroyed: destroyedEmailIds,
|
||||
));
|
||||
verify(stateDataSource.saveState(any, any, any));
|
||||
});
|
||||
|
||||
test(
|
||||
'when getChanges has more changes should fetch all changes '
|
||||
'and first email change has destroyed mail '
|
||||
'should delete destroyed mail in cache', () async {
|
||||
// Arrange
|
||||
final localEmails = List.generate(
|
||||
ThreadConstants.defaultLimit.value.toInt(),
|
||||
(index) => Email(id: EmailId(Id('local_$index'))),
|
||||
);
|
||||
when(threadDataSource.getAllEmailCache(
|
||||
any,
|
||||
any,
|
||||
filterOption: anyNamed('filterOption'),
|
||||
inMailboxId: anyNamed('inMailboxId'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
)).thenAnswer((_) => Future.value(localEmails));
|
||||
|
||||
when(stateDataSource.getState(any, any, any))
|
||||
.thenAnswer((_) => Future.value(State('local_state')));
|
||||
|
||||
final firstChanges = List.generate(
|
||||
5,
|
||||
(index) => Email(id: EmailId(Id('change1_$index'))),
|
||||
);
|
||||
final secondChanges = List.generate(
|
||||
5,
|
||||
(index) => Email(id: EmailId(Id('change2_$index'))),
|
||||
);
|
||||
|
||||
var callCount = 0;
|
||||
|
||||
final firstDestroyedEmailIds = List.generate(
|
||||
5,
|
||||
(index) => EmailId(Id('destroyed_mail_$index')),
|
||||
);
|
||||
|
||||
when(threadDataSource.getChanges(
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
propertiesCreated: anyNamed('propertiesCreated'),
|
||||
propertiesUpdated: anyNamed('propertiesUpdated'),
|
||||
)).thenAnswer((_) {
|
||||
callCount++;
|
||||
if (callCount == 1) {
|
||||
return Future.value(EmailChangeResponse(
|
||||
hasMoreChanges: true,
|
||||
created: firstChanges,
|
||||
destroyed: firstDestroyedEmailIds,
|
||||
newStateChanges: State('intermediate_state'),
|
||||
newStateEmail: State('intermediate_state_email'),
|
||||
));
|
||||
} else {
|
||||
return Future.value(EmailChangeResponse(
|
||||
hasMoreChanges: false,
|
||||
created: secondChanges,
|
||||
newStateChanges: State('final_state'),
|
||||
newStateEmail: State('final_state_email'),
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
final responses = await threadRepository
|
||||
.getAllEmail(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
getLatestChanges: true,
|
||||
)
|
||||
.toList();
|
||||
|
||||
// Assert
|
||||
expect(responses.length, 2);
|
||||
verify(threadDataSource.getChanges(
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
propertiesCreated: anyNamed('propertiesCreated'),
|
||||
propertiesUpdated: anyNamed('propertiesUpdated'),
|
||||
)).called(2);
|
||||
verify(threadDataSource.update(
|
||||
any,
|
||||
any,
|
||||
created: anyNamed('created'),
|
||||
destroyed: anyNamed('destroyed'),
|
||||
));
|
||||
verify(stateDataSource.saveState(any, any, any));
|
||||
});
|
||||
|
||||
test(
|
||||
'when local has mail in cache but network errors '
|
||||
'and return email from local '
|
||||
'should not call update email cache', () async {
|
||||
// Arrange
|
||||
final localEmails = List.generate(
|
||||
ThreadConstants.defaultLimit.value.toInt(),
|
||||
(index) => Email(id: EmailId(Id('local_$index'))),
|
||||
);
|
||||
when(threadDataSource.getAllEmailCache(
|
||||
any,
|
||||
any,
|
||||
filterOption: anyNamed('filterOption'),
|
||||
inMailboxId: anyNamed('inMailboxId'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
)).thenAnswer((_) => Future.value(localEmails));
|
||||
|
||||
when(stateDataSource.getState(any, any, any))
|
||||
.thenAnswer((_) => Future.value(State('local_state')));
|
||||
|
||||
when(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
limit: anyNamed('limit'),
|
||||
position: anyNamed('position'),
|
||||
sort: anyNamed('sort'),
|
||||
filter: anyNamed('filter'),
|
||||
properties: anyNamed('properties'),
|
||||
)).thenThrow(Exception('Network error'));
|
||||
|
||||
// Act
|
||||
final responses = await threadRepository
|
||||
.getAllEmail(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
getLatestChanges: false,
|
||||
)
|
||||
.toList();
|
||||
|
||||
// Assert
|
||||
expect(responses.length, 2);
|
||||
expect(responses[0].emailList, localEmails);
|
||||
verifyNever(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
limit: anyNamed('limit'),
|
||||
position: anyNamed('position'),
|
||||
sort: anyNamed('sort'),
|
||||
filter: anyNamed('filter'),
|
||||
properties: anyNamed('properties'),
|
||||
));
|
||||
verifyNever(threadDataSource.update(
|
||||
any,
|
||||
any,
|
||||
created: anyNamed('created'),
|
||||
updated: anyNamed('updated'),
|
||||
destroyed: anyNamed('created'),
|
||||
));
|
||||
verifyNever(stateDataSource.saveState(
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
));
|
||||
});
|
||||
|
||||
test(
|
||||
'when fetching more emails '
|
||||
'and network has mails and not found mails '
|
||||
'should delete mail not found in cache', () async {
|
||||
// Arrange
|
||||
final networkEmails = List.generate(
|
||||
ThreadConstants.defaultLimit.value.toInt(),
|
||||
(index) => Email(id: EmailId(Id('network_$index'))),
|
||||
);
|
||||
|
||||
final networkNotFoundEmailIds = List.generate(
|
||||
5,
|
||||
(index) => EmailId(Id('network_not_found_$index')),
|
||||
);
|
||||
|
||||
when(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
position: anyNamed('position'),
|
||||
filter: anyNamed('filter'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
properties: anyNamed('properties'),
|
||||
)).thenAnswer((_) => Future.value(EmailsResponse(
|
||||
emailList: networkEmails,
|
||||
notFoundEmailIds: networkNotFoundEmailIds,
|
||||
state: State('network_state'),
|
||||
)));
|
||||
|
||||
// Act
|
||||
final responses = await threadRepository
|
||||
.loadMoreEmails(GetEmailRequest(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
))
|
||||
.toList();
|
||||
|
||||
// Assert
|
||||
expect(responses.length, 1);
|
||||
expect(responses[0].emailList, networkEmails);
|
||||
expect(responses[0].notFoundEmailIds, networkNotFoundEmailIds);
|
||||
verify(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
limit: anyNamed('limit'),
|
||||
position: anyNamed('position'),
|
||||
sort: anyNamed('sort'),
|
||||
filter: anyNamed('filter'),
|
||||
properties: anyNamed('properties'),
|
||||
));
|
||||
verify(threadDataSource.update(
|
||||
any,
|
||||
any,
|
||||
created: networkEmails,
|
||||
destroyed: networkNotFoundEmailIds,
|
||||
));
|
||||
});
|
||||
|
||||
test(
|
||||
'when fetching more emails and network errors '
|
||||
'should not call update mail cache', () async {
|
||||
// Arrange
|
||||
when(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
position: anyNamed('position'),
|
||||
filter: anyNamed('filter'),
|
||||
limit: anyNamed('limit'),
|
||||
sort: anyNamed('sort'),
|
||||
properties: anyNamed('properties'),
|
||||
)).thenThrow(Exception('Network error'));
|
||||
|
||||
// Assert
|
||||
expectLater(
|
||||
() => threadRepository
|
||||
.loadMoreEmails(GetEmailRequest(
|
||||
SessionFixtures.aliceSession,
|
||||
AccountFixtures.aliceAccountId,
|
||||
))
|
||||
.toList(),
|
||||
throwsA(isA<Exception>().having((e) => e.toString(), 'description', contains('Network error'))),
|
||||
);
|
||||
verifyNever(threadDataSource.getAllEmail(
|
||||
any,
|
||||
any,
|
||||
limit: anyNamed('limit'),
|
||||
position: anyNamed('position'),
|
||||
sort: anyNamed('sort'),
|
||||
filter: anyNamed('filter'),
|
||||
properties: anyNamed('properties'),
|
||||
));
|
||||
verifyNever(threadDataSource.update(
|
||||
any,
|
||||
any,
|
||||
created: anyNamed('created'),
|
||||
updated: anyNamed('updated'),
|
||||
destroyed: anyNamed('created'),
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user