Skip to content

Commit f2f1ed7

Browse files
AnthonyMDevgh-action-runner
authored andcommitted
Fix SelectionSet Equality bug with child merged field from fragment (#832)
1 parent 52efc90 commit f2f1ed7

File tree

4 files changed

+212
-59
lines changed

4 files changed

+212
-59
lines changed

.package.resolved

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"originHash" : "50edd0e2dbb26f1cb3132d77b60af378a095dcdf6eaae1fa5795d80bcb5e753b",
23
"pins" : [
34
{
45
"identity" : "cwlcatchexception",
@@ -32,8 +33,8 @@
3233
"kind" : "remoteSourceControl",
3334
"location" : "https://github.com/Quick/Nimble.git",
3435
"state" : {
35-
"revision" : "7795df4fff1a9cd231fe4867ae54f4dc5f5734f9",
36-
"version" : "13.7.1"
36+
"revision" : "cc945f7bdf3b485adedc315e18685c065059b4ca",
37+
"version" : "13.8.0"
3738
}
3839
},
3940
{
@@ -45,15 +46,6 @@
4546
"version" : "1.5.0"
4647
}
4748
},
48-
{
49-
"identity" : "swift-atomics",
50-
"kind" : "remoteSourceControl",
51-
"location" : "https://github.com/apple/swift-atomics",
52-
"state" : {
53-
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
54-
"version" : "1.2.0"
55-
}
56-
},
5749
{
5850
"identity" : "swift-collections",
5951
"kind" : "remoteSourceControl",
@@ -64,5 +56,5 @@
6456
}
6557
}
6658
],
67-
"version" : 2
59+
"version" : 3
6860
}

Tests/ApolloPaginationTests/Mocks.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ enum Mocks {
99
override class var __selections: [Selection] { [
1010
.field("hero", Hero?.self, arguments: ["id": .variable("id")])
1111
]}
12+
override class var __fulfilledFragments: [any SelectionSet.Type] {
13+
[BidirectionalFriendsQuery.self]
14+
}
1215

1316
var hero: Hero { __data["hero"] }
1417

@@ -23,6 +26,9 @@ enum Mocks {
2326
"after": .variable("after"),
2427
]),
2528
]}
29+
override class var __fulfilledFragments: [any SelectionSet.Type] {
30+
[Hero.self]
31+
}
2632

2733
var name: String { __data["name"] }
2834
var id: String { __data["id"] }
@@ -35,6 +41,9 @@ enum Mocks {
3541
.field("friends", [Character].self),
3642
.field("pageInfo", PageInfo.self),
3743
]}
44+
override class var __fulfilledFragments: [any SelectionSet.Type] {
45+
[FriendsConnection.self]
46+
}
3847

3948
var totalCount: Int32 { __data["totalCount"] }
4049
var friends: [Character] { __data["friends"] }
@@ -46,6 +55,9 @@ enum Mocks {
4655
.field("name", String.self),
4756
.field("id", String.self),
4857
]}
58+
override class var __fulfilledFragments: [any SelectionSet.Type] {
59+
[Character.self]
60+
}
4961

5062
var name: String { __data["name"] }
5163
var id: String { __data["id"] }
@@ -59,6 +71,9 @@ enum Mocks {
5971
.field("endCursor", Optional<String>.self),
6072
.field("hasNextPage", Bool.self),
6173
]}
74+
override class var __fulfilledFragments: [any SelectionSet.Type] {
75+
[PageInfo.self]
76+
}
6277

6378
var endCursor: String? { __data["endCursor"] }
6479
var hasNextPage: Bool { __data["hasNextPage"] }
@@ -72,6 +87,9 @@ enum Mocks {
7287
override class var __selections: [Selection] { [
7388
.field("hero", Hero?.self, arguments: ["id": .variable("id")])
7489
]}
90+
override class var __fulfilledFragments: [any SelectionSet.Type] {
91+
[ReverseFriendsQuery.self]
92+
}
7593

7694
var hero: Hero { __data["hero"] }
7795

@@ -85,6 +103,9 @@ enum Mocks {
85103
"before": .variable("before"),
86104
]),
87105
]}
106+
override class var __fulfilledFragments: [any SelectionSet.Type] {
107+
[Hero.self]
108+
}
88109

