Skip to main content
Glama
ConfigCommandTests.swift10.5 kB
import Foundation import PeekabooCore import Testing @testable import PeekabooCLI @Suite("ConfigCommand Tests", .tags(.safe)) struct ConfigCommandTests { // MARK: - Helpers private func makeRuntime(json: Bool = false) -> CommandRuntime { CommandRuntime( configuration: .init(verbose: false, jsonOutput: json, logLevel: nil), services: PeekabooServices() ) } private func withTempConfigDir(_ body: @escaping (URL) async throws -> Void) async throws { let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent( UUID().uuidString, isDirectory: true ) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) setenv("PEEKABOO_CONFIG_DIR", tempDir.path, 1) setenv("PEEKABOO_CONFIG_NONINTERACTIVE", "1", 1) setenv("PEEKABOO_CONFIG_DISABLE_MIGRATION", "1", 1) #if DEBUG PeekabooCore.ConfigurationManager.shared.resetForTesting() #endif defer { unsetenv("PEEKABOO_CONFIG_DIR") unsetenv("PEEKABOO_CONFIG_NONINTERACTIVE") unsetenv("PEEKABOO_CONFIG_DISABLE_MIGRATION") #if DEBUG PeekabooCore.ConfigurationManager.shared.resetForTesting() #endif try? FileManager.default.removeItem(at: tempDir) } try await body(tempDir) } @Test("ConfigCommand exists and has correct subcommands") func configCommandStructure() { // Verify the command exists let command = ConfigCommand.self // Check command configuration #expect(command.commandDescription.commandName == "config") #expect(command.commandDescription.abstract == "Manage Peekaboo configuration") // Check subcommands let subcommands = command.commandDescription.subcommands #expect(subcommands.count == 12) let hasInit = subcommands.contains { $0 == ConfigCommand.InitCommand.self } #expect(hasInit) let hasAdd = subcommands.contains { $0 == ConfigCommand.AddCommand.self } #expect(hasAdd) let hasShow = subcommands.contains { $0 == ConfigCommand.ShowCommand.self } #expect(hasShow) let hasEdit = subcommands.contains { $0 == ConfigCommand.EditCommand.self } #expect(hasEdit) let hasValidate = subcommands.contains { $0 == ConfigCommand.ValidateCommand.self } #expect(hasValidate) let hasLogin = subcommands.contains { $0 == ConfigCommand.LoginCommand.self } #expect(hasLogin) let hasSetCredential = subcommands.contains { $0 == ConfigCommand.SetCredentialCommand.self } #expect(hasSetCredential) let hasAddProvider = subcommands.contains { $0 == ConfigCommand.AddProviderCommand.self } #expect(hasAddProvider) let hasListProviders = subcommands.contains { $0 == ConfigCommand.ListProvidersCommand.self } #expect(hasListProviders) let hasTestProvider = subcommands.contains { $0 == ConfigCommand.TestProviderCommand.self } #expect(hasTestProvider) let hasRemoveProvider = subcommands.contains { $0 == ConfigCommand.RemoveProviderCommand.self } #expect(hasRemoveProvider) let hasModelsProvider = subcommands.contains { $0 == ConfigCommand.ModelsProviderCommand.self } #expect(hasModelsProvider) } @Test("InitCommand has correct configuration") func initCommand() { let command = ConfigCommand.InitCommand.self #expect(command.commandDescription.commandName == "init") #expect(command.commandDescription.abstract == "Create a default configuration file") } @Test("ShowCommand has correct configuration") func showCommand() { let command = ConfigCommand.ShowCommand.self #expect(command.commandDescription.commandName == "show") #expect(command.commandDescription.abstract == "Display current configuration") } @Test("EditCommand has correct configuration") func editCommand() { let command = ConfigCommand.EditCommand.self #expect(command.commandDescription.commandName == "edit") #expect(command.commandDescription.abstract == "Open configuration file in your default editor") } @Test("ValidateCommand has correct configuration") func validateCommand() { let command = ConfigCommand.ValidateCommand.self #expect(command.commandDescription.commandName == "validate") #expect(command.commandDescription.abstract == "Validate configuration file syntax") } @Test("SetCredentialCommand has correct configuration") func setCredentialCommand() { let command = ConfigCommand.SetCredentialCommand.self #expect(command.commandDescription.commandName == "set-credential") #expect(command.commandDescription.abstract == "Set an API key or credential securely") } @Test("AddProviderCommand validates provider IDs") func providerIdValidation() { #expect(ConfigCommand.AddProviderCommand.isValidProviderId("openrouter")) #expect(ConfigCommand.AddProviderCommand.isValidProviderId("acme-123")) #expect(!ConfigCommand.AddProviderCommand.isValidProviderId("spaces not-allowed")) #expect(!ConfigCommand.AddProviderCommand.isValidProviderId("🥸")) } @Test("AddProviderCommand parses headers and rejects invalid formats") func headerParsing() throws { let parsed = try ConfigCommand.AddProviderCommand.parseHeaders("X-Key:one,Auth: Bearer") #expect(parsed?["x-key"] == "one") #expect(parsed?["auth"] == "Bearer") #expect(throws: ConfigCommand.AddProviderCommand.HeaderParseError.self) { _ = try ConfigCommand.AddProviderCommand.parseHeaders("missingColon") } } @Test("Init command creates config file at overridden path", .serialized) func initCreatesConfig() async throws { try await self.withTempConfigDir { dir in var command = ConfigCommand.InitCommand() try await command.run(using: self.makeRuntime()) let configPath = dir.appendingPathComponent("config.json") #expect(FileManager.default.fileExists(atPath: configPath.path)) } } @Test("Add provider dry-run does not write", .serialized) func addProviderDryRun() async throws { try await self.withTempConfigDir { dir in var command = ConfigCommand.AddProviderCommand() command.providerId = "openrouter" command.type = "openai" command.name = "OpenRouter" command.baseUrl = "https://openrouter.ai/api/v1" command.apiKey = "{env:OPENROUTER_API_KEY}" command.dryRun = true try await command.run(using: self.makeRuntime()) let configPath = dir.appendingPathComponent("config.json") #expect(!FileManager.default.fileExists(atPath: configPath.path)) } } @Test("Add provider rejects invalid URL", .serialized) func addProviderInvalidURL() async throws { try await self.withTempConfigDir { _ in var command = ConfigCommand.AddProviderCommand() command.providerId = "bad" command.type = "openai" command.name = "Bad" command.baseUrl = "localhost" command.apiKey = "{env:BAD_API_KEY}" await #expect(throws: (any Error).self) { try await command.run(using: self.makeRuntime()) } } } @Test("Remove provider dry-run leaves config intact", .serialized) func removeProviderDryRun() async throws { try await self.withTempConfigDir { _ in var add = ConfigCommand.AddProviderCommand() add.providerId = "keep" add.type = "openai" add.name = "Keep" add.baseUrl = "https://api.keep/v1" add.apiKey = "{env:KEEP_API_KEY}" try await add.run(using: self.makeRuntime()) var remove = ConfigCommand.RemoveProviderCommand() remove.providerId = "keep" remove.dryRun = true try await remove.run(using: self.makeRuntime()) let providersAfter = PeekabooCore.ConfigurationManager.shared.listCustomProviders() #expect(providersAfter["keep"] != nil) } } @Test("Validate command fails on malformed config", .serialized) func validateMalformedConfig() async throws { try await self.withTempConfigDir { dir in let badConfig = dir.appendingPathComponent("config.json") try "{ invalid json".write(to: badConfig, atomically: true, encoding: .utf8) var command = ConfigCommand.ValidateCommand() await #expect(throws: (any Error).self) { try await command.run(using: self.makeRuntime()) } } } @Test("Add/remove provider persists to config", .serialized) func addAndRemoveProvider() async throws { try await self.withTempConfigDir { _ in var add = ConfigCommand.AddProviderCommand() add.providerId = "local" add.type = "openai" add.name = "Local" add.baseUrl = "https://api.local/v1" add.apiKey = "{env:LOCAL_API_KEY}" try await add.run(using: self.makeRuntime()) let providersAfterAdd = PeekabooCore.ConfigurationManager.shared.listCustomProviders() #expect(providersAfterAdd["local"] != nil) #expect(providersAfterAdd["local"]?.options.baseURL == "https://api.local/v1") var remove = ConfigCommand.RemoveProviderCommand() remove.providerId = "local" remove.force = true try await remove.run(using: self.makeRuntime()) let providersAfterRemove = PeekabooCore.ConfigurationManager.shared.listCustomProviders() #expect(providersAfterRemove["local"] == nil) } } @Test("Edit print-path leaves filesystem untouched", .serialized) func editPrintPath() async throws { try await self.withTempConfigDir { dir in let configPath = dir.appendingPathComponent("config.json").path var command = ConfigCommand.EditCommand() command.printPath = true try await command.run(using: self.makeRuntime()) #expect(!FileManager.default.fileExists(atPath: configPath)) } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steipete/Peekaboo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server