身高/体重对成人变化慢,作为 Profile 静态字段比每次录入 Indicator 更合适。 - MonitorMetric:6 case(从 8 减),删 .height / .weight - UserProfile:加 weightKG: Double?(支持小数),加 bmi computed - summaryLine 加体重段:'175cm · 68.5kg'(整数省小数) - ProfileEditView basics 加 weight 行 + footer 显示 BMI + 分类(偏瘦/正常/超重/肥胖) - IndicatorQuickSheet:删 .height 回写 Profile 的特殊逻辑 - UserProfileTests:+5 个(weight 字段、summaryLine 含 weight、BMI 计算) 兼容性:老 Indicator 里的 seriesKey 'weight' / 'height' 数据保留(SwiftData String? 不变),只是新录入路径走 Profile 不走 Indicator;Trends 仍能用 String seriesKey 查询历史(如果将来要展示老数据)。 测试:60 case pass / 0 fail / 0 warning。
111 lines
3.7 KiB
Swift
111 lines
3.7 KiB
Swift
import Testing
|
|
import SwiftData
|
|
import Foundation
|
|
@testable import 康康
|
|
|
|
@MainActor
|
|
struct UserProfileTests {
|
|
|
|
private func makeContext() throws -> ModelContext {
|
|
let schema = Schema([UserProfile.self])
|
|
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
|
|
let container = try ModelContainer(for: schema, configurations: [config])
|
|
return ModelContext(container)
|
|
}
|
|
|
|
@Test func freshProfileHasNilDemographics() {
|
|
let p = UserProfile()
|
|
#expect(p.birthYear == nil)
|
|
#expect(p.heightCM == nil)
|
|
#expect(p.weightKG == nil)
|
|
#expect(p.biologicalSexRaw == "")
|
|
#expect(p.bloodTypeRaw == "")
|
|
#expect(p.allergies.isEmpty)
|
|
#expect(p.chronicConditions.isEmpty)
|
|
#expect(p.currentMedications.isEmpty)
|
|
#expect(p.familyHistory.isEmpty)
|
|
#expect(p.age == nil)
|
|
#expect(p.sex == .undisclosed)
|
|
#expect(p.bmi == nil)
|
|
#expect(p.hasAnyBasics == false)
|
|
#expect(p.summaryLine == "")
|
|
}
|
|
|
|
@Test func ageComputedFromBirthYear() {
|
|
let p = UserProfile(birthYear: 1985)
|
|
let currentYear = Calendar.current.component(.year, from: .now)
|
|
#expect(p.age == currentYear - 1985)
|
|
}
|
|
|
|
@Test func sexEnumRoundtripsThroughRaw() {
|
|
let p = UserProfile(biologicalSexRaw: "male")
|
|
#expect(p.sex == .male)
|
|
p.sex = .female
|
|
#expect(p.biologicalSexRaw == "female")
|
|
p.sex = .undisclosed
|
|
#expect(p.biologicalSexRaw == "")
|
|
}
|
|
|
|
@Test func summaryLineSkipsEmptyFields() {
|
|
let p = UserProfile(birthYear: 1990, biologicalSexRaw: "female", heightCM: 162)
|
|
let line = p.summaryLine
|
|
#expect(line.contains("岁"))
|
|
#expect(line.contains("女"))
|
|
#expect(line.contains("162cm"))
|
|
#expect(!line.contains("kg")) // 体重空,不出现
|
|
#expect(!line.contains("型")) // 血型空,不出现
|
|
}
|
|
|
|
@Test func summaryLineIncludesWeightWhenSet() {
|
|
let p = UserProfile(heightCM: 175, weightKG: 68.5)
|
|
#expect(p.summaryLine.contains("175cm"))
|
|
#expect(p.summaryLine.contains("68.5kg"))
|
|
}
|
|
|
|
@Test func summaryLineFormatsIntegerWeightWithoutDecimal() {
|
|
let p = UserProfile(weightKG: 70)
|
|
#expect(p.summaryLine == "70kg")
|
|
}
|
|
|
|
@Test func bmiNilWithoutBothHeightAndWeight() {
|
|
#expect(UserProfile(heightCM: 175).bmi == nil)
|
|
#expect(UserProfile(weightKG: 68).bmi == nil)
|
|
#expect(UserProfile().bmi == nil)
|
|
}
|
|
|
|
@Test func bmiComputedFromHeightAndWeight() throws {
|
|
let p = UserProfile(heightCM: 175, weightKG: 70)
|
|
let bmi = try #require(p.bmi)
|
|
// 70 / (1.75 * 1.75) = 22.857
|
|
#expect(abs(bmi - 22.857) < 0.01)
|
|
}
|
|
|
|
@Test func loadOrCreateReturnsExistingSingleton() throws {
|
|
let ctx = try makeContext()
|
|
let first = UserProfileStore.loadOrCreate(in: ctx)
|
|
first.birthYear = 1990
|
|
try ctx.save()
|
|
|
|
let second = UserProfileStore.loadOrCreate(in: ctx)
|
|
#expect(second.birthYear == 1990)
|
|
|
|
let all = try ctx.fetch(FetchDescriptor<UserProfile>())
|
|
#expect(all.count == 1)
|
|
}
|
|
|
|
@Test func arrayFieldsRoundtripThroughSwiftData() throws {
|
|
let ctx = try makeContext()
|
|
let p = UserProfile(
|
|
chronicConditions: ["高血压", "糖尿病"],
|
|
currentMedications: ["缬沙坦 80mg qd", "二甲双胍 500mg bid"]
|
|
)
|
|
ctx.insert(p)
|
|
try ctx.save()
|
|
|
|
let fetched = try #require(try ctx.fetch(FetchDescriptor<UserProfile>()).first)
|
|
#expect(fetched.chronicConditions == ["高血压", "糖尿病"])
|
|
#expect(fetched.currentMedications.count == 2)
|
|
#expect(fetched.currentMedications.first == "缬沙坦 80mg qd")
|
|
}
|
|
}
|