mirror of
https://github.com/alireza0/x-ui.git
synced 2026-08-27 03:43:28 +00:00
176 lines
5 KiB
Go
176 lines
5 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/go-telegram/bot/models"
|
|
)
|
|
|
|
// commandMessage builds the update Telegram sends for a command: the entity
|
|
// spans the "/name" token at the start of the text.
|
|
func commandMessage(text string, commandLen int) *models.Message {
|
|
return &models.Message{
|
|
Text: text,
|
|
Entities: []models.MessageEntity{
|
|
{Type: models.MessageEntityTypeBotCommand, Offset: 0, Length: commandLen},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestCommandParsing(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
message *models.Message
|
|
command string
|
|
args string
|
|
isACommand bool
|
|
}{
|
|
{
|
|
name: "bare command",
|
|
message: commandMessage("/start", 6),
|
|
command: "start",
|
|
args: "",
|
|
isACommand: true,
|
|
},
|
|
{
|
|
// Telegram appends the bot name when a command is used in a group.
|
|
name: "command addressed to the bot",
|
|
message: commandMessage("/start@my_panel_bot", 19),
|
|
command: "start",
|
|
args: "",
|
|
isACommand: true,
|
|
},
|
|
{
|
|
name: "command with an argument",
|
|
message: commandMessage("/usage 3f1a-uuid", 6),
|
|
command: "usage",
|
|
args: "3f1a-uuid",
|
|
isACommand: true,
|
|
},
|
|
{
|
|
name: "addressed command with an argument",
|
|
message: commandMessage("/usage@my_panel_bot 3f1a-uuid", 19),
|
|
command: "usage",
|
|
args: "3f1a-uuid",
|
|
isACommand: true,
|
|
},
|
|
{
|
|
name: "argument keeps its inner spaces",
|
|
message: commandMessage("/inbound my inbound name", 8),
|
|
command: "inbound",
|
|
args: "my inbound name",
|
|
isACommand: true,
|
|
},
|
|
{
|
|
// Emails and remarks are routinely non-ASCII; slicing by bytes here
|
|
// would cut a multi-byte rune in half.
|
|
name: "multi-byte argument",
|
|
message: commandMessage("/inbound سرور اصلی", 8),
|
|
command: "inbound",
|
|
args: "سرور اصلی",
|
|
isACommand: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if got := isCommand(test.message); got != test.isACommand {
|
|
t.Fatalf("isCommand = %v, want %v", got, test.isACommand)
|
|
}
|
|
if got := commandOf(test.message); got != test.command {
|
|
t.Errorf("commandOf = %q, want %q", got, test.command)
|
|
}
|
|
if got := commandArgumentsOf(test.message); got != test.args {
|
|
t.Errorf("commandArgumentsOf = %q, want %q", got, test.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNotACommand(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
message *models.Message
|
|
}{
|
|
{"nil message", nil},
|
|
{"plain text", &models.Message{Text: "hello"}},
|
|
{"no entities", &models.Message{Text: "/start"}},
|
|
{
|
|
// "look at /start" mentions a command but does not invoke one.
|
|
name: "command not at the start",
|
|
message: &models.Message{
|
|
Text: "look at /start",
|
|
Entities: []models.MessageEntity{
|
|
{Type: models.MessageEntityTypeBotCommand, Offset: 8, Length: 6},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "a different entity type",
|
|
message: &models.Message{
|
|
Text: "@someone hi",
|
|
Entities: []models.MessageEntity{
|
|
{Type: models.MessageEntityTypeMention, Offset: 0, Length: 8},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if isCommand(test.message) {
|
|
t.Error("isCommand = true, want false")
|
|
}
|
|
if got := commandOf(test.message); got != "" {
|
|
t.Errorf("commandOf = %q, want empty", got)
|
|
}
|
|
if got := commandArgumentsOf(test.message); got != "" {
|
|
t.Errorf("commandArgumentsOf = %q, want empty", got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// A malformed entity must not panic the update handler, since it would take the
|
|
// whole receiver goroutine down with it.
|
|
func TestCommandParsingSurvivesBadEntities(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
message *models.Message
|
|
}{
|
|
{name: "length past the end", message: commandMessage("/hi", 99)},
|
|
{name: "zero length", message: commandMessage("/hi", 0)},
|
|
{name: "empty text", message: commandMessage("", 5)},
|
|
// Telegram never sends these, but the fields are plain ints and this
|
|
// parser runs on whatever arrives from the network; a panic here would
|
|
// take the update worker down with it.
|
|
{name: "negative length", message: commandMessage("/hi there", -5)},
|
|
{name: "negative length, no argument", message: commandMessage("/hi", -1)},
|
|
{
|
|
name: "negative offset",
|
|
message: &models.Message{
|
|
Text: "/hi there",
|
|
Entities: []models.MessageEntity{
|
|
{Type: models.MessageEntityTypeBotCommand, Offset: -3, Length: 3},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
commandOf(test.message)
|
|
commandArgumentsOf(test.message)
|
|
})
|
|
}
|
|
}
|
|
|
|
// The dispatcher in answerCommand switches on these exact strings; a parser
|
|
// change that renamed any of them would silently disable that command.
|
|
func TestKnownCommandsStillParse(t *testing.T) {
|
|
for _, name := range []string{"help", "start", "status", "id", "usage", "inbound"} {
|
|
message := commandMessage("/"+name, len(name)+1)
|
|
if got := commandOf(message); got != name {
|
|
t.Errorf("commandOf(/%s) = %q", name, got)
|
|
}
|
|
}
|
|
}
|