mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
322 lines
8.0 KiB
Go
322 lines
8.0 KiB
Go
package channels
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/bus"
|
|
"github.com/sipeed/picoclaw/pkg/config"
|
|
)
|
|
|
|
func TestBaseChannelIsAllowed(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
allowList []string
|
|
senderID string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "empty allowlist allows all",
|
|
allowList: nil,
|
|
senderID: "anyone",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "compound sender matches numeric allowlist",
|
|
allowList: []string{"123456"},
|
|
senderID: "123456|alice",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "compound sender matches username allowlist",
|
|
allowList: []string{"@alice"},
|
|
senderID: "123456|alice",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "numeric sender matches legacy compound allowlist",
|
|
allowList: []string{"123456|alice"},
|
|
senderID: "123456",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "non matching sender is denied",
|
|
allowList: []string{"123456"},
|
|
senderID: "654321|bob",
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ch := NewBaseChannel("test", nil, nil, tt.allowList)
|
|
if got := ch.IsAllowed(tt.senderID); got != tt.want {
|
|
t.Fatalf("IsAllowed(%q) = %v, want %v", tt.senderID, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestShouldRespondInGroup(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
gt config.GroupTriggerConfig
|
|
isMentioned bool
|
|
content string
|
|
wantRespond bool
|
|
wantContent string
|
|
}{
|
|
{
|
|
name: "no config - permissive default",
|
|
gt: config.GroupTriggerConfig{},
|
|
isMentioned: false,
|
|
content: "hello world",
|
|
wantRespond: true,
|
|
wantContent: "hello world",
|
|
},
|
|
{
|
|
name: "no config - mentioned",
|
|
gt: config.GroupTriggerConfig{},
|
|
isMentioned: true,
|
|
content: "hello world",
|
|
wantRespond: true,
|
|
wantContent: "hello world",
|
|
},
|
|
{
|
|
name: "mention_only - not mentioned",
|
|
gt: config.GroupTriggerConfig{MentionOnly: true},
|
|
isMentioned: false,
|
|
content: "hello world",
|
|
wantRespond: false,
|
|
wantContent: "hello world",
|
|
},
|
|
{
|
|
name: "mention_only - mentioned",
|
|
gt: config.GroupTriggerConfig{MentionOnly: true},
|
|
isMentioned: true,
|
|
content: "hello world",
|
|
wantRespond: true,
|
|
wantContent: "hello world",
|
|
},
|
|
{
|
|
name: "prefix match",
|
|
gt: config.GroupTriggerConfig{Prefixes: []string{"/ask"}},
|
|
isMentioned: false,
|
|
content: "/ask hello",
|
|
wantRespond: true,
|
|
wantContent: "hello",
|
|
},
|
|
{
|
|
name: "prefix no match - not mentioned",
|
|
gt: config.GroupTriggerConfig{Prefixes: []string{"/ask"}},
|
|
isMentioned: false,
|
|
content: "hello world",
|
|
wantRespond: false,
|
|
wantContent: "hello world",
|
|
},
|
|
{
|
|
name: "prefix no match - but mentioned",
|
|
gt: config.GroupTriggerConfig{Prefixes: []string{"/ask"}},
|
|
isMentioned: true,
|
|
content: "hello world",
|
|
wantRespond: true,
|
|
wantContent: "hello world",
|
|
},
|
|
{
|
|
name: "multiple prefixes - second matches",
|
|
gt: config.GroupTriggerConfig{Prefixes: []string{"/ask", "/bot"}},
|
|
isMentioned: false,
|
|
content: "/bot help me",
|
|
wantRespond: true,
|
|
wantContent: "help me",
|
|
},
|
|
{
|
|
name: "mention_only with prefixes - mentioned overrides",
|
|
gt: config.GroupTriggerConfig{MentionOnly: true, Prefixes: []string{"/ask"}},
|
|
isMentioned: true,
|
|
content: "hello",
|
|
wantRespond: true,
|
|
wantContent: "hello",
|
|
},
|
|
{
|
|
name: "mention_only with prefixes - not mentioned, no prefix",
|
|
gt: config.GroupTriggerConfig{MentionOnly: true, Prefixes: []string{"/ask"}},
|
|
isMentioned: false,
|
|
content: "hello",
|
|
wantRespond: false,
|
|
wantContent: "hello",
|
|
},
|
|
{
|
|
name: "empty prefix in list is skipped",
|
|
gt: config.GroupTriggerConfig{Prefixes: []string{"", "/ask"}},
|
|
isMentioned: false,
|
|
content: "/ask test",
|
|
wantRespond: true,
|
|
wantContent: "test",
|
|
},
|
|
{
|
|
name: "prefix strips leading whitespace after prefix",
|
|
gt: config.GroupTriggerConfig{Prefixes: []string{"/ask "}},
|
|
isMentioned: false,
|
|
content: "/ask hello",
|
|
wantRespond: true,
|
|
wantContent: "hello",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ch := NewBaseChannel("test", nil, nil, nil, WithGroupTrigger(tt.gt))
|
|
gotRespond, gotContent := ch.ShouldRespondInGroup(tt.isMentioned, tt.content)
|
|
if gotRespond != tt.wantRespond {
|
|
t.Errorf("ShouldRespondInGroup() respond = %v, want %v", gotRespond, tt.wantRespond)
|
|
}
|
|
if gotContent != tt.wantContent {
|
|
t.Errorf("ShouldRespondInGroup() content = %q, want %q", gotContent, tt.wantContent)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsAllowedSender(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
allowList []string
|
|
sender bus.SenderInfo
|
|
want bool
|
|
}{
|
|
{
|
|
name: "empty allowlist allows all",
|
|
allowList: nil,
|
|
sender: bus.SenderInfo{PlatformID: "anyone"},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "numeric ID matches PlatformID",
|
|
allowList: []string{"123456"},
|
|
sender: bus.SenderInfo{
|
|
Platform: "telegram",
|
|
PlatformID: "123456",
|
|
CanonicalID: "telegram:123456",
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "canonical format matches",
|
|
allowList: []string{"telegram:123456"},
|
|
sender: bus.SenderInfo{
|
|
Platform: "telegram",
|
|
PlatformID: "123456",
|
|
CanonicalID: "telegram:123456",
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "canonical format wrong platform",
|
|
allowList: []string{"discord:123456"},
|
|
sender: bus.SenderInfo{
|
|
Platform: "telegram",
|
|
PlatformID: "123456",
|
|
CanonicalID: "telegram:123456",
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "@username matches",
|
|
allowList: []string{"@alice"},
|
|
sender: bus.SenderInfo{
|
|
Platform: "telegram",
|
|
PlatformID: "123456",
|
|
CanonicalID: "telegram:123456",
|
|
Username: "alice",
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "compound id|username matches by ID",
|
|
allowList: []string{"123456|alice"},
|
|
sender: bus.SenderInfo{
|
|
Platform: "telegram",
|
|
PlatformID: "123456",
|
|
CanonicalID: "telegram:123456",
|
|
Username: "alice",
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "non matching sender denied",
|
|
allowList: []string{"654321"},
|
|
sender: bus.SenderInfo{
|
|
Platform: "telegram",
|
|
PlatformID: "123456",
|
|
CanonicalID: "telegram:123456",
|
|
},
|
|
want: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ch := NewBaseChannel("test", nil, nil, tt.allowList)
|
|
if got := ch.IsAllowedSender(tt.sender); got != tt.want {
|
|
t.Fatalf("IsAllowedSender(%+v) = %v, want %v", tt.sender, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHandleInboundContext_PublishesNormalizedContext(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
inbound bus.InboundContext
|
|
wantChat string
|
|
wantSender string
|
|
}{
|
|
{
|
|
name: "direct uses sender as peer",
|
|
inbound: bus.InboundContext{
|
|
Channel: "test",
|
|
ChatID: "chat-1",
|
|
ChatType: "direct",
|
|
SenderID: "user-1",
|
|
MessageID: "msg-1",
|
|
},
|
|
wantChat: "chat-1",
|
|
wantSender: "user-1",
|
|
},
|
|
{
|
|
name: "group uses chat as peer",
|
|
inbound: bus.InboundContext{
|
|
Channel: "test",
|
|
ChatID: "group-1",
|
|
ChatType: "group",
|
|
SenderID: "user-2",
|
|
MessageID: "msg-2",
|
|
},
|
|
wantChat: "group-1",
|
|
wantSender: "user-2",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
msgBus := bus.NewMessageBus()
|
|
defer msgBus.Close()
|
|
|
|
ch := NewBaseChannel("test", nil, msgBus, nil)
|
|
ch.HandleInboundContext(context.Background(), tt.inbound.ChatID, "hello", nil, tt.inbound)
|
|
|
|
msg := <-msgBus.InboundChan()
|
|
if msg.ChatID != tt.wantChat {
|
|
t.Fatalf("ChatID = %q, want %q", msg.ChatID, tt.wantChat)
|
|
}
|
|
if msg.SenderID != tt.wantSender {
|
|
t.Fatalf("SenderID = %q, want %q", msg.SenderID, tt.wantSender)
|
|
}
|
|
if msg.Context.ChatType != tt.inbound.ChatType {
|
|
t.Fatalf("ChatType = %q, want %q", msg.Context.ChatType, tt.inbound.ChatType)
|
|
}
|
|
})
|
|
}
|
|
}
|