- 替换 QuickCaptureFlow 和 ArchiveFlow 为 UnifiedCaptureFlow 统一流程 - 新增 VLSession 封装 Qwen2.5-VL 模型进行图像文本推理 - 实现 AIRuntime 中 VL 模型的准备和分析功能 - 添加 VLPrompts 定义体检化验单识别的 JSON 输出模板 - 创建 CaptureReviewForm 提供 VL 解析结果的可编辑表单界面 - 集成 VisionKit 文档扫描器支持真机多页文档扫描 - 为模拟器实现 PhotosPicker 回退方案选择已有照片 - 在 RootView 中统一使用 UnifiedCaptureFlow 处理快速和归档流程 - 添加 CustomMetricEditor 支持自定义监测指标的创建编辑删除 - 扩展 KangkangApp 模型配置以支持新数据类型 - 实现档案列表中症状结束功能通过时间线行点击触发
108 lines
4.2 KiB
Swift
108 lines
4.2 KiB
Swift
import Testing
|
|
import Foundation
|
|
@testable import 康康
|
|
|
|
struct SeriesBucketTests {
|
|
|
|
private func makeIndicator(
|
|
name: String = "测试",
|
|
value: String,
|
|
unit: String = "mmol/L",
|
|
range: String = "",
|
|
status: IndicatorStatus = .normal,
|
|
capturedAt: Date,
|
|
seriesKey: String?
|
|
) -> Indicator {
|
|
Indicator(name: name, value: value, unit: unit, range: range,
|
|
status: status, capturedAt: capturedAt,
|
|
seriesKey: seriesKey)
|
|
}
|
|
|
|
@Test func skipsIndicatorsWithoutSeriesKey() {
|
|
let now = Date()
|
|
let items = [
|
|
makeIndicator(value: "5.0", capturedAt: now, seriesKey: nil),
|
|
makeIndicator(value: "5.2", capturedAt: now, seriesKey: nil),
|
|
]
|
|
let buckets = SeriesBucket.build(from: items)
|
|
#expect(buckets.isEmpty)
|
|
}
|
|
|
|
@Test func filtersOutSeriesWithFewerThanMinPoints() {
|
|
let now = Date()
|
|
let items = [
|
|
makeIndicator(value: "5.0", capturedAt: now, seriesKey: "glucose.fasting"),
|
|
]
|
|
let buckets = SeriesBucket.build(from: items, minPoints: 2)
|
|
#expect(buckets.isEmpty)
|
|
}
|
|
|
|
@Test func singleSeriesBucketSortedAscending() {
|
|
let day = { (offset: Int) -> Date in
|
|
Calendar.current.date(byAdding: .day, value: offset, to: .now)!
|
|
}
|
|
let items = [
|
|
makeIndicator(value: "5.5", capturedAt: day(-3), seriesKey: "glucose.fasting"),
|
|
makeIndicator(value: "5.2", capturedAt: day(-1), seriesKey: "glucose.fasting"),
|
|
makeIndicator(value: "5.8", capturedAt: day(-2), seriesKey: "glucose.fasting"),
|
|
]
|
|
let buckets = SeriesBucket.build(from: items)
|
|
#expect(buckets.count == 1)
|
|
let line = try! #require(buckets.first?.lines.first)
|
|
// sorted ascending → -3, -2, -1
|
|
let values = line.points.map(\.value)
|
|
#expect(values == [5.5, 5.8, 5.2])
|
|
}
|
|
|
|
@Test func bloodPressureMergesIntoSingleBucket() {
|
|
let now = Date()
|
|
let day = { (offset: Int) -> Date in
|
|
Calendar.current.date(byAdding: .day, value: offset, to: now)!
|
|
}
|
|
let items = [
|
|
makeIndicator(value: "125", capturedAt: day(-2), seriesKey: "bp.systolic"),
|
|
makeIndicator(value: "82", capturedAt: day(-2), seriesKey: "bp.diastolic"),
|
|
makeIndicator(value: "130", capturedAt: day(-1), seriesKey: "bp.systolic"),
|
|
makeIndicator(value: "85", capturedAt: day(-1), seriesKey: "bp.diastolic"),
|
|
]
|
|
let buckets = SeriesBucket.build(from: items)
|
|
let bp = try! #require(buckets.first { $0.id == "bp" })
|
|
#expect(bp.lines.count == 2)
|
|
#expect(bp.title == "血压")
|
|
#expect(bp.lines.contains { $0.seriesKey == "bp.systolic" })
|
|
#expect(bp.lines.contains { $0.seriesKey == "bp.diastolic" })
|
|
}
|
|
|
|
@Test func mixedSeriesProducesMultipleBucketsSortedByRecency() {
|
|
let cal = Calendar.current
|
|
let day = { (offset: Int) -> Date in
|
|
cal.date(byAdding: .day, value: offset, to: .now)!
|
|
}
|
|
let items = [
|
|
// weight 较旧
|
|
makeIndicator(value: "68", capturedAt: day(-10), seriesKey: "weight"),
|
|
makeIndicator(value: "67", capturedAt: day(-7), seriesKey: "weight"),
|
|
// glucose 较新
|
|
makeIndicator(value: "5.1", capturedAt: day(-2), seriesKey: "glucose.fasting"),
|
|
makeIndicator(value: "5.3", capturedAt: day(-1), seriesKey: "glucose.fasting"),
|
|
]
|
|
let buckets = SeriesBucket.build(from: items)
|
|
#expect(buckets.count == 2)
|
|
// 最新的 glucose 排前面
|
|
#expect(buckets.first?.id == "glucose.fasting")
|
|
#expect(buckets.last?.id == "weight")
|
|
}
|
|
|
|
@Test func nonNumericValueDropped() {
|
|
let now = Date()
|
|
let items = [
|
|
makeIndicator(value: "高", capturedAt: now, seriesKey: "weight"),
|
|
makeIndicator(value: "68", capturedAt: now, seriesKey: "weight"),
|
|
makeIndicator(value: "67", capturedAt: now.addingTimeInterval(60), seriesKey: "weight"),
|
|
]
|
|
let buckets = SeriesBucket.build(from: items)
|
|
let line = try! #require(buckets.first?.lines.first)
|
|
#expect(line.points.count == 2) // "高" 被丢
|
|
}
|
|
}
|