mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 19:23:42 +08:00
The PRIVATE_ITEMS list contained the bare 'test' entry, which rsync interprets as 'any directory named test at any depth' — so it caught the root-level /test/ scratch directory (intended) AND every src/test/ under each module (not intended). Pattern is already anchored to /test (root-only). This commit rsyncs the accumulated src/test/ tree forward so opensource has the unit tests that have been written / updated against existing src/main/ code since the pattern regression. Going forward each per-commit sync will carry src/test/ files along with the main change.
77 lines
3.8 KiB
Java
77 lines
3.8 KiB
Java
package vip.mate.agent;
|
|
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Test;
|
|
import vip.mate.workspace.conversation.model.MessageEntity;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
/**
|
|
* Closure regression test for RFC-067 PR 7.
|
|
* <p>
|
|
* PR 1 §4.1.5 flips {@code MessageEntity.status} from {@code awaiting_approval}
|
|
* to {@code completed} (approve) or {@code stopped} (deny) inside
|
|
* {@link vip.mate.workspace.conversation.ConversationService#markPendingApprovalsResolved}.
|
|
* That status flip travels into history sanitization on subsequent LLM turns;
|
|
* if any sanitizer stage accidentally treated the post-flip status as a stub
|
|
* marker, the original assistant content would be dropped from history and the
|
|
* user-visible conversation would lose context after every approval.
|
|
* <p>
|
|
* These tests pin the boundary: only the explicit {@code [等待审批]} content
|
|
* placeholder is dropped by stage 1; a message that carries real streamed text
|
|
* + {@code status=awaiting_approval | completed | stopped} is preserved exactly
|
|
* regardless of where in the approval lifecycle it sits.
|
|
*/
|
|
class BaseAgentApprovalSanitizationTest {
|
|
|
|
@Test
|
|
@DisplayName("Real assistant content with status=awaiting_approval is NOT a Stage 1 placeholder")
|
|
void realContentDuringAwaiting() {
|
|
// Common shape: streamed partial answer + tool_approval_requested mid-flight,
|
|
// doOnComplete persists with status=awaiting_approval (PR 5).
|
|
MessageEntity msg = entity("我准备读取你的简历文件。", "awaiting_approval");
|
|
assertFalse(BaseAgent.isApprovalPlaceholder(msg.getContent()),
|
|
"real text must not match the placeholder regex — sanitizer would drop it otherwise");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Post-approve message (status=completed, real content) is NOT a placeholder")
|
|
void postApproveMessageSurvives() {
|
|
// After PR 1 §4.1.5 reconciles approval: status flips awaiting_approval → completed,
|
|
// metadata.pendingApproval.status flips pending_approval → approved, content is unchanged.
|
|
MessageEntity msg = entity("已读取简历,关键信息: ...", "completed");
|
|
assertFalse(BaseAgent.isApprovalPlaceholder(msg.getContent()),
|
|
"approved-and-completed history entry must survive sanitization for the next LLM turn");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Post-deny message (status=stopped, real content) is NOT a placeholder")
|
|
void postDenyMessageSurvives() {
|
|
// Deny path: status flips awaiting_approval → stopped, content stays as the
|
|
// partial assistant text. The LLM should still see this on the next turn so
|
|
// it understands "I started reading then was denied" rather than amnesia.
|
|
MessageEntity msg = entity("用户拒绝执行工具 write_file", "stopped");
|
|
assertFalse(BaseAgent.isApprovalPlaceholder(msg.getContent()),
|
|
"denied turn's assistant text must survive history sanitization");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Pure placeholder content IS dropped (Stage 1's actual job)")
|
|
void placeholderStubIsFiltered() {
|
|
// The "[等待审批]" stub is the historical placeholder format that Stage 1 catches —
|
|
// those rows have no streamed content and add no value to the LLM context.
|
|
MessageEntity msg = entity("[等待审批]", "awaiting_approval");
|
|
assertTrue(BaseAgent.isApprovalPlaceholder(msg.getContent()),
|
|
"stub-only placeholder content must still match so Stage 1 keeps filtering it");
|
|
}
|
|
|
|
private static MessageEntity entity(String content, String status) {
|
|
MessageEntity m = new MessageEntity();
|
|
m.setRole("assistant");
|
|
m.setContent(content);
|
|
m.setStatus(status);
|
|
return m;
|
|
}
|
|
}
|