import CodexBarCoreimport Foundationimport Testing@testable import CodexBar struct AzureOpenAIUsageFetcherTests { private func makeContext(environment: [String: String]) -> ProviderFetchContext { let browserDetection = BrowserDetection(cacheTTL: 0) return ProviderFetchContext( runtime: .app, sourceMode: .auto, includeCredits: false, webTimeout: 1, webDebugDumpHTML: false, verbose: false, env: environment, settings: nil, fetcher: UsageFetcher(environment: environment), claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection), browserDetection: browserDetection) } @Test func `settings reader trims env vars and normalizes endpoint`() { let environment = [ AzureOpenAISettingsReader.apiKeyEnvironmentKey: " 'azure-key' ", AzureOpenAISettingsReader.endpointEnvironmentKey: "my-resource.openai.azure.com", AzureOpenAISettingsReader.deploymentNameEnvironmentKey: " \"chat-deployment\" ", ] #expect(AzureOpenAISettingsReader.apiKey(environment: environment) == "azure-key") #expect( AzureOpenAISettingsReader.endpoint(environment: environment)?.absoluteString == "https://my-resource.openai.azure.com") #expect(AzureOpenAISettingsReader.deploymentName(environment: environment) == "chat-deployment") #expect(AzureOpenAISettingsReader.apiVersion(environment: [:]) == AzureOpenAISettingsReader.defaultAPIVersion) } @Test func `missing deployment config returns precise provider error`() async { let descriptor = ProviderDescriptorRegistry.descriptor(for: .azureopenai) let outcome = await descriptor.fetchPlan.fetchOutcome( context: self.makeContext(environment: [ AzureOpenAISettingsReader.apiKeyEnvironmentKey: "azure-key", AzureOpenAISettingsReader.endpointEnvironmentKey: "https://example-resource.openai.azure.com", ]), provider: .azureopenai) guard case let .failure(error) = outcome.result else { Issue.record("Expected missing deployment to fail") return } #expect(error as? AzureOpenAIUsageError == .missingDeploymentName) #expect(error.localizedDescription.contains("deployment not configured")) #expect(outcome.attempts.map(\.wasAvailable) == [true]) } @Test func `fetcher validates deployment with chat completions request`() async throws { let endpoint = try #require(URL(string: "https://example-resource.openai.azure.com")) let updatedAt = Date(timeIntervalSince1970: 1_800_000_000) let transport = ProviderHTTPTransportStub { request in #expect(request.httpMethod == "POST") #expect(request.url?.path == "/openai/deployments/chat-prod/chat/completions") #expect(request.url?.query == "api-version=2024-10-21") #expect(request.value(forHTTPHeaderField: "api-key") == "azure-key") #expect(request.value(forHTTPHeaderField: "Accept") == "application/json") #expect(request.value(forHTTPHeaderField: "Content-Type") == "application/json") let body = try #require(request.httpBody) let json = try #require(JSONSerialization.jsonObject(with: body) as? [String: Any]) #expect(json["max_tokens"] as? Int == 1) #expect(json["temperature"] == nil) let messages = try #require(json["messages"] as? [[String: String]]) #expect(messages.first?["content"] == "ping") let response = try HTTPURLResponse( url: #require(request.url), statusCode: 200, httpVersion: "HTTP/1.1", headerFields: ["Content-Type": "application/json"])! return (Data(#"{"id":"cmpl-1","model":"gpt-4o-mini"}"#.utf8), response) } let snapshot = try await AzureOpenAIUsageFetcher.fetchUsage( apiKey: "azure-key", endpoint: endpoint, deploymentName: "chat-prod", transport: transport, updatedAt: updatedAt) #expect(snapshot.endpointHost == "example-resource.openai.azure.com") #expect(snapshot.deploymentName == "chat-prod") #expect(snapshot.model == "gpt-4o-mini") #expect(snapshot.apiVersion == AzureOpenAISettingsReader.defaultAPIVersion) #expect(snapshot.updatedAt == updatedAt) let usage = snapshot.toUsageSnapshot() #expect(usage.identity?.providerID == .azureopenai) #expect(usage.identity?.accountOrganization == "example-resource.openai.azure.com") #expect(usage.identity?.loginMethod == "Deployment: chat-prod") #expect(usage.primary?.resetDescription == "Deployment: chat-prod · Model: gpt-4o-mini") } @Test func `chat completions URL preserves endpoint path and deployment escaping`() throws { let endpoint = try #require(URL(string: "https://proxy.example.com/base")) let url = try AzureOpenAIUsageFetcher._chatCompletionsURLForTesting( endpoint: endpoint, deploymentName: "chat prod", apiVersion: "2024-10-21") #expect( url.absoluteString == "https://proxy.example.com/base/openai/deployments/chat%20prod/chat/completions?api-version=2024-10-21") } @Test func `chat completions URL does not duplicate openai endpoint suffix`() throws { let endpoint = try #require(URL(string: "https://proxy.example.com/base/openai")) let url = try AzureOpenAIUsageFetcher._chatCompletionsURLForTesting( endpoint: endpoint, deploymentName: "chat-prod", apiVersion: "2024-10-21") #expect( url.absoluteString == "https://proxy.example.com/base/openai/deployments/chat-prod/chat/completions?api-version=2024-10-21") } @Test func `v1 API validates with OpenAI compatible path and model field`() async throws { let endpoint = try #require(URL(string: "https://example-resource.openai.azure.com")) let transport = ProviderHTTPTransportStub { request in #expect(request.httpMethod == "POST") #expect( request.url?.absoluteString == "https://example-resource.openai.azure.com/openai/v1/chat/completions") #expect(request.value(forHTTPHeaderField: "api-key") == "azure-key") let body = try #require(request.httpBody) let json = try #require(JSONSerialization.jsonObject(with: body) as? [String: Any]) #expect(json["model"] as? String == "chat-prod") #expect(json["max_completion_tokens"] as? Int == 1) #expect(json["max_tokens"] == nil) #expect(json["temperature"] == nil) let response = try HTTPURLResponse( url: #require(request.url), statusCode: 200, httpVersion: "HTTP/1.1", headerFields: ["Content-Type": "application/json"])! return (Data(#"{"id":"cmpl-1","model":"gpt-4o-mini"}"#.utf8), response) } let snapshot = try await AzureOpenAIUsageFetcher.fetchUsage( apiKey: "azure-key", endpoint: endpoint, deploymentName: "chat-prod", apiVersion: "v1", transport: transport) #expect(snapshot.apiVersion == "v1") #expect(snapshot.deploymentName == "chat-prod") #expect(snapshot.model == "gpt-4o-mini") } @Test func `v1 API accepts documented openai v1 base URL`() throws { let endpoint = try #require(URL(string: "https://example-resource.openai.azure.com/openai/v1")) let url = try AzureOpenAIUsageFetcher._chatCompletionsURLForTesting( endpoint: endpoint, deploymentName: "chat-prod", apiVersion: "v1") #expect( url.absoluteString == "https://example-resource.openai.azure.com/openai/v1/chat/completions") }} @MainActorstruct AzureOpenAIMenuDescriptorTests { @Test func `azure openai deployment detail appears in menu`() throws { let suite = "AzureOpenAIMenuDescriptorTests-menu" let defaults = try #require(UserDefaults(suiteName: suite)) defaults.removePersistentDomain(forName: suite) let settings = SettingsStore( userDefaults: defaults, configStore: testConfigStore(suiteName: suite), zaiTokenStore: NoopZaiTokenStore(), syntheticTokenStore: NoopSyntheticTokenStore()) settings.statusChecksEnabled = false let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings) let snapshot = AzureOpenAIUsageSnapshot( endpointHost: "example-resource.openai.azure.com", deploymentName: "chat-prod", model: "gpt-4o-mini", apiVersion: "2024-10-21", updatedAt: Date(timeIntervalSince1970: 1_800_000_000)) store._setSnapshotForTesting(snapshot.toUsageSnapshot(), provider: .azureopenai) let descriptor = MenuDescriptor.build( provider: .azureopenai, store: store, settings: settings, account: AccountInfo(email: nil, plan: nil), updateReady: false, includeContextualActions: false) let lines = descriptor.sections .flatMap(\.entries) .compactMap { entry -> String? in guard case let .text(text, _) = entry else { return nil } return text } #expect(lines.contains("Azure OpenAI")) #expect(lines.contains("Deployment: chat-prod · Model: gpt-4o-mini")) }}
import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
struct AzureOpenAIUsageFetcherTests {
private func makeContext(environment: [String: String]) -> ProviderFetchContext {
let browserDetection = BrowserDetection(cacheTTL: 0)
return ProviderFetchContext(
runtime: .app,
sourceMode: .auto,
includeCredits: false,
webTimeout: 1,
webDebugDumpHTML: false,
verbose: false,
env: environment,
settings: nil,
fetcher: UsageFetcher(environment: environment),
claudeFetcher: ClaudeUsageFetcher(browserDetection: browserDetection),
browserDetection: browserDetection)
}
@Test
func `settings reader trims env vars and normalizes endpoint`() {
let environment = [
AzureOpenAISettingsReader.apiKeyEnvironmentKey: " 'azure-key' ",
AzureOpenAISettingsReader.endpointEnvironmentKey: "my-resource.openai.azure.com",
AzureOpenAISettingsReader.deploymentNameEnvironmentKey: " \"chat-deployment\" ",
]
#expect(AzureOpenAISettingsReader.apiKey(environment: environment) == "azure-key")
#expect(
AzureOpenAISettingsReader.endpoint(environment: environment)?.absoluteString ==
"https://my-resource.openai.azure.com")
#expect(AzureOpenAISettingsReader.deploymentName(environment: environment) == "chat-deployment")
#expect(AzureOpenAISettingsReader.apiVersion(environment: [:]) == AzureOpenAISettingsReader.defaultAPIVersion)
}
@Test
func `missing deployment config returns precise provider error`() async {
let descriptor = ProviderDescriptorRegistry.descriptor(for: .azureopenai)
let outcome = await descriptor.fetchPlan.fetchOutcome(
context: self.makeContext(environment: [
AzureOpenAISettingsReader.apiKeyEnvironmentKey: "azure-key",
AzureOpenAISettingsReader.endpointEnvironmentKey: "https://example-resource.openai.azure.com",
]),
provider: .azureopenai)
guard case let .failure(error) = outcome.result else {
Issue.record("Expected missing deployment to fail")
return
}
#expect(error as? AzureOpenAIUsageError == .missingDeploymentName)
#expect(error.localizedDescription.contains("deployment not configured"))
#expect(outcome.attempts.map(\.wasAvailable) == [true])
}
@Test
func `fetcher validates deployment with chat completions request`() async throws {
let endpoint = try #require(URL(string: "https://example-resource.openai.azure.com"))
let updatedAt = Date(timeIntervalSince1970: 1_800_000_000)
let transport = ProviderHTTPTransportStub { request in
#expect(request.httpMethod == "POST")
#expect(request.url?.path == "/openai/deployments/chat-prod/chat/completions")
#expect(request.url?.query == "api-version=2024-10-21")
#expect(request.value(forHTTPHeaderField: "api-key") == "azure-key")
#expect(request.value(forHTTPHeaderField: "Accept") == "application/json")
#expect(request.value(forHTTPHeaderField: "Content-Type") == "application/json")
let body = try #require(request.httpBody)
let json = try #require(JSONSerialization.jsonObject(with: body) as? [String: Any])
#expect(json["max_tokens"] as? Int == 1)
#expect(json["temperature"] == nil)
let messages = try #require(json["messages"] as? [[String: String]])
#expect(messages.first?["content"] == "ping")
let response = try HTTPURLResponse(
url: #require(request.url),
statusCode: 200,
httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (Data(#"{"id":"cmpl-1","model":"gpt-4o-mini"}"#.utf8), response)
}
let snapshot = try await AzureOpenAIUsageFetcher.fetchUsage(
apiKey: "azure-key",
endpoint: endpoint,
deploymentName: "chat-prod",
transport: transport,
updatedAt: updatedAt)
#expect(snapshot.endpointHost == "example-resource.openai.azure.com")
#expect(snapshot.deploymentName == "chat-prod")
#expect(snapshot.model == "gpt-4o-mini")
#expect(snapshot.apiVersion == AzureOpenAISettingsReader.defaultAPIVersion)
#expect(snapshot.updatedAt == updatedAt)
let usage = snapshot.toUsageSnapshot()
#expect(usage.identity?.providerID == .azureopenai)
#expect(usage.identity?.accountOrganization == "example-resource.openai.azure.com")
#expect(usage.identity?.loginMethod == "Deployment: chat-prod")
#expect(usage.primary?.resetDescription == "Deployment: chat-prod · Model: gpt-4o-mini")
}
@Test
func `chat completions URL preserves endpoint path and deployment escaping`() throws {
let endpoint = try #require(URL(string: "https://proxy.example.com/base"))
let url = try AzureOpenAIUsageFetcher._chatCompletionsURLForTesting(
endpoint: endpoint,
deploymentName: "chat prod",
apiVersion: "2024-10-21")
#expect(
url.absoluteString ==
"https://proxy.example.com/base/openai/deployments/chat%20prod/chat/completions?api-version=2024-10-21")
}
@Test
func `chat completions URL does not duplicate openai endpoint suffix`() throws {
let endpoint = try #require(URL(string: "https://proxy.example.com/base/openai"))
let url = try AzureOpenAIUsageFetcher._chatCompletionsURLForTesting(
endpoint: endpoint,
deploymentName: "chat-prod",
apiVersion: "2024-10-21")
#expect(
url.absoluteString ==
"https://proxy.example.com/base/openai/deployments/chat-prod/chat/completions?api-version=2024-10-21")
}
@Test
func `v1 API validates with OpenAI compatible path and model field`() async throws {
let endpoint = try #require(URL(string: "https://example-resource.openai.azure.com"))
let transport = ProviderHTTPTransportStub { request in
#expect(request.httpMethod == "POST")
#expect(
request.url?.absoluteString ==
"https://example-resource.openai.azure.com/openai/v1/chat/completions")
#expect(request.value(forHTTPHeaderField: "api-key") == "azure-key")
let body = try #require(request.httpBody)
let json = try #require(JSONSerialization.jsonObject(with: body) as? [String: Any])
#expect(json["model"] as? String == "chat-prod")
#expect(json["max_completion_tokens"] as? Int == 1)
#expect(json["max_tokens"] == nil)
#expect(json["temperature"] == nil)
let response = try HTTPURLResponse(
url: #require(request.url),
statusCode: 200,
httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (Data(#"{"id":"cmpl-1","model":"gpt-4o-mini"}"#.utf8), response)
}
let snapshot = try await AzureOpenAIUsageFetcher.fetchUsage(
apiKey: "azure-key",
endpoint: endpoint,
deploymentName: "chat-prod",
apiVersion: "v1",
transport: transport)
#expect(snapshot.apiVersion == "v1")
#expect(snapshot.deploymentName == "chat-prod")
#expect(snapshot.model == "gpt-4o-mini")
}
@Test
func `v1 API accepts documented openai v1 base URL`() throws {
let endpoint = try #require(URL(string: "https://example-resource.openai.azure.com/openai/v1"))
let url = try AzureOpenAIUsageFetcher._chatCompletionsURLForTesting(
endpoint: endpoint,
deploymentName: "chat-prod",
apiVersion: "v1")
#expect(
url.absoluteString ==
"https://example-resource.openai.azure.com/openai/v1/chat/completions")
}
}
@MainActor
struct AzureOpenAIMenuDescriptorTests {
@Test
func `azure openai deployment detail appears in menu`() throws {
let suite = "AzureOpenAIMenuDescriptorTests-menu"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let snapshot = AzureOpenAIUsageSnapshot(
endpointHost: "example-resource.openai.azure.com",
deploymentName: "chat-prod",
model: "gpt-4o-mini",
apiVersion: "2024-10-21",
updatedAt: Date(timeIntervalSince1970: 1_800_000_000))
store._setSnapshotForTesting(snapshot.toUsageSnapshot(), provider: .azureopenai)
let descriptor = MenuDescriptor.build(
provider: .azureopenai,
store: store,
settings: settings,
account: AccountInfo(email: nil, plan: nil),
updateReady: false,
includeContextualActions: false)
let lines = descriptor.sections
.flatMap(\.entries)
.compactMap { entry -> String? in
guard case let .text(text, _) = entry else { return nil }
return text
}
#expect(lines.contains("Azure OpenAI"))
#expect(lines.contains("Deployment: chat-prod · Model: gpt-4o-mini"))
}
}