Add Discord event announcement functionality to backend
This commit introduces a new Discord event announcer to the backend, allowing for event announcements via Discord. It includes the addition of new environment variables for Discord configuration in `.env.example` and `compose.yaml`. The `main.go` file has been updated to initialize the announcer, and a new `discord` package has been created, containing the announcer logic and tests. Additionally, the service layer has been modified to support bracket draft management, enhancing the overall event workflow. Integration tests have been updated to ensure proper functionality of the new features.
This commit is contained in:
112
backend/internal/application/service_test.go
Normal file
112
backend/internal/application/service_test.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"mixmaker/backend/internal/domain"
|
||||
)
|
||||
|
||||
type createEventStore struct {
|
||||
Store
|
||||
created domain.Event
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *createEventStore) CreateEvent(_ context.Context, event domain.Event) (domain.Event, error) {
|
||||
s.created = event
|
||||
if s.err != nil {
|
||||
return domain.Event{}, s.err
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
|
||||
type recordingPublisher struct {
|
||||
topics []string
|
||||
}
|
||||
|
||||
func (p *recordingPublisher) Publish(topic string, _ any) {
|
||||
p.topics = append(p.topics, topic)
|
||||
}
|
||||
|
||||
type recordingAnnouncer struct {
|
||||
events []domain.Event
|
||||
err error
|
||||
hasDeadline bool
|
||||
}
|
||||
|
||||
func (a *recordingAnnouncer) AnnounceEventCreated(ctx context.Context, event domain.Event) error {
|
||||
a.events = append(a.events, event)
|
||||
_, a.hasDeadline = ctx.Deadline()
|
||||
return a.err
|
||||
}
|
||||
|
||||
func TestCreateEventAnnouncesAfterPersistence(t *testing.T) {
|
||||
store := &createEventStore{}
|
||||
bus := &recordingPublisher{}
|
||||
announcer := &recordingAnnouncer{}
|
||||
service := New(store, bus, announcer)
|
||||
now := time.Date(2026, time.July, 19, 8, 0, 0, 0, time.UTC)
|
||||
service.Now = func() time.Time { return now }
|
||||
|
||||
created, err := service.CreateEvent(context.Background(), domain.Account{ID: "admin", Role: domain.RoleAdmin}, validEvent(now))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(announcer.events) != 1 {
|
||||
t.Fatalf("expected one announcement, got %d", len(announcer.events))
|
||||
}
|
||||
if announcer.events[0].ID != created.ID || created.ID == "" {
|
||||
t.Fatalf("announced event ID %q does not match created event ID %q", announcer.events[0].ID, created.ID)
|
||||
}
|
||||
if !announcer.hasDeadline {
|
||||
t.Fatal("announcement context must have a timeout")
|
||||
}
|
||||
if len(bus.topics) != 1 || bus.topics[0] != "events" {
|
||||
t.Fatalf("unexpected published topics: %v", bus.topics)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateEventDoesNotAnnouncePersistenceFailure(t *testing.T) {
|
||||
persistenceErr := errors.New("database unavailable")
|
||||
store := &createEventStore{err: persistenceErr}
|
||||
announcer := &recordingAnnouncer{}
|
||||
service := New(store, &recordingPublisher{}, announcer)
|
||||
now := time.Date(2026, time.July, 19, 8, 0, 0, 0, time.UTC)
|
||||
service.Now = func() time.Time { return now }
|
||||
|
||||
_, err := service.CreateEvent(context.Background(), domain.Account{ID: "admin", Role: domain.RoleAdmin}, validEvent(now))
|
||||
if !errors.Is(err, persistenceErr) {
|
||||
t.Fatalf("expected persistence error, got %v", err)
|
||||
}
|
||||
if len(announcer.events) != 0 {
|
||||
t.Fatalf("expected no announcements, got %d", len(announcer.events))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateEventIgnoresAnnouncementFailure(t *testing.T) {
|
||||
announcementErr := errors.New("discord unavailable")
|
||||
announcer := &recordingAnnouncer{err: announcementErr}
|
||||
service := New(&createEventStore{}, &recordingPublisher{}, announcer)
|
||||
now := time.Date(2026, time.July, 19, 8, 0, 0, 0, time.UTC)
|
||||
service.Now = func() time.Time { return now }
|
||||
|
||||
created, err := service.CreateEvent(context.Background(), domain.Account{ID: "admin", Role: domain.RoleAdmin}, validEvent(now))
|
||||
if err != nil {
|
||||
t.Fatalf("announcement failure must not fail event creation: %v", err)
|
||||
}
|
||||
if created.ID == "" || len(announcer.events) != 1 {
|
||||
t.Fatalf("event was not created and announced exactly once: event=%+v announcements=%d", created, len(announcer.events))
|
||||
}
|
||||
}
|
||||
|
||||
func validEvent(now time.Time) domain.Event {
|
||||
return domain.Event{
|
||||
Name: "Sunday Mix",
|
||||
Description: "Community scrim",
|
||||
StartsAt: now.Add(24 * time.Hour),
|
||||
EndsAt: now.Add(28 * time.Hour),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user