Skip to content

Commit d068657

Browse files
authored
feat(Auth): Adding Auth test cases for test harness (#2579)
* feat(Auth): Adding test cases for Auth Test Harness * feat(Auth): Adding the ability to parse multiple API's * feat(Auth): Adding cognito identity APIs * feat(Auth): Adding codable responses and inputs * fix(Auth): optimized decoding of cognito api's * feat(Auth): Add sign in test cases * feat(Auth): add test case for device SRP * feat(Auth): Adding test cases for fetch Auth session * feat(Auth): adding signOut test cases * feat(Auth): Add sign out tests cases * chore: fix test case paths * fix: using correct states and allowing cognito to be created without response
1 parent 4df9905 commit d068657

File tree

61 files changed

+2654
-376
lines changed

Some content is hidden

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

61 files changed

+2654
-376
lines changed

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AmplifyAPIDecoding/TestHarnessAPIDecoder.swift

Lines changed: 169 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,210 @@ struct TestHarnessAPIDecoder {
1515
static func decode(
1616
specification: FeatureSpecification) -> AmplifyAPI {
1717

18-
guard let expectedAmplifyResponseValidation = specification.validations.first(where: { validation in
18+
let expectedAmplifyResponseValidation = specification.validations.first(where: { validation in
1919
validation.value(at: "type") == .string("amplify")
20-
}) else {
21-
fatalError("Expected Amplify response not found")
22-
}
20+
})
2321

24-
guard case .string(let responseType) = expectedAmplifyResponseValidation["responseType"] else {
25-
fatalError("Expected Amplify response type not found")
22+
var responseType: String? = nil
23+
if case .string(let responseTypeUnwrapped) = expectedAmplifyResponseValidation?["responseType"] {
24+
responseType = responseTypeUnwrapped
2625
}
2726

28-
guard case .object(let response) = expectedAmplifyResponseValidation["response"] else {
29-
fatalError("Expected Amplify response not found")
27+
var data: Data? = nil
28+
if case .object(let response) = expectedAmplifyResponseValidation?["response"] {
29+
data = try? JSONEncoder().encode(response)
3030
}
3131

32-
let data = try! JSONEncoder().encode(response)
33-
3432
switch specification.api.name {
3533
case .resetPassword:
3634
return resetPasswordAPI(
3735
params: specification.api.params,
3836
responseType: responseType,
3937
data: data
4038
)
39+
case .signIn:
40+
return signInAPI(
41+
params: specification.api.params,
42+
responseType: responseType,
43+
data: data)
44+
case .signUp:
45+
return signUpAPI(
46+
params: specification.api.params,
47+
responseType: responseType,
48+
data: data)
49+
case .deleteUser:
50+
return deleteUserAPI(
51+
params: specification.api.params,
52+
responseType: responseType,
53+
data: data)
54+
case .confirmSignIn:
55+
return confirmSignInAPI(
56+
params: specification.api.params,
57+
responseType: responseType,
58+
data: data)
59+
case .fetchAuthSession:
60+
return fetchAuthSession(
61+
params: specification.api.params,
62+
responseType: responseType,
63+
data: data)
64+
case .signOut:
65+
return signOutApi(
66+
options: specification.api.options,
67+
responseType: responseType,
68+
data: data)
4169
default:
4270
fatalError()
4371
}
4472
}
4573

74+
private static func signInAPI(
75+
params: JSONValue,
76+
responseType: String?,
77+
data: Data?
78+
) -> AmplifyAPI {
79+
guard case .string(let username) = params["username"] else {
80+
fatalError("missing username parameter")
81+
}
82+
var inputPassword: String?
83+
if case .string(let password) = params["password"] {
84+
inputPassword = password
85+
}
86+
return .signIn(
87+
input: .init(
88+
username: username,
89+
password: inputPassword, options: .init()),
90+
expectedOutput: generateResult(responseType: responseType, data: data))
91+
}
92+
93+
private static func signUpAPI(
94+
params: JSONValue,
95+
responseType: String?,
96+
data: Data?
97+
) -> AmplifyAPI {
98+
guard case .string(let username) = params["username"] else {
99+
fatalError("missing username parameter")
100+
}
101+
var inputPassword: String?
102+
if case .string(let password) = params["password"] {
103+
inputPassword = password
104+
}
105+
return .signUp(
106+
input: .init(
107+
username: username,
108+
password: inputPassword, options: .init()),
109+
expectedOutput: generateResult(responseType: responseType, data: data))
110+
}
111+
46112
private static func resetPasswordAPI(
47113
params: JSONValue,
48-
responseType: String,
49-
data: Data
114+
responseType: String?,
115+
data: Data?
50116
) -> AmplifyAPI {
51117
guard case .string(let username) = params["username"] else {
52118
fatalError("missing username parameter")
53119
}
120+
return .resetPassword(
121+
input: .init(username: username,
122+
options: .init()),
123+
expectedOutput: generateResult(responseType: responseType, data: data))
124+
}
125+
126+
private static func confirmSignInAPI(
127+
params: JSONValue,
128+
responseType: String?,
129+
data: Data?
130+
) -> AmplifyAPI {
131+
guard case .string(let challengeResponse) = params["challengeResponse"] else {
132+
fatalError("missing username parameter")
133+
}
134+
return .confirmSignIn(
135+
input: .init(challengeResponse: challengeResponse, options: .init()),
136+
expectedOutput: generateResult(responseType: responseType, data: data))
137+
}
138+
139+
private static func fetchAuthSession(
140+
params: JSONValue,
141+
responseType: String?,
142+
data: Data?
143+
) -> AmplifyAPI {
144+
145+
let result: Result<AWSAuthCognitoSession, AuthError>? = generateResult(
146+
responseType: responseType, data: data)
147+
148+
return .fetchAuthSession(
149+
input: .init(options: .init()),
150+
expectedOutput: result)
151+
}
152+
153+
private static func signOutApi(
154+
options: JSONValue,
155+
responseType: String?,
156+
data: Data?
157+
) -> AmplifyAPI {
158+
159+
var globalSignOut = false
160+
if case .boolean(let globalSignOutVal) = options["globalSignOut"] {
161+
globalSignOut = globalSignOutVal
162+
}
163+
164+
let result: Result<AWSCognitoSignOutResult, AuthError>? = generateResult(
165+
responseType: responseType, data: data)
54166

55-
let result: Result<AuthResetPasswordResult, AuthError>
167+
return .signOut(
168+
input: .init(options: .init(globalSignOut: globalSignOut)),
169+
expectedOutput: result)
170+
}
171+
172+
private static func deleteUserAPI(
173+
params: JSONValue,
174+
responseType: String?,
175+
data: Data?
176+
) -> AmplifyAPI {
177+
178+
guard let responseType = responseType, let data = data else {
179+
return .deleteUser(
180+
input: (),
181+
expectedOutput: nil)
182+
}
183+
184+
let result: Result<Void, AuthError>
56185

57186
switch responseType {
58187
case "failure":
59188
let authError = try! JSONDecoder().decode(
60189
AuthError.self, from: data)
61190
result = .failure(authError)
62191
case "success":
63-
let resetPasswordResult = try! JSONDecoder().decode(
64-
AuthResetPasswordResult.self, from: data)
65-
result = .success(resetPasswordResult)
192+
result = .success(())
66193
default:
67194
fatalError("invalid response type")
68195
}
69-
return .resetPassword(
70-
input: .init(username: username,
71-
options: .init()),
196+
return .deleteUser(
197+
input: (),
72198
expectedOutput: result)
73199
}
200+
201+
private static func generateResult<Output: Decodable>(
202+
responseType: String?, data: Data?) -> Result<Output, AuthError>? {
203+
204+
guard let responseType = responseType, let data = data else {
205+
return nil
206+
}
207+
208+
let result: Result<Output, AuthError>
209+
210+
switch responseType {
211+
case "failure":
212+
let authError = try! JSONDecoder().decode(
213+
AuthError.self, from: data)
214+
result = .failure(authError)
215+
case "success":
216+
let output = try! JSONDecoder().decode(
217+
Output.self, from: data)
218+
result = .success(output)
219+
default:
220+
fatalError("invalid response type")
221+
}
222+
return result
223+
}
74224
}

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TestHarness/AmplifyAuthCognitoPluginTests.swift

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,25 @@ class AmplifyAuthCognitoPluginTests: XCTestCase {
1818

1919
// Load the json configs
2020
let bundle = Bundle.authCognitoTestBundle()
21-
let testInputFiles = try! FileManager.default.contentsOfDirectory(
22-
atPath: bundle.resourcePath! + "/" + AuthTestHarnessConstants.testSuitesPath)
21+
let testSuiteDirectories = try! FileManager.default.contentsOfDirectory(
22+
atPath: "\(bundle.resourcePath!)/\(AuthTestHarnessConstants.testSuitesPath)")
2323

24-
for testInputFile in testInputFiles {
25-
XCTContext.runActivity(named: testInputFile) { activity in
26-
let specification = FeatureSpecification.init(fileName: testInputFile)
27-
let authTestHarness = AuthTestHarness(featureSpecification: specification)
28-
beginTest(for: authTestHarness.plugin,
29-
with: authTestHarness)
24+
for directory in testSuiteDirectories {
25+
26+
let testSuiteSubdirectoryPath = "\(bundle.resourcePath!)/\(AuthTestHarnessConstants.testSuitesPath)/\(directory)"
27+
let testSuiteFiles = try! FileManager.default.contentsOfDirectory(
28+
atPath: testSuiteSubdirectoryPath)
29+
30+
for testSuiteFile in testSuiteFiles {
31+
XCTContext.runActivity(named: testSuiteFile) { activity in
32+
print("Test Suite File: ---> \(directory)/\(testSuiteFile)")
33+
let specification = FeatureSpecification(
34+
fileName: testSuiteFile,
35+
subdirectory: "\(AuthTestHarnessConstants.testSuitesPath)/\(directory)")
36+
let authTestHarness = AuthTestHarness(featureSpecification: specification)
37+
beginTest(for: authTestHarness.plugin,
38+
with: authTestHarness)
39+
}
3040
}
3141
}
3242
}
@@ -41,9 +51,58 @@ class AmplifyAuthCognitoPluginTests: XCTestCase {
4151
validateAPI(expectedOutput: expectedOutput) {
4252
return try await plugin.resetPassword(
4353
for: resetPasswordRequest.username,
44-
options: .init())
54+
options: resetPasswordRequest.options)
55+
}
56+
case .signUp(let signUpRequest,
57+
let expectedOutput):
58+
validateAPI(expectedOutput: expectedOutput) {
59+
return try await plugin.signUp(
60+
username: signUpRequest.username,
61+
password: signUpRequest.password, options: signUpRequest.options)
62+
}
63+
case .signIn(let request,
64+
let expectedOutput):
65+
validateAPI(expectedOutput: expectedOutput) {
66+
return try await plugin.signIn(
67+
username: request.username,
68+
password: request.password, options: request.options)
69+
}
70+
case .fetchAuthSession(let request,
71+
let expectedOutput):
72+
validateAPI(expectedOutput: expectedOutput) {
73+
return try await plugin.fetchAuthSession(options: request.options) as! AWSAuthCognitoSession
74+
}
75+
case .signOut(let request, let expectedOutput):
76+
validateAPI(expectedOutput: expectedOutput) {
77+
return await plugin.signOut(options: request.options) as! AWSCognitoSignOutResult
78+
}
79+
80+
case .deleteUser(_, let expectedOutput):
81+
let expectation = expectation(description: "expectation")
82+
Task {
83+
do {
84+
try await plugin.deleteUser()
85+
expectation.fulfill()
86+
} catch let error as AuthError {
87+
if case .failure(let expectedError) = expectedOutput {
88+
XCTAssertEqual(error, expectedError)
89+
} else {
90+
XCTFail("API should not throw AuthError")
91+
}
92+
expectation.fulfill()
93+
} catch {
94+
XCTFail("API should not throw AuthError")
95+
expectation.fulfill()
96+
}
97+
}
98+
wait(for: [expectation], timeout: apiTimeout)
99+
case .confirmSignIn(let request, expectedOutput: let expectedOutput):
100+
validateAPI(expectedOutput: expectedOutput) {
101+
return try await plugin.confirmSignIn(
102+
challengeResponse: request.challengeResponse, options: request.options)
45103
}
46104
}
105+
47106
}
48107

49108

@@ -52,7 +111,7 @@ class AmplifyAuthCognitoPluginTests: XCTestCase {
52111
expectedOutput: Result<T, AuthError>?,
53112
apiCall: @escaping () async throws -> T) {
54113

55-
let expectation = expectation(description: "Reset password expectation")
114+
let expectation = expectation(description: "expectation")
56115
Task {
57116
do {
58117
let result = try await apiCall()
@@ -62,7 +121,7 @@ class AmplifyAuthCognitoPluginTests: XCTestCase {
62121
XCTAssertEqual(expectedOutput, Result.failure(error))
63122
expectation.fulfill()
64123
} catch {
65-
XCTFail("Reset password API should throw AuthError")
124+
XCTFail("API should not throw AuthError")
66125
expectation.fulfill()
67126
}
68127
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 AWSCognitoIdentityProvider
9+
import AWSCognitoIdentity
10+
import ClientRuntime
11+
12+
extension ConfirmDeviceInput: Decodable {
13+
enum CodingKeys: String, CodingKey {
14+
case accessToken
15+
case deviceKey
16+
case deviceName
17+
case deviceSecretVerifierConfig
18+
case passwordVerifier
19+
case salt
20+
}
21+
22+
public init(from decoder: Decoder) throws {
23+
let values = try decoder.container(keyedBy: CodingKeys.self)
24+
let nestedChild = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .deviceSecretVerifierConfig)
25+
let accessToken = try values.decodeIfPresent(String.self, forKey: .accessToken)
26+
let deviceKey = try values.decodeIfPresent(String.self, forKey: .deviceKey)
27+
let deviceName = try values.decodeIfPresent(String.self, forKey: .deviceName)
28+
let passwordVerifier = try nestedChild.decodeIfPresent(String.self, forKey: .passwordVerifier)
29+
let salt = try nestedChild.decodeIfPresent(String.self, forKey: .salt)
30+
self.init(
31+
accessToken: accessToken,
32+
deviceKey: deviceKey,
33+
deviceName: deviceName,
34+
deviceSecretVerifierConfig: .init(
35+
passwordVerifier: passwordVerifier,
36+
salt: salt)
37+
)
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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 AWSCognitoIdentityProvider
9+
import ClientRuntime
10+
11+
extension DeleteUserInput: Decodable {
12+
13+
public init(from decoder: Decoder) throws {
14+
self.init()
15+
}
16+
}

0 commit comments

Comments
 (0)