All checks were successful
Users not on the allowed_users allowlist now get an explicit "not allowed" reply rather than the command silently doing nothing.
204 lines
5.8 KiB
Go
204 lines
5.8 KiB
Go
package gallerydl
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.nakama.town/fmartingr/butterrobot/internal/model"
|
|
)
|
|
|
|
// fakeDownloader returns a Downloader that optionally writes a file and/or
|
|
// returns an error, and records whether it was called and with which URL.
|
|
func fakeDownloader(writeFile bool, returnErr error, gotURL *string) Downloader {
|
|
return func(ctx context.Context, binPath, destDir, url string) error {
|
|
if gotURL != nil {
|
|
*gotURL = url
|
|
}
|
|
if writeFile {
|
|
_ = os.WriteFile(filepath.Join(destDir, "media.jpg"), []byte("data"), 0o600)
|
|
}
|
|
return returnErr
|
|
}
|
|
}
|
|
|
|
func newMessage(text, author, replyTo string, raw map[string]interface{}) *model.Message {
|
|
return &model.Message{
|
|
Text: text,
|
|
Author: author,
|
|
Chat: "chat1",
|
|
ID: "msg1",
|
|
ReplyTo: replyTo,
|
|
Raw: raw,
|
|
Channel: &model.Channel{Platform: "telegram", PlatformChannelID: "chat1"},
|
|
}
|
|
}
|
|
|
|
func telegramReplyRaw(originalText string) map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"message": map[string]interface{}{
|
|
"reply_to_message": map[string]interface{}{
|
|
"text": originalText,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestOnMessage(t *testing.T) {
|
|
allowed := map[string]interface{}{"allowed_users": "alice, bob"}
|
|
|
|
tests := []struct {
|
|
name string
|
|
msg *model.Message
|
|
config map[string]interface{}
|
|
writeFile bool
|
|
downloadErr error
|
|
wantActions int
|
|
wantType model.ActionType
|
|
wantReplyTo string
|
|
wantText string // substring expected in a text reply (if any)
|
|
wantDownload bool
|
|
wantDownURL string
|
|
}{
|
|
{
|
|
name: "not the command",
|
|
msg: newMessage("hello there", "alice", "", nil),
|
|
config: allowed,
|
|
wantActions: 0,
|
|
},
|
|
{
|
|
name: "deny by default (empty allowlist)",
|
|
msg: newMessage("!gallerydl https://example.com/x", "alice", "", nil),
|
|
config: map[string]interface{}{},
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMessage,
|
|
wantText: "not allowed",
|
|
},
|
|
{
|
|
name: "deny user not in allowlist",
|
|
msg: newMessage("!gallerydl https://example.com/x", "carol", "", nil),
|
|
config: allowed,
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMessage,
|
|
wantText: "not allowed",
|
|
},
|
|
{
|
|
name: "authorized with URL in message",
|
|
msg: newMessage("!gallerydl https://example.com/gallery/1", "alice", "", nil),
|
|
config: allowed,
|
|
writeFile: true,
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMedia,
|
|
wantReplyTo: "msg1",
|
|
wantDownload: true,
|
|
wantDownURL: "https://example.com/gallery/1",
|
|
},
|
|
{
|
|
name: "authorized with URL from replied-to message",
|
|
msg: newMessage("!gallerydl", "bob", "orig99", telegramReplyRaw("look https://example.com/gallery/2 nice")),
|
|
config: allowed,
|
|
writeFile: true,
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMedia,
|
|
wantReplyTo: "orig99",
|
|
wantDownload: true,
|
|
wantDownURL: "https://example.com/gallery/2",
|
|
},
|
|
{
|
|
name: "authorized via @-prefixed allowlist entry",
|
|
msg: newMessage("!gallerydl https://example.com/z", "alice", "", nil),
|
|
config: map[string]interface{}{"allowed_users": "@Alice"},
|
|
writeFile: true,
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMedia,
|
|
wantReplyTo: "msg1",
|
|
wantDownload: true,
|
|
},
|
|
{
|
|
name: "authorized but no link",
|
|
msg: newMessage("!gallerydl", "alice", "", nil),
|
|
config: allowed,
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMessage,
|
|
wantText: "No link found",
|
|
},
|
|
{
|
|
name: "download error",
|
|
msg: newMessage("!gallerydl https://example.com/x", "alice", "", nil),
|
|
config: allowed,
|
|
downloadErr: errors.New("boom"),
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMessage,
|
|
wantText: "Download failed",
|
|
wantDownload: true,
|
|
},
|
|
{
|
|
name: "nothing downloaded",
|
|
msg: newMessage("!gallerydl https://example.com/x", "alice", "", nil),
|
|
config: allowed,
|
|
writeFile: false,
|
|
wantActions: 1,
|
|
wantType: model.ActionSendMessage,
|
|
wantText: "Nothing was downloaded",
|
|
wantDownload: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
p := New()
|
|
var gotURL string
|
|
p.download = fakeDownloader(tt.writeFile, tt.downloadErr, &gotURL)
|
|
|
|
actions := p.OnMessage(tt.msg, tt.config, nil)
|
|
|
|
if len(actions) != tt.wantActions {
|
|
t.Fatalf("got %d actions, want %d", len(actions), tt.wantActions)
|
|
}
|
|
if tt.wantActions == 0 {
|
|
if gotURL != "" {
|
|
t.Errorf("downloader should not have run, got url %q", gotURL)
|
|
}
|
|
return
|
|
}
|
|
|
|
action := actions[0]
|
|
if action.Type != tt.wantType {
|
|
t.Fatalf("got action type %q, want %q", action.Type, tt.wantType)
|
|
}
|
|
|
|
switch action.Type {
|
|
case model.ActionSendMedia:
|
|
if action.Message.ReplyTo != tt.wantReplyTo {
|
|
t.Errorf("ReplyTo = %q, want %q", action.Message.ReplyTo, tt.wantReplyTo)
|
|
}
|
|
if len(action.Message.Files) == 0 {
|
|
t.Errorf("expected media files, got none")
|
|
}
|
|
if dir, ok := action.Message.Raw["cleanup_dir"].(string); !ok || dir == "" {
|
|
t.Errorf("expected cleanup_dir in Raw, got %v", action.Message.Raw)
|
|
} else {
|
|
t.Cleanup(func() { _ = os.RemoveAll(dir) })
|
|
}
|
|
case model.ActionSendMessage:
|
|
if tt.wantText != "" && !strings.Contains(action.Message.Text, tt.wantText) {
|
|
t.Errorf("reply text %q does not contain %q", action.Message.Text, tt.wantText)
|
|
}
|
|
}
|
|
|
|
if tt.wantDownload {
|
|
if gotURL == "" {
|
|
t.Errorf("expected downloader to run, but it did not")
|
|
}
|
|
if tt.wantDownURL != "" && gotURL != tt.wantDownURL {
|
|
t.Errorf("downloaded url = %q, want %q", gotURL, tt.wantDownURL)
|
|
}
|
|
} else if gotURL != "" {
|
|
t.Errorf("downloader should not have run, got url %q", gotURL)
|
|
}
|
|
})
|
|
}
|
|
}
|