Skip to content

Commit 824a867

Browse files
author
Di Wu
authored
chore(test): add API subscription integration test case for LazyLoading (#2768)
* chore(test): add API subscription integration test case for LazyLoading * chore(test): refactor subscription integration tests * chore(test): refactor with template function
1 parent 4d3d424 commit 824a867

File tree

3 files changed

+373
-0
lines changed

3 files changed

+373
-0
lines changed

AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginLazyLoadTests/GraphQLLazyLoadBaseTest.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,66 @@ class GraphQLLazyLoadBaseTest: XCTestCase {
185185
decodePath: document.name)
186186
return try await query(request)
187187
}
188+
189+
func subscribe<M: Model>(
190+
of modelType: M.Type,
191+
type: GraphQLSubscriptionType,
192+
verifyChange: @escaping (M) async throws -> Bool
193+
) async throws -> (AsyncExpectation, AmplifyAsyncThrowingSequence<GraphQLSubscriptionEvent<M>>) {
194+
let connected = asyncExpectation(description: "Subscription connected")
195+
let eventReceived = asyncExpectation(description: "\(type.rawValue) received")
196+
let subscription = Amplify.API.subscribe(request: .subscription(of: modelType, type: type))
197+
198+
Task {
199+
for try await subscriptionEvent in subscription {
200+
if subscriptionEvent.isConnected() {
201+
await connected.fulfill()
202+
}
203+
204+
if let error = subscriptionEvent.extractError() {
205+
XCTFail("Failed to \(type.rawValue) \(modelType), error: \(error.errorDescription)")
206+
}
207+
208+
if let data = subscriptionEvent.extractData(),
209+
try await verifyChange(data)
210+
{
211+
await eventReceived.fulfill()
212+
}
213+
}
214+
}
215+
216+
await waitForExpectations([connected], timeout: 10)
217+
return (eventReceived, subscription)
218+
}
188219
}
189220

190221
extension LazyReferenceIdentifier: Equatable {
191222
public static func == (lhs: LazyReferenceIdentifier, rhs: LazyReferenceIdentifier) -> Bool {
192223
return lhs.name == rhs.name && lhs.value == rhs.value
193224
}
194225
}
226+
227+
228+
extension GraphQLSubscriptionEvent {
229+
func isConnected() -> Bool {
230+
if case .connection(.connected) = self {
231+
return true
232+
}
233+
return false
234+
}
235+
236+
func extractData() -> T? {
237+
if case .data(.success(let data)) = self {
238+
return data
239+
}
240+
return nil
241+
}
242+
243+
func extractError() -> GraphQLResponseError<T>? {
244+
if case .data(.failure(let error)) = self {
245+
return error
246+
}
247+
return nil
248+
}
249+
250+
}

AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginLazyLoadTests/LL12/DefaultPK/GraphQLLazyLoadDefaultPKTests.swift

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,169 @@ final class GraphQLLazyLoadDefaultPKTests: GraphQLLazyLoadBaseTest {
101101
where: DefaultPKChild.keys.id == defaultPKChild.id))
102102
assertList(queriedChildren, state: .isLoaded(count: 1))
103103
}
104+
105+
/*
106+
- Given: Api category setup with DefaultPKModels
107+
- When:
108+
- Subscribe onCreate events of DefaultPKChild
109+
- Create new DefaultPKChild instance with API
110+
- Then:
111+
- the newly created instance is successfully created through API. onCreate event is received.
112+
*/
113+
func testSubscribeDefaultPKChildOnCreate() async throws {
114+
await setup(withModels: DefaultPKModels())
115+
let child = DefaultPKChild()
116+
let (onCreate, subscription) = try await subscribe(of: DefaultPKChild.self, type: .onCreate) { newChild in
117+
newChild.identifier == child.identifier
118+
}
119+
120+
try await mutate(.create(child))
121+
await waitForExpectations([onCreate], timeout: 10)
122+
subscription.cancel()
123+
}
124+
125+
/*
126+
- Given: Api category setup with DefaultPKModels
127+
- When:
128+
- Subscribe onCreate events of DefaultPKParent
129+
- Create new DefaultPKChild and DefaultPKParent instances with API
130+
- Then:
131+
- the newly created parent is successfully created through API. onCreate event is received.
132+
*/
133+
func testSubscribeDefaultPKParentOnCreate() async throws {
134+
await setup(withModels: DefaultPKModels())
135+
136+
let parent = DefaultPKParent()
137+
let child = DefaultPKChild(parent: parent)
138+
let (onCreate, subscription) = try await subscribe(of: DefaultPKParent.self, type: .onCreate) { newParent in
139+
try await newParent.children?.fetch()
140+
if case .some(.loaded(let associatedChildren)) = newParent.children?.loadedState {
141+
return newParent.identifier == parent.identifier
142+
&& associatedChildren.map(\.identifier).contains(child.identifier)
143+
}
144+
return false
145+
}
146+
147+
try await mutate(.create(child))
148+
try await mutate(.create(parent))
149+
await waitForExpectations([onCreate], timeout: 10)
150+
subscription.cancel()
151+
}
152+
153+
/*
154+
- Given: Api category setup with DefaultPKModels
155+
- When:
156+
- Subscribe onUpdate events of DefaultPKChild
157+
- Create new DefaultPKChild instance with API
158+
- Update the newly created with API
159+
- Then:
160+
- an onUpdate event is received, the identifier is same to the updated one.
161+
*/
162+
func testSubscriptionDefaultPKChildOnUpdate() async throws {
163+
await setup(withModels: DefaultPKModels())
164+
165+
let child = DefaultPKChild()
166+
let (onUpdate, subscription) = try await subscribe(of: DefaultPKChild.self, type: .onUpdate) { updatedChild in
167+
updatedChild.identifier == child.identifier
168+
}
169+
170+
try await mutate(.create(child))
171+
var updatingChild = child
172+
updatingChild.content = UUID().uuidString
173+
try await mutate(.update(updatingChild))
174+
await waitForExpectations([onUpdate], timeout: 10)
175+
subscription.cancel()
176+
}
177+
178+
/*
179+
- Given: Api category setup with DefaultPKModels
180+
- When:
181+
- Subscribe onUpdate events of HasOneParent
182+
- Create new DefaultPKChild instance with API
183+
- Create new DefaultPKParent instance with API
184+
- Update the newly created parent to another DefaultPKChild instance
185+
- Then:
186+
- an onUpdate event is received, the identifier is same to the updated one.
187+
*/
188+
func testSubscriptionDefaultPKParentOnUpdate() async throws {
189+
await setup(withModels: DefaultPKModels())
190+
191+
let parent = DefaultPKParent()
192+
let child = DefaultPKChild(parent: parent)
193+
let anotherChild = DefaultPKChild(parent: parent)
194+
195+
let (onUpdate, subscription) = try await subscribe(of: DefaultPKParent.self, type: .onUpdate) { updatedParent in
196+
try await updatedParent.children?.fetch()
197+
if case .some(.loaded(let associatedChildren)) = updatedParent.children?.loadedState {
198+
return updatedParent.identifier == parent.identifier
199+
&& associatedChildren.map(\.identifier).contains(child.identifier)
200+
&& associatedChildren.map(\.identifier).contains(anotherChild.identifier)
201+
}
202+
return false
203+
}
204+
205+
try await mutate(.create(child))
206+
try await mutate(.create(parent))
207+
try await mutate(.create(anotherChild))
208+
var updatingParent = parent
209+
updatingParent.content = UUID().uuidString
210+
try await mutate(.update(updatingParent))
211+
await waitForExpectations([onUpdate], timeout: 10)
212+
subscription.cancel()
213+
}
214+
215+
/*
216+
- Given: Api category setup with DefaultPKModels
217+
- When:
218+
- Subscribe onDelete events of DefaultPKChild
219+
- Create new DefaultPKChild instance with API
220+
- Delete the newly created one with API
221+
- Then:
222+
- an onDelete event is received, the identifier is same to the newly created one.
223+
*/
224+
func testSubscriptionDefaultPKChildOnDelete() async throws {
225+
await setup(withModels: DefaultPKModels())
226+
let child = DefaultPKChild()
227+
let (onDelete, subscription) = try await subscribe(of: DefaultPKChild.self, type: .onDelete, verifyChange: { deletedChild in
228+
deletedChild.identifier == child.identifier
229+
})
230+
231+
try await mutate(.create(child))
232+
try await mutate(.delete(child))
233+
await waitForExpectations([onDelete], timeout: 10)
234+
subscription.cancel()
235+
}
236+
237+
/*
238+
- Given: Api category setup with DefaultPKModels
239+
- When:
240+
- Subscribe onDelete events of DefaultPKParent
241+
- Create new DefaultPKChild instance with API
242+
- Create new DefaultPKParent instance with API
243+
- Delete the newly created parent with API
244+
- Then:
245+
- an onDelete event is received, the identifier is same to the newly created one.
246+
*/
247+
func testSubscriptionDefaultPKParentOnDelete() async throws {
248+
await setup(withModels: DefaultPKModels())
249+
250+
let parent = DefaultPKParent()
251+
let child = DefaultPKChild(parent: parent)
252+
let (onDelete, subscription) = try await subscribe(of: DefaultPKParent.self, type: .onDelete, verifyChange: { deletedParent in
253+
try await deletedParent.children?.fetch()
254+
if case .some(.loaded(let associatedChildren)) = deletedParent.children?.loadedState {
255+
return deletedParent.identifier == parent.identifier
256+
&& associatedChildren.map(\.identifier).contains(child.identifier)
257+
}
258+
return false
259+
})
260+
261+
try await mutate(.create(child))
262+
try await mutate(.create(parent))
263+
try await mutate(.delete(parent))
264+
await waitForExpectations([onDelete], timeout: 10)
265+
subscription.cancel()
266+
}
104267
}
105268

