spazio-solazzo/test/spazio_solazzo/booking_system/booking_test.exs
Víctor Martínez 2cbce8ec39
refactor: make phone number optional (#9)
This pull request makes phone numbers optional for both user registrations and bookings, and updates validation, database schema, and UI to reflect this change. The main focus is to ensure that users are no longer required to provide a phone number, and that the application gracefully handles cases where a phone number is absent.

**Database & Resource Model Updates**

* Made the `phone_number` attribute in the `users` table and the `customer_phone` attribute in the `bookings` table nullable, including migration and resource snapshot updates. [[1]](diffhunk://#diff-baa6aed3674c4d6cbbebeafb076662df02dc4c25231dbd9dc9c8f0534ed1a1bfR1-R29) [[2]](diffhunk://#diff-a401f66b2ae5bfb798eb1bc2221bfeeac943e258950c90d59570b0bae05d3664R1-R244) [[3]](diffhunk://#diff-0c1180d6f6abc19b5987c8703bdee9ef67905535202f950e8327c32bd5b89d8aR1-R82)
* Updated Ash resource definitions in `user.ex` and `booking.ex` to allow `phone_number` and `customer_phone` to be `nil`. [[1]](diffhunk://#diff-9194b9d80dce091f6dcb56f784217272ae160e35454c4b4ccc8850ad5ee06e38L152-R152) [[2]](diffhunk://#diff-4b1ddd6d86899f2144c69d142883b8719c755e32c03dbda5da2188208a5ad503L55-R55) [[3]](diffhunk://#diff-4b1ddd6d86899f2144c69d142883b8719c755e32c03dbda5da2188208a5ad503L170-R170)

**Validation & Parsing Logic**

* Renamed and refactored user registration field validation to `ParseRegistrationFields`, allowing phone numbers to be omitted and trimming input values. Empty phone numbers are now treated as absent rather than as errors. [[1]](diffhunk://#diff-8ffdd76e260e3cda6f0816c8e585ae76b993a90d2519c38185a5fe22b4b49e47L1-R1) [[2]](diffhunk://#diff-8ffdd76e260e3cda6f0816c8e585ae76b993a90d2519c38185a5fe22b4b49e47R14-R60)
* Updated the authentication callback logic to trim input values and omit the phone number parameter if it is blank.

**User Interface Improvements**

* Updated registration and booking forms to indicate that phone numbers are optional, removed the required attribute, and improved placeholder text. [[1]](diffhunk://#diff-f356eb84970d8c9ee6ff1992c297b0cae07bade37ff967c1e6e0de6f8b67081cL101-R115) [[2]](diffhunk://#diff-43c0e1f7a869ee5c43a911bc10dc80cbb265a8672340ef0fa7c1d3009c047f02L92-R92)
* Updated email templates and confirmation screens to display "N/A" or "-" when phone numbers are missing. [[1]](diffhunk://#diff-48468ef2d1bb2c33b5ffb40457b77532815c7faf1830932661f665bff58b2177R6-R11) [[2]](diffhunk://#diff-3f33187b4021450b481ce53fe13166addea582c627f2cfbc99c75c7ce5c34857L10-R10) [[3]](diffhunk://#diff-43c0e1f7a869ee5c43a911bc10dc80cbb265a8672340ef0fa7c1d3009c047f02L92-R92)

**Profile Management**

* Improved profile update flow to ensure the form reflects the latest user data after saving changes.
* Made the "Full Name" field explicitly required in the profile form UI.
2026-01-15 15:57:54 +01:00

234 lines
6.1 KiB
Elixir

defmodule SpazioSolazzo.BookingSystem.BookingTest do
use ExUnit.Case, async: true
use SpazioSolazzo.DataCase
alias SpazioSolazzo.BookingSystem
alias SpazioSolazzo.BookingSystem.Booking
alias SpazioSolazzo.BookingSystem.Booking.EmailWorker
setup do
{:ok, space} = BookingSystem.create_space("Test", "test2", "desc")
{:ok, asset} = BookingSystem.create_asset("Table 1", space.id)
{:ok, time_slot} =
BookingSystem.create_time_slot_template(
~T[09:00:00],
~T[18:00:00],
:monday,
space.id
)
user = register_user("test@example.com")
%{space: space, asset: asset, time_slot: time_slot, user: user}
end
test "it can create a booking from a time slot template", %{
asset: asset,
time_slot: time_slot,
user: user
} do
{:ok, booking} =
BookingSystem.create_booking(
time_slot.id,
asset.id,
user.id,
Date.utc_today(),
"John",
"john@example.com",
"+393627384027",
"test"
)
assert booking.start_time == time_slot.start_time
assert booking.end_time == time_slot.end_time
assert booking.state == :reserved
assert booking.user_id == user.id
end
test "it sends a confirmation email after the booking is created", %{
asset: asset,
time_slot: time_slot,
user: user
} do
{:ok, booking} =
BookingSystem.create_booking(
time_slot.id,
asset.id,
user.id,
Date.utc_today(),
"John",
"john@example.com",
"+393627384027",
"test"
)
formatted_date = Calendar.strftime(booking.date, "%A, %B %d")
assert_enqueued worker: EmailWorker,
args: %{
"booking_id" => booking.id,
"customer_name" => booking.customer_name,
"customer_email" => booking.customer_email,
"date" => formatted_date,
"start_time" => booking.start_time,
"end_time" => booking.end_time
}
end
test "it can confirm a booking was paid", %{asset: asset, time_slot: time_slot, user: user} do
{:ok, booking} =
BookingSystem.create_booking(
time_slot.id,
asset.id,
user.id,
Date.utc_today(),
"John",
"john@example.com",
"+393627384027",
"test"
)
assert booking.state == :reserved
assert {:ok, booking} = BookingSystem.confirm_booking(booking)
assert booking.state == :completed
end
test "it can cancel a booking", %{asset: asset, time_slot: time_slot, user: user} do
{:ok, booking} =
BookingSystem.create_booking(
time_slot.id,
asset.id,
user.id,
Date.utc_today(),
"John",
"john@example.com",
"+393627384027",
"test"
)
assert booking.state == :reserved
assert {:ok, booking} = BookingSystem.cancel_booking(booking)
assert booking.state == :cancelled
end
test "it can list asset bookings by date", %{
asset: asset,
space: space,
time_slot: time_slot,
user: user
} do
{:ok, asset2} = BookingSystem.create_asset("Table 2", space.id)
{:ok, asset3} = BookingSystem.create_asset("Table 3", space.id)
today_date = Date.utc_today()
{:ok, time_slot2} =
BookingSystem.create_time_slot_template(~T[13:00:00], ~T[18:00:00], :tuesday, space.id)
{:ok, time_slot3} =
BookingSystem.create_time_slot_template(~T[09:00:00], ~T[13:00:00], :tuesday, space.id)
# Create the bookings we want to query
assert {:ok, _} =
BookingSystem.create_booking(
time_slot2.id,
asset.id,
user.id,
today_date,
"John",
"john@example.com",
"+393627384027",
"test"
)
assert {:ok, _} =
BookingSystem.create_booking(
time_slot3.id,
asset.id,
user.id,
today_date,
"John",
"john@example.com",
"+393627384027",
"test"
)
# Create bookings for asset but another date
assert {:ok, _} =
BookingSystem.create_booking(
time_slot2.id,
asset.id,
user.id,
Date.add(today_date, 1),
"John",
"john@example.com",
"+393627384027",
"test"
)
# Create bookings for other assets
assert {:ok, _} =
BookingSystem.create_booking(
time_slot.id,
asset2.id,
user.id,
today_date,
"John",
"john@example.com",
"+393627384027",
"test"
)
assert {:ok, _} =
BookingSystem.create_booking(
time_slot.id,
asset3.id,
user.id,
today_date,
"John",
"john@example.com",
"+393627384027",
"test"
)
assert {:ok, bookings} =
BookingSystem.list_active_asset_bookings_by_date(asset.id, Date.utc_today())
asset_id = asset.id
assert [
%Booking{date: ^today_date, asset_id: ^asset_id},
%Booking{date: ^today_date, asset_id: ^asset_id}
] = bookings
end
test "booking belongs to the user who created it", %{
asset: asset,
time_slot: time_slot,
user: user
} do
{:ok, booking} =
BookingSystem.create_booking(
time_slot.id,
asset.id,
user.id,
Date.utc_today(),
user.name,
user.email,
user.phone_number,
"test comment"
)
assert booking.user_id == user.id
# Load the booking with the user relationship
{:ok, booking_with_user} = Ash.load(booking, :user, authorize?: false)
assert booking_with_user.user.id == user.id
assert booking_with_user.user.email == user.email
assert booking_with_user.user.name == user.name
end
end