f512d94c74
git-subtree-dir: clients/ds-app git-subtree-mainline:a24e3e6f22git-subtree-split:5206cf3b0c
40 lines
1.7 KiB
Swift
40 lines
1.7 KiB
Swift
import XCTest
|
|
@testable import AIFabric
|
|
|
|
final class SpecializedProviderTests: XCTestCase {
|
|
|
|
func testScaffoldUnavailableAndNotImplemented() async throws {
|
|
let p = SpecializedProvider()
|
|
let available = await p.isAvailable
|
|
XCTAssertFalse(available)
|
|
do {
|
|
_ = try await p.complete(AICompletionRequest(task: .vision, prompt: "p"))
|
|
XCTFail("scaffold must throw notImplemented")
|
|
} catch let AIProviderError.notImplemented(id) {
|
|
XCTAssertEqual(id, .specialized)
|
|
}
|
|
}
|
|
|
|
/// .vision 체인 [.specialized, .onDevice] — specialized 등록·불가 → onDevice 로 **가시** 폴백(라우터 log).
|
|
/// (onDevice 는 providerUsed=id 를 정직히 반환하는 CountingProvider 사용 — MockAIProvider 는 vision 케이스에서
|
|
/// providerUsed 를 .specialized 로 하드코딩하므로 부적합.)
|
|
func testVisionFallsBackToOnDeviceVisibly() async throws {
|
|
let sink = LogSink()
|
|
let onDevice = CountingProvider(id: .onDevice, available: true)
|
|
let router = AIRouter(
|
|
providers: [
|
|
.specialized: SpecializedProvider(),
|
|
.onDevice: onDevice,
|
|
],
|
|
log: { sink.append($0) }
|
|
)
|
|
let resp = try await router.route(AICompletionRequest(task: .vision, prompt: "도면 보기"))
|
|
XCTAssertEqual(resp.providerUsed, .onDevice)
|
|
XCTAssertEqual(resp.routingNote, "fallback from specialized → onDevice")
|
|
let calls = await onDevice.completeCalls
|
|
XCTAssertEqual(calls, 1)
|
|
XCTAssertTrue(sink.lines.contains { $0.contains("specialized") && $0.contains("unavailable") },
|
|
"specialized 불가가 침묵 아닌 log 로 가시화")
|
|
}
|
|
}
|