diff --git a/Sources/Auth/Defaults.swift b/Sources/Auth/Defaults.swift index f293fe6b..309f5c3d 100644 --- a/Sources/Auth/Defaults.swift +++ b/Sources/Auth/Defaults.swift @@ -10,46 +10,23 @@ import Foundation import Helpers extension AuthClient.Configuration { - private static let supportedDateFormatters: [UncheckedSendable] = [ - ISO8601DateFormatter.iso8601WithFractionalSeconds, - ISO8601DateFormatter.iso8601, - ] - /// The default JSONEncoder instance used by the ``AuthClient``. public static let jsonEncoder: JSONEncoder = { - let encoder = JSONEncoder() + let encoder = JSONEncoder.supabase() encoder.keyEncodingStrategy = .convertToSnakeCase - encoder.dateEncodingStrategy = .custom { date, encoder in - let string = ISO8601DateFormatter.iso8601WithFractionalSeconds.value.string(from: date) - var container = encoder.singleValueContainer() - try container.encode(string) - } return encoder }() /// The default JSONDecoder instance used by the ``AuthClient``. public static let jsonDecoder: JSONDecoder = { - let decoder = JSONDecoder() + let decoder = JSONDecoder.supabase() decoder.keyDecodingStrategy = .convertFromSnakeCase - decoder.dateDecodingStrategy = .custom { decoder in - let container = try decoder.singleValueContainer() - let string = try container.decode(String.self) - - for formatter in supportedDateFormatters { - if let date = formatter.value.date(from: string) { - return date - } - } - - throw DecodingError.dataCorruptedError( - in: container, debugDescription: "Invalid date format: \(string)" - ) - } return decoder }() + /// The default headers used by the ``AuthClient``. public static let defaultHeaders: [String: String] = [ - "X-Client-Info": "auth-swift/\(version)", + "X-Client-Info": "auth-swift/\(version)" ] /// The default ``AuthFlowType`` used when initializing a ``AuthClient`` instance. diff --git a/Sources/Auth/Internal/SessionStorage.swift b/Sources/Auth/Internal/SessionStorage.swift index a75338e2..be2cf77c 100644 --- a/Sources/Auth/Internal/SessionStorage.swift +++ b/Sources/Auth/Internal/SessionStorage.swift @@ -34,6 +34,7 @@ extension SessionStorage { let migrations: [StorageMigration] = [ .sessionNewKey(clientID: clientID), .storeSessionDirectly(clientID: clientID), + .useDefaultEncoder(clientID: clientID), ] var key: String { @@ -46,14 +47,16 @@ extension SessionStorage { do { try migration.run() } catch { - logger?.error("Storage migration failed: \(error.localizedDescription)") + logger?.error( + "Storage migration '\(migration.name)' failed: \(error.localizedDescription)" + ) } } do { let storedData = try storage.retrieve(key: key) return try storedData.flatMap { - try AuthClient.Configuration.jsonDecoder.decode(Session.self, from: $0) + try JSONDecoder().decode(Session.self, from: $0) } } catch { logger?.error("Failed to retrieve session: \(error.localizedDescription)") @@ -64,7 +67,7 @@ extension SessionStorage { do { try storage.store( key: key, - value: AuthClient.Configuration.jsonEncoder.encode(session) + value: JSONEncoder().encode(session) ) } catch { logger?.error("Failed to store session: \(error.localizedDescription)") @@ -82,6 +85,7 @@ extension SessionStorage { } struct StorageMigration { + var name: String var run: @Sendable () throws -> Void } @@ -89,7 +93,7 @@ extension StorageMigration { /// Migrate stored session from `supabase.session` key to the custom provided storage key /// or the default `supabase.auth.token` key. static func sessionNewKey(clientID: AuthClientID) -> StorageMigration { - StorageMigration { + StorageMigration(name: "sessionNewKey") { let storage = Dependencies[clientID].configuration.localStorage let newKey = SessionStorage.key(clientID) @@ -117,16 +121,38 @@ extension StorageMigration { var expirationDate: Date } - return StorageMigration { + return StorageMigration(name: "storeSessionDirectly") { let storage = Dependencies[clientID].configuration.localStorage let key = SessionStorage.key(clientID) if let data = try? storage.retrieve(key: key), - let storedSession = try? AuthClient.Configuration.jsonDecoder.decode(StoredSession.self, from: data) + let storedSession = try? AuthClient.Configuration.jsonDecoder.decode( + StoredSession.self, + from: data + ) { let session = try AuthClient.Configuration.jsonEncoder.encode(storedSession.session) try storage.store(key: key, value: session) } } } + + static func useDefaultEncoder(clientID: AuthClientID) -> StorageMigration { + StorageMigration(name: "useDefaultEncoder") { + let storage = Dependencies[clientID].configuration.localStorage + let key = SessionStorage.key(clientID) + + let storedData = try? storage.retrieve(key: key) + let sessionUsingOldDecoder = storedData.flatMap { + try? AuthClient.Configuration.jsonDecoder.decode(Session.self, from: $0) + } + + if let sessionUsingOldDecoder { + try storage.store( + key: key, + value: JSONEncoder().encode(sessionUsingOldDecoder) + ) + } + } + } } diff --git a/Sources/Helpers/AnyJSON/AnyJSON+Codable.swift b/Sources/Helpers/AnyJSON/AnyJSON+Codable.swift index f3b1f997..d4255fca 100644 --- a/Sources/Helpers/AnyJSON/AnyJSON+Codable.swift +++ b/Sources/Helpers/AnyJSON/AnyJSON+Codable.swift @@ -10,37 +10,12 @@ import Foundation extension AnyJSON { /// The decoder instance used for transforming AnyJSON to some Codable type. public static let decoder: JSONDecoder = { - let decoder = JSONDecoder() - decoder.dataDecodingStrategy = .base64 - decoder.dateDecodingStrategy = .custom { decoder in - let container = try decoder.singleValueContainer() - let dateString = try container.decode(String.self) - - let date = - ISO8601DateFormatter.iso8601WithFractionalSeconds.value.date(from: dateString) - ?? ISO8601DateFormatter.iso8601.value.date(from: dateString) - - guard let decodedDate = date else { - throw DecodingError.dataCorruptedError( - in: container, debugDescription: "Invalid date format: \(dateString)" - ) - } - - return decodedDate - } - return decoder + JSONDecoder.supabase() }() /// The encoder instance used for transforming AnyJSON to some Codable type. public static let encoder: JSONEncoder = { - let encoder = JSONEncoder() - encoder.dataEncodingStrategy = .base64 - encoder.dateEncodingStrategy = .custom { date, encoder in - let string = ISO8601DateFormatter.iso8601WithFractionalSeconds.value.string(from: date) - var container = encoder.singleValueContainer() - try container.encode(string) - } - return encoder + JSONEncoder.supabase() }() } diff --git a/Sources/Helpers/Codable.swift b/Sources/Helpers/Codable.swift index 88a73e69..e6b38877 100644 --- a/Sources/Helpers/Codable.swift +++ b/Sources/Helpers/Codable.swift @@ -9,22 +9,15 @@ import ConcurrencyExtras import Foundation extension JSONDecoder { - private static let supportedDateFormatters: [UncheckedSendable] = [ - ISO8601DateFormatter.iso8601WithFractionalSeconds, - ISO8601DateFormatter.iso8601, - ] - /// Default `JSONDecoder` for decoding types from Supabase. - package static let `default`: JSONDecoder = { + package static func supabase() -> JSONDecoder { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .custom { decoder in let container = try decoder.singleValueContainer() let string = try container.decode(String.self) - for formatter in supportedDateFormatters { - if let date = formatter.value.date(from: string) { - return date - } + if let date = string.date { + return date } throw DecodingError.dataCorruptedError( @@ -32,5 +25,17 @@ extension JSONDecoder { ) } return decoder - }() + } +} +extension JSONEncoder { + /// Default `JSONEncoder` for encoding types to Supabase. + package static func supabase() -> JSONEncoder { + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .custom { date, encoder in + var container = encoder.singleValueContainer() + let string = date.iso8601String + try container.encode(string) + } + return encoder + } } diff --git a/Sources/Helpers/DateFormatter.swift b/Sources/Helpers/DateFormatter.swift index 5d5acc84..a15f9936 100644 --- a/Sources/Helpers/DateFormatter.swift +++ b/Sources/Helpers/DateFormatter.swift @@ -5,19 +5,72 @@ // Created by Guilherme Souza on 28/12/23. // -import ConcurrencyExtras import Foundation -extension ISO8601DateFormatter { - package static let iso8601: UncheckedSendable = { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = [.withInternetDateTime] - return UncheckedSendable(formatter) +extension DateFormatter { + fileprivate static func iso8601(includingFractionalSeconds: Bool) -> DateFormatter { + includingFractionalSeconds ? iso8601Fractional : iso8601Whole + } + + fileprivate static let iso8601Fractional: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + return formatter }() - package static let iso8601WithFractionalSeconds: UncheckedSendable = { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] - return UncheckedSendable(formatter) + fileprivate static let iso8601Whole: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + return formatter }() } + +@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) +extension Date.ISO8601FormatStyle { + fileprivate func currentTimestamp(includingFractionalSeconds: Bool) -> Self { + year().month().day() + .dateTimeSeparator(.standard) + .time(includingFractionalSeconds: includingFractionalSeconds) + } +} + +extension Date { + package var iso8601String: String { + if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) { + return formatted(.iso8601.currentTimestamp(includingFractionalSeconds: true)) + } else { + return DateFormatter.iso8601(includingFractionalSeconds: true).string(from: self) + } + } +} + +extension String { + package var date: Date? { + if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) { + if let date = try? Date( + self, + strategy: .iso8601.currentTimestamp(includingFractionalSeconds: true) + ) { + return date + } + return try? Date( + self, + strategy: .iso8601.currentTimestamp(includingFractionalSeconds: false) + ) + } else { + guard + let date = DateFormatter.iso8601(includingFractionalSeconds: true).date(from: self) + ?? DateFormatter.iso8601(includingFractionalSeconds: false).date(from: self) + else { + return nil + } + return date + } + } +} diff --git a/Sources/Helpers/SupabaseLogger.swift b/Sources/Helpers/SupabaseLogger.swift index 52d9c4ad..313b5a92 100644 --- a/Sources/Helpers/SupabaseLogger.swift +++ b/Sources/Helpers/SupabaseLogger.swift @@ -55,8 +55,7 @@ public struct SupabaseLogMessage: Codable, CustomStringConvertible, Sendable { } public var description: String { - let date = ISO8601DateFormatter.iso8601.value.string( - from: Date(timeIntervalSince1970: timestamp)) + let date = Date(timeIntervalSince1970: timestamp).iso8601String let file = fileID.split(separator: ".", maxSplits: 1).first.map(String.init) ?? fileID var description = "\(date) [\(level)] [\(system)] [\(file).\(function):\(line)] \(message)" if !additionalContext.isEmpty { diff --git a/Sources/PostgREST/Defaults.swift b/Sources/PostgREST/Defaults.swift index 238047a2..bceff89c 100644 --- a/Sources/PostgREST/Defaults.swift +++ b/Sources/PostgREST/Defaults.swift @@ -12,39 +12,18 @@ import Helpers let version = Helpers.version extension PostgrestClient.Configuration { - private static let supportedDateFormatters: [UncheckedSendable] = [ - ISO8601DateFormatter.iso8601WithFractionalSeconds, - ISO8601DateFormatter.iso8601, - ] - /// The default `JSONDecoder` instance for ``PostgrestClient`` responses. - public static let jsonDecoder = { () -> JSONDecoder in - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .custom { decoder in - let container = try decoder.singleValueContainer() - let string = try container.decode(String.self) - - for formatter in supportedDateFormatters { - if let date = formatter.value.date(from: string) { - return date - } - } - - throw DecodingError.dataCorruptedError( - in: container, debugDescription: "Invalid date format: \(string)" - ) - } - return decoder + public static let jsonDecoder: JSONDecoder = { + JSONDecoder.supabase() }() /// The default `JSONEncoder` instance for ``PostgrestClient`` requests. - public static let jsonEncoder = { () -> JSONEncoder in - let encoder = JSONEncoder() - encoder.dateEncodingStrategy = .iso8601 - return encoder + public static let jsonEncoder: JSONEncoder = { + JSONEncoder.supabase() }() + /// The default headers for ``PostgrestClient`` requests. public static let defaultHeaders: [String: String] = [ - "X-Client-Info": "postgrest-swift/\(version)", + "X-Client-Info": "postgrest-swift/\(version)" ] } diff --git a/Sources/Storage/Codable.swift b/Sources/Storage/Codable.swift index 54916cdf..37995c77 100644 --- a/Sources/Storage/Codable.swift +++ b/Sources/Storage/Codable.swift @@ -21,7 +21,7 @@ extension JSONEncoder { extension JSONDecoder { @available(*, deprecated, message: "Access to storage decoder is going to be removed.") - public static var defaultStorageDecoder: JSONDecoder { - .default - } + public static let defaultStorageDecoder: JSONDecoder = { + JSONDecoder.supabase() + }() } diff --git a/Tests/AuthTests/AuthClientTests.swift b/Tests/AuthTests/AuthClientTests.swift index abb24d79..259ee839 100644 --- a/Tests/AuthTests/AuthClientTests.swift +++ b/Tests/AuthTests/AuthClientTests.swift @@ -28,11 +28,11 @@ final class AuthClientTests: XCTestCase { var sut: AuthClient! #if !os(Windows) && !os(Linux) && !os(Android) - override func invokeTest() { - withMainSerialExecutor { - super.invokeTest() + override func invokeTest() { + withMainSerialExecutor { + super.invokeTest() + } } - } #endif override func setUp() { @@ -71,7 +71,7 @@ final class AuthClientTests: XCTestCase { } } - XCTAssertEqual(events.value, [.initialSession]) + expectNoDifference(events.value, [.initialSession]) handle.remove() } @@ -131,7 +131,7 @@ final class AuthClientTests: XCTestCase { } let events = await eventsTask.value.map(\.event) - XCTAssertEqual(events, [.initialSession, .signedOut]) + expectNoDifference(events, [.initialSession, .signedOut]) } func testSignOutWithOthersScopeShouldNotRemoveLocalSession() async throws { @@ -343,11 +343,11 @@ final class AuthClientTests: XCTestCase { let events = await eventsTask.value.map(\.event) let sessions = await eventsTask.value.map(\.session) - XCTAssertEqual(events, [.initialSession, .signedIn]) - XCTAssertEqual(sessions, [nil, session]) + expectNoDifference(events, [.initialSession, .signedIn]) + expectNoDifference(sessions, [nil, session]) - XCTAssertEqual(sut.currentSession, session) - XCTAssertEqual(sut.currentUser, session.user) + expectNoDifference(sut.currentSession, session) + expectNoDifference(sut.currentUser, session.user) } func testSignInWithOAuth() async throws { @@ -392,7 +392,7 @@ final class AuthClientTests: XCTestCase { let events = await eventsTask.value.map(\.event) - XCTAssertEqual(events, [.initialSession, .signedIn]) + expectNoDifference(events, [.initialSession, .signedIn]) } func testGetLinkIdentityURL() async throws { @@ -480,7 +480,7 @@ final class AuthClientTests: XCTestCase { try await sut.linkIdentity(provider: .github) - XCTAssertEqual(receivedURL.value?.absoluteString, url) + expectNoDifference(receivedURL.value?.absoluteString, url) } func testAdminListUsers() async throws { @@ -511,9 +511,9 @@ final class AuthClientTests: XCTestCase { let sut = makeSUT() let response = try await sut.admin.listUsers() - XCTAssertEqual(response.total, 669) - XCTAssertEqual(response.nextPage, 2) - XCTAssertEqual(response.lastPage, 14) + expectNoDifference(response.total, 669) + expectNoDifference(response.nextPage, 2) + expectNoDifference(response.lastPage, 14) } func testAdminListUsers_noNextPage() async throws { @@ -543,9 +543,9 @@ final class AuthClientTests: XCTestCase { let sut = makeSUT() let response = try await sut.admin.listUsers() - XCTAssertEqual(response.total, 669) + expectNoDifference(response.total, 669) XCTAssertNil(response.nextPage) - XCTAssertEqual(response.lastPage, 14) + expectNoDifference(response.lastPage, 14) } func testSessionFromURL_withError() async throws { @@ -809,7 +809,7 @@ final class AuthClientTests: XCTestCase { redirectTo: URL(string: "https://dummy-url.com/redirect")!, queryParams: [("extra_key", "extra_value")] ) - XCTAssertEqual( + expectNoDifference( url, URL( string: @@ -884,7 +884,7 @@ final class AuthClientTests: XCTestCase { refreshToken: "refreshtoken", user: User(fromMockNamed: "user") ) - XCTAssertEqual(session, expectedSession) + expectNoDifference(session, expectedSession) } #endif @@ -928,7 +928,7 @@ final class AuthClientTests: XCTestCase { do { try await sut.session(from: url) } catch let AuthError.implicitGrantRedirect(message) { - XCTAssertEqual(message, "Not a valid implicit grant flow URL: \(url)") + expectNoDifference(message, "Not a valid implicit grant flow URL: \(url)") } } @@ -943,7 +943,7 @@ final class AuthClientTests: XCTestCase { do { try await sut.session(from: url) } catch let AuthError.implicitGrantRedirect(message) { - XCTAssertEqual(message, "Invalid code") + expectNoDifference(message, "Invalid code") } } @@ -983,7 +983,7 @@ final class AuthClientTests: XCTestCase { try await sut.session(from: url) let events = await eventsTask.value - XCTAssertEqual(events, [.initialSession, .signedIn, .passwordRecovery]) + expectNoDifference(events, [.initialSession, .signedIn, .passwordRecovery]) } func testSessionWithURL_pkceFlow_error() async throws { @@ -997,9 +997,9 @@ final class AuthClientTests: XCTestCase { do { try await sut.session(from: url) } catch let AuthError.pkceGrantCodeExchange(message, error, code) { - XCTAssertEqual(message, "Invalid code") - XCTAssertEqual(error, "invalid_grant") - XCTAssertEqual(code, "500") + expectNoDifference(message, "Invalid code") + expectNoDifference(error, "invalid_grant") + expectNoDifference(code, "500") } } @@ -1014,9 +1014,9 @@ final class AuthClientTests: XCTestCase { do { try await sut.session(from: url) } catch let AuthError.pkceGrantCodeExchange(message, error, code) { - XCTAssertEqual(message, "Error in URL with unspecified error_description.") - XCTAssertEqual(error, "invalid_grant") - XCTAssertEqual(code, "500") + expectNoDifference(message, "Error in URL with unspecified error_description.") + expectNoDifference(error, "invalid_grant") + expectNoDifference(code, "500") } } @@ -1326,7 +1326,7 @@ final class AuthClientTests: XCTestCase { captchaToken: "captcha-token" ) - XCTAssertEqual(response.messageId, "12345") + expectNoDifference(response.messageId, "12345") } func testDeleteUser() async throws { @@ -1449,7 +1449,7 @@ final class AuthClientTests: XCTestCase { captchaToken: "captcha-token" ) - XCTAssertEqual(response.url, URL(string: "https://supabase.com")!) + expectNoDifference(response.url, URL(string: "https://supabase.com")!) } func testSignInWithSSOUsingProviderId() async throws { @@ -1482,7 +1482,7 @@ final class AuthClientTests: XCTestCase { captchaToken: "captcha-token" ) - XCTAssertEqual(response.url, URL(string: "https://supabase.com")!) + expectNoDifference(response.url, URL(string: "https://supabase.com")!) } func testMFAEnrollLegacy() async throws { @@ -1526,8 +1526,8 @@ final class AuthClientTests: XCTestCase { ) ) - XCTAssertEqual(response.id, "12345") - XCTAssertEqual(response.type, "totp") + expectNoDifference(response.id, "12345") + expectNoDifference(response.type, "totp") } func testMFAEnrollTotp() async throws { @@ -1571,8 +1571,8 @@ final class AuthClientTests: XCTestCase { ) ) - XCTAssertEqual(response.id, "12345") - XCTAssertEqual(response.type, "totp") + expectNoDifference(response.id, "12345") + expectNoDifference(response.type, "totp") } func testMFAEnrollPhone() async throws { @@ -1616,8 +1616,8 @@ final class AuthClientTests: XCTestCase { ) ) - XCTAssertEqual(response.id, "12345") - XCTAssertEqual(response.type, "phone") + expectNoDifference(response.id, "12345") + expectNoDifference(response.type, "phone") } func testMFAChallenge() async throws { @@ -1656,7 +1656,7 @@ final class AuthClientTests: XCTestCase { let response = try await sut.mfa.challenge(params: .init(factorId: factorId)) - XCTAssertEqual( + expectNoDifference( response, AuthMFAChallengeResponse( id: "12345", @@ -1710,7 +1710,7 @@ final class AuthClientTests: XCTestCase { ) ) - XCTAssertEqual( + expectNoDifference( response, AuthMFAChallengeResponse( id: "12345", @@ -1782,7 +1782,7 @@ final class AuthClientTests: XCTestCase { let factorId = try await sut.mfa.unenroll(params: .init(factorId: "123")).factorId - XCTAssertEqual(factorId, "123") + expectNoDifference(factorId, "123") } func testMFAChallengeAndVerify() async throws { @@ -1893,8 +1893,8 @@ final class AuthClientTests: XCTestCase { Dependencies[sut.clientID].sessionStorage.store(session) let factors = try await sut.mfa.listFactors() - XCTAssertEqual(factors.totp.map(\.id), ["1"]) - XCTAssertEqual(factors.phone.map(\.id), ["3"]) + expectNoDifference(factors.totp.map(\.id), ["1"]) + expectNoDifference(factors.phone.map(\.id), ["3"]) } func testGetAuthenticatorAssuranceLevel_whenAALAndVerifiedFactor_shouldReturnAAL2() async throws { @@ -1921,7 +1921,7 @@ final class AuthClientTests: XCTestCase { let aal = try await sut.mfa.getAuthenticatorAssuranceLevel() - XCTAssertEqual( + expectNoDifference( aal, AuthMFAGetAuthenticatorAssuranceLevelResponse( currentLevel: "aal1", diff --git a/Tests/AuthTests/Resources/local-storage.json b/Tests/AuthTests/Resources/local-storage.json index 73f03145..624c4cb1 100644 --- a/Tests/AuthTests/Resources/local-storage.json +++ b/Tests/AuthTests/Resources/local-storage.json @@ -1,41 +1,41 @@ { "supabase.auth.token" : { - "access_token" : "accesstoken", - "expires_at" : 1711977907, - "expires_in" : 120, - "refresh_token" : "refreshtoken", - "token_type" : "bearer", + "accessToken" : "accesstoken", + "expiresAt" : 1711977907, + "expiresIn" : 120, + "refreshToken" : "refreshtoken", + "tokenType" : "bearer", "user" : { - "app_metadata" : { + "appMetadata" : { "provider" : "email", "providers" : [ "email" ] }, "aud" : "authenticated", - "confirmation_sent_at" : "2022-04-09T11:57:01.000Z", - "created_at" : "2022-04-09T11:57:01.000Z", + "confirmationSentAt" : 671198221, + "createdAt" : 671198221, "email" : "johndoe@supabsae.com", "id" : "859F402D-B3DE-4105-A1B9-932836D9193B", "identities" : [ { - "created_at" : "2022-04-09T11:57:01.000Z", + "createdAt" : 671198221, "id" : "859f402d-b3de-4105-a1b9-932836d9193b", - "identity_data" : { + "identityData" : { "sub" : "859f402d-b3de-4105-a1b9-932836d9193b" }, - "identity_id" : "859F402D-B3DE-4105-A1B9-932836D9193B", - "last_sign_in_at" : "2022-04-09T11:57:01.000Z", + "identityId" : "859F402D-B3DE-4105-A1B9-932836D9193B", + "lastSignInAt" : 671198221, "provider" : "email", - "updated_at" : "2022-04-09T11:57:01.000Z", - "user_id" : "859F402D-B3DE-4105-A1B9-932836D9193B" + "updatedAt" : 671198221, + "userId" : "859F402D-B3DE-4105-A1B9-932836D9193B" } ], - "is_anonymous" : false, + "isAnonymous" : false, "phone" : "", "role" : "authenticated", - "updated_at" : "2022-04-09T11:57:01.000Z", - "user_metadata" : { + "updatedAt" : 671198221, + "userMetadata" : { "referrer_id" : null } } diff --git a/Tests/AuthTests/SessionManagerTests.swift b/Tests/AuthTests/SessionManagerTests.swift index 4b4a46a3..4fa316bd 100644 --- a/Tests/AuthTests/SessionManagerTests.swift +++ b/Tests/AuthTests/SessionManagerTests.swift @@ -44,11 +44,11 @@ final class SessionManagerTests: XCTestCase { } #if !os(Windows) && !os(Linux) && !os(Android) - override func invokeTest() { - withMainSerialExecutor { - super.invokeTest() + override func invokeTest() { + withMainSerialExecutor { + super.invokeTest() + } } - } #endif func testSession_shouldFailWithSessionNotFound() async { @@ -112,7 +112,10 @@ final class SessionManagerTests: XCTestCase { } // Verify that refresher and storage was called only once. - XCTAssertEqual(refreshSessionCallCount.value, 1) - XCTAssertEqual(try result.map { try $0.get() }, (0..<10).map { _ in validSession }) + expectNoDifference(refreshSessionCallCount.value, 1) + expectNoDifference( + try result.map { try $0.get()?.accessToken }, + (0..<10).map { _ in validSession.accessToken } + ) } } diff --git a/Tests/IntegrationTests/DotEnv.swift b/Tests/IntegrationTests/DotEnv.swift new file mode 100644 index 00000000..c7b179a5 --- /dev/null +++ b/Tests/IntegrationTests/DotEnv.swift @@ -0,0 +1,7 @@ +enum DotEnv { + static let SUPABASE_URL = "http://localhost:54321" + static let SUPABASE_ANON_KEY = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im91dGxvb2stZGV2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTM3MzYwMjgsImV4cCI6MjAyOTMxMjAyOH0.6Y900000000000000000000000000000000000000000000000000000000000000" + static let SUPABASE_SERVICE_ROLE_KEY = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im91dGxvb2stZGV2Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxMzczNjAyOCwiZXhwIjoyMDI5MzEyMDI4fQ.0000000000000000000000000000000000000000000000000000000000000000" +}