package message import ( "testing" ) func TestParse_CommandWithPrefix(t *testing.T) { opts := ParseOptions{ CommandPrefix: "!", BotUserID: "@bot:server", } tests := []struct { name string body string wantCmd string wantArgs []string }{ { name: "standard command", body: "!help", wantCmd: "help", }, { name: "command with args", body: "!deploy prod", wantCmd: "deploy", wantArgs: []string{"prod"}, }, { name: "no prefix means no command", body: "help", wantCmd: "", }, { name: "normal message", body: "hola mundo", wantCmd: "", }, { name: "command is lowercased", body: "!HELP", wantCmd: "help", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := Parse(tt.body, "@user:server", "!room:server", 0, false, opts) if ctx.Command != tt.wantCmd { t.Errorf("Command = %q, want %q", ctx.Command, tt.wantCmd) } if tt.wantArgs != nil { if len(ctx.Args) != len(tt.wantArgs) { t.Errorf("Args = %v, want %v", ctx.Args, tt.wantArgs) } for i, arg := range tt.wantArgs { if i < len(ctx.Args) && ctx.Args[i] != arg { t.Errorf("Args[%d] = %q, want %q", i, ctx.Args[i], arg) } } } }) } } func TestParse_CommandWithoutPrefix(t *testing.T) { opts := ParseOptions{ CommandPrefix: "", BotUserID: "@bot:server", } tests := []struct { name string body string wantCmd string wantArgs []string }{ { name: "bare command", body: "help", wantCmd: "help", }, { name: "bare command with args", body: "deploy prod", wantCmd: "deploy", wantArgs: []string{"prod"}, }, { name: "bang prefix stripped for backward compatibility", body: "!help", wantCmd: "help", }, { name: "bang prefix stripped with args", body: "!deploy staging", wantCmd: "deploy", wantArgs: []string{"staging"}, }, { name: "first token is command", body: "hola mundo", wantCmd: "hola", wantArgs: []string{"mundo"}, }, { name: "command is lowercased", body: "HELP", wantCmd: "help", }, { name: "bang-only message produces no command", body: "!", wantCmd: "", }, { name: "empty message", body: "", wantCmd: "", }, { name: "whitespace-only message", body: " ", wantCmd: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := Parse(tt.body, "@user:server", "!room:server", 0, false, opts) if ctx.Command != tt.wantCmd { t.Errorf("Command = %q, want %q", ctx.Command, tt.wantCmd) } if tt.wantArgs != nil { if len(ctx.Args) != len(tt.wantArgs) { t.Errorf("Args = %v, want %v", ctx.Args, tt.wantArgs) } for i, arg := range tt.wantArgs { if i < len(ctx.Args) && ctx.Args[i] != arg { t.Errorf("Args[%d] = %q, want %q", i, ctx.Args[i], arg) } } } }) } } func TestParse_MentionDetection(t *testing.T) { // Ensure mention detection still works regardless of command prefix mode. botUID := "@bot:server" tests := []struct { name string body string mentions []string wantMention bool }{ { name: "mentioned via m.mentions", body: "hello", mentions: []string{botUID}, wantMention: true, }, { name: "mentioned via body text", body: "hey @bot:server what's up", wantMention: true, }, { name: "not mentioned", body: "hello world", wantMention: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { opts := ParseOptions{ CommandPrefix: "!", BotUserID: botUID, MentionedUserIDs: tt.mentions, } ctx := Parse(tt.body, "@user:server", "!room:server", 0, false, opts) if ctx.IsMention != tt.wantMention { t.Errorf("IsMention = %v, want %v", ctx.IsMention, tt.wantMention) } }) } } func TestParse_NoPrefixBackwardCompatibility(t *testing.T) { // Regression: when prefix is "" and user sends "!help", the result should be // identical to when prefix is "!" and user sends "!help". prefixed := Parse("!help", "@user:server", "!room:server", 0, false, ParseOptions{ CommandPrefix: "!", BotUserID: "@bot:server", }) noPrefix := Parse("!help", "@user:server", "!room:server", 0, false, ParseOptions{ CommandPrefix: "", BotUserID: "@bot:server", }) if prefixed.Command != noPrefix.Command { t.Errorf("Command mismatch: prefixed=%q, noPrefix=%q", prefixed.Command, noPrefix.Command) } if len(prefixed.Args) != len(noPrefix.Args) { t.Errorf("Args length mismatch: prefixed=%v, noPrefix=%v", prefixed.Args, noPrefix.Args) } }