Skip to content

Commit 1adaf09

Browse files
committed
Extract country/currency POS eligibility validator
Eliminates duplicate country/currency lists and validation logic between POSTabVisibilityChecker and POSTabEligibilityChecker. Changes: - Created POSCountryCurrencyValidator in Yosemite module - Centralized supported countries ([.US, .GB]) and currencies - Updated both eligibility checkers to use shared validator - Added comprehensive unit tests Benefits: - Single source of truth for country/currency rules - Easier to add new supported countries/currencies - Consistent validation across all POS eligibility checks Note: Type conversion from ValidationResult to SiteSettingsEligibilityState remains in each checker due to module boundaries and additional site setting cases beyond country/currency. Part of: WOOMOB-1287 Extract country/currency POS eligibility validator Eliminates duplicate country/currency lists and validation logic between POSTabVisibilityChecker and POSTabEligibilityChecker. Changes: - Created POSCountryCurrencyValidator in Yosemite module - Centralized supported countries ([.US, .GB]) and currencies - Updated both eligibility checkers to use shared validator - Added comprehensive unit tests Benefits: - Single source of truth for country/currency rules - Easier to add new supported countries/currencies - Consistent validation across all POS eligibility checks Note: Type conversion from ValidationResult to SiteSettingsEligibilityState remains in each checker due to module boundaries and additional site setting cases beyond country/currency. Part of: WOOMOB-1287
1 parent b6a03c3 commit 1adaf09

File tree

4 files changed

+229
-25
lines changed

4 files changed

