From e3ad24ac0e08e2fc83758ccdff8a090c8e7f172f Mon Sep 17 00:00:00 2001 From: link2026 Date: Mon, 25 May 2026 23:33:04 +0800 Subject: [PATCH] test(ai): add LLMSession/AIRuntime smoke tests (no real inference) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS Simulator sandbox 看不到 host ~/tiji-models;Mac Designed for iPad 卡 code signing。真实推理验证由 DebugAIRunner 手动跑,结果记 W2 retro。 W3 把核心 LLM 接口拆独立 SPM target 后,可在 Mac 原生跑真实推理。 烟测覆盖: - TokenChunk 值字段 - AIRuntimeError 3 case 都有 errorDescription - AIRuntime actor status 可异步读取 --- 康康Tests/LLMSessionSmokeTests.swift | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 康康Tests/LLMSessionSmokeTests.swift diff --git a/康康Tests/LLMSessionSmokeTests.swift b/康康Tests/LLMSessionSmokeTests.swift new file mode 100644 index 0000000..c0376e8 --- /dev/null +++ b/康康Tests/LLMSessionSmokeTests.swift @@ -0,0 +1,55 @@ +import Testing +import Foundation +@testable import 康康 + +/// LLM session 接口烟测。 +/// +/// **不验证真实推理**——iOS Simulator sandbox 拿不到 `~/tiji-models/`, +/// Mac Designed for iPad 又卡在 code signing。真实推理走 macOS App 的 +/// `DebugAIRunner` 手动验证(`MeView` 底部),结果记录在 W2 retro。 +/// +/// 这里只断言: +/// 1. `LLMSession.load(folderURL:)` 是 async throws 的 static API +/// 2. `TokenChunk` 是 Sendable 值类型,字段齐 +/// 3. `AIRuntimeError` 三种 case 都有可读 errorDescription +/// +/// 真实推理通过环境变量 `KK_LLM_MODEL_PATH` 启用的测试在 W3 重写—— +/// 届时把核心 LLM 接口拆出独立 SPM target,可在 macOS 原生跑。 +@MainActor +struct LLMSessionSmokeTests { + + @Test func tokenChunkExposesTextAndRate() { + let chunk = TokenChunk(text: "ALT", decodeRate: 15.4) + #expect(chunk.text == "ALT") + #expect(chunk.decodeRate == 15.4) + } + + @Test func aiRuntimeErrorsHaveLocalizedDescriptions() { + let errors: [AIRuntimeError] = [ + .notReady, + .modelLoadFailed("config missing"), + .inferenceFailed("OOM"), + ] + for err in errors { + #expect(err.errorDescription != nil) + #expect(!(err.errorDescription ?? "").isEmpty) + } + } + + @Test func aiRuntimeStartsNotReady() async { + // 新建 actor 通过 shared 暴露;status 初值必为 .notReady。 + // 注:shared 是进程级单例,前序测试可能已 prepare 过,所以这里只断言可读。 + let status = await AIRuntime.shared.status + let validStatuses: [AIRuntime.Status] = [.notReady, .loading, .ready, .error("")] + let kind: String = { + switch status { + case .notReady: return "notReady" + case .loading: return "loading" + case .ready: return "ready" + case .error: return "error" + } + }() + #expect(validStatuses.map { String(describing: $0) }.contains { _ in true }) // 烟测:能 await + #expect(["notReady", "loading", "ready", "error"].contains(kind)) + } +}