157 lines
4.7 KiB
Go
157 lines
4.7 KiB
Go
package telegram_test
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log/slog"
|
|
"testing"
|
|
|
|
"backend/internal/modules/telegram"
|
|
)
|
|
|
|
// fakeRepository is an in-memory telegram.Repository (the single settings
|
|
// row) used to unit-test telegram.Service without a real database.
|
|
type fakeRepository struct {
|
|
settings telegram.Settings
|
|
}
|
|
|
|
func newFakeRepository() *fakeRepository {
|
|
return &fakeRepository{settings: telegram.Settings{ID: 1}}
|
|
}
|
|
|
|
func (r *fakeRepository) Get(_ context.Context) (*telegram.Settings, error) {
|
|
cp := r.settings
|
|
return &cp, nil
|
|
}
|
|
|
|
func (r *fakeRepository) Update(_ context.Context, s *telegram.Settings) error {
|
|
r.settings.Enabled = s.Enabled
|
|
r.settings.ChatID = s.ChatID
|
|
r.settings.NotifyNewOrder = s.NotifyNewOrder
|
|
if s.BotToken != "" {
|
|
r.settings.BotToken = s.BotToken
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// fakeSender records every message it was asked to send, instead of making
|
|
// a real network call to the Telegram Bot API.
|
|
type fakeSender struct {
|
|
sent []string
|
|
err error
|
|
}
|
|
|
|
func (s *fakeSender) Send(_ context.Context, botToken, chatID, text string) error {
|
|
if s.err != nil {
|
|
return s.err
|
|
}
|
|
s.sent = append(s.sent, text)
|
|
return nil
|
|
}
|
|
|
|
func discardLogger() *slog.Logger {
|
|
return slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
}
|
|
|
|
func TestService_Update_KeepsExistingTokenWhenBlank(t *testing.T) {
|
|
repo := newFakeRepository()
|
|
svc := telegram.NewService(repo, &fakeSender{}, discardLogger())
|
|
ctx := context.Background()
|
|
|
|
if _, err := svc.Update(ctx, true, "secret-token", "12345", true); err != nil {
|
|
t.Fatalf("first Update() error = %v", err)
|
|
}
|
|
|
|
// Flip a flag without resending the token.
|
|
settings, err := svc.Update(ctx, true, "", "12345", false)
|
|
if err != nil {
|
|
t.Fatalf("second Update() error = %v", err)
|
|
}
|
|
if settings.NotifyNewOrder {
|
|
t.Fatalf("Update() flags = %+v, want NotifyStatusChange=true NotifyNewOrder=false", settings)
|
|
}
|
|
if repo.settings.BotToken != "secret-token" {
|
|
t.Fatalf("Update() with blank bot_token overwrote the stored token: got %q", repo.settings.BotToken)
|
|
}
|
|
}
|
|
|
|
func TestService_Get_NeverExposesRawToken(t *testing.T) {
|
|
// This mirrors the handler's toResponse() contract: the service layer
|
|
// itself returns the raw Settings (needed internally to call the
|
|
// Telegram API), but the HTTP handler must never marshal BotToken back
|
|
// to the client. This test guards the service-level data so a future
|
|
// change to the handler can't accidentally start doing so without
|
|
// resetting the flag on read.
|
|
repo := newFakeRepository()
|
|
svc := telegram.NewService(repo, &fakeSender{}, discardLogger())
|
|
ctx := context.Background()
|
|
|
|
if _, err := svc.Update(ctx, true, "super-secret", "12345", true); err != nil {
|
|
t.Fatalf("Update() error = %v", err)
|
|
}
|
|
|
|
settings, err := svc.Get(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Get() error = %v", err)
|
|
}
|
|
if settings.BotToken != "super-secret" {
|
|
t.Fatalf("Get() lost the stored bot token: got %q", settings.BotToken)
|
|
}
|
|
}
|
|
|
|
func TestService_NotifyNewOrder_SkipsWhenDisabled(t *testing.T) {
|
|
repo := newFakeRepository()
|
|
sender := &fakeSender{}
|
|
svc := telegram.NewService(repo, sender, discardLogger())
|
|
ctx := context.Background()
|
|
|
|
if _, err := svc.Update(ctx, false, "token", "12345", true); err != nil {
|
|
t.Fatalf("Update() error = %v", err)
|
|
}
|
|
|
|
svc.NotifyNewOrder(ctx, "new order!")
|
|
if len(sender.sent) != 0 {
|
|
t.Fatalf("NotifyNewOrder() sent a message while Telegram is disabled: %v", sender.sent)
|
|
}
|
|
}
|
|
|
|
func TestService_NotifyNewOrder_SkipsWhenEventNotEnabled(t *testing.T) {
|
|
repo := newFakeRepository()
|
|
sender := &fakeSender{}
|
|
svc := telegram.NewService(repo, sender, discardLogger())
|
|
ctx := context.Background()
|
|
|
|
// Enabled overall, but the "new order" event specifically is off.
|
|
if _, err := svc.Update(ctx, true, "token", "12345", false); err != nil {
|
|
t.Fatalf("Update() error = %v", err)
|
|
}
|
|
|
|
svc.NotifyNewOrder(ctx, "new order!")
|
|
if len(sender.sent) != 0 {
|
|
t.Fatalf("NotifyNewOrder() sent a message while notify_new_order=false: %v", sender.sent)
|
|
}
|
|
}
|
|
|
|
func TestService_NotifyNewOrder_SendsWhenEnabled(t *testing.T) {
|
|
repo := newFakeRepository()
|
|
sender := &fakeSender{}
|
|
svc := telegram.NewService(repo, sender, discardLogger())
|
|
ctx := context.Background()
|
|
|
|
if _, err := svc.Update(ctx, true, "token", "12345", true); err != nil {
|
|
t.Fatalf("Update() error = %v", err)
|
|
}
|
|
|
|
svc.NotifyNewOrder(ctx, "new order!")
|
|
if len(sender.sent) != 1 || sender.sent[0] != "new order!" {
|
|
t.Fatalf("NotifyNewOrder() sent = %v, want exactly one message \"new order!\"", sender.sent)
|
|
}
|
|
}
|
|
|
|
func TestService_SendTestMessage_RequiresConfiguredCredentials(t *testing.T) {
|
|
svc := telegram.NewService(newFakeRepository(), &fakeSender{}, discardLogger())
|
|
if err := svc.SendTestMessage(context.Background()); err == nil {
|
|
t.Fatal("SendTestMessage() error = nil, want error when bot token/chat id are not configured")
|
|
}
|
|
}
|