Files
template-vitrine/backend/test/telegram/service_test.go
T
2026-09-14 20:50:19 +02:00

158 lines
4.8 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
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")
}
}