From 04462e18da0329ddde995c41fa48e9b5add10abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Thu, 12 Sep 2024 14:38:39 +0200 Subject: [PATCH 1/9] Add MarkOrderAsReadUseCaseTests template --- .../WooCommerce.xcodeproj/project.pbxproj | 4 +++ .../Model/MarkOrderAsReadUseCaseTests.swift | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index fa80e00d42c..55288c9b107 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -2433,6 +2433,7 @@ DA1D68C22C36F0980097859A /* PointOfSaleAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1D68C12C36F0980097859A /* PointOfSaleAssets.swift */; }; DA25ADDD2C86145E00AE81FE /* MarkOrderAsReadUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA25ADDC2C86145E00AE81FE /* MarkOrderAsReadUseCase.swift */; }; DA25ADDF2C87403900AE81FE /* PushNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA25ADDE2C87403900AE81FE /* PushNotificationTests.swift */; }; + DA3F99BA2C92F6D30034BDA5 /* MarkOrderAsReadUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA3F99B92C92F6D30034BDA5 /* MarkOrderAsReadUseCaseTests.swift */; }; DA41043A2C247B6900E8456A /* POSOrderPreviewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4104392C247B6900E8456A /* POSOrderPreviewService.swift */; }; DA706BF92C8063B600E08A5B /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 575472802452185300A94C3C /* PushNotification.swift */; }; DA706BFA2C80642800E08A5B /* Dictionary+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57C5C9521B80E5400FF82B2 /* Dictionary+Woo.swift */; }; @@ -5481,6 +5482,7 @@ DA1D68C12C36F0980097859A /* PointOfSaleAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointOfSaleAssets.swift; sourceTree = ""; }; DA25ADDC2C86145E00AE81FE /* MarkOrderAsReadUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkOrderAsReadUseCase.swift; sourceTree = ""; }; DA25ADDE2C87403900AE81FE /* PushNotificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationTests.swift; sourceTree = ""; }; + DA3F99B92C92F6D30034BDA5 /* MarkOrderAsReadUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkOrderAsReadUseCaseTests.swift; sourceTree = ""; }; DA4104392C247B6900E8456A /* POSOrderPreviewService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSOrderPreviewService.swift; sourceTree = ""; }; DABF35262C11B426006AF826 /* PointOfSaleDashboardViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleDashboardViewModelTests.swift; sourceTree = ""; }; DAD988C52C4A9CF9009DE9E3 /* CartItem+Order.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CartItem+Order.swift"; sourceTree = ""; }; @@ -10529,6 +10531,7 @@ 2631D4F929ED108400F13F20 /* WPComPlanNameSanitizer.swift */, B98FF43F2AAA096200326D16 /* AddressWooTests.swift */, 023BD5852BFDCECF00A10D7B /* BetaFeaturesConfigurationViewModelTests.swift */, + DA3F99B92C92F6D30034BDA5 /* MarkOrderAsReadUseCaseTests.swift */, ); path = Model; sourceTree = ""; @@ -16570,6 +16573,7 @@ EEB221A729B9B5B300662A12 /* CouponLineDetailsViewModelTests.swift in Sources */, 026D684B2A0E0A9600D8C22C /* LocalNotificationSchedulerTests.swift in Sources */, 02038C612AF222D600CD36D9 /* ConfigurableVariableBundleAttributePickerViewModelTests.swift in Sources */, + DA3F99BA2C92F6D30034BDA5 /* MarkOrderAsReadUseCaseTests.swift in Sources */, 26C0D1E32B460E5700F6EDA5 /* OrderNotificationViewModel.swift in Sources */, 86E40AED2B597DEC00990365 /* BlazeCampaignCreationCoordinatorTests.swift in Sources */, 20A3AFE12B0F750B0033AF2D /* MockInPersonPaymentsCashOnDeliveryToggleRowViewModel.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift new file mode 100644 index 00000000000..8874e8d90e5 --- /dev/null +++ b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift @@ -0,0 +1,35 @@ +import XCTest +@testable import WooCommerce +@testable import Yosemite +@testable import Networking + +final class MarkOrderAsReadUseCaseTests: XCTestCase { + + @MainActor + func test_markOrderNoteAsReadIfNeeded_with_stores() async { + let stores: StoresManager = MockStoresManager(sessionManager: .makeForTesting()) + + let noteID: Int64 = 1 + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: stores, noteID: noteID, orderID: 1) + switch result { + case .success(let markedNote): + XCTAssertEqual(noteID, markedNote.noteID) + case .failure(let error): + XCTFail(error.localizedDescription) + } + } + + @MainActor + func test_markOrderNoteAsReadIfNeeded_with_network() async { + let network: Network = MockNetwork() + + let noteID: Int64 = 1 + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: network, noteID: 1, orderID: 1) + switch result { + case .success(let markedNoteID): + XCTAssertEqual(noteID, markedNoteID) + case .failure(let error): + XCTFail(error.localizedDescription) + } + } +} From 046b0f3d9438e0a2d859d5a8bc8b769382a0f19b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Mon, 23 Sep 2024 15:24:38 +0200 Subject: [PATCH 2/9] Add sample notification in json file used for marking notification as read --- .../Responses/notifications-load-all.json | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Networking/NetworkingTests/Responses/notifications-load-all.json b/Networking/NetworkingTests/Responses/notifications-load-all.json index ae239b1bba5..04f8f5df875 100644 --- a/Networking/NetworkingTests/Responses/notifications-load-all.json +++ b/Networking/NetworkingTests/Responses/notifications-load-all.json @@ -7670,6 +7670,53 @@ "text": "Wooooooo welcome home @jkmassel!!!! (YES late reply, catching up with my inbox!!!)" } ] + },{ + "id": 100041, + "note_hash": 987654, + "type": "store_order", + "read": false, + "noticon": "\uf447", + "timestamp": "2018-10-22T15:29:36+00:00", + "icon": "https:\/\/s.wp.com\/wp-content\/mu-plugins\/notes\/images\/update-payment-2x.png", + "url": "https:\/\/aaaaaaaaa.mystagingwebsite.com\/wp-admin\/post.php?post=1476&action=edit", + "subject": [{ + "text": "\ud83c\udf89 You have a new order!" + }, { + "text": "Someone placed a $20.00 order from Bananza World", + "ranges": [{ + "type": "site", + "indices": [35, 48], + "url": "https:\/\/somewhere.tld", + "id": 123456 + }] + }], + "body": [{ + "text": "", + "media": [{ + "type": "image", + "indices": [0, 0], + "height": "48", + "width": "48", + "url": "https:\/\/s.wp.com\/wp-content\/mu-plugins\/notes\/images\/store-cart-icon.png" + }] + }, { + "text": "Order Number: 1476\nDate: October 22, 2018\nTotal: $20.00\nPayment Method: Credit Card (Stripe)\nShipping Method: Free shipping" + }, { + "text": "Products:\n\nAlmonds \u00d7 1\n" + }, { + "text": "\u2139\ufe0f View Order", + "ranges": [{ + "url": "https:\/\/somewhere.tld\/wp-admin\/post.php?post=1476&action=edit", + "indices": [0, 13], + "type": "link" + }] + }], + "meta": { + "ids": { + "order": 123456 + } + }, + "title": "New Order" }, { "id": 8401476681, From 9c888c0c5ecd3b26022ca84dad09bc44f8330208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Mon, 23 Sep 2024 15:24:44 +0200 Subject: [PATCH 3/9] Update MarkOrderAsReadUseCase.swift --- WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift b/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift index 813609a9857..f56a376c2ce 100644 --- a/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift +++ b/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift @@ -88,7 +88,9 @@ struct MarkOrderAsReadUseCase { switch loadedNotes { case .success(let notes): - guard let note = notes.first else { + guard let note = notes.first(where: { goalNote in + return goalNote.noteID == noteID + }) else { return .failure(MarkOrderAsReadUseCase.Error.unavailableNote) } guard let syncronizedNoteOrderID = note.meta.identifier(forKey: .order), From 4594dbb37967a48c26e4b961e6cbedabb545a5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Mon, 23 Sep 2024 15:27:33 +0200 Subject: [PATCH 4/9] Update MarkOrderAsReadUseCaseTests with proper testing --- .../Model/MarkOrderAsReadUseCaseTests.swift | 136 +++++++++++++++--- 1 file changed, 116 insertions(+), 20 deletions(-) diff --git a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift index 8874e8d90e5..cefcd596ded 100644 --- a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift +++ b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift @@ -1,35 +1,131 @@ import XCTest -@testable import WooCommerce +import WooFoundation @testable import Yosemite @testable import Networking +@testable import Storage final class MarkOrderAsReadUseCaseTests: XCTestCase { + /// Mock Dispatcher! + /// + private var dispatcher: Dispatcher! + + /// Mock Network: Allows us to inject predefined responses! + /// + private var network: MockNetwork! + + /// Mock Storage: InMemory + /// + private var storageManager: MockStorageManager! + + /// Mock stores manager + /// + private var storesManager: MockStoresManager! + + /// Convenience Property: Returns the StorageType associated with the main thread. + /// + private var viewStorage: StorageType { + return storageManager.viewStorage + } + + /// Sample Notes + /// + private lazy var sampleNotes: [Yosemite.Note] = { + return try! mapNotes(from: "notifications-load-all") + }() + + private var sampleNoteWithOrderID: Yosemite.Note? { + for note in sampleNotes { + if note.read == false, note.meta.identifier(forKey: .order) != nil { + return note + } + } + return nil + } + + override func setUp() { + super.setUp() + dispatcher = Dispatcher() + storageManager = MockStorageManager() + storesManager = MockStoresManager(sessionManager: .makeForTesting()) + network = MockNetwork() + + // Need to nuke this in-between tests otherwise some will randomly fail + NotificationStore.resetSharedDerivedStorage() + } @MainActor - func test_markOrderNoteAsReadIfNeeded_with_stores() async { - let stores: StoresManager = MockStoresManager(sessionManager: .makeForTesting()) - - let noteID: Int64 = 1 - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: stores, noteID: noteID, orderID: 1) - switch result { - case .success(let markedNote): - XCTAssertEqual(noteID, markedNote.noteID) - case .failure(let error): - XCTFail(error.localizedDescription) + func test_markOrderNoteAsReadIfNeeded_with_stores() { + let expectation = self.expectation(description: "Mark order as read with stores") + let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) + + if let note = sampleNoteWithOrderID, let orderID = note.meta.identifier(forKey: .order) { + self.storesManager.whenReceivingAction(ofType: NotificationAction.self) { action in + switch action { + case let .synchronizeNotifications(onCompletion): + onCompletion(nil) + case let .synchronizeNotification(_, onCompletion): + onCompletion(note, nil) + case let .updateReadStatus(noteID, read, onCompletion): + noteStore.updateLocalNoteReadStatus(for: [noteID], read: read) { + onCompletion(nil) + } + default: + XCTFail("Unexpected action: \(action)") + } + } + noteStore.updateLocalNotes(with: [note]) { + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) + Task { + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: note.noteID, orderID: orderID) + switch result { + case .success(let markedNote): + XCTAssertEqual(note.noteID, markedNote.noteID) + let storageNote = self.viewStorage.loadNotification(noteID: markedNote.noteID) + XCTAssertEqual(storageNote?.read, true) + expectation.fulfill() + case .failure(let error): + XCTFail(error.localizedDescription) + } + } + } + } else { + XCTFail() } + wait(for: [expectation], timeout: Constants.expectationTimeout) } @MainActor func test_markOrderNoteAsReadIfNeeded_with_network() async { - let network: Network = MockNetwork() - - let noteID: Int64 = 1 - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: network, noteID: 1, orderID: 1) - switch result { - case .success(let markedNoteID): - XCTAssertEqual(noteID, markedNoteID) - case .failure(let error): - XCTFail(error.localizedDescription) + let expectation = self.expectation(description: "Mark order as read with network") + + if let note = sampleNoteWithOrderID, let orderID = note.meta.identifier(forKey: .order) { + Task { + self.network.simulateResponse(requestUrlSuffix: "notifications", filename: "notifications-load-all") + self.network.simulateResponse(requestUrlSuffix: "notifications/read", filename: "generic_success") + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: self.network, noteID: note.noteID, orderID: orderID) + switch result { + case .success(let markedNoteID): + XCTAssertEqual(note.noteID, markedNoteID) + expectation.fulfill() + case .failure(let error): + XCTFail(error.localizedDescription) + } + } + } else { + XCTFail() } + await fulfillment(of: [expectation], timeout: Constants.expectationTimeout) + } +} + +/// Private Methods. +/// +private extension MarkOrderAsReadUseCaseTests { + + /// Returns the NoteListMapper output upon receiving `filename` (Data Encoded) + /// + func mapNotes(from filename: String) throws -> [Yosemite.Note] { + let response = Loader.contentsOf(filename)! + return try NoteListMapper().map(response: response) } } From 023af88accca780d67d0169a1549519bd67732f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Mon, 23 Sep 2024 16:07:38 +0200 Subject: [PATCH 5/9] Update total number of sample notifications --- Networking/NetworkingTests/Mapper/NoteListMapperTests.swift | 2 +- .../NetworkingTests/Remote/NotificationsRemoteTests.swift | 2 +- .../NetworkingTests/Responses/notifications-load-all.json | 3 ++- Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift b/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift index 2dce7324e95..f3e4fbd5b77 100644 --- a/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift @@ -24,7 +24,7 @@ final class NoteListMapperTests: XCTestCase { /// Verifies that all of the Sample Notifications are properly parsed. /// func test_sample_notifications_are_properly_decoded() { - XCTAssertEqual(sampleNotes.count, 44) + XCTAssertEqual(sampleNotes.count, 45) } /// Verifies that the Broken Notification documents are properly parsed. diff --git a/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift b/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift index 3417467de43..cbec8382222 100644 --- a/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift +++ b/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift @@ -40,7 +40,7 @@ final class NotificationsRemoteTests: XCTestCase { XCTAssertTrue(result.isSuccess) let notes = try result.get() - XCTAssertEqual(notes.count, 44) + XCTAssertEqual(notes.count, 45) } /// Verifies that `loadHashes` properly returns all of the retrieved hashes. diff --git a/Networking/NetworkingTests/Responses/notifications-load-all.json b/Networking/NetworkingTests/Responses/notifications-load-all.json index 04f8f5df875..aa6a4b92b40 100644 --- a/Networking/NetworkingTests/Responses/notifications-load-all.json +++ b/Networking/NetworkingTests/Responses/notifications-load-all.json @@ -7670,7 +7670,8 @@ "text": "Wooooooo welcome home @jkmassel!!!! (YES late reply, catching up with my inbox!!!)" } ] - },{ + }, + { "id": 100041, "note_hash": 987654, "type": "store_order", diff --git a/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift b/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift index 566d911e924..ef8eb6ffa4a 100644 --- a/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift +++ b/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift @@ -49,7 +49,7 @@ class NotificationStoreTests: XCTestCase { XCTAssertEqual(viewStorage.countObjects(ofType: Storage.Note.self), 0) let action = NotificationAction.synchronizeNotifications() { (error) in XCTAssertNil(error) - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 44) + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 45) if let note = self.viewStorage.loadNotification(noteID: 100036, noteHash: 987654)?.toReadOnly() { // Plain Fields @@ -111,7 +111,7 @@ class NotificationStoreTests: XCTestCase { /// Initial Sync /// let initialSyncAction = NotificationAction.synchronizeNotifications() { (error) in - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 44) + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 45) notificationStore.onAction(nestedSyncAction) } From a4a42be0979e9addbe47c3f245144c29588526c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Tue, 24 Sep 2024 09:18:33 +0200 Subject: [PATCH 6/9] Update tests --- .../Mapper/NoteListMapperTests.swift | 2 +- .../Remote/NotificationsRemoteTests.swift | 2 +- .../Responses/notifications-load-all.json | 48 +++++++++ .../Model/MarkOrderAsReadUseCase.swift | 8 +- .../Model/MarkOrderAsReadUseCaseTests.swift | 98 +++++++++++++++---- .../Stores/NotificationStoreTests.swift | 4 +- 6 files changed, 135 insertions(+), 27 deletions(-) diff --git a/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift b/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift index f3e4fbd5b77..be4c51ea8cb 100644 --- a/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift @@ -24,7 +24,7 @@ final class NoteListMapperTests: XCTestCase { /// Verifies that all of the Sample Notifications are properly parsed. /// func test_sample_notifications_are_properly_decoded() { - XCTAssertEqual(sampleNotes.count, 45) + XCTAssertEqual(sampleNotes.count, 46) } /// Verifies that the Broken Notification documents are properly parsed. diff --git a/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift b/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift index cbec8382222..e5c63ba7199 100644 --- a/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift +++ b/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift @@ -40,7 +40,7 @@ final class NotificationsRemoteTests: XCTestCase { XCTAssertTrue(result.isSuccess) let notes = try result.get() - XCTAssertEqual(notes.count, 45) + XCTAssertEqual(notes.count, 46) } /// Verifies that `loadHashes` properly returns all of the retrieved hashes. diff --git a/Networking/NetworkingTests/Responses/notifications-load-all.json b/Networking/NetworkingTests/Responses/notifications-load-all.json index aa6a4b92b40..62007213530 100644 --- a/Networking/NetworkingTests/Responses/notifications-load-all.json +++ b/Networking/NetworkingTests/Responses/notifications-load-all.json @@ -7719,6 +7719,54 @@ }, "title": "New Order" }, + { + "id": 100042, + "note_hash": 987654, + "type": "store_order", + "read": true, + "noticon": "\uf447", + "timestamp": "2018-10-22T15:29:36+00:00", + "icon": "https:\/\/s.wp.com\/wp-content\/mu-plugins\/notes\/images\/update-payment-2x.png", + "url": "https:\/\/aaaaaaaaa.mystagingwebsite.com\/wp-admin\/post.php?post=1476&action=edit", + "subject": [{ + "text": "\ud83c\udf89 You have a new order!" + }, { + "text": "Someone placed a $20.00 order from Bananza World", + "ranges": [{ + "type": "site", + "indices": [35, 48], + "url": "https:\/\/somewhere.tld", + "id": 123456 + }] + }], + "body": [{ + "text": "", + "media": [{ + "type": "image", + "indices": [0, 0], + "height": "48", + "width": "48", + "url": "https:\/\/s.wp.com\/wp-content\/mu-plugins\/notes\/images\/store-cart-icon.png" + }] + }, { + "text": "Order Number: 1476\nDate: October 22, 2018\nTotal: $20.00\nPayment Method: Credit Card (Stripe)\nShipping Method: Free shipping" + }, { + "text": "Products:\n\nAlmonds \u00d7 1\n" + }, { + "text": "\u2139\ufe0f View Order", + "ranges": [{ + "url": "https:\/\/somewhere.tld\/wp-admin\/post.php?post=1476&action=edit", + "indices": [0, 13], + "type": "link" + }] + }], + "meta": { + "ids": { + "order": 123456 + } + }, + "title": "New Order" + }, { "id": 8401476681, "type": "blaze_approved_note", diff --git a/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift b/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift index f56a376c2ce..c7852cec166 100644 --- a/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift +++ b/WooCommerce/Classes/Model/MarkOrderAsReadUseCase.swift @@ -40,7 +40,8 @@ struct MarkOrderAsReadUseCase { switch syncronizedNoteResult { case .success(let syncronizedNote): guard let syncronizedNoteOrderID = syncronizedNote.meta.identifier(forKey: .order), - syncronizedNoteOrderID == orderID else { + syncronizedNoteOrderID == orderID, + syncronizedNote.read == false else { return .failure(MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead) } @@ -93,8 +94,9 @@ struct MarkOrderAsReadUseCase { }) else { return .failure(MarkOrderAsReadUseCase.Error.unavailableNote) } - guard let syncronizedNoteOrderID = note.meta.identifier(forKey: .order), - syncronizedNoteOrderID == orderID else { + guard let loadedNoteOrderID = note.meta.identifier(forKey: .order), + loadedNoteOrderID == orderID, + note.read == false else { return .failure(MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead) } diff --git a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift index cefcd596ded..3b032c5d1cd 100644 --- a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift +++ b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift @@ -33,9 +33,9 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { return try! mapNotes(from: "notifications-load-all") }() - private var sampleNoteWithOrderID: Yosemite.Note? { + private func sampleNote(read: Bool) -> Yosemite.Note? { for note in sampleNotes { - if note.read == false, note.meta.identifier(forKey: .order) != nil { + if note.read == read, note.meta.identifier(forKey: .order) != nil { return note } } @@ -53,26 +53,30 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { NotificationStore.resetSharedDerivedStorage() } + private func setupStoreManagerReceivingNotificationActions(for note: Yosemite.Note, noteStore: NotificationStore) { + self.storesManager.whenReceivingAction(ofType: NotificationAction.self) { action in + switch action { + case let .synchronizeNotifications(onCompletion): + onCompletion(nil) + case let .synchronizeNotification(_, onCompletion): + onCompletion(note, nil) + case let .updateReadStatus(noteID, read, onCompletion): + noteStore.updateLocalNoteReadStatus(for: [noteID], read: read) { + onCompletion(nil) + } + default: + XCTFail("Unexpected action: \(action)") + } + } + } + @MainActor - func test_markOrderNoteAsReadIfNeeded_with_stores() { + func test_markOrderNoteAsReadIfNeeded_with_stores_unreadNote() { let expectation = self.expectation(description: "Mark order as read with stores") let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) - if let note = sampleNoteWithOrderID, let orderID = note.meta.identifier(forKey: .order) { - self.storesManager.whenReceivingAction(ofType: NotificationAction.self) { action in - switch action { - case let .synchronizeNotifications(onCompletion): - onCompletion(nil) - case let .synchronizeNotification(_, onCompletion): - onCompletion(note, nil) - case let .updateReadStatus(noteID, read, onCompletion): - noteStore.updateLocalNoteReadStatus(for: [noteID], read: read) { - onCompletion(nil) - } - default: - XCTFail("Unexpected action: \(action)") - } - } + if let note = sampleNote(read: false), let orderID = note.meta.identifier(forKey: .order) { + setupStoreManagerReceivingNotificationActions(for: note, noteStore: noteStore) noteStore.updateLocalNotes(with: [note]) { XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) Task { @@ -95,10 +99,39 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { } @MainActor - func test_markOrderNoteAsReadIfNeeded_with_network() async { + func test_markOrderNoteAsReadIfNeeded_with_stores_alreadyReadNote() { + let expectation = self.expectation(description: "Mark order as read with stores") + let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) + + if let note = sampleNote(read: true), let orderID = note.meta.identifier(forKey: .order) { + setupStoreManagerReceivingNotificationActions(for: note, noteStore: noteStore) + noteStore.updateLocalNotes(with: [note]) { + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) + Task { + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: note.noteID, orderID: orderID) + switch result { + case .success: + XCTFail("Note was already read, it should not be marked as read again.") + case .failure(let error): + if case MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead = error { + expectation.fulfill() + } else { + XCTFail("Got wrong error \(error.localizedDescription)") + } + } + } + } + } else { + XCTFail() + } + wait(for: [expectation], timeout: Constants.expectationTimeout) + } + + @MainActor + func test_markOrderNoteAsReadIfNeeded_with_network_unreadNote() async { let expectation = self.expectation(description: "Mark order as read with network") - if let note = sampleNoteWithOrderID, let orderID = note.meta.identifier(forKey: .order) { + if let note = sampleNote(read: false), let orderID = note.meta.identifier(forKey: .order) { Task { self.network.simulateResponse(requestUrlSuffix: "notifications", filename: "notifications-load-all") self.network.simulateResponse(requestUrlSuffix: "notifications/read", filename: "generic_success") @@ -116,6 +149,31 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { } await fulfillment(of: [expectation], timeout: Constants.expectationTimeout) } + + @MainActor + func test_markOrderNoteAsReadIfNeeded_with_network_alreadyReadNote() async { + let expectation = self.expectation(description: "Mark order as read with network") + + if let note = sampleNote(read: true), let orderID = note.meta.identifier(forKey: .order) { + Task { + self.network.simulateResponse(requestUrlSuffix: "notifications", filename: "notifications-load-all") + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: self.network, noteID: note.noteID, orderID: orderID) + switch result { + case .success: + XCTFail("Note was already read, it should not be marked as read again.") + case .failure(let error): + if case MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead = error { + expectation.fulfill() + } else { + XCTFail("Got wrong error \(error.localizedDescription)") + } + } + } + } else { + XCTFail() + } + await fulfillment(of: [expectation], timeout: Constants.expectationTimeout) + } } /// Private Methods. diff --git a/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift b/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift index ef8eb6ffa4a..ed494ad82a1 100644 --- a/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift +++ b/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift @@ -49,7 +49,7 @@ class NotificationStoreTests: XCTestCase { XCTAssertEqual(viewStorage.countObjects(ofType: Storage.Note.self), 0) let action = NotificationAction.synchronizeNotifications() { (error) in XCTAssertNil(error) - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 45) + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 46) if let note = self.viewStorage.loadNotification(noteID: 100036, noteHash: 987654)?.toReadOnly() { // Plain Fields @@ -111,7 +111,7 @@ class NotificationStoreTests: XCTestCase { /// Initial Sync /// let initialSyncAction = NotificationAction.synchronizeNotifications() { (error) in - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 45) + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 46) notificationStore.onAction(nestedSyncAction) } From 31f5303976ad58bbdb1dbc0e7b88de588d2927fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Wed, 25 Sep 2024 11:20:42 +0200 Subject: [PATCH 7/9] Add notificationLoadAllJSONCount magic number --- Networking/Networking/Network/MockNetwork.swift | 4 ++++ Networking/NetworkingTests/Mapper/NoteListMapperTests.swift | 2 +- .../NetworkingTests/Remote/NotificationsRemoteTests.swift | 2 +- Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Networking/Networking/Network/MockNetwork.swift b/Networking/Networking/Network/MockNetwork.swift index 7bb663c85ad..613d769bae3 100644 --- a/Networking/Networking/Network/MockNetwork.swift +++ b/Networking/Networking/Network/MockNetwork.swift @@ -27,6 +27,10 @@ class MockNetwork: Network { /// var requestsForResponseData = [URLRequestConvertible]() + /// Number of notification objects in notifications-load-all.json file. + /// + static let notificationLoadAllJSONCount = 46 + /// Note: If the useResponseQueue param is `true`, any responses added via `simulateResponse` will stored in a FIFO queue /// and used once for a matching request (then removed from the queue). Subsequent requests will use the next response in the queue, and so on. /// diff --git a/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift b/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift index be4c51ea8cb..7c61b8783ed 100644 --- a/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift +++ b/Networking/NetworkingTests/Mapper/NoteListMapperTests.swift @@ -24,7 +24,7 @@ final class NoteListMapperTests: XCTestCase { /// Verifies that all of the Sample Notifications are properly parsed. /// func test_sample_notifications_are_properly_decoded() { - XCTAssertEqual(sampleNotes.count, 46) + XCTAssertEqual(sampleNotes.count, MockNetwork.notificationLoadAllJSONCount) } /// Verifies that the Broken Notification documents are properly parsed. diff --git a/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift b/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift index e5c63ba7199..3697b4300d7 100644 --- a/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift +++ b/Networking/NetworkingTests/Remote/NotificationsRemoteTests.swift @@ -40,7 +40,7 @@ final class NotificationsRemoteTests: XCTestCase { XCTAssertTrue(result.isSuccess) let notes = try result.get() - XCTAssertEqual(notes.count, 46) + XCTAssertEqual(notes.count, MockNetwork.notificationLoadAllJSONCount) } /// Verifies that `loadHashes` properly returns all of the retrieved hashes. diff --git a/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift b/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift index ed494ad82a1..97cef08d3c7 100644 --- a/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift +++ b/Yosemite/YosemiteTests/Stores/NotificationStoreTests.swift @@ -49,7 +49,7 @@ class NotificationStoreTests: XCTestCase { XCTAssertEqual(viewStorage.countObjects(ofType: Storage.Note.self), 0) let action = NotificationAction.synchronizeNotifications() { (error) in XCTAssertNil(error) - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 46) + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), MockNetwork.notificationLoadAllJSONCount) if let note = self.viewStorage.loadNotification(noteID: 100036, noteHash: 987654)?.toReadOnly() { // Plain Fields @@ -111,7 +111,7 @@ class NotificationStoreTests: XCTestCase { /// Initial Sync /// let initialSyncAction = NotificationAction.synchronizeNotifications() { (error) in - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 46) + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), MockNetwork.notificationLoadAllJSONCount) notificationStore.onAction(nestedSyncAction) } From b086a7fdb9cb21fe47870c8c92dda9879aa7bde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Wed, 25 Sep 2024 11:21:03 +0200 Subject: [PATCH 8/9] Clean up MarkOrderAsReadUseCaseTests --- .../Model/MarkOrderAsReadUseCaseTests.swift | 173 ++++++++---------- 1 file changed, 76 insertions(+), 97 deletions(-) diff --git a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift index 3b032c5d1cd..b68a9295167 100644 --- a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift +++ b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift @@ -5,41 +5,21 @@ import WooFoundation @testable import Storage final class MarkOrderAsReadUseCaseTests: XCTestCase { - /// Mock Dispatcher! - /// private var dispatcher: Dispatcher! - - /// Mock Network: Allows us to inject predefined responses! - /// private var network: MockNetwork! - - /// Mock Storage: InMemory - /// private var storageManager: MockStorageManager! - - /// Mock stores manager - /// private var storesManager: MockStoresManager! - - /// Convenience Property: Returns the StorageType associated with the main thread. - /// private var viewStorage: StorageType { return storageManager.viewStorage } - - /// Sample Notes - /// private lazy var sampleNotes: [Yosemite.Note] = { return try! mapNotes(from: "notifications-load-all") }() private func sampleNote(read: Bool) -> Yosemite.Note? { - for note in sampleNotes { - if note.read == read, note.meta.identifier(forKey: .order) != nil { - return note - } + return sampleNotes.first { note in + return note.read == read && note.meta.identifier(forKey: .order) != nil } - return nil } override func setUp() { @@ -49,12 +29,16 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { storesManager = MockStoresManager(sessionManager: .makeForTesting()) network = MockNetwork() - // Need to nuke this in-between tests otherwise some will randomly fail NotificationStore.resetSharedDerivedStorage() } + override func tearDown() { + NotificationStore.resetSharedDerivedStorage() + super.tearDown() + } + private func setupStoreManagerReceivingNotificationActions(for note: Yosemite.Note, noteStore: NotificationStore) { - self.storesManager.whenReceivingAction(ofType: NotificationAction.self) { action in + storesManager.whenReceivingAction(ofType: NotificationAction.self) { action in switch action { case let .synchronizeNotifications(onCompletion): onCompletion(nil) @@ -71,93 +55,48 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { } @MainActor - func test_markOrderNoteAsReadIfNeeded_with_stores_unreadNote() { - let expectation = self.expectation(description: "Mark order as read with stores") - let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) - - if let note = sampleNote(read: false), let orderID = note.meta.identifier(forKey: .order) { - setupStoreManagerReceivingNotificationActions(for: note, noteStore: noteStore) - noteStore.updateLocalNotes(with: [note]) { - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) - Task { - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: note.noteID, orderID: orderID) - switch result { - case .success(let markedNote): - XCTAssertEqual(note.noteID, markedNote.noteID) - let storageNote = self.viewStorage.loadNotification(noteID: markedNote.noteID) - XCTAssertEqual(storageNote?.read, true) - expectation.fulfill() - case .failure(let error): - XCTFail(error.localizedDescription) - } - } - } - } else { - XCTFail() - } - wait(for: [expectation], timeout: Constants.expectationTimeout) - } + func test_markOrderNoteAsReadIfNeeded_with_stores_unreadNote() throws { + let unreadNote = try XCTUnwrap(sampleNote(read: false)) + let orderID = try XCTUnwrap(unreadNote.meta.identifier(forKey: .order)) - @MainActor - func test_markOrderNoteAsReadIfNeeded_with_stores_alreadyReadNote() { - let expectation = self.expectation(description: "Mark order as read with stores") + let expectation = expectation(description: "Mark order as read with stores") let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) - if let note = sampleNote(read: true), let orderID = note.meta.identifier(forKey: .order) { - setupStoreManagerReceivingNotificationActions(for: note, noteStore: noteStore) - noteStore.updateLocalNotes(with: [note]) { - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) - Task { - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: note.noteID, orderID: orderID) - switch result { - case .success: - XCTFail("Note was already read, it should not be marked as read again.") - case .failure(let error): - if case MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead = error { - expectation.fulfill() - } else { - XCTFail("Got wrong error \(error.localizedDescription)") - } - } - } - } - } else { - XCTFail() - } - wait(for: [expectation], timeout: Constants.expectationTimeout) - } + setupStoreManagerReceivingNotificationActions(for: unreadNote, noteStore: noteStore) - @MainActor - func test_markOrderNoteAsReadIfNeeded_with_network_unreadNote() async { - let expectation = self.expectation(description: "Mark order as read with network") - - if let note = sampleNote(read: false), let orderID = note.meta.identifier(forKey: .order) { + noteStore.updateLocalNotes(with: [unreadNote]) { + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) Task { - self.network.simulateResponse(requestUrlSuffix: "notifications", filename: "notifications-load-all") - self.network.simulateResponse(requestUrlSuffix: "notifications/read", filename: "generic_success") - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: self.network, noteID: note.noteID, orderID: orderID) + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: unreadNote.noteID, orderID: orderID) switch result { - case .success(let markedNoteID): - XCTAssertEqual(note.noteID, markedNoteID) + case .success(let markedNote): + XCTAssertEqual(unreadNote.noteID, markedNote.noteID) + let storageNote = self.viewStorage.loadNotification(noteID: markedNote.noteID) + XCTAssertEqual(storageNote?.read, true) expectation.fulfill() case .failure(let error): XCTFail(error.localizedDescription) } } - } else { - XCTFail() } - await fulfillment(of: [expectation], timeout: Constants.expectationTimeout) + + wait(for: [expectation], timeout: Constants.expectationTimeout) } @MainActor - func test_markOrderNoteAsReadIfNeeded_with_network_alreadyReadNote() async { - let expectation = self.expectation(description: "Mark order as read with network") + func test_markOrderNoteAsReadIfNeeded_with_stores_alreadyReadNote() throws { + let readNote = try XCTUnwrap(sampleNote(read: true)) + let orderID = try XCTUnwrap(readNote.meta.identifier(forKey: .order)) + + let expectation = expectation(description: "Mark order as read with stores") + let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) - if let note = sampleNote(read: true), let orderID = note.meta.identifier(forKey: .order) { + setupStoreManagerReceivingNotificationActions(for: readNote, noteStore: noteStore) + + noteStore.updateLocalNotes(with: [readNote]) { + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) Task { - self.network.simulateResponse(requestUrlSuffix: "notifications", filename: "notifications-load-all") - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: self.network, noteID: note.noteID, orderID: orderID) + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: readNote.noteID, orderID: orderID) switch result { case .success: XCTFail("Note was already read, it should not be marked as read again.") @@ -169,10 +108,50 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { } } } - } else { - XCTFail() } - await fulfillment(of: [expectation], timeout: Constants.expectationTimeout) + + wait(for: [expectation], timeout: Constants.expectationTimeout) + } + + @MainActor + func test_markOrderNoteAsReadIfNeeded_with_network_unreadNote() async throws { + let unreadNote = try XCTUnwrap(sampleNote(read: false)) + let orderID = try XCTUnwrap(unreadNote.meta.identifier(forKey: .order)) + + network.simulateResponse(requestUrlSuffix: "notifications", filename: "notifications-load-all") + network.simulateResponse(requestUrlSuffix: "notifications/read", filename: "generic_success") + + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: network, + noteID: unreadNote.noteID, + orderID: orderID) + + switch result { + case .success(let markedNoteID): + XCTAssertEqual(unreadNote.noteID, markedNoteID) + case .failure(let error): + XCTFail(error.localizedDescription) + } + } + + @MainActor + func test_markOrderNoteAsReadIfNeeded_with_network_alreadyReadNote() async throws { + let readNote = try XCTUnwrap(sampleNote(read: true)) + let orderID = try XCTUnwrap(readNote.meta.identifier(forKey: .order)) + + network.simulateResponse(requestUrlSuffix: "notifications", filename: "notifications-load-all") + + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(network: network, + noteID: readNote.noteID, + orderID: orderID) + + switch result { + case .success: + XCTFail("Note was already read, it should not be marked as read again.") + case .failure(let error): + if case MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead = error {} else { + XCTFail("Got wrong error \(error.localizedDescription)") + } + } } } From e20e45360f96c1c6c1ce3f73af5d71fef2e13ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boz=CC=8Cidar=20S=CC=8Cevo?= Date: Thu, 26 Sep 2024 15:01:20 +0200 Subject: [PATCH 9/9] Update MarkOrderAsReadUseCaseTests.swift --- .../Model/MarkOrderAsReadUseCaseTests.swift | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift index b68a9295167..eac7983300a 100644 --- a/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift +++ b/WooCommerce/WooCommerceTests/Model/MarkOrderAsReadUseCaseTests.swift @@ -55,62 +55,57 @@ final class MarkOrderAsReadUseCaseTests: XCTestCase { } @MainActor - func test_markOrderNoteAsReadIfNeeded_with_stores_unreadNote() throws { + func test_markOrderNoteAsReadIfNeeded_with_stores_unreadNote() async throws { let unreadNote = try XCTUnwrap(sampleNote(read: false)) let orderID = try XCTUnwrap(unreadNote.meta.identifier(forKey: .order)) - let expectation = expectation(description: "Mark order as read with stores") let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) setupStoreManagerReceivingNotificationActions(for: unreadNote, noteStore: noteStore) - noteStore.updateLocalNotes(with: [unreadNote]) { - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) - Task { - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: unreadNote.noteID, orderID: orderID) - switch result { - case .success(let markedNote): - XCTAssertEqual(unreadNote.noteID, markedNote.noteID) - let storageNote = self.viewStorage.loadNotification(noteID: markedNote.noteID) - XCTAssertEqual(storageNote?.read, true) - expectation.fulfill() - case .failure(let error): - XCTFail(error.localizedDescription) - } + await withCheckedContinuation { continuation in + noteStore.updateLocalNotes(with: [unreadNote]) { + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) + continuation.resume() } } - wait(for: [expectation], timeout: Constants.expectationTimeout) + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: storesManager, noteID: unreadNote.noteID, orderID: orderID) + switch result { + case .success(let markedNote): + XCTAssertEqual(unreadNote.noteID, markedNote.noteID) + let storageNote = viewStorage.loadNotification(noteID: markedNote.noteID) + XCTAssertEqual(storageNote?.read, true) + case .failure(let error): + XCTFail(error.localizedDescription) + } } @MainActor - func test_markOrderNoteAsReadIfNeeded_with_stores_alreadyReadNote() throws { + func test_markOrderNoteAsReadIfNeeded_with_stores_alreadyReadNote() async throws { let readNote = try XCTUnwrap(sampleNote(read: true)) let orderID = try XCTUnwrap(readNote.meta.identifier(forKey: .order)) - let expectation = expectation(description: "Mark order as read with stores") let noteStore = NotificationStore(dispatcher: dispatcher, storageManager: storageManager, network: network) setupStoreManagerReceivingNotificationActions(for: readNote, noteStore: noteStore) - noteStore.updateLocalNotes(with: [readNote]) { - XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) - Task { - let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: self.storesManager, noteID: readNote.noteID, orderID: orderID) - switch result { - case .success: - XCTFail("Note was already read, it should not be marked as read again.") - case .failure(let error): - if case MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead = error { - expectation.fulfill() - } else { - XCTFail("Got wrong error \(error.localizedDescription)") - } - } + await withCheckedContinuation { continuation in + noteStore.updateLocalNotes(with: [readNote]) { + XCTAssertEqual(self.viewStorage.countObjects(ofType: Storage.Note.self), 1) + continuation.resume() } } - wait(for: [expectation], timeout: Constants.expectationTimeout) + let result = await MarkOrderAsReadUseCase.markOrderNoteAsReadIfNeeded(stores: storesManager, noteID: readNote.noteID, orderID: orderID) + switch result { + case .success: + XCTFail("Note was already read, it should not be marked as read again.") + case .failure(let error): + if case MarkOrderAsReadUseCase.Error.noNeedToMarkAsRead = error {} else { + XCTFail("Got wrong error \(error.localizedDescription)") + } + } } @MainActor