2024-01-18 20:05:30 +00:00
|
|
|
-- Test booking
|
|
|
|
set client_min_messages to warning;
|
|
|
|
create extension if not exists pgtap;
|
|
|
|
reset client_min_messages;
|
|
|
|
|
|
|
|
begin;
|
|
|
|
|
Allow many campsites for each reservation
This is actually only used for plots, but, of course, it means that
every booking now can potentially have many booked campsites, and have
to create a relation for it.
I now have a conundrum regarding stay dates: i need them to be in the
same table as the campsite_id, because constraints only work on a single
relation and without the dates i can not make sure that i am not
overbooking a given campsite; but, on the other hand, all campsites
under the same booking must be for the same dates.
Where does stay belong, then? In booking or booking_campsite? If in
booking then i can not have a constraint that most assuredly will bite
me in the back, but if in booking_campsite then each campsite could
potentially have different dates.
As far as i can see, i can not use a exclude constraint with <> for
dates in booking_campsite to ensure that all rows with the same
booking_id have the same stay (i.e., exclude those that have a different
stay for the same booking_id).
For now, the say is in **both** relations: in booking, because i need it
when it is a prebooking, at least, and in booking_campsite for the
aforementioned constraint requirements.
Will this come back and bite me? Yes, it will. But what can i do?
2024-04-21 19:28:41 +00:00
|
|
|
select plan(78);
|
2024-01-18 20:05:30 +00:00
|
|
|
|
|
|
|
set search_path to camper, public;
|
|
|
|
|
|
|
|
select has_table('booking');
|
|
|
|
select has_pk('booking');
|
|
|
|
select table_privs_are('booking', 'guest', array[]::text[]);
|
|
|
|
select table_privs_are('booking', 'employee', array['SELECT', 'INSERT', 'UPDATE']);
|
|
|
|
select table_privs_are('booking', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
|
|
|
select table_privs_are('booking', 'authenticator', array[]::text[]);
|
|
|
|
|
|
|
|
select has_column('booking', 'booking_id');
|
|
|
|
select col_is_pk('booking', 'booking_id');
|
|
|
|
select col_type_is('booking', 'booking_id', 'integer');
|
|
|
|
select col_not_null('booking', 'booking_id');
|
|
|
|
select col_hasnt_default('booking', 'booking_id');
|
|
|
|
|
|
|
|
select has_column('booking', 'company_id');
|
|
|
|
select col_is_fk('booking', 'company_id');
|
|
|
|
select fk_ok('booking', 'company_id', 'company', 'company_id');
|
|
|
|
select col_type_is('booking', 'company_id', 'integer');
|
|
|
|
select col_not_null('booking', 'company_id');
|
|
|
|
select col_hasnt_default('booking', 'company_id');
|
|
|
|
|
|
|
|
select has_column('booking', 'slug');
|
|
|
|
select col_is_unique('booking', 'slug');
|
|
|
|
select col_type_is('booking', 'slug', 'uuid');
|
|
|
|
select col_not_null('booking', 'slug');
|
|
|
|
select col_has_default('booking', 'slug');
|
|
|
|
select col_default_is('booking', 'slug', 'gen_random_uuid()');
|
|
|
|
|
|
|
|
select has_column('booking', 'campsite_type_id');
|
|
|
|
select col_is_fk('booking', 'campsite_type_id');
|
|
|
|
select fk_ok('booking', 'campsite_type_id', 'campsite_type', 'campsite_type_id');
|
|
|
|
select col_type_is('booking', 'campsite_type_id', 'integer');
|
|
|
|
select col_not_null('booking', 'campsite_type_id');
|
|
|
|
select col_hasnt_default('booking', 'campsite_type_id');
|
|
|
|
|
|
|
|
select has_column('booking', 'holder_name');
|
|
|
|
select col_type_is('booking', 'holder_name', 'text');
|
|
|
|
select col_not_null('booking', 'holder_name');
|
|
|
|
select col_hasnt_default('booking', 'holder_name');
|
|
|
|
|
2024-04-19 19:09:28 +00:00
|
|
|
select has_column('booking', 'stay');
|
|
|
|
select col_type_is('booking', 'stay', 'daterange');
|
|
|
|
select col_not_null('booking', 'stay');
|
|
|
|
select col_hasnt_default('booking', 'stay');
|
2024-01-18 20:05:30 +00:00
|
|
|
|
|
|
|
select has_column('booking', 'number_dogs');
|
|
|
|
select col_type_is('booking', 'number_dogs', 'integer');
|
|
|
|
select col_not_null('booking', 'number_dogs');
|
|
|
|
select col_hasnt_default('booking', 'number_dogs');
|
|
|
|
|
|
|
|
select has_column('booking', 'acsi_card');
|
|
|
|
select col_type_is('booking', 'acsi_card', 'boolean');
|
|
|
|
select col_not_null('booking', 'acsi_card');
|
|
|
|
select col_hasnt_default('booking', 'acsi_card');
|
|
|
|
|
|
|
|
select has_column('booking', 'booking_status');
|
|
|
|
select col_is_fk('booking', 'booking_status');
|
|
|
|
select fk_ok('booking', 'booking_status', 'booking_status', 'booking_status');
|
|
|
|
select col_type_is('booking', 'booking_status', 'text');
|
|
|
|
select col_not_null('booking', 'booking_status');
|
|
|
|
select col_has_default('booking', 'booking_status');
|
|
|
|
select col_default_is('booking', 'booking_status', 'created');
|
|
|
|
|
|
|
|
select has_column('booking', 'created_at');
|
|
|
|
select col_type_is('booking', 'created_at', 'timestamp with time zone');
|
|
|
|
select col_not_null('booking', 'created_at');
|
|
|
|
select col_has_default('booking', 'created_at');
|
|
|
|
select col_default_is('booking', 'created_at', 'CURRENT_TIMESTAMP');
|
|
|
|
|
|
|
|
|
|
|
|
set client_min_messages to warning;
|
|
|
|
truncate booking cascade;
|
|
|
|
truncate campsite_type cascade;
|
|
|
|
truncate media cascade;
|
|
|
|
truncate media_content cascade;
|
|
|
|
truncate company_host cascade;
|
|
|
|
truncate company_user cascade;
|
|
|
|
truncate company cascade;
|
|
|
|
truncate auth."user" cascade;
|
|
|
|
reset client_min_messages;
|
|
|
|
|
|
|
|
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
|
|
|
values (1, 'employee2@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
|
|
|
, (3, 'admin2@tandem.blog', 'Admin', 'test', '6d8215c4888ffac017c3e4b8438e9a1a5559decd719df9c790', current_timestamp + interval '1 month')
|
|
|
|
, (5, 'employee4@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
|
|
|
;
|
|
|
|
|
2024-02-27 18:45:47 +00:00
|
|
|
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
|
|
|
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
|
|
|
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
2024-01-18 20:05:30 +00:00
|
|
|
;
|
|
|
|
|
|
|
|
insert into company_user (company_id, user_id, role)
|
|
|
|
values (2, 1, 'employee')
|
|
|
|
, (2, 3, 'admin')
|
|
|
|
, (4, 5, 'employee')
|
|
|
|
;
|
|
|
|
|
|
|
|
insert into company_host (company_id, host)
|
|
|
|
values (2, 'co2')
|
|
|
|
, (4, 'co4')
|
|
|
|
;
|
|
|
|
|
|
|
|
insert into media_content (media_type, bytes)
|
|
|
|
values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
|
|
|
;
|
|
|
|
|
|
|
|
insert into media (media_id, company_id, original_filename, content_hash)
|
|
|
|
values (6, 2, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
|
|
|
, (8, 4, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};'))
|
|
|
|
;
|
|
|
|
|
Add campsite_type_pet_cost relation to hold price of dogs in campsites
It is a separate relation, instead of having a field in campsite_type,
because not all campsite types allow dogs. I could have added a new
field to campsite_type, but then its values it would be meaningless for
campsites that do not allow dogs, and a nullable field is not a valid
solution because NULL means “unknown”, but we **do** know the price —
none.
A separate relation encodes the same information without ambiguities nor
null values, and, in fact, removed the dogs_allowed field from
campsite_type to prevent erroneous status, such as a campsite type that
allows dogs without having a cost — even if the cost is zero, it has to
be added to the new relation.
2024-02-10 05:18:30 +00:00
|
|
|
insert into campsite_type (campsite_type_id, company_id, name, media_id, max_campers, bookable_nights)
|
|
|
|
values (10, 2, 'Wooden lodge', 6, 7, '[1, 7]')
|
|
|
|
, (12, 4, 'Bungalow', 8, 6, '[2, 6]')
|
2024-01-18 20:05:30 +00:00
|
|
|
;
|
|
|
|
|
2024-04-19 19:09:28 +00:00
|
|
|
insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card)
|
|
|
|
values (2, 10, 'Holder 2', daterange('2024-01-18', '2024-01-19'), 0, false)
|
|
|
|
, (4, 12, 'Holder 4', daterange('2024-01-18', '2024-01-19'), 0, false)
|
2024-01-18 20:05:30 +00:00
|
|
|
;
|
|
|
|
|
|
|
|
prepare booking_data as
|
|
|
|
select company_id, holder_name
|
|
|
|
from booking
|
|
|
|
order by company_id, holder_name;
|
|
|
|
|
|
|
|
|
|
|
|
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/employee4@tandem.blog', 'co4');
|
|
|
|
|
|
|
|
select bag_eq(
|
|
|
|
'booking_data',
|
|
|
|
$$ values (4, 'Holder 4')
|
|
|
|
$$,
|
|
|
|
'Should only list bookings from second company'
|
|
|
|
);
|
|
|
|
|
|
|
|
reset role;
|
|
|
|
|
|
|
|
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/employee2@tandem.blog', 'co2');
|
|
|
|
|
|
|
|
select bag_eq(
|
|
|
|
'booking_data',
|
|
|
|
$$ values (2, 'Holder 2')
|
|
|
|
$$,
|
|
|
|
'Should only list bookings from first company'
|
|
|
|
);
|
|
|
|
|
|
|
|
select lives_ok(
|
2024-04-19 19:09:28 +00:00
|
|
|
$$ insert into booking(company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, 'New Holder', daterange('2024-01-18', '2024-01-19'), 0, false) $$,
|
2024-01-18 20:05:30 +00:00
|
|
|
'Users from company 2 should be able to insert a new booking type to their company.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select bag_eq(
|
|
|
|
'booking_data',
|
|
|
|
$$ values (2, 'Holder 2')
|
|
|
|
, (2, 'New Holder')
|
|
|
|
$$,
|
|
|
|
'The new row should have been added'
|
|
|
|
);
|
|
|
|
|
|
|
|
select lives_ok(
|
|
|
|
$$ update booking set holder_name = 'Another Holder' where company_id = 2 and holder_name = 'New Holder' $$,
|
|
|
|
'Users from company 2 should be able to update bookins of their company.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select bag_eq(
|
|
|
|
'booking_data',
|
|
|
|
$$ values (2, 'Holder 2')
|
|
|
|
, (2, 'Another Holder')
|
|
|
|
$$,
|
|
|
|
'The row should have been updated.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
2024-04-19 19:09:28 +00:00
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (4, 12, 'Another holder', daterange('2024-01-18', '2024-01-19'), 0, false) $$,
|
2024-01-18 20:05:30 +00:00
|
|
|
'42501', 'new row violates row-level security policy for table "booking"',
|
|
|
|
'Users from company 2 should NOT be able to insert new bookings to company 4.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select lives_ok(
|
|
|
|
$$ update booking set holder_name = 'Nope' where company_id = 4 $$,
|
|
|
|
'Users from company 2 should not be able to update new campsite types of company 4, but no error if company_id is not changed.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select bag_eq(
|
|
|
|
'booking_data',
|
|
|
|
$$ values (2, 'Holder 2')
|
|
|
|
, (2, 'Another Holder')
|
|
|
|
$$,
|
|
|
|
'No row should have been changed.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
|
|
|
$$ update booking set company_id = 4 where company_id = 2 $$,
|
|
|
|
'42501', 'new row violates row-level security policy for table "booking"',
|
|
|
|
'Users from company 2 should NOT be able to move bookings to company 4'
|
|
|
|
);
|
|
|
|
|
|
|
|
reset role;
|
|
|
|
|
|
|
|
|
|
|
|
select set_cookie('6d8215c4888ffac017c3e4b8438e9a1a5559decd719df9c790/admin2@tandem.blog', 'co2');
|
|
|
|
|
|
|
|
select lives_ok(
|
|
|
|
$$ delete from booking where company_id = 2 and holder_name = 'Another Holder' $$,
|
|
|
|
'Admins from company 2 should be able to delete campsite type from their company.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select bag_eq(
|
|
|
|
'booking_data',
|
|
|
|
$$ values (2, 'Holder 2')
|
|
|
|
$$,
|
|
|
|
'The row should have been deleted.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select lives_ok(
|
|
|
|
$$ delete from booking where company_id = 4 $$,
|
|
|
|
'Admins from company 2 should NOT be able to delete bookins from company 4, but not error is thrown'
|
|
|
|
);
|
|
|
|
|
|
|
|
reset role;
|
|
|
|
|
|
|
|
select bag_eq(
|
|
|
|
'booking_data',
|
|
|
|
$$ values (2, 'Holder 2')
|
|
|
|
, (4, 'Holder 4')
|
|
|
|
$$,
|
|
|
|
'No row should have been changed'
|
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
2024-04-19 19:09:28 +00:00
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, ' ', daterange('2024-01-18', '2024-01-19'), 0, false) $$,
|
2024-01-18 20:05:30 +00:00
|
|
|
'23514', 'new row for relation "booking" violates check constraint "holder_name_not_empty"',
|
|
|
|
'Should not be able to add bookings with a blank holder name.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
2024-04-19 19:09:28 +00:00
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-18'), 0, false) $$,
|
|
|
|
'23514', 'new row for relation "booking" violates check constraint "stay_not_empty"',
|
|
|
|
'Should not be able to add bookings with an empty stay.'
|
2024-01-18 20:05:30 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
2024-04-19 19:09:28 +00:00
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), -1, false) $$,
|
|
|
|
'23514', 'new row for relation "booking" violates check constraint "number_dogs_nonnegative"',
|
|
|
|
'Should not be able to add bookings owing dogs to holder.'
|
2024-01-18 20:05:30 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
2024-04-19 19:09:28 +00:00
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'confirmed') $$,
|
|
|
|
'23514', 'new row for relation "booking" violates check constraint "booking_needs_campsite"',
|
|
|
|
'Should not be able to confirm bookings without a campsite.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'checked-in') $$,
|
|
|
|
'23514', 'new row for relation "booking" violates check constraint "booking_needs_campsite"',
|
|
|
|
'Should not be able to checke bookings in without a campsite.'
|
|
|
|
);
|
|
|
|
|
|
|
|
select throws_ok(
|
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'invoiced') $$,
|
|
|
|
'23514', 'new row for relation "booking" violates check constraint "booking_needs_campsite"',
|
|
|
|
'Should not be able to invoice bookings without a campsite.'
|
2024-01-18 20:05:30 +00:00
|
|
|
);
|
|
|
|
|
2024-04-19 19:09:28 +00:00
|
|
|
select lives_ok(
|
|
|
|
$$ insert into booking (company_id, campsite_type_id, holder_name, stay, number_dogs, acsi_card, booking_status) values (2, 10, 'Holder', daterange('2024-01-18', '2024-01-19'), 0, false, 'cancelled') $$,
|
|
|
|
'Should be able to cancel bookings even without a campsite.'
|
|
|
|
);
|
|
|
|
|
|
|
|
|
2024-01-18 20:05:30 +00:00
|
|
|
|
|
|
|
select *
|
|
|
|
from finish();
|
|
|
|
|
|
|
|
rollback;
|
|
|
|
|