Files
Bookra/apps/backend/internal/bookings/activity_test.go
T
Tomas DvorakandDevin d86cba61e3 feat(activity): per-tenant audit feed in notification dropdown
activity_events records booking lifecycle transitions with the actor
behind each change — staff (dashboard), customer (self-service), or
system (auto-promotion). The notification dropdown leads with the real
feed instead of derived recent-booking guesses.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:06:13 +02:00

93 lines
2.9 KiB
Go

package bookings
import (
"context"
"testing"
"bookra/apps/backend/internal/db"
)
const activityTenantID = "5d6b3551-0a3e-4b86-bdf0-e9df20a47148"
var activitySessionID = "4bf74c12-44dd-45ca-86bb-b104f16f2435"
func seedSessionBooking(t *testing.T, repo *db.MemoryRepository, name, status string) db.BookingRecord {
t.Helper()
created, err := repo.CreateBooking(context.Background(), db.CreateBookingParams{
TenantID: activityTenantID,
ClassSessionID: &activitySessionID,
CustomerName: name,
CustomerEmail: name + "@example.com",
Status: status,
Reference: "BK-" + name,
ManageToken: "tok-" + name,
})
if err != nil {
t.Fatalf("seed booking: %v", err)
}
booking, err := repo.GetBookingByReference(context.Background(), created.Reference)
if err != nil {
t.Fatalf("get booking: %v", err)
}
return booking
}
func TestCustomerCancelWritesActivityEvent(t *testing.T) {
repo := db.NewMemoryRepository()
booking := newBookingFixture(t, repo)
svc := NewCustomerService(repo, nil, "http://localhost:3000")
if err := svc.CancelBooking(context.Background(), booking.Reference, "secret-manage-token"); err != nil {
t.Fatalf("cancel booking: %v", err)
}
events, err := repo.ListActivityEvents(context.Background(), activityTenantID, 1)
if err != nil {
t.Fatalf("list activity: %v", err)
}
if len(events) != 1 {
t.Fatalf("expected 1 event, got %d", len(events))
}
event := events[0]
if event.Action != "booking.cancelled" {
t.Fatalf("expected booking.cancelled, got %s", event.Action)
}
if event.ActorKind != "customer" || event.ActorName != "Test Customer" {
t.Fatalf("expected customer actor Test Customer, got %s %q", event.ActorKind, event.ActorName)
}
if event.EntityID == nil || *event.EntityID != booking.ID {
t.Fatalf("expected entity id %s, got %v", booking.ID, event.EntityID)
}
}
func TestPromoteWaitlistWritesSystemEvent(t *testing.T) {
repo := db.NewMemoryRepository()
// Demo session capacity is 4 — three confirmed leaves exactly one seat.
for _, name := range []string{"fill-a", "fill-b", "fill-c"} {
seedSessionBooking(t, repo, name, "confirmed")
}
waiting := seedSessionBooking(t, repo, "queued", "waitlisted")
promoted, err := PromoteWaitlist(context.Background(), repo, activityTenantID, activitySessionID)
if err != nil {
t.Fatalf("promote waitlist: %v", err)
}
if len(promoted) != 1 || promoted[0].ID != waiting.ID {
t.Fatalf("expected queued booking promoted, got %v", promoted)
}
events, err := repo.ListActivityEvents(context.Background(), activityTenantID, 10)
if err != nil {
t.Fatalf("list activity: %v", err)
}
for _, event := range events {
if event.Action == "booking.promoted" && event.ActorKind == "system" {
if event.EntityID == nil || *event.EntityID != waiting.ID {
t.Fatalf("expected entity id %s, got %v", waiting.ID, event.EntityID)
}
return
}
}
t.Fatal("expected system booking.promoted event")
}