- Added UndoService with 10-action history stack. - Integrated Undo Snackbar into EmailListScreen and EmailDetailScreen. - Added EmailRepository.cancelPendingChange to optimize undo by removing unsynced local mutations. - Fixed sorting bug in compareMailboxes for unknown roles. - Increased unit coverage to 83% with new model and utility tests. - Verified with full test suite (task check).
110 lines
3.2 KiB
Dart
110 lines
3.2 KiB
Dart
import 'package:drift/drift.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:mockito/annotations.dart';
|
|
import 'package:sharedinbox/core/storage/secure_storage.dart';
|
|
import 'package:sharedinbox/data/db/database.dart';
|
|
import 'package:sharedinbox/data/repositories/account_repository_impl.dart';
|
|
import 'package:sharedinbox/data/repositories/email_repository_impl.dart';
|
|
|
|
import 'db_test_helper.dart';
|
|
import 'email_repository_cancel_change_test.mocks.dart';
|
|
|
|
@GenerateMocks([http.Client, SecureStorage])
|
|
void main() {
|
|
late AppDatabase db;
|
|
late EmailRepositoryImpl repo;
|
|
late MockClient mockHttpClient;
|
|
late MockSecureStorage mockStorage;
|
|
|
|
setUpAll(() {
|
|
configureSqliteForTests();
|
|
});
|
|
|
|
setUp(() async {
|
|
db = openTestDatabase();
|
|
mockHttpClient = MockClient();
|
|
mockStorage = MockSecureStorage();
|
|
final accounts = AccountRepositoryImpl(db, mockStorage);
|
|
repo = EmailRepositoryImpl(
|
|
db,
|
|
accounts,
|
|
httpClient: mockHttpClient,
|
|
);
|
|
});
|
|
|
|
tearDown(() async {
|
|
await db.close();
|
|
});
|
|
|
|
test('cancelPendingChange removes an unattempted change', () async {
|
|
await db.into(db.pendingChanges).insert(
|
|
PendingChangesCompanion.insert(
|
|
accountId: 'acc1',
|
|
resourceType: 'Email',
|
|
resourceId: 'e1',
|
|
changeType: 'move',
|
|
payload: '{}',
|
|
createdAt: DateTime.now(),
|
|
),
|
|
);
|
|
|
|
final cancelled = await repo.cancelPendingChange('e1', 'move');
|
|
expect(cancelled, isTrue);
|
|
|
|
final remaining = await db.select(db.pendingChanges).get();
|
|
expect(remaining, isEmpty);
|
|
});
|
|
|
|
test('cancelPendingChange does not remove attempted changes', () async {
|
|
await db.into(db.pendingChanges).insert(
|
|
PendingChangesCompanion.insert(
|
|
accountId: 'acc1',
|
|
resourceType: 'Email',
|
|
resourceId: 'e1',
|
|
changeType: 'move',
|
|
payload: '{}',
|
|
createdAt: DateTime.now(),
|
|
attempts: const Value(1),
|
|
),
|
|
);
|
|
|
|
final cancelled = await repo.cancelPendingChange('e1', 'move');
|
|
expect(cancelled, isFalse);
|
|
|
|
final remaining = await db.select(db.pendingChanges).get();
|
|
expect(remaining, hasLength(1));
|
|
});
|
|
|
|
test('cancelPendingChange only removes the latest matching change', () async {
|
|
final now = DateTime.now();
|
|
await db.into(db.pendingChanges).insert(
|
|
PendingChangesCompanion.insert(
|
|
accountId: 'acc1',
|
|
resourceType: 'Email',
|
|
resourceId: 'e1',
|
|
changeType: 'move',
|
|
payload: '{"id": 1}',
|
|
createdAt: now,
|
|
),
|
|
);
|
|
await db.into(db.pendingChanges).insert(
|
|
PendingChangesCompanion.insert(
|
|
accountId: 'acc1',
|
|
resourceType: 'Email',
|
|
resourceId: 'e1',
|
|
changeType: 'move',
|
|
payload: '{"id": 2}',
|
|
createdAt: now.add(const Duration(seconds: 1)),
|
|
),
|
|
);
|
|
|
|
final cancelled = await repo.cancelPendingChange('e1', 'move');
|
|
expect(cancelled, isTrue);
|
|
|
|
final remaining = await db.select(db.pendingChanges).get();
|
|
expect(remaining, hasLength(1));
|
|
expect(remaining.first.payload, '{"id": 1}');
|
|
});
|
|
}
|