diff --git a/Sources/tart/OCI/Registry.swift b/Sources/tart/OCI/Registry.swift index 083ea52..4a9ebe0 100644 --- a/Sources/tart/OCI/Registry.swift +++ b/Sources/tart/OCI/Registry.swift @@ -69,12 +69,15 @@ struct TokenResponse: Decodable, Authentication { let dateFormatter = ISO8601DateFormatter() dateFormatter.formatOptions = [.withInternetDateTime] dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + let fractionalDateFormatter = ISO8601DateFormatter() + fractionalDateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + fractionalDateFormatter.timeZone = TimeZone(secondsFromGMT: 0) decoder.dateDecodingStrategy = .custom { decoder in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) - return dateFormatter.date(from: dateString) ?? Date() + return dateFormatter.date(from: dateString) ?? fractionalDateFormatter.date(from: dateString) ?? Date() } var response = try decoder.decode(TokenResponse.self, from: fromData) diff --git a/Tests/TartTests/TokenResponseTests.swift b/Tests/TartTests/TokenResponseTests.swift index 6eb5ae2..91326b6 100644 --- a/Tests/TartTests/TokenResponseTests.swift +++ b/Tests/TartTests/TokenResponseTests.swift @@ -2,6 +2,17 @@ import XCTest @testable import tart final class TokenResponseTests: XCTestCase { + func testFractionalIssuedAt() throws { + for timestamp in ["1970-01-01T00:00:00.125Z", "1970-01-01T01:30:00.125+01:30", "1970-01-01T00:00:00.125000000Z"] { + let data = Data("{\"token\":\"test-token\",\"expires_in\":3600,\"issued_at\":\"\(timestamp)\"}".utf8) + let response = try TokenResponse.parse(fromData: data) + + XCTAssertEqual(response.issuedAt!.timeIntervalSince1970, 0.125, accuracy: 0.0001) + XCTAssertEqual(response.tokenExpiresAt.timeIntervalSince1970, 3600.125, accuracy: 0.0001) + XCTAssertFalse(response.isValid()) + } + } + func testBasic() throws { var expectedTokenExpiresAtRange = DateInterval() let tokenResponseRaw = Data("{\"token\":\"some token\"}".utf8)