Ignore cache if it failed and write unit test for forceEmailQuery = true

(cherry picked from commit 05496806823b6daed2b0abd889915a5fd7d5f88f)
This commit is contained in:
dab246
2025-11-27 16:48:40 +07:00
committed by Dat H. Pham
parent 0518115566
commit f89bfaca74
2 changed files with 367 additions and 15 deletions
@@ -159,8 +159,12 @@ class ThreadRepositoryImpl extends ThreadRepository {
return;
}
// Load cached emails + cached state
final cachedList = await localDataSource.getAllEmailCache(
List<Email> cachedList = const [];
jmap.State? cachedState;
// Load cached emails
try {
cachedList = await localDataSource.getAllEmailCache(
accountId,
session.username,
inMailboxId: emailFilter?.mailboxId,
@@ -168,12 +172,26 @@ class ThreadRepositoryImpl extends ThreadRepository {
limit: limit,
filterOption: emailFilter?.filterOption,
);
} catch (_) {
logError(
'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): '
'Failed to load cached emails',
);
}
final cachedState = await stateDataSource.getState(
// Load cached state
try {
cachedState = await stateDataSource.getState(
accountId,
session.username,
StateType.email,
);
} catch (_) {
logError(
'ThreadRepositoryImpl::forceQueryAllEmailsForWeb(): '
'Failed to load cached email state',
);
}
final localResponse = EmailsResponse(
emailList: cachedList,
@@ -211,7 +229,7 @@ class ThreadRepositoryImpl extends ThreadRepository {
// Combine server list + keep existing state
yield EmailsResponse(
emailList: serverResponse.emailList,
state: cachedState,
state: cachedState ?? serverResponse.state,
);
}
@@ -103,5 +103,339 @@ void main() {
)
}));
});
test('should use forceQueryAllEmailsForWeb when forceEmailQuery = true',
() async {
final sort = <EmailComparator>{
EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false),
};
final emailFilter = EmailFilter(
filter: EmailFilterCondition(
inMailbox: MailboxFixtures.inboxMailbox.id,
),
mailboxId: MailboxFixtures.inboxMailbox.id,
);
when(threadRepository.forceQueryAllEmailsForWeb(
session: SessionFixtures.aliceSession,
accountId: AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
)).thenAnswer(
(_) => Stream<EmailsResponse>.fromIterable([
EmailsResponse(
emailList: [
EmailFixtures.email1,
],
state: jmap.State('s_cached'),
),
EmailsResponse(
emailList: [
EmailFixtures.email1,
EmailFixtures.email2,
EmailFixtures.email3,
],
state: jmap.State('s_cached'),
),
]),
);
final resultStream = getEmailsInMailboxInteractor.execute(
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
forceEmailQuery: true,
);
final states = await resultStream.toList();
expect(states.length, 3);
expect(states[0], Right(GetAllEmailLoading()));
expect(
states[1],
Right(
GetAllEmailSuccess(
emailList: [
EmailFixtures.email1.toPresentationEmail(),
],
currentEmailState: jmap.State('s_cached'),
currentMailboxId: MailboxFixtures.inboxMailbox.id,
),
),
);
expect(
states[2],
Right(
GetAllEmailSuccess(
emailList: [
EmailFixtures.email1.toPresentationEmail(),
EmailFixtures.email2.toPresentationEmail(),
EmailFixtures.email3.toPresentationEmail(),
],
currentEmailState: jmap.State('s_cached'),
currentMailboxId: MailboxFixtures.inboxMailbox.id,
),
),
);
verify(threadRepository.forceQueryAllEmailsForWeb(
session: SessionFixtures.aliceSession,
accountId: AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
)).called(1);
verifyNever(threadRepository.getAllEmail(
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
limit: anyNamed('limit'),
sort: anyNamed('sort'),
emailFilter: anyNamed('emailFilter'),
propertiesCreated: anyNamed('propertiesCreated'),
propertiesUpdated: anyNamed('propertiesUpdated'),
));
});
test(
'forceEmailQuery = true, should continue when cache throws error and still return server emails',
() async {
final sort = <EmailComparator>{
EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false),
};
final emailFilter = EmailFilter(
filter: EmailFilterCondition(
inMailbox: MailboxFixtures.inboxMailbox.id,
),
mailboxId: MailboxFixtures.inboxMailbox.id,
);
when(threadRepository.forceQueryAllEmailsForWeb(
session: SessionFixtures.aliceSession,
accountId: AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
)).thenAnswer(
(_) => Stream<EmailsResponse>.fromIterable([
const EmailsResponse(
emailList: null,
state: null,
),
EmailsResponse(
emailList: [
EmailFixtures.email1,
EmailFixtures.email2,
],
state: jmap.State('server_1'),
),
]),
);
final resultStream = getEmailsInMailboxInteractor.execute(
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
forceEmailQuery: true,
);
final states = await resultStream.toList();
expect(states.length, 2 + 1);
expect(states[0], Right(GetAllEmailLoading()));
expect(
states[1],
Right(
GetAllEmailSuccess(
emailList: const [],
currentEmailState: null,
currentMailboxId: MailboxFixtures.inboxMailbox.id,
),
),
);
expect(
states[2],
Right(
GetAllEmailSuccess(
emailList: [
EmailFixtures.email1.toPresentationEmail(),
EmailFixtures.email2.toPresentationEmail(),
],
currentEmailState: jmap.State('server_1'),
currentMailboxId: MailboxFixtures.inboxMailbox.id,
),
),
);
});
test(
'forceEmailQuery = true, should emit Failure when forceQueryAllEmailsForWeb throws exception',
() async {
final sort = <EmailComparator>{
EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false),
};
final emailFilter = EmailFilter(
filter: EmailFilterCondition(
inMailbox: MailboxFixtures.inboxMailbox.id,
),
mailboxId: MailboxFixtures.inboxMailbox.id,
);
when(threadRepository.forceQueryAllEmailsForWeb(
session: SessionFixtures.aliceSession,
accountId: AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
)).thenThrow(Exception('server_failed'));
final resultStream = getEmailsInMailboxInteractor.execute(
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
forceEmailQuery: true,
);
final states = await resultStream.toList();
expect(states[0], Right(GetAllEmailLoading()));
expect(states[1].isLeft(), true);
states[1].fold(
(failure) => expect(failure, isA<GetAllEmailFailure>()),
(_) => fail('Should not produce success'),
);
});
test(
'forceEmailQuery = false, should call getAllEmail() and not call forceQueryAllEmailsForWeb()',
() async {
final sort = <EmailComparator>{
EmailComparator(EmailComparatorProperty.sentAt)..setIsAscending(false),
};
final emailFilter = EmailFilter(
filter: EmailFilterCondition(
inMailbox: MailboxFixtures.inboxMailbox.id,
),
mailboxId: MailboxFixtures.inboxMailbox.id,
);
when(threadRepository.getAllEmail(
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
getLatestChanges: true,
)).thenAnswer(
(_) => Stream<EmailsResponse>.fromIterable([
EmailsResponse(
emailList: [
EmailFixtures.email1,
],
state: jmap.State('s1'),
),
EmailsResponse(
emailList: [
EmailFixtures.email1,
EmailFixtures.email2,
EmailFixtures.email3,
],
state: jmap.State('s1'),
),
]),
);
final resultStream = getEmailsInMailboxInteractor.execute(
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
forceEmailQuery: false,
);
final states = await resultStream.toList();
expect(states.length, 3);
expect(states[0], Right(GetAllEmailLoading()));
expect(
states[1],
Right(
GetAllEmailSuccess(
emailList: [
EmailFixtures.email1.toPresentationEmail(),
],
currentEmailState: jmap.State('s1'),
currentMailboxId: MailboxFixtures.inboxMailbox.id,
),
),
);
expect(
states[2],
Right(
GetAllEmailSuccess(
emailList: [
EmailFixtures.email1.toPresentationEmail(),
EmailFixtures.email2.toPresentationEmail(),
EmailFixtures.email3.toPresentationEmail(),
],
currentEmailState: jmap.State('s1'),
currentMailboxId: MailboxFixtures.inboxMailbox.id,
),
),
);
verify(threadRepository.getAllEmail(
SessionFixtures.aliceSession,
AccountFixtures.aliceAccountId,
limit: UnsignedInt(20),
sort: sort,
emailFilter: emailFilter,
propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
getLatestChanges: true,
)).called(1);
verifyNever(threadRepository.forceQueryAllEmailsForWeb(
session: SessionFixtures.aliceSession,
accountId: AccountFixtures.aliceAccountId,
limit: anyNamed('limit'),
sort: anyNamed('sort'),
emailFilter: anyNamed('emailFilter'),
propertiesCreated: anyNamed('propertiesCreated'),
));
});
});
}