24
24
25
25
import Foundation
26
26
import XCTest
27
+ import MSAL
27
28
28
29
final class MSALNativeAuthResetPasswordEndToEndTests : MSALNativeAuthEndToEndBaseTestCase {
29
30
// Hero Scenario 3.1.1. SSPR – without automatic sign in
31
+ private let codeRetryCount = 3
32
+
30
33
func test_resetPassword_withoutAutomaticSignIn_succeeds( ) async throws {
31
34
guard let sut = initialisePublicClientApplication ( ) ,
32
35
let username = retrieveUsernameForResetPassword ( )
@@ -52,31 +55,16 @@ final class MSALNativeAuthResetPasswordEndToEndTests: MSALNativeAuthEndToEndBase
52
55
XCTAssertNotNil ( resetPasswordStartDelegate. codeLength)
53
56
54
57
// Now submit the code...
55
-
56
- let passwordRequiredExp = expectation ( description: " password required " )
57
- let resetPasswordVerifyDelegate = ResetPasswordVerifyCodeDelegateSpy ( expectation: passwordRequiredExp)
58
-
59
- guard let code = await retrieveCodeFor ( email: username) else {
60
- XCTFail ( " OTP code not retrieved from email " )
61
- return
62
- }
63
-
64
- resetPasswordStartDelegate. newState? . submitCode ( code: code, delegate: resetPasswordVerifyDelegate)
65
-
66
- await fulfillment ( of: [ passwordRequiredExp] )
67
- XCTAssertTrue ( resetPasswordVerifyDelegate. onPasswordRequiredCalled)
68
-
69
- guard resetPasswordVerifyDelegate. onPasswordRequiredCalled else {
70
- XCTFail ( " onPasswordRequired not called " )
71
- return
72
- }
58
+ let newPasswordRequiredState = await retrieveAndSubmitCode ( resetPasswordStartDelegate: resetPasswordStartDelegate,
59
+ username: username,
60
+ retries: codeRetryCount)
73
61
74
62
// Now submit the password...
75
63
let resetPasswordCompletedExp = expectation ( description: " reset password completed " )
76
64
let resetPasswordRequiredDelegate = ResetPasswordRequiredDelegateSpy ( expectation: resetPasswordCompletedExp)
77
65
78
66
let uniquePassword = generateRandomPassword ( )
79
- resetPasswordVerifyDelegate . newPasswordRequiredState? . submitPassword ( password: uniquePassword, delegate: resetPasswordRequiredDelegate)
67
+ newPasswordRequiredState? . submitPassword ( password: uniquePassword, delegate: resetPasswordRequiredDelegate)
80
68
81
69
await fulfillment ( of: [ resetPasswordCompletedExp] )
82
70
XCTAssertTrue ( resetPasswordRequiredDelegate. onResetPasswordCompletedCalled)
@@ -108,31 +96,16 @@ final class MSALNativeAuthResetPasswordEndToEndTests: MSALNativeAuthEndToEndBase
108
96
XCTAssertNotNil ( resetPasswordStartDelegate. codeLength)
109
97
110
98
// Now submit the code...
111
-
112
- let passwordRequiredExp = expectation ( description: " password required " )
113
- let resetPasswordVerifyDelegate = ResetPasswordVerifyCodeDelegateSpy ( expectation: passwordRequiredExp)
114
-
115
- guard let code = await retrieveCodeFor ( email: username) else {
116
- XCTFail ( " OTP code not retrieved from email " )
117
- return
118
- }
119
-
120
- resetPasswordStartDelegate. newState? . submitCode ( code: code, delegate: resetPasswordVerifyDelegate)
121
-
122
- await fulfillment ( of: [ passwordRequiredExp] )
123
- XCTAssertTrue ( resetPasswordVerifyDelegate. onPasswordRequiredCalled)
124
-
125
- guard resetPasswordVerifyDelegate. onPasswordRequiredCalled else {
126
- XCTFail ( " onPasswordRequired not called " )
127
- return
128
- }
99
+ let newPasswordRequiredState = await retrieveAndSubmitCode ( resetPasswordStartDelegate: resetPasswordStartDelegate,
100
+ username: username,
101
+ retries: codeRetryCount)
129
102
130
103
// Now submit the password...
131
104
let resetPasswordCompletedExp = expectation ( description: " reset password completed " )
132
105
let resetPasswordRequiredDelegate = ResetPasswordRequiredDelegateSpy ( expectation: resetPasswordCompletedExp)
133
106
134
107
let uniquePassword = generateRandomPassword ( )
135
- resetPasswordVerifyDelegate . newPasswordRequiredState? . submitPassword ( password: uniquePassword, delegate: resetPasswordRequiredDelegate)
108
+ newPasswordRequiredState? . submitPassword ( password: uniquePassword, delegate: resetPasswordRequiredDelegate)
136
109
137
110
await fulfillment ( of: [ resetPasswordCompletedExp] )
138
111
XCTAssertTrue ( resetPasswordRequiredDelegate. onResetPasswordCompletedCalled)
@@ -155,4 +128,27 @@ final class MSALNativeAuthResetPasswordEndToEndTests: MSALNativeAuthEndToEndBase
155
128
XCTAssertNotNil ( signInAfterResetPasswordDelegate. result? . idToken)
156
129
XCTAssertNotNil ( signInAfterResetPasswordDelegate. result? . account. accountClaims)
157
130
}
131
+
132
+ // This method tries to fetch a code from 1secmail API and submit it
133
+ private func retrieveAndSubmitCode( resetPasswordStartDelegate: ResetPasswordStartDelegateSpy , username: String , retries: Int ) async -> ResetPasswordRequiredState ? {
134
+ let passwordRequiredExp = expectation ( description: " password required " )
135
+ let resetPasswordVerifyDelegate = ResetPasswordVerifyCodeDelegateSpy ( expectation: passwordRequiredExp)
136
+
137
+ guard let code = await retrieveCodeFor ( email: username) else {
138
+ XCTFail ( " OTP code not retrieved from email " )
139
+ return nil
140
+ }
141
+
142
+ resetPasswordStartDelegate. newState? . submitCode ( code: code, delegate: resetPasswordVerifyDelegate)
143
+
144
+ await fulfillment ( of: [ passwordRequiredExp] )
145
+ if resetPasswordVerifyDelegate. onResetPasswordVerifyCodeErrorCalled && resetPasswordVerifyDelegate. error? . isInvalidCode == true && retries > 0 {
146
+ return await retrieveAndSubmitCode ( resetPasswordStartDelegate: resetPasswordStartDelegate, username: username, retries: retries - 1 )
147
+ }
148
+ guard resetPasswordVerifyDelegate. onPasswordRequiredCalled else {
149
+ XCTFail ( " onPasswordRequired not called " )
150
+ return nil
151
+ }
152
+ return resetPasswordVerifyDelegate. newPasswordRequiredState
153
+ }
158
154
}
0 commit comments