import Foundationimport SwiftUIimport Testing@testable import CodexBar@testable import CodexBarCore @MainActorstruct DeepgramProviderTests { @Test func `deepgram field kinds and bindings`() throws { let suite = "DeepgramProviderTests-field-kinds" let defaults = try #require(UserDefaults(suiteName: suite)) defaults.removePersistentDomain(forName: suite) let configStore = testConfigStore(suiteName: suite) let settings = SettingsStore( userDefaults: defaults, configStore: configStore, zaiTokenStore: NoopZaiTokenStore(), syntheticTokenStore: NoopSyntheticTokenStore()) let store = UsageStore( fetcher: UsageFetcher(environment: [:]), browserDetection: BrowserDetection(cacheTTL: 0), settings: settings) let context = ProviderSettingsContext( provider: .deepgram, settings: settings, store: store, boolBinding: { keyPath in Binding( get: { settings[keyPath: keyPath] }, set: { settings[keyPath: keyPath] = $0 }) }, stringBinding: { keyPath in Binding( get: { settings[keyPath: keyPath] }, set: { settings[keyPath: keyPath] = $0 }) }, statusText: { _ in nil }, setStatusText: { _, _ in }, lastAppActiveRunAt: { _ in nil }, setLastAppActiveRunAt: { _, _ in }, requestConfirmation: { _ in }, runLoginFlow: {}) let implementation = DeepgramProviderImplementation() let fields = implementation.settingsFields(context: context) let apiField = try #require(fields.first(where: { $0.id == "deepgram-api-key" })) let projectField = try #require(fields.first(where: { $0.id == "deepgram-project-id" })) #expect(apiField.kind == .secure) #expect(projectField.kind == .plain) // Verify bindings update the SettingsStore apiField.binding.wrappedValue = "dg_test_token" #expect(settings.deepgramAPIKey == "dg_test_token") projectField.binding.wrappedValue = "proj-1234" #expect(settings.deepgramProjectID == "proj-1234") } @Test nonisolated func `parses usage breakdown response into visible usage notes`() throws { let body = #""" { "start": "2025-01-16", "end": "2025-01-23", "resolution": { "units": "day", "amount": 1 }, "results": [ { "hours": 1619.7242069444444, "total_hours": 1621.7395791666668, "agent_hours": 41.33564388888889, "tokens_in": 1200, "tokens_out": 340, "tts_characters": 9158866, "requests": 373381, "grouping": { "start": "2025-01-16", "end": "2025-01-16", "endpoint": "listen" } }, { "hours": 2.25, "total_hours": 3.5, "requests": 19, "grouping": { "start": "2025-01-17", "end": "2025-01-17", "endpoint": "speak" } } ] } """# let updatedAt = Date(timeIntervalSince1970: 123) let snapshot = try DeepgramUsageFetcher._parseSnapshotForTesting( Data(body.utf8), projectID: "project-123", updatedAt: updatedAt) let usage = snapshot.toUsageSnapshot() #expect(snapshot.requests == 373_400) #expect(snapshot.hours == 1621.9742069444444) #expect(snapshot.totalHours == 1625.2395791666668) #expect(snapshot.agentHours == 41.33564388888889) #expect(snapshot.tokensIn == 1200) #expect(snapshot.tokensOut == 340) #expect(snapshot.ttsCharacters == 9_158_866) #expect(usage.deepgramUsage?.requests == 373_400) #expect(usage.loginMethod(for: .deepgram) == "Project: project-123") #expect(usage.deepgramUsage?.displayLines == [ "Requests: 373,400", "1,622.0 audio hours · 1,625.2 billable hours", "41.3 agent hours · 1,540 tokens · 9,158,866 TTS chars", "Period: 2025-01-16 to 2025-01-23", ]) } @Test nonisolated func `fetch usage calls breakdown endpoint with token auth`() async throws { let transport = ProviderHTTPTransportStub { request in guard let url = request.url else { throw URLError(.badURL) } #expect(url.path == "/v1/projects/project-123/usage/breakdown") let components = URLComponents(url: url, resolvingAgainstBaseURL: false) #expect(components?.queryItems?.contains(URLQueryItem(name: "start", value: "2025-01-16")) == true) #expect(components?.queryItems?.contains(URLQueryItem(name: "end", value: "2025-01-23")) == true) #expect(request.value(forHTTPHeaderField: "Authorization") == "Token dg-test") #expect(request.value(forHTTPHeaderField: "Accept") == "application/json") #expect(request.timeoutInterval == 15) let body = #""" { "start": "2025-01-16", "end": "2025-01-23", "resolution": { "units": "day", "amount": 1 }, "results": [ { "hours": 1.5, "total_hours": 2, "requests": 7 } ] } """# return Self.makeResponse(url: url, body: body) } let usage = try await DeepgramUsageFetcher.fetchUsage( apiKey: " dg-test ", projectID: " project-123 ", query: DeepgramUsageQuery(start: "2025-01-16", end: "2025-01-23"), environment: ["DEEPGRAM_API_URL": "https://deepgram.test/v1"], transport: transport) #expect(usage.projectID == "project-123") #expect(usage.requests == 7) #expect(usage.hours == 1.5) #expect(usage.totalHours == 2) let requests = await transport.requests() #expect(requests.count == 1) } @Test nonisolated func `fetch usage discovers projects when project id is omitted`() async throws { let transport = ProviderHTTPTransportStub { request in let url = try #require(request.url) #expect(request.value(forHTTPHeaderField: "Authorization") == "Token dg-test") switch url.path { case "/v1/projects": return Self.makeResponse(url: url, body: #""" { "projects": [ { "project_id": "project-a", "name": "Alpha" }, { "project_id": "project-b", "name": "Beta" } ] } """#) case "/v1/projects/project-a/usage/breakdown": return Self.makeResponse(url: url, body: #""" { "start": "2025-01-16", "end": "2025-01-23", "results": [ { "hours": 1, "total_hours": 2, "requests": 3 } ] } """#) case "/v1/projects/project-b/usage/breakdown": return Self.makeResponse(url: url, body: #""" { "start": "2025-01-17", "end": "2025-01-24", "results": [ { "hours": 4, "total_hours": 5, "requests": 6 } ] } """#) default: throw URLError(.badURL) } } let usage = try await DeepgramUsageFetcher.fetchUsage( apiKey: "dg-test", environment: ["DEEPGRAM_API_URL": "https://deepgram.test/v1"], transport: transport) #expect(usage.projectID == "all") #expect(usage.projectCount == 2) #expect(usage.requests == 9) #expect(usage.hours == 5) #expect(usage.totalHours == 7) #expect(usage.start == "2025-01-16") #expect(usage.end == "2025-01-24") #expect(usage.toUsageSnapshot().loginMethod(for: .deepgram) == "2 projects") let requests = await transport.requests() #expect(requests.map { $0.url?.path } == [ "/v1/projects", "/v1/projects/project-a/usage/breakdown", "/v1/projects/project-b/usage/breakdown", ]) } private nonisolated static func makeResponse( url: URL, body: String, statusCode: Int = 200) -> (Data, URLResponse) { let response = HTTPURLResponse( url: url, statusCode: statusCode, httpVersion: "HTTP/1.1", headerFields: ["Content-Type": "application/json"])! return (Data(body.utf8), response) }}
import Foundation
import SwiftUI
import Testing
@testable import CodexBar
@testable import CodexBarCore
@MainActor
struct DeepgramProviderTests {
@Test
func `deepgram field kinds and bindings`() throws {
let suite = "DeepgramProviderTests-field-kinds"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let configStore = testConfigStore(suiteName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: configStore,
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
let store = UsageStore(
fetcher: UsageFetcher(environment: [:]),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
let context = ProviderSettingsContext(
provider: .deepgram,
settings: settings,
store: store,
boolBinding: { keyPath in
Binding(
get: { settings[keyPath: keyPath] },
set: { settings[keyPath: keyPath] = $0 })
},
stringBinding: { keyPath in
Binding(
get: { settings[keyPath: keyPath] },
set: { settings[keyPath: keyPath] = $0 })
},
statusText: { _ in nil },
setStatusText: { _, _ in },
lastAppActiveRunAt: { _ in nil },
setLastAppActiveRunAt: { _, _ in },
requestConfirmation: { _ in },
runLoginFlow: {})
let implementation = DeepgramProviderImplementation()
let fields = implementation.settingsFields(context: context)
let apiField = try #require(fields.first(where: { $0.id == "deepgram-api-key" }))
let projectField = try #require(fields.first(where: { $0.id == "deepgram-project-id" }))
#expect(apiField.kind == .secure)
#expect(projectField.kind == .plain)
// Verify bindings update the SettingsStore
apiField.binding.wrappedValue = "dg_test_token"
#expect(settings.deepgramAPIKey == "dg_test_token")
projectField.binding.wrappedValue = "proj-1234"
#expect(settings.deepgramProjectID == "proj-1234")
}
@Test
nonisolated func `parses usage breakdown response into visible usage notes`() throws {
let body = #"""
{
"start": "2025-01-16",
"end": "2025-01-23",
"resolution": {
"units": "day",
"amount": 1
},
"results": [
{
"hours": 1619.7242069444444,
"total_hours": 1621.7395791666668,
"agent_hours": 41.33564388888889,
"tokens_in": 1200,
"tokens_out": 340,
"tts_characters": 9158866,
"requests": 373381,
"grouping": {
"start": "2025-01-16",
"end": "2025-01-16",
"endpoint": "listen"
}
},
{
"hours": 2.25,
"total_hours": 3.5,
"requests": 19,
"grouping": {
"start": "2025-01-17",
"end": "2025-01-17",
"endpoint": "speak"
}
}
]
}
"""#
let updatedAt = Date(timeIntervalSince1970: 123)
let snapshot = try DeepgramUsageFetcher._parseSnapshotForTesting(
Data(body.utf8),
projectID: "project-123",
updatedAt: updatedAt)
let usage = snapshot.toUsageSnapshot()
#expect(snapshot.requests == 373_400)
#expect(snapshot.hours == 1621.9742069444444)
#expect(snapshot.totalHours == 1625.2395791666668)
#expect(snapshot.agentHours == 41.33564388888889)
#expect(snapshot.tokensIn == 1200)
#expect(snapshot.tokensOut == 340)
#expect(snapshot.ttsCharacters == 9_158_866)
#expect(usage.deepgramUsage?.requests == 373_400)
#expect(usage.loginMethod(for: .deepgram) == "Project: project-123")
#expect(usage.deepgramUsage?.displayLines == [
"Requests: 373,400",
"1,622.0 audio hours · 1,625.2 billable hours",
"41.3 agent hours · 1,540 tokens · 9,158,866 TTS chars",
"Period: 2025-01-16 to 2025-01-23",
])
}
@Test
nonisolated func `fetch usage calls breakdown endpoint with token auth`() async throws {
let transport = ProviderHTTPTransportStub { request in
guard let url = request.url else { throw URLError(.badURL) }
#expect(url.path == "/v1/projects/project-123/usage/breakdown")
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
#expect(components?.queryItems?.contains(URLQueryItem(name: "start", value: "2025-01-16")) == true)
#expect(components?.queryItems?.contains(URLQueryItem(name: "end", value: "2025-01-23")) == true)
#expect(request.value(forHTTPHeaderField: "Authorization") == "Token dg-test")
#expect(request.value(forHTTPHeaderField: "Accept") == "application/json")
#expect(request.timeoutInterval == 15)
let body = #"""
{
"start": "2025-01-16",
"end": "2025-01-23",
"resolution": {
"units": "day",
"amount": 1
},
"results": [
{
"hours": 1.5,
"total_hours": 2,
"requests": 7
}
]
}
"""#
return Self.makeResponse(url: url, body: body)
}
let usage = try await DeepgramUsageFetcher.fetchUsage(
apiKey: " dg-test ",
projectID: " project-123 ",
query: DeepgramUsageQuery(start: "2025-01-16", end: "2025-01-23"),
environment: ["DEEPGRAM_API_URL": "https://deepgram.test/v1"],
transport: transport)
#expect(usage.projectID == "project-123")
#expect(usage.requests == 7)
#expect(usage.hours == 1.5)
#expect(usage.totalHours == 2)
let requests = await transport.requests()
#expect(requests.count == 1)
}
@Test
nonisolated func `fetch usage discovers projects when project id is omitted`() async throws {
let transport = ProviderHTTPTransportStub { request in
let url = try #require(request.url)
#expect(request.value(forHTTPHeaderField: "Authorization") == "Token dg-test")
switch url.path {
case "/v1/projects":
return Self.makeResponse(url: url, body: #"""
{
"projects": [
{ "project_id": "project-a", "name": "Alpha" },
{ "project_id": "project-b", "name": "Beta" }
]
}
"""#)
case "/v1/projects/project-a/usage/breakdown":
return Self.makeResponse(url: url, body: #"""
{
"start": "2025-01-16",
"end": "2025-01-23",
"results": [
{ "hours": 1, "total_hours": 2, "requests": 3 }
]
}
"""#)
case "/v1/projects/project-b/usage/breakdown":
return Self.makeResponse(url: url, body: #"""
{
"start": "2025-01-17",
"end": "2025-01-24",
"results": [
{ "hours": 4, "total_hours": 5, "requests": 6 }
]
}
"""#)
default:
throw URLError(.badURL)
}
}
let usage = try await DeepgramUsageFetcher.fetchUsage(
apiKey: "dg-test",
environment: ["DEEPGRAM_API_URL": "https://deepgram.test/v1"],
transport: transport)
#expect(usage.projectID == "all")
#expect(usage.projectCount == 2)
#expect(usage.requests == 9)
#expect(usage.hours == 5)
#expect(usage.totalHours == 7)
#expect(usage.start == "2025-01-16")
#expect(usage.end == "2025-01-24")
#expect(usage.toUsageSnapshot().loginMethod(for: .deepgram) == "2 projects")
let requests = await transport.requests()
#expect(requests.map { $0.url?.path } == [
"/v1/projects",
"/v1/projects/project-a/usage/breakdown",
"/v1/projects/project-b/usage/breakdown",
])
}
private nonisolated static func makeResponse(
url: URL,
body: String,
statusCode: Int = 200) -> (Data, URLResponse)
{
let response = HTTPURLResponse(
url: url,
statusCode: statusCode,
httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"])!
return (Data(body.utf8), response)
}
}