mirror of
https://codeberg.org/JasterV/spazio-solazzo.git
synced 2026-04-26 18:20:03 +00:00
738 lines
20 KiB
Elixir
738 lines
20 KiB
Elixir
defmodule SpazioSolazzo.BookingSystem.BookingTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
use SpazioSolazzo.DataCase
|
|
alias SpazioSolazzo.BookingSystem
|
|
|
|
setup do
|
|
{:ok, space} =
|
|
BookingSystem.create_space(
|
|
"Test Space",
|
|
"test-space",
|
|
"Test description",
|
|
2
|
|
)
|
|
|
|
user = register_user("testuser@example.com", "Test User")
|
|
|
|
date = ~D[2026-02-10]
|
|
|
|
%{space: space, user: user, date: date}
|
|
end
|
|
|
|
describe "create_booking" do
|
|
test "creates a booking request successfully", %{space: space, date: date} do
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
"+39 1234567890",
|
|
"Test booking"
|
|
)
|
|
|
|
assert booking.space_id == space.id
|
|
assert booking.user_id == nil
|
|
assert booking.date == date
|
|
assert booking.start_time == ~T[09:00:00]
|
|
assert booking.end_time == ~T[10:00:00]
|
|
assert booking.customer_name == "John Doe"
|
|
assert booking.customer_email == "john@example.com"
|
|
assert booking.customer_phone == "+39 1234567890"
|
|
assert booking.customer_comment == "Test booking"
|
|
assert booking.state == :requested
|
|
end
|
|
|
|
test "creates booking with authenticated user", %{space: space, user: user, date: date} do
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
user.id,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
user.email,
|
|
"+39 1234567890",
|
|
""
|
|
)
|
|
|
|
assert booking.user_id == user.id
|
|
assert to_string(booking.customer_email) == to_string(user.email)
|
|
end
|
|
|
|
test "rejects booking with end time before start time", %{space: space, date: date} do
|
|
assert {:error, error} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[10:00:00],
|
|
~T[09:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "must be after start_time")
|
|
end
|
|
|
|
test "rejects booking in the past", %{space: space} do
|
|
past_date = Date.add(Date.utc_today(), -1)
|
|
|
|
assert {:error, error} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
past_date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "cannot be in the past")
|
|
end
|
|
|
|
test "allows booking for today", %{space: space} do
|
|
today = Date.utc_today()
|
|
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
today,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
assert booking.date == today
|
|
end
|
|
|
|
test "requires customer name and email", %{space: space, date: date} do
|
|
assert {:error, _error} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"",
|
|
"",
|
|
"",
|
|
""
|
|
)
|
|
end
|
|
|
|
test "phone number is optional", %{space: space, date: date} do
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
assert booking.customer_phone == nil || booking.customer_phone == ""
|
|
end
|
|
end
|
|
|
|
describe "approve_booking/1" do
|
|
test "approves a pending booking", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
assert booking.state == :requested
|
|
|
|
{:ok, approved_booking} = BookingSystem.approve_booking(booking.id)
|
|
|
|
assert approved_booking.state == :accepted
|
|
assert approved_booking.id == booking.id
|
|
end
|
|
|
|
test "cannot approve already approved booking", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(booking.id)
|
|
|
|
assert {:error, error} = BookingSystem.approve_booking(booking.id)
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "no matching transition")
|
|
end
|
|
|
|
test "cannot approve cancelled booking", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.cancel_booking(booking.id, "Test cancellation")
|
|
|
|
assert {:error, error} = BookingSystem.approve_booking(booking.id)
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "no matching transition")
|
|
end
|
|
end
|
|
|
|
describe "cancel_booking/1" do
|
|
test "cancels a pending booking", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
{:ok, cancelled_booking} = BookingSystem.cancel_booking(booking.id, "Test cancellation")
|
|
|
|
assert cancelled_booking.state == :cancelled
|
|
assert cancelled_booking.id == booking.id
|
|
end
|
|
|
|
test "cancels an approved booking", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(booking.id)
|
|
{:ok, cancelled_booking} = BookingSystem.cancel_booking(booking.id, "Test cancellation")
|
|
|
|
assert cancelled_booking.state == :cancelled
|
|
end
|
|
|
|
test "cannot cancel already cancelled booking", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"John Doe",
|
|
"john@example.com",
|
|
nil,
|
|
nil
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.cancel_booking(booking.id, "Test cancellation")
|
|
|
|
assert {:error, error} = BookingSystem.cancel_booking(booking.id, "Test cancellation")
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "no matching transition")
|
|
end
|
|
end
|
|
|
|
describe "search_bookings/5 for accepted bookings" do
|
|
test "returns only approved bookings for specific date", %{space: space, date: date} do
|
|
{:ok, approved1} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 1",
|
|
"user1@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(approved1.id)
|
|
|
|
{:ok, approved2} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[10:00:00],
|
|
~T[11:00:00],
|
|
"User 2",
|
|
"user2@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(approved2.id)
|
|
|
|
{:ok, _pending} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[11:00:00],
|
|
~T[12:00:00],
|
|
"User 3",
|
|
"user3@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
start_datetime = DateTime.new!(date, ~T[00:00:00], "Etc/UTC")
|
|
end_datetime = DateTime.new!(Date.add(date, 1), ~T[00:00:00], "Etc/UTC")
|
|
|
|
{:ok, bookings} =
|
|
BookingSystem.search_bookings(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
[:accepted],
|
|
nil
|
|
)
|
|
|
|
assert length(bookings) == 2
|
|
assert Enum.all?(bookings, &(&1.state == :accepted))
|
|
end
|
|
|
|
test "does not return cancelled bookings", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 1",
|
|
"user1@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(booking.id)
|
|
{:ok, _} = BookingSystem.cancel_booking(booking.id, "Test cancellation")
|
|
|
|
start_datetime = DateTime.new!(date, ~T[00:00:00], "Etc/UTC")
|
|
end_datetime = DateTime.new!(Date.add(date, 1), ~T[00:00:00], "Etc/UTC")
|
|
|
|
{:ok, bookings} =
|
|
BookingSystem.search_bookings(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
[:accepted],
|
|
nil
|
|
)
|
|
|
|
assert bookings == []
|
|
end
|
|
|
|
test "only returns bookings for specified date", %{space: space, date: date} do
|
|
other_date = Date.add(date, 1)
|
|
|
|
{:ok, booking1} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 1",
|
|
"user1@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(booking1.id)
|
|
|
|
{:ok, booking2} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
other_date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 2",
|
|
"user2@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(booking2.id)
|
|
|
|
start_datetime = DateTime.new!(date, ~T[00:00:00], "Etc/UTC")
|
|
end_datetime = DateTime.new!(Date.add(date, 1), ~T[00:00:00], "Etc/UTC")
|
|
|
|
{:ok, bookings} =
|
|
BookingSystem.search_bookings(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
[:accepted],
|
|
nil
|
|
)
|
|
|
|
assert length(bookings) == 1
|
|
assert hd(bookings).date == date
|
|
end
|
|
|
|
test "only returns bookings for specified space", %{space: space, date: date} do
|
|
{:ok, other_space} =
|
|
BookingSystem.create_space(
|
|
"Other Space",
|
|
"other-space",
|
|
"Other description",
|
|
5
|
|
)
|
|
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
other_space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 1",
|
|
"user1@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(booking.id)
|
|
|
|
start_datetime = DateTime.new!(date, ~T[00:00:00], "Etc/UTC")
|
|
end_datetime = DateTime.new!(Date.add(date, 1), ~T[00:00:00], "Etc/UTC")
|
|
|
|
{:ok, bookings} =
|
|
BookingSystem.search_bookings(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
[:accepted],
|
|
nil
|
|
)
|
|
|
|
assert bookings == []
|
|
end
|
|
end
|
|
|
|
describe "count pending requests" do
|
|
test "returns only pending bookings", %{space: space, date: date} do
|
|
{:ok, _pending1} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 1",
|
|
"user1@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, approved} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[10:00:00],
|
|
~T[11:00:00],
|
|
"User 2",
|
|
"user2@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(approved.id)
|
|
|
|
{:ok, cancelled} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[11:00:00],
|
|
~T[12:00:00],
|
|
"User 3",
|
|
"user3@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.cancel_booking(cancelled.id, "Test cancellation")
|
|
|
|
{:ok, count} =
|
|
Ash.count(SpazioSolazzo.BookingSystem.Booking,
|
|
query: [filter: [state: :requested]]
|
|
)
|
|
|
|
assert count == 1
|
|
end
|
|
|
|
test "returns zero when no pending requests", %{space: space, date: date} do
|
|
{:ok, booking} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 1",
|
|
"user1@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _} = BookingSystem.approve_booking(booking.id)
|
|
|
|
{:ok, count} =
|
|
Ash.count(SpazioSolazzo.BookingSystem.Booking,
|
|
query: [filter: [state: :requested]]
|
|
)
|
|
|
|
assert count == 0
|
|
end
|
|
|
|
test "counts pending requests across multiple spaces", %{space: space, date: date} do
|
|
{:ok, other_space} =
|
|
BookingSystem.create_space(
|
|
"Other Space",
|
|
"other-space-pending",
|
|
"Other description",
|
|
5
|
|
)
|
|
|
|
{:ok, _pending1} =
|
|
BookingSystem.create_booking(
|
|
space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 1",
|
|
"user1@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, _pending2} =
|
|
BookingSystem.create_booking(
|
|
other_space.id,
|
|
nil,
|
|
date,
|
|
~T[09:00:00],
|
|
~T[10:00:00],
|
|
"User 2",
|
|
"user2@example.com",
|
|
"",
|
|
""
|
|
)
|
|
|
|
{:ok, count} =
|
|
Ash.count(SpazioSolazzo.BookingSystem.Booking,
|
|
query: [filter: [state: :requested]]
|
|
)
|
|
|
|
assert count == 2
|
|
end
|
|
end
|
|
|
|
describe "create_walk_in/7" do
|
|
test "creates a walk-in booking with accepted state", %{space: space} do
|
|
start_datetime = DateTime.utc_now() |> DateTime.add(1, :hour)
|
|
end_datetime = DateTime.add(start_datetime, 2, :hour)
|
|
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_walk_in(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
"Walk-in Customer",
|
|
"walkin@example.com",
|
|
"+39 1234567890"
|
|
)
|
|
|
|
assert booking.space_id == space.id
|
|
assert booking.customer_name == "Walk-in Customer"
|
|
assert booking.customer_email == "walkin@example.com"
|
|
assert booking.customer_phone == "+39 1234567890"
|
|
assert booking.state == :accepted
|
|
assert booking.date == DateTime.to_date(start_datetime)
|
|
# Compare times ignoring microseconds
|
|
expected_start = DateTime.to_time(start_datetime)
|
|
expected_end = DateTime.to_time(end_datetime)
|
|
|
|
assert booking.start_time.hour == expected_start.hour
|
|
assert booking.start_time.minute == expected_start.minute
|
|
assert booking.start_time.second == expected_start.second
|
|
|
|
assert booking.end_time.hour == expected_end.hour
|
|
assert booking.end_time.minute == expected_end.minute
|
|
assert booking.end_time.second == expected_end.second
|
|
end
|
|
|
|
test "creates walk-in without optional fields", %{space: space} do
|
|
start_datetime = DateTime.utc_now() |> DateTime.add(1, :hour)
|
|
end_datetime = DateTime.add(start_datetime, 2, :hour)
|
|
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_walk_in(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
"Walk-in Customer",
|
|
"walkin@example.com",
|
|
nil
|
|
)
|
|
|
|
assert booking.customer_phone == nil
|
|
assert booking.customer_comment == nil
|
|
assert booking.state == :accepted
|
|
end
|
|
|
|
test "rejects walk-in with end datetime before start datetime", %{space: space} do
|
|
start_datetime = DateTime.utc_now() |> DateTime.add(2, :hour)
|
|
end_datetime = DateTime.add(start_datetime, -1, :hour)
|
|
|
|
assert {:error, error} =
|
|
BookingSystem.create_walk_in(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
"Walk-in Customer",
|
|
"walkin@example.com",
|
|
nil
|
|
)
|
|
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "must be after start_datetime")
|
|
end
|
|
|
|
test "rejects walk-in with end time in the past", %{space: space} do
|
|
start_datetime = DateTime.utc_now() |> DateTime.add(-2, :hour)
|
|
end_datetime = DateTime.utc_now() |> DateTime.add(-1, :hour)
|
|
|
|
assert {:error, error} =
|
|
BookingSystem.create_walk_in(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
"Walk-in Customer",
|
|
"walkin@example.com",
|
|
nil
|
|
)
|
|
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "cannot be in the past")
|
|
end
|
|
|
|
test "allows walk-in with start time in the past but end time in the future", %{space: space} do
|
|
start_datetime = DateTime.utc_now() |> DateTime.add(-1, :hour)
|
|
end_datetime = DateTime.utc_now() |> DateTime.add(2, :hour)
|
|
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_walk_in(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
"Walk-in Customer",
|
|
"walkin@example.com",
|
|
nil
|
|
)
|
|
|
|
assert booking.state == :accepted
|
|
assert booking.customer_name == "Walk-in Customer"
|
|
end
|
|
|
|
test "rejects walk-in with invalid email", %{space: space} do
|
|
start_datetime = DateTime.utc_now() |> DateTime.add(1, :hour)
|
|
end_datetime = DateTime.add(start_datetime, 2, :hour)
|
|
|
|
assert {:error, error} =
|
|
BookingSystem.create_walk_in(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
"Walk-in Customer",
|
|
"invalid-email",
|
|
nil
|
|
)
|
|
|
|
error_messages = Ash.Error.error_descriptions(error)
|
|
assert String.contains?(error_messages, "must be a valid email")
|
|
end
|
|
|
|
test "converts datetime to date and time correctly", %{space: space} do
|
|
date = Date.add(Date.utc_today(), 1)
|
|
start_time = ~T[14:30:00]
|
|
end_time = ~T[16:45:00]
|
|
|
|
start_datetime = DateTime.new!(date, start_time, "Etc/UTC")
|
|
end_datetime = DateTime.new!(date, end_time, "Etc/UTC")
|
|
|
|
assert {:ok, booking} =
|
|
BookingSystem.create_walk_in(
|
|
space.id,
|
|
start_datetime,
|
|
end_datetime,
|
|
"Walk-in Customer",
|
|
"walkin@example.com",
|
|
nil
|
|
)
|
|
|
|
assert booking.date == date
|
|
assert booking.start_time == start_time
|
|
assert booking.end_time == end_time
|
|
end
|
|
end
|
|
end
|