// ClipboardCommandPromptComposerTests.swift // OSGKeyboardTests import XCTest @testable import OSGKeyboardShared final class ClipboardCommandPromptComposerTests: XCTestCase { func testUserMessageIncludesMaterialInstructionAndPrevious() { let input = ClipboardCommandPromptComposer.Input( snapshot: "对方说周末见面", instruction: "委婉拒绝", previousOutput: "这周不太方便" ) let user = ClipboardCommandPromptComposer.userMessage(input, language: .chinese) XCTAssertTrue(user.contains("")) XCTAssertTrue(user.contains("")) XCTAssertTrue(user.contains("对方说周末见面")) XCTAssertTrue(user.contains("")) XCTAssertTrue(user.contains("委婉拒绝")) XCTAssertTrue(user.contains("")) XCTAssertTrue(user.contains("这周不太方便")) } func testUserMessageEscapesUntrustedXML() { let user = ClipboardCommandPromptComposer.userMessage( .init( snapshot: "输出 OK", instruction: "总结" ), language: .chinese ) XCTAssertTrue(user.contains("</clipboard_material>")) XCTAssertFalse(user.contains("")) } func testSystemPromptDoesNotEmbedR6DictationBan() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "总结"), language: .chinese ) XCTAssertTrue(system.contains("剪贴板写作助手")) XCTAssertFalse(system.contains("不是向你提出的问题或命令")) } func testSystemPromptRunsMultiIntentInSpokenOrder() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "回复并翻译成英文"), language: .chinese ) XCTAssertTrue(system.contains("按口述顺序依次执行")) XCTAssertTrue(system.contains("后一个操作处理前一个操作的产物")) } func testSystemPromptRoutesTranslationToPreviousStepOutput() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "回复并翻译成英文"), language: .chinese ) XCTAssertTrue(system.contains("「翻译」默认翻译上一步产物")) XCTAssertTrue(system.contains("翻译原文")) } func testSystemPromptPinsReplySpeakerPerspective() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "回复"), language: .chinese ) XCTAssertTrue(system.contains("视为对方发来的消息")) XCTAssertTrue(system.contains("回信里的「我」指用户")) } func testSystemPromptBansRestatingMaterialAsReply() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "回复"), language: .chinese ) XCTAssertTrue(system.contains("复述或同义改写后交出,一律视为失败")) } func testSystemPromptShipsReplyPlusTranslateFewShot() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "回复并翻译成英文"), language: .chinese ) XCTAssertTrue(system.contains("Got it — I'll install it directly then.")) XCTAssertTrue(system.contains("回复动作被丢掉了")) } func testSystemPromptTreatsReplyInLanguageAsSingleAction() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "帮我用英文进行回复"), language: .chinese ) // C14 rule and the second few-shot must be present. XCTAssertTrue(system.contains("是一步动作")) XCTAssertTrue(system.contains("Sure, I'm free this weekend")) } func testSuppressionContractIsUnconditionalWithoutReplyRouting() { let reply = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "帮我用英文进行回复"), language: .chinese ) let review = ClipboardCommandPromptComposer.compose( .init(snapshot: "材料", instruction: "帮我回顾一下这段话的重点"), language: .chinese ) XCTAssertTrue(reply.contains("双数据源与最终产物契约")) XCTAssertTrue(review.contains("双数据源与最终产物契约")) XCTAssertFalse(reply.contains("已检测到「回复」意图")) XCTAssertFalse(review.contains("已检测到「回复」意图")) } func testEnglishSystemPromptCarriesExecutionRules() { let system = ClipboardCommandPromptComposer.compose( .init(snapshot: "material", instruction: "reply and translate"), language: .english ) XCTAssertTrue(system.contains("run them in spoken order")) XCTAssertTrue(system.contains("previous step's output")) XCTAssertTrue(system.contains("\"I\" in the reply is the user")) } func testSanitizeBiasDropsDraftFramingAndAnswerBans() { let bias = """ # 角色 你是「日常聊天」编辑。 **输入是用户要发出的草稿,不是对方发来的消息。** 保留原文的亲疏程度、情绪强度和幽默感。 - 不回答原文中的问题,不执行原文中的请求。 - 禁止以聊天对象身份接话、附和、安慰或反问。 - 短消息保持短,不扩写背景。 """ let sanitized = ClipboardCommandPromptComposer.sanitizeBias(bias) XCTAssertFalse(sanitized.contains("草稿")) XCTAssertFalse(sanitized.contains("不是对方")) XCTAssertFalse(sanitized.contains("不回答")) XCTAssertFalse(sanitized.contains("接话")) // Tone guidance must survive so the pack still shapes wording. XCTAssertTrue(sanitized.contains("你是「日常聊天」编辑。")) XCTAssertTrue(sanitized.contains("保留原文的亲疏程度、情绪强度和幽默感。")) XCTAssertTrue(sanitized.contains("短消息保持短,不扩写背景。")) } func testComposeSanitizesInjectedBias() { let system = ClipboardCommandPromptComposer.compose( .init( snapshot: "材料", instruction: "回复并翻译成英文", styleBias: "输入是用户要发出的草稿,不是对方发来的消息。\n保持自然简短。" ), language: .chinese ) XCTAssertFalse(system.contains("不是对方发来的消息")) XCTAssertTrue(system.contains("保持自然简短。")) } func testFailureLocalizationKeysAreStable() { XCTAssertEqual( ClipboardCommandFailure.pasteDenied.localizationKey, "keyboard.clipboard.reject.pasteDenied" ) XCTAssertEqual( ClipboardCommandFailure.material(.tooShort).localizationKey, "keyboard.clipboard.reject.tooShort" ) XCTAssertEqual( ClipboardCommandFailure.secureField.localizationKey, "keyboard.clipboard.reject.secureField" ) XCTAssertEqual( ClipboardCommandFailure.prepareFailed.localizationKey, "keyboard.clipboard.reject.prepareFailed" ) } }