106269
extension GraphQLLazyLoadDefaultPKTests: DefaultLogger { }

AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginLazyLoadTests/LL12/HasOneParentChild/GraphQLLazyLoadHasOneTests.swift

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,160 @@ final class GraphQLLazyLoadHasOneTests: GraphQLLazyLoadBaseTest {
9797
where: HasOneChild.keys.id == hasOneChild.id))
9898
assertList(queriedChildren, state: .isLoaded(count: 1))
9999
}
100+
101+
/*
102+
- Given: Api category setup with HasOneParentChildModels
103+
- When:
104+
- Subscribe onCreate events of HasOneChild
105+
- Create new HasOneChild instance with API
106+
- Then:
107+
- the newly created instance is successfully created through API. onCreate event is received.
108+
*/
109+
func testSubscribeHasOneChildOnCreate() async throws {
110+
await setup(withModels: HasOneParentChildModels())
111+
let child = HasOneChild()
112+
let (onCreate, subscription) = try await subscribe(of: HasOneChild.self, type: .onCreate) { newChild in
113+
newChild.identifier == child.identifier
114+
}
115+
try await mutate(.create(child))
116+
await waitForExpectations([onCreate], timeout: 10)
117+
subscription.cancel()
118+
}
119+
120+
/*
121+
- Given: Api category setup with HasOneParentChildModels
122+
- When:
123+
- Subscribe onCreate events of HasOneParent
124+
- Create new HasOneChild and HasOneParent instances with API
125+
- Then:
126+
- the newly created parent is successfully created through API. onCreate event is received.
127+
*/
128+
func testSubscribeHasOneParentOnCreate() async throws {
129+
await setup(withModels: HasOneParentChildModels())
130+
let child = HasOneChild()
131+
let parent = HasOneParent(child: child, hasOneParentChildId: child.id)
132+
let (onCreate, subscription) = try await subscribe(of: HasOneParent.self, type: .onCreate) { newParent in
133+
if let associatedChild = try await newParent.child {
134+
return newParent.identifier == parent.identifier
135+
&& associatedChild.identifier == child.identifier
136+
}
137+
return false
138+
}
139+
140+
try await mutate(.create(child))
141+
try await mutate(.create(parent))
142+
await waitForExpectations([onCreate], timeout: 10)
143+
subscription.cancel()
144+
}
145+
146+
/*
147+
- Given: Api category setup with HasOneParentChildModels
148+
- When:
149+
- Subscribe onUpdate events of HasOneChild
150+
- Create new HasOneChild instance with API
151+
- Update the newly created with API
152+
- Then:
153+
- an onUpdate event is received, the identifier is same to the updated one.
154+
*/
155+
func testSubscriptionHasOnChildOnUpdate() async throws {
156+
await setup(withModels: HasOneParentChildModels())
157+
let child = HasOneChild()
158+
let (onUpdate, subscription) = try await subscribe(of: HasOneChild.self, type: .onUpdate) { updatedChild in
159+
updatedChild.identifier == child.identifier
160+
}
161+
162+
try await mutate(.create(child))
163+
var updatingChild = child
164+
updatingChild.content = UUID().uuidString
165+
try await mutate(.update(updatingChild))
166+
await waitForExpectations([onUpdate], timeout: 10)
167+
subscription.cancel()
168+
}
169+
170+
/*
171+
- Given: Api category setup with HasOneParentChildModels
172+
- When:
173+
- Subscribe onUpdate events of HasOneParent
174+
- Create new HasOneChild instance with API
175+
- Create new HasOneParent instance with API
176+
- Update the newly created parent to another HasOneChild instance
177+
- Then:
178+
- an onUpdate event is received, the identifier is same to the updated one.
179+
*/
180+
func testSubscriptionHasOnParentOnUpdate() async throws {
181+
await setup(withModels: HasOneParentChildModels())
182+
let child = HasOneChild()
183+
let anotherChild = HasOneChild()
184+
let parent = HasOneParent(child: child, hasOneParentChildId: child.id)
185+
let (onUpdate, subscription) = try await subscribe(of: HasOneParent.self, type: .onUpdate) { updatedParent in
186+
if let associatedChild = try await updatedParent.child {
187+
return updatedParent.identifier == parent.identifier
188+
&& associatedChild.identifier == anotherChild.identifier
189+
}
190+
return false
191+
}
192+
193+
try await mutate(.create(child))
194+
try await mutate(.create(parent))
195+
var updatingParent = parent
196+
updatingParent.setChild(anotherChild)
197+
updatingParent.hasOneParentChildId = anotherChild.id
198+
try await mutate(.create(anotherChild))
199+
try await mutate(.update(updatingParent))
200+
await waitForExpectations([onUpdate], timeout: 10)
201+
subscription.cancel()
202+
}
203+
204+
/*
205+
- Given: Api category setup with HasOneParentChildModels
206+
- When:
207+
- Subscribe onDelete events of HasOneChild
208+
- Create new HasOneChild instance with API
209+
- Delete the newly created one with API
210+
- Then:
211+
- an onDelete event is received, the identifier is same to the newly created one.
212+
*/
213+
func testSubscriptionHasOneChildOnDelete() async throws {
214+
await setup(withModels: HasOneParentChildModels())
215+
let child = HasOneChild()
216+
let (onDelete, subscription) = try await subscribe(of: HasOneChild.self, type: .onDelete) { deletedChild in
217+
deletedChild.identifier == child.identifier
218+
}
219+
220+
try await mutate(.create(child))
221+
try await mutate(.delete(child))
222+
await waitForExpectations([onDelete], timeout: 10)
223+
subscription.cancel()
224+
}
225+
226+
/*
227+
- Given: Api category setup with HasOneParentChildModels
228+
- When:
229+
- Subscribe onDelete events of HasOneParent
230+
- Create new HasOneChild instance with API
231+
- Create new HasOneParent instance with API
232+
- Delete the newly created parent with API
233+
- Then:
234+
- an onDelete event is received, the identifier is same to the newly created one.
235+
*/
236+
func testSubscriptionHasOneParentOnDelete() async throws {
237+
await setup(withModels: HasOneParentChildModels())
238+
let child = HasOneChild()
239+
let parent = HasOneParent(child: child, hasOneParentChildId: child.id)
240+
let (onDelete, subscription) = try await subscribe(of: HasOneParent.self, type: .onDelete, verifyChange: { deletedParent in
241+
if let associatedChild = try await deletedParent.child {
242+
return deletedParent.identifier == parent.identifier
243+
&& associatedChild.identifier == child.identifier
244+
}
245+
return false
246+
})
247+
248+
try await mutate(.create(child))
249+
try await mutate(.create(parent))
250+
try await mutate(.delete(parent))
251+
await waitForExpectations([onDelete], timeout: 10)
252+
subscription.cancel()
253+
}
100254
}
101255

102256
extension GraphQLLazyLoadHasOneTests: DefaultLogger { }

0 commit comments

Comments
 (0)