Skip to content

Commit 47c62f4

Browse files
committed
Add test cases for POSCatalogSettingsService. Date comparison allows a range due to different precision in database.
1 parent 28f706f commit 47c62f4

File tree

1 file changed

+274
-0
lines changed

1 file changed

+274
-0
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
import Foundation
2+
import Testing
3+
import GRDB
4+
@testable import Yosemite
5+
@testable import Storage
6+
7+
struct POSCatalogSettingsServiceTests {
8+
private let grdbManager: GRDBManager
9+
private let sut: POSCatalogSettingsService
10+
private let sampleSiteID: Int64 = 134
11+
12+
init() throws {
13+
self.grdbManager = try GRDBManager()
14+
self.sut = POSCatalogSettingsService(grdbManager: grdbManager)
15+
}
16+
17+
// MARK: - `loadCatalogStatistics` Tests
18+
19+
@Test(arguments: [6, 8], [7, 0])
20+
func loadCatalogStatistics_returns_correct_counts_for_products_and_variations(productCount: Int, variationCount: Int) async throws {
21+
// Given
22+
try insertSite(siteID: sampleSiteID)
23+
try insertTestProducts(siteID: sampleSiteID, productCount: productCount, variationCount: variationCount)
24+
25+
// When
26+
let statistics = try await sut.loadCatalogStatistics(for: sampleSiteID)
27+
28+
// Then
29+
#expect(statistics.productCount == productCount)
30+
#expect(statistics.variationCount == variationCount)
31+
}
32+
33+
@Test func loadCatalogStatistics_returns_zero_counts_when_no_data_exists() async throws {
34+
// Given - no data in database for the site
35+
36+
// When
37+
let statistics = try await sut.loadCatalogStatistics(for: sampleSiteID)
38+
39+
// Then
40+
#expect(statistics.productCount == 0)
41+
#expect(statistics.variationCount == 0)
42+
}
43+
44+
@Test func loadCatalogStatistics_only_counts_items_for_specified_site() async throws {
45+
// Given
46+
let siteA: Int64 = 100
47+
let siteB: Int64 = 200
48+
try insertSite(siteID: siteA)
49+
try insertTestProducts(siteID: siteA, productCount: 3, variationCount: 4)
50+
try insertSite(siteID: siteB)
51+
try insertTestProducts(siteID: siteB, productCount: 2, variationCount: 1)
52+
53+
// When
54+
let statisticsA = try await sut.loadCatalogStatistics(for: siteA)
55+
let statisticsB = try await sut.loadCatalogStatistics(for: siteB)
56+
57+
// Then
58+
#expect(statisticsA.productCount == 3)
59+
#expect(statisticsA.variationCount == 4)
60+
#expect(statisticsB.productCount == 2)
61+
#expect(statisticsB.variationCount == 1)
62+
}
63+
64+
@Test func loadCatalogStatistics_propagates_database_errors() async throws {
65+
// Given - close the database to simulate an error
66+
try grdbManager.databaseConnection.close()
67+
68+
// When/Then
69+
await #expect(throws: DatabaseError.self) {
70+
_ = try await sut.loadCatalogStatistics(for: sampleSiteID)
71+
}
72+
}
73+
74+
// MARK: - `loadSyncDates` Tests
75+
76+
@Test func loadSyncDates_returns_sync_dates_when_site_has_sync_history() async throws {
77+
// Given
78+
let fullSyncDate = Date(timeIntervalSinceNow: -3600) // 1 hour ago
79+
let incrementalSyncDate = Date(timeIntervalSinceNow: -1800) // 30 minutes ago
80+
try insertSite(siteID: sampleSiteID,
81+
lastFullSyncDate: fullSyncDate,
82+
lastIncrementalSyncDate: incrementalSyncDate)
83+
84+
// When
85+
let syncDates = try await sut.loadSyncDates(for: sampleSiteID)
86+
87+
// Then
88+
#expect(syncDates.lastFullSyncDate?.timeIntervalSince(fullSyncDate) ?? 0 < 1.0)
89+
#expect(syncDates.lastIncrementalSyncDate?.timeIntervalSince(incrementalSyncDate) ?? 0 < 1.0)
90+
}
91+
92+
@Test func loadSyncDates_returns_nil_dates_when_site_has_no_sync_history() async throws {
93+
// Given
94+
try insertSite(siteID: sampleSiteID,
95+
lastFullSyncDate: nil,
96+
lastIncrementalSyncDate: nil)
97+
98+
// When
99+
let syncDates = try await sut.loadSyncDates(for: sampleSiteID)
100+
101+
// Then
102+
#expect(syncDates.lastFullSyncDate == nil)
103+
#expect(syncDates.lastIncrementalSyncDate == nil)
104+
}
105+
106+
@Test func loadSyncDates_returns_nil_dates_when_site_does_not_exist() async throws {
107+
// Given - site does not exist in database
108+
109+
// When
110+
let syncDates = try await sut.loadSyncDates(for: sampleSiteID)
111+
112+
// Then
113+
#expect(syncDates.lastFullSyncDate == nil)
114+
#expect(syncDates.lastIncrementalSyncDate == nil)
115+
}
116+
117+
@Test func loadSyncDates_returns_partial_sync_dates() async throws {
118+
// Given - only full sync date is set
119+
let fullSyncDate = Date(timeIntervalSinceNow: -7200) // 2 hours ago
120+
121+
try insertSite(siteID: sampleSiteID,
122+
lastFullSyncDate: fullSyncDate,
123+
lastIncrementalSyncDate: nil)
124+
125+
// When
126+
let syncDates = try await sut.loadSyncDates(for: sampleSiteID)
127+
128+
// Then
129+
#expect(syncDates.lastFullSyncDate?.timeIntervalSince(fullSyncDate) ?? 0 < 1.0)
130+
#expect(syncDates.lastIncrementalSyncDate == nil)
131+
}
132+
133+
@Test func loadSyncDates_handles_different_sites_independently() async throws {
134+
// Given
135+
let siteA: Int64 = 100
136+
let siteB: Int64 = 200
137+
let dateA = Date(timeIntervalSinceNow: -3600)
138+
let dateB = Date(timeIntervalSinceNow: -7200)
139+
140+
try insertSite(siteID: siteA,
141+
lastFullSyncDate: dateA,
142+
lastIncrementalSyncDate: nil)
143+
try insertSite(siteID: siteB,
144+
lastFullSyncDate: nil,
145+
lastIncrementalSyncDate: dateB)
146+
147+
// When
148+
let syncDatesA = try await sut.loadSyncDates(for: siteA)
149+
let syncDatesB = try await sut.loadSyncDates(for: siteB)
150+
151+
// Then
152+
#expect(syncDatesA.lastFullSyncDate?.timeIntervalSince(dateA) ?? 0 < 1.0)
153+
#expect(syncDatesA.lastIncrementalSyncDate == nil)
154+
#expect(syncDatesB.lastFullSyncDate == nil)
155+
#expect(syncDatesB.lastIncrementalSyncDate?.timeIntervalSince(dateB) ?? 0 < 1.0)
156+
}
157+
158+
@Test func loadSyncDates_propagates_database_errors() async throws {
159+
// Given - close the database to simulate an error
160+
try grdbManager.databaseConnection.close()
161+
162+
// When/Then
163+
await #expect(throws: DatabaseError.self) {
164+
_ = try await sut.loadSyncDates(for: sampleSiteID)
165+
}
166+
}
167+
168+
// MARK: - Concurrent Operations Tests
169+
170+
@Test func concurrent_loadCatalogStatistics_calls_work_correctly() async throws {
171+
// Given
172+
let siteA: Int64 = 100
173+
let siteB: Int64 = 200
174+
try insertSite(siteID: siteA)
175+
try insertTestProducts(siteID: siteA, productCount: 10, variationCount: 15)
176+
try insertSite(siteID: siteB)
177+
try insertTestProducts(siteID: siteB, productCount: 6, variationCount: 8)
178+
179+
// When
180+
async let statisticsA = sut.loadCatalogStatistics(for: siteA)
181+
async let statisticsB = sut.loadCatalogStatistics(for: siteB)
182+
183+
let (resultA, resultB) = try await (statisticsA, statisticsB)
184+
185+
// Then
186+
#expect(resultA.productCount == 10)
187+
#expect(resultA.variationCount == 15)
188+
#expect(resultB.productCount == 6)
189+
#expect(resultB.variationCount == 8)
190+
}
191+
192+
@Test func concurrent_loadSyncDates_calls_work_correctly() async throws {
193+
// Given
194+
let siteA: Int64 = 100
195+
let siteB: Int64 = 200
196+
let dateA = Date(timeIntervalSinceNow: -3600)
197+
let dateB = Date(timeIntervalSinceNow: -7200)
198+
199+
try insertSite(siteID: siteA, lastFullSyncDate: dateA, lastIncrementalSyncDate: nil)
200+
try insertSite(siteID: siteB, lastFullSyncDate: nil, lastIncrementalSyncDate: dateB)
201+
202+
// When
203+
async let syncDatesA = sut.loadSyncDates(for: siteA)
204+
async let syncDatesB = sut.loadSyncDates(for: siteB)
205+
206+
let (resultA, resultB) = try await (syncDatesA, syncDatesB)
207+
208+
// Then
209+
#expect(resultA.lastFullSyncDate?.timeIntervalSince(dateA) ?? 0 < 1.0)
210+
#expect(resultA.lastIncrementalSyncDate == nil)
211+
#expect(resultB.lastFullSyncDate == nil)
212+
#expect(resultB.lastIncrementalSyncDate?.timeIntervalSince(dateB) ?? 0 < 1.0)
213+
}
214+
215+
@Test func concurrent_mixed_operations_work_correctly() async throws {
216+
// Given
217+
let syncDate = Date(timeIntervalSinceNow: -1800)
218+
try insertSite(siteID: sampleSiteID, lastFullSyncDate: syncDate, lastIncrementalSyncDate: syncDate)
219+
try insertTestProducts(siteID: sampleSiteID, productCount: 20, variationCount: 30)
220+
221+
// When
222+
async let statistics = sut.loadCatalogStatistics(for: sampleSiteID)
223+
async let syncDates = sut.loadSyncDates(for: sampleSiteID)
224+
225+
let (statsResult, datesResult) = try await (statistics, syncDates)
226+
227+
// Then
228+
#expect(statsResult.productCount == 20)
229+
#expect(statsResult.variationCount == 30)
230+
#expect(datesResult.lastFullSyncDate?.timeIntervalSince(syncDate) ?? 0 < 1.0)
231+
#expect(datesResult.lastIncrementalSyncDate?.timeIntervalSince(syncDate) ?? 0 < 1.0)
232+
}
233+
}
234+
235+
// MARK: - Helper Methods
236+
237+
private extension POSCatalogSettingsServiceTests {
238+
func insertTestProducts(siteID: Int64, productCount: Int, variationCount: Int) throws {
239+
try grdbManager.databaseConnection.write { db in
240+
if productCount > 0 {
241+
for i in 1...productCount {
242+
let product = PersistedProduct(from: POSProduct.fake().copy(
243+
siteID: siteID,
244+
productID: Int64(i),
245+
name: "Product \(i)"
246+
))
247+
try product.insert(db)
248+
}
249+
}
250+
251+
if variationCount > 0, productCount > 0 {
252+
for i in 1...variationCount {
253+
let variation = PersistedProductVariation(from: POSProductVariation.fake().copy(
254+
siteID: siteID,
255+
productID: Int64(1),
256+
productVariationID: Int64(i)
257+
))
258+
try variation.insert(db)
259+
}
260+
}
261+
}
262+
}
263+
264+
func insertSite(siteID: Int64, lastFullSyncDate: Date? = nil, lastIncrementalSyncDate: Date? = nil) throws {
265+
try grdbManager.databaseConnection.write { db in
266+
let site = PersistedSite(
267+
id: siteID,
268+
lastCatalogIncrementalSyncDate: lastIncrementalSyncDate,
269+
lastCatalogFullSyncDate: lastFullSyncDate
270+
)
271+
try site.insert(db)
272+
}
273+
}
274+
}

0 commit comments

Comments
 (0)