Skip to content

Commit 21fff20

Browse files
Merge pull request #62 from alexanderjordanbaker/ResolveMicrosecondDates
Resolve issue where Dates passed as input that contain a microsecond …
2 parents ba0ae3a + d9de068 commit 21fff20

File tree

3 files changed

+38
-6
lines changed

3 files changed

+38
-6
lines changed

Sources/AppStoreServerLibrary/Utility.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ internal func getJsonDecoder() -> JSONDecoder {
1919
}
2020

2121
internal func getJsonEncoder() -> JSONEncoder {
22-
let decoder = JSONEncoder()
23-
decoder.dateEncodingStrategy = .millisecondsSince1970
24-
return decoder
22+
let encoder = JSONEncoder()
23+
encoder.dateEncodingStrategy = .custom({ date, e in
24+
// To encode the same as millisecondsSince1970, however truncating the decimal part
25+
var container = e.singleValueContainer()
26+
try container.encode((date.timeIntervalSince1970 * 1000.0).rounded(.towardZero))
27+
})
28+
return encoder
2529
}

Tests/AppStoreServerLibraryTests/AppStoreServerAPIClientTests.swift

+19
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,25 @@ final class AppStoreServerAPIClientTests: XCTestCase {
246246
TestingUtility.confirmCodableInternallyConsistent(notificationHistoryResponse)
247247
}
248248

249+
public func testGetNotificationHistoryWithMicrosecondValues() async throws {
250+
let client = try getClientWithBody("resources/models/getNotificationHistoryResponse.json") { request, body in
251+
let decodedJson = try! JSONSerialization.jsonObject(with: body!) as! [String: Any]
252+
XCTAssertEqual(1698148900000, decodedJson["startDate"] as! Int)
253+
XCTAssertEqual(1698148950000, decodedJson["endDate"] as! Int)
254+
}
255+
256+
let notificationHistoryRequest = NotificationHistoryRequest(
257+
startDate: Date(timeIntervalSince1970: 1698148900).advanced(by: 0.000_9), // 900 microseconds
258+
endDate: Date(timeIntervalSince1970: 1698148950).advanced(by: 0.000_001), // 1 microsecond
259+
notificationType: NotificationTypeV2.subscribed,
260+
notificationSubtype: Subtype.initialBuy,
261+
transactionId: "999733843",
262+
onlyFailures: true
263+
)
264+
265+
let _ = await client.getNotificationHistory(paginationToken: "a036bc0e-52b8-4bee-82fc-8c24cb6715d6", notificationHistoryRequest: notificationHistoryRequest)
266+
}
267+
249268
public func testGetTransactionHistoryV1() async throws {
250269
let client = try getClientWithBody("resources/models/transactionHistoryResponse.json") { request, body in
251270
XCTAssertEqual(.GET, request.method)

Tests/AppStoreServerLibraryTests/XcodeSignedDataVerifierTests.swift

+12-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
3030
XCTAssertNil(appTransaction.preorderDate)
3131
XCTAssertEqual(.xcode, appTransaction.receiptType)
3232
XCTAssertEqual("Xcode", appTransaction.rawReceiptType)
33-
TestingUtility.confirmCodableInternallyConsistent(appTransaction)
33+
confirmCodableInternallyConsistentForXcode(appTransaction)
3434
}
3535

3636
public func testXcodeSignedTransaction() async throws {
@@ -72,7 +72,7 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
7272
XCTAssertEqual("143441", transaction.storefrontId)
7373
XCTAssertEqual(TransactionReason.purchase, transaction.transactionReason)
7474
XCTAssertEqual("PURCHASE", transaction.rawTransactionReason)
75-
TestingUtility.confirmCodableInternallyConsistent(transaction)
75+
confirmCodableInternallyConsistentForXcode(transaction)
7676
}
7777

7878
public func testXcodeSignedRenewalInfo() async throws {
@@ -102,7 +102,7 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
102102
XCTAssertEqual("Xcode", renewalInfo.rawEnvironment)
103103
compareXcodeDates(Date(timeIntervalSince1970: 1697679936.049), renewalInfo.recentSubscriptionStartDate)
104104
compareXcodeDates(Date(timeIntervalSince1970: 1700358336.049), renewalInfo.renewalDate)
105-
TestingUtility.confirmCodableInternallyConsistent(renewalInfo)
105+
confirmCodableInternallyConsistentForXcode(renewalInfo)
106106
}
107107

108108
public func testXcodeSignedAppTransactionWithProductionEnvironment() async throws {
@@ -122,4 +122,13 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
122122
private func compareXcodeDates(_ first: Date, _ second: Date?) {
123123
XCTAssertEqual(floor((first.timeIntervalSince1970 * 1000)), floor(((second?.timeIntervalSince1970 ?? 0.0) * 1000)))
124124
}
125+
126+
private func confirmCodableInternallyConsistentForXcode<T>(_ codable: T) where T : Codable, T : Equatable {
127+
let type = type(of: codable)
128+
let encoder = JSONEncoder()
129+
// Xcode receipts contain a decimal value, we encode the value as encoded in those receipts
130+
encoder.dateEncodingStrategy = .millisecondsSince1970
131+
let parsedValue = try! getJsonDecoder().decode(type, from: encoder.encode(codable))
132+
XCTAssertEqual(parsedValue, codable)
133+
}
125134
}

0 commit comments

Comments
 (0)