diff --git a/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart b/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart index ccfc7a8d7..b68d41e88 100644 --- a/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart +++ b/lib/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart @@ -1,3 +1,4 @@ +import 'dart:collection'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; @@ -5,6 +6,8 @@ import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/extensions/mailbox_extension.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart'; @@ -14,24 +17,40 @@ class DeleteMultipleMailboxInteractor { DeleteMultipleMailboxInteractor(this._mailboxRepository); Stream> execute( - Session session, - AccountId accountId, - Map> mapMailboxIdToDelete, - List listMailboxIdToDelete + Session session, + AccountId accountId, + List selectedMailboxIds, ) async* { try { yield Right(LoadingDeleteMultipleMailboxAll()); final currentMailboxState = await _mailboxRepository.getMailboxState(session, accountId); + final mailboxResponses = await _mailboxRepository.getAllMailbox(session, accountId).toList(); + + final Set seenIds = {}; + final List allMailboxes = []; + for (final response in mailboxResponses) { + for (final mailbox in response.mailboxes) { + final presentation = mailbox.toPresentationMailbox(); + if (seenIds.add(presentation.id)) { + allMailboxes.add(presentation); + } + } + } + + final deleteMap = _buildDeleteMap(selectedMailboxIds, allMailboxes); + final mapMailboxIdToDelete = deleteMap.value1; + final listMailboxIdToDelete = deleteMap.value2; + final listResult = await Future.wait( - mapMailboxIdToDelete.keys.map((mailboxId) { - final mailboxIdsToDelete = mapMailboxIdToDelete[mailboxId]!; - return _mailboxRepository.deleteMultipleMailbox( - session, - accountId, - mailboxIdsToDelete); - }) + mapMailboxIdToDelete.keys.map((mailboxId) { + return _mailboxRepository.deleteMultipleMailbox( + session, + accountId, + mapMailboxIdToDelete[mailboxId]!, + ); + }), ); final allSuccess = listResult.every((result) => result.isEmpty); @@ -39,18 +58,85 @@ class DeleteMultipleMailboxInteractor { if (allSuccess) { yield Right(DeleteMultipleMailboxAllSuccess( - listMailboxIdToDelete, - currentMailboxState: currentMailboxState)); + listMailboxIdToDelete, + currentMailboxState: currentMailboxState, + )); } else if (allFailed) { yield Left(DeleteMultipleMailboxAllFailure()); } else { yield Right(DeleteMultipleMailboxHasSomeSuccess( - listMailboxIdToDelete, - currentMailboxState: currentMailboxState)); + listMailboxIdToDelete, + currentMailboxState: currentMailboxState, + )); } } catch (e) { logWarning('DeleteMultipleMailboxInteractor::execute(): exception: $e'); yield Left(DeleteMultipleMailboxFailure(e)); } } -} \ No newline at end of file + + /// Builds the map of [MailboxId] → ordered delete list for each selected root, + /// plus a flat list of all IDs to delete. + /// + /// Uses [allMailboxes] (subscribed and unsubscribed) so hidden subfolders are + /// automatically included — no separate unsubscribed-tree pass is needed. + Tuple2>, List> _buildDeleteMap( + List selectedMailboxIds, + List allMailboxes, + ) { + final parentToChildren = _buildParentToChildrenMap(allMailboxes); + final Map> mapDescendantIds = {}; + final Set processedIds = {}; + + for (final mailboxId in selectedMailboxIds) { + if (processedIds.contains(mailboxId)) continue; + + // Collect all descendants (pre-order DFS), then reverse so deepest children + // come first — the server requires children to be deleted before parents. + final descendants = _collectDescendantsPreOrder(mailboxId, parentToChildren); + final deleteOrder = descendants.reversed.toList(); + + mapDescendantIds[mailboxId] = deleteOrder; + processedIds.addAll(deleteOrder); + } + + final allMailboxIds = mapDescendantIds.values.expand((ids) => ids).toList(); + return Tuple2(mapDescendantIds, allMailboxIds); + } + + Map> _buildParentToChildrenMap( + List mailboxes, + ) { + final Map> parentToChildren = HashMap(); + for (final mailbox in mailboxes) { + final parentId = mailbox.parentId; + if (parentId != null) { + parentToChildren.putIfAbsent(parentId, () => []).add(mailbox.id); + } + } + return parentToChildren; + } + + /// Pre-order DFS: root first, then children. Caller should reverse the result + /// to get deepest-first deletion order. + List _collectDescendantsPreOrder( + MailboxId rootId, + Map> parentToChildren, + ) { + final result = []; + final stack = ListQueue()..addLast(rootId); + + while (stack.isNotEmpty) { + final current = stack.removeLast(); + result.add(current); + final children = parentToChildren[current]; + if (children != null) { + // Push in reverse so the first child is processed first (stack is LIFO). + for (final child in children.reversed) { + stack.addLast(child); + } + } + } + return result; + } +} diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 7ffbad243..11dfc1e39 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -79,7 +79,6 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.d import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/open_mailbox_view_event.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_action_reactor.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart'; @@ -565,7 +564,7 @@ class MailboxController extends BaseMailboxController consumeState(Stream.value(Left(GetAllMailboxFailure(NotFoundSessionException())))); } } - + void _refreshMailboxChanges({required jmap.State newState}) { log('MailboxController::_refreshMailboxChanges():newState: $newState'); if (accountId == null || @@ -1039,18 +1038,10 @@ class MailboxController extends BaseMailboxController void _deleteMailboxAction(PresentationMailbox presentationMailbox) { if (session != null && accountId != null) { - final tupleMap = MailboxUtils.generateMapDescendantIdsAndMailboxIdList( - [presentationMailbox], - defaultMailboxTree.value, - personalMailboxTree.value); - final mapDescendantIds = tupleMap.value1; - final listMailboxId = tupleMap.value2; - consumeState(_deleteMultipleMailboxInteractor.execute( session!, accountId!, - mapDescendantIds, - listMailboxId, + [presentationMailbox.id], )); } else { _deleteMailboxFailure(DeleteMultipleMailboxFailure(null)); diff --git a/lib/features/mailbox/presentation/utils/mailbox_utils.dart b/lib/features/mailbox/presentation/utils/mailbox_utils.dart index 776fac198..108c3ec48 100644 --- a/lib/features/mailbox/presentation/utils/mailbox_utils.dart +++ b/lib/features/mailbox/presentation/utils/mailbox_utils.dart @@ -1,60 +1,9 @@ -import 'package:core/presentation/utils/responsive_utils.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; import 'package:email_recovery/email_recovery/capability_deleted_messages_vault.dart'; -import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; -import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/extensions/list_mailbox_node_extension.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; class MailboxUtils { - - static Tuple2>, List> generateMapDescendantIdsAndMailboxIdList( - List selectedMailboxList, - MailboxTree defaultMailboxTree, - MailboxTree folderMailboxTree, - ) { - Map> mapDescendantIds = {}; - List allMailboxIds = []; - - for (var mailbox in selectedMailboxList) { - final currentMailboxId = mailbox.id; - - if (allMailboxIds.contains(currentMailboxId)) { - continue; - } else { - final matchedNode = defaultMailboxTree.findNode((node) => node.item.id == currentMailboxId) - ?? folderMailboxTree.findNode((node) => node.item.id == currentMailboxId); - - if (matchedNode != null) { - final descendantIds = matchedNode.descendantsAsList().mailboxIds; - final descendantIdsReversed = descendantIds.reversed.toList(); - - mapDescendantIds[currentMailboxId] = descendantIdsReversed; - allMailboxIds.addAll(descendantIdsReversed); - } - } - } - log('MailboxUtils::generateMapDescendantIdsAndMailboxIdList(): mapDescendantIds: $mapDescendantIds'); - return Tuple2(mapDescendantIds, allMailboxIds); - } - - static EdgeInsets getPaddingListViewMailboxSearched(BuildContext context, ResponsiveUtils responsiveUtils) { - if (responsiveUtils.isWebDesktop(context)) { - return const EdgeInsets.only(left: 16, right: 16, bottom: 16); - } else { - if (responsiveUtils.isScreenWithShortestSide(context)) { - return const EdgeInsets.all(16); - } else { - return const EdgeInsets.symmetric(horizontal: 32, vertical: 16); - } - } - } - static bool isDeletedMessageVaultSupported(Session? session, AccountId? accountId) { if (session == null || accountId == null) { return false; diff --git a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart index 87ace5b8c..05689eb43 100644 --- a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart +++ b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart @@ -63,7 +63,6 @@ import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentat import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_action_reactor.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart'; @@ -584,19 +583,10 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa void _deleteMailboxAction(PresentationMailbox presentationMailbox) { if (session != null && accountId != null) { - final tupleMap = MailboxUtils.generateMapDescendantIdsAndMailboxIdList( - [presentationMailbox], - defaultMailboxTree.value, - personalMailboxTree.value - ); - final mapDescendantIds = tupleMap.value1; - final listMailboxId = tupleMap.value2; - consumeState(_deleteMultipleMailboxInteractor.execute( session!, accountId!, - mapDescendantIds, - listMailboxId + [presentationMailbox.id], )); } else { _deleteMailboxFailure(DeleteMultipleMailboxFailure(null)); diff --git a/test/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor_test.dart b/test/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor_test.dart new file mode 100644 index 000000000..360848f0e --- /dev/null +++ b/test/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor_test.dart @@ -0,0 +1,277 @@ +import 'package:dartz/dartz.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jmap_dart_client/jmap/core/error/error_type.dart'; +import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/jmap_mailbox_response.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; +import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart'; +import '../../../../fixtures/account_fixtures.dart'; +import '../../../../fixtures/mailbox_fixtures.dart'; +import '../../../../fixtures/session_fixtures.dart'; +import 'delete_multiple_mailbox_interactor_test.mocks.dart'; + +@GenerateMocks([MailboxRepository]) + +void _stubGetMailboxState(MailboxRepository repo, jmap.State state) { + when( + repo.getMailboxState(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId), + ).thenAnswer((_) => Future.value(state)); +} + +void _stubGetAllMailboxWithHiddenMailboxes(MailboxRepository repo, jmap.State state) { + when( + repo.getAllMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId), + ).thenAnswer((_) => Stream.fromIterable({ + JmapMailboxResponse( + mailboxes: [ + MailboxFixtures.inboxMailbox, + MailboxFixtures.sentMailbox, + MailboxFixtures.selectedFolderToDelete, + MailboxFixtures.selectedFolderToDelete_1, + MailboxFixtures.selectedFolderToDelete_2, + ], + state: state, + ), + JmapMailboxResponse( + mailboxes: [ + MailboxFixtures.inboxMailbox, + MailboxFixtures.sentMailbox, + MailboxFixtures.selectedFolderToDelete, + MailboxFixtures.selectedFolderToDelete_1, + MailboxFixtures.selectedFolderToDelete_2, + MailboxFixtures.selectedFolderToDelete_3, + MailboxFixtures.selectedFolderToDelete_4, + MailboxFixtures.selectedFolderToDelete_5, + MailboxFixtures.selectedFolderToDelete_6, + MailboxFixtures.selectedFolderToDelete_7, + MailboxFixtures.selectedFolderToDelete_8, + MailboxFixtures.selectedFolderToDelete_9, + MailboxFixtures.selectedFolderToDelete_10, + ], + state: state, + ), + })); +} + +void _stubGetAllMailboxWithSubscribedFolders(MailboxRepository repo, jmap.State state) { + when( + repo.getAllMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId), + ).thenAnswer((_) => Stream.value(JmapMailboxResponse( + mailboxes: [MailboxFixtures.subscribedFolder, MailboxFixtures.subscribedChild], + state: state, + ))); +} + +void _stubDeleteMailboxSuccess(MailboxRepository repo, List ids) { + when( + repo.deleteMultipleMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId, ids), + ).thenAnswer((_) => Future.value({})); +} + +void _stubDeleteMailboxFailure( + MailboxRepository repo, + List ids, + Map errors, +) { + when( + repo.deleteMultipleMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId, ids), + ).thenAnswer((_) => Future.value(errors)); +} + +Stream _executeDelete( + DeleteMultipleMailboxInteractor interactor, + List ids, +) => interactor.execute(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId, ids); + +Matcher _emitsLoadingThen(dynamic result) => emitsInOrder([ + Right(LoadingDeleteMultipleMailboxAll()), + result, +]); + +void main() { + late MailboxRepository mailboxRepository; + late DeleteMultipleMailboxInteractor deleteMultipleMailboxInteractor; + + group('[DeleteMultipleMailboxInteractor]', () { + setUp(() { + mailboxRepository = MockMailboxRepository(); + deleteMultipleMailboxInteractor = DeleteMultipleMailboxInteractor(mailboxRepository); + }); + + group('with hidden mailbox responses', () { + final state = jmap.State('s1'); + + setUp(() { + _stubGetMailboxState(mailboxRepository, state); + _stubGetAllMailboxWithHiddenMailboxes(mailboxRepository, state); + // second batch defaults to success; override per-test when needed + _stubDeleteMailboxSuccess(mailboxRepository, MailboxFixtures.listDescendantMailboxForSelectedFolder2); + }); + + test('Should execute to delete selected mailbox an all its children included hidden mailbox', () { + _stubDeleteMailboxSuccess(mailboxRepository, MailboxFixtures.listDescendantMailboxForSelectedFolder); + + expect( + _executeDelete(deleteMultipleMailboxInteractor, MailboxFixtures.listMailboxIdsToDelete), + _emitsLoadingThen(Right(DeleteMultipleMailboxAllSuccess( + MailboxFixtures.listMailboxIdToDelete, + currentMailboxState: state, + ))), + ); + }); + + test('Should execute and yield DeleteMultipleMailboxAllFailure when delete selected mailbox all fail', () { + _stubDeleteMailboxFailure( + mailboxRepository, + MailboxFixtures.listDescendantMailboxForSelectedFolder, + {Id('folderToDelete'): SetError(ErrorType('error'))}, + ); + _stubDeleteMailboxFailure( + mailboxRepository, + MailboxFixtures.listDescendantMailboxForSelectedFolder2, + {Id('folderToDelete_8'): SetError(ErrorType('error'))}, + ); + + expect( + _executeDelete(deleteMultipleMailboxInteractor, MailboxFixtures.listMailboxIdsToDelete), + _emitsLoadingThen(Left(DeleteMultipleMailboxAllFailure())), + ); + }); + + test('Should execute and yield DeleteMultipleMailboxHasSomeSuccess when delete selected mailbox has some fail', () { + _stubDeleteMailboxFailure( + mailboxRepository, + MailboxFixtures.listDescendantMailboxForSelectedFolder, + {Id('folderToDelete'): SetError(ErrorType('error'))}, + ); + + expect( + _executeDelete(deleteMultipleMailboxInteractor, MailboxFixtures.listMailboxIdsToDelete), + _emitsLoadingThen(Right(DeleteMultipleMailboxHasSomeSuccess( + MailboxFixtures.listMailboxIdToDelete, + currentMailboxState: state, + ))), + ); + }); + }); + + test('Should yield AllSuccess with empty lists when selectedMailboxIds is empty', () { + final state = jmap.State('s1'); + _stubGetMailboxState(mailboxRepository, state); + when( + mailboxRepository.getAllMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId), + ).thenAnswer((_) => Stream.value(JmapMailboxResponse( + mailboxes: [MailboxFixtures.inboxMailbox, MailboxFixtures.sentMailbox], + state: state, + ))); + + expect( + _executeDelete(deleteMultipleMailboxInteractor, []), + _emitsLoadingThen(Right(DeleteMultipleMailboxAllSuccess( + [], + currentMailboxState: state, + ))), + ); + }); + + test('Should execute and yield DeleteMultipleMailboxFailure when getAllMailbox fail', () { + final state = jmap.State('s1'); + _stubGetMailboxState(mailboxRepository, state); + when( + mailboxRepository.getAllMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId), + ).thenThrow('error'); + + expect( + _executeDelete(deleteMultipleMailboxInteractor, MailboxFixtures.listMailboxIdsToDelete), + _emitsLoadingThen(Left(DeleteMultipleMailboxFailure('error'))), + ); + }); + + test('Should delete selected mailbox alone when it is absent from allMailboxes (no children found)', () { + final state = jmap.State('s1'); + final ghostMailboxId = MailboxId(Id('ghostMailbox')); + _stubGetMailboxState(mailboxRepository, state); + when( + mailboxRepository.getAllMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId), + ).thenAnswer((_) => Stream.value(JmapMailboxResponse( + mailboxes: [MailboxFixtures.inboxMailbox, MailboxFixtures.sentMailbox], + state: state, + ))); + _stubDeleteMailboxSuccess(mailboxRepository, [ghostMailboxId]); + + expect( + _executeDelete(deleteMultipleMailboxInteractor, [ghostMailboxId]), + _emitsLoadingThen(Right(DeleteMultipleMailboxAllSuccess( + [ghostMailboxId], + currentMailboxState: state, + ))), + ); + }); + + group('with subscribed folders', () { + final state = jmap.State('s1'); + + setUp(() { + _stubGetMailboxState(mailboxRepository, state); + _stubGetAllMailboxWithSubscribedFolders(mailboxRepository, state); + _stubDeleteMailboxSuccess(mailboxRepository, MailboxFixtures.expectedDeleteListSubscribedOnly); + }); + + test('Should delete subscribed child before parent when no hidden subfolders exist', () { + expect( + _executeDelete(deleteMultipleMailboxInteractor, MailboxFixtures.listMailboxIdsForSubscribedOnly), + _emitsLoadingThen(Right(DeleteMultipleMailboxAllSuccess( + MailboxFixtures.expectedDeleteListSubscribedOnly, + currentMailboxState: state, + ))), + ); + }); + + test('Should skip already-processed descendant when it also appears in selectedMailboxIds', () { + // subscribedChild is a descendant of subscribedFolder — it must be skipped + // as a root and subsumed into the parent's batch instead. + expect( + _executeDelete(deleteMultipleMailboxInteractor, [ + MailboxFixtures.subscribedFolder.id!, + MailboxFixtures.subscribedChild.id!, + ]), + _emitsLoadingThen(Right(DeleteMultipleMailboxAllSuccess( + MailboxFixtures.expectedDeleteListSubscribedOnly, + currentMailboxState: state, + ))), + ); + }); + }); + + test('Should delete 3-level unsubscribed nesting in deepest-first order', () { + final state = jmap.State('s1'); + _stubGetMailboxState(mailboxRepository, state); + when( + mailboxRepository.getAllMailbox(SessionFixtures.aliceSession, AccountFixtures.aliceAccountId), + ).thenAnswer((_) => Stream.value(JmapMailboxResponse( + mailboxes: [ + MailboxFixtures.parentFolder, + MailboxFixtures.hiddenLevel1, + MailboxFixtures.hiddenLevel2, + MailboxFixtures.hiddenLevel3, + ], + state: state, + ))); + _stubDeleteMailboxSuccess(mailboxRepository, MailboxFixtures.expectedDeleteListThreeLevelNesting); + + expect( + _executeDelete(deleteMultipleMailboxInteractor, [MailboxFixtures.parentFolder.id!]), + _emitsLoadingThen(Right(DeleteMultipleMailboxAllSuccess( + MailboxFixtures.expectedDeleteListThreeLevelNesting, + currentMailboxState: state, + ))), + ); + }); + }); +} diff --git a/test/features/mailbox/presentation/utils/generate_mapping_delete_multiple_mailbox_test.dart b/test/features/mailbox/presentation/utils/generate_mapping_delete_multiple_mailbox_test.dart deleted file mode 100644 index 00455d6c4..000000000 --- a/test/features/mailbox/presentation/utils/generate_mapping_delete_multiple_mailbox_test.dart +++ /dev/null @@ -1,282 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:jmap_dart_client/jmap/core/id.dart'; -import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart'; - -void main() { - group('generate mapping delete multiple mailbox test', () { - - final expectedMapOfTwoMailboxId = { - MailboxId(Id("A")): [ - MailboxId(Id('A_1_1')), - MailboxId(Id('A_1')), - MailboxId(Id('A_2_1')), - MailboxId(Id('A_2')), - MailboxId(Id('A')) - ], - MailboxId(Id("B_1")): [MailboxId(Id('B_1'))], - }; - - final expectedListMailboxId = [ - MailboxId(Id('A_1_1')), - MailboxId(Id('A_1')), - MailboxId(Id('A_2_1')), - MailboxId(Id('A_2')), - MailboxId(Id('A')), - MailboxId(Id('B_1')), - ]; - - final expectedMapOfOneMailboxId = { - MailboxId(Id("A")): [ - MailboxId(Id('A_1_1')), - MailboxId(Id('A_1')), - MailboxId(Id('A_2_1')), - MailboxId(Id('A_2')), - MailboxId(Id('A')) - ] - }; - - final expectedMapOfThreeMailboxId = { - MailboxId(Id("A")): [ - MailboxId(Id('A_1_1')), - MailboxId(Id('A_1')), - MailboxId(Id('A_2_1')), - MailboxId(Id('A_2')), - MailboxId(Id('A')) - ], - MailboxId(Id("B_1")): [MailboxId(Id('B_1'))], - MailboxId(Id("C_1")): [MailboxId(Id('C_1'))], - }; - - test('_generateMapDescendantIdsAndMailboxIdList should return map with 2 items when mailboxes belong to 2 different tree', () async { - final defaultMailboxTree = MailboxTree(MailboxNode(PresentationMailbox(MailboxId(Id('root'))))); - - final folderMailboxTree = MailboxTree( - MailboxNode( - PresentationMailbox(MailboxId(Id('root'))), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A_2')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_2_1')), parentId: MailboxId(Id('A_2')))) - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('A_1')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_1_1')), parentId: MailboxId(Id('A_1')))) - ] - ), - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('B')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('B_2')), parentId: MailboxId(Id('B'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('B_2_1')), parentId: MailboxId(Id('B_2')))) - ] - ), - MailboxNode(PresentationMailbox(MailboxId(Id('B_1')), parentId: MailboxId(Id('B')))), - ] - ) - ] - ) - ); - - final selectedMailboxList = [ - PresentationMailbox(MailboxId(Id("A")), parentId: null), - PresentationMailbox(MailboxId(Id('A_1')), parentId: MailboxId(Id('A'))), - PresentationMailbox(MailboxId(Id('A_2_1')), parentId: MailboxId(Id('A_2'))), - PresentationMailbox(MailboxId(Id("B_1")), parentId: MailboxId(Id('B'))) - ]; - - final tupleResult = MailboxUtils.generateMapDescendantIdsAndMailboxIdList( - selectedMailboxList, - defaultMailboxTree, - folderMailboxTree); - - expect(tupleResult.value1, equals(expectedMapOfTwoMailboxId)); - }); - - test('_generateMapDescendantIdsAndMailboxIdList should return list with 5 item when mailboxes belong to 2 different tree', () async { - final defaultMailboxTree = MailboxTree(MailboxNode(PresentationMailbox(MailboxId(Id('root'))))); - - final folderMailboxTree = MailboxTree( - MailboxNode( - PresentationMailbox(MailboxId(Id('root'))), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A_2')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_2_1')), parentId: MailboxId(Id('A_2')))) - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('A_1')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_1_1')), parentId: MailboxId(Id('A_1')))) - ] - ), - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('B')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('B_2')), parentId: MailboxId(Id('B'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('B_2_1')), parentId: MailboxId(Id('B_2')))) - ] - ), - MailboxNode(PresentationMailbox(MailboxId(Id('B_1')), parentId: MailboxId(Id('B')))), - ] - ) - ] - ) - ); - - final selectedMailboxList = [ - PresentationMailbox(MailboxId(Id("A")), parentId: null), - PresentationMailbox(MailboxId(Id('A_1')), parentId: MailboxId(Id('A'))), - PresentationMailbox(MailboxId(Id('A_2_1')), parentId: MailboxId(Id('A_2'))), - PresentationMailbox(MailboxId(Id("B_1")), parentId: MailboxId(Id('B'))) - ]; - - final tupleResult = MailboxUtils.generateMapDescendantIdsAndMailboxIdList( - selectedMailboxList, - defaultMailboxTree, - folderMailboxTree); - - expect(tupleResult.value2, equals(expectedListMailboxId)); - }); - - test('_generateMapDescendantIdsAndMailboxIdList should return map with 1 items when mailboxes belong to 2 different tree', () async { - final defaultMailboxTree = MailboxTree(MailboxNode(PresentationMailbox(MailboxId(Id('root'))))); - - final folderMailboxTree = MailboxTree( - MailboxNode( - PresentationMailbox(MailboxId(Id('root'))), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A_2')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_2_1')), parentId: MailboxId(Id('A_2')))) - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('A_1')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_1_1')), parentId: MailboxId(Id('A_1')))) - ] - ), - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('B')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('B_2')), parentId: MailboxId(Id('B'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('B_2_1')), parentId: MailboxId(Id('B_2')))) - ] - ), - MailboxNode(PresentationMailbox(MailboxId(Id('B_1')), parentId: MailboxId(Id('B')))), - ] - ) - ] - ) - ); - - final selectedMailboxList = [ - PresentationMailbox(MailboxId(Id("A")), parentId: null), - ]; - - final tupleResult = MailboxUtils.generateMapDescendantIdsAndMailboxIdList( - selectedMailboxList, - defaultMailboxTree, - folderMailboxTree); - - expect(tupleResult.value1, equals(expectedMapOfOneMailboxId)); - }); - - test('_generateMapDescendantIdsAndMailboxIdList should return map with 3 items when mailboxes belong to 2 different tree', () async { - final defaultMailboxTree = MailboxTree(MailboxNode(PresentationMailbox(MailboxId(Id('root'))))); - - final folderMailboxTree = MailboxTree( - MailboxNode( - PresentationMailbox(MailboxId(Id('root'))), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('A_2')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_2_1')), parentId: MailboxId(Id('A_2')))) - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('A_1')), parentId: MailboxId(Id('A'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('A_1_1')), parentId: MailboxId(Id('A_1')))) - ] - ), - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('B')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('B_2')), parentId: MailboxId(Id('B'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('B_2_1')), parentId: MailboxId(Id('B_2')))) - ] - ), - MailboxNode(PresentationMailbox(MailboxId(Id('B_1')), parentId: MailboxId(Id('B')))), - ] - ), - MailboxNode( - PresentationMailbox(MailboxId(Id('C')), parentId: null), - childrenItems: [ - MailboxNode( - PresentationMailbox(MailboxId(Id('C_2')), parentId: MailboxId(Id('C'))), - childrenItems: [ - MailboxNode(PresentationMailbox(MailboxId(Id('C_2_1')), parentId: MailboxId(Id('C_2')))) - ] - ), - MailboxNode(PresentationMailbox(MailboxId(Id('C_1')), parentId: MailboxId(Id('C')))), - ] - ) - ] - ) - ); - - final selectedMailboxList = [ - PresentationMailbox(MailboxId(Id("A")), parentId: null), - PresentationMailbox(MailboxId(Id("B_1")), parentId: MailboxId(Id('B'))), - PresentationMailbox(MailboxId(Id("C_1")), parentId: MailboxId(Id('C'))) - ]; - - final tupleResult = MailboxUtils.generateMapDescendantIdsAndMailboxIdList( - selectedMailboxList, - defaultMailboxTree, - folderMailboxTree); - - expect(tupleResult.value1, equals(expectedMapOfThreeMailboxId)); - }); - }); -} \ No newline at end of file diff --git a/test/fixtures/mailbox_fixtures.dart b/test/fixtures/mailbox_fixtures.dart index d596748ce..41120f21d 100644 --- a/test/fixtures/mailbox_fixtures.dart +++ b/test/fixtures/mailbox_fixtures.dart @@ -107,4 +107,407 @@ class MailboxFixtures { PresentationMailbox.actionRequiredFolder; static final currentState = State('2c9f1b12-b35a-43e6-9af2-0106fb53a943'); + + static final selectedFolderToDelete = Mailbox( + id: MailboxId(Id('folderToDelete')), + name: MailboxName('folderToDelete'), + parentId: null, + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(true) + ); + + static final selectedFolderToDelete_1 = Mailbox( + id: MailboxId(Id('folderToDelete_1')), + name: MailboxName('folderToDelete_1'), + parentId: MailboxId(Id("folderToDelete")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final selectedFolderToDelete_2 = Mailbox( + id: MailboxId(Id('folderToDelete_2')), + name: MailboxName('folderToDelete_2'), + parentId: MailboxId(Id("folderToDelete_1")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final selectedFolderToDelete_3 = Mailbox( + id: MailboxId(Id('folderToDelete_3')), + name: MailboxName('folderToDelete_3'), + parentId: MailboxId(Id("folderToDelete")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(true) + ); + + static final selectedFolderToDelete_4 = Mailbox( + id: MailboxId(Id('folderToDelete_4')), + name: MailboxName('folderToDelete_4'), + parentId: MailboxId(Id("folderToDelete_3")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final selectedFolderToDelete_5 = Mailbox( + id: MailboxId(Id('folderToDelete_5')), + name: MailboxName('folderToDelete_5'), + parentId: MailboxId(Id("folderToDelete_3")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final selectedFolderToDelete_6 = Mailbox( + id: MailboxId(Id('folderToDelete_6')), + name: MailboxName('folderToDelete_6'), + parentId: MailboxId(Id("folderToDelete_5")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final selectedFolderToDelete_7 = Mailbox( + id: MailboxId(Id('folderToDelete_7')), + name: MailboxName('folderToDelete_7'), + parentId: MailboxId(Id("folderToDelete_6")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final selectedFolderToDelete_8 = Mailbox( + id: MailboxId(Id('folderToDelete_8')), + name: MailboxName('folderToDelete_8'), + parentId: null, + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(true) + ); + + static final selectedFolderToDelete_9 = Mailbox( + id: MailboxId(Id('folderToDelete_9')), + name: MailboxName('folderToDelete_9'), + parentId: MailboxId(Id("folderToDelete_8")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final selectedFolderToDelete_10 = Mailbox( + id: MailboxId(Id('folderToDelete_10')), + name: MailboxName('folderToDelete_10'), + parentId: MailboxId(Id("folderToDelete_8")), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(123)), + unreadEmails: UnreadEmails(UnsignedInt(12)), + totalThreads: TotalThreads(UnsignedInt(123)), + unreadThreads: UnreadThreads(UnsignedInt(12)), + myRights: MailboxRights( + true, + true, + true, + true, + true, + true, + true, + true, + true + ), + isSubscribed: IsSubscribed(false) + ); + + static final listMailboxIdToDelete = [ + MailboxId(Id('folderToDelete_7')), + MailboxId(Id('folderToDelete_6')), + MailboxId(Id('folderToDelete_5')), + MailboxId(Id('folderToDelete_4')), + MailboxId(Id('folderToDelete_3')), + MailboxId(Id('folderToDelete_2')), + MailboxId(Id('folderToDelete_1')), + MailboxId(Id('folderToDelete')), + MailboxId(Id('folderToDelete_10')), + MailboxId(Id('folderToDelete_9')), + MailboxId(Id('folderToDelete_8')) + ]; + + static final listDescendantMailboxForSelectedFolder = [ + MailboxId(Id('folderToDelete_7')), + MailboxId(Id('folderToDelete_6')), + MailboxId(Id('folderToDelete_5')), + MailboxId(Id('folderToDelete_4')), + MailboxId(Id('folderToDelete_3')), + MailboxId(Id('folderToDelete_2')), + MailboxId(Id('folderToDelete_1')), + MailboxId(Id('folderToDelete')), + ]; + + static final listDescendantMailboxForSelectedFolder2 = [ + MailboxId(Id('folderToDelete_10')), + MailboxId(Id('folderToDelete_9')), + MailboxId(Id('folderToDelete_8')) + ]; + + static final listMailboxIdsToDelete = [ + selectedFolderToDelete.id!, + selectedFolderToDelete_8.id!, + ]; + + // Fixtures for "no hidden children" scenario — all mailboxes are subscribed. + static final subscribedFolder = Mailbox( + id: MailboxId(Id('subscribedFolder')), + name: MailboxName('subscribedFolder'), + parentId: null, + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(0)), + unreadEmails: UnreadEmails(UnsignedInt(0)), + totalThreads: TotalThreads(UnsignedInt(0)), + unreadThreads: UnreadThreads(UnsignedInt(0)), + myRights: MailboxRights(true, true, true, true, true, true, true, true, true), + isSubscribed: IsSubscribed(true), + ); + + static final subscribedChild = Mailbox( + id: MailboxId(Id('subscribedChild')), + name: MailboxName('subscribedChild'), + parentId: MailboxId(Id('subscribedFolder')), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(0)), + unreadEmails: UnreadEmails(UnsignedInt(0)), + totalThreads: TotalThreads(UnsignedInt(0)), + unreadThreads: UnreadThreads(UnsignedInt(0)), + myRights: MailboxRights(true, true, true, true, true, true, true, true, true), + isSubscribed: IsSubscribed(true), + ); + + static final listMailboxIdsForSubscribedOnly = [ + MailboxId(Id('subscribedFolder')), + ]; + + static final expectedDeleteListSubscribedOnly = [ + MailboxId(Id('subscribedChild')), + MailboxId(Id('subscribedFolder')), + ]; + + // Fixtures for "3-level unsubscribed nesting" scenario. + // Tree: parentFolder (subscribed) → level1 (unsubscribed) → level2 (unsubscribed) → level3 (unsubscribed) + static final parentFolder = Mailbox( + id: MailboxId(Id('parentFolder')), + name: MailboxName('parentFolder'), + parentId: null, + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(0)), + unreadEmails: UnreadEmails(UnsignedInt(0)), + totalThreads: TotalThreads(UnsignedInt(0)), + unreadThreads: UnreadThreads(UnsignedInt(0)), + myRights: MailboxRights(true, true, true, true, true, true, true, true, true), + isSubscribed: IsSubscribed(true), + ); + + static final hiddenLevel1 = Mailbox( + id: MailboxId(Id('hiddenLevel1')), + name: MailboxName('hiddenLevel1'), + parentId: MailboxId(Id('parentFolder')), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(0)), + unreadEmails: UnreadEmails(UnsignedInt(0)), + totalThreads: TotalThreads(UnsignedInt(0)), + unreadThreads: UnreadThreads(UnsignedInt(0)), + myRights: MailboxRights(true, true, true, true, true, true, true, true, true), + isSubscribed: IsSubscribed(false), + ); + + static final hiddenLevel2 = Mailbox( + id: MailboxId(Id('hiddenLevel2')), + name: MailboxName('hiddenLevel2'), + parentId: MailboxId(Id('hiddenLevel1')), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(0)), + unreadEmails: UnreadEmails(UnsignedInt(0)), + totalThreads: TotalThreads(UnsignedInt(0)), + unreadThreads: UnreadThreads(UnsignedInt(0)), + myRights: MailboxRights(true, true, true, true, true, true, true, true, true), + isSubscribed: IsSubscribed(false), + ); + + static final hiddenLevel3 = Mailbox( + id: MailboxId(Id('hiddenLevel3')), + name: MailboxName('hiddenLevel3'), + parentId: MailboxId(Id('hiddenLevel2')), + role: null, + sortOrder: SortOrder(sortValue: 1000), + totalEmails: TotalEmails(UnsignedInt(0)), + unreadEmails: UnreadEmails(UnsignedInt(0)), + totalThreads: TotalThreads(UnsignedInt(0)), + unreadThreads: UnreadThreads(UnsignedInt(0)), + myRights: MailboxRights(true, true, true, true, true, true, true, true, true), + isSubscribed: IsSubscribed(false), + ); + + static final expectedDeleteListThreeLevelNesting = [ + MailboxId(Id('hiddenLevel3')), + MailboxId(Id('hiddenLevel2')), + MailboxId(Id('hiddenLevel1')), + MailboxId(Id('parentFolder')), + ]; } \ No newline at end of file