Files
artex/intercept/intercept_localized_test.go
dela 0335d572de
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
detections / detections (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
First Commit
2026-10-09 08:38:16 +08:00

101 lines
3.3 KiB
Go

package intercept
import (
"fmt"
"strings"
"testing"
"unicode"
)
// hasHangul reports whether s contains any Hangul syllable.
func hasHangul(s string) bool {
for _, r := range s {
if r >= 0xAC00 && r <= 0xD7A3 {
return true
}
}
return false
}
// hasHan reports whether s contains any CJK Han ideograph.
func hasHan(s string) bool {
for _, r := range s {
if unicode.Is(unicode.Han, r) {
return true
}
}
return false
}
// assertKorean fails if s lacks Hangul or still carries Han ideographs. Reverting
// any localized message back to Chinese trips hasHan, so the test is not vacuous.
func assertKorean(t *testing.T, label, s string) {
t.Helper()
if !hasHangul(s) {
t.Errorf("%s: 한글이 없습니다: %q", label, s)
}
if hasHan(s) {
t.Errorf("%s: 한자가 남아 있습니다: %q", label, s)
}
}
// TestInterceptMessagesLocalized pins the user-facing judge/approval messages to
// Korean. These surface in the approval record reason, the activity stream, and
// the 409 response for an already-decided request.
func TestInterceptMessagesLocalized(t *testing.T) {
for _, c := range []struct{ name, s string }{
{"msgReviewContextIncomplete", msgReviewContextIncomplete},
{"msgModelApprovalFailed", msgModelApprovalFailed},
{"msgModelOutputUnparsable", msgModelOutputUnparsable},
{"reasonWorkCanceled", reasonWorkCanceled},
{"reasonWorkCanceledPreExec", reasonWorkCanceledPreExec},
{"reasonApprovalTimeout", reasonApprovalTimeout},
{"reasonManualDeny", reasonManualDeny},
{"reasonManualAllow", reasonManualAllow},
{"ErrAlreadyDecided", ErrAlreadyDecided.Error()},
} {
assertKorean(t, c.name, c.s)
}
}
// TestJudgeActionLabelLocalized checks the three judge verdict labels are Korean
// (허용 / 차단 / 확인 요청, per the glossary) and that an unknown action still
// passes through untranslated.
func TestJudgeActionLabelLocalized(t *testing.T) {
for _, action := range []string{"allow", "deny", "ask"} {
assertKorean(t, "judgeActionLabel("+action+")", judgeActionLabel(action))
}
if got := judgeActionLabel("weird"); got != "weird" {
t.Errorf("judgeActionLabel(weird) = %q, want passthrough", got)
}
}
// TestDefaultMessageLocalized checks the rule-derived deny/ask messages are Korean
// and still embed the rule name, while allow stays empty.
func TestDefaultMessageLocalized(t *testing.T) {
for _, action := range []string{"deny", "ask"} {
msg := defaultMessage(action, "R1")
assertKorean(t, "defaultMessage("+action+")", msg)
if !strings.Contains(msg, "R1") {
t.Errorf("defaultMessage(%s) dropped rule name: %q", action, msg)
}
}
if got := defaultMessage("allow", "R1"); got != "" {
t.Errorf("defaultMessage(allow) = %q, want empty", got)
}
}
// TestToolApprovalSummaryLocalized checks the activity summary is Korean and stays
// parseable by transcript.tsx, which extracts the pending id via /\(#(\d+)\)/ and
// the tool name via /도구\s+(\S+)\s+승인/.
func TestToolApprovalSummaryLocalized(t *testing.T) {
s := fmt.Sprintf(msgToolApprovalRequestFmt, "Bash", 42)
assertKorean(t, "msgToolApprovalRequestFmt", s)
if !strings.Contains(s, "(#42)") {
t.Errorf("summary lost the (#N) marker (transcript.tsx pending_id regex): %q", s)
}
if !strings.Contains(s, "도구 Bash 승인") {
t.Errorf("summary lost the '도구 X 승인' shape (transcript.tsx toolName regex): %q", s)
}
}