Skip to content

Commit 803cd82

Browse files
committed
basic login tests
1 parent dfc3ebd commit 803cd82

File tree

4 files changed

+115
-11
lines changed

4 files changed

+115
-11
lines changed

Sources/App/Controllers/Manage/LoginController.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import Dependencies
23
import Fluent
34
import Plot
45
import Vapor
@@ -14,13 +15,24 @@ enum LoginController {
1415

1516
@Sendable
1617
static func login(req: Request) async throws -> Response {
18+
@Dependency(\.cognito) var cognito
19+
let awsClient = AWSClient(httpClientProvider: .shared(req.application.http.client.shared))
20+
let awsCognitoConfiguration = CognitoConfiguration(
21+
userPoolId: Environment.get("POOL_ID")!,
22+
clientId: Environment.get("CLIENT_ID")!,
23+
clientSecret: Environment.get("CLIENT_SECRET")!,
24+
cognitoIDP: CognitoIdentityProvider(client: awsClient, region: .useast2),
25+
adminClient: true
26+
)
27+
req.application.cognito.authenticatable = CognitoAuthenticatable(configuration: awsCognitoConfiguration)
1728
struct UserCreds: Content {
1829
var email: String
1930
var password: String
2031
}
2132
do {
2233
let user = try req.content.decode(UserCreds.self)
23-
try await Cognito.authenticate(req: req, username: user.email, password: user.password)
34+
try await cognito.authenticate(req: req, username: user.email, password: user.password)
35+
try await awsClient.shutdown()
2436
return req.redirect(to: SiteURL.portal.relativeURL(), redirectType: .normal)
2537
} catch let error as SotoCognitoError {
2638
var model = Login.Model(errorMessage: "There was an error. Please try again.")
@@ -32,10 +44,13 @@ enum LoginController {
3244
case .invalidPublicKey:
3345
break
3446
}
47+
try await awsClient.shutdown()
3548
return Login.View(path: req.url.path, model: model).document().encodeResponse(status: .unauthorized)
3649
} catch let error as AWSClientError {
50+
try await awsClient.shutdown()
3751
return Login.View(path: SiteURL.signup.relativeURL(), model: Login.Model(errorMessage: "An AWS client error occurred: \(error.errorCode)")).document().encodeResponse(status: .unauthorized)
3852
} catch {
53+
try await awsClient.shutdown()
3954
return Login.View(path: SiteURL.signup.relativeURL(), model: Login.Model(errorMessage: "An unknown error occurred: \(error.localizedDescription)")).document().encodeResponse(status: .unauthorized)
4055
}
4156

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Dependencies
16+
import DependenciesMacros
17+
import Vapor
18+
19+
@DependencyClient
20+
struct CognitoClient {
21+
var authenticate: @Sendable (_ req: Request, _ username: String, _ password: String) async throws -> Void
22+
}
23+
24+
extension CognitoClient: DependencyKey {
25+
static var liveValue: CognitoClient {
26+
.init(
27+
authenticate: { req, username, password in try await Cognito.authenticate(req: req, username: username, password: password) }
28+
)
29+
}
30+
}
31+
32+
extension CognitoClient: Sendable, TestDependencyKey {
33+
static var testValue: Self { Self() }
34+
}
35+
36+
extension DependencyValues {
37+
var cognito: CognitoClient {
38+
get { self[CognitoClient.self] }
39+
set { self[CognitoClient.self] = newValue }
40+
}
41+
}
42+

Sources/App/configure.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,6 @@ public func configure(_ app: Application) async throws -> String {
8181

8282
app.sessions.use(.memory)
8383

84-
let awsClient = AWSClient(httpClientProvider: .shared(app.http.client.shared))
85-
let awsCognitoConfiguration = CognitoConfiguration(
86-
userPoolId: Environment.get("POOL_ID")!,
87-
clientId: Environment.get("CLIENT_ID")!,
88-
clientSecret: Environment.get("CLIENT_SECRET")!,
89-
cognitoIDP: CognitoIdentityProvider(client: awsClient, region: .useast2),
90-
adminClient: true
91-
)
92-
app.cognito.authenticatable = CognitoAuthenticatable(configuration: awsCognitoConfiguration)
93-
9484

9585
// Configures cookie value creation.
9686
app.sessions.configuration.cookieFactory = { sessionID in

Tests/AppTests/ManageTests.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
@testable import App
16+
17+
import XCTest
18+
import Fluent
19+
import Vapor
20+
import Dependencies
21+
22+
23+
class ManageTests: AppTestCase {
24+
25+
func test_portal_route_protected() throws {
26+
try app.test(.GET, "portal") { res in
27+
XCTAssertEqual(res.status, .seeOther)
28+
}
29+
}
30+
31+
func test_login_successful_redirect() throws {
32+
// not throwing in auth is a successful authentication and user
33+
// should be redirected
34+
try withDependencies {
35+
var mock: @Sendable (_ req: Request, _ username: String, _ password: String) async throws -> Void = { _, _, _ in }
36+
$0.cognito.authenticate = mock
37+
} operation: {
38+
try app.test(.POST, "login", beforeRequest: { req in try req.content.encode(["email": "testemail", "password": "testpassword"])
39+
}, afterResponse: { res in
40+
XCTAssertEqual(res.status, .seeOther)
41+
})
42+
}
43+
}
44+
45+
func test_login_throws() throws {
46+
struct SomeError: Error {}
47+
try withDependencies {
48+
var mock: @Sendable (_ req: Request, _ username: String, _ password: String) async throws -> Void = { _, _, _ in throw SomeError() }
49+
$0.cognito.authenticate = mock
50+
} operation: {
51+
try app.test(.POST, "login") { res in
52+
XCTAssertEqual(res.status, .unauthorized)
53+
}
54+
}
55+
}
56+
}
57+

0 commit comments

Comments
 (0)