140 lines
5.7 KiB
Swift
140 lines
5.7 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import CodexBarCore
|
|
|
|
private final class CapturedEnvironment: @unchecked Sendable {
|
|
private let lock = NSLock()
|
|
private var stored: [String: String] = [:]
|
|
func record(_ environment: [String: String]) {
|
|
self.lock.withLock { self.stored = environment }
|
|
}
|
|
|
|
var value: [String: String] {
|
|
self.lock.withLock { self.stored }
|
|
}
|
|
}
|
|
|
|
@Suite(.serialized)
|
|
struct BedrockCredentialResolverTests {
|
|
private static let credentialsJSON = #"""
|
|
{"Version":1,"AccessKeyId":"AKIAPROFILE","SecretAccessKey":"profile-secret","SessionToken":"profile-token"}
|
|
"""#
|
|
|
|
/// Fake AWS CLI runner: returns exported credentials and a profile region.
|
|
private func profileProvider(region: String = "ap-southeast-2") -> BedrockProfileCredentialProvider {
|
|
BedrockProfileCredentialProvider(awsBinaryPath: "/usr/bin/aws") { arguments, _ in
|
|
if arguments.contains("export-credentials") {
|
|
return SubprocessResult(stdout: Self.credentialsJSON, stderr: "")
|
|
}
|
|
if arguments.contains("get") {
|
|
return SubprocessResult(stdout: region + "\n", stderr: "")
|
|
}
|
|
return SubprocessResult(stdout: "", stderr: "")
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `keys mode resolves static credentials and region`() async throws {
|
|
let env = [
|
|
BedrockSettingsReader.accessKeyIDKey: "AKIAKEYS",
|
|
BedrockSettingsReader.secretAccessKeyKey: "keys-secret",
|
|
BedrockSettingsReader.regionKeys[0]: "us-west-2",
|
|
]
|
|
let resolved = try await BedrockCredentialResolver.resolve(environment: env)
|
|
#expect(resolved.credentials.accessKeyID == "AKIAKEYS")
|
|
#expect(resolved.credentials.secretAccessKey == "keys-secret")
|
|
#expect(resolved.region == "us-west-2")
|
|
}
|
|
|
|
@Test
|
|
func `keys mode without credentials throws missingCredentials`() async {
|
|
await #expect(throws: BedrockUsageError.missingCredentials) {
|
|
try await BedrockCredentialResolver.resolve(environment: [:])
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `profile mode resolves credentials via the AWS CLI`() async throws {
|
|
let env = [
|
|
BedrockSettingsReader.authModeKey: "profile",
|
|
BedrockSettingsReader.profileKey: "work",
|
|
]
|
|
let resolved = try await BedrockCredentialResolver.resolve(
|
|
environment: env,
|
|
resolveAWSBinary: { _ in "/usr/bin/aws" },
|
|
makeProvider: { _ in self.profileProvider() })
|
|
#expect(resolved.credentials.accessKeyID == "AKIAPROFILE")
|
|
#expect(resolved.credentials.sessionToken == "profile-token")
|
|
// No explicit region in env, so it is derived from the profile.
|
|
#expect(resolved.region == "ap-southeast-2")
|
|
}
|
|
|
|
@Test
|
|
func `profile mode prefers explicit region over the profile region`() async throws {
|
|
let env = [
|
|
BedrockSettingsReader.authModeKey: "profile",
|
|
BedrockSettingsReader.profileKey: "work",
|
|
BedrockSettingsReader.regionKeys[0]: "eu-central-1",
|
|
]
|
|
let resolved = try await BedrockCredentialResolver.resolve(
|
|
environment: env,
|
|
resolveAWSBinary: { _ in "/usr/bin/aws" },
|
|
makeProvider: { _ in self.profileProvider() })
|
|
#expect(resolved.region == "eu-central-1")
|
|
}
|
|
|
|
@Test
|
|
func `profile mode without a profile name throws missingCredentials`() async {
|
|
let env = [BedrockSettingsReader.authModeKey: "profile"]
|
|
await #expect(throws: BedrockUsageError.missingCredentials) {
|
|
try await BedrockCredentialResolver.resolve(
|
|
environment: env,
|
|
resolveAWSBinary: { _ in "/usr/bin/aws" },
|
|
makeProvider: { _ in self.profileProvider() })
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `profile mode preserves source credentials but removes AWS_PROFILE for AWS CLI`() async throws {
|
|
let captured = CapturedEnvironment()
|
|
let env = [
|
|
BedrockSettingsReader.authModeKey: "profile",
|
|
BedrockSettingsReader.profileKey: "work",
|
|
BedrockSettingsReader.accessKeyIDKey: "AKIAINHERITED",
|
|
BedrockSettingsReader.secretAccessKeyKey: "inherited-secret",
|
|
BedrockSettingsReader.sessionTokenKey: "inherited-token",
|
|
]
|
|
_ = try await BedrockCredentialResolver.resolve(
|
|
environment: env,
|
|
resolveAWSBinary: { _ in "/usr/bin/aws" },
|
|
makeProvider: { _ in
|
|
BedrockProfileCredentialProvider(awsBinaryPath: "/usr/bin/aws") { arguments, environment in
|
|
captured.record(environment)
|
|
if arguments.contains("export-credentials") {
|
|
return SubprocessResult(stdout: Self.credentialsJSON, stderr: "")
|
|
}
|
|
return SubprocessResult(stdout: "us-east-1\n", stderr: "")
|
|
}
|
|
})
|
|
let seen = captured.value
|
|
#expect(seen[BedrockSettingsReader.accessKeyIDKey] == "AKIAINHERITED")
|
|
#expect(seen[BedrockSettingsReader.secretAccessKeyKey] == "inherited-secret")
|
|
#expect(seen[BedrockSettingsReader.sessionTokenKey] == "inherited-token")
|
|
#expect(seen[BedrockSettingsReader.profileKey] == nil)
|
|
}
|
|
|
|
@Test
|
|
func `profile mode without the AWS CLI throws awsCLINotFound`() async {
|
|
let env = [
|
|
BedrockSettingsReader.authModeKey: "profile",
|
|
BedrockSettingsReader.profileKey: "work",
|
|
]
|
|
await #expect(throws: BedrockUsageError.awsCLINotFound) {
|
|
try await BedrockCredentialResolver.resolve(
|
|
environment: env,
|
|
resolveAWSBinary: { _ in nil },
|
|
makeProvider: { _ in self.profileProvider() })
|
|
}
|
|
}
|
|
}
|