This commit is contained in:
308
Tests/PlaybookClientTests/PlaybookClientTests.swift
Normal file
308
Tests/PlaybookClientTests/PlaybookClientTests.swift
Normal file
@@ -0,0 +1,308 @@
|
||||
import CodersClient
|
||||
@_spi(Internal) import CommandClient
|
||||
@_spi(Internal) import ConfigurationClient
|
||||
import Dependencies
|
||||
import FileClient
|
||||
import Foundation
|
||||
@_spi(Internal) import PlaybookClient
|
||||
import ShellClient
|
||||
import Testing
|
||||
import TestSupport
|
||||
|
||||
@Suite("PlaybookClientTests")
|
||||
struct PlaybookClientTests: TestCase {
|
||||
|
||||
static var sharedRunOptions: PlaybookClient.RunPlaybook.SharedRunOptions {
|
||||
.init(loggingOptions: loggingOptions)
|
||||
}
|
||||
|
||||
static let defaultPlaybookPath = "~/.local/share/hpa/playbook/main.yml"
|
||||
static let defaultInventoryPath = "~/.local/share/hpa/playbook/inventory.ini"
|
||||
static let mockVaultArg = Configuration.mock.vault.args![0]
|
||||
|
||||
@Test(.tags(.repository))
|
||||
func repositoryInstallation() async throws {
|
||||
try await withTestLogger(key: "repositoryInstallation") {
|
||||
$0.fileClient = .liveValue
|
||||
$0.asyncShellClient = .liveValue
|
||||
$0.commandClient = .liveValue
|
||||
} operation: {
|
||||
try await withTemporaryDirectory { tempDirectory in
|
||||
@Dependency(\.fileClient) var fileClient
|
||||
@Dependency(\.logger) var logger
|
||||
let pathUrl = tempDirectory.appending(path: "playbook")
|
||||
let playbookClient = PlaybookClient.liveValue
|
||||
|
||||
let configuration = Configuration(playbook: .init(directory: pathUrl.cleanFilePath))
|
||||
|
||||
try? FileManager.default.removeItem(at: pathUrl)
|
||||
try await playbookClient.repository.install(configuration)
|
||||
logger.debug("Done cloning playbook")
|
||||
let exists = try await fileClient.isDirectory(pathUrl)
|
||||
#expect(exists)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(
|
||||
.tags(.repository),
|
||||
arguments: [
|
||||
(Configuration(), PlaybookClient.Constants.defaultInstallationPath),
|
||||
(Configuration(playbook: .init(directory: "playbook")), "playbook")
|
||||
]
|
||||
)
|
||||
func repositoryDirectory(configuration: Configuration, expected: String) async throws {
|
||||
let client = PlaybookClient.liveValue
|
||||
let result = try await client.repository.directory(configuration)
|
||||
#expect(result == expected)
|
||||
}
|
||||
|
||||
@Test(.tags(.run))
|
||||
func runBuildProject() async throws {
|
||||
let captured = CommandClient.CapturingClient()
|
||||
|
||||
try await withMockConfiguration(captured, key: "runBuildProject") {
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
try await playbookClient.run.buildProject(.init(projectDirectory: "/foo", shared: Self.sharedRunOptions))
|
||||
|
||||
let arguments = await captured.options!.arguments
|
||||
print(arguments)
|
||||
|
||||
#expect(arguments == [
|
||||
"ansible-playbook", Self.defaultPlaybookPath,
|
||||
"--inventory", Self.defaultInventoryPath,
|
||||
Self.mockVaultArg,
|
||||
"--tags", "build-project",
|
||||
"--extra-vars", "project_dir=/foo"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@Test(
|
||||
.tags(.run),
|
||||
arguments: [
|
||||
(true, "\'{\"template\":{\"path\":\"\(Configuration.mock.template.directory!)\"}}\'"),
|
||||
(false, "\'{\"template\":{\"repo\":{\"url\":\"\(Configuration.mock.template.url!)\",\"version\":\"\(Configuration.mock.template.version!)\"}}}\'")
|
||||
]
|
||||
)
|
||||
func runCreateProject(useLocalTemplateDirectory: Bool, json: String) async throws {
|
||||
let captured = CommandClient.CapturingClient()
|
||||
|
||||
try await withMockConfiguration(captured, key: "runBuildProject") {
|
||||
@Dependency(\.logger) var logger
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
try await playbookClient.run.createProject(
|
||||
.init(
|
||||
projectDirectory: "/project",
|
||||
shared: Self.sharedRunOptions,
|
||||
useLocalTemplateDirectory: useLocalTemplateDirectory
|
||||
)
|
||||
)
|
||||
|
||||
let arguments = await captured.options!.arguments
|
||||
logger.debug("\(arguments)")
|
||||
|
||||
#expect(arguments == [
|
||||
"ansible-playbook", Self.defaultPlaybookPath,
|
||||
"--inventory", Self.defaultInventoryPath,
|
||||
Self.mockVaultArg,
|
||||
"--tags", "setup-project",
|
||||
"--extra-vars", "project_dir=/project",
|
||||
"--extra-vars", json
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: CreateJsonTestOption.testCases)
|
||||
func createJson(input: CreateJsonTestOption) {
|
||||
withTestLogger(key: "generateJson") {
|
||||
$0.coders.jsonEncoder = { jsonEncoder }
|
||||
$0.configurationClient = .mock(input.configuration)
|
||||
} operation: {
|
||||
@Dependency(\.coders) var coders
|
||||
|
||||
let jsonData = try? input.options.createJSONData(
|
||||
configuration: input.configuration,
|
||||
encoder: coders.jsonEncoder()
|
||||
)
|
||||
|
||||
switch input.expectation {
|
||||
case let .success(expected):
|
||||
let json = String(data: jsonData!, encoding: .utf8)!
|
||||
if json != expected {
|
||||
print("json:", json)
|
||||
print("expected:", expected)
|
||||
}
|
||||
#expect(json == expected)
|
||||
case .failure:
|
||||
#expect(jsonData == nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func generateTemplate() async throws {
|
||||
try await withCapturingCommandClient("generateTemplate") {
|
||||
$0.configurationClient = .mock()
|
||||
$0.playbookClient = .liveValue
|
||||
} run: {
|
||||
@Dependency(\.playbookClient) var playbookClient
|
||||
|
||||
let output = try await playbookClient.run.generateTemplate(.init(
|
||||
shared: Self.sharedRunOptions,
|
||||
templateDirectory: "/template"
|
||||
))
|
||||
|
||||
#expect(output == "/template")
|
||||
|
||||
} assert: { output in
|
||||
|
||||
let expected = [
|
||||
"ansible-playbook", Self.defaultPlaybookPath,
|
||||
"--inventory", Self.defaultInventoryPath,
|
||||
Self.mockVaultArg,
|
||||
"--tags", "repo-template",
|
||||
"--extra-vars", "output_dir=/template"
|
||||
]
|
||||
|
||||
#expect(output.arguments == expected)
|
||||
}
|
||||
}
|
||||
|
||||
func withMockConfiguration(
|
||||
_ capturing: CommandClient.CapturingClient,
|
||||
configuration: Configuration = .mock,
|
||||
key: String,
|
||||
logLevel: Logger.Level = .trace,
|
||||
depednencies setupDependencies: @escaping (inout DependencyValues) -> Void = { _ in },
|
||||
operation: @Sendable @escaping () async throws -> Void
|
||||
) async rethrows {
|
||||
try await withDependencies {
|
||||
$0.configurationClient = .mock(configuration)
|
||||
$0.commandClient = .capturing(capturing)
|
||||
$0.playbookClient = .liveValue
|
||||
setupDependencies(&$0)
|
||||
} operation: {
|
||||
try await operation()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct CreateJsonTestOption: Sendable {
|
||||
let options: PlaybookClient.RunPlaybook.CreateOptions
|
||||
let configuration: Configuration
|
||||
let expectation: Result<String, TestError>
|
||||
|
||||
static let testCases: [Self] = [
|
||||
CreateJsonTestOption(
|
||||
options: .init(
|
||||
projectDirectory: "/project",
|
||||
shared: PlaybookClientTests.sharedRunOptions,
|
||||
template: .init(url: nil, version: nil, directory: nil),
|
||||
useLocalTemplateDirectory: true
|
||||
),
|
||||
configuration: .init(),
|
||||
expectation: .failing
|
||||
),
|
||||
CreateJsonTestOption(
|
||||
options: .init(
|
||||
projectDirectory: "/project",
|
||||
shared: PlaybookClientTests.sharedRunOptions,
|
||||
template: .init(url: nil, version: nil, directory: nil),
|
||||
useLocalTemplateDirectory: false
|
||||
),
|
||||
configuration: .init(),
|
||||
expectation: .failing
|
||||
),
|
||||
CreateJsonTestOption(
|
||||
options: .init(
|
||||
projectDirectory: "/project",
|
||||
shared: PlaybookClientTests.sharedRunOptions,
|
||||
template: .init(url: nil, version: nil, directory: "/template"),
|
||||
useLocalTemplateDirectory: true
|
||||
),
|
||||
configuration: .init(template: .init(directory: "/template")),
|
||||
expectation: .success("""
|
||||
{
|
||||
"template" : {
|
||||
"path" : "/template"
|
||||
}
|
||||
}
|
||||
""")
|
||||
),
|
||||
CreateJsonTestOption(
|
||||
options: .init(
|
||||
projectDirectory: "/project",
|
||||
shared: PlaybookClientTests.sharedRunOptions,
|
||||
template: .init(url: nil, version: nil, directory: "/template"),
|
||||
useLocalTemplateDirectory: true
|
||||
),
|
||||
configuration: .init(template: .init(directory: "/template")),
|
||||
expectation: .success("""
|
||||
{
|
||||
"template" : {
|
||||
"path" : "/template"
|
||||
}
|
||||
}
|
||||
""")
|
||||
),
|
||||
CreateJsonTestOption(
|
||||
options: .init(
|
||||
projectDirectory: "/project",
|
||||
shared: PlaybookClientTests.sharedRunOptions,
|
||||
template: .init(url: "https://git.example.com/template.git", version: "main", directory: nil),
|
||||
useLocalTemplateDirectory: false
|
||||
),
|
||||
configuration: .init(),
|
||||
expectation: .success("""
|
||||
{
|
||||
"template" : {
|
||||
"repo" : {
|
||||
"url" : "https://git.example.com/template.git",
|
||||
"version" : "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
),
|
||||
CreateJsonTestOption(
|
||||
options: .init(
|
||||
projectDirectory: "/project",
|
||||
shared: PlaybookClientTests.sharedRunOptions,
|
||||
template: .init(url: "https://git.example.com/template.git", version: "v0.1.0", directory: nil),
|
||||
useLocalTemplateDirectory: false
|
||||
),
|
||||
configuration: .init(template: .init(url: "https://git.example.com/template.git", version: "v0.1.0")),
|
||||
expectation: .success("""
|
||||
{
|
||||
"template" : {
|
||||
"repo" : {
|
||||
"url" : "https://git.example.com/template.git",
|
||||
"version" : "v0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
extension Result where Failure == TestError {
|
||||
static var failing: Self { .failure(TestError()) }
|
||||
}
|
||||
|
||||
struct TestError: Error {}
|
||||
|
||||
extension Tag {
|
||||
@Tag static var repository: Self
|
||||
@Tag static var run: Self
|
||||
}
|
||||
|
||||
let jsonEncoder: JSONEncoder = {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes, .sortedKeys]
|
||||
return encoder
|
||||
}()
|
||||
Reference in New Issue
Block a user