89110
var name: String { __data["name"] }
90111
var id: String { __data["id"] }
@@ -97,6 +118,9 @@ enum Mocks {
97118
.field("friends", [Character].self),
98119
.field("pageInfo", PageInfo.self),
99120
]}
121+
override class var __fulfilledFragments: [any SelectionSet.Type] {
122+
[FriendsConnection.self]
123+
}
100124

101125
var totalCount: Int32 { __data["totalCount"] }
102126
var friends: [Character] { __data["friends"] }
@@ -108,6 +132,9 @@ enum Mocks {
108132
.field("name", String.self),
109133
.field("id", String.self),
110134
]}
135+
override class var __fulfilledFragments: [any SelectionSet.Type] {
136+
[Character.self]
137+
}
111138

112139
var name: String { __data["name"] }
113140
var id: String { __data["id"] }
@@ -119,6 +146,9 @@ enum Mocks {
119146
.field("startCursor", Optional<String>.self),
120147
.field("hasPreviousPage", Bool.self),
121148
]}
149+
override class var __fulfilledFragments: [any SelectionSet.Type] {
150+
[PageInfo.self]
151+
}
122152

123153
var startCursor: String? { __data["startCursor"] }
124154
var hasPreviousPage: Bool { __data["hasPreviousPage"] }
@@ -130,6 +160,9 @@ enum Mocks {
130160
override class var __selections: [Selection] { [
131161
.field("hero", Hero?.self, arguments: ["id": .variable("id")])
132162
]}
163+
override class var __fulfilledFragments: [any SelectionSet.Type] {
164+
[FriendsQuery.self]
165+
}
133166

134167
var hero: Hero { __data["hero"] }
135168

@@ -143,6 +176,9 @@ enum Mocks {
143176
"after": .variable("after"),
144177
]),
145178
]}
179+
override class var __fulfilledFragments: [any SelectionSet.Type] {
180+
[Hero.self]
181+
}
146182

147183
var name: String { __data["name"] }
148184
var id: String { __data["id"] }
@@ -155,6 +191,9 @@ enum Mocks {
155191
.field("friends", [Character].self),
156192
.field("pageInfo", PageInfo.self),
157193
]}
194+
override class var __fulfilledFragments: [any SelectionSet.Type] {
195+
[FriendsConnection.self]
196+
}
158197

159198
var totalCount: Int32 { __data["totalCount"] }
160199
var friends: [Character] { __data["friends"] }
@@ -166,6 +205,9 @@ enum Mocks {
166205
.field("name", String.self),
167206
.field("id", String.self),
168207
]}
208+
override class var __fulfilledFragments: [any SelectionSet.Type] {
209+
[Character.self]
210+
}
169211

170212
var name: String { __data["name"] }
171213
var id: String { __data["id"] }
@@ -177,6 +219,9 @@ enum Mocks {
177219
.field("endCursor", Optional<String>.self),
178220
.field("hasNextPage", Bool.self),
179221
]}
222+
override class var __fulfilledFragments: [any SelectionSet.Type] {
223+
[PageInfo.self]
224+
}
180225

181226
var endCursor: String? { __data["endCursor"] }
182227
var hasNextPage: Bool { __data["hasNextPage"] }
@@ -189,6 +234,9 @@ enum Mocks {
189234
override class var __selections: [Selection] { [
190235
.field("hero", Hero?.self, arguments: ["id": .variable("id")])
191236
]}
237+
override class var __fulfilledFragments: [any SelectionSet.Type] {
238+
[OffsetFriendsQuery.self]
239+
}
192240

193241
var hero: Hero { __data["hero"] }
194242

@@ -202,6 +250,9 @@ enum Mocks {
202250
"limit": .variable("limit"),
203251
]),
204252
]}
253+
override class var __fulfilledFragments: [any SelectionSet.Type] {
254+
[Hero.self]
255+
}
205256

206257
var name: String { __data["name"] }
207258
var id: String { __data["id"] }
@@ -213,6 +264,9 @@ enum Mocks {
213264
.field("name", String.self),
214265
.field("id", String.self),
215266
]}
267+
override class var __fulfilledFragments: [any SelectionSet.Type] {
268+
[Character.self]
269+
}
216270

