// // ModelTests.swift // Zyquo Cloud // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // import Foundation import Testing @testable import ZyquoCloud @Suite struct ModelTests { @Test func conversationJSONRoundTrip() throws { // ISO8601 persistence has whole-second precision; use round dates. let date = Date(timeIntervalSince1970: 1_785_400_000) var message = Message(role: .user, text: "Hello", createdAt: date) message.attachments = [ Attachment(kind: .image, fileName: "pic.png", data: Data([1, 2, 3]), mimeType: "image/png") ] var reply = Message(role: .assistant, text: "Hi!", reasoning: "thinking…", createdAt: date) reply.usage = TokenUsage(inputTokens: 10, outputTokens: 5) reply.citations = [Citation(index: 1, url: URL(string: "https://example.com")!, title: "Example")] let conversation = Conversation( title: "Test", messages: [message, reply], modelID: "gpt-4o-mini", provider: .openai, systemPrompt: "Be brief.", createdAt: date, updatedAt: date ) let encoder = JSONEncoder() encoder.dateEncodingStrategy = .iso8601 let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let data = try encoder.encode(conversation) let decoded = try decoder.decode(Conversation.self, from: data) #expect(decoded == conversation) #expect(decoded.totalUsage.totalTokens == 15) } @Test func pricingCost() { let pricing = ModelPricing(inputPerMTok: 2, outputPerMTok: 10) #expect(abs(pricing.cost(inputTokens: 1_000_000, outputTokens: 500_000) - 7.0) < 0.0001) } @Test func contextBadge() { func model(_ ctx: Int) -> AIModel { AIModel( id: "m", provider: .openai, displayName: "M", contextWindow: ctx, maxOutputTokens: nil, capabilities: ModelCapabilities(), pricing: nil, parameterSupport: ParameterSupport() ) } #expect(model(1_000_000).contextBadge == "1M ctx") #expect(model(128_000).contextBadge == "128K ctx") } @Test func providerErrorMapping() { let unauthorized = ProviderError.from(status: 401, body: Data(), provider: .mistral) guard case .invalidAPIKey(let provider) = unauthorized else { Issue.record("expected invalidAPIKey") return } #expect(provider == .mistral) let body = #"{"error":{"message":"model not found"}}"#.data(using: .utf8)! let notFound = ProviderError.from(status: 404, body: body, provider: .openai) guard case .badRequest(_, let message) = notFound else { Issue.record("expected badRequest") return } #expect(message == "model not found") } @Test @MainActor func cheapestModelPrefersNonReasoning() { let catalog = ModelCatalog() for provider in ProviderID.builtIn { guard let cheapest = catalog.cheapestModel(for: provider) else { continue } let plainExists = catalog.models(for: provider) .contains { !$0.isLegacy && !$0.capabilities.reasoning } if plainExists { #expect(!cheapest.capabilities.reasoning, "\(provider): utility calls must not use a reasoning model") } } } @Test func everyBuiltInProviderHasBaseURLAndFormat() { for provider in ProviderID.builtIn { #expect(provider.defaultBaseURL != nil, "\(provider) missing base URL") } #expect(ProviderID.anthropic.wireFormat == .anthropicMessages) #expect(ProviderID.builtIn.count == 12) } }