package repository import ( "context" "errors" "testing" "time" "github.com/jackc/pgx/v5/pgconn" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models" ) func TestBookingRepo_Create_CalculatesTotalPrice(t *testing.T) { if testPool == nil { t.Skip("DATABASE_URL_TEST not set") } cleanTables(t, "bookings", "places", "users") user := insertTestUser(t) rate := 1000 place := insertTestPlace(t, user.ID, &rate) repo := NewBookingRepo(testPool) start := time.Now().Truncate(time.Hour).Add(2 * time.Hour) end := start.Add(3 * time.Hour) booking := &models.Booking{ PlaceID: place.ID, UserID: user.ID, StartTime: start, EndTime: end, } if err := repo.Create(context.Background(), booking); err != nil { t.Fatalf("Create: %v", err) } if booking.ID == "" { t.Fatal("expected non-empty booking ID") } if booking.TotalPrice == nil { t.Fatal("expected TotalPrice to be calculated") } expected := 1000 * 3 // 1000 RUB/h * 3h if *booking.TotalPrice != expected { t.Errorf("TotalPrice = %d, want %d", *booking.TotalPrice, expected) } if booking.Currency != "RUB" { t.Errorf("Currency = %s, want RUB", booking.Currency) } if booking.Status != "pending" { t.Errorf("Status = %s, want pending", booking.Status) } } func TestBookingRepo_Create_NoPriceForNonStudio(t *testing.T) { if testPool == nil { t.Skip("DATABASE_URL_TEST not set") } cleanTables(t, "bookings", "places", "users") user := insertTestUser(t) place := insertTestPlace(t, user.ID, nil) place.Type = "place" repo := NewBookingRepo(testPool) start := time.Now().Truncate(time.Hour).Add(2 * time.Hour) end := start.Add(3 * time.Hour) booking := &models.Booking{ PlaceID: place.ID, UserID: user.ID, StartTime: start, EndTime: end, } if err := repo.Create(context.Background(), booking); err != nil { t.Fatalf("Create: %v", err) } if booking.TotalPrice != nil { t.Errorf("TotalPrice = %d, want nil (place without hourly_rate)", *booking.TotalPrice) } } func TestBookingRepo_Create_PlaceNotFound(t *testing.T) { if testPool == nil { t.Skip("DATABASE_URL_TEST not set") } cleanTables(t, "bookings") repo := NewBookingRepo(testPool) booking := &models.Booking{ PlaceID: "00000000-0000-0000-0000-000000000000", UserID: "00000000-0000-0000-0000-000000000000", StartTime: time.Now(), EndTime: time.Now().Add(1 * time.Hour), } err := repo.Create(context.Background(), booking) if !errors.Is(err, ErrPlaceNotFound) { t.Errorf("expected ErrPlaceNotFound, got %v", err) } } func TestBookingRepo_Create_OverlapRejected(t *testing.T) { if testPool == nil { t.Skip("DATABASE_URL_TEST not set") } cleanTables(t, "bookings", "places", "users") user := insertTestUser(t) rate := 500 place := insertTestPlace(t, user.ID, &rate) repo := NewBookingRepo(testPool) start := time.Now().Truncate(time.Hour).Add(5 * time.Hour) end := start.Add(2 * time.Hour) // First booking succeeds b1 := &models.Booking{ PlaceID: place.ID, UserID: user.ID, StartTime: start, EndTime: end, } if err := repo.Create(context.Background(), b1); err != nil { t.Fatalf("first booking: %v", err) } // Overlapping booking fails with 23P01 (exclusion constraint) b2 := &models.Booking{ PlaceID: place.ID, UserID: user.ID, StartTime: start.Add(30 * time.Minute), EndTime: end.Add(30 * time.Minute), } err := repo.Create(context.Background(), b2) if err == nil { t.Fatal("expected error for overlapping booking, got nil") } var pgErr *pgconn.PgError if !errors.As(err, &pgErr) || pgErr.Code != "23P01" { t.Errorf("expected 23P01 exclusion violation, got: %v", err) } } func TestBookingRepo_Create_SameSlotDifferentPlace(t *testing.T) { if testPool == nil { t.Skip("DATABASE_URL_TEST not set") } cleanTables(t, "bookings", "places", "users") user := insertTestUser(t) rate := 500 p1 := insertTestPlace(t, user.ID, &rate) p2 := insertTestPlace(t, user.ID, &rate) repo := NewBookingRepo(testPool) start := time.Now().Truncate(time.Hour).Add(10 * time.Hour) end := start.Add(2 * time.Hour) b1 := &models.Booking{PlaceID: p1.ID, UserID: user.ID, StartTime: start, EndTime: end} if err := repo.Create(context.Background(), b1); err != nil { t.Fatalf("first booking: %v", err) } b2 := &models.Booking{PlaceID: p2.ID, UserID: user.ID, StartTime: start, EndTime: end} if err := repo.Create(context.Background(), b2); err != nil { t.Fatalf("same slot different place: %v", err) } }