package identity import ( "testing" "github.com/sipeed/picoclaw/pkg/bus" ) func TestBuildCanonicalID(t *testing.T) { tests := []struct { platform string platformID string want string }{ {"telegram", "123456", "telegram:123456"}, {"Discord", "98765432", "discord:98765432"}, {"SLACK", "U123ABC", "slack:U123ABC"}, {"", "123", ""}, {"telegram", "", ""}, {" telegram ", " 123 ", "telegram:123"}, } for _, tt := range tests { got := BuildCanonicalID(tt.platform, tt.platformID) if got != tt.want { t.Errorf("BuildCanonicalID(%q, %q) = %q, want %q", tt.platform, tt.platformID, got, tt.want) } } } func TestParseCanonicalID(t *testing.T) { tests := []struct { input string wantPlatform string wantID string wantOk bool }{ {"telegram:123456", "telegram", "123456", true}, {"discord:98765432", "discord", "98765432", true}, {"slack:U123ABC", "slack", "U123ABC", true}, {"nocolon", "", "", false}, {"", "", "", false}, {":missing", "", "", false}, {"missing:", "", "", false}, } for _, tt := range tests { platform, id, ok := ParseCanonicalID(tt.input) if ok != tt.wantOk || platform != tt.wantPlatform || id != tt.wantID { t.Errorf("ParseCanonicalID(%q) = (%q, %q, %v), want (%q, %q, %v)", tt.input, platform, id, ok, tt.wantPlatform, tt.wantID, tt.wantOk) } } } func TestMatchAllowed(t *testing.T) { telegramSender := bus.SenderInfo{ Platform: "telegram", PlatformID: "123456", CanonicalID: "telegram:123456", Username: "alice", DisplayName: "Alice Smith", } discordSender := bus.SenderInfo{ Platform: "discord", PlatformID: "98765432", CanonicalID: "discord:98765432", Username: "bob", DisplayName: "bob#1234", } noCanonicalSender := bus.SenderInfo{ Platform: "telegram", PlatformID: "999", Username: "carol", } tests := []struct { name string sender bus.SenderInfo allowed string want bool }{ // Pure numeric ID matching { name: "numeric ID matches PlatformID", sender: telegramSender, allowed: "123456", want: true, }, { name: "numeric ID does not match", sender: telegramSender, allowed: "654321", want: false, }, { name: "negative numeric ID matches PlatformID", sender: bus.SenderInfo{ Platform: "telegram", PlatformID: "-1001234567890", }, allowed: "-1001234567890", want: true, }, // Username matching { name: "@username matches Username", sender: telegramSender, allowed: "@alice", want: true, }, { name: "plain entry does not match username", sender: bus.SenderInfo{ Platform: "discord", PlatformID: "999999", Username: "123456", }, allowed: "123456", want: false, }, { name: "@username does not match", sender: telegramSender, allowed: "@bob", want: false, }, // Compound format "id|username" { name: "compound matches by ID", sender: telegramSender, allowed: "123456|alice", want: true, }, { name: "compound matches by username", sender: telegramSender, allowed: "999|alice", want: true, }, { name: "compound matches by ID when username differs", sender: bus.SenderInfo{ Platform: "discord", PlatformID: "123456", Username: "not123456", }, allowed: "123456|alice", want: true, }, { name: "compound does not match", sender: telegramSender, allowed: "654321|bob", want: false, }, // Canonical format "platform:id" { name: "canonical matches exactly", sender: telegramSender, allowed: "telegram:123456", want: true, }, { name: "canonical case-insensitive platform", sender: telegramSender, allowed: "Telegram:123456", want: true, }, { name: "canonical wrong platform", sender: telegramSender, allowed: "discord:123456", want: false, }, { name: "canonical wrong ID", sender: telegramSender, allowed: "telegram:654321", want: false, }, // Cross-platform canonical { name: "discord canonical match", sender: discordSender, allowed: "discord:98765432", want: true, }, { name: "telegram canonical does not match discord sender", sender: discordSender, allowed: "telegram:98765432", want: false, }, // Sender without canonical ID { name: "canonical match falls back to platform+platformID", sender: noCanonicalSender, allowed: "telegram:999", want: true, }, { name: "platform mismatch on fallback", sender: noCanonicalSender, allowed: "discord:999", want: false, }, // Empty allowed string { name: "empty allowed never matches", sender: telegramSender, allowed: "", want: false, }, // Whitespace handling { name: "trimmed allowed matches", sender: telegramSender, allowed: " 123456 ", want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := MatchAllowed(tt.sender, tt.allowed) if got != tt.want { t.Errorf("MatchAllowed(%+v, %q) = %v, want %v", tt.sender, tt.allowed, got, tt.want) } }) } } func TestIsNumeric(t *testing.T) { tests := []struct { input string want bool }{ {"123456", true}, {"0", true}, {"", false}, {"abc", false}, {"12a34", false}, {"telegram", false}, {"-1001234567890", true}, {"-", false}, {"-12a34", false}, } for _, tt := range tests { got := isNumeric(tt.input) if got != tt.want { t.Errorf("isNumeric(%q) = %v, want %v", tt.input, got, tt.want) } } }