+229
-25
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import Foundation
2+
import enum WooFoundation.CountryCode
3+
import enum WooFoundation.CurrencyCode
4+
5+
/// Validator for POS country and currency support
6+
/// Single source of truth for supported countries and currencies
7+
public enum POSCountryCurrencyValidator {
8+
/// Supported countries for POS feature
9+
public static let supportedCountries: [CountryCode] = [.US, .GB]
10+
11+
/// Supported currencies per country for POS feature
12+
public static let supportedCurrencies: [CountryCode: [CurrencyCode]] = [
13+
.US: [.USD],
14+
.GB: [.GBP]
15+
]
16+
17+
/// Validates if a country and currency combination is eligible for POS
18+
/// - Parameters:
19+
/// - countryCode: The store's country code
20+
/// - currencyCode: The store's currency code
21+
/// - Returns: Eligibility state with reason if ineligible
22+
public static func validate(countryCode: CountryCode, currencyCode: CurrencyCode) -> ValidationResult {
23+
// Check country first
24+
guard supportedCountries.contains(countryCode) else {
25+
return .ineligible(reason: .unsupportedCountry(supportedCountries: supportedCountries))
26+
}
27+
28+
// Check currency for the country
29+
let supportedCurrenciesForCountry = supportedCurrencies[countryCode] ?? []
30+
guard supportedCurrenciesForCountry.contains(currencyCode) else {
31+
return .ineligible(reason: .unsupportedCurrency(countryCode: countryCode, supportedCurrencies: supportedCurrenciesForCountry))
32+
}
33+
34+
return .eligible
35+
}
36+
}
37+
38+
// MARK: - Validation Result
39+
40+
public extension POSCountryCurrencyValidator {
41+
enum ValidationResult: Equatable {
42+
case eligible
43+
case ineligible(reason: IneligibleReason)
44+
}
45+
46+
enum IneligibleReason: Equatable {
47+
case unsupportedCountry(supportedCountries: [CountryCode])
48+
case unsupportedCurrency(countryCode: CountryCode, supportedCurrencies: [CurrencyCode])
49+
}
50+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import Foundation
2+
import Testing
3+
import enum WooFoundation.CountryCode
4+
import enum WooFoundation.CurrencyCode
5+
@testable import Yosemite
6+
7+
@Suite("POSCountryCurrencyValidator Tests")
8+
struct POSCountryCurrencyValidatorTests {
9+
10+
// MARK: - Eligible Combinations
11+
12+
@Test("US with USD is eligible")
13+
func testUSWithUSDIsEligible() {
14+
let result = POSCountryCurrencyValidator.validate(countryCode: .US, currencyCode: .USD)
15+
#expect(result == .eligible)
16+
}
17+
18+
@Test("GB with GBP is eligible")
19+
func testGBWithGBPIsEligible() {
20+
let result = POSCountryCurrencyValidator.validate(countryCode: .GB, currencyCode: .GBP)
21+
#expect(result == .eligible)
22+
}
23+
24+
// MARK: - Unsupported Countries
25+
26+
@Test("CA with USD is ineligible due to unsupported country")
27+
func testCAWithUSDIsIneligible() {
28+
let result = POSCountryCurrencyValidator.validate(countryCode: .CA, currencyCode: .USD)
29+
30+
guard case .ineligible(let reason) = result else {
31+
Issue.record("Expected ineligible result")
32+
return
33+
}
34+
35+
guard case .unsupportedCountry(let supportedCountries) = reason else {
36+
Issue.record("Expected unsupportedCountry reason")
37+
return
38+
}
39+
40+
#expect(supportedCountries.contains(.US))
41+
#expect(supportedCountries.contains(.GB))
42+
#expect(!supportedCountries.contains(.CA))
43+
}
44+
45+
@Test("AU with AUD is ineligible due to unsupported country")
46+
func testAUWithAUDIsIneligible() {
47+
let result = POSCountryCurrencyValidator.validate(countryCode: .AU, currencyCode: .AUD)
48+
49+
guard case .ineligible(let reason) = result else {
50+
Issue.record("Expected ineligible result")
51+
return
52+
}
53+
54+
guard case .unsupportedCountry = reason else {
55+
Issue.record("Expected unsupportedCountry reason")
56+
return
57+
}
58+
}
59+
60+
// MARK: - Unsupported Currencies for Supported Countries
61+
62+
@Test("US with EUR is ineligible due to unsupported currency")
63+
func testUSWithEURIsIneligible() {
64+
let result = POSCountryCurrencyValidator.validate(countryCode: .US, currencyCode: .EUR)
65+
66+
guard case .ineligible(let reason) = result else {
67+
Issue.record("Expected ineligible result")
68+
return
69+
}
70+
71+
guard case .unsupportedCurrency(let countryCode, let supportedCurrencies) = reason else {
72+
Issue.record("Expected unsupportedCurrency reason")
73+
return
74+
}
75+
76+
#expect(countryCode == .US)
77+
#expect(supportedCurrencies == [.USD])
78+
}
79+
80+
@Test("US with GBP is ineligible due to unsupported currency")
81+
func testUSWithGBPIsIneligible() {
82+
let result = POSCountryCurrencyValidator.validate(countryCode: .US, currencyCode: .GBP)
83+
84+
guard case .ineligible(let reason) = result else {
85+
Issue.record("Expected ineligible result")
86+
return
87+
}
88+
89+
guard case .unsupportedCurrency(let countryCode, let supportedCurrencies) = reason else {
90+
Issue.record("Expected unsupportedCurrency reason")
91+
return
92+
}
93+
94+
#expect(countryCode == .US)
95+
#expect(supportedCurrencies == [.USD])
96+
}
97+
98+
@Test("GB with USD is ineligible due to unsupported currency")
99+
func testGBWithUSDIsIneligible() {
100+
let result = POSCountryCurrencyValidator.validate(countryCode: .GB, currencyCode: .USD)
101+
102+
guard case .ineligible(let reason) = result else {
103+
Issue.record("Expected ineligible result")
104+
return
105+
}
106+
107+
guard case .unsupportedCurrency(let countryCode, let supportedCurrencies) = reason else {
108+
Issue.record("Expected unsupportedCurrency reason")
109+
return
110+
}
111+
112+
#expect(countryCode == .GB)
113+
#expect(supportedCurrencies == [.GBP])
114+
}
115+
116+
@Test("GB with EUR is ineligible due to unsupported currency")
117+
func testGBWithEURIsIneligible() {
118+
let result = POSCountryCurrencyValidator.validate(countryCode: .GB, currencyCode: .EUR)
119+
120+
guard case .ineligible(let reason) = result else {
121+
Issue.record("Expected ineligible result")
122+
return
123+
}
124+
125+
guard case .unsupportedCurrency(let countryCode, let supportedCurrencies) = reason else {
126+
Issue.record("Expected unsupportedCurrency reason")
127+
return
128+
}
129+
130+
#expect(countryCode == .GB)
131+
#expect(supportedCurrencies == [.GBP])
132+
}
133+
134+
// MARK: - Supported Countries List
135+
136+
@Test("Supported countries list contains US and GB")
137+
func testSupportedCountriesList() {
138+
let supportedCountries = POSCountryCurrencyValidator.supportedCountries
139+
#expect(supportedCountries.count == 2)
140+
#expect(supportedCountries.contains(.US))
141+
#expect(supportedCountries.contains(.GB))
142+
}
143+
144+
// MARK: - Supported Currencies Map
145+
146+
@Test("Supported currencies map has correct structure")
147+
func testSupportedCurrenciesMap() {
148+
let supportedCurrencies = POSCountryCurrencyValidator.supportedCurrencies
149+
150+
#expect(supportedCurrencies[.US] == [.USD])
151+
#expect(supportedCurrencies[.GB] == [.GBP])
152+
#expect(supportedCurrencies[.CA] == nil)
153+
}
154+
}

WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import class WooFoundation.VersionHelpers
2020
import protocol PointOfSale.POSEntryPointEligibilityCheckerProtocol
2121
import enum PointOfSale.POSEligibilityState
2222
import enum PointOfSale.POSIneligibleReason
23+
import enum Yosemite.POSCountryCurrencyValidator
2324

2425
final class POSTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
2526
private let siteID: Int64
@@ -209,20 +210,19 @@ private extension POSTabEligibilityChecker {
209210
}
210211

211212
func isEligibleFromCountryAndCurrencyCode(countryCode: CountryCode, currencyCode: CurrencyCode) -> SiteSettingsEligibilityState {
212-
let supportedCountries: [CountryCode] = [.US, .GB]
213-
let supportedCurrencies: [CountryCode: [CurrencyCode]] = [.US: [.USD],
214-
.GB: [.GBP]]
213+
let validationResult = POSCountryCurrencyValidator.validate(countryCode: countryCode, currencyCode: currencyCode)
215214

216-
// Checks country first.
217-
guard supportedCountries.contains(countryCode) else {
218-
return .ineligible(reason: .unsupportedCountry(supportedCountries: supportedCountries))
219-
}
220-
221-
let supportedCurrenciesForCountry = supportedCurrencies[countryCode] ?? []
222-
guard supportedCurrenciesForCountry.contains(currencyCode) else {
223-
return .ineligible(reason: .unsupportedCurrency(countryCode: countryCode, supportedCurrencies: supportedCurrenciesForCountry))
215+
switch validationResult {
216+
case .eligible:
217+
return .eligible
218+
case .ineligible(let reason):
219+
switch reason {
220+
case .unsupportedCountry(let supportedCountries):
221+
return .ineligible(reason: .unsupportedCountry(supportedCountries: supportedCountries))
222+
case .unsupportedCurrency(let countryCode, let supportedCurrencies):
223+
return .ineligible(reason: .unsupportedCurrency(countryCode: countryCode, supportedCurrencies: supportedCurrencies))
224+
}
224225
}
225-
return .eligible
226226
}
227227

228228
@MainActor

WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabVisibilityChecker.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import protocol Yosemite.StoresManager
1111
import class Yosemite.POSEligibilityService
1212
import enum Yosemite.FeatureFlagAction
1313
import class Yosemite.SiteAddress
14+
import enum Yosemite.POSCountryCurrencyValidator
1415

1516
final class POSTabVisibilityChecker: POSTabVisibilityCheckerProtocol {
1617
private let site: Site
@@ -106,20 +107,19 @@ private extension POSTabVisibilityChecker {
106107
}
107108

108109
func isEligibleFromCountryAndCurrencyCode(countryCode: CountryCode, currencyCode: CurrencyCode) -> SiteSettingsEligibilityState {
109-
let supportedCountries: [CountryCode] = [.US, .GB]
110-
let supportedCurrencies: [CountryCode: [CurrencyCode]] = [.US: [.USD],
111-
.GB: [.GBP]]
112-
113-
// Checks country first.
114-
guard supportedCountries.contains(countryCode) else {
115-
return .ineligible(reason: .unsupportedCountry(supportedCountries: supportedCountries))
116-
}
117-
118-
let supportedCurrenciesForCountry = supportedCurrencies[countryCode] ?? []
119-
guard supportedCurrenciesForCountry.contains(currencyCode) else {
120-
return .ineligible(reason: .unsupportedCurrency(countryCode: countryCode, supportedCurrencies: supportedCurrenciesForCountry))
110+
let validationResult = POSCountryCurrencyValidator.validate(countryCode: countryCode, currencyCode: currencyCode)
111+
112+
switch validationResult {
113+
case .eligible:
114+
return .eligible
115+
case .ineligible(let reason):
116+
switch reason {
117+
case .unsupportedCountry(let supportedCountries):
118+
return .ineligible(reason: .unsupportedCountry(supportedCountries: supportedCountries))
119+
case .unsupportedCurrency(let countryCode, let supportedCurrencies):
120+
return .ineligible(reason: .unsupportedCurrency(countryCode: countryCode, supportedCurrencies: supportedCurrencies))
121+
}
121122
}
122-
return .eligible
123123
}
124124
}
125125

0 commit comments

Comments
 (0)