Files
SwiftDBAI/Tests/SwiftDBAITests/ProviderConfigurationTests.swift
Krishna Kumar b1724fe7ca Initial implementation of SwiftDBAI
Chat with any SQLite database using natural language. Built on
AnyLanguageModel (HuggingFace) for LLM-agnostic provider support
and GRDB for SQLite access.

Core features:
- Auto schema introspection from sqlite_master (zero config)
- NL → SQL generation via any AnyLanguageModel provider
- Three rendering modes: text summary, data table, Swift Charts
- Drop-in DataChatView (SwiftUI) and headless ChatEngine
- Operation allowlist with read-only default
- Mutation policy with per-table control
- ToolExecutionDelegate for destructive operation confirmation
- Multi-turn conversation context
- 352 tests across 24 suites, all passing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 09:30:56 -05:00

326 lines
10 KiB
Swift

// ProviderConfigurationTests.swift
// SwiftDBAI Tests
//
// Tests for ProviderConfiguration verifying all cloud provider configurations
// produce valid LanguageModel instances with correct settings.
import AnyLanguageModel
import Foundation
@testable import SwiftDBAI
import Testing
@Suite("ProviderConfiguration")
struct ProviderConfigurationTests {
// MARK: - OpenAI Configuration
@Test("OpenAI configuration stores provider and model")
func openAIBasicConfiguration() {
let config = ProviderConfiguration.openAI(
apiKey: "sk-test-key-123",
model: "gpt-4o"
)
#expect(config.provider == .openAI)
#expect(config.model == "gpt-4o")
#expect(config.apiKey == "sk-test-key-123")
#expect(config.hasValidAPIKey)
}
@Test("OpenAI configuration produces a valid LanguageModel")
func openAIMakeModel() {
let config = ProviderConfiguration.openAI(
apiKey: "sk-test-key",
model: "gpt-4o-mini"
)
let model = config.makeModel()
#expect(model is OpenAILanguageModel)
}
@Test("OpenAI with custom base URL for compatible services")
func openAICustomBaseURL() {
let customURL = URL(string: "https://my-proxy.example.com/v1/")!
let config = ProviderConfiguration.openAI(
apiKey: "sk-proxy-key",
model: "gpt-4o",
baseURL: customURL
)
#expect(config.baseURL == customURL)
let model = config.makeModel()
#expect(model is OpenAILanguageModel)
}
@Test("OpenAI with Responses API variant")
func openAIResponsesVariant() {
let config = ProviderConfiguration.openAI(
apiKey: "sk-test",
model: "gpt-4o",
variant: .responses
)
#expect(config.openAIVariant == .responses)
let model = config.makeModel()
#expect(model is OpenAILanguageModel)
}
@Test("OpenAI with dynamic key provider captures key by reference")
func openAIDynamicKeyProvider() {
nonisolated(unsafe) var currentKey = "sk-initial"
let config = ProviderConfiguration.openAI(
apiKeyProvider: { currentKey },
model: "gpt-4o"
)
#expect(config.apiKey == "sk-initial")
currentKey = "sk-rotated"
#expect(config.apiKey == "sk-rotated")
}
// MARK: - Anthropic Configuration
@Test("Anthropic configuration stores provider and model")
func anthropicBasicConfiguration() {
let config = ProviderConfiguration.anthropic(
apiKey: "sk-ant-test-key",
model: "claude-sonnet-4-20250514"
)
#expect(config.provider == .anthropic)
#expect(config.model == "claude-sonnet-4-20250514")
#expect(config.apiKey == "sk-ant-test-key")
#expect(config.hasValidAPIKey)
}
@Test("Anthropic configuration produces a valid LanguageModel")
func anthropicMakeModel() {
let config = ProviderConfiguration.anthropic(
apiKey: "sk-ant-test",
model: "claude-sonnet-4-20250514"
)
let model = config.makeModel()
#expect(model is AnthropicLanguageModel)
}
@Test("Anthropic with API version and betas")
func anthropicWithVersionAndBetas() {
let config = ProviderConfiguration.anthropic(
apiKey: "sk-ant-test",
model: "claude-sonnet-4-20250514",
apiVersion: "2024-01-01",
betas: ["computer-use"]
)
#expect(config.apiVersion == "2024-01-01")
#expect(config.betas == ["computer-use"])
let model = config.makeModel()
#expect(model is AnthropicLanguageModel)
}
@Test("Anthropic with dynamic key provider captures key by reference")
func anthropicDynamicKeyProvider() {
nonisolated(unsafe) var currentKey = "sk-ant-initial"
let config = ProviderConfiguration.anthropic(
apiKeyProvider: { currentKey },
model: "claude-sonnet-4-20250514"
)
#expect(config.apiKey == "sk-ant-initial")
currentKey = "sk-ant-rotated"
#expect(config.apiKey == "sk-ant-rotated")
}
// MARK: - Gemini Configuration
@Test("Gemini configuration stores provider and model")
func geminiBasicConfiguration() {
let config = ProviderConfiguration.gemini(
apiKey: "AIzaSyTest123",
model: "gemini-2.0-flash"
)
#expect(config.provider == .gemini)
#expect(config.model == "gemini-2.0-flash")
#expect(config.apiKey == "AIzaSyTest123")
#expect(config.hasValidAPIKey)
}
@Test("Gemini configuration produces a valid LanguageModel")
func geminiMakeModel() {
let config = ProviderConfiguration.gemini(
apiKey: "AIzaSyTest",
model: "gemini-2.0-flash"
)
let model = config.makeModel()
#expect(model is GeminiLanguageModel)
}
@Test("Gemini with custom API version")
func geminiCustomVersion() {
let config = ProviderConfiguration.gemini(
apiKey: "AIzaSyTest",
model: "gemini-2.0-flash",
apiVersion: "v1"
)
#expect(config.apiVersion == "v1")
let model = config.makeModel()
#expect(model is GeminiLanguageModel)
}
@Test("Gemini with dynamic key provider captures key by reference")
func geminiDynamicKeyProvider() {
nonisolated(unsafe) var currentKey = "AIza-initial"
let config = ProviderConfiguration.gemini(
apiKeyProvider: { currentKey },
model: "gemini-2.0-flash"
)
#expect(config.apiKey == "AIza-initial")
currentKey = "AIza-rotated"
#expect(config.apiKey == "AIza-rotated")
}
// MARK: - OpenAI-Compatible Configuration
@Test("OpenAI-compatible configuration with custom base URL")
func openAICompatibleConfiguration() {
let baseURL = URL(string: "https://api.together.xyz/v1/")!
let config = ProviderConfiguration.openAICompatible(
apiKey: "together-key",
model: "meta-llama/Llama-3.1-70B",
baseURL: baseURL
)
#expect(config.provider == .openAICompatible)
#expect(config.model == "meta-llama/Llama-3.1-70B")
#expect(config.baseURL == baseURL)
let model = config.makeModel()
#expect(model is OpenAILanguageModel)
}
@Test("OpenAI-compatible with dynamic key provider")
func openAICompatibleDynamicKey() {
let baseURL = URL(string: "http://localhost:1234/v1/")!
nonisolated(unsafe) var currentKey = "local-key"
let config = ProviderConfiguration.openAICompatible(
apiKeyProvider: { currentKey },
model: "local-model",
baseURL: baseURL
)
#expect(config.apiKey == "local-key")
currentKey = "new-local-key"
#expect(config.apiKey == "new-local-key")
}
// MARK: - API Key Validation
@Test("Empty API key reports invalid")
func emptyAPIKeyInvalid() {
let config = ProviderConfiguration.openAI(
apiKey: "",
model: "gpt-4o"
)
#expect(!config.hasValidAPIKey)
}
@Test("Whitespace-only API key reports invalid")
func whitespaceAPIKeyInvalid() {
let config = ProviderConfiguration.openAI(
apiKey: " \n\t ",
model: "gpt-4o"
)
#expect(!config.hasValidAPIKey)
}
@Test("Non-empty API key reports valid")
func nonEmptyAPIKeyValid() {
let config = ProviderConfiguration.openAI(
apiKey: "x",
model: "gpt-4o"
)
#expect(config.hasValidAPIKey)
}
// MARK: - Environment Variable Configuration
@Test("fromEnvironment creates configuration for each provider")
func fromEnvironmentCreatesConfig() {
let openAI = ProviderConfiguration.fromEnvironment(
provider: .openAI,
environmentVariable: "SWIFTDAI_TEST_OPENAI_KEY",
model: "gpt-4o"
)
#expect(openAI.provider == .openAI)
#expect(openAI.model == "gpt-4o")
let anthropic = ProviderConfiguration.fromEnvironment(
provider: .anthropic,
environmentVariable: "SWIFTDAI_TEST_ANTHROPIC_KEY",
model: "claude-sonnet-4-20250514"
)
#expect(anthropic.provider == .anthropic)
let gemini = ProviderConfiguration.fromEnvironment(
provider: .gemini,
environmentVariable: "SWIFTDAI_TEST_GEMINI_KEY",
model: "gemini-2.0-flash"
)
#expect(gemini.provider == .gemini)
}
@Test("fromEnvironment returns empty key when variable not set")
func fromEnvironmentMissingVariable() {
let config = ProviderConfiguration.fromEnvironment(
provider: .openAI,
environmentVariable: "NONEXISTENT_KEY_VAR_SWIFTDBAI_TEST",
model: "gpt-4o"
)
#expect(!config.hasValidAPIKey)
#expect(config.apiKey == "")
}
// MARK: - Provider Enum
@Test("Provider enum has all expected cases")
func providerCases() {
let cases = ProviderConfiguration.Provider.allCases
#expect(cases.count == 6)
#expect(cases.contains(.openAI))
#expect(cases.contains(.anthropic))
#expect(cases.contains(.gemini))
#expect(cases.contains(.openAICompatible))
#expect(cases.contains(.ollama))
#expect(cases.contains(.llamaCpp))
}
// MARK: - Cross-Provider Model Creation
@Test("All providers produce available models")
func allProvidersCreateAvailableModels() {
let configs: [ProviderConfiguration] = [
.openAI(apiKey: "test", model: "gpt-4o"),
.anthropic(apiKey: "test", model: "claude-sonnet-4-20250514"),
.gemini(apiKey: "test", model: "gemini-2.0-flash"),
.openAICompatible(
apiKey: "test",
model: "local",
baseURL: URL(string: "http://localhost:8080/v1/")!
),
]
for config in configs {
let model = config.makeModel()
#expect(model.isAvailable, "Model for \(config.provider) should be available")
}
}
}