Skip to content

Commit

Permalink
Resolve issue where Dates passed as input that contain a microsecond …
Browse files Browse the repository at this point in the history
…portion would cause API exceptions
  • Loading branch information
alexanderjordanbaker committed Jul 16, 2024
1 parent 1cea28d commit d9de068
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 6 deletions.
10 changes: 7 additions & 3 deletions Sources/AppStoreServerLibrary/Utility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ internal func getJsonDecoder() -> JSONDecoder {
}

internal func getJsonEncoder() -> JSONEncoder {
let decoder = JSONEncoder()
decoder.dateEncodingStrategy = .millisecondsSince1970
return decoder
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .custom({ date, e in
// To encode the same as millisecondsSince1970, however truncating the decimal part
var container = e.singleValueContainer()
try container.encode((date.timeIntervalSince1970 * 1000.0).rounded(.towardZero))
})
return encoder
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,25 @@ final class AppStoreServerAPIClientTests: XCTestCase {
TestingUtility.confirmCodableInternallyConsistent(notificationHistoryResponse)
}

public func testGetNotificationHistoryWithMicrosecondValues() async throws {
let client = try getClientWithBody("resources/models/getNotificationHistoryResponse.json") { request, body in
let decodedJson = try! JSONSerialization.jsonObject(with: body!) as! [String: Any]
XCTAssertEqual(1698148900000, decodedJson["startDate"] as! Int)
XCTAssertEqual(1698148950000, decodedJson["endDate"] as! Int)
}

let notificationHistoryRequest = NotificationHistoryRequest(
startDate: Date(timeIntervalSince1970: 1698148900).advanced(by: 0.000_9), // 900 microseconds
endDate: Date(timeIntervalSince1970: 1698148950).advanced(by: 0.000_001), // 1 microsecond
notificationType: NotificationTypeV2.subscribed,
notificationSubtype: Subtype.initialBuy,
transactionId: "999733843",
onlyFailures: true
)

let _ = await client.getNotificationHistory(paginationToken: "a036bc0e-52b8-4bee-82fc-8c24cb6715d6", notificationHistoryRequest: notificationHistoryRequest)
}

public func testGetTransactionHistoryV1() async throws {
let client = try getClientWithBody("resources/models/transactionHistoryResponse.json") { request, body in
XCTAssertEqual(.GET, request.method)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
XCTAssertNil(appTransaction.preorderDate)
XCTAssertEqual(.xcode, appTransaction.receiptType)
XCTAssertEqual("Xcode", appTransaction.rawReceiptType)
TestingUtility.confirmCodableInternallyConsistent(appTransaction)
confirmCodableInternallyConsistentForXcode(appTransaction)
}

public func testXcodeSignedTransaction() async throws {
Expand Down Expand Up @@ -72,7 +72,7 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
XCTAssertEqual("143441", transaction.storefrontId)
XCTAssertEqual(TransactionReason.purchase, transaction.transactionReason)
XCTAssertEqual("PURCHASE", transaction.rawTransactionReason)
TestingUtility.confirmCodableInternallyConsistent(transaction)
confirmCodableInternallyConsistentForXcode(transaction)
}

public func testXcodeSignedRenewalInfo() async throws {
Expand Down Expand Up @@ -102,7 +102,7 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
XCTAssertEqual("Xcode", renewalInfo.rawEnvironment)
compareXcodeDates(Date(timeIntervalSince1970: 1697679936.049), renewalInfo.recentSubscriptionStartDate)
compareXcodeDates(Date(timeIntervalSince1970: 1700358336.049), renewalInfo.renewalDate)
TestingUtility.confirmCodableInternallyConsistent(renewalInfo)
confirmCodableInternallyConsistentForXcode(renewalInfo)
}

public func testXcodeSignedAppTransactionWithProductionEnvironment() async throws {
Expand All @@ -122,4 +122,13 @@ final class XcodeSignedDataVerifierTests: XCTestCase {
private func compareXcodeDates(_ first: Date, _ second: Date?) {
XCTAssertEqual(floor((first.timeIntervalSince1970 * 1000)), floor(((second?.timeIntervalSince1970 ?? 0.0) * 1000)))
}

private func confirmCodableInternallyConsistentForXcode<T>(_ codable: T) where T : Codable, T : Equatable {
let type = type(of: codable)
let encoder = JSONEncoder()
// Xcode receipts contain a decimal value, we encode the value as encoded in those receipts
encoder.dateEncodingStrategy = .millisecondsSince1970
let parsedValue = try! getJsonDecoder().decode(type, from: encoder.encode(codable))
XCTAssertEqual(parsedValue, codable)
}
}

0 comments on commit d9de068

Please sign in to comment.