first
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
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
|
||||
r.settings.NotifyStatusChange = s.NotifyStatusChange
|
||||
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, false); err != nil {
|
||||
t.Fatalf("first Update() error = %v", err)
|
||||
}
|
||||
|
||||
// Flip a flag without resending the token.
|
||||
settings, err := svc.Update(ctx, true, "", "12345", false, true)
|
||||
if err != nil {
|
||||
t.Fatalf("second Update() error = %v", err)
|
||||
}
|
||||
if !settings.NotifyStatusChange || 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, false); 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, 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 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, 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 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, false); 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user