package bookings import ( "context" "testing" "time" "bookra/apps/backend/internal/db" "bookra/apps/backend/internal/domain" ) func newBookingFixture(t *testing.T, repo *db.MemoryRepository) db.BookingRecord { t.Helper() created, err := repo.CreateBooking(context.Background(), db.CreateBookingParams{ TenantID: "5d6b3551-0a3e-4b86-bdf0-e9df20a47148", CustomerName: "Test Customer", CustomerEmail: "customer@example.com", StartsAt: time.Now().Add(24 * time.Hour), EndsAt: time.Now().Add(25 * time.Hour), Status: "confirmed", Reference: "BOK-TEST-1", ManageToken: "secret-manage-token", }) if err != nil { t.Fatalf("create booking: %v", err) } booking, err := repo.GetBookingByReference(context.Background(), created.Reference) if err != nil { t.Fatalf("get booking: %v", err) } return booking } func TestGetBookingRequiresManageToken(t *testing.T) { repo := db.NewMemoryRepository() booking := newBookingFixture(t, repo) svc := NewCustomerService(repo, nil, "http://localhost:3000") if _, err := svc.GetBookingByReference(context.Background(), booking.Reference, ""); err != ErrUnauthorized { t.Fatalf("expected ErrUnauthorized for missing token, got %v", err) } if _, err := svc.GetBookingByReference(context.Background(), booking.Reference, "wrong"); err != ErrUnauthorized { t.Fatalf("expected ErrUnauthorized for wrong token, got %v", err) } view, err := svc.GetBookingByReference(context.Background(), booking.Reference, "secret-manage-token") if err != nil { t.Fatalf("expected authorized view, got %v", err) } if view.Reference != booking.Reference { t.Fatalf("expected reference %s, got %s", booking.Reference, view.Reference) } } func TestRescheduleRequiresManageToken(t *testing.T) { repo := db.NewMemoryRepository() booking := newBookingFixture(t, repo) svc := NewCustomerService(repo, nil, "http://localhost:3000") req := domain.RescheduleBookingRequest{ NewStartsAt: time.Now().Add(48 * time.Hour).Format(time.RFC3339), NewEndsAt: time.Now().Add(49 * time.Hour).Format(time.RFC3339), } if err := svc.RescheduleBooking(context.Background(), booking.Reference, req, ""); err != ErrUnauthorized { t.Fatalf("expected ErrUnauthorized, got %v", err) } if err := svc.RescheduleBooking(context.Background(), booking.Reference, req, "wrong"); err != ErrUnauthorized { t.Fatalf("expected ErrUnauthorized, got %v", err) } } func TestCancelRequiresManageToken(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, ""); err != ErrUnauthorized { t.Fatalf("expected ErrUnauthorized, got %v", err) } if err := svc.CancelBooking(context.Background(), booking.Reference, "wrong"); err != ErrUnauthorized { t.Fatalf("expected ErrUnauthorized, got %v", err) } if err := svc.CancelBooking(context.Background(), booking.Reference, "secret-manage-token"); err != nil { t.Fatalf("expected successful cancel, got %v", err) } } func TestRescheduleRejectsConflictingSlot(t *testing.T) { repo := db.NewMemoryRepository() svc := NewCustomerService(repo, nil, "http://localhost:3000") base := time.Now().Add(48 * time.Hour) // Existing booking in the same tenant occupying the target slot. _, err := repo.CreateBooking(context.Background(), db.CreateBookingParams{ TenantID: "5d6b3551-0a3e-4b86-bdf0-e9df20a47148", StartsAt: base, EndsAt: base.Add(time.Hour), Status: "confirmed", Reference: "BOK-OCCUPIED", ManageToken: "token-a", }) if err != nil { t.Fatalf("create occupying booking: %v", err) } booking := newBookingFixture(t, repo) err = svc.RescheduleBooking(context.Background(), booking.Reference, domain.RescheduleBookingRequest{ NewStartsAt: base.Add(30 * time.Minute).Format(time.RFC3339), // overlaps BOK-OCCUPIED NewEndsAt: base.Add(90 * time.Minute).Format(time.RFC3339), }, "secret-manage-token") if err != ErrBookingConflict { t.Fatalf("expected ErrBookingConflict, got %v", err) } }