Skip to content

Commit a9ed47c

Browse files
committed
Rewrite and enable OAuth2ImplicitGrantTests
1 parent e61fa88 commit a9ed47c

File tree

4 files changed

+169
-148
lines changed

4 files changed

+169
-148
lines changed

OAuth2.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
879EE6F72CF61295008B3D74 /* OAuth2TokenTypeIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EE6EE2CF61295008B3D74 /* OAuth2TokenTypeIdentifiers.swift */; };
4747
879EE6F82CF61295008B3D74 /* OAuth2GrantTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EE6EC2CF61295008B3D74 /* OAuth2GrantTypes.swift */; };
4848
87B3E07C29F6AF240075C4DC /* OAuth2DeviceGrantTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B3E07B29F6AF240075C4DC /* OAuth2DeviceGrantTests.swift */; };
49+
87FABDD32DC8D77000E0C67B /* XCTAssertThrowsErrorAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FABDD22DC8D77000E0C67B /* XCTAssertThrowsErrorAsync.swift */; };
4950
CCCE40D6B4EAD9BF05C92ACE /* OAuth2CustomAuthorizer+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCE4C8DC3CB7713E59BC1EE /* OAuth2CustomAuthorizer+iOS.swift */; };
5051
DD0CCBAD1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift */; };
5152
EA9758181B222CEA007744B1 /* OAuth2PasswordGrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9758171B222CEA007744B1 /* OAuth2PasswordGrant.swift */; };
@@ -187,6 +188,7 @@
187188
879EE6ED2CF61295008B3D74 /* OAuth2ResponseTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth2ResponseTypes.swift; sourceTree = "<group>"; };
188189
879EE6EE2CF61295008B3D74 /* OAuth2TokenTypeIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth2TokenTypeIdentifiers.swift; sourceTree = "<group>"; };
189190
87B3E07B29F6AF240075C4DC /* OAuth2DeviceGrantTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2DeviceGrantTests.swift; sourceTree = "<group>"; };
191+
87FABDD22DC8D77000E0C67B /* XCTAssertThrowsErrorAsync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTAssertThrowsErrorAsync.swift; sourceTree = "<group>"; };
190192
CCCE4C8DC3CB7713E59BC1EE /* OAuth2CustomAuthorizer+iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OAuth2CustomAuthorizer+iOS.swift"; sourceTree = "<group>"; };
191193
DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OAuth2WebViewController+macOS.swift"; sourceTree = "<group>"; };
192194
EA9758171B222CEA007744B1 /* OAuth2PasswordGrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = OAuth2PasswordGrant.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
@@ -305,6 +307,14 @@
305307
path = Constants;
306308
sourceTree = "<group>";
307309
};
310+
87FABDD52DC8D7C300E0C67B /* Utils */ = {
311+
isa = PBXGroup;
312+
children = (
313+
87FABDD22DC8D77000E0C67B /* XCTAssertThrowsErrorAsync.swift */,
314+
);
315+
path = Utils;
316+
sourceTree = "<group>";
317+
};
308318
EE2486281AC85DD4002B31AF /* iOS */ = {
309319
isa = PBXGroup;
310320
children = (
@@ -474,6 +484,7 @@
474484
EE4EBD801D7FF38200E6A9CA /* Flows */,
475485
EEB9A9811D86CD540022EF66 /* DataLoader */,
476486
EEE209A419427DFE00736F1A /* Supporting Files */,
487+
87FABDD52DC8D7C300E0C67B /* Utils */,
477488
);
478489
path = Tests;
479490
sourceTree = "<group>";
@@ -806,6 +817,7 @@
806817
buildActionMask = 2147483647;
807818
files = (
808819
EE4EBD8D1D7FF38200E6A9CA /* OAuth2PasswordGrantTests.swift in Sources */,
820+
87FABDD32DC8D77000E0C67B /* XCTAssertThrowsErrorAsync.swift in Sources */,
809821
EE4EBD881D7FF38200E6A9CA /* OAuth2AuthRequestTests.swift in Sources */,
810822
EE4EBD891D7FF38200E6A9CA /* OAuth2ClientCredentialsTests.swift in Sources */,
811823
871279852DB7DD1B00A5AF72 /* OAuth2ExchangeAccessTokenForResourceTests.swift in Sources */,

Tests/FlowTests/OAuth2CodeGrantTests.swift

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ class OAuth2CodeGrantTests: XCTestCase {
376376
XCTAssertEqual(comp.host!, "auth.ful.io", "Correct host")
377377
}
378378

379-
func testTokenResponse() async {
379+
func testTokenResponse() async throws {
380380
let settings = [
381381
"client_id": "abc",
382382
"client_secret": "xyz",
@@ -471,21 +471,16 @@ class OAuth2CodeGrantTests: XCTestCase {
471471
performer.responseJSON = response
472472
performer.responseStatus = 403
473473
oauth.context.redirectURL = "https://localhost"
474-
// oauth.didAuthorizeOrFail = { json, error in
475-
// XCTAssertNil(json)
476-
// XCTAssertNotNil(error)
477-
// XCTAssertEqual(OAuth2Error.forbidden, error)
478-
// }
479-
await oauth.exchangeCodeForToken("MNOP")
474+
475+
await XCTAssertThrowsErrorAsync(try await oauth.exchangeCodeForToken("MNOP")) { error in
476+
XCTAssertEqual(OAuth2Error.forbidden, error.asOAuth2Error)
477+
}
480478

481479
// test round trip - should succeed because of good HTTP status
482480
performer.responseStatus = 301
483-
// oauth.didAuthorizeOrFail = { json, error in
484-
// XCTAssertNotNil(json)
485-
// XCTAssertNil(error)
486-
// XCTAssertEqual("tGzv3JOkF0XG5Qx2TlKWIA", json?["refresh_token"] as? String)
487-
// }
488-
await oauth.exchangeCodeForToken("MNOP")
481+
482+
let json = try await oauth.exchangeCodeForToken("MNOP")
483+
XCTAssertEqual("tGzv3JOkF0XG5Qx2TlKWIA", json["refresh_token"] as? String)
489484
}
490485
}
491486

Tests/FlowTests/OAuth2ImplicitGrantTests.swift

Lines changed: 106 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -31,138 +31,109 @@ import OAuth2
3131
#endif
3232

3333

34-
//class OAuth2ImplicitGrantTests: XCTestCase
35-
//{
36-
// func testInit() {
37-
// let oauth = OAuth2ImplicitGrant(settings: [
38-
// "client_id": "abc",
39-
// "keychain": false,
40-
// "authorize_uri": "https://auth.ful.io",
41-
// ])
42-
// XCTAssertEqual(oauth.clientId, "abc", "Must init `client_id`")
43-
// XCTAssertNil(oauth.scope, "Empty scope")
44-
//
45-
// XCTAssertEqual(oauth.authURL, URL(string: "https://auth.ful.io")!, "Must init `authorize_uri`")
46-
// }
47-
//
48-
// func testReturnURLHandling() {
49-
// let oauth = OAuth2ImplicitGrant(settings: [
50-
// "client_id": "abc",
51-
// "authorize_uri": "https://auth.ful.io",
52-
// "keychain": false,
53-
// ])
54-
//
55-
// // Empty redirect URL
56-
// oauth.didAuthorizeOrFail = { authParameters, error in
57-
// XCTAssertNil(authParameters, "Nil auth dict expected")
58-
// XCTAssertNotNil(error, "Error message expected")
59-
// XCTAssertEqual(error, OAuth2Error.invalidRedirectURL("file:///"))
60-
// }
61-
// oauth.afterAuthorizeOrFail = { authParameters, error in
62-
// XCTAssertNil(authParameters, "Nil auth dict expected")
63-
// XCTAssertNotNil(error, "Error message expected")
64-
// }
65-
// oauth.context._state = "ONSTUH"
66-
// oauth.handleRedirectURL(URL(string: "file:///")!)
67-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
68-
//
69-
// // No params in redirect URL
70-
// oauth.didAuthorizeOrFail = { authParameters, error in
71-
// XCTAssertNil(authParameters, "Nil auth dict expected")
72-
// XCTAssertNotNil(error, "Error message expected")
73-
// XCTAssertEqual(error, OAuth2Error.invalidRedirectURL("https://auth.ful.io"))
74-
// }
75-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io")!)
76-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
77-
//
78-
// // standard error
79-
// oauth.context._state = "ONSTUH" // because it has been reset
80-
// oauth.didAuthorizeOrFail = { authParameters, error in
81-
// XCTAssertNil(authParameters, "Nil auth dict expected")
82-
// XCTAssertNotNil(error, "Error message expected")
83-
// XCTAssertEqual(error, OAuth2Error.accessDenied(nil))
84-
// XCTAssertEqual(error?.description, "The resource owner or authorization server denied the request.")
85-
// }
86-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#error=access_denied")!)
87-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
88-
//
89-
// // explicit error
90-
// oauth.context._state = "ONSTUH" // because it has been reset
91-
// oauth.didAuthorizeOrFail = { authParameters, error in
92-
// XCTAssertNil(authParameters, "Nil auth dict expected")
93-
// XCTAssertNotNil(error, "Error message expected")
94-
// XCTAssertNotEqual(error, OAuth2Error.generic("Not good"))
95-
// XCTAssertEqual(error, OAuth2Error.responseError("Not good"))
96-
// XCTAssertEqual(error?.description, "Not good")
97-
// }
98-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#error_description=Not+good")!)
99-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
100-
//
101-
// // no token type
102-
// oauth.context._state = "ONSTUH" // because it has been reset
103-
// oauth.didAuthorizeOrFail = { authParameters, error in
104-
// XCTAssertNil(authParameters, "Nil auth dict expected")
105-
// XCTAssertNotNil(error, "Error message expected")
106-
// XCTAssertEqual(error, OAuth2Error.noTokenType)
107-
// }
108-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#access_token=abc&state=\(oauth.context.state)")!)
109-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
110-
//
111-
// // unsupported token type
112-
// oauth.context._state = "ONSTUH" // because it has been reset
113-
// oauth.didAuthorizeOrFail = { authParameters, error in
114-
// XCTAssertNil(authParameters, "Nil auth dict expected")
115-
// XCTAssertNotNil(error, "Error message expected")
116-
// XCTAssertEqual(error, OAuth2Error.unsupportedTokenType("Only “bearer” token is supported, but received “helicopter”"))
117-
// }
118-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=helicopter&access_token=abc&state=\(oauth.context.state)")!)
119-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
120-
//
121-
// // Missing state
122-
// oauth.context._state = "ONSTUH" // because it has been reset
123-
// oauth.didAuthorizeOrFail = { authParameters, error in
124-
// XCTAssertNil(authParameters, "Nil auth dict expected")
125-
// XCTAssertNotNil(error, "Error message expected")
126-
// XCTAssertEqual(error, OAuth2Error.missingState)
127-
// }
128-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc")!)
129-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
130-
//
131-
// // Invalid state
132-
// oauth.context._state = "ONSTUH" // because it has been reset
133-
// oauth.didAuthorizeOrFail = { authParameters, error in
134-
// XCTAssertNil(authParameters, "Nil auth dict expected")
135-
// XCTAssertNotNil(error, "Error message expected")
136-
// XCTAssertEqual(error, OAuth2Error.invalidState)
137-
// }
138-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc&state=ONSTOH")!)
139-
// XCTAssertNil(oauth.accessToken, "Must not have an access token")
140-
//
141-
// // success 1
142-
// oauth.didAuthorizeOrFail = { authParameters, error in
143-
// XCTAssertNotNil(authParameters, "auth parameters expected")
144-
// XCTAssertNil(error, "No error message expected")
145-
// }
146-
// oauth.afterAuthorizeOrFail = { authParameters, error in
147-
// XCTAssertNotNil(authParameters, "auth parameters expected")
148-
// XCTAssertNil(error, "No error message expected")
149-
// }
150-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc&state=\(oauth.context.state)&expires_in=3599")!)
151-
// XCTAssertNotNil(oauth.accessToken, "Must have an access token")
152-
// XCTAssertEqual(oauth.accessToken!, "abc")
153-
// XCTAssertNotNil(oauth.accessTokenExpiry)
154-
// XCTAssertTrue(oauth.hasUnexpiredAccessToken())
155-
//
156-
// // success 2
157-
// oauth.didAuthorizeOrFail = { authParameters, error in
158-
// XCTAssertNotNil(authParameters, "auth parameters expected")
159-
// XCTAssertNil(error, "No error message expected")
160-
// }
161-
// oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc&state=\(oauth.context.state)")!)
162-
// XCTAssertNotNil(oauth.accessToken, "Must have an access token")
163-
// XCTAssertEqual(oauth.accessToken!, "abc")
164-
// XCTAssertNil(oauth.accessTokenExpiry)
165-
// XCTAssertTrue(oauth.hasUnexpiredAccessToken())
166-
// }
167-
//}
168-
34+
@OAuth2Actor
35+
class OAuth2ImplicitGrantTests: XCTestCase
36+
{
37+
func testInit() {
38+
let oauth = OAuth2ImplicitGrant(settings: [
39+
"client_id": "abc",
40+
"keychain": false,
41+
"authorize_uri": "https://auth.ful.io",
42+
])
43+
XCTAssertEqual(oauth.clientId, "abc", "Must init `client_id`")
44+
XCTAssertNil(oauth.scope, "Empty scope")
45+
46+
XCTAssertEqual(oauth.authURL, URL(string: "https://auth.ful.io")!, "Must init `authorize_uri`")
47+
}
48+
49+
func testReturnURLHandling() async {
50+
let oauth = OAuth2ImplicitGrant(settings: [
51+
"client_id": "abc",
52+
"authorize_uri": "https://auth.ful.io",
53+
"keychain": false,
54+
])
55+
56+
// Empty redirect URL
57+
oauth.afterAuthorizeOrFail = { authParameters, error in
58+
XCTAssertNil(authParameters, "Nil auth dict expected")
59+
XCTAssertNotNil(error, "Error message expected")
60+
}
61+
62+
oauth.context._state = "ONSTUH"
63+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "file:///")!)) { error in
64+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.invalidRedirectURL("file:///"))
65+
}
66+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
67+
68+
// No params in redirect URL
69+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "https://auth.ful.io")!)) { error in
70+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.invalidRedirectURL("https://auth.ful.io"))
71+
}
72+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
73+
74+
// standard error
75+
oauth.context._state = "ONSTUH" // because it has been reset
76+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#error=access_denied")!)) { error in
77+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.accessDenied(nil))
78+
XCTAssertEqual(error.asOAuth2Error.description, "The resource owner or authorization server denied the request.")
79+
}
80+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
81+
82+
// explicit error
83+
oauth.context._state = "ONSTUH" // because it has been reset
84+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#error_description=Not+good")!)) { error in
85+
XCTAssertNotEqual(error.asOAuth2Error, OAuth2Error.generic("Not good"))
86+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.responseError("Not good"))
87+
XCTAssertEqual(error.asOAuth2Error.description, "Not good")
88+
}
89+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
90+
91+
// no token type
92+
oauth.context._state = "ONSTUH" // because it has been reset
93+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#access_token=abc&state=\(oauth.context.state)")!)) { error in
94+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.noTokenType)
95+
}
96+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
97+
98+
// unsupported token type
99+
oauth.context._state = "ONSTUH" // because it has been reset
100+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=helicopter&access_token=abc&state=\(oauth.context.state)")!)) { error in
101+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.unsupportedTokenType("Only “bearer” token is supported, but received “helicopter”"))
102+
}
103+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
104+
105+
// Missing state
106+
oauth.context._state = "ONSTUH" // because it has been reset
107+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc")!)) { error in
108+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.missingState)
109+
}
110+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
111+
112+
// Invalid state
113+
oauth.context._state = "ONSTUH" // because it has been reset
114+
await XCTAssertThrowsErrorAsync(try await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc&state=ONSTOH")!)) { error in
115+
XCTAssertEqual(error.asOAuth2Error, OAuth2Error.invalidState)
116+
}
117+
XCTAssertNil(oauth.accessToken, "Must not have an access token")
118+
119+
// success 1
120+
oauth.afterAuthorizeOrFail = { authParameters, error in
121+
XCTAssertNotNil(authParameters, "auth parameters expected")
122+
XCTAssertNil(error, "No error message expected")
123+
}
124+
let authParameters1 = try? await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc&state=\(oauth.context.state)&expires_in=3599")!)
125+
XCTAssertNotNil(authParameters1, "auth parameters expected")
126+
XCTAssertNotNil(oauth.accessToken, "Must have an access token")
127+
XCTAssertEqual(oauth.accessToken!, "abc")
128+
XCTAssertNotNil(oauth.accessTokenExpiry)
129+
XCTAssertTrue(oauth.hasUnexpiredAccessToken())
130+
131+
// success 2
132+
let authParameters2 = try? await oauth.handleRedirectURL(URL(string: "https://auth.ful.io#token_type=bearer&access_token=abc&state=\(oauth.context.state)")!)
133+
XCTAssertNotNil(authParameters2, "auth parameters expected")
134+
XCTAssertNotNil(oauth.accessToken, "Must have an access token")
135+
XCTAssertEqual(oauth.accessToken!, "abc")
136+
XCTAssertNil(oauth.accessTokenExpiry)
137+
XCTAssertTrue(oauth.hasUnexpiredAccessToken())
138+
}
139+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import XCTest
2+
import OAuth2
3+
4+
/// Asserts that an asynchronous expression throws an error.
5+
/// (Intended to function as a drop-in asynchronous version of `XCTAssertThrowsError`.)
6+
///
7+
/// Example usage:
8+
///
9+
/// await assertThrowsAsyncError(
10+
/// try await sut.function()
11+
/// ) { error in
12+
/// XCTAssertEqual(error as? MyError, MyError.specificError)
13+
/// }
14+
///
15+
/// - Parameters:
16+
/// - expression: An asynchronous expression that can throw an error.
17+
/// - message: An optional description of a failure.
18+
/// - file: The file where the failure occurs.
19+
/// The default is the filename of the test case where you call this function.
20+
/// - line: The line number where the failure occurs.
21+
/// The default is the line number where you call this function.
22+
/// - errorHandler: An optional handler for errors that expression throws.
23+
@OAuth2Actor
24+
func XCTAssertThrowsErrorAsync<T: Sendable>(
25+
_ expression: @autoclosure () async throws -> T,
26+
_ message: @autoclosure () -> String = "",
27+
file: StaticString = #filePath,
28+
line: UInt = #line,
29+
_ errorHandler: (_ error: Error) -> Void = { _ in }
30+
) async {
31+
do {
32+
_ = try await expression()
33+
// expected error to be thrown, but it was not
34+
let customMessage = message()
35+
if customMessage.isEmpty {
36+
XCTFail("Asynchronous call did not throw an error.", file: file, line: line)
37+
} else {
38+
XCTFail(customMessage, file: file, line: line)
39+
}
40+
} catch {
41+
errorHandler(error)
42+
}
43+
}

0 commit comments

Comments
 (0)