test: move syncEmails checkpoint test to integration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Thomas Güttler
2026-04-21 08:15:02 +02:00
co-authored by Claude Sonnet 4.6
parent 6a457a9f7a
commit 0da88bbc4b
3 changed files with 23 additions and 22 deletions
+4
View File
@@ -1,5 +1,9 @@
# Later
Create a re-usable JMAP package.
---
done?
think about that: Maybe we should not mock jmap/imap/smtp. We have a temproary Stalwart.
@@ -6,11 +6,13 @@
// STALWART_SMTP_HOST, STALWART_SMTP_PORT
// STALWART_USER_B / STALWART_PASS_B (alice@localhost)
import 'dart:convert';
import 'dart:io';
import 'package:enough_mail/enough_mail.dart';
import 'package:sharedinbox/core/models/account.dart';
import 'package:sharedinbox/core/models/email.dart';
import 'package:sharedinbox/data/db/database.dart' hide Account;
import 'package:sharedinbox/data/repositories/account_repository_impl.dart';
import 'package:sharedinbox/data/repositories/email_repository_impl.dart';
import 'package:test/test.dart';
@@ -131,7 +133,8 @@ void main() {
return client;
}
({AccountRepositoryImpl accounts, EmailRepositoryImpl emails}) makeRepo() {
({AppDatabase db, AccountRepositoryImpl accounts, EmailRepositoryImpl emails})
makeRepo() {
final db = openTestDatabase();
final storage = MapSecureStorage();
final accounts = AccountRepositoryImpl(db, storage);
@@ -142,7 +145,7 @@ void main() {
smtpConnect: testSmtpConnect,
getCacheDir: () async => cacheDir,
);
return (accounts: accounts, emails: emails);
return (db: db, accounts: accounts, emails: emails);
}
Future<void> appendToInbox(String subject, {String body = 'Body'}) async {
@@ -181,6 +184,20 @@ void main() {
expect(emails.first.isSeen, isFalse);
});
test('syncEmails saves IMAP checkpoint after full sync', () async {
await appendToInbox('checkpoint-test');
final r = makeRepo();
await r.accounts.addAccount(account, userPass);
await r.emails.syncEmails('test', 'INBOX');
final states = await r.db.select(r.db.syncStates).get();
expect(states, hasLength(1));
final checkpoint = jsonDecode(states.first.state) as Map<String, dynamic>;
expect(checkpoint['uidValidity'], isA<int>());
expect((checkpoint['lastUid'] as int), greaterThan(0));
});
test('getEmailBody fetches body from IMAP and caches it', () async {
await appendToInbox('body-test', body: 'Hello from IMAP body');
-20
View File
@@ -481,26 +481,6 @@ void main() {
expect(await r.emails.getEmail('acc-1:5'), isNull);
});
test('syncEmails saves IMAP checkpoint after full sync', () async {
final r = _makeReposWithFakes();
await r.accounts.addAccount(_account, 'pw');
r.fakeImap.uidValidityResult = 1000;
// Full sync now uses UID SEARCH ALL then UID FETCH.
r.fakeImap.searchUids = [10, 20];
r.fakeImap.uidFetchResults = [
buildEnvelopeMessage(uid: 10, subject: 'First'),
buildEnvelopeMessage(uid: 20, subject: 'Second'),
];
await r.emails.syncEmails('acc-1', 'INBOX');
final states = await r.db.select(r.db.syncStates).get();
expect(states, hasLength(1));
final checkpoint = jsonDecode(states.first.state) as Map<String, dynamic>;
expect(checkpoint['uidValidity'], 1000);
expect(checkpoint['lastUid'], 20);
});
test(
'syncEmails incremental sync fetches only messages newer than checkpoint',
() async {