Skip to content

Commit 0d1eb60

Browse files
authored
chore(datastore): multi-auth integration tests (#1268)
* multi-auth integration tests * update test instructions, remove OIDC
1 parent 56bc2a7 commit 0d1eb60

File tree

73 files changed

+4509
-8
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+4509
-8
lines changed

AmplifyPlugins/Core/AWSPluginsCore/Auth/AWSAuthModeStrategy.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public class AWSDefaultAuthModeStrategy: AuthModeStrategy {
9696
}
9797
}
9898

99+
99100
// MARK: - AWSMultiAuthModeStrategy
100101

101102
/// Multi-auth strategy implementation based on schema metadata

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/RemoteSyncEngine.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ extension RemoteSyncEngine: AuthModeStrategyDelegate {
422422
return true
423423
}
424424
}
425-
425+
426426
return auth?.getCurrentUser() != nil
427427
}
428428
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Foundation
9+
10+
struct TestUser {
11+
let username: String
12+
let password: String
13+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Foundation
9+
import XCTest
10+
import Combine
11+
12+
import AmplifyPlugins
13+
import AWSDataStoreCategoryPlugin
14+
import AWSPluginsCore
15+
16+
@testable import Amplify
17+
@testable import AmplifyTestCommon
18+
19+
class AWSDataStoreMultiAuthBaseTest: XCTestCase {
20+
var requests: Set<AnyCancellable> = []
21+
22+
var user1: TestUser?
23+
var user2: TestUser?
24+
25+
static let amplifyConfigurationFile = "AWSDataStoreCategoryPluginMultiAuthIntegrationTests-amplifyconfiguration"
26+
static let credentialsFile = "AWSDataStoreCategoryPluginMultiAuthIntegrationTests-credentials"
27+
28+
var authRecorderInterceptor: AuthRecorderInterceptor = AuthRecorderInterceptor()
29+
30+
override func setUp() {
31+
Amplify.Logging.logLevel = .verbose
32+
33+
do {
34+
let credentials = try TestConfigHelper.retrieveCredentials(forResource: Self.credentialsFile)
35+
36+
guard let user1 = credentials["user1"],
37+
let user2 = credentials["user2"],
38+
let passwordUser1 = credentials["passwordUser1"],
39+
let passwordUser2 = credentials["passwordUser2"] else {
40+
XCTFail("Invalid \(Self.credentialsFile).json data")
41+
return
42+
}
43+
44+
self.user1 = TestUser(username: user1, password: passwordUser1)
45+
self.user2 = TestUser(username: user2, password: passwordUser2)
46+
47+
authRecorderInterceptor.reset()
48+
49+
} catch {
50+
XCTFail("Error during setup: \(error)")
51+
}
52+
}
53+
54+
override func tearDownWithError() throws {
55+
sleep(10)
56+
Amplify.reset()
57+
}
58+
59+
// MARK: - Test Helpers
60+
func makeExpectations() -> MultiAuthTestExpectations {
61+
MultiAuthTestExpectations(
62+
subscriptionsEstablished: expectation(description: "Subscription established"),
63+
modelsSynced: expectation(description: "Model synced"),
64+
65+
query: expectation(description: "Query success"),
66+
67+
mutationSave: expectation(description: "Mutation save success"),
68+
mutationSaveProcessed: expectation(description: "Mutation save processed"),
69+
70+
mutationDelete: expectation(description: "Mutation delete success"),
71+
mutationDeleteProcessed: expectation(description: "Mutation delete processed"),
72+
73+
ready: expectation(description: "Ready"))
74+
}
75+
76+
/// Setup DataStore with given models
77+
/// - Parameter models: DataStore models
78+
func setup(withModels models: AmplifyModelRegistration) {
79+
do {
80+
let datastoreConfig = DataStoreConfiguration.custom(authModeStrategy: .multiAuth)
81+
82+
try Amplify.add(plugin: AWSDataStorePlugin(modelRegistration: models,
83+
configuration: datastoreConfig))
84+
85+
let apiPlugin = AWSAPIPlugin()
86+
87+
try Amplify.add(plugin: apiPlugin)
88+
try Amplify.add(plugin: AWSCognitoAuthPlugin())
89+
90+
let amplifyConfig = try TestConfigHelper.retrieveAmplifyConfiguration(forResource: Self.amplifyConfigurationFile)
91+
try Amplify.configure(amplifyConfig)
92+
93+
// register auth recorder interceptor
94+
try apiPlugin.add(interceptor: authRecorderInterceptor, for: "datastoreintegtestmu")
95+
96+
signOut()
97+
clearDataStore()
98+
} catch {
99+
XCTFail("Error during setup: \(error)")
100+
}
101+
}
102+
103+
func clearDataStore() {
104+
let semaphore = DispatchSemaphore(value: 0)
105+
Amplify.DataStore.clear {
106+
if case let .failure(error) = $0 {
107+
XCTFail("DataStore clear failed \(error)")
108+
}
109+
semaphore.signal()
110+
}
111+
semaphore.wait()
112+
}
113+
}
114+
115+
// MARK: - Auth helpers
116+
extension AWSDataStoreMultiAuthBaseTest {
117+
/// Signin given user
118+
/// - Parameter user
119+
func signIn(user: TestUser?) {
120+
guard let user = user else {
121+
XCTFail("Invalid user")
122+
return
123+
}
124+
let signInInvoked = expectation(description: "sign in completed")
125+
_ = Amplify.Auth.signIn(username: user.username,
126+
password: user.password, options: nil) { result in
127+
switch result {
128+
case .failure(let error):
129+
XCTFail("Signin failure \(error)")
130+
signInInvoked.fulfill() // won't count as pass
131+
case .success:
132+
signInInvoked.fulfill()
133+
}
134+
}
135+
wait(for: [signInInvoked], timeout: TestCommonConstants.networkTimeout)
136+
}
137+
138+
/// Signout current signed-in user
139+
func signOut() {
140+
let signoutInvoked = expectation(description: "sign out completed")
141+
_ = Amplify.Auth.signOut { result in
142+
switch result {
143+
case .failure(let error):
144+
XCTFail("Signout failure \(error)")
145+
signoutInvoked.fulfill() // won't count as pass
146+
147+
case .success:
148+
signoutInvoked.fulfill()
149+
}
150+
}
151+
wait(for: [signoutInvoked], timeout: TestCommonConstants.networkTimeout)
152+
}
153+
}
154+
155+
// MARK: - DataStore behavior assert helpers
156+
extension AWSDataStoreMultiAuthBaseTest {
157+
/// Asserts that query with given `Model` succeeds
158+
/// - Parameters:
159+
/// - modelType: model type
160+
/// - expectation: success XCTestExpectation
161+
/// - onFailure: on failure callback
162+
func assertQuerySuccess<M: Model>(modelType: M.Type,
163+
_ expectations: MultiAuthTestExpectations,
164+
onFailure: @escaping (_ error: DataStoreError) -> Void) {
165+
Amplify.DataStore.query(modelType).sink {
166+
if case let .failure(error) = $0 {
167+
onFailure(error)
168+
}
169+
}
170+
receiveValue: { posts in
171+
XCTAssertNotNil(posts)
172+
expectations.query.fulfill()
173+
}.store(in: &requests)
174+
wait(for: [expectations.query],
175+
timeout: 60)
176+
}
177+
178+
/// Asserts that DataStore is in a ready state and subscriptions are established
179+
/// - Parameter events: DataStore Hub events
180+
func assertDataStoreReady(_ expectations: MultiAuthTestExpectations,
181+
expectedModelSynced: Int = 1) {
182+
var modelSyncedCount = 0
183+
let dataStoreEvents = HubPayload.EventName.DataStore.self
184+
_ = Amplify.Hub.listen(to: .dataStore) { event in
185+
// subscription fulfilled
186+
if event.eventName == dataStoreEvents.subscriptionsEstablished {
187+
expectations.subscriptionsEstablished.fulfill()
188+
}
189+
190+
// syncQueryReady fulfilled
191+
if event.eventName == dataStoreEvents.modelSynced {
192+
modelSyncedCount += 1
193+
if modelSyncedCount == expectedModelSynced {
194+
expectations.modelsSynced.fulfill()
195+
}
196+
}
197+
198+
if event.eventName == dataStoreEvents.ready {
199+
expectations.ready.fulfill()
200+
}
201+
}
202+
Amplify.DataStore.start { _ in }
203+
wait(for: [expectations.subscriptionsEstablished,
204+
expectations.modelsSynced,
205+
expectations.ready],
206+
timeout: 60)
207+
}
208+
209+
/// Assert that a save and a delete mutation complete successfully.
210+
/// - Parameters:
211+
/// - model: model instance saved and then deleted
212+
/// - expectations: test expectatinos
213+
/// - onFailure: failure callback
214+
func assertMutations<M: Model>(model: M,
215+
_ expectations: MultiAuthTestExpectations,
216+
onFailure: @escaping (_ error: DataStoreError) -> Void) {
217+
_ = Amplify.Hub.listen(to: .dataStore, eventName: HubPayload.EventName.DataStore.syncReceived) { payload in
218+
guard let mutationEvent = payload.data as? MutationEvent,
219+
mutationEvent.modelId == model.id else {
220+
return
221+
}
222+
223+
if mutationEvent.mutationType == GraphQLMutationType.create.rawValue {
224+
expectations.mutationSaveProcessed.fulfill()
225+
return
226+
}
227+
228+
if mutationEvent.mutationType == GraphQLMutationType.delete.rawValue {
229+
expectations.mutationDeleteProcessed.fulfill()
230+
return
231+
}
232+
}
233+
234+
Amplify.DataStore.save(model).sink {
235+
if case let .failure(error) = $0 {
236+
onFailure(error)
237+
}
238+
}
239+
receiveValue: { posts in
240+
XCTAssertNotNil(posts)
241+
expectations.mutationSave.fulfill()
242+
}.store(in: &requests)
243+
244+
wait(for: [expectations.mutationSave, expectations.mutationSaveProcessed], timeout: 60)
245+
246+
Amplify.DataStore.delete(M.self, withId: model.id).sink {
247+
if case let .failure(error) = $0 {
248+
onFailure(error)
249+
}
250+
}
251+
receiveValue: { posts in
252+
XCTAssertNotNil(posts)
253+
expectations.mutationDelete.fulfill()
254+
}.store(in: &requests)
255+
256+
wait(for: [expectations.mutationDelete, expectations.mutationDeleteProcessed], timeout: 60)
257+
}
258+
259+
/// Asserts that save mutation with given `Model` succeeds
260+
/// - Parameters:
261+
/// - model: model type
262+
/// - expectation: success XCTestExpectation
263+
/// - onFailure: on failure callback
264+
func assertMutations<M: Model>(model: M,
265+
_ expectation: XCTestExpectation,
266+
onFailure: @escaping (_ error: DataStoreError) -> Void) {
267+
Amplify.DataStore.save(model).sink {
268+
if case let .failure(error) = $0 {
269+
onFailure(error)
270+
}
271+
}
272+
receiveValue: { posts in
273+
XCTAssertNotNil(posts)
274+
expectation.fulfill()
275+
}.store(in: &requests)
276+
}
277+
278+
func assertUsedAuthTypes(_ authTypes: [AWSAuthorizationType],
279+
file: StaticString = #file,
280+
line: UInt = #line) {
281+
XCTAssertEqual(authRecorderInterceptor.consumedAuthTypes,
282+
Set(authTypes),
283+
file: file,
284+
line: line)
285+
}
286+
}
287+
288+
// MARK: - Expectations
289+
extension AWSDataStoreMultiAuthBaseTest {
290+
struct MultiAuthTestExpectations {
291+
var subscriptionsEstablished: XCTestExpectation
292+
var modelsSynced: XCTestExpectation
293+
var query: XCTestExpectation
294+
var mutationSave: XCTestExpectation
295+
var mutationSaveProcessed: XCTestExpectation
296+
var mutationDelete: XCTestExpectation
297+
var mutationDeleteProcessed: XCTestExpectation
298+
var ready: XCTestExpectation
299+
var expectations: [XCTestExpectation] {
300+
return [subscriptionsEstablished,
301+
modelsSynced,
302+
query,
303+
mutationSave,
304+
mutationSaveProcessed
305+
]
306+
}
307+
}
308+
}

0 commit comments

Comments
 (0)