picoclaw/pkg/providers/messageutil/messageutil_test.go
maxiaoyang d1e9bb6ae3 feat(messageutil): add SanitizeMessageName for OpenAI wire compliance
OpenAI's Chat Completions name field requires ^[a-zA-Z0-9_-]{1,64}$.
Real-world sender IDs (Discord alice#1234, numeric Telegram IDs, Slack
U07AB12C3DEF, free-form CLI names) often violate this, so a sanitizer
sits between the raw inbound ID and any wire payload.

Disallowed characters collapse into a single underscore, leading and
trailing underscores are trimmed, and the result is byte-safely
truncated to 64 (output is pure ASCII).

This helper will be wired into the agent pipeline in a later commit.

Refs #2702.
2026-04-29 20:59:21 +08:00

67 lines
1.8 KiB
Go

// PicoClaw - Ultra-lightweight personal AI agent
// License: MIT
//
// Copyright (c) 2026 PicoClaw contributors
package messageutil
import (
"strings"
"testing"
)
func TestSanitizeMessageName(t *testing.T) {
tests := []struct {
name string
in string
want string
}{
{"empty", "", ""},
{"whitespace only", " ", ""},
{"plain ascii", "alice", "alice"},
{"discord-style id", "alice#1234", "alice_1234"},
{"telegram numeric id", "141455495", "141455495"},
{"slack-style id", "U07AB12C3DEF", "U07AB12C3DEF"},
{"already valid mixed case", "Alice_Bob-99", "Alice_Bob-99"},
{"strips leading/trailing underscores", "@alice@", "alice"},
{"collapses runs", "a@@@b", "a_b"},
{"non-ascii becomes underscores", "李华", ""},
{"non-ascii mixed", "alice 李", "alice"},
{"all special chars collapses to empty", "@@@!!!", ""},
{"truncates to 64", strings.Repeat("a", 100), strings.Repeat("a", 64)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := SanitizeMessageName(tt.in)
if got != tt.want {
t.Errorf("SanitizeMessageName(%q) = %q, want %q", tt.in, got, tt.want)
}
})
}
}
func TestSanitizeMessageName_OutputAlwaysWireSafe(t *testing.T) {
inputs := []string{
"alice#1234", "U07AB12C3DEF", "141455495",
"@@@", "李华", "alice 李", "Alice_Bob-99",
strings.Repeat("a", 200),
}
for _, in := range inputs {
got := SanitizeMessageName(in)
if got == "" {
continue
}
if len(got) > maxMessageNameLen {
t.Errorf("SanitizeMessageName(%q) length %d exceeds max %d", in, len(got), maxMessageNameLen)
}
for _, r := range got {
ok := (r >= 'a' && r <= 'z') ||
(r >= 'A' && r <= 'Z') ||
(r >= '0' && r <= '9') ||
r == '_' || r == '-'
if !ok {
t.Errorf("SanitizeMessageName(%q) = %q contains invalid rune %q", in, got, r)
}
}
}
}