217271
var name: String { __data["name"] }
218272
var id: String { __data["id"] }
@@ -226,6 +280,9 @@ enum Mocks {
226280
static var __selections: [Selection] { [
227281
.field("hero", Hero?.self, arguments: ["id": .variable("id")])
228282
]}
283+
static var __fulfilledFragments: [any SelectionSet.Type] {
284+
[NameCacheMutation.self]
285+
}
229286

230287
var hero: Hero? {
231288
get { __data["hero"] }
@@ -240,6 +297,9 @@ enum Mocks {
240297
.field("id", String.self),
241298
.field("name", String.self),
242299
]}
300+
static var __fulfilledFragments: [any SelectionSet.Type] {
301+
[Hero.self]
302+
}
243303

244304
var id: String {
245305
get { __data["id"] }

Tests/ApolloTests/SelectionSet_EquatableTests.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,97 @@ class SelectionSet_EquatableTests: XCTestCase {
543543
expect(selectionSet1.hashValue).toNot(equal(selectionSet2.hashValue))
544544
}
545545

546+
// MARK: Fragment Merging
547+
548+
// Unit test to reproduce https://github.com/apollographql/apollo-ios/issues/3602
549+
func test__equatable__childObject_inBothSelfAndNamedFragment_withNestedInlineFragment_differentValues_returns_false() {
550+
// given
551+
final class Hero: MockSelectionSet, @unchecked Sendable {
552+
typealias Schema = MockSchemaMetadata
553+
554+
override class var __parentType: any ParentType { Types.Hero }
555+
override class var __selections: [Selection] {[
556+
.field("friends", [Friend].self),
557+
.fragment(HeroFriendFragment.self)
558+
]}
559+
560+
override class var __fulfilledFragments: [any SelectionSet.Type] {
561+
[Hero.self, HeroFriendFragment.self]
562+
}
563+
564+
class Friend: MockSelectionSet, @unchecked Sendable {
565+
override class var __selections: [Selection] {[
566+
.inlineFragment(AsCharacter.self)
567+
]}
568+
569+
override class var __fulfilledFragments: [any SelectionSet.Type] {
570+
[Friend.self, HeroFriendFragment.Friend.self]
571+
}
572+
573+
final class AsCharacter: ConcreteMockTypeCase<Friend>, @unchecked Sendable {
574+
typealias Schema = MockSchemaMetadata
575+
576+
override class var __parentType: any ParentType { Types.Character }
577+
override class var __selections: [Selection] {[
578+
.field("age", Int?.self),
579+
]}
580+
override class var __fulfilledFragments: [any SelectionSet.Type] {
581+
[Friend.self, Friend.AsCharacter.self, HeroFriendFragment.Friend.self]
582+
}
583+
}
584+
}
585+
}
586+
587+
final class HeroFriendFragment: MockFragment, @unchecked Sendable {
588+
typealias Schema = MockSchemaMetadata
589+
590+
override class var __parentType: any ParentType { Types.Hero }
591+
override class var __selections: [Selection] {[
592+
.field("friends", [Friend].self)
593+
]}
594+
override class var __fulfilledFragments: [any SelectionSet.Type] {
595+
[HeroFriendFragment.self]
596+
}
597+
598+
class Friend: MockSelectionSet, @unchecked Sendable {
599+
override class var __selections: [Selection] {[
600+
.field("name", String?.self),
601+
]}
602+
603+
override class var __fulfilledFragments: [any SelectionSet.Type] {
604+
[Friend.self]
605+
}
606+
}
607+
}
608+
609+
// when
610+
let selectionSet1 = Hero.Friend.AsCharacter.init(_dataDict: DataDict(
611+
data: [
612+
"name": "Name 1"
613+
],
614+
fulfilledFragments: [
615+
ObjectIdentifier(Hero.Friend.self),
616+
ObjectIdentifier(Hero.Friend.AsCharacter.self),
617+
ObjectIdentifier(HeroFriendFragment.Friend.self)
618+
]
619+
))
620+
621+
let selectionSet2 = Hero.Friend.AsCharacter.init(_dataDict: DataDict(
622+
data: [
623+
"name": "Name 2"
624+
],
625+
fulfilledFragments: [
626+
ObjectIdentifier(Hero.Friend.self),
627+
ObjectIdentifier(Hero.Friend.AsCharacter.self),
628+
ObjectIdentifier(HeroFriendFragment.Friend.self)
629+
]
630+
))
631+
632+
// then
633+
expect(selectionSet1).toNot(equal(selectionSet2))
634+
expect(selectionSet1.hashValue).toNot(equal(selectionSet2.hashValue))
635+
}
636+
546637
// MARK: Named Fragment
547638

548639
func test__equatable__namedFragment_differentValue_forFieldInChildNamedFragment_returns_false() {

0 commit comments

Comments
 (0)