import Testing @testable import 康康 /// 日记追问对话的纯逻辑:轮数计数、收尾判定、离线兜底草稿。 /// DiaryChatService 是 @MainActor struct,测试整体标 @MainActor 规避隔离。 @MainActor struct DiaryChatServiceTests { // MARK: roundsUsed —— 开场白不算追问(assistant 轮数 − 1),user 轮不计 @Test func roundsUsedIsZeroForEmptyTurns() { #expect(DiaryChatService.roundsUsed(in: []) == 0) } @Test func roundsUsedIsZeroForOpeningOnly() { // 仅 1 条 assistant(开场白)→ max(0, 1-1) = 0 let turns: [HealthExportDialogueTurn] = [.assistant("说说你今天的情况")] #expect(DiaryChatService.roundsUsed(in: turns) == 0) } @Test func roundsUsedCountsAssistantRepliesAfterOpening() { // 开场 + 2 条追问 = 3 条 assistant → max(0, 3-1) = 2 let turns: [HealthExportDialogueTurn] = [ .assistant("开场"), .user("我头疼"), .assistant("追问一"), .user("还发烧"), .assistant("追问二") ] #expect(DiaryChatService.roundsUsed(in: turns) == 2) } @Test func roundsUsedIgnoresUserTurns() { // 夹杂多条 user 不影响计数:仍是 3 条 assistant → 2 let turns: [HealthExportDialogueTurn] = [ .assistant("开场"), .user("u1"), .user("u2"), .assistant("追问一"), .user("u3"), .user("u4"), .user("u5"), .assistant("追问二"), .user("u6") ] #expect(DiaryChatService.roundsUsed(in: turns) == 2) } // MARK: isWrappedUp —— roundsUsed >= maxRounds(4) @Test func isNotWrappedUpAtThreeRounds() { // 4 条 assistant(开场 + 3 追问)→ roundsUsed = 3 < 4 → 未收尾 let turns: [HealthExportDialogueTurn] = [ .assistant("开场"), .user("a"), .assistant("q1"), .user("b"), .assistant("q2"), .user("c"), .assistant("q3") ] #expect(DiaryChatService.roundsUsed(in: turns) == 3) #expect(DiaryChatService.isWrappedUp(turns: turns) == false) } @Test func isWrappedUpAtFourRounds() { // 5 条 assistant(开场 + 4 追问)→ roundsUsed = 4 = maxRounds → 收尾 let turns: [HealthExportDialogueTurn] = [ .assistant("开场"), .user("a"), .assistant("q1"), .user("b"), .assistant("q2"), .user("c"), .assistant("q3"), .user("d"), .assistant("q4") ] #expect(DiaryChatService.roundsUsed(in: turns) == DiaryChatPrompts.maxRounds) #expect(DiaryChatService.isWrappedUp(turns: turns) == true) } // MARK: fallbackDraft —— 只拼 user 原话,assistant 绝不进草稿 @Test func fallbackDraftIsEmptyWhenNoUserTurns() { let turns: [HealthExportDialogueTurn] = [ .assistant("开场"), .assistant("再问一句") ] #expect(DiaryChatService.fallbackDraft(turns: turns) == "") } @Test func fallbackDraftReturnsSingleUserVerbatimWithoutBullet() { // 恰 1 条 user → 原话本身,无「· 」前缀;数值原样 let turns: [HealthExportDialogueTurn] = [ .assistant("说说你今天的情况"), .user("今天头疼,量了血压 140/90") ] let draft = DiaryChatService.fallbackDraft(turns: turns) #expect(draft == "今天头疼,量了血压 140/90") #expect(!draft.contains("· ")) } @Test func fallbackDraftBulletsMultipleUserTurns() { // ≥2 条 user → 每条前缀「· 」按换行拼接 let turns: [HealthExportDialogueTurn] = [ .assistant("开场"), .user("今天头疼"), .assistant("量血压了吗?"), .user("量了血压 140/90") ] let draft = DiaryChatService.fallbackDraft(turns: turns) #expect(draft == "· 今天头疼\n· 量了血压 140/90") } @Test func fallbackDraftDropsBlankUserTurns() { // 纯空格 / 换行空白的 user 轮被丢弃,剩两条有效仍带「· 」 let turns: [HealthExportDialogueTurn] = [ .user("第一条有效"), .user(" "), .user("\n \n"), .user("第二条有效") ] let draft = DiaryChatService.fallbackDraft(turns: turns) #expect(draft == "· 第一条有效\n· 第二条有效") } @Test func fallbackDraftNeverIncludesAssistantTextAndKeepsNumbers() { // assistant 文本绝不出现;140/90 原样保留 let turns: [HealthExportDialogueTurn] = [ .assistant("上次你记过头疼"), .user("今天又头疼了,血压 140/90"), .assistant("需要提醒复诊吗?") ] let draft = DiaryChatService.fallbackDraft(turns: turns) #expect(!draft.contains("上次你记过头疼")) #expect(!draft.contains("需要提醒复诊吗?")) #expect(draft.contains("140/90")) } }