@@ -21,53 +21,6 @@ struct PointOfSaleLocalSearchPurchasableItemFetchStrategyTests {
2121 }
2222 }
2323
24- // MARK: - Analytics Tests
25-
26- @Test ( " fetchProducts tracks analytics for first page " )
27- func test_fetchProducts_tracks_analytics_for_first_page( ) async throws {
28- // Given
29- let product = makeProduct ( id: 1 , name: " Test Product " )
30- try await insertProduct ( product)
31-
32- let strategy = PointOfSaleLocalSearchPurchasableItemFetchStrategy (
33- siteID: siteID,
34- searchTerm: searchTerm,
35- grdbManager: grdbManager,
36- variationsRemote: variationsRemote,
37- analytics: mockAnalytics
38- )
39-
40- // When
41- _ = try await strategy. fetchProducts ( pageNumber: 1 )
42-
43- // Then
44- #expect( mockAnalytics. spyLocalSearchMilliseconds != nil )
45- #expect( mockAnalytics. spyLocalSearchTotalItems == 1 )
46- }
47-
48- @Test ( " fetchProducts does not track analytics for subsequent pages " )
49- func test_fetchProducts_does_not_track_analytics_for_subsequent_pages( ) async throws {
50- // Given
51- for i in 1 ... 50 {
52- try await insertProduct ( makeProduct ( id: Int64 ( i) , name: " Test Product \( i) " ) )
53- }
54-
55- let strategy = PointOfSaleLocalSearchPurchasableItemFetchStrategy (
56- siteID: siteID,
57- searchTerm: searchTerm,
58- grdbManager: grdbManager,
59- variationsRemote: variationsRemote,
60- analytics: mockAnalytics
61- )
62-
63- // When
64- _ = try await strategy. fetchProducts ( pageNumber: 2 )
65-
66- // Then - no analytics should be tracked for page 2
67- #expect( mockAnalytics. spyLocalSearchMilliseconds == nil )
68- #expect( mockAnalytics. spyLocalSearchTotalItems == nil )
69- }
70-
7124 // MARK: - Search Functionality Tests
7225
7326 @Test ( " fetchProducts returns matching products " )
@@ -365,19 +318,13 @@ struct PointOfSaleLocalSearchPurchasableItemFetchStrategyTests {
365318
366319 // MARK: - Variations Tests
367320
368- @Test ( " fetchVariations delegates to remote " )
369- func test_fetchVariations_delegates_to_remote ( ) async throws {
321+ @Test ( " fetchVariations returns variations for parent product from local catalog " )
322+ func test_fetchVariations_returns_variations_from_local_catalog ( ) async throws {
370323 // Given
371324 let parentProductID : Int64 = 100
372- let expectedVariations = [
373- POSProductVariation . fake ( ) . copy ( productVariationID: 1 , productID: parentProductID) ,
374- POSProductVariation . fake ( ) . copy ( productVariationID: 2 , productID: parentProductID)
375- ]
376- variationsRemote. whenLoadingVariationsForPointOfSale ( thenReturn: . success( PagedItems (
377- items: expectedVariations,
378- hasMorePages: false ,
379- totalItems: 2
380- ) ) )
325+ try await insertProduct ( makeProduct ( id: parentProductID, name: " Variable Product " , productTypeKey: " variable " ) )
326+ try await insertVariation ( makeVariation ( id: 1 , productID: parentProductID, price: " 10.00 " ) )
327+ try await insertVariation ( makeVariation ( id: 2 , productID: parentProductID, price: " 15.00 " ) )
381328
382329 let strategy = PointOfSaleLocalSearchPurchasableItemFetchStrategy (
383330 siteID: siteID,
@@ -394,6 +341,127 @@ struct PointOfSaleLocalSearchPurchasableItemFetchStrategyTests {
394341 #expect( result. items. count == 2 )
395342 #expect( result. items [ 0 ] . productVariationID == 1 )
396343 #expect( result. items [ 1 ] . productVariationID == 2 )
344+ #expect( result. totalItems == 2 )
345+ #expect( result. hasMorePages == false )
346+ }
347+
348+ @Test ( " fetchVariations filters out downloadable variations " )
349+ func test_fetchVariations_filters_out_downloadable_variations( ) async throws {
350+ // Given
351+ let parentProductID : Int64 = 100
352+ try await insertProduct ( makeProduct ( id: parentProductID, name: " Variable Product " , productTypeKey: " variable " ) )
353+ try await insertVariation ( makeVariation ( id: 1 , productID: parentProductID, downloadable: false ) )
354+ try await insertVariation ( makeVariation ( id: 2 , productID: parentProductID, downloadable: true ) )
355+
356+ let strategy = PointOfSaleLocalSearchPurchasableItemFetchStrategy (
357+ siteID: siteID,
358+ searchTerm: searchTerm,
359+ grdbManager: grdbManager,
360+ variationsRemote: variationsRemote,
361+ analytics: mockAnalytics
362+ )
363+
364+ // When
365+ let result = try await strategy. fetchVariations ( parentProductID: parentProductID, pageNumber: 1 )
366+
367+ // Then
368+ #expect( result. items. count == 1 )
369+ #expect( result. items. first? . productVariationID == 1 )
370+ #expect( result. items. first? . downloadable == false )
371+ }
372+
373+ @Test ( " fetchVariations returns empty result when no variations " )
374+ func test_fetchVariations_returns_empty_when_no_variations( ) async throws {
375+ // Given
376+ let parentProductID : Int64 = 100
377+ try await insertProduct ( makeProduct ( id: parentProductID, name: " Simple Product " , productTypeKey: " simple " ) )
378+
379+ let strategy = PointOfSaleLocalSearchPurchasableItemFetchStrategy (
380+ siteID: siteID,
381+ searchTerm: searchTerm,
382+ grdbManager: grdbManager,
383+ variationsRemote: variationsRemote,
384+ analytics: mockAnalytics
385+ )
386+
387+ // When
388+ let result = try await strategy. fetchVariations ( parentProductID: parentProductID, pageNumber: 1 )
389+
390+ // Then
391+ #expect( result. items. isEmpty)
392+ #expect( result. totalItems == 0 )
393+ #expect( result. hasMorePages == false )
394+ }
395+
396+ @Test ( " fetchVariations handles pagination correctly " )
397+ func test_fetchVariations_handles_pagination_correctly( ) async throws {
398+ // Given
399+ let parentProductID : Int64 = 100
400+ try await insertProduct ( makeProduct ( id: parentProductID, name: " Variable Product " , productTypeKey: " variable " ) )
401+
402+ // Insert 30 variations
403+ for i in 1 ... 30 {
404+ try await insertVariation ( makeVariation ( id: Int64 ( i) , productID: parentProductID, price: " \( i) .00 " ) )
405+ }
406+
407+ let strategy = PointOfSaleLocalSearchPurchasableItemFetchStrategy (
408+ siteID: siteID,
409+ searchTerm: searchTerm,
410+ grdbManager: grdbManager,
411+ variationsRemote: variationsRemote,
412+ analytics: mockAnalytics,
413+ pageSize: 10
414+ )
415+
416+ // When
417+ let page1 = try await strategy. fetchVariations ( parentProductID: parentProductID, pageNumber: 1 )
418+ let page2 = try await strategy. fetchVariations ( parentProductID: parentProductID, pageNumber: 2 )
419+ let page3 = try await strategy. fetchVariations ( parentProductID: parentProductID, pageNumber: 3 )
420+ let page4 = try await strategy. fetchVariations ( parentProductID: parentProductID, pageNumber: 4 )
421+
422+ // Then
423+ #expect( page1. items. count == 10 )
424+ #expect( page1. hasMorePages == true )
425+ #expect( page1. totalItems == 30 )
426+
427+ #expect( page2. items. count == 10 )
428+ #expect( page2. hasMorePages == true )
429+ #expect( page2. totalItems == 30 )
430+
431+ #expect( page3. items. count == 10 )
432+ #expect( page3. hasMorePages == false )
433+ #expect( page3. totalItems == 30 )
434+
435+ #expect( page4. items. isEmpty)
436+ #expect( page4. hasMorePages == false )
437+ #expect( page4. totalItems == 30 )
438+ }
439+
440+ @Test ( " fetchVariations only returns variations for specified parent " )
441+ func test_fetchVariations_respects_parent_product_isolation( ) async throws {
442+ // Given
443+ let parentProduct1ID : Int64 = 100
444+ let parentProduct2ID : Int64 = 200
445+ try await insertProduct ( makeProduct ( id: parentProduct1ID, name: " Variable Product 1 " , productTypeKey: " variable " ) )
446+ try await insertProduct ( makeProduct ( id: parentProduct2ID, name: " Variable Product 2 " , productTypeKey: " variable " ) )
447+ try await insertVariation ( makeVariation ( id: 1 , productID: parentProduct1ID) )
448+ try await insertVariation ( makeVariation ( id: 2 , productID: parentProduct2ID) )
449+
450+ let strategy = PointOfSaleLocalSearchPurchasableItemFetchStrategy (
451+ siteID: siteID,
452+ searchTerm: searchTerm,
453+ grdbManager: grdbManager,
454+ variationsRemote: variationsRemote,
455+ analytics: mockAnalytics
456+ )
457+
458+ // When
459+ let result = try await strategy. fetchVariations ( parentProductID: parentProduct1ID, pageNumber: 1 )
460+
461+ // Then
462+ #expect( result. items. count == 1 )
463+ #expect( result. items. first? . productVariationID == 1 )
464+ #expect( result. items. first? . productID == parentProduct1ID)
397465 }
398466
399467 // MARK: - Helper Methods
@@ -430,4 +498,33 @@ struct PointOfSaleLocalSearchPurchasableItemFetchStrategyTests {
430498 try product. insert ( db)
431499 }
432500 }
501+
502+ private func makeVariation(
503+ id: Int64 ,
504+ productID: Int64 ,
505+ siteID: Int64 ? = nil ,
506+ sku: String ? = nil ,
507+ price: String = " 10.00 " ,
508+ downloadable: Bool = false
509+ ) -> PersistedProductVariation {
510+ PersistedProductVariation (
511+ id: id,
512+ siteID: siteID ?? self . siteID,
513+ productID: productID,
514+ sku: sku,
515+ globalUniqueID: nil ,
516+ price: price,
517+ downloadable: downloadable,
518+ fullDescription: nil ,
519+ manageStock: false ,
520+ stockQuantity: nil ,
521+ stockStatusKey: " instock "
522+ )
523+ }
524+
525+ private func insertVariation( _ variation: PersistedProductVariation ) async throws {
526+ try await grdbManager. databaseConnection. write { db in
527+ try variation. insert ( db)
528+ }
529+ }
433530}
0 commit comments