diff --git a/Tests/ApolloInternalTestHelpers/MockNetworkTransport.swift b/Tests/ApolloInternalTestHelpers/MockNetworkTransport.swift index e67d45bab..da3c9a7a1 100644 --- a/Tests/ApolloInternalTestHelpers/MockNetworkTransport.swift +++ b/Tests/ApolloInternalTestHelpers/MockNetworkTransport.swift @@ -6,6 +6,8 @@ public final class MockNetworkTransport: NetworkTransport { public let mockServer: MockGraphQLServer public let requestChainTransport: RequestChainNetworkTransport + public var url: URL { requestChainTransport.endpointURL } + public init( mockServer: MockGraphQLServer = MockGraphQLServer(), store: ApolloStore @@ -87,7 +89,7 @@ public final class MockNetworkTransport: NetworkTransport { } let httpResponse = HTTPURLResponse( - url: TestURL.mockServer.url, + url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil diff --git a/Tests/ApolloTests/ApolloStore+ReaderWriterTests.swift b/Tests/ApolloTests/ApolloStore+ReaderWriterTests.swift deleted file mode 100644 index 5022473e0..000000000 --- a/Tests/ApolloTests/ApolloStore+ReaderWriterTests.swift +++ /dev/null @@ -1,11 +0,0 @@ -import XCTest -import Nimble -@testable import Apollo - -final class ApolloStore_ReaderWriterTests: XCTestCase { - - #warning("TODO: Implement or kill") - func test__multipleConcurrentReads__occurConcurrently() { - - } -} diff --git a/Tests/ApolloTests/ApolloStore_BatchedLoadTests.swift b/Tests/ApolloTests/ApolloStore_BatchedLoadTests.swift index ef6037654..306dd73ea 100644 --- a/Tests/ApolloTests/ApolloStore_BatchedLoadTests.swift +++ b/Tests/ApolloTests/ApolloStore_BatchedLoadTests.swift @@ -114,6 +114,7 @@ class ApolloStore_BatchedLoadTests: XCTestCase { XCTAssertEqual(data.hero?.name, "R2-D2") XCTAssertEqual(data.hero?.friends?.count, 100) + // 3 loads: ROOT_QUERY.hero, hero.friends, list of friends XCTAssertEqual(cache.numberOfBatchLoads, 3) } diff --git a/Tests/ApolloTests/AutomaticPersistedQueriesTests.swift b/Tests/ApolloTests/AutomaticPersistedQueriesTests.swift index 2a561cae4..8e329c1df 100644 --- a/Tests/ApolloTests/AutomaticPersistedQueriesTests.swift +++ b/Tests/ApolloTests/AutomaticPersistedQueriesTests.swift @@ -228,7 +228,6 @@ class AutomaticPersistedQueriesTests: XCTestCase, MockResponseProvider { expectation.to(equal("{\"episode\":\"\(episode.rawValue)\"}")) case .none: - #warning("TODO: write test to test this case actually happens") expectation.to(equal("{}")) case .null: diff --git a/Tests/ApolloTests/Cache/DeferOperationCacheReadTests.swift b/Tests/ApolloTests/Cache/DeferOperationCacheReadTests.swift index 0c5c0d78f..0d5bd996f 100644 --- a/Tests/ApolloTests/Cache/DeferOperationCacheReadTests.swift +++ b/Tests/ApolloTests/Cache/DeferOperationCacheReadTests.swift @@ -1,29 +1,34 @@ -import XCTest -@testable import Apollo import ApolloAPI import ApolloInternalTestHelpers import Nimble +import XCTest -fileprivate class AnimalQuery: MockQuery, @unchecked Sendable { +@testable import Apollo + +private class AnimalQuery: MockQuery, @unchecked Sendable { class AnAnimal: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata - - override class var __selections: [Selection] {[ - .field("animal", Animal.self), - ]} - + + override class var __selections: [Selection] { + [ + .field("animal", Animal.self) + ] + } + var animal: Animal { __data["animal"] } - + class Animal: AbstractMockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("species", String.self), - .deferred(DeferredGenus.self, label: "deferredGenus"), - .deferred(DeferredFriend.self, label: "deferredFriend"), - ]} - + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("species", String.self), + .deferred(DeferredGenus.self, label: "deferredGenus"), + .deferred(DeferredFriend.self, label: "deferredFriend"), + ] + } + var species: String { __data["species"] } - + struct Fragments: FragmentContainer { public let __data: DataDict public init(_dataDict: DataDict) { @@ -31,32 +36,38 @@ fileprivate class AnimalQuery: MockQuery, @unchecked Senda _deferredGenus = Deferred(_dataDict: _dataDict) _deferredFriend = Deferred(_dataDict: _dataDict) } - + @Deferred var deferredGenus: DeferredGenus? @Deferred var deferredFriend: DeferredFriend? } - + class DeferredGenus: MockTypeCase, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("genus", String.self), - ]} - + override class var __selections: [Selection] { + [ + .field("genus", String.self) + ] + } + var genus: String { __data["genus"] } } - + class DeferredFriend: MockTypeCase, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("friend", Friend.self), - ]} - + override class var __selections: [Selection] { + [ + .field("friend", Friend.self) + ] + } + var friend: Friend { __data["friend"] } - + class Friend: AbstractMockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("name", String.self), - .deferred(DeferredFriendSpecies.self, label: "deferredFriendSpecies"), - ]} - + override class var __selections: [Selection] { + [ + .field("name", String.self), + .deferred(DeferredFriendSpecies.self, label: "deferredFriendSpecies"), + ] + } + var name: String { __data["name"] } struct Fragments: FragmentContainer { @@ -70,9 +81,11 @@ fileprivate class AnimalQuery: MockQuery, @unchecked Senda } class DeferredFriendSpecies: MockTypeCase, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("species", String.self), - ]} + override class var __selections: [Selection] { + [ + .field("species", String.self) + ] + } var species: String { __data["species"] } } @@ -116,7 +129,7 @@ class DeferOperationCacheReadTests: XCTestCase, CacheDependentTesting { func test__fetch__givenPartialAndIncrementalDataIsCached_returnsAllDeferredFragmentsAsFulfilled() async throws { await mergeRecordsIntoCache([ "QUERY_ROOT": [ - "animal": CacheReference("QUERY_ROOT.animal"), + "animal": CacheReference("QUERY_ROOT.animal") ], "QUERY_ROOT.animal": [ "__typename": "animal", @@ -127,115 +140,79 @@ class DeferOperationCacheReadTests: XCTestCase, CacheDependentTesting { "QUERY_ROOT.animal.friend": [ "name": "American Badger", "species": "Taxidea taxus", - ] + ], ]) let query = AnimalQuery() - let resultObserver = makeResultObserver(for: query) - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - expect(result).to(beSuccess()) + let result = try await client.fetch(query: query, cachePolicy: .cacheOnly) + let animal = try unwrap(result?.data?.animal) - let animal = try unwrap(try result.get().data?.animal) - - expect(animal.__typename).to(equal("animal")) - expect(animal.species).to(equal("Canis latrans")) - expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) - expect(animal.fragments.deferredFriend?.friend.name).to(equal("American Badger")) - expect(animal.fragments.deferredFriend?.friend.fragments.deferredFriendSpecies?.species).to(equal("Taxidea taxus")) - } - - client.fetch( - query: query, - cachePolicy: .returnCacheDataDontFetch, - resultHandler: resultObserver.handler - ) - - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) + expect(animal.__typename).to(equal("animal")) + expect(animal.species).to(equal("Canis latrans")) + expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) + expect(animal.fragments.deferredFriend?.friend.name).to(equal("American Badger")) + expect(animal.fragments.deferredFriend?.friend.fragments.deferredFriendSpecies?.species).to(equal("Taxidea taxus")) } func test__fetch__givenOnlyPartialDataIsCached_returnsAllDeferredFragmentsAsPending() async throws { await mergeRecordsIntoCache([ "QUERY_ROOT": [ - "animal": CacheReference("QUERY_ROOT.animal"), + "animal": CacheReference("QUERY_ROOT.animal") ], "QUERY_ROOT.animal": [ "__typename": "animal", "species": "Canis latrans", - // 'genus' not cached - // 'friend' not cached + // 'genus' not cached + // 'friend' not cached ], ]) let query = AnimalQuery() - let resultObserver = makeResultObserver(for: query) - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - expect(result).to(beSuccess()) - - let animal = try unwrap(try result.get().data?.animal) - - expect(animal.__typename).to(equal("animal")) - expect(animal.species).to(equal("Canis latrans")) - expect(animal.fragments.$deferredGenus).to(equal(.pending)) - expect(animal.fragments.$deferredFriend).to(equal(.pending)) - } + let result = try await client.fetch(query: query, cachePolicy: .cacheOnly) + let animal = try unwrap(result?.data?.animal) - client.fetch( - query: query, - cachePolicy: .returnCacheDataDontFetch, - resultHandler: resultObserver.handler - ) - - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) + expect(animal.__typename).to(equal("animal")) + expect(animal.species).to(equal("Canis latrans")) + expect(animal.fragments.$deferredGenus).to(equal(.pending)) + expect(animal.fragments.$deferredFriend).to(equal(.pending)) } - func test__fetch__givenPartialAndSomeIncrementalDataIsCached_returnsCachedDeferredFragmentAsFulfilledAndUncachedDeferredFragmentsAsPending() async throws { + func + test__fetch__givenPartialAndSomeIncrementalDataIsCached_returnsCachedDeferredFragmentAsFulfilledAndUncachedDeferredFragmentsAsPending() + async throws + { await mergeRecordsIntoCache([ "QUERY_ROOT": [ - "animal": CacheReference("QUERY_ROOT.animal"), + "animal": CacheReference("QUERY_ROOT.animal") ], "QUERY_ROOT.animal": [ "__typename": "animal", "species": "Canis latrans", "genus": "Canis", - // 'friend' not cached + // 'friend' not cached ], ]) let query = AnimalQuery() - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - expect(result).to(beSuccess()) - - let animal = try unwrap(try result.get().data?.animal) - expect(animal.__typename).to(equal("animal")) - expect(animal.species).to(equal("Canis latrans")) - expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) - expect(animal.fragments.$deferredFriend).to(equal(.pending)) - } - - client.fetch( - query: query, - cachePolicy: .returnCacheDataDontFetch, - resultHandler: resultObserver.handler - ) + let result = try await client.fetch(query: query, cachePolicy: .cacheOnly) + let animal = try unwrap(result?.data?.animal) - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) + expect(animal.__typename).to(equal("animal")) + expect(animal.species).to(equal("Canis latrans")) + expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) + expect(animal.fragments.$deferredFriend).to(equal(.pending)) } - func test__fetch__givenNestedIncrementalDataIsNotCached_returnsNestedDeferredFragmentsAsPending_otherDeferredFragmentsAsFulfilled() async throws { + func + test__fetch__givenNestedIncrementalDataIsNotCached_returnsNestedDeferredFragmentsAsPending_otherDeferredFragmentsAsFulfilled() + async throws + { await mergeRecordsIntoCache([ "QUERY_ROOT": [ - "animal": CacheReference("QUERY_ROOT.animal"), + "animal": CacheReference("QUERY_ROOT.animal") ], "QUERY_ROOT.animal": [ "__typename": "animal", @@ -244,41 +221,31 @@ class DeferOperationCacheReadTests: XCTestCase, CacheDependentTesting { "friend": CacheReference("QUERY_ROOT.animal.friend"), ], "QUERY_ROOT.animal.friend": [ - "name": "American Badger", - // 'species' not cached - ] + "name": "American Badger" + // 'species' not cached + ], ]) let query = AnimalQuery() - let resultObserver = makeResultObserver(for: query) - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - expect(result).to(beSuccess()) + let result = try await client.fetch(query: query, cachePolicy: .cacheOnly) + let animal = try unwrap(result?.data?.animal) - let animal = try unwrap(try result.get().data?.animal) - - expect(animal.__typename).to(equal("animal")) - expect(animal.species).to(equal("Canis latrans")) - expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) - expect(animal.fragments.deferredFriend?.friend.name).to(equal("American Badger")) - expect(animal.fragments.deferredFriend?.friend.fragments.$deferredFriendSpecies).to(equal(.pending)) - } + expect(animal.__typename).to(equal("animal")) + expect(animal.species).to(equal("Canis latrans")) + expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) + expect(animal.fragments.deferredFriend?.friend.name).to(equal("American Badger")) + expect(animal.fragments.deferredFriend?.friend.fragments.$deferredFriendSpecies).to(equal(.pending)) - client.fetch( - query: query, - cachePolicy: .returnCacheDataDontFetch, - resultHandler: resultObserver.handler - ) - - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) } - func test__fetch__givenMissingValueInDeferredFragment_returnsDeferredFragmentAsPending_otherDeferredFragmentsAsFulfilled() async throws { + func + test__fetch__givenMissingValueInDeferredFragment_returnsDeferredFragmentAsPending_otherDeferredFragmentsAsFulfilled() + async throws + { await mergeRecordsIntoCache([ "QUERY_ROOT": [ - "animal": CacheReference("QUERY_ROOT.animal"), + "animal": CacheReference("QUERY_ROOT.animal") ], "QUERY_ROOT.animal": [ "__typename": "animal", @@ -288,39 +255,26 @@ class DeferOperationCacheReadTests: XCTestCase, CacheDependentTesting { ], "QUERY_ROOT.animal.friend": [ // 'name' missing - "species": "Taxidea taxus", - ] + "species": "Taxidea taxus" + ], ]) let query = AnimalQuery() - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - expect(result).to(beSuccess()) - - let animal = try unwrap(try result.get().data?.animal) - expect(animal.__typename).to(equal("animal")) - expect(animal.species).to(equal("Canis latrans")) - expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) - expect(animal.fragments.$deferredFriend).to(equal(.pending)) - } + let result = try await client.fetch(query: query, cachePolicy: .cacheOnly) + let animal = try unwrap(result?.data?.animal) - client.fetch( - query: query, - cachePolicy: .returnCacheDataDontFetch, - resultHandler: resultObserver.handler - ) + expect(animal.__typename).to(equal("animal")) + expect(animal.species).to(equal("Canis latrans")) + expect(animal.fragments.deferredGenus?.genus).to(equal("Canis")) + expect(animal.fragments.$deferredFriend).to(equal(.pending)) - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) } func test__fetch__givenMissingValueInPartialData_shouldReturnNil() async throws { await mergeRecordsIntoCache([ "QUERY_ROOT": [ - "animal": CacheReference("QUERY_ROOT.animal"), + "animal": CacheReference("QUERY_ROOT.animal") ], "QUERY_ROOT.animal": [ "__typename": "animal", @@ -331,7 +285,7 @@ class DeferOperationCacheReadTests: XCTestCase, CacheDependentTesting { "QUERY_ROOT.animal.friend": [ "name": "American Badger", "species": "Taxidea taxus", - ] + ], ]) let query = AnimalQuery() diff --git a/Tests/ApolloTests/Cache/FetchQueryTests.swift b/Tests/ApolloTests/Cache/FetchQueryTests.swift index 5753f1be3..5f2c20241 100644 --- a/Tests/ApolloTests/Cache/FetchQueryTests.swift +++ b/Tests/ApolloTests/Cache/FetchQueryTests.swift @@ -1,429 +1,366 @@ -import XCTest -@testable import Apollo import ApolloAPI import ApolloInternalTestHelpers +import Nimble +import XCTest + +@testable import Apollo class FetchQueryTests: XCTestCase, CacheDependentTesting { - + var cacheType: any TestCacheProvider.Type { InMemoryTestCacheProvider.self } - + static let defaultWaitTimeout: TimeInterval = 1 - + var cache: (any NormalizedCache)! var server: MockGraphQLServer! var client: ApolloClient! - + override func setUp() async throws { try await super.setUp() cache = try await makeNormalizedCache() let store = ApolloStore(cache: cache) - + server = MockGraphQLServer() let networkTransport = MockNetworkTransport(mockServer: server, store: store) client = ApolloClient(networkTransport: networkTransport, store: store) } - + override func tearDownWithError() throws { cache = nil server = nil client = nil - + try super.tearDownWithError() } - - func test__fetch__givenCachePolicy_fetchIgnoringCacheData_onlyHitsNetwork() async throws { + + func test__fetch__givenCachePolicy_networkOnly_onlyHitsNetwork() async throws { class HeroNameSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + ] + } } } let query = MockQuery() - + await mergeRecordsIntoCache([ "QUERY_ROOT": ["hero": CacheReference("hero")], "hero": [ "name": "R2-D2", "__typename": "Droid", - ] + ], ]) - + let serverRequestExpectation = - await server.expect(MockQuery.self) { request in - [ - "data": [ - "hero": [ - "name": "Luke Skywalker", - "__typename": "Human" + await server.expect(MockQuery.self) { request in + [ + "data": [ + "hero": [ + "name": "Luke Skywalker", + "__typename": "Human", + ] ] ] - ] - } - - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromServerExpectation = resultObserver.expectation( - description: "Received result from server" - ) { result in - try XCTAssertSuccessResult(result) { graphQLResult in - XCTAssertEqual(graphQLResult.source, .server) - XCTAssertNil(graphQLResult.errors) - - let data = try XCTUnwrap(graphQLResult.data) - XCTAssertEqual(data.hero?.name, "Luke Skywalker") } - } - - client.fetch(query: query, cachePolicy: .fetchIgnoringCacheData, resultHandler: resultObserver.handler) - - await fulfillment(of: [serverRequestExpectation, fetchResultFromServerExpectation], timeout: Self.defaultWaitTimeout) + + let result = try await client.fetch(query: query, cachePolicy: .networkOnly) + + XCTAssertEqual(result.source, .server) + XCTAssertNil(result.errors) + + let data = try XCTUnwrap(result.data) + XCTAssertEqual(data.hero?.name, "Luke Skywalker") + + await fulfillment(of: [serverRequestExpectation], timeout: Self.defaultWaitTimeout) } - - func test__fetch__givenCachePolicy_returnCacheDataAndFetch_hitsCacheFirstAndNetworkAfter() async throws { + + func test__fetch__givenCachePolicy_cacheAndNetwork_hitsCacheFirstAndNetworkAfter() async throws { class HeroNameSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + ] + } } } let query = MockQuery() - + await mergeRecordsIntoCache([ "QUERY_ROOT": ["hero": CacheReference("hero")], "hero": [ "name": "R2-D2", "__typename": "Droid", - ] + ], ]) - + let serverRequestExpectation = - await server.expect(MockQuery.self) { request in - [ - "data": [ - "hero": [ - "name": "Luke Skywalker", - "__typename": "Human" + await server.expect(MockQuery.self) { request in + [ + "data": [ + "hero": [ + "name": "Luke Skywalker", + "__typename": "Human", + ] ] ] - ] - } - - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - try XCTAssertSuccessResult(result) { graphQLResult in - XCTAssertEqual(graphQLResult.source, .cache) - XCTAssertNil(graphQLResult.errors) - - let data = try XCTUnwrap(graphQLResult.data) - XCTAssertEqual(data.hero?.name, "R2-D2") - } - } - - let fetchResultFromServerExpectation = resultObserver.expectation( - description: "Received result from server" - ) { result in - try XCTAssertSuccessResult(result) { graphQLResult in - XCTAssertEqual(graphQLResult.source, .server) - XCTAssertNil(graphQLResult.errors) - - let data = try XCTUnwrap(graphQLResult.data) - XCTAssertEqual(data.hero?.name, "Luke Skywalker") } - } - - client.fetch(query: query, cachePolicy: .returnCacheDataAndFetch, resultHandler: resultObserver.handler) - - await fulfillment(of: [fetchResultFromCacheExpectation, serverRequestExpectation, fetchResultFromServerExpectation], timeout: Self.defaultWaitTimeout) + + let results = try await client.fetch(query: query, cachePolicy: .cacheAndNetwork).getAllValues() + + expect(results.count).to(equal(2)) + + let cacheResult = results[0] + XCTAssertEqual(cacheResult.source, .cache) + XCTAssertNil(cacheResult.errors) + + let cacheResultData = try XCTUnwrap(cacheResult.data) + XCTAssertEqual(cacheResultData.hero?.name, "R2-D2") + + let networkResult = results[1] + XCTAssertEqual(networkResult.source, .server) + XCTAssertNil(networkResult.errors) + + let networkResultData = try XCTUnwrap(networkResult.data) + XCTAssertEqual(networkResultData.hero?.name, "Luke Skywalker") + + await fulfillment( + of: [serverRequestExpectation], + timeout: Self.defaultWaitTimeout + ) } - - func test__fetch__givenCachePolicy_returnCacheDataElseFetch_givenDataIsCached_doesntHitNetwork() async throws { + + func test__fetch__givenCachePolicy_cacheFirst_givenDataIsCached_doesntHitNetwork() async throws { class HeroNameSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + ] + } } } let query = MockQuery() - + await mergeRecordsIntoCache([ "QUERY_ROOT": ["hero": CacheReference("QUERY_ROOT.hero")], "QUERY_ROOT.hero": [ "name": "R2-D2", "__typename": "Droid", - ] + ], ]) - - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - try XCTAssertSuccessResult(result) { graphQLResult in - XCTAssertEqual(graphQLResult.source, .cache) - XCTAssertNil(graphQLResult.errors) - - let data = try XCTUnwrap(graphQLResult.data) - XCTAssertEqual(data.hero?.name, "R2-D2") - } - } - - client.fetch(query: query, - cachePolicy: .returnCacheDataElseFetch, - resultHandler: resultObserver.handler) - - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) + + let result = try await client.fetch(query: query, cachePolicy: .cacheFirst) + + XCTAssertEqual(result.source, .cache) + XCTAssertNil(result.errors) + + let data = try XCTUnwrap(result.data) + XCTAssertEqual(data.hero?.name, "R2-D2") } - - func test__fetch__givenCachePolicy_returnCacheDataElseFetch_givenNotAllDataIsCached_hitsNetwork() async throws { + + func test__fetch__givenCachePolicy_cacheFirst_givenNotAllDataIsCached_hitsNetwork() async throws { class HeroNameAndAppearsInSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self), - .field("appearsIn", [String]?.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + .field("appearsIn", [String]?.self), + ] + } } } let query = MockQuery() - + await mergeRecordsIntoCache([ "QUERY_ROOT": ["hero": CacheReference("hero")], "hero": [ "name": "R2-D2", - "__typename": "Droid" - ] + "__typename": "Droid", + ], ]) - + let serverRequestExpectation = - await server.expect(MockQuery.self) { request in - [ - "data": [ - "hero": [ - "name": "R2-D2", - "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"], - "__typename": "Droid" - ] - ] as JSONValue - ] - } - - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromServerExpectation = resultObserver.expectation(description: "Received result from server") { result in - try XCTAssertSuccessResult(result) { graphQLResult in - XCTAssertEqual(graphQLResult.source, .server) - XCTAssertNil(graphQLResult.errors) - - let data = try XCTUnwrap(graphQLResult.data) - XCTAssertEqual(data.hero?.name, "R2-D2") - XCTAssertEqual(data.hero?.appearsIn, ["NEWHOPE", "EMPIRE", "JEDI"]) + await server.expect(MockQuery.self) { request in + [ + "data": [ + "hero": [ + "name": "R2-D2", + "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"], + "__typename": "Droid", + ] + ] as JSONValue + ] } - } - - client.fetch(query: query, cachePolicy: .returnCacheDataAndFetch, resultHandler: resultObserver.handler) - - await fulfillment(of: [serverRequestExpectation, fetchResultFromServerExpectation], timeout: Self.defaultWaitTimeout) + + let result = try await client.fetch(query: query, cachePolicy: .cacheFirst) + + XCTAssertEqual(result.source, .server) + XCTAssertNil(result.errors) + + let data = try XCTUnwrap(result.data) + XCTAssertEqual(data.hero?.name, "R2-D2") + XCTAssertEqual(data.hero?.appearsIn, ["NEWHOPE", "EMPIRE", "JEDI"]) + + await fulfillment( + of: [serverRequestExpectation], + timeout: Self.defaultWaitTimeout + ) } - - func test__fetch__givenCachePolicy_returnCacheDataDontFetch_givenDataIsCached_doesntHitNetwork() async throws { + + func test__fetch__givenCachePolicy_returnCacheOnly_givenDataIsCached_doesntHitNetwork() async throws { class HeroNameSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + ] + } } } let query = MockQuery() - + await mergeRecordsIntoCache([ "QUERY_ROOT": ["hero": CacheReference("hero")], "hero": [ "name": "R2-D2", "__typename": "Droid", - ] + ], ]) - - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromCacheExpectation = resultObserver.expectation(description: "Received result from cache") { result in - try XCTAssertSuccessResult(result) { graphQLResult in - XCTAssertEqual(graphQLResult.source, .cache) - XCTAssertNil(graphQLResult.errors) - - let data = try XCTUnwrap(graphQLResult.data) - XCTAssertEqual(data.hero?.name, "R2-D2") - } - } - - client.fetch(query: query, cachePolicy: .returnCacheDataDontFetch, resultHandler: resultObserver.handler) - - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) + + let result = try await client.fetch(query: query, cachePolicy: .cacheOnly) + + XCTAssertEqual(result?.source, .cache) + XCTAssertNil(result?.errors) + + let data = try XCTUnwrap(result?.data) + XCTAssertEqual(data.hero?.name, "R2-D2") } - - func test__fetch__givenCachePolicy_returnCacheDataDontFetch_givenNotAllDataIsCached_returnsError() async throws { + + func test__fetch__givenCachePolicy_cacheOnly_givenNotAllDataIsCached_returnsNil() async throws { class HeroNameAndAppearsInSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self), - .field("appearsIn", [String]?.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + .field("appearsIn", [String]?.self), + ] + } } } let query = MockQuery() - + await mergeRecordsIntoCache([ "QUERY_ROOT": ["hero": CacheReference("hero")], "hero": [ "name": "R2-D2", - "__typename": "Droid" - ] + "__typename": "Droid", + ], ]) - - let resultObserver = makeResultObserver(for: query) - - let cacheMissResultExpectation = resultObserver.expectation(description: "Received cache miss error") { result in - // TODO: We should check for a specific error type once we've defined a cache miss error. - XCTAssertThrowsError(try result.get()) - } - - client.fetch(query: query, - cachePolicy: .returnCacheDataDontFetch, - resultHandler: resultObserver.handler) - await fulfillment(of: [cacheMissResultExpectation], timeout: Self.defaultWaitTimeout) + let result = try await client.fetch(query: query, cachePolicy: .cacheOnly) + + // cache miss + expect(result).to(beNil()) } - - func test__fetch_afterClearCache_givenCachePolicy_returnCacheDataDontFetch_throwsCacheMissError() async throws { + + func test__fetch_afterClearCache_givenCachePolicy_cacheOnly_returnsNil() async throws { class HeroNameSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + ] + } } } let query = MockQuery() - + await mergeRecordsIntoCache([ "QUERY_ROOT": ["hero": CacheReference("hero")], "hero": [ "name": "R2-D2", "__typename": "Droid", - ] + ], ]) - - let resultObserver = makeResultObserver(for: query) - - let fetchResultFromCacheExpectation = resultObserver.expectation( - description: "Received result from cache" - ) { result in - try XCTAssertSuccessResult(result) { graphQLResult in - XCTAssertEqual(graphQLResult.source, .cache) - XCTAssertNil(graphQLResult.errors) - - let data = try XCTUnwrap(graphQLResult.data) - XCTAssertEqual(data.hero?.name, "R2-D2") - } - } - - client.fetch(query: query, cachePolicy: .returnCacheDataDontFetch, resultHandler: resultObserver.handler) - - await fulfillment(of: [fetchResultFromCacheExpectation], timeout: Self.defaultWaitTimeout) - // Clear the cache - let cacheClearedExpectation = expectation(description: "Cache cleared") - client.clearCache { result in - XCTAssertSuccessResult(result) - cacheClearedExpectation.fulfill() - } + let firstResult = try await client.fetch(query: query, cachePolicy: .cacheOnly) - await fulfillment(of: [cacheClearedExpectation], timeout: Self.defaultWaitTimeout) + XCTAssertEqual(firstResult?.source, .cache) + XCTAssertNil(firstResult?.errors) - // Fetch from cache and expect cache miss failure - let cacheMissResultExpectation = resultObserver.expectation(description: "Received cache miss error") { result in - // TODO: We should check for a specific error type once we've defined a cache miss error. - XCTAssertThrowsError(try result.get()) - } + let data = try XCTUnwrap(firstResult?.data) + XCTAssertEqual(data.hero?.name, "R2-D2") + + // Clear the cache + try await client.clearCache() - client.fetch(query: query, cachePolicy: .returnCacheDataDontFetch, resultHandler: resultObserver.handler) + // Fetch from cache and expect cache miss failure + let cacheMissResult = try await client.fetch(query: query, cachePolicy: .cacheOnly) - await fulfillment(of: [cacheMissResultExpectation], timeout: Self.defaultWaitTimeout) - } - - func testCompletionHandlerIsCalledOnTheSpecifiedQueue() async { - let queue = DispatchQueue(label: "label") - - let key = DispatchSpecificKey() - queue.setSpecific(key: key, value: ()) - - let query = MockQuery.mock() - - let serverRequestExpectation = await server.expect(MockQuery.self) { request in - ["data": [:] as JSONValue] - } - - let resultObserver = makeResultObserver(for: query) - - let fetchResultExpectation = resultObserver.expectation( - description: "Received fetch result" - ) { result in - XCTAssertNotNil(DispatchQueue.getSpecific(key: key)) - } - - client.fetch(query: query, - cachePolicy: .fetchIgnoringCacheData, - queue: queue, resultHandler: resultObserver.handler) - - await fulfillment(of: [serverRequestExpectation, fetchResultExpectation], timeout: Self.defaultWaitTimeout) + expect(cacheMissResult).to(beNil()) } + } diff --git a/Tests/ApolloTests/Cache/SQLite/CachePersistenceTests.swift b/Tests/ApolloTests/Cache/SQLite/CachePersistenceTests.swift index d33dd0dd7..9e97f7d9d 100644 --- a/Tests/ApolloTests/Cache/SQLite/CachePersistenceTests.swift +++ b/Tests/ApolloTests/Cache/SQLite/CachePersistenceTests.swift @@ -1,26 +1,31 @@ -import XCTest -import Nimble -@testable import Apollo import ApolloAPI -@testable import ApolloSQLite import ApolloInternalTestHelpers +import Nimble import SQLite3 import StarWarsAPI +import XCTest + +@testable import Apollo +@testable import ApolloSQLite class CachePersistenceTests: XCTestCase { func testFetchAndPersist() async throws { // given class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self) - ]} + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + ] + } } } @@ -41,18 +46,17 @@ class CachePersistenceTests: XCTestCase { "data": [ "hero": [ "name": "Luke Skywalker", - "__typename": "Human" + "__typename": "Human", ] ] ] } - let graphQLResult1 = try await client.fetch(query: query, cachePolicy: .networkOnly) XCTAssertEqual(graphQLResult1.data?.hero?.name, "Luke Skywalker") - // Do another fetch from cache to ensure that data is cached before creating new cache + // Do another fetch from cache to ensure that data is cached before creating new cache let _ = try await client.fetch(query: query, cachePolicy: .cacheOnly) let (newCache, _) = await SQLiteTestCacheProvider.makeNormalizedCache(fileURL: sqliteFileURL) @@ -67,14 +71,18 @@ class CachePersistenceTests: XCTestCase { func testFetchAndPersistWithPeriodArguments() async throws { // given class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] { [ - .field("hero", Hero.self, arguments: ["text": .variable("term")]) - ]} + override class var __selections: [Selection] { + [ + .field("hero", Hero.self, arguments: ["text": .variable("term")]) + ] + } class Hero: MockSelectionSet, @unchecked Sendable { - override class var __selections: [Selection] {[ - .field("name", String.self) - ]} + override class var __selections: [Selection] { + [ + .field("name", String.self) + ] + } } } @@ -97,62 +105,43 @@ class CachePersistenceTests: XCTestCase { "data": [ "hero": [ "name": "Luke Skywalker", - "__typename": "Human" + "__typename": "Human", ] ] ] } - let networkExpectation = self.expectation(description: "Fetching query from network") - let newCacheExpectation = self.expectation(description: "Fetch query from new cache") - - client.fetch(query: query, cachePolicy: .fetchIgnoringCacheData) { outerResult in - defer { networkExpectation.fulfill() } - - switch outerResult { - case .failure(let error): - XCTFail("Unexpected error: \(error)") - return - case .success(let graphQLResult): - XCTAssertEqual(graphQLResult.data?.hero?.name, "Luke Skywalker") - - // Do another fetch from cache to ensure that data is cached before creating new cache - client.fetch(query: query, cachePolicy: .returnCacheDataDontFetch) { innerResult in - Task { - let (cache, _) = await SQLiteTestCacheProvider.makeNormalizedCache(fileURL: sqliteFileURL) - let newStore = ApolloStore(cache: cache) - let newClient = ApolloClient(networkTransport: networkTransport, store: newStore) - - newClient.fetch(query: query, cachePolicy: .returnCacheDataDontFetch) { newClientResult in - defer { newCacheExpectation.fulfill() } - switch newClientResult { - case .success(let newClientGraphQLResult): - XCTAssertEqual(newClientGraphQLResult.data?.hero?.name, "Luke Skywalker") - case .failure(let error): - XCTFail("Unexpected error with new client: \(error)") - } - _ = newClient // Workaround for a bug - ensure that newClient is retained until this block is run - } - } - } - } - } + let graphQLResult = try await client.fetch(query: query, cachePolicy: .networkOnly) + + XCTAssertEqual(graphQLResult.data?.hero?.name, "Luke Skywalker") + + // Do another fetch from cache to ensure that data is cached before creating new cache + _ = try await client.fetch(query: query, cachePolicy: .cacheOnly) + + let (newCache, _) = await SQLiteTestCacheProvider.makeNormalizedCache(fileURL: sqliteFileURL) + let newStore = ApolloStore(cache: newCache) + let newClient = ApolloClient(networkTransport: networkTransport, store: newStore) - await fulfillment(of: [networkExpectation, newCacheExpectation], timeout: 2) + let newClientResult = try await newClient.fetch(query: query, cachePolicy: .cacheOnly) + XCTAssertEqual(newClientResult?.data?.hero?.name, "Luke Skywalker") } func testClearCache() async throws { // given - class GivenSelectionSet: MockSelectionSet { - override class var __selections: [Selection] { [ - .field("hero", Hero.self) - ]} - - class Hero: MockSelectionSet { - override class var __selections: [Selection] {[ - .field("__typename", String.self), - .field("name", String.self) - ]} + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { + override class var __selections: [Selection] { + [ + .field("hero", Hero.self) + ] + } + + class Hero: MockSelectionSet, @unchecked Sendable { + override class var __selections: [Selection] { + [ + .field("__typename", String.self), + .field("name", String.self), + ] + } } } @@ -173,7 +162,7 @@ class CachePersistenceTests: XCTestCase { "data": [ "hero": [ "name": "Luke Skywalker", - "__typename": "Human" + "__typename": "Human", ] ] ] diff --git a/Tests/ApolloTests/Cache/WatchQueryTests.swift b/Tests/ApolloTests/Cache/WatchQueryTests.swift index 09ae062c1..b3111b4d4 100644 --- a/Tests/ApolloTests/Cache/WatchQueryTests.swift +++ b/Tests/ApolloTests/Cache/WatchQueryTests.swift @@ -212,18 +212,10 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } } - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: MockQuery(), cachePolicy: .networkOnly) await fulfillment( - of: [refetchServerRequestExpectation, otherFetchCompletedExpectation, updatedWatcherResultExpectation], + of: [refetchServerRequestExpectation, updatedWatcherResultExpectation], timeout: Self.defaultWaitTimeout ) } @@ -306,23 +298,17 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { ) { _ in } noUpdatedResultExpectation.isInverted = true - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - let newQuery = MockQuery() newQuery.__variables = ["episode": "JEDI"] - client.fetch(query: newQuery, cachePolicy: .fetchIgnoringCacheData) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: newQuery, cachePolicy: .networkOnly) await fulfillment( - of: [secondServerRequestExpectation, otherFetchCompletedExpectation, noUpdatedResultExpectation], + of: [secondServerRequestExpectation, noUpdatedResultExpectation], timeout: Self.defaultWaitTimeout ) } - @MainActor func testWatchedQueryGetsUpdatedWhenSameObjectHasChangedInAnotherQueryWithDifferentVariables() async throws { class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { @@ -342,7 +328,7 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } } - MockSchemaMetadata.stub_cacheKeyInfoForType_Object(IDCacheKeyProvider.resolver) + await MockSchemaMetadata.stub_cacheKeyInfoForType_Object(IDCacheKeyProvider.resolver) let watchedQuery = MockQuery() watchedQuery.__variables = ["episode": "EMPIRE"] @@ -415,18 +401,13 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } } - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - let query = MockQuery() query.__variables = ["episode": "JEDI"] - client.fetch(query: query, cachePolicy: .fetchIgnoringCacheData) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: query, cachePolicy: .networkOnly) await fulfillment( - of: [secondServerRequestExpectation, otherFetchCompletedExpectation, updatedWatcherResultExpectation], + of: [secondServerRequestExpectation, updatedWatcherResultExpectation], timeout: Self.defaultWaitTimeout ) } @@ -559,18 +540,10 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } } - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: MockQuery(), cachePolicy: .networkOnly) await fulfillment( - of: [secondServerRequestExpectation, otherFetchCompletedExpectation, updatedWatcherResultExpectation], + of: [secondServerRequestExpectation, updatedWatcherResultExpectation], timeout: Self.defaultWaitTimeout ) } @@ -727,18 +700,10 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } } - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: MockQuery(), cachePolicy: .networkOnly) await fulfillment( - of: [secondServerRequestExpectation, otherFetchCompletedExpectation, updatedWatcherResultExpectation], + of: [secondServerRequestExpectation, updatedWatcherResultExpectation], timeout: Self.defaultWaitTimeout ) } @@ -915,19 +880,12 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } } - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: MockQuery(), cachePolicy: .networkOnly) await fulfillment( of: [ - secondServerRequestExpectation, otherFetchCompletedExpectation, refetchServerRequestExpectation, + secondServerRequestExpectation, + refetchServerRequestExpectation, updatedWatcherResultExpectation, ], timeout: Self.defaultWaitTimeout @@ -1084,18 +1042,10 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { ) { _ in } updatedWatcherResultExpectation.isInverted = true - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: MockQuery(), cachePolicy: .networkOnly) await fulfillment( - of: [secondServerRequestExpectation, otherFetchCompletedExpectation, updatedWatcherResultExpectation], + of: [secondServerRequestExpectation, updatedWatcherResultExpectation], timeout: Self.defaultWaitTimeout ) } @@ -1439,16 +1389,11 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } noRefetchExpectation.isInverted = true - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - let query = MockQuery() - client.fetch(query: query, cachePolicy: .fetchIgnoringCacheData) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: query, cachePolicy: .networkOnly) await fulfillment( - of: [secondServerRequestExpectation, otherFetchCompletedExpectation, noRefetchExpectation], + of: [secondServerRequestExpectation, noRefetchExpectation], timeout: Self.defaultWaitTimeout ) } @@ -1541,17 +1486,15 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { let otherFetchesCompletedExpectation = expectation(description: "Other fetches completed") otherFetchesCompletedExpectation.expectedFulfillmentCount = numberOfFetches - DispatchQueue.concurrentPerform(iterations: numberOfFetches) { _ in - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { [weak self] result in - otherFetchesCompletedExpectation.fulfill() - - if let self = self, case .failure(let error) = result { - self.record(error) + try await withThrowingTaskGroup { group in + for _ in 0..(), cachePolicy: .networkOnly) + otherFetchesCompletedExpectation.fulfill() } } + + try await group.waitForAll() } await fulfillment( @@ -1660,17 +1603,15 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { let otherFetchesCompletedExpectation = expectation(description: "Other fetches completed") otherFetchesCompletedExpectation.expectedFulfillmentCount = numberOfFetches - DispatchQueue.concurrentPerform(iterations: numberOfFetches) { _ in - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { [weak self] result in - otherFetchesCompletedExpectation.fulfill() - - if let self = self, case .failure(let error) = result { - self.record(error) + try await withThrowingTaskGroup { group in + for _ in 0..(), cachePolicy: .networkOnly) + otherFetchesCompletedExpectation.fulfill() } } + + try await group.waitForAll() } await fulfillment( @@ -1862,7 +1803,7 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } @MainActor - func testWatchedQueryDependentKeysAreUpdatedAfterOtherFetchReturnsChangedData() async { + func testWatchedQueryDependentKeysAreUpdatedAfterOtherFetchReturnsChangedData() async throws { class HeroAndFriendsNameWithIDsSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [ @@ -2010,18 +1951,10 @@ class WatchQueryTests: XCTestCase, CacheDependentTesting { } } - let otherFetchCompletedExpectation = expectation(description: "Other fetch completed") - - client.fetch( - query: MockQuery(), - cachePolicy: .fetchIgnoringCacheData - ) { result in - defer { otherFetchCompletedExpectation.fulfill() } - XCTAssertSuccessResult(result) - } + _ = try await client.fetch(query: MockQuery(), cachePolicy: .networkOnly) await fulfillment( - of: [secondServerRequestExpectation, otherFetchCompletedExpectation, updatedWatcherResultExpectation], + of: [secondServerRequestExpectation, updatedWatcherResultExpectation], timeout: Self.defaultWaitTimeout ) } diff --git a/Tests/ApolloTests/DefaultInterceptorProviderTests.swift b/Tests/ApolloTests/DefaultInterceptorProviderTests.swift index 97cfbd5ce..9fe752dcc 100644 --- a/Tests/ApolloTests/DefaultInterceptorProviderTests.swift +++ b/Tests/ApolloTests/DefaultInterceptorProviderTests.swift @@ -1,4 +1,5 @@ import XCTest +import Nimble import Apollo import ApolloAPI import ApolloInternalTestHelpers @@ -31,7 +32,7 @@ class DefaultInterceptorProviderTests: XCTestCase { super.tearDown() } - func testLoading() async { + func testLoading() async throws { // given final class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [ @@ -50,20 +51,18 @@ class DefaultInterceptorProviderTests: XCTestCase { DefaultInterceptorProviderTests.mockData } - client.fetch(query: MockQuery()) { result in - switch result { - case .success(let graphQLResult): - XCTAssertEqual(graphQLResult.source, .server) - XCTAssertEqual(graphQLResult.data?.hero?.name, "R2-D2") - case .failure(let error): - XCTFail("Unexpected error: \(error)") - } - } + let results = try await client.fetch(query: MockQuery()).getAllValues() + + expect(results.count).to(equal(1)) + + let graphQLResult = results.first + XCTAssertEqual(graphQLResult?.source, .server) + XCTAssertEqual(graphQLResult?.data?.hero?.name, "R2-D2") await fulfillment(of: [expectation]) } - func testInitialLoadFromNetworkAndSecondaryLoadFromCache() async { + func testInitialLoadFromNetworkAndSecondaryLoadFromCache() async throws { // given final class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [ @@ -82,36 +81,21 @@ class DefaultInterceptorProviderTests: XCTestCase { DefaultInterceptorProviderTests.mockData } initialLoadExpectation.assertForOverFulfill = false - let fetchCompleteExpectation = self.expectation(description: "fetch complete") - - client.fetch(query: MockQuery()) { result in - defer { fetchCompleteExpectation.fulfill() } - switch result { - case .success(let graphQLResult): - XCTAssertEqual(graphQLResult.source, .server) - XCTAssertEqual(graphQLResult.data?.hero?.name, "R2-D2") - case .failure(let error): - XCTFail("Unexpected error: \(error)") - } - } - await fulfillment(of: [initialLoadExpectation, fetchCompleteExpectation]) + let results = try await client.fetch(query: MockQuery()).getAllValues() - let secondLoadExpectation = self.expectation(description: "loaded with default client") + expect(results.count).to(equal(1)) + let graphQLResult = results.first - client.fetch(query: MockQuery(), cachePolicy: .returnCacheDataElseFetch) { result in - switch result { - case .success(let graphQLResult): - XCTAssertEqual(graphQLResult.source, .cache) - XCTAssertEqual(graphQLResult.data?.hero?.name, "R2-D2") - case .failure(let error): - XCTFail("Unexpected error: \(error)") + XCTAssertEqual(graphQLResult?.source, .server) + XCTAssertEqual(graphQLResult?.data?.hero?.name, "R2-D2") - } - secondLoadExpectation.fulfill() - } + await fulfillment(of: [initialLoadExpectation]) + + let secondLoadResult = try await client.fetch(query: MockQuery(), cachePolicy: .cacheFirst) - await fulfillment(of: [secondLoadExpectation], timeout: 10) + XCTAssertEqual(secondLoadResult.source, .cache) + XCTAssertEqual(secondLoadResult.data?.hero?.name, "R2-D2") } diff --git a/Tests/ApolloTests/GraphQLExecutor_ResultNormalizer_FromResponse_Tests.swift b/Tests/ApolloTests/GraphQLExecutor_ResultNormalizer_FromResponse_Tests.swift index c4123baaa..3087f6c50 100644 --- a/Tests/ApolloTests/GraphQLExecutor_ResultNormalizer_FromResponse_Tests.swift +++ b/Tests/ApolloTests/GraphQLExecutor_ResultNormalizer_FromResponse_Tests.swift @@ -30,12 +30,12 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { func test__execute__givenObjectWithNoCacheKey_normalizesRecordToPathFromQueryRoot() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} @@ -59,12 +59,12 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { func test__execute__givenObjectWithNoCacheKey_forFieldWithStringArgument_normalizesRecordToPathFromQueryRootIncludingArgument() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self, arguments: ["episode": .variable("episode")]) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} @@ -96,12 +96,12 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { case JEDI } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self, arguments: ["episode": .variable("episode")]) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} @@ -127,18 +127,18 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { func test__execute__givenObjectWithNoCacheKey_andNestedArrayOfObjectsWithNoCacheKey_normalizesRecordsToPathsFromQueryRoot() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self), .field("friends", [Friend].self) ]} - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} @@ -179,19 +179,19 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { @MainActor func test__execute__givenObjectWithCacheKey_andNestedArrayOfObjectsWithCacheKey_normalizesRecordsToIndividualReferences() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("id", String.self), .field("name", String.self), .field("friends", [Friend].self) ]} - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("id", String.self), .field("name", String.self) @@ -241,20 +241,20 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { func test__execute__givenFieldForObjectWithNoCacheKey_andAliasedFieldForSameFieldName_normalizesRecordsForBothFieldsIntoOneRecord() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self), .field("hero", alias: "r2", R2.self) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("name", String.self) ]} } - class R2: MockSelectionSet { + class R2: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("catchphrase", String.self) ]} @@ -292,26 +292,26 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { } }) - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self), ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .inlineFragment(AsHuman.self), .inlineFragment(AsDroid.self), ]} - class AsHuman: MockTypeCase { + class AsHuman: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ .field("name", alias: "property", String.self) ]} } - class AsDroid: MockTypeCase { + class AsDroid: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Droid } override class var __selections: [Selection] {[ .field("primaryFunction", alias: "property", String.self) @@ -350,18 +350,18 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { } }) - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self), ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .inlineFragment(AsHuman.self), .inlineFragment(AsDroid.self), ]} - class AsHuman: MockTypeCase { + class AsHuman: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ .field("__typename", String.self), @@ -369,7 +369,7 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { ]} } - class AsDroid: MockTypeCase { + class AsDroid: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Droid } override class var __selections: [Selection] {[ .field("__typename", String.self), @@ -409,38 +409,38 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { } }) - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self), ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .inlineFragment(AsHuman.self), .inlineFragment(AsDroid.self), ]} - class AsHuman: MockTypeCase { + class AsHuman: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ .field("friend", Friend.self), ]} - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("height", Double.self, arguments: ["unit": "FOOT"]) ]} } } - class AsDroid: MockTypeCase { + class AsDroid: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Droid } override class var __selections: [Selection] {[ .field("friend", Friend.self), ]} - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("height", Double.self, arguments: ["unit": "METER"]) ]} @@ -483,38 +483,38 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { } }) - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self), ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .inlineFragment(AsHuman.self), .inlineFragment(AsDroid.self), ]} - class AsHuman: MockTypeCase { + class AsHuman: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ .field("friend", Friend.self), ]} - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("height", Double.self, arguments: ["unit": "FOOT"]) ]} } } - class AsDroid: MockTypeCase { + class AsDroid: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Droid } override class var __selections: [Selection] {[ .field("friend", Friend.self), ]} - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("height", Double.self, arguments: ["unit": "METER"]) ]} @@ -546,12 +546,12 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { @MainActor func test__execute__givenObjectOfTypeWithKeyFields_normalizesRecordsToReference() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { Object(typename: "Hero", implementedInterfaces: [], keyFields: ["name"]) } @@ -586,12 +586,12 @@ class GraphQLExecutor_ResultNormalizer_FromResponse_Tests: XCTestCase { @MainActor func test__execute__givenObjectOfUnknownType_forInterfaceFieldWithKeyFields_normalizesRecordsToReference() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("hero", Hero.self) ]} - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { Interface(name: "Hero", keyFields: ["name"], implementingObjects: ["Hero"]) } diff --git a/Tests/ApolloTests/GraphQLExecutor_SelectionSetMapper_FromResponse_Tests.swift b/Tests/ApolloTests/GraphQLExecutor_SelectionSetMapper_FromResponse_Tests.swift index b8e2290ac..d7e9b39e6 100644 --- a/Tests/ApolloTests/GraphQLExecutor_SelectionSetMapper_FromResponse_Tests.swift +++ b/Tests/ApolloTests/GraphQLExecutor_SelectionSetMapper_FromResponse_Tests.swift @@ -49,7 +49,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_scalar__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String.self)] } } let object: JSONObject = ["name": "Luke Skywalker"] @@ -63,13 +63,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_scalar__givenDataMissingKeyForField_throwsMissingValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String.self)] } } let object: JSONObject = [:] // when - await expecta(try await self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["name"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -78,13 +79,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_scalar__givenDataHasNullValueForField_throwsNullValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String.self)] } } let object: JSONObject = ["name": NSNull()] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["name"])) expect(error.underlying).to(matchError(JSONDecodingError.nullValue)) @@ -93,7 +95,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_scalar__givenDataWithTypeConvertibleToFieldType_getsConvertedValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String.self)] } } let object: JSONObject = ["name": 10] @@ -107,13 +109,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_scalar__givenDataWithTypeNotConvertibleToFieldType_throwsCouldNotConvertError() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String.self)] } } let object: JSONObject = ["name": false] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then if case JSONDecodingError.couldNotConvert(let value, let expectedType) = error.underlying { expect(error.path).to(equal(["name"])) @@ -129,7 +132,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { // given typealias GivenCustomScalar = String - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("customScalar", GivenCustomScalar.self)] } } let object: JSONObject = ["customScalar": Int(12345678)] @@ -145,7 +148,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { // given typealias GivenCustomScalar = String - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("customScalar", GivenCustomScalar.self)] } } let object: JSONObject = ["customScalar": Int64(989561700)] @@ -161,7 +164,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { // given typealias GivenCustomScalar = String - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("customScalar", GivenCustomScalar.self)] } } let object: JSONObject = ["customScalar": Double(1234.5678)] @@ -186,7 +189,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var _jsonValue: JSONValue { value } } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("customScalar", GivenCustomScalar.self)] } } let object: JSONObject = ["customScalar": Int64(989561700)] @@ -202,7 +205,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_scalar__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String?.self)] } } let object: JSONObject = ["name": "Luke Skywalker"] @@ -216,13 +219,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_scalar__givenDataMissingKeyForField_throwsMissingValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String?.self)] } } let object: JSONObject = [:] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["name"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -231,7 +235,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_scalar__givenDataHasNullValueForField_returnsNilValueForField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String?.self)] } } let object: JSONObject = ["name": NSNull()] @@ -245,7 +249,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_scalar__givenDataWithTypeConvertibleToFieldType_getsConvertedValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String?.self)] } } let object: JSONObject = ["name": 10] @@ -259,13 +263,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_scalar__givenDataWithTypeNotConvertibleToFieldType_throwsCouldNotConvertError() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("name", String?.self)] } } let object: JSONObject = ["name": false] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then if case JSONDecodingError.couldNotConvert(let value, let expectedType) = error.underlying { expect(error.path).to(equal(["name"])) @@ -285,7 +290,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_enum__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("size", GraphQLEnum.self)] } } let object: JSONObject = ["size": "SMALL"] @@ -299,7 +304,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_enum__givenDataIsNotAnEnumCase_getsValueAsUnknownCase() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("size", GraphQLEnum.self)] } } let object: JSONObject = ["size": "GIGANTIC"] @@ -313,13 +318,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_enum__givenDataMissingKeyForField_throwsMissingValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("size", GraphQLEnum.self)] } } let object: JSONObject = [:] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["size"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -328,7 +334,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_enum__givenDataHasNullValueForField_throwsNullValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("size", GraphQLEnum.self) ]} @@ -336,7 +342,8 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { let object: JSONObject = ["size": NSNull()] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["size"])) expect(error.underlying).to(matchError(JSONDecodingError.nullValue)) @@ -345,13 +352,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_enum__givenDataWithType_Int_throwsCouldNotConvertError() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("size", GraphQLEnum.self)] } } let object: JSONObject = ["size": 10] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then if case JSONDecodingError.couldNotConvert(let value, let expectedType) = error.underlying { expect(error.path).to(equal(["size"])) @@ -363,13 +371,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_enum__givenDataWithType_Double_throwsCouldNotConvertError() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("size", GraphQLEnum.self)] } } let object: JSONObject = ["size": 10.0] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then if case JSONDecodingError.couldNotConvert(let value, let expectedType) = error.underlying { expect(error.path).to(equal(["size"])) @@ -383,7 +392,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_scalar__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String].self)] } } let object: JSONObject = ["favorites": ["Purple", "Potatoes", "iPhone"]] @@ -397,7 +406,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_scalar__givenEmptyDataArray_getsValueAsEmptyArray() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String].self)] } } let object: JSONObject = ["favorites": [] as JSONValue] @@ -411,13 +420,13 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_scalar__givenDataMissingKeyForField_throwsMissingValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String].self)] } } let object: JSONObject = [:] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) }.to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["favorites"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -426,13 +435,13 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_scalar__givenDataIsNullForField_throwsNullValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String].self)] } } let object: JSONObject = ["favorites": NSNull()] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) }.to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["favorites"])) expect(error.underlying).to(matchError(JSONDecodingError.nullValue)) @@ -441,7 +450,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_scalar__givenDataWithElementTypeConvertibleToFieldType_getsConvertedValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String].self)] } } let object: JSONObject = ["favorites": [10, 20, 30]] @@ -455,7 +464,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_enum__givenDataWithStringsNotEnumValue_getsValueAsUnknownCase() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [ .field("favorites", [GraphQLEnum].self) ] } @@ -475,13 +484,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_scalar__givenDataWithElementTypeNotConvertibleToFieldType_throwsCouldNotConvertError() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String].self)] } } let object: JSONObject = ["favorites": [true, false, true]] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then if case JSONDecodingError.couldNotConvert(let value, let expectedType) = error.underlying { expect(error.path).to(equal(["favorites", "0"])) @@ -495,7 +505,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_nonnull_scalar__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String]?.self)] } } let object: JSONObject = ["favorites": ["Purple", "Potatoes", "iPhone"]] @@ -509,7 +519,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_nonnull_scalar__givenEmptyDataArray_getsValueAsEmptyArray() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String]?.self)] } } let object: JSONObject = ["favorites": [] as JSONValue] @@ -523,13 +533,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_nonnull_scalar__givenDataMissingKeyForField_throwsMissingValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String]?.self)] } } let object: JSONObject = [:] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["favorites"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -538,7 +549,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_nonnull_scalar__givenDataIsNullForField_valueIsNil() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String]?.self)] } } let object: JSONObject = ["favorites": NSNull()] @@ -552,7 +563,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_nonnull_scalar__givenDataWithElementTypeConvertibleToFieldType_getsConvertedValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String]?.self)] } } let object: JSONObject = ["favorites": [10, 20, 30]] @@ -566,13 +577,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_nonnull_scalar__givenDataWithElementTypeNotConvertibleToFieldType_throwsCouldNotConvertError() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String]?.self)] } } let object: JSONObject = ["favorites": [true, false, false]] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then if case JSONDecodingError.couldNotConvert(let value, let expectedType) = error.underlying { expect(error.path).to(equal(["favorites", "0"])) @@ -586,7 +598,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_optional_scalar__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String?].self)] } } let object: JSONObject = ["favorites": ["Purple", "Potatoes", "iPhone"]] @@ -600,7 +612,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_optional_scalar__givenEmptyDataArray_getsValueAsEmptyArray() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String?].self)] } } let object: JSONObject = ["favorites": [] as JSONValue] @@ -614,13 +626,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_optional_scalar__givenDataMissingKeyForField_throwsMissingValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String?].self)] } } let object: JSONObject = [:] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["favorites"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -629,13 +642,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_optional__givenDataIsNullForField_throwsNullValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String?].self)] } } let object: JSONObject = ["favorites": NSNull()] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["favorites"])) expect(error.underlying).to(matchError(JSONDecodingError.nullValue)) @@ -644,7 +658,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_list_nonnull_optional__givenDataIsArrayWithNullElement_valueIsArrayWithValuesIncludingNilElement() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String?].self)] } } let object: JSONObject = ["favorites": ["Red", NSNull(), "Bird"] as JSONValue] @@ -659,7 +673,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_optional_scalar__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [.field("favorites", [String?]?.self)] } } let object: JSONObject = ["favorites": ["Purple", "Potatoes", "iPhone"]] @@ -673,7 +687,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_optional_enum__givenDataWithUnknownEnumCaseElement_getsValueWithUnknownEnumCaseElement() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [ .field("favorites", [GraphQLEnum?]?.self) ] } @@ -689,7 +703,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__optional_list_optional_enum__givenDataWithNonConvertibleTypeElement_getsValueWithUnknownEnumCaseElement() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] { [ .field("favorites", [GraphQLEnum?]?.self) ] } @@ -697,7 +711,8 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { let object: JSONObject = ["favorites": [10]] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then if case JSONDecodingError.couldNotConvert(let value, let expectedType) = error.underlying { expect(error.path).to(equal(["favorites", "0"])) @@ -711,12 +726,12 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_nestedObject__givenData_getsValue() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("child", Child.self) ]} - class Child: MockSelectionSet { + class Child: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} @@ -738,12 +753,12 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_nestedObject__givenDataMissingKeyForField_throwsMissingValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("child", Child.self) ]} - class Child: MockSelectionSet { + class Child: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} @@ -752,7 +767,8 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { let object: JSONObject = ["child": ["__typename": "Child"]] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["child", "name"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -761,12 +777,12 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__nonnull_nestedObject__givenDataHasNullValueForField_throwsNullValueError() async { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("child", Child.self) ]} - class Child: MockSelectionSet { + class Child: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} @@ -780,7 +796,8 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { ] // when - await expecta(await try self.readValues(GivenSelectionSet.self, from: object)).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(GivenSelectionSet.self, from: object) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["child", "name"])) expect(error.underlying).to(matchError(JSONDecodingError.nullValue)) @@ -796,14 +813,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let MockChildObject = Object(typename: "MockChildObject", implementedInterfaces: []) } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Object.mock } override class var __selections: [Selection] {[ .field("child", Child.self), ]} - class Child: MockSelectionSet { + class Child: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.MockChildObject } @@ -812,7 +829,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { .inlineFragment(AsHuman.self) ]} - class AsHuman: MockTypeCase { + class AsHuman: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ .field("name", String.self), @@ -848,7 +865,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__inlineFragment__givenDataForDeferredSelection_doesNotSelectDeferredFields() async throws { // given - class AnAnimal: MockSelectionSet { + class AnAnimal: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -857,7 +874,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var animal: Animal { __data["animal"] } - class Animal: AbstractMockSelectionSet { + class Animal: AbstractMockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("name", String.self), @@ -874,7 +891,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { @Deferred var deferredSpecies: DeferredSpecies? } - class DeferredSpecies: MockTypeCase { + class DeferredSpecies: MockTypeCase, @unchecked Sendable { override class var __selections: [Selection] {[ .field("species", String.self), ]} @@ -905,7 +922,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__deferredInlineFragment__givenPartialDataForSelection_withConditionEvaluatingTrue_collectsDeferredFragment() async throws { // given - class AnAnimal: MockSelectionSet { + class AnAnimal: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -914,7 +931,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var animal: Animal { __data["animal"] } - class Animal: AbstractMockSelectionSet { + class Animal: AbstractMockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("name", String.self), @@ -931,7 +948,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { @Deferred var deferredSpecies: DeferredSpecies? } - class DeferredSpecies: MockTypeCase { + class DeferredSpecies: MockTypeCase, @unchecked Sendable { override class var __selections: [Selection] {[ .field("species", String.self), ]} @@ -959,7 +976,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__deferredInlineFragment__givenPartialDataForSelection_withConditionEvaluatingFalse_doesCollectFulfilledFragmentAndFields() async throws { // given - class AnAnimal: MockSelectionSet { + class AnAnimal: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -968,7 +985,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var animal: Animal { __data["animal"] } - class Animal: AbstractMockSelectionSet { + class Animal: AbstractMockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("name", String.self), @@ -985,7 +1002,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { @Deferred var deferredSpecies: DeferredSpecies? } - class DeferredSpecies: MockTypeCase { + class DeferredSpecies: MockTypeCase, @unchecked Sendable { override class var __selections: [Selection] {[ .field("species", String.self), ]} @@ -1018,7 +1035,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__deferredInlineFragment__givenPartialDataForSelection_withConditionEvaluatingFalse_whenMissingDeferredIncrementalData_shouldThrow() async throws { // given - class AnAnimal: MockSelectionSet { + class AnAnimal: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1027,7 +1044,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var animal: Animal { __data["animal"] } - class Animal: AbstractMockSelectionSet { + class Animal: AbstractMockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("name", String.self), @@ -1044,7 +1061,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { @Deferred var deferredSpecies: DeferredSpecies? } - class DeferredSpecies: MockTypeCase { + class DeferredSpecies: MockTypeCase, @unchecked Sendable { override class var __selections: [Selection] {[ .field("species", String.self), ]} @@ -1060,7 +1077,8 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { ] // when + then - await expecta(await try self.readValues(AnAnimal.self, from: object, variables: ["varA": false])).to(throwError { (error: GraphQLExecutionError) in + await expect { try await self.readValues(AnAnimal.self, from: object, variables: ["varA": false]) } + .to(throwError { (error: GraphQLExecutionError) in // then expect(error.path).to(equal(["animal.species"])) expect(error.underlying).to(matchError(JSONDecodingError.missingValue)) @@ -1069,7 +1087,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__deferredInlineFragment__givenIncrementalDataForDeferredSelection_selectsFieldsAndFulfillsFragment() async throws { // given - class AnAnimal: MockSelectionSet { + class AnAnimal: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1078,7 +1096,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var animal: Animal { __data["animal"] } - class Animal: AbstractMockSelectionSet { + class Animal: AbstractMockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("name", String.self), @@ -1095,7 +1113,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { @Deferred var deferredSpecies: DeferredSpecies? } - class DeferredSpecies: MockTypeCase { + class DeferredSpecies: MockTypeCase, @unchecked Sendable { override class var __selections: [Selection] {[ .field("species", String.self), ]} @@ -1125,20 +1143,20 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let MockChildObject = Object(typename: "MockChildObject", implementedInterfaces: []) } - class GivenFragment: MockFragment { + class GivenFragment: MockFragment, @unchecked Sendable { override class var __parentType: any ParentType { Types.MockChildObject } override class var __selections: [Selection] {[ .field("child", Child.self) ]} - class Child: MockSelectionSet { + class Child: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self) ]} } } - class GivenSelectionSet: AbstractMockSelectionSet { + class GivenSelectionSet: AbstractMockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { Types.MockChildObject } override class var __selections: [Selection] {[ .fragment(GivenFragment.self) @@ -1176,7 +1194,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_singleField__givenVariableIsTrue_getsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "variable", .field("name", String.self)) ]} @@ -1193,7 +1211,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_singleField__givenVariableIsFalse_doesNotGetsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "variable", .field("name", String.self)) ]} @@ -1210,7 +1228,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_singleField__givenGraphQLNullableVariableIsTrue_getsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "variable", .field("name", String.self)) ]} @@ -1227,7 +1245,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_singleField__givenGraphQLNullableVariableIsFalse_doesNotGetsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "variable", .field("name", String.self)) ]} @@ -1244,7 +1262,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_multipleIncludes_singleField__givenAllVariablesAreTrue_getsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "one" || "two", .field("name", String.self)) ]} @@ -1261,7 +1279,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_singleField__givenVariableIsFalse_givenOtherSelection_doesNotGetsValueForConditionalField_doesGetOtherSelection() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("id", String.self), .include(if: "variable", .field("name", String.self)) @@ -1280,7 +1298,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_multipleFields__givenVariableIsTrue_getsValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "variable", [ .field("name", String.self), @@ -1301,7 +1319,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_multipleFields__givenVariableIsFalse_doesNotGetValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "variable", [ .field("name", String.self), @@ -1322,13 +1340,13 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_fragment__givenVariableIsTrue_getsValuesForFragmentFields() async throws { // given - class GivenFragment: MockFragment { + class GivenFragment: MockFragment, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self), ]} } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("id", String.self), .include(if: "variable", .fragment(GivenFragment.self)) @@ -1347,13 +1365,13 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_include_fragment__givenVariableIsFalse_doesNotGetValuesForFragmentFields() async throws { // given - class GivenFragment: MockFragment { + class GivenFragment: MockFragment, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self), ]} } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("id", String.self), .include(if: "variable", .fragment(GivenFragment.self)) @@ -1376,14 +1394,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let Person = Object(typename: "Person", implementedInterfaces: []) } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("id", String.self), .include(if: "variable", .inlineFragment(AsPerson.self)) ]} - class AsPerson: MockTypeCase { + class AsPerson: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Person } override class var __selections: [Selection] {[ .field("name", String.self), @@ -1412,14 +1430,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let Person = Object(typename: "Person", implementedInterfaces: []) } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("id", String.self), .include(if: "variable", .inlineFragment(AsPerson.self)) ]} - class AsPerson: MockTypeCase { + class AsPerson: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Person } override class var __selections: [Selection] {[ .field("name", String.self), @@ -1448,14 +1466,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let Person = Object(typename: "Person", implementedInterfaces: []) } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("id", String.self), .include(if: "variable", .inlineFragment(AsPerson.self)) ]} - class AsPerson: MockTypeCase { + class AsPerson: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Person } override class var __selections: [Selection] {[ .field("name", String.self), @@ -1484,14 +1502,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let Person = Object(typename: "Person", implementedInterfaces: []) } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("id", String.self), .inlineFragment(AsPerson.self) ]} - class AsPerson: MockTypeCase { + class AsPerson: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Person } override class var __selections: [Selection] {[ .include(if: "variable", .field("name", String.self)), @@ -1520,14 +1538,14 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let Person = Object(typename: "Person", implementedInterfaces: []) } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("id", String.self), .inlineFragment(AsPerson.self) ]} - class AsPerson: MockTypeCase { + class AsPerson: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Person } override class var __selections: [Selection] {[ .include(if: "variable", .field("name", String.self)), @@ -1556,19 +1574,19 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { static let Person = Object(typename: "Person", implementedInterfaces: []) } - class GivenFragment: MockFragment { + class GivenFragment: MockFragment, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self), ]} } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .field("id", String.self), .include(if: "variable", .inlineFragment(AsPerson.self)) ]} - class AsPerson: MockTypeCase { + class AsPerson: MockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { Types.Person } override class var __selections: [Selection] {[ .fragment(GivenFragment.self), @@ -1595,7 +1613,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_skip_singleField__givenVariableIsFalse_getsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"variable", .field("name", String.self)) ]} @@ -1612,7 +1630,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_skip_singleField__givenVariableIsTrue_doesNotGetsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"variable", .field("name", String.self)) ]} @@ -1629,7 +1647,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_skip_singleField__givenGraphQLNullableVariableIsFalse_getsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"variable", .field("name", String.self)) ]} @@ -1646,7 +1664,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_skip_singleField__givenGraphQLNullableVariableIsTrue_doesNotGetsValueForConditionalField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"variable", .field("name", String.self)) ]} @@ -1663,7 +1681,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_skip_multipleFields__givenVariableIsFalse_getsValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"variable", [ .field("name", String.self), @@ -1684,7 +1702,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_skip_multipleFields__givenVariableIsTrue_doesNotGetValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"variable", [ .field("name", String.self), @@ -1705,13 +1723,13 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_skip_singleField__givenVariableIsTrue_givenFieldIdSelectedByAnotherSelection_getsValueForField() async throws { // given - class GivenFragment: MockFragment { + class GivenFragment: MockFragment, @unchecked Sendable { override class var __selections: [Selection] {[ .field("name", String.self), ]} } - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"variable", .field("name", String.self)), .fragment(GivenFragment.self) @@ -1732,7 +1750,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_multipleFields__givenSkipIsTrue_includeIsTrue_doesNotGetValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip" && "include", [ .field("name", String.self), @@ -1754,7 +1772,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_multipleFields__givenSkipIsTrue_includeIsFalse_doesNotGetValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip" && "include", [ .field("name", String.self), @@ -1778,7 +1796,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_multipleFields__givenSkipIsFalse_includeIsFalse_doesNotGetValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip" && "include", [ .field("name", String.self), @@ -1802,7 +1820,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_multipleFields__givenSkipIsFalse_includeIsTrue_getValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip" && "include", [ .field("name", String.self), @@ -1826,7 +1844,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelection__givenSkipIsTrue_includeIsTrue_getsValuesForField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip", .field("name", String.self)), .include(if: "include", .field("name", String.self)) @@ -1847,7 +1865,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelectionMergedAsOrCondition__givenSkipIsTrue_includeIsTrue_getsValuesForField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "include" || !"skip", .field("name", String.self)) ]} @@ -1867,7 +1885,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelection__givenSkipIsFalse_includeIsFalse_getsValuesForField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip", .field("name", String.self)), .include(if: "include", .field("name", String.self)) @@ -1888,7 +1906,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelectionMergedAsOrCondition__givenSkipIsFalse_includeIsFalse_getsValuesForField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "include" || !"skip", .field("name", String.self)) ]} @@ -1908,7 +1926,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelection__givenSkipIsFalse_includeIsTrue_getsValuesForField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip", .field("name", String.self)), .include(if: "include", .field("name", String.self)) @@ -1929,7 +1947,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelectionMergedAsOrCondition__givenSkipIsFalse_includeIsTrue_getsValuesForField() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "include" || !"skip", .field("name", String.self)) ]} @@ -1949,7 +1967,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelection__givenSkipIsTrue_includeIsFalse_doesNotGetValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: !"skip", .field("name", String.self)), .include(if: "include", .field("name", String.self)) @@ -1970,7 +1988,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_onSeperateFieldsForSameSelectionMergedAsOrCondition__givenSkipIsTrue_includeIsFalse_doesNotGetValuesForConditionalFields() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: "include" || !"skip", .field("name", String.self)) ]} @@ -1990,7 +2008,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { func test__booleanCondition_bothSkipAndInclude_mergedAsComplexLogicalCondition_correctlyEvaluatesConditionalSelections() async throws { // given - class GivenSelectionSet: MockSelectionSet { + class GivenSelectionSet: MockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .include(if: ("a" && !"b" && "c") || "d" || !"e", .field("name", String?.self)) ]} @@ -2040,7 +2058,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { } }) - class Character: MockSelectionSet { + class Character: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -2052,7 +2070,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var friend: Friend { __data["friend"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -2061,7 +2079,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { ]} } - class AsHero: ConcreteMockTypeCase { + class AsHero: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Hero } @@ -2071,7 +2089,7 @@ class GraphQLExecutor_SelectionSetMapper_FromResponse_Tests: XCTestCase { var friend: Friend { __data["friend"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } diff --git a/Tests/ApolloTests/GraphQLMapEncodingTests.swift b/Tests/ApolloTests/GraphQLMapEncodingTests.swift deleted file mode 100644 index 19cf146f6..000000000 --- a/Tests/ApolloTests/GraphQLMapEncodingTests.swift +++ /dev/null @@ -1,98 +0,0 @@ -import XCTest -@testable import Apollo -import ApolloAPI -import ApolloInternalTestHelpers -import StarWarsAPI - -#warning("TODO: Do we refactor these for [String: InputValue] or delete them?") -class GraphQLMapEncodingTests: XCTestCase { -// private struct MockGraphQLMapConvertible: GraphQLMapConvertible { -// let graphQLMap: GraphQLMap -// } - -// private func serializeAndDeserialize(_ map: GraphQLMap) -> NSDictionary { -// let input = MockGraphQLMapConvertible(graphQLMap: map) -// let data = try! JSONSerializationFormat.serialize(value: input.jsonValue as! [String: JSONEncodable?]) -// return try! JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary -// } -// -// func testEncodeValue() { -// let map: GraphQLMap = ["name": "Luke Skywalker"] -// XCTAssertEqual(serializeAndDeserialize(map), ["name": "Luke Skywalker"]) -// } -// -// func testEncodeOptionalValue() { -// let map: GraphQLMap = ["name": "Luke Skywalker" as Optional] -// XCTAssertEqual(serializeAndDeserialize(map), ["name": "Luke Skywalker"]) -// } -// -// func testEncodeOptionalValueWithValueMissing() { -// let map: GraphQLMap = ["name": Optional.none] -// XCTAssertEqual(serializeAndDeserialize(map), [:]) -// } -// -// func testEncodeOptionalValueWithExplicitNull() { -// let map: GraphQLMap = ["name": Optional.some(.none)] -// XCTAssertEqual(serializeAndDeserialize(map), ["name": NSNull()]) -// } -// -// func testEncodeEnumValue() { -// let map: GraphQLMap = ["favoriteEpisode": Episode.jedi] -// XCTAssertEqual(serializeAndDeserialize(map), ["favoriteEpisode": "JEDI"]) -// } -// -// func testEncodeMap() { -// let map: GraphQLMap = ["hero": ["name": "Luke Skywalker"]] -// XCTAssertEqual(serializeAndDeserialize(map), ["hero": ["name": "Luke Skywalker"]]) -// } -// -// func testEncodeOptionalMapWithValueMissing() { -// let map: GraphQLMap = ["hero": Optional.none] -// XCTAssertEqual(serializeAndDeserialize(map), [:]) -// } -// -// func testEncodeList() { -// let map: GraphQLMap = ["appearsIn": [.jedi, .empire] as [Episode]] -// XCTAssertEqual(serializeAndDeserialize(map), ["appearsIn": ["JEDI", "EMPIRE"]]) -// } -// -// func testEncodeOptionalList() { -// let map: GraphQLMap = ["appearsIn": [.jedi, .empire] as Optional<[Episode]?>] -// XCTAssertEqual(serializeAndDeserialize(map), ["appearsIn": ["JEDI", "EMPIRE"]]) -// } -// -// func testEncodeOptionalListWithValueMissing() { -// let map: GraphQLMap = ["appearsIn": Optional<[Episode]?>.none] -// XCTAssertEqual(serializeAndDeserialize(map), [:]) -// } -// -// func testEncodeInputObject() { -// let review = ReviewInput(stars: 5, commentary: "This is a great movie!") -// let map: GraphQLMap = ["review": review] -// XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5, "commentary": "This is a great movie!"]]) -// } -// -// func testEncodeInputObjectWithOptionalPropertyMissing() { -// let review = ReviewInput(stars: 5) -// let map: GraphQLMap = ["review": review] -// XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5]]) -// } -// -// func testEncodeInputObjectWithExplicitNilForOptionalProperty() { -// let review = ReviewInput(stars: 5, commentary: nil) -// let map: GraphQLMap = ["review": review] -// XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5]]) -// } -// -// func testEncodeInputObjectWithExplicitSomeNilForOptionalProperty() { -// let review = ReviewInput(stars: 5, commentary: .some(nil)) -// let map: GraphQLMap = ["review": review] -// XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5, "commentary": NSNull()]]) -// } -// -// func testEncodeInputObjectWithNestedInputObject() { -// let review = ReviewInput(stars: 5, favoriteColor: ColorInput(red: 0, green: 0, blue: 0)) -// let map: GraphQLMap = ["review": review] -// XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5, "favorite_color": ["red": 0, "blue": 0, "green": 0]]]) -// } -} diff --git a/Tests/ApolloTests/GraphQLOperationVariableEncodingTests.swift b/Tests/ApolloTests/GraphQLOperationVariableEncodingTests.swift new file mode 100644 index 000000000..03cd75189 --- /dev/null +++ b/Tests/ApolloTests/GraphQLOperationVariableEncodingTests.swift @@ -0,0 +1,93 @@ +import XCTest +@testable import Apollo +import ApolloAPI +import ApolloInternalTestHelpers +import StarWarsAPI + +class GraphQLOperationVariableEncodingTests: XCTestCase { + + private func serializeAndDeserialize(_ input: GraphQLOperation.Variables) -> NSDictionary { + let data = try! JSONSerializationFormat.serialize(value: input._jsonEncodableObject._jsonObject) + return try! JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary + } + + func testEncodeValue() { + let map: GraphQLOperation.Variables = ["name": "Luke Skywalker"] + XCTAssertEqual(serializeAndDeserialize(map), ["name": "Luke Skywalker"]) + } + + func testEncodeGraphQLNullableValue_withValue() { + let map: GraphQLOperation.Variables = ["name": GraphQLNullable.some("Luke Skywalker")] + XCTAssertEqual(serializeAndDeserialize(map), ["name": "Luke Skywalker"]) + } + + func testEncodeOptionalValueWithExplicitNil() { + let map: GraphQLOperation.Variables = ["name": GraphQLNullable.none] + XCTAssertEqual(serializeAndDeserialize(map), [:]) + } + + func testEncodeOptionalValueWithExplicitNull() { + let map: GraphQLOperation.Variables = ["name": GraphQLNullable.null] + XCTAssertEqual(serializeAndDeserialize(map), ["name": NSNull()]) + } + + func testEncodeEnumValue() { + let map: GraphQLOperation.Variables = ["favoriteEpisode": Episode.jedi] + XCTAssertEqual(serializeAndDeserialize(map), ["favoriteEpisode": "JEDI"]) + } + + func testEncodeMap() { + let map: GraphQLOperation.Variables = ["hero": ["name": "Luke Skywalker"]] + XCTAssertEqual(serializeAndDeserialize(map), ["hero": ["name": "Luke Skywalker"]]) + } + + func testEncodeOptionalInputObjectWithValueMissing() { + let map: GraphQLOperation.Variables = ["hero": GraphQLNullable.none] + XCTAssertEqual(serializeAndDeserialize(map), [:]) + } + + func testEncodeList() { + let map: GraphQLOperation.Variables = ["appearsIn": [.jedi, .empire] as [Episode]] + XCTAssertEqual(serializeAndDeserialize(map), ["appearsIn": ["JEDI", "EMPIRE"]]) + } + + func testEncodeOptionalListWithValue() { + let map: GraphQLOperation.Variables = ["appearsIn": GraphQLNullable<[Episode]>.some([.jedi, .empire])] + XCTAssertEqual(serializeAndDeserialize(map), ["appearsIn": ["JEDI", "EMPIRE"]]) + } + + func testEncodeOptionalListWithExplicitNil() { + let map: GraphQLOperation.Variables = ["appearsIn": GraphQLNullable<[Episode]>.none] + XCTAssertEqual(serializeAndDeserialize(map), [:]) + } + + func testEncodeInputObject() { + let review = ReviewInput(stars: 5, commentary: "This is a great movie!") + let map: GraphQLOperation.Variables = ["review": review] + XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5, "commentary": "This is a great movie!"]]) + } + + func testEncodeInputObjectWithOptionalPropertyMissing() { + let review = ReviewInput(stars: 5) + let map: GraphQLOperation.Variables = ["review": review] + XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5]]) + } + + func testEncodeInputObjectWithExplicitNilForOptionalProperty() { + let review = ReviewInput(stars: 5, commentary: nil) + let map: GraphQLOperation.Variables = ["review": review] + XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5]]) + } + + func testEncodeInputObjectWithExplicitNullForOptionalProperty() { + let review = ReviewInput(stars: 5, commentary: .null) + let map: GraphQLOperation.Variables = ["review": review] + XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5, "commentary": NSNull()]]) + } + + func testEncodeInputObjectWithNestedInputObject() { + let review = ReviewInput(stars: 5, favoriteColor: .some(ColorInput(red: 0, green: 0, blue: 0))) + let map: GraphQLOperation.Variables = ["review": review] + XCTAssertEqual(serializeAndDeserialize(map), ["review": ["stars": 5, "favorite_color": ["red": 0, "blue": 0, "green": 0]]]) + } +} diff --git a/Tests/ApolloTests/Interceptors/GraphQLInterceptor_ErrorHandling_Tests.swift b/Tests/ApolloTests/Interceptors/GraphQLInterceptor_ErrorHandling_Tests.swift index 3394888a1..37f98132e 100644 --- a/Tests/ApolloTests/Interceptors/GraphQLInterceptor_ErrorHandling_Tests.swift +++ b/Tests/ApolloTests/Interceptors/GraphQLInterceptor_ErrorHandling_Tests.swift @@ -31,281 +31,6 @@ class GraphQLInterceptor_ErrorHandling_Tests: XCTestCase, CacheDependentTesting, } // MARK: - Tests - #warning("TODO: Refactor these tests") - // - // func test__send__ErrorInterceptorGetsCalledAfterAnErrorIsReceived() { - // class ErrorInterceptor: ApolloErrorInterceptor { - // var error: (any Error)? = nil - // - // func handleErrorAsync( - // error: any Error, - // chain: any RequestChain, - // request: HTTPRequest, - // response: HTTPResponse?, - // completion: @escaping (Result, any Error>) -> Void) { - // - // self.error = error - // completion(.failure(error)) - // } - // } - // - // class TestProvider: InterceptorProvider { - // let errorInterceptor = ErrorInterceptor() - // func interceptors( - // for operation: Operation - // ) -> [any ApolloInterceptor] { - // return [ - // // An interceptor which will error without a response - // AutomaticPersistedQueryInterceptor() - // ] - // } - // - // func additionalErrorInterceptor(for operation: Operation) -> (any ApolloErrorInterceptor)? { - // return self.errorInterceptor - // } - // } - // - // let provider = TestProvider() - // let transport = RequestChainNetworkTransport(interceptorProvider: provider, - // endpointURL: TestURL.mockServer.url, - // autoPersistQueries: true) - // - // let expectation = self.expectation(description: "Hero name query complete") - // _ = transport.send(operation: MockQuery.mock()) { result in - // defer { - // expectation.fulfill() - // } - // switch result { - // case .success: - // XCTFail("This should not have succeeded") - // case .failure(let error): - // switch error { - // case AutomaticPersistedQueryInterceptor.APQError.noParsedResponse: - // // This is what we want. - // break - // default: - // XCTFail("Unexpected error: \(error)") - // } - // } - // } - // - // self.wait(for: [expectation], timeout: 1) - // - // switch provider.errorInterceptor.error { - // case .some(let error): - // switch error { - // case AutomaticPersistedQueryInterceptor.APQError.noParsedResponse: - // // Again, this is what we expect. - // break - // default: - // XCTFail("Unexpected error on the interceptor: \(error)") - // } - // case .none: - // XCTFail("Error interceptor did not receive an error!") - // } - // } - // - // func test__upload__ErrorInterceptorGetsCalledAfterAnErrorIsReceived() throws { - // class ErrorInterceptor: ApolloErrorInterceptor { - // var error: (any Error)? = nil - // - // func handleErrorAsync( - // error: any Error, - // chain: any RequestChain, - // request: HTTPRequest, - // response: HTTPResponse?, - // completion: @escaping (Result, any Error>) -> Void) { - // - // self.error = error - // completion(.failure(error)) - // } - // } - // - // class TestProvider: InterceptorProvider { - // let errorInterceptor = ErrorInterceptor() - // func interceptors( - // for operation: Operation - // ) -> [any ApolloInterceptor] { - // return [ - // // An interceptor which will error without a response - // ResponseCodeInterceptor() - // ] - // } - // - // func additionalErrorInterceptor(for operation: Operation) -> (any ApolloErrorInterceptor)? { - // return self.errorInterceptor - // } - // } - // - // let provider = TestProvider() - // let transport = RequestChainNetworkTransport(interceptorProvider: provider, - // endpointURL: TestURL.mockServer.url, - // autoPersistQueries: true) - // - // let fileURL = TestFileHelper.fileURLForFile(named: "a", extension: "txt") - // let file = try GraphQLFile( - // fieldName: "file", - // originalName: "a.txt", - // fileURL: fileURL - // ) - // - // let expectation = self.expectation(description: "Hero name query complete") - // _ = transport.upload(operation: MockQuery.mock(), files: [file], context: nil) { result in - // defer { - // expectation.fulfill() - // } - // switch result { - // case .success: - // XCTFail("This should not have succeeded") - // case .failure(let error): - // switch error { - // case ResponseCodeInterceptor.ResponseCodeError.invalidResponseCode: - // // This is what we want. - // break - // default: - // XCTFail("Unexpected error: \(error)") - // } - // } - // } - // - // self.wait(for: [expectation], timeout: 1) - // - // switch provider.errorInterceptor.error { - // case .some(let error): - // switch error { - // case ResponseCodeInterceptor.ResponseCodeError.invalidResponseCode: - // // Again, this is what we expect. - // break - // default: - // XCTFail("Unexpected error on the interceptor: \(error)") - // } - // case .none: - // XCTFail("Error interceptor did not receive an error!") - // } - // } - // - // func testErrorInterceptorGetsCalledInDefaultInterceptorProviderSubclass() { - // class ErrorInterceptor: ApolloErrorInterceptor { - // var error: (any Error)? = nil - // - // func handleErrorAsync( - // error: any Error, - // chain: any RequestChain, - // request: HTTPRequest, - // response: HTTPResponse?, - // completion: @escaping (Result, any Error>) -> Void) { - // - // self.error = error - // completion(.failure(error)) - // } - // } - // - // class TestProvider: DefaultInterceptorProvider { - // let errorInterceptor = ErrorInterceptor() - // - // override func interceptors( - // for operation: Operation - // ) -> [any ApolloInterceptor] { - // return [ - // // An interceptor which will error without a response - // AutomaticPersistedQueryInterceptor() - // ] - // } - // - // override func additionalErrorInterceptor(for operation: Operation) -> (any ApolloErrorInterceptor)? { - // return self.errorInterceptor - // } - // } - // - // let provider = TestProvider(store: ApolloStore()) - // let transport = RequestChainNetworkTransport(interceptorProvider: provider, - // endpointURL: TestURL.mockServer.url, - // autoPersistQueries: true) - // - // let expectation = self.expectation(description: "Hero name query complete") - // _ = transport.send(operation: MockQuery.mock()) { result in - // defer { - // expectation.fulfill() - // } - // switch result { - // case .success: - // XCTFail("This should not have succeeded") - // case .failure(let error): - // switch error { - // case AutomaticPersistedQueryInterceptor.APQError.noParsedResponse: - // // This is what we want. - // break - // default: - // XCTFail("Unexpected error: \(error)") - // } - // } - // } - // - // self.wait(for: [expectation], timeout: 1) - // - // switch provider.errorInterceptor.error { - // case .some(let error): - // switch error { - // case AutomaticPersistedQueryInterceptor.APQError.noParsedResponse: - // // Again, this is what we expect. - // break - // default: - // XCTFail("Unexpected error on the interceptor: \(error)") - // } - // case .none: - // XCTFail("Error interceptor did not receive an error!") - // } - // } - // - // func test__error__givenGraphqlError_withoutData_shouldReturnError() { - // // given - // let client = MockURLSessionClient( - // response: .mock( - // url: TestURL.mockServer.url, - // statusCode: 200, - // httpVersion: nil, - // headerFields: nil - // ), - // data: """ - // { - // "errors": [{ - // "message": "Bad request, could not start execution!" - // }] - // } - // """.data(using: .utf8) - // ) - // - // let interceptorProvider = DefaultInterceptorProvider(client: client, store: ApolloStore()) - // let interceptors = interceptorProvider.interceptors(for: MockQuery.mock()) - // let requestChain = InterceptorRequestChain(interceptors: interceptors) - // - // let expectation = expectation(description: "Response received") - // - // let request = JSONRequest( - // operation: MockQuery(), - // graphQLEndpoint: TestURL.mockServer.url, - // clientName: "test-client", - // clientVersion: "test-client-version" - // ) - // - // // when + then - // requestChain.kickoff(request: request) { result in - // defer { - // expectation.fulfill() - // } - // - // switch (result) { - // case let .success(data): - // XCTAssertEqual(data.errors, [ - // GraphQLError("Bad request, could not start execution!") - // ]) - // case let .failure(error): - // XCTFail("Unexpected failure result - \(error)") - // } - // } - // - // wait(for: [expectation], timeout: 1) - // } func test__errorInterceptor__givenNextInterceptorThrowsBeforeCallingNext__mapErrorIsCalledWithThrownError() async throws diff --git a/Tests/ApolloTests/JSONValueMatchers.swift b/Tests/ApolloTests/JSONValueMatchers.swift index ba45b2452..dc8a43a59 100644 --- a/Tests/ApolloTests/JSONValueMatchers.swift +++ b/Tests/ApolloTests/JSONValueMatchers.swift @@ -44,16 +44,3 @@ public func contain( return MatcherStatus(bool: matches) } } - -#warning("TODO: can we kill these two?") -extension AnyHashable: @retroactive ExpressibleByDictionaryLiteral { - public init(dictionaryLiteral elements: (AnyHashable, AnyHashable)...) { - self.init(Dictionary(elements)) - } -} - -extension AnyHashable: @retroactive ExpressibleByArrayLiteral { - public init(arrayLiteral elements: AnyHashable...) { - self.init(elements) - } -} diff --git a/Tests/ApolloTests/MultipartFormDataTests.swift b/Tests/ApolloTests/MultipartFormDataTests.swift index 985f9ee64..4776eaf0d 100644 --- a/Tests/ApolloTests/MultipartFormDataTests.swift +++ b/Tests/ApolloTests/MultipartFormDataTests.swift @@ -1,11 +1,3 @@ -// -// MultipartFormDataTests.swift -// Apollo -// -// Created by Ellen Shapiro on 9/21/20. -// Copyright © 2020 Apollo GraphQL. All rights reserved. -// - import XCTest import Apollo import ApolloInternalTestHelpers diff --git a/Tests/ApolloTests/RequestBodyCreatorTests.swift b/Tests/ApolloTests/RequestBodyCreatorTests.swift index 45770ab00..3b276b790 100644 --- a/Tests/ApolloTests/RequestBodyCreatorTests.swift +++ b/Tests/ApolloTests/RequestBodyCreatorTests.swift @@ -26,6 +26,20 @@ class RequestBodyCreatorTests: XCTestCase { ) } + struct TestCustomRequestBodyCreator: JSONRequestBodyCreator { + + var stubbedRequestBody: JSONEncodableDictionary = ["TestCustomRequestBodyCreator": "TestBodyValue"] + + func requestBody( + for operation: Operation, + sendQueryDocument: Bool, + autoPersistQuery: Bool + ) -> JSONEncodableDictionary { + stubbedRequestBody + } + } + + // MARK: - Tests func testRequestBodyWithApolloRequestBodyCreator() { @@ -98,12 +112,4 @@ class RequestBodyCreatorTests: XCTestCase { expect(actual["variables"]).to(equalJSONValue(["TestVar": "123"])) expect(actual["query"]).to(equalJSONValue("Test Query Document")) } - - #warning( - """ - TODO: Test generated input objects converted to variables correctly. - - nil variable value - - null variable value - """ - ) } diff --git a/Tests/ApolloTests/RetryToCountThenSucceedInterceptor.swift b/Tests/ApolloTests/RetryToCountThenSucceedInterceptor.swift index ac4640ef4..2d851ac20 100644 --- a/Tests/ApolloTests/RetryToCountThenSucceedInterceptor.swift +++ b/Tests/ApolloTests/RetryToCountThenSucceedInterceptor.swift @@ -1,11 +1,3 @@ -// -// RetryToCountThenSucceedInterceptor.swift -// ApolloTests -// -// Created by Ellen Shapiro on 8/19/20. -// Copyright © 2020 Apollo GraphQL. All rights reserved. -// - import Apollo import ApolloAPI import Foundation diff --git a/Tests/ApolloTests/SelectionSetTests.swift b/Tests/ApolloTests/SelectionSetTests.swift index 68b5fb6ec..7fb312c13 100644 --- a/Tests/ApolloTests/SelectionSetTests.swift +++ b/Tests/ApolloTests/SelectionSetTests.swift @@ -35,7 +35,7 @@ class SelectionSetTests: XCTestCase { func test__selection_givenOptionalField_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -60,7 +60,7 @@ class SelectionSetTests: XCTestCase { func test__selection_givenOptionalField_missingValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -84,7 +84,7 @@ class SelectionSetTests: XCTestCase { func test__selection_givenOptionalField_givenNilValue__returnsNil() async throws { // given - final class Hero: MockSelectionSet { + final class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -111,7 +111,7 @@ class SelectionSetTests: XCTestCase { func test__selection__nestedArrayOfScalar_nonNull_givenValue__returnsValue() async { // given - final class Hero: MockSelectionSet { + final class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -138,7 +138,7 @@ class SelectionSetTests: XCTestCase { func test__selection_givenRequiredEntityField_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -148,7 +148,7 @@ class SelectionSetTests: XCTestCase { var friend: Friend { __data["friend"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -175,7 +175,7 @@ class SelectionSetTests: XCTestCase { func test__selection_givenOptionalEntityField_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -204,7 +204,7 @@ class SelectionSetTests: XCTestCase { func test__selection_givenOptionalEntityField_givenNilValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -228,7 +228,7 @@ class SelectionSetTests: XCTestCase { func test__selection_givenOptionalEntityField_givenNullValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -255,7 +255,7 @@ class SelectionSetTests: XCTestCase { func test__selection__arrayOfEntity_nonNull_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -293,7 +293,7 @@ class SelectionSetTests: XCTestCase { func test__selection__arrayOfEntity_nullableEntity_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -331,7 +331,7 @@ class SelectionSetTests: XCTestCase { func test__selection__arrayOfEntity_nullableEntity_givenNilValueInList__returnsArrayWithNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -368,7 +368,7 @@ class SelectionSetTests: XCTestCase { func test__selection__arrayOfEntity_nullableList_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -406,7 +406,7 @@ class SelectionSetTests: XCTestCase { func test__selection__arrayOfEntity_nullableList_givenNoListValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -432,7 +432,7 @@ class SelectionSetTests: XCTestCase { func test__selection__nestedArrayOfEntity_nonNull_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -470,7 +470,7 @@ class SelectionSetTests: XCTestCase { func test__selection__nestedArrayOfEntity_nullableInnerList_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -508,7 +508,7 @@ class SelectionSetTests: XCTestCase { func test__selection__nestedArrayOfEntity_nullableInnerList_givenNilValues__returnsListWithNils() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -544,7 +544,7 @@ class SelectionSetTests: XCTestCase { func test__selection__nestedArrayOfEntity_nullableEntity_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -582,7 +582,7 @@ class SelectionSetTests: XCTestCase { func test__selection__nestedArrayOfEntity_nullableOuterList_givenValue__returnsValue() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -636,7 +636,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -648,7 +648,7 @@ class SelectionSetTests: XCTestCase { var asHuman: AsHuman? { _asInlineFragment() } var asDroid: AsDroid? { _asInlineFragment() } - class AsHuman: MockTypeCase { + class AsHuman: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -657,7 +657,7 @@ class SelectionSetTests: XCTestCase { ]} } - class AsDroid: MockTypeCase { + class AsDroid: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Droid } @@ -695,7 +695,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -705,7 +705,7 @@ class SelectionSetTests: XCTestCase { var asHumanoid: AsHumanoid? { _asInlineFragment() } - class AsHumanoid: MockTypeCase { + class AsHumanoid: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Humanoid } @@ -743,7 +743,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -753,7 +753,7 @@ class SelectionSetTests: XCTestCase { var asHumanoid: AsHumanoid? { _asInlineFragment() } - class AsHumanoid: MockTypeCase { + class AsHumanoid: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Humanoid } @@ -791,7 +791,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -801,7 +801,7 @@ class SelectionSetTests: XCTestCase { var asCharacter: AsCharacter? { _asInlineFragment() } - class AsCharacter: MockTypeCase { + class AsCharacter: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -838,7 +838,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -848,7 +848,7 @@ class SelectionSetTests: XCTestCase { var asCharacter: AsCharacter? { _asInlineFragment() } - class AsCharacter: MockTypeCase { + class AsCharacter: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -885,7 +885,7 @@ class SelectionSetTests: XCTestCase { } }) - class RootData: MockSelectionSet { + class RootData: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Query } @@ -895,7 +895,7 @@ class SelectionSetTests: XCTestCase { var asAdminQuery: AsAdminQuery? { _asInlineFragment() } - class AsAdminQuery: MockTypeCase { + class AsAdminQuery: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.AdminQuery } @@ -923,9 +923,9 @@ class SelectionSetTests: XCTestCase { func test__toFragment_givenInclusionCondition_true_returnsFragment() async { // given - class GivenFragment: MockFragment { } + class GivenFragment: MockFragment, @unchecked Sendable { } - class Hero: AbstractMockSelectionSet { + class Hero: AbstractMockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .include(if: "includeFragment", .fragment(GivenFragment.self)) @@ -953,9 +953,9 @@ class SelectionSetTests: XCTestCase { func test__toFragment_givenInclusionCondition_false_returnsNil() async { // given - class GivenFragment: MockFragment { } + class GivenFragment: MockFragment, @unchecked Sendable { } - class Hero: AbstractMockSelectionSet { + class Hero: AbstractMockSelectionSet, @unchecked Sendable { override class var __selections: [Selection] {[ .field("__typename", String.self), .include(if: "includeFragment", .fragment(GivenFragment.self)) @@ -998,7 +998,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1008,7 +1008,7 @@ class SelectionSetTests: XCTestCase { var asAnimal: AsAnimal? { _asInlineFragment() } - class AsAnimal: ConcreteMockTypeCase { + class AsAnimal: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Animal } @@ -1057,7 +1057,7 @@ class SelectionSetTests: XCTestCase { } }) - class Data: MockSelectionSet { + class Data: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Query } @@ -1076,7 +1076,7 @@ class SelectionSetTests: XCTestCase { ], fulfilledFragments: [ObjectIdentifier(Self.self)])) } - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1086,7 +1086,7 @@ class SelectionSetTests: XCTestCase { var asAnimal: AsAnimal? { _asInlineFragment() } - class AsAnimal: ConcreteMockTypeCase { + class AsAnimal: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Animal } @@ -1134,7 +1134,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1144,7 +1144,7 @@ class SelectionSetTests: XCTestCase { var ifA: IfA? { _asInlineFragment() } - class IfA: ConcreteMockTypeCase { + class IfA: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata typealias RootEntityType = Hero @@ -1190,7 +1190,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1202,7 +1202,7 @@ class SelectionSetTests: XCTestCase { var ifA: IfA? { _asInlineFragment() } var ifB: IfB? { _asInlineFragment() } - class IfA: ConcreteMockTypeCase { + class IfA: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ @@ -1222,7 +1222,7 @@ class SelectionSetTests: XCTestCase { ])) } } - class IfB: ConcreteMockTypeCase { + class IfB: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ @@ -1253,7 +1253,7 @@ class SelectionSetTests: XCTestCase { } }) - class Data: MockSelectionSet { + class Data: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Query } @@ -1272,7 +1272,7 @@ class SelectionSetTests: XCTestCase { ], fulfilledFragments: [ObjectIdentifier(Self.self)])) } - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1284,7 +1284,7 @@ class SelectionSetTests: XCTestCase { var ifA: IfA? { _asInlineFragment() } var ifB: IfB? { _asInlineFragment() } - class IfA: ConcreteMockTypeCase { + class IfA: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ @@ -1308,7 +1308,7 @@ class SelectionSetTests: XCTestCase { ])) } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1318,7 +1318,7 @@ class SelectionSetTests: XCTestCase { var ifNotC: IfNotC? { _asInlineFragment() } - class IfNotC: ConcreteMockTypeCase { + class IfNotC: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ @@ -1341,7 +1341,7 @@ class SelectionSetTests: XCTestCase { } } - class IfB: ConcreteMockTypeCase { + class IfB: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[]} @@ -1386,7 +1386,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1396,7 +1396,7 @@ class SelectionSetTests: XCTestCase { var ifA: IfA? { _asInlineFragment() } - class IfA: ConcreteMockTypeCase { + class IfA: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ @@ -1442,7 +1442,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1496,7 +1496,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1515,7 +1515,7 @@ class SelectionSetTests: XCTestCase { ], fulfilledFragments: [ObjectIdentifier(Self.self)])) } - class Child: MockSelectionSet { + class Child: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1570,7 +1570,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1589,7 +1589,7 @@ class SelectionSetTests: XCTestCase { ], fulfilledFragments: [ObjectIdentifier(Self.self)])) } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1636,7 +1636,7 @@ class SelectionSetTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -1716,7 +1716,7 @@ class SelectionSetTests: XCTestCase { // MARK Selection dict intializer func test__selectionDictInitializer_givenNonOptionalEntityField_givenValue__setsFieldDataCorrectly() async { - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1741,7 +1741,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_givenOptionalEntityField_givenNilValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1765,7 +1765,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_giveDictionaryEntityFiled_givenNonOptionalValue__setsFieldDataCorrectly() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1775,7 +1775,7 @@ class SelectionSetTests: XCTestCase { var friend: Friend { __data["friend"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1798,7 +1798,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_giveOptionalDictionaryEntityFiled_givenNilValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1808,7 +1808,7 @@ class SelectionSetTests: XCTestCase { var friend: Friend? { __data["friend"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1830,7 +1830,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_giveDictionaryArrayEntityField_givenNonOptionalValue__setsFieldDataCorrectly() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1840,7 +1840,7 @@ class SelectionSetTests: XCTestCase { var friends: [Friend] { __data["friends"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1867,7 +1867,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_giveOptionalDictionaryArrayEntityField_givenNilValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1877,7 +1877,7 @@ class SelectionSetTests: XCTestCase { var friends: [Friend]? { __data["friends"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1899,7 +1899,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_giveDictionaryArrayEntityField_givenEmptyValue__returnsEmpty() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1909,7 +1909,7 @@ class SelectionSetTests: XCTestCase { var friends: [Friend] { __data["friends"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1932,7 +1932,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_giveNestedListEntityField_givenNonOptionalValue__setsFieldDataCorrectly() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ @@ -1970,7 +1970,7 @@ class SelectionSetTests: XCTestCase { func test__selectionDictInitializer_giveOptionalNestedListEntityField_givenNilValue__returnsNil() async { // given - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __selections: [Selection] {[ diff --git a/Tests/ApolloTests/SelectionSet_JSONInitializerTests.swift b/Tests/ApolloTests/SelectionSet_JSONInitializerTests.swift index 9a9be692e..d987eccfa 100644 --- a/Tests/ApolloTests/SelectionSet_JSONInitializerTests.swift +++ b/Tests/ApolloTests/SelectionSet_JSONInitializerTests.swift @@ -20,7 +20,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { } }) - class GivenFragment: MockFragment { + class GivenFragment: MockFragment, @unchecked Sendable { override class var __parentType: any ParentType { Types.Human } override class var __selections: [Selection] {[ .field("height", Float.self) @@ -28,7 +28,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { var height: Float { __data["height"] } } - class Hero: AbstractMockSelectionSet { + class Hero: AbstractMockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -70,7 +70,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -105,7 +105,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { } }) - class Hero: MockSelectionSet { + class Hero: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -142,7 +142,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { } }) - class Character: MockSelectionSet { + class Character: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -157,7 +157,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { var asHero: AsHero? { _asInlineFragment() } var asHuman: AsHuman? { _asInlineFragment() } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -168,7 +168,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { var asHuman: AsHuman? { _asInlineFragment() } - class AsHuman: ConcreteMockTypeCase { + class AsHuman: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -180,7 +180,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { } } - class AsHero: ConcreteMockTypeCase { + class AsHero: ConcreteMockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Hero } @@ -190,7 +190,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { var friend: Friend { __data["friend"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -202,7 +202,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { } } - class AsHuman: MockTypeCase { + class AsHuman: MockTypeCase, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } @@ -213,7 +213,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { var name: String? { __data["name"] } var friend: Friend { __data["friend"] } - class Friend: MockSelectionSet { + class Friend: MockSelectionSet, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Character } @@ -221,7 +221,7 @@ class SelectionSet_JSONInitializerTests: XCTestCase { var heroName: String? { __data["heroName"] } var asHuman: AsHuman? { _asInlineFragment() } - class AsHuman: ConcreteMockTypeCase, CompositeInlineFragment { + class AsHuman: ConcreteMockTypeCase, CompositeInlineFragment, @unchecked Sendable { typealias Schema = MockSchemaMetadata override class var __parentType: any ParentType { Types.Human } diff --git a/Tests/ApolloTests/TestCustomRequestBodyCreator.swift b/Tests/ApolloTests/TestCustomRequestBodyCreator.swift deleted file mode 100644 index 7ae0a828b..000000000 --- a/Tests/ApolloTests/TestCustomRequestBodyCreator.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// TestCustomRequestBodyCreator.swift -// Apollo -// -// Created by Kim de Vos on 02/10/2019. -// Copyright © 2019 Apollo GraphQL. All rights reserved. -// - -import Apollo -import ApolloAPI - -struct TestCustomRequestBodyCreator: JSONRequestBodyCreator { - - var stubbedRequestBody: JSONEncodableDictionary = ["TestCustomRequestBodyCreator": "TestBodyValue"] - - func requestBody( - for operation: Operation, - sendQueryDocument: Bool, - autoPersistQuery: Bool - ) -> JSONEncodableDictionary { - stubbedRequestBody - } -} diff --git a/Tests/ApolloTests/TestMockTests.swift b/Tests/ApolloTests/TestMockTests.swift index 6365a4b83..ec4c31abf 100644 --- a/Tests/ApolloTests/TestMockTests.swift +++ b/Tests/ApolloTests/TestMockTests.swift @@ -345,7 +345,7 @@ class TestMockTests: XCTestCase { func test__convertToSelectionSet_givenSelectionSetWithVariableForInclusionCondition_isTrue_canAccessConditionalField() async throws { // given - final class Animal: TestMockSchema.MockSelectionSet { + final class Animal: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .include(if: "a", .inlineFragment(IfA.self)), @@ -353,7 +353,7 @@ class TestMockTests: XCTestCase { var ifA: IfA? { _asInlineFragment() } - final class IfA: TestMockSchema.ConcreteMockTypeCase { + final class IfA: TestMockSchema.ConcreteMockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .field("species", String.self), @@ -375,7 +375,7 @@ class TestMockTests: XCTestCase { func test__convertToSelectionSet_givenSelectionSetWithVariableForInclusionCondition_isFalse_canNotAccessConditionalField() async throws { // given - final class Animal: TestMockSchema.MockSelectionSet { + final class Animal: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .include(if: "a", .inlineFragment(IfA.self)), @@ -383,7 +383,7 @@ class TestMockTests: XCTestCase { var ifA: IfA? { _asInlineFragment() } - final class IfA: TestMockSchema.ConcreteMockTypeCase { + final class IfA: TestMockSchema.ConcreteMockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .field("species", String.self), @@ -405,7 +405,7 @@ class TestMockTests: XCTestCase { func test__convertToSelectionSet_givenSelectionSetWithTypeCondition_canConvert_canAccessConditionalField() async throws { // given - final class Animal: TestMockSchema.MockSelectionSet { + final class Animal: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .inlineFragment(AsDog.self), @@ -413,7 +413,7 @@ class TestMockTests: XCTestCase { var asDog: AsDog? { _asInlineFragment() } - final class AsDog: TestMockSchema.ConcreteMockTypeCase { + final class AsDog: TestMockSchema.ConcreteMockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Types.Dog } override class var __selections: [Selection] {[ .field("species", String.self), @@ -435,7 +435,7 @@ class TestMockTests: XCTestCase { func test__convertToSelectionSet_givenSelectionSetWithTypeCondition_canNotConvert_canNotAccessConditionalField() async throws { // given - final class Animal: TestMockSchema.MockSelectionSet { + final class Animal: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .inlineFragment(AsDog.self), @@ -443,7 +443,7 @@ class TestMockTests: XCTestCase { var asDog: AsDog? { _asInlineFragment() } - final class AsDog: TestMockSchema.ConcreteMockTypeCase { + final class AsDog: TestMockSchema.ConcreteMockTypeCase, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Types.Dog } override class var __selections: [Selection] {[ .field("species", String.self), @@ -465,7 +465,7 @@ class TestMockTests: XCTestCase { func test__convertToSelectionSet_givenRequiredFieldNotInitialized_doesNotThrow() async throws { // given - final class Animal: TestMockSchema.MockSelectionSet { + final class Animal: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .field("species", String.self), @@ -485,7 +485,7 @@ class TestMockTests: XCTestCase { func test__convertToSelectionSet__givenGraphQLEnumField__canAccessField() async throws { // given - final class Animal: TestMockSchema.MockSelectionSet { + final class Animal: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .field("speciesType", GraphQLEnum.self), @@ -506,7 +506,7 @@ class TestMockTests: XCTestCase { func test__convertToSelectionSet__setNestedListOfObjectsField__canAccessField() async throws { // given - final class Animal: TestMockSchema.MockSelectionSet { + final class Animal: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Interfaces.Animal } override class var __selections: [Selection] {[ .field("nestedListOfObjects", [[CatData]].self), @@ -514,7 +514,7 @@ class TestMockTests: XCTestCase { var nestedListOfObjects: [[CatData]] { __data["nestedListOfObjects"] } - final class CatData: TestMockSchema.MockSelectionSet { + final class CatData: TestMockSchema.MockSelectionSet, @unchecked Sendable { override class var __parentType: any ParentType { TestMockSchema.Types.Cat } override class var __selections: [Selection] {[ .field("species", String.self), diff --git a/Tests/ApolloTests/URL+QueryDict.swift b/Tests/ApolloTests/URL+QueryDict.swift index 2fa8468e7..6f3e00c08 100644 --- a/Tests/ApolloTests/URL+QueryDict.swift +++ b/Tests/ApolloTests/URL+QueryDict.swift @@ -1,11 +1,3 @@ -// -// URL+QueryDict.swift -// ApolloTests -// -// Created by Ellen Shapiro on 10/14/19. -// Copyright © 2019 Apollo GraphQL. All rights reserved. -// - import Foundation extension URL { diff --git a/Tests/ApolloTests/UploadRequestTests.swift b/Tests/ApolloTests/UploadRequestTests.swift index 6881bbbea..391198d94 100644 --- a/Tests/ApolloTests/UploadRequestTests.swift +++ b/Tests/ApolloTests/UploadRequestTests.swift @@ -81,8 +81,6 @@ class UploadRequestTests: XCTestCase { writeResultsToCache: false ) - let urlRequest = try uploadRequest.toURLRequest() - let multipartData = try uploadRequest.requestMultipartFormData() let actual = try multipartData.toTestString() @@ -150,8 +148,6 @@ class UploadRequestTests: XCTestCase { writeResultsToCache: false ) - let urlRequest = try uploadRequest.toURLRequest() - let multipartData = try uploadRequest.requestMultipartFormData() let actual = try multipartData.toTestString() diff --git a/apollo-ios/Sources/Apollo/ApolloClient.swift b/apollo-ios/Sources/Apollo/ApolloClient.swift index 54f2996e8..dc554a2be 100644 --- a/apollo-ios/Sources/Apollo/ApolloClient.swift +++ b/apollo-ios/Sources/Apollo/ApolloClient.swift @@ -19,8 +19,10 @@ public struct RequestConfiguration: Sendable { } // MARK: - -/// The `ApolloClient` class implements the core API for Apollo by conforming to `ApolloClientProtocol`. -public final class ApolloClient: ApolloClientProtocol, Sendable { + +/// `ApolloClient` is the primary public entry point for interacting with a GraphQL server and a local GraphQL +/// normalized cache. This class provides the +public final class ApolloClient: Sendable { let networkTransport: any NetworkTransport @@ -72,7 +74,12 @@ public final class ApolloClient: ApolloClientProtocol, Sendable { self.context = ClientContext(clientAwarenessMetadata: clientAwarenessMetadata) } - /// Creates a client with a `RequestChainNetworkTransport` connecting to the specified URL. + /// Convenience initializer that creates a client with a default network transport and cache setup. + /// + /// This initializer creates a client that uses an in-memory only cache and a `RequestChainNetworkTransport` + /// connecting to the specified URL with a default interceptor setup. + /// + /// The ``InMemoryNormalizedCache`` used by this client does not persist data between application runs. /// /// - Parameters: /// - url: The URL of a GraphQL server to connect to. diff --git a/apollo-ios/Sources/Apollo/ApolloClientProtocol.swift b/apollo-ios/Sources/Apollo/ApolloClientProtocol.swift deleted file mode 100644 index 0e9e0b35c..000000000 --- a/apollo-ios/Sources/Apollo/ApolloClientProtocol.swift +++ /dev/null @@ -1,171 +0,0 @@ -import Foundation -#if !COCOAPODS -import ApolloAPI -#endif - -/// The `ApolloClientProtocol` provides the core API for Apollo. This API provides methods to fetch and watch queries, and to perform mutations. -#warning("TODO: move this to ApolloTestSupport? In test support, should have extension that implements all cache policy type functions from fetch behavior function") -public protocol ApolloClientProtocol: AnyObject, Sendable { - - /// A store used as a local cache. - var store: ApolloStore { get } - - /// Clears the `NormalizedCache` of the client's `ApolloStore`. - func clearCache() async throws - - // MARK: - Fetch Functions - - /// Fetches a query from the server or from the local cache, depending on the current contents of the cache and the - /// specified cache policy. - /// - /// - Parameters: - /// - query: The query to fetch. - /// - fetchBehavior: A ``FetchBehavior`` that specifies when results should be fetched from the server or from the - /// local cache. - /// - requestConfiguration: A configuration used to configure per-request behaviors for this request - func fetch( - query: Query, - fetchBehavior: FetchBehavior, - requestConfiguration: RequestConfiguration? - ) throws -> AsyncThrowingStream, any Swift.Error> - - func fetch( - query: Query, - cachePolicy: CachePolicy.Query.CacheAndNetwork, - requestConfiguration: RequestConfiguration? - ) throws -> AsyncThrowingStream, any Swift.Error> - where Query.ResponseFormat == SingleResponseFormat - - func fetch( - query: Query, - cachePolicy: CachePolicy.Query.SingleResponse, - requestConfiguration: RequestConfiguration? - ) throws -> AsyncThrowingStream, any Swift.Error> - where Query.ResponseFormat == IncrementalDeferredResponseFormat - - func fetch( - query: Query, - cachePolicy: CachePolicy.Query.CacheAndNetwork, - requestConfiguration: RequestConfiguration? - ) throws -> AsyncThrowingStream, any Swift.Error> - where Query.ResponseFormat == IncrementalDeferredResponseFormat - - /// Fetches a query from the local cache. Does not attempt to fetch results from the server. - /// - /// - Parameters: - /// - query: The query to fetch. - /// - cachePolicy: The `CacheOnly` cache policy. - /// - requestConfiguration: A configuration used to configure per-request behaviors for this request - /// - /// - Returns: The response loaded from the cache. On a cache miss, this will return `nil`. - func fetch( - query: Query, - cachePolicy: CachePolicy.Query.CacheOnly, - requestConfiguration: RequestConfiguration? - ) async throws -> GraphQLResponse? - - // MARK: - Watch Functions - - /// Watches a query by first fetching an initial result from the server or from the local cache, depending on the - /// current contents of the cache and the specified cache policy. After the initial fetch, the returned query - /// watcher object will get notified whenever any of the data the query result depends on changes in the local cache, - /// and calls the result handler again with the new result. - /// - /// - Parameters: - /// - query: The query to fetch. - /// - fetchBehavior: A ``FetchBehavior`` that specifies when results should be fetched from the server or from the - /// local cache. - /// - requestConfiguration: A ``RequestConfiguration`` to use for the watcher's initial fetch. If `nil` the - /// client's `defaultRequestConfiguration` will be used. - /// - refetchOnFailedUpdates: Should the watcher perform a network fetch when it's watched - /// objects have changed, but reloading them from the cache fails. Defaults to `true`. - /// - resultHandler: A closure that is called when query results are available or when an error occurs. - /// - Returns: A query watcher object that can be used to control the watching behavior. - func watch( - query: Query, - fetchBehavior: FetchBehavior, - requestConfiguration: RequestConfiguration?, - refetchOnFailedUpdates: Bool, - resultHandler: @escaping GraphQLQueryWatcher.ResultHandler - ) async -> GraphQLQueryWatcher - - func watch( - query: Query, - cachePolicy: CachePolicy.Query.SingleResponse, - requestConfiguration: RequestConfiguration?, - refetchOnFailedUpdates: Bool, - resultHandler: @escaping GraphQLQueryWatcher.ResultHandler - ) async -> GraphQLQueryWatcher - - func watch( - query: Query, - cachePolicy: CachePolicy.Query.CacheAndNetwork, - requestConfiguration: RequestConfiguration?, - refetchOnFailedUpdates: Bool, - resultHandler: @escaping GraphQLQueryWatcher.ResultHandler - ) async -> GraphQLQueryWatcher - - func watch( - query: Query, - cachePolicy: CachePolicy.Query.CacheOnly, - requestConfiguration: RequestConfiguration?, - refetchOnFailedUpdates: Bool, - resultHandler: @escaping GraphQLQueryWatcher.ResultHandler - ) async -> GraphQLQueryWatcher - - // MARK: - Mutation Functions - - /// Performs a mutation by sending it to the server. - /// - /// Mutations always need to send their mutation data to the server, so there is no `cachePolicy` or `fetchBehavior` - /// parameter. - /// - /// - Parameters: - /// - mutation: The mutation to perform. - /// - requestConfiguration: A ``RequestConfiguration`` to use for the watcher's initial fetch. If `nil` the - /// client's `defaultRequestConfiguration` will be used. - func perform( - mutation: Mutation, - requestConfiguration: RequestConfiguration? - ) async throws -> GraphQLResponse - where Mutation.ResponseFormat == SingleResponseFormat - - func perform( - mutation: Mutation, - requestConfiguration: RequestConfiguration? - ) throws -> AsyncThrowingStream, any Swift.Error> - where Mutation.ResponseFormat == IncrementalDeferredResponseFormat - - // MARK: - Upload Functions - - /// Uploads the given files with the given operation. - /// - /// - Parameters: - /// - operation: The operation to send - /// - files: An array of `GraphQLFile` objects to send. - /// - requestConfiguration: A ``RequestConfiguration`` to use for the watcher's initial fetch. If `nil` the - /// client's `defaultRequestConfiguration` will be used. - /// - /// - Note: An error will be thrown If your `networkTransport` does not also conform to `UploadingNetworkTransport`. - func upload( - operation: Operation, - files: [GraphQLFile], - requestConfiguration: RequestConfiguration? - ) async throws -> GraphQLResponse - where Operation.ResponseFormat == SingleResponseFormat - - // MARK: - Subscription Functions - - /// Subscribe to a subscription - /// - /// - Parameters: - /// - subscription: The subscription to subscribe to. - /// - cachePolicy: A cache policy that specifies when results should be fetched from the server or from the - /// local cache. - func subscribe( - subscription: Subscription, - cachePolicy: CachePolicy.Subscription, - requestConfiguration: RequestConfiguration? - ) async throws -> AsyncThrowingStream, any Swift.Error> - -} diff --git a/apollo-ios/Sources/Apollo/GraphQLQueryWatcher.swift b/apollo-ios/Sources/Apollo/GraphQLQueryWatcher.swift index 345331cfc..3f97c20c6 100644 --- a/apollo-ios/Sources/Apollo/GraphQLQueryWatcher.swift +++ b/apollo-ios/Sources/Apollo/GraphQLQueryWatcher.swift @@ -30,7 +30,7 @@ public actor GraphQLQueryWatcher: ApolloStoreSubscriber { /// If set to `false`, the watcher will not receive updates if the cache load fails. public let refetchOnFailedUpdates: Bool - public var cancelled: Bool = false + public private(set) var cancelled: Bool = false private var lastFetch: FetchContext? private var dependentKeys: Set? = nil diff --git a/apollo-ios/Sources/ApolloAPI/GraphQLOperation.swift b/apollo-ios/Sources/ApolloAPI/GraphQLOperation.swift index f8144a76c..3208fb04a 100644 --- a/apollo-ios/Sources/ApolloAPI/GraphQLOperation.swift +++ b/apollo-ios/Sources/ApolloAPI/GraphQLOperation.swift @@ -196,15 +196,6 @@ where Wrapped: GraphQLOperationVariableValue { } } -extension Optional: GraphQLOperationVariableValue where Wrapped: GraphQLOperationVariableValue { - @inlinable public var _jsonEncodableValue: (any JSONEncodable)? { - switch self { - case .none: return nil - case let .some(value): return value._jsonEncodableValue - } - } -} - extension JSONEncodable where Self: GraphQLOperationVariableValue { @inlinable public var _jsonEncodableValue: (any JSONEncodable)? { self } }