Files
oai-swift/oAITests/EmbeddingServiceTests.swift
T
rune a053d4a983 Phase 2: tests for the provider layer and other exposed pure helpers
66 new tests across 8 files (93 total in the suite now):

- GitSyncServiceTests: convertToSSH/injectCredentials/sanitizeFilename/
  detectSecretsInText, including a below-threshold false-positive check
  on the secret regex.
- ChatViewModelPureLogicTests: detectGoodbyePhrase, inferProvider,
  calculateCost -- including the cache-read (0.1x) / cache-write (1.25x)
  pricing multipliers, which is real billing-affecting logic.
- EmbeddingServiceTests / ContextSelectionServiceTests: embedding
  (de)serialization round-trip, importance-score weighting, and the
  token-estimate fallback (content.count / 4) when no real count exists.
- OpenRouterProviderTests / OllamaProviderTests / OpenAIProviderTests /
  AnthropicProviderTests: request-building (attachments, online mode,
  cache_control breakpoints, o1/o3 temperature omission, tool schema
  conversion) and response-parsing (text, tool_use blocks, empty-choices
  fallback behavior) for all four providers, with no network involved.

Also marks calculateCost/inferProvider/detectGoodbyePhrase (ChatViewModel)
and serializeEmbedding/deserializeEmbedding (EmbeddingService) as
`nonisolated` -- discovered via the actual test failures, not
speculation: the project's `SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`
setting isolates the classes that carry an explicit `@MainActor`
(ChatViewModel), so calling their static members from a plain
synchronous @Test needs the pure ones marked `nonisolated`. Matches
the existing convention already used elsewhere in EmbeddingService
(cosineSimilarity was already nonisolated before this change).

Phase 2 of the test-suite rollout plan (peaceful-baking-kurzweil).
2026-07-23 14:11:50 +02:00

36 lines
1.1 KiB
Swift

//
// EmbeddingServiceTests.swift
// oAITests
//
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
// Copyright (C) 2026 Rune Olsen
import Testing
@testable import oAI
@Suite("EmbeddingService serialization")
struct EmbeddingServiceTests {
@Test("Serialize then deserialize round-trips an embedding exactly")
func roundTrip() {
let original: [Float] = [0.0, 1.0, -1.0, 3.14159, -0.0001, 1_000_000.5]
let data = EmbeddingService.shared.serializeEmbedding(original)
let restored = EmbeddingService.shared.deserializeEmbedding(data)
#expect(restored == original)
}
@Test("Serialized data is exactly 4 bytes per float")
func serializedSizeIsFourBytesPerFloat() {
let embedding: [Float] = Array(repeating: 0.5, count: 10)
let data = EmbeddingService.shared.serializeEmbedding(embedding)
#expect(data.count == 40)
}
@Test("Empty embedding serializes to empty data and back")
func emptyEmbedding() {
let data = EmbeddingService.shared.serializeEmbedding([])
#expect(data.isEmpty)
#expect(EmbeddingService.shared.deserializeEmbedding(data).isEmpty)
}
}