Skip to content

Commit

Permalink
[ApolloPagination] Update PaginationOutput to operate over `GraphQL…
Browse files Browse the repository at this point in the history
…Result`s (#428)
  • Loading branch information
Iron-Ham authored Aug 14, 2024
1 parent 27a11c6 commit 07e12aa
Show file tree
Hide file tree
Showing 19 changed files with 554 additions and 850 deletions.
223 changes: 137 additions & 86 deletions Tests/ApolloPaginationTests/AsyncGraphQLQueryPagerTests.swift

Large diffs are not rendered by default.

73 changes: 38 additions & 35 deletions Tests/ApolloPaginationTests/BidirectionalPaginationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
let pager = createPager()
let serverExpectation = Mocks.Hero.BidirectionalFriendsQuery.expectationForFirstFetchInMiddleOfList(server: server)

var results: [Result<(PaginationOutput<Query, Query>, UpdateSource), any Error>] = []
var results: [Result<PaginationOutput<Query, Query>, any Error>] = []
let firstPageExpectation = expectation(description: "First page")
var subscription = await pager.subscribe(onUpdate: { _ in
firstPageExpectation.fulfill()
Expand All @@ -102,11 +102,11 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
subscription.cancel()
var result = try await XCTUnwrapping(await pager.currentValue)
results.append(result)
XCTAssertSuccessResult(result) { (output, source) in
XCTAssertSuccessResult(result) { output in
XCTAssertTrue(output.nextPages.isEmpty)
XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(output.initialPage?.source, .server)
}

let secondPageExpectation = Mocks.Hero.BidirectionalFriendsQuery.expectationForLastPage(server: server)
Expand All @@ -123,17 +123,17 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
result = try await XCTUnwrapping(await pager.currentValue)
results.append(result)

try XCTAssertSuccessResult(result) { (output, source) in
try XCTAssertSuccessResult(result) { output in
// Assert first page is unchanged
XCTAssertEqual(try? results.first?.get().0.initialPage, try? results.last?.get().0.initialPage)
XCTAssertEqual(try? results.first?.get().initialPage, try? results.last?.get().initialPage)

XCTAssertFalse(output.nextPages.isEmpty)
XCTAssertEqual(output.nextPages.count, 1)
XCTAssertTrue(output.previousPages.isEmpty)
XCTAssertEqual(output.previousPages.count, 0)
let page = try XCTUnwrap(output.nextPages.first)
XCTAssertEqual(page.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(page.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(page.source, .server)
}
var previousCount = await pager.previousPageVarMap.values.count
XCTAssertEqual(previousCount, 0)
Expand All @@ -155,17 +155,17 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
result = try await XCTUnwrapping(await pager.currentValue)
results.append(result)

try XCTAssertSuccessResult(result) { (output, source) in
try XCTAssertSuccessResult(result) { output in
// Assert first page is unchanged
XCTAssertEqual(try? results.first?.get().0.initialPage, try? results.last?.get().0.initialPage)
XCTAssertEqual(try? results.first?.get().initialPage, try? results.last?.get().initialPage)

XCTAssertFalse(output.nextPages.isEmpty)
XCTAssertEqual(output.nextPages.count, 1)
XCTAssertFalse(output.previousPages.isEmpty)
XCTAssertEqual(output.previousPages.count, 1)
let page = try XCTUnwrap(output.previousPages.first)
XCTAssertEqual(page.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(page.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(page.source, .server)
}
previousCount = await pager.previousPageVarMap.values.count
XCTAssertEqual(previousCount, 1)
Expand All @@ -190,12 +190,14 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
timeout: 5
)

let (result, _) = try await XCTUnwrapping(try await pager.currentValue?.get())
let result = try await XCTUnwrapping(try await pager.currentValue?.get())
XCTAssertFalse(result.previousPages.isEmpty)
XCTAssertEqual(result.initialPage.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(result.initialPage?.data?.hero.friendsConnection.friends.count, 1)
XCTAssertFalse(result.nextPages.isEmpty)

let friends = (result.previousPages.first?.hero.friendsConnection.friends ?? []) + result.initialPage.hero.friendsConnection.friends + (result.nextPages.first?.hero.friendsConnection.friends ?? [])
let friends = (
result.previousPages.compactMap(\.data?.hero.friendsConnection.friends)
+ result.nextPages.compactMap(\.data?.hero.friendsConnection.friends)
).flatMap { $0 } + (result.initialPage?.data?.hero.friendsConnection.friends ?? [])

XCTAssertEqual(Set(friends).count, 3)
}
Expand All @@ -206,7 +208,7 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
let pager = GraphQLQueryPagerCoordinator(pager: createPager())
let serverExpectation = Mocks.Hero.BidirectionalFriendsQuery.expectationForFirstFetchInMiddleOfList(server: server)

var results: [Result<(PaginationOutput<Query, Query>, UpdateSource), any Error>] = []
var results: [Result<PaginationOutput<Query, Query>, any Error>] = []
let firstPageExpectation = expectation(description: "First page")
var subscription = await pager.publisher.sink { _ in
firstPageExpectation.fulfill()
Expand All @@ -216,11 +218,11 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
subscription.cancel()
var result = try await XCTUnwrapping(await pager.pager.currentValue)
results.append(result)
XCTAssertSuccessResult(result) { (output, source) in
XCTAssertSuccessResult(result) { output in
XCTAssertTrue(output.nextPages.isEmpty)
XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(output.initialPage?.source, .server)
}

let secondPageExpectation = Mocks.Hero.BidirectionalFriendsQuery.expectationForLastPage(server: server)
Expand All @@ -237,17 +239,17 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
result = try await XCTUnwrapping(await pager.pager.currentValue)
results.append(result)

try XCTAssertSuccessResult(result) { (output, source) in
try XCTAssertSuccessResult(result) { output in
// Assert first page is unchanged
XCTAssertEqual(try? results.first?.get().0.initialPage, try? results.last?.get().0.initialPage)
XCTAssertEqual(try? results.first?.get().initialPage, try? results.last?.get().initialPage)

XCTAssertFalse(output.nextPages.isEmpty)
XCTAssertEqual(output.nextPages.count, 1)
XCTAssertTrue(output.previousPages.isEmpty)
XCTAssertEqual(output.previousPages.count, 0)
let page = try XCTUnwrap(output.nextPages.first)
XCTAssertEqual(page.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(page.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(page.source, .server)
}

let previousPageExpectation = Mocks.Hero.BidirectionalFriendsQuery.expectationForPreviousPage(server: server)
Expand All @@ -265,17 +267,17 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
result = try await XCTUnwrapping(await pager.pager.currentValue)
results.append(result)

try XCTAssertSuccessResult(result) { (output, source) in
try XCTAssertSuccessResult(result) { output in
// Assert first page is unchanged
XCTAssertEqual(try? results.first?.get().0.initialPage, try? results.last?.get().0.initialPage)
XCTAssertEqual(try? results.first?.get().initialPage, try? results.last?.get().initialPage)

XCTAssertFalse(output.nextPages.isEmpty)
XCTAssertEqual(output.nextPages.count, 1)
XCTAssertFalse(output.previousPages.isEmpty)
XCTAssertEqual(output.previousPages.count, 1)
let page = try XCTUnwrap(output.previousPages.first)
XCTAssertEqual(page.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(page.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(page.source, .server)
}
}

Expand All @@ -296,14 +298,15 @@ final class BidirectionalPaginationTests: XCTestCase, CacheDependentTesting {
timeout: 5
)

let result = try await XCTUnwrapping(try await pager.pager.currentValue?.get().0)
let result = try await XCTUnwrapping(try await pager.pager.currentValue?.get())
XCTAssertFalse(result.previousPages.isEmpty)
XCTAssertEqual(result.initialPage.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(result.initialPage?.data?.hero.friendsConnection.friends.count, 1)
XCTAssertFalse(result.nextPages.isEmpty)

let friends = (result.previousPages.first?.hero.friendsConnection.friends ?? [])
+ result.initialPage.hero.friendsConnection.friends
+ (result.nextPages.first?.hero.friendsConnection.friends ?? [])
let friends = (
result.previousPages.compactMap(\.data?.hero.friendsConnection.friends)
+ result.nextPages.compactMap(\.data?.hero.friendsConnection.friends)
).flatMap { $0 } + (result.initialPage?.data?.hero.friendsConnection.friends ?? [])

XCTAssertEqual(Set(friends).count, 3)
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/ApolloPaginationTests/ConcurrencyTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ final class ConcurrencyTests: XCTestCase {

func test_concurrentFetches() async throws {
let pager = createPager()
var results: [Result<(PaginationOutput<Query, Query>, UpdateSource), any Error>] = []
var results: [Result<PaginationOutput<Query, Query>, any Error>] = []
let resultsExpectation = expectation(description: "Results arrival")
resultsExpectation.expectedFulfillmentCount = 2
await pager.subscribe { result in
Expand All @@ -160,7 +160,7 @@ final class ConcurrencyTests: XCTestCase {

func test_concurrentFetches_nonisolated() throws {
let pager = createNonisolatedPager()
var results: [Result<(PaginationOutput<Query, Query>, UpdateSource), any Error>] = []
var results: [Result<PaginationOutput<Query, Query>, any Error>] = []
let initialExpectation = expectation(description: "Initial")
initialExpectation.assertForOverFulfill = false
let nextExpectation = expectation(description: "Next")
Expand Down
40 changes: 20 additions & 20 deletions Tests/ApolloPaginationTests/ForwardPaginationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting {

let serverExpectation = Mocks.Hero.FriendsQuery.expectationForFirstPage(server: server)

var results: [Result<(PaginationOutput<Query, Query>, UpdateSource), any Error>] = []
var results: [Result<PaginationOutput<Query, Query>, any Error>] = []
let firstPageExpectation = expectation(description: "First page")
var subscription = await pager.subscribe(onUpdate: { _ in
firstPageExpectation.fulfill()
Expand All @@ -57,11 +57,11 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting {
subscription.cancel()
var result = try await XCTUnwrapping(await pager.currentValue)
results.append(result)
XCTAssertSuccessResult(result) { (output, source) in
XCTAssertSuccessResult(result) { output in
XCTAssertTrue(output.nextPages.isEmpty)
XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 2)
XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.friends.count, 2)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(output.initialPage?.source, .server)
}

let secondPageExpectation = Mocks.Hero.FriendsQuery.expectationForSecondPage(server: server)
Expand All @@ -78,17 +78,17 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting {
result = try await XCTUnwrapping(await pager.currentValue)
results.append(result)

try XCTAssertSuccessResult(result) { (output, source) in
try XCTAssertSuccessResult(result) { output in
// Assert first page is unchanged
XCTAssertEqual(try? results.first?.get().0.initialPage, try? results.last?.get().0.initialPage)
XCTAssertEqual(try? results.first?.get().initialPage, try? results.last?.get().initialPage)

XCTAssertFalse(output.nextPages.isEmpty)
XCTAssertEqual(output.nextPages.count, 1)
XCTAssertTrue(output.previousPages.isEmpty)
XCTAssertEqual(output.previousPages.count, 0)
let page = try XCTUnwrap(output.nextPages.first)
XCTAssertEqual(page.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(page.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(page.source, .server)
}
let previousCount = await pager.previousPageVarMap.values.count
XCTAssertEqual(previousCount, 0)
Expand Down Expand Up @@ -183,11 +183,11 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting {
await fulfillment(of: [serverExpectation, firstPageExpectation], timeout: 1)
subscription.cancel()
let result = try await XCTUnwrapping(await pager.currentValue)
XCTAssertSuccessResult(result) { (output, source) in
XCTAssertSuccessResult(result) { output in
XCTAssertTrue(output.nextPages.isEmpty)
XCTAssertEqual(output.initialPage.hero.friendsConnection.friends.count, 2)
XCTAssertEqual(output.initialPage.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.friends.count, 2)
XCTAssertEqual(output.initialPage?.data?.hero.friendsConnection.totalCount, 3)
XCTAssertEqual(output.initialPage?.source, .server)
}

let secondPageExpectation = Mocks.Hero.FriendsQuery.expectationForSecondPage(server: server)
Expand All @@ -201,14 +201,14 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting {
await fulfillment(of: [secondPageExpectation, secondPageFetch], timeout: 1)
subscription.cancel()
let newResult = try await XCTUnwrapping(await pager.currentValue)
try XCTAssertSuccessResult(newResult) { (output, source) in
try XCTAssertSuccessResult(newResult) { output in
// Assert first page is unchanged
XCTAssertEqual(try? result.get().0.initialPage, try? newResult.get().0.initialPage)
XCTAssertEqual(try? result.get().initialPage, try? newResult.get().initialPage)
XCTAssertFalse(output.nextPages.isEmpty)
XCTAssertEqual(output.nextPages.count, 1)
let page = try XCTUnwrap(output.nextPages.first)
XCTAssertEqual(page.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(source, .fetch)
XCTAssertEqual(page.data?.hero.friendsConnection.friends.count, 1)
XCTAssertEqual(page.source, .server)
}
let count = await pager.nextPageVarMap.values.count
XCTAssertEqual(count, 1)
Expand All @@ -227,10 +227,10 @@ final class ForwardPaginationTests: XCTestCase, CacheDependentTesting {
}
await fulfillment(of: [transactionExpectation, mutationExpectation])
let finalResult = try await XCTUnwrapping(await pager.currentValue)
XCTAssertSuccessResult(finalResult) { (output, _) in
XCTAssertEqual(output.initialPage.hero.name, "C3PO")
XCTAssertSuccessResult(finalResult) { output in
XCTAssertEqual(output.initialPage?.data?.hero.name, "C3PO")
XCTAssertEqual(output.nextPages.count, 1)
XCTAssertEqual(output.nextPages.first?.hero.name, "C3PO")
XCTAssertEqual(output.nextPages.first?.data?.hero.name, "C3PO")
}
}

Expand Down
87 changes: 87 additions & 0 deletions Tests/ApolloPaginationTests/FriendsQuery+TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,93 @@ extension Mocks.Hero.FriendsQuery {
]
}
}

static func expectationForFirstPageWithErrors(server: MockGraphQLServer) -> XCTestExpectation {
let query = MockQuery<Mocks.Hero.FriendsQuery>()
query.__variables = ["id": "2001", "first": 2, "after": GraphQLNullable<String>.null]
return server.expect(query) { _ in
let pageInfo: [AnyHashable: AnyHashable] = [
"__typename": "PageInfo",
"endCursor": "Y3Vyc29yMg==",
"hasNextPage": true,
]
let friends: [[String: AnyHashable]] = [
[
"__typename": "Human",
"name": "Luke Skywalker",
"id": "1000",
],
[
"__typename": "Human",
"name": "Han Solo",
"id": "1002",
],
]
let friendsConnection: [String: AnyHashable] = [
"__typename": "FriendsConnection",
"totalCount": 3,
"friends": friends,
"pageInfo": pageInfo,
]

let hero: [String: AnyHashable] = [
"__typename": "Droid",
"id": "2001",
"name": "R2-D2",
"friendsConnection": friendsConnection,
]

let data: [String: AnyHashable] = [
"hero": hero
]

return [
"data": data,
"errors": [
[
"message": "uh oh!"
],
[
"message": "Some error"
],
],
]
}
}

static func expectationForFirstPageErrorsOnly(server: MockGraphQLServer) -> XCTestExpectation {
let query = MockQuery<Mocks.Hero.FriendsQuery>()
query.__variables = ["id": "2001", "first": 2, "after": GraphQLNullable<String>.null]
return server.expect(query) { _ in
return [
"errors": [
[
"message": "uh oh!"
],
[
"message": "Some error"
],
],
]
}
}

static func expectationForSecondPageErrorsOnly(server: MockGraphQLServer) -> XCTestExpectation {
let query = MockQuery<Mocks.Hero.FriendsQuery>()
query.__variables = ["id": "2001", "first": 2, "after": "Y3Vyc29yMg=="]
return server.expect(query) { _ in
return [
"errors": [
[
"message": "uh oh!"
],
[
"message": "Some error"
],
],
]
}
}
}

extension Mocks.Hero.ReverseFriendsQuery {
Expand Down
Loading

0 comments on commit 07e12aa

Please sign in to comment.