{{( pgettext "Features" "title" )}}
+-
+ {{ range . -}}
+
- {{ .Name }} + {{- end }} +
diff --git a/demo/demo.sql b/demo/demo.sql index fdea1a0..12dd57f 100644 --- a/demo/demo.sql +++ b/demo/demo.sql @@ -1501,4 +1501,20 @@ values (52, 72, 'Juli Verd', current_date + interval '23 days', current_date + i , (52, 76, 'Hortènsia Grisa', current_date + interval '29 days', current_date + interval '34 days', 0, false, 'invoiced') ; + +alter table amenity alter column amenity_id restart with 132; +select add_amenity(52, 'edifici-camping', 'Edifici Càmping', '
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec scelerisque lorem vestibulum enim sollicitudin ornare. Aliquam egestas pretium porttitor. Donec iaculis tempus est, id lobortis risus semper vel. Maecenas ut imperdiet neque. Donec mattis purus felis, vitae interdum risus egestas pharetra. Vestibulum dui neque, condimentum ultrices erat sed, fringilla pharetra ante. Maecenas hendrerit neque mattis risus consectetur euismod. Cras urna metus, bibendum a neque sed, pharetra commodo magna.
', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec scelerisque lorem vestibulum enim sollicitudin ornare. Aliquam egestas pretium porttitor. Donec iaculis tempus est, id lobortis risus semper vel. Maecenas ut imperdiet neque. Donec mattis purus felis, vitae interdum risus egestas pharetra. Vestibulum dui neque, condimentum ultrices erat sed, fringilla pharetra ante. Maecenas hendrerit neque mattis risus consectetur euismod. Cras urna metus, bibendum a neque sed, pharetra commodo magna.
'); + +select add_amenity_carousel_slide(52, 'edifici-camping', 80, 'Llegenda'); +select add_amenity_carousel_slide(52, 'edifici-camping', 81, 'Llegenda'); +select add_amenity_carousel_slide(52, 'edifici-camping', 82, 'Llegenda'); +select add_amenity_carousel_slide(52, 'edifici-camping', 83, 'Llegenda'); +select add_amenity_carousel_slide(52, 'edifici-camping', 84, 'Llegenda'); +select add_amenity_carousel_slide(52, 'edifici-camping', 85, 'Llegenda'); + +select add_amenity_feature(52, 'edifici-camping', 'person', 'Máx. 6 pers.'); +select add_amenity_feature(52, 'edifici-camping', 'area', 'de 55 a 65 m²'); +select add_amenity_feature(52, 'edifici-camping', 'electricity', 'Electricitat'); +select add_amenity_feature(52, 'edifici-camping', 'shower', 'Accés als serveis'); + commit; diff --git a/deploy/add_amenity.sql b/deploy/add_amenity.sql new file mode 100644 index 0000000..70b33a2 --- /dev/null +++ b/deploy/add_amenity.sql @@ -0,0 +1,23 @@ +-- Deploy camper:add_amenity to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity + +begin; + +set search_path to camper, public; + +create or replace function add_amenity(company_id integer, label text, name text, info1 text, info2 text) returns integer as +$$ + insert into amenity (company_id, label, name, info1, info2) + values (company_id, label, name, xmlparse(content info1), xmlparse(content info2)) + returning amenity_id + ; +$$ + language sql +; + +revoke execute on function add_amenity(integer, text, text, text, text) from public; +grant execute on function add_amenity(integer, text, text, text, text) to admin; + +commit; diff --git a/deploy/add_amenity_carousel_slide.sql b/deploy/add_amenity_carousel_slide.sql new file mode 100644 index 0000000..c9fa153 --- /dev/null +++ b/deploy/add_amenity_carousel_slide.sql @@ -0,0 +1,32 @@ +-- Deploy camper:add_amenity_carousel_slide to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity +-- requires: amenity_carousel + +begin; + +set search_path to camper, public; + +create or replace function add_amenity_carousel_slide(company_id integer, label text, media_id integer, caption text) returns integer as +$$ + insert into amenity_carousel (amenity_id, media_id, caption) + select amenity_id, media_id, coalesce(caption, '') + from amenity + where label = add_amenity_carousel_slide.label + and company_id = add_amenity_carousel_slide.company_id + union all + select -1, 1, '' + limit 1 + on conflict (amenity_id, media_id) do update + set caption = excluded.caption + returning amenity_id + ; +$$ + language sql +; + +revoke execute on function add_amenity_carousel_slide(integer, text, integer, text) from public; +grant execute on function add_amenity_carousel_slide(integer, text, integer, text) to admin; + +commit; diff --git a/deploy/add_amenity_feature.sql b/deploy/add_amenity_feature.sql new file mode 100644 index 0000000..df7992e --- /dev/null +++ b/deploy/add_amenity_feature.sql @@ -0,0 +1,30 @@ +-- Deploy camper:add_amenity_feature to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_feature +-- requires: amenity + +begin; + +set search_path to camper, public; + +create or replace function add_amenity_feature(company_id integer, amenity_label text, icon_name text, name text) returns integer as +$$ + insert into amenity_feature (amenity_id, icon_name, name) + select amenity_id, icon_name, add_amenity_feature.name + from amenity + where label = amenity_label + and company_id = add_amenity_feature.company_id + union all + select -1, 'baby', 'name' + limit 1 + returning amenity_feature_id + ; +$$ + language sql +; + +revoke execute on function add_amenity_feature(integer, text, text, text) from public; +grant execute on function add_amenity_feature(integer, text, text, text) to admin; + +commit; diff --git a/deploy/amenity.sql b/deploy/amenity.sql new file mode 100644 index 0000000..08556f1 --- /dev/null +++ b/deploy/amenity.sql @@ -0,0 +1,58 @@ +-- Deploy camper:amenity to pg +-- requires: roles +-- requires: schema_camper +-- requires: company +-- requires: user_profile + +begin; + +set search_path to camper, public; + +create table amenity ( + amenity_id integer generated by default as identity primary key, + company_id integer not null references company, + label text not null constraint label_not_empty check(length(trim(label)) > 0), + name text not null constraint name_not_empty check(length(trim(name)) > 0), + info1 xml not null default '', + info2 xml not null default '', + active boolean not null default true, + unique (company_id, label) +); + +grant select on table amenity to guest; +grant select on table amenity to employee; +grant select, insert, update, delete on table amenity to admin; + +alter table amenity enable row level security; + +create policy guest_ok +on amenity +for select +using (true) +; + +create policy insert_to_company +on amenity +for insert +with check ( + company_id in (select company_id from user_profile) +) +; + +create policy update_company +on amenity +for update +using ( + company_id in (select company_id from user_profile) +) +; + +create policy delete_from_company +on amenity +for delete +using ( + company_id in (select company_id from user_profile) +) +; + +commit; diff --git a/deploy/amenity_carousel.sql b/deploy/amenity_carousel.sql new file mode 100644 index 0000000..b1d2885 --- /dev/null +++ b/deploy/amenity_carousel.sql @@ -0,0 +1,56 @@ +-- Deploy camper:amenity_carousel to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity +-- requires: media +-- requires: user_profile + +begin; + +set search_path to camper, public; + +create table amenity_carousel ( + amenity_id integer not null references amenity, + media_id integer not null references media, + caption text not null, + position integer not null default 2147483647, + primary key (amenity_id, media_id) +); + +grant select on table amenity_carousel to guest; +grant select on table amenity_carousel to employee; +grant select, insert, update, delete on table amenity_carousel to admin; + +alter table amenity_carousel enable row level security; + +create policy guest_ok +on amenity_carousel +for select +using (true) +; + +create policy insert_to_company +on amenity_carousel +for insert +with check ( + exists (select 1 from amenity join media using (company_id) join user_profile using (company_id) where amenity.amenity_id = amenity_carousel.amenity_id and media.media_id = amenity_carousel.media_id) +) +; + +create policy update_company +on amenity_carousel +for update +using ( + exists (select 1 from amenity join media using (company_id) join user_profile using (company_id) where amenity.amenity_id = amenity_carousel.amenity_id and media.media_id = amenity_carousel.media_id) +) +; + +create policy delete_from_company +on amenity_carousel +for delete +using ( + exists (select 1 from amenity join media using (company_id) join user_profile using (company_id) where amenity.amenity_id = amenity_carousel.amenity_id and media.media_id = amenity_carousel.media_id) +) +; + +commit; diff --git a/deploy/amenity_carousel_i18n.sql b/deploy/amenity_carousel_i18n.sql new file mode 100644 index 0000000..14be918 --- /dev/null +++ b/deploy/amenity_carousel_i18n.sql @@ -0,0 +1,24 @@ +-- Deploy camper:amenity_carousel_i18n to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_carousel +-- requires: language + +begin; + +set search_path to camper, public; + +create table amenity_carousel_i18n ( + amenity_id integer not null, + media_id integer not null, + lang_tag text not null references language, + caption text, + primary key (amenity_id, media_id, lang_tag), + foreign key (amenity_id, media_id) references amenity_carousel +); + +grant select on table amenity_carousel_i18n to guest; +grant select on table amenity_carousel_i18n to employee; +grant select, insert, update, delete on table amenity_carousel_i18n to admin; + +commit; diff --git a/deploy/amenity_feature.sql b/deploy/amenity_feature.sql new file mode 100644 index 0000000..20c8c9e --- /dev/null +++ b/deploy/amenity_feature.sql @@ -0,0 +1,56 @@ +-- Deploy camper:amenity_feature to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity +-- requires: icon +-- requires: user_profile + +begin; + +set search_path to camper, public; + +create table amenity_feature ( + amenity_feature_id integer generated by default as identity primary key, + amenity_id integer not null references amenity, + icon_name text not null references icon, + name text not null constraint name_not_empty check(length(trim(name)) > 0), + position integer not null default 2147483647 +); + +grant select on table amenity_feature to guest; +grant select on table amenity_feature to employee; +grant select, insert, update, delete on table amenity_feature to admin; + +alter table amenity_feature enable row level security; + +create policy guest_ok +on amenity_feature +for select +using (true) +; + +create policy insert_to_company +on amenity_feature +for insert +with check ( + exists (select 1 from amenity join user_profile using (company_id) where amenity.amenity_id = amenity_feature.amenity_id) +) +; + +create policy update_company +on amenity_feature +for update +using ( + exists (select 1 from amenity join user_profile using (company_id) where amenity.amenity_id = amenity_feature.amenity_id) +) +; + +create policy delete_from_company +on amenity_feature +for delete +using ( + exists (select 1 from amenity join user_profile using (company_id) where amenity.amenity_id = amenity_feature.amenity_id) +) +; + +commit; diff --git a/deploy/amenity_feature_i18n.sql b/deploy/amenity_feature_i18n.sql new file mode 100644 index 0000000..cfba081 --- /dev/null +++ b/deploy/amenity_feature_i18n.sql @@ -0,0 +1,22 @@ +-- Deploy camper:amenity_feature_i18n to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_feature +-- requires: language + +begin; + +set search_path to camper, public; + +create table amenity_feature_i18n ( + amenity_feature_id integer not null references amenity_feature, + lang_tag text not null references language, + name text, + primary key (amenity_feature_id, lang_tag) +); + +grant select on table amenity_feature_i18n to guest; +grant select on table amenity_feature_i18n to employee; +grant select, insert, update, delete on table amenity_feature_i18n to admin; + +commit; diff --git a/deploy/amenity_i18n.sql b/deploy/amenity_i18n.sql new file mode 100644 index 0000000..0a4a048 --- /dev/null +++ b/deploy/amenity_i18n.sql @@ -0,0 +1,24 @@ +-- Deploy camper:amenity_i18n to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity +-- requires: language + +begin; + +set search_path to camper, public; + +create table amenity_i18n ( + amenity_id integer not null references amenity, + lang_tag text not null references language, + name text, + info1 xml, + info2 xml, + primary key (amenity_id, lang_tag) +); + +grant select on amenity_i18n to guest; +grant select on amenity_i18n to employee; +grant select, insert, update, delete on amenity_i18n to admin; + +commit; diff --git a/deploy/edit_amenity.sql b/deploy/edit_amenity.sql new file mode 100644 index 0000000..6fd6867 --- /dev/null +++ b/deploy/edit_amenity.sql @@ -0,0 +1,27 @@ +-- Deploy camper:edit_amenity to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity + +begin; + +set search_path to camper, public; + +create or replace function edit_amenity(amenity_id integer, new_label text, new_name text, new_info1 text, new_info2 text, active boolean) returns integer as +$$ + update amenity + set label = new_label + , name = new_name + , info1 = xmlparse(content new_info1) + , info2 = xmlparse(content new_info2) + , active = edit_amenity.active + where amenity_id = edit_amenity.amenity_id + returning amenity_id; +$$ + language sql +; + +revoke execute on function edit_amenity(integer, text, text, text, text, boolean) from public; +grant execute on function edit_amenity(integer, text, text, text, text, boolean) to admin; + +commit; diff --git a/deploy/edit_amenity_feature.sql b/deploy/edit_amenity_feature.sql new file mode 100644 index 0000000..4860d14 --- /dev/null +++ b/deploy/edit_amenity_feature.sql @@ -0,0 +1,25 @@ +-- Deploy camper:edit_amenity_feature to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_feature + +begin; + +set search_path to camper, public; + +create or replace function edit_amenity_feature(feature_id integer, icon_name text, name text) returns integer as +$$ + update amenity_feature + set icon_name = edit_amenity_feature.icon_name + , name = edit_amenity_feature.name + where amenity_feature_id = feature_id + returning amenity_feature_id + ; +$$ + language sql +; + +revoke execute on function edit_amenity_feature(integer, text, text) from public; +grant execute on function edit_amenity_feature(integer, text, text) to admin; + +commit; diff --git a/deploy/order_amenity_carousel.sql b/deploy/order_amenity_carousel.sql new file mode 100644 index 0000000..9844771 --- /dev/null +++ b/deploy/order_amenity_carousel.sql @@ -0,0 +1,27 @@ +-- Deploy camper:order_amenity_carousel to pg +-- requires: roles +-- requires: amenity_carousel +-- requires: amenity + +begin; + +set search_path to camper, public; + +create or replace function order_amenity_carousel(label text, company_id integer, positions integer[]) returns void as +$$ + update amenity_carousel + set position = cast(temp.position as integer) + from unnest(positions) with ordinality as temp(media_id, position) + join amenity on amenity.label = order_amenity_carousel.label + and amenity.company_id = order_amenity_carousel.company_id + where amenity_carousel.amenity_id = amenity.amenity_id + and amenity_carousel.media_id = temp.media_id + ; +$$ + language sql +; + +revoke execute on function order_amenity_carousel(text, integer, integer[]) from public; +grant execute on function order_amenity_carousel(text, integer, integer[]) to admin; + +commit; diff --git a/deploy/order_amenity_features.sql b/deploy/order_amenity_features.sql new file mode 100644 index 0000000..6495053 --- /dev/null +++ b/deploy/order_amenity_features.sql @@ -0,0 +1,24 @@ +-- Deploy camper:order_amenity_features to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_feature + +begin; + +set search_path to camper, public; + +create or replace function order_amenity_features(positions integer[]) returns void as +$$ + update amenity_feature + set position = cast(temp.position as integer) + from unnest(positions) with ordinality as temp(feature_id, position) + where amenity_feature_id = temp.feature_id + ; +$$ + language sql +; + +revoke execute on function order_amenity_features(integer[]) from public; +grant execute on function order_amenity_features(integer[]) to admin; + +commit; diff --git a/deploy/remove_amenity.sql b/deploy/remove_amenity.sql new file mode 100644 index 0000000..595d046 --- /dev/null +++ b/deploy/remove_amenity.sql @@ -0,0 +1,50 @@ +-- Deploy camper:remove_amenity to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity +-- requires: amenity_i18n +-- requires: amenity_carousel +-- requires: amenity_carousel_i18n +-- requires: amenity_feature +-- requires: amenity_feature_i18n + +begin; + +set search_path to camper, public; + +create or replace function remove_amenity(amenity_id integer) returns void as +$$ + delete from amenity_feature_i18n + where amenity_feature_id in ( + select amenity_feature_id + from amenity_feature + where amenity_id = remove_amenity.amenity_id + ); + + delete from amenity_feature + where amenity_id = remove_amenity.amenity_id + ; + + delete from amenity_carousel_i18n + where amenity_id = remove_amenity.amenity_id + ; + + delete from amenity_carousel + where amenity_id = remove_amenity.amenity_id + ; + + delete from amenity_i18n + where amenity_id = remove_amenity.amenity_id + ; + + delete from amenity + where amenity_id = remove_amenity.amenity_id + ; +$$ + language sql +; + +revoke execute on function remove_amenity(integer) from public; +grant execute on function remove_amenity(integer) to admin; + +commit; diff --git a/deploy/remove_amenity_carousel_slide.sql b/deploy/remove_amenity_carousel_slide.sql new file mode 100644 index 0000000..a3723a3 --- /dev/null +++ b/deploy/remove_amenity_carousel_slide.sql @@ -0,0 +1,40 @@ +-- Deploy camper:remove_amenity_carousel_slide to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_carousel +-- requires: amenity_carousel_i18n + +begin; + +set search_path to camper, public; + +create or replace function remove_amenity_carousel_slide(company_id integer, label text, media_id integer) returns void as +$$ +declare + csid integer; +begin + select amenity_id + into csid + from amenity + where amenity.label = remove_amenity_carousel_slide.label + and amenity.company_id = remove_amenity_carousel_slide.company_id + ; + + delete from amenity_carousel_i18n + where amenity_id = csid + and amenity_carousel_i18n.media_id = remove_amenity_carousel_slide.media_id + ; + + delete from amenity_carousel + where amenity_id = csid + and amenity_carousel.media_id = remove_amenity_carousel_slide.media_id + ; +end +$$ + language plpgsql +; + +revoke execute on function remove_amenity_carousel_slide(integer, text, integer) from public; +grant execute on function remove_amenity_carousel_slide(integer, text, integer) to admin; + +commit; diff --git a/deploy/remove_amenity_feature.sql b/deploy/remove_amenity_feature.sql new file mode 100644 index 0000000..a2557af --- /dev/null +++ b/deploy/remove_amenity_feature.sql @@ -0,0 +1,22 @@ +-- Deploy camper:remove_amenity_feature to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_feature +-- requires: amenity_feature_i18n + +begin; + +set search_path to camper, public; + +create or replace function remove_amenity_feature(feature_id integer) returns void as +$$ + delete from amenity_feature_i18n where amenity_feature_id = feature_id; + delete from amenity_feature where amenity_feature_id = feature_id; +$$ + language sql +; + +revoke execute on function remove_amenity_feature(integer) from public; +grant execute on function remove_amenity_feature(integer) to admin; + +commit; diff --git a/deploy/translate_amenity.sql b/deploy/translate_amenity.sql new file mode 100644 index 0000000..6cf72a7 --- /dev/null +++ b/deploy/translate_amenity.sql @@ -0,0 +1,27 @@ +-- Deploy camper:translate_amenity to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_i18n + +begin; + +set search_path to camper, public; + +create or replace function translate_amenity(amenity_id integer, lang_tag text, name text, info1 text, info2 text) returns void as +$$ + insert into amenity_i18n (amenity_id, lang_tag, name, info1, info2) + values (amenity_id, lang_tag, case trim(name) when '' then null else name end, case trim(info1) when '' then null else xmlparse(content info1) end, case trim(info2) when '' then null else xmlparse(content info2) end) + on conflict (amenity_id, lang_tag) + do update + set name = excluded.name + , info1 = excluded.info1 + , info2 = excluded.info2 + ; +$$ + language sql +; + +revoke execute on function translate_amenity(integer, text, text, text, text) from public; +grant execute on function translate_amenity(integer, text, text, text, text) to admin; + +commit; diff --git a/deploy/translate_amenity_carousel_slide.sql b/deploy/translate_amenity_carousel_slide.sql new file mode 100644 index 0000000..eaa92d6 --- /dev/null +++ b/deploy/translate_amenity_carousel_slide.sql @@ -0,0 +1,28 @@ +-- Deploy camper:translate_amenity_carousel_slide to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity +-- requires: amenity_carousel_i18n + +begin; + +set search_path to camper, public; + +create or replace function translate_amenity_carousel_slide(company_id integer, label text, media_id integer, lang_tag text, caption text) returns void as +$$ + insert into amenity_carousel_i18n (amenity_id, media_id, lang_tag, caption) + select amenity_id, translate_amenity_carousel_slide.media_id, lang_tag, case trim(caption) when '' then null else caption end + from amenity + where label = translate_amenity_carousel_slide.label + and company_id = translate_amenity_carousel_slide.company_id + on conflict (amenity_id, media_id, lang_tag) do update + set caption = excluded.caption + ; +$$ + language sql +; + +revoke execute on function translate_amenity_carousel_slide(integer, text, integer, text, text) from public; +grant execute on function translate_amenity_carousel_slide(integer, text, integer, text, text) to admin; + +commit; diff --git a/deploy/translate_amenity_feature.sql b/deploy/translate_amenity_feature.sql new file mode 100644 index 0000000..00638fc --- /dev/null +++ b/deploy/translate_amenity_feature.sql @@ -0,0 +1,24 @@ +-- Deploy camper:translate_amenity_feature to pg +-- requires: roles +-- requires: schema_camper +-- requires: amenity_feature_i18n + +begin; + +set search_path to camper, public; + +create or replace function translate_amenity_feature(feature_id integer, lang_tag text, name text) returns void as +$$ + insert into amenity_feature_i18n (amenity_feature_id, lang_tag, name) + values (feature_id, lang_tag, case trim(name) when '' then null else name end) + on conflict (amenity_feature_id, lang_tag) do update + set name = excluded.name + ; +$$ + language sql +; + +revoke execute on function translate_amenity_feature(integer, text, text) from public; +grant execute on function translate_amenity_feature(integer, text, text) to admin; + +commit; diff --git a/pkg/amenity/admin.go b/pkg/amenity/admin.go new file mode 100644 index 0000000..8c86e10 --- /dev/null +++ b/pkg/amenity/admin.go @@ -0,0 +1,289 @@ +/* + * SPDX-FileCopyrightText: 2023 jordi fita masA1.1
', 'A1.2
') $$, + 'Should be able to add a amenity to the first company' +); + +select lives_ok( + $$ select add_amenity(1, 'B1', 'Amenity B1', 'B1.1
', 'B1.2
') $$, + 'Should be able to add a amenity to the same company, but with a different label' +); + +select lives_ok( + $$ select add_amenity(2, 'C1', 'Amenity C1', 'C1.1
', 'C1.2
') $$, + 'Should be able to add a amenity to the second company' +); + +select throws_ok( + $$ select add_amenity(3, 'C2', 'Amenity C2', '', '') $$, + '23503', 'insert or update on table "amenity" violates foreign key constraint "amenity_company_id_fkey"', + 'Should raise an error if the company is not valid.' +); + +select bag_eq( + $$ select company_id, label, name, info1::text, info2::text, active from amenity $$, + $$ values (1, 'A1', 'Amenity A1', 'A1.1
', 'A1.2
', true) + , (1, 'B1', 'Amenity B1', 'B1.1
', 'B1.2
', true) + , (2, 'C1', 'Amenity C1', 'C1.1
', 'C1.2
', true) + $$, + 'Should have added all amenities' +); + +select * +from finish(); + +select * +from finish(); + +rollback; diff --git a/test/add_amenity_carousel_slide.sql b/test/add_amenity_carousel_slide.sql new file mode 100644 index 0000000..e43f489 --- /dev/null +++ b/test/add_amenity_carousel_slide.sql @@ -0,0 +1,102 @@ +-- Test add_amenity_carousel_slide +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(15); + +set search_path to camper, public; + +select has_function('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text']); +select function_lang_is('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text'], 'sql'); +select function_returns('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text'], 'integer'); +select isnt_definer('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text']); +select volatility_is('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text'], 'volatile'); +select function_privs_are('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'add_amenity_carousel_slide', array['integer', 'text', 'integer', 'text'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_carousel cascade; +truncate amenity cascade; +truncate media cascade; +truncate media_content cascade; +truncate company cascade; +reset client_min_messages; + + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into media_content (media_type, bytes) +values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ff00ff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffff00","a"};') + , ('text/plain', 'hello, world!') + , ('image/svg+xml', '') +; + +insert into media (media_id, company_id, original_filename, content_hash) +values (3, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) + , (4, 2, 'cover3.xpm', sha256('static char *s[]={"1 1 1 1","a c #ff00ff","a"};')) + , (5, 1, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffff00","a"};')) + , (6, 1, 'text.txt', sha256('hello, world!')) + , (7, 1, 'image.svg', sha256('')) +; + +insert into amenity (amenity_id, company_id, label, name) +values (11, 1, 'A1', 'Amenity A1') + , (12, 1, 'Z9', 'Amenity Z9') + , (13, 2, 'A1', 'Amenity A1') +; + +insert into amenity_carousel (amenity_id, media_id, caption) +values (11, 5, 'A caption') +; + +select lives_ok( + $$ select add_amenity_carousel_slide(1, 'A1', 6, 'A caption') $$, + 'Should be able to add a carousel slide with a caption' +); + +select lives_ok( + $$ select add_amenity_carousel_slide(1, 'Z9', 7, null) $$, + 'Should be able to add a carousel slide without caption' +); + +select lives_ok( + $$ select add_amenity_carousel_slide(1, 'A1', 5, 'New caption') $$, + 'Should be able to overwrite a slide with a new caption' +); + +select throws_ok( + $$ select add_amenity_carousel_slide(1, 'A2', 3, '') $$, + '23503', 'insert or update on table "amenity_carousel" violates foreign key constraint "amenity_carousel_amenity_id_fkey"', + 'Should raise an error if the label is not valid.' +); + +select throws_ok( + $$ select add_amenity_carousel_slide(3, 'A1', 3, '') $$, + '23503', 'insert or update on table "amenity_carousel" violates foreign key constraint "amenity_carousel_amenity_id_fkey"', + 'Should raise an error if the company is not valid.' +); + +select bag_eq( + $$ select amenity_id, media_id, caption from amenity_carousel $$, + $$ values (11, 5, 'New caption') + , (11, 6, 'A caption') + , (12, 7, '') + $$, + 'Should have all three slides' +); + +select * +from finish(); + +rollback; diff --git a/test/add_amenity_feature.sql b/test/add_amenity_feature.sql new file mode 100644 index 0000000..f40554e --- /dev/null +++ b/test/add_amenity_feature.sql @@ -0,0 +1,82 @@ +-- Test add_amenity_feature +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(14); + +set search_path to camper, public; + +select has_function('camper', 'add_amenity_feature', array['integer', 'text', 'text', 'text']); +select function_lang_is('camper', 'add_amenity_feature', array['integer', 'text', 'text', 'text'], 'sql'); +select function_returns('camper', 'add_amenity_feature', array['integer', 'text', 'text', 'text'], 'integer'); +select isnt_definer('camper', 'add_amenity_feature', array['integer', 'text', 'text', 'text']); +select volatility_is('camper', 'add_amenity_feature', array['integer', 'text', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'add_amenity_feature', array ['integer', 'text', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'add_amenity_feature', array ['integer', 'text', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'add_amenity_feature', array ['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'add_amenity_feature', array ['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_feature cascade; +truncate amenity cascade; +truncate company cascade; +reset client_min_messages; + + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +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 (3, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) +; + +insert into amenity (amenity_id, company_id, label, name) +values (6, 1, 'A1', 'Amenity A1') + , (7, 2, 'A1', 'Amenity A1') +; + +select lives_ok( + $$ select add_amenity_feature(1, 'A1', 'wifi', 'Feature 1') $$, + 'Should be able to add an feature to the first amenity' +); + +select lives_ok( + $$ select add_amenity_feature(2, 'A1', 'information', 'Feature 2') $$, + 'Should be able to add an feature to the second amenity' +); + +select throws_ok( + $$ select add_amenity_feature(1, 'A2', 'baby', 'Nope') $$, + '23503', 'insert or update on table "amenity_feature" violates foreign key constraint "amenity_feature_amenity_id_fkey"', + 'Should raise an error if the label is not valid.' +); + +select throws_ok( + $$ select add_amenity_feature(3, 'A1', 'baby', 'Nope') $$, + '23503', 'insert or update on table "amenity_feature" violates foreign key constraint "amenity_feature_amenity_id_fkey"', + 'Should raise an error if the company is not valid.' +); + + +select bag_eq( + $$ select amenity_id, icon_name, name from amenity_feature $$, + $$ values (6, 'wifi', 'Feature 1') + , (7, 'information', 'Feature 2') + $$, + 'Should have added all two amenity features' +); + +select * +from finish(); + +rollback; diff --git a/test/amenity.sql b/test/amenity.sql new file mode 100644 index 0000000..81af06b --- /dev/null +++ b/test/amenity.sql @@ -0,0 +1,209 @@ +-- Test amenity +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(54); + +set search_path to camper, public; + +select has_table('amenity'); +select has_pk('amenity'); +select col_is_unique('amenity', array['company_id', 'label']); +select table_privs_are('amenity', 'guest', array['SELECT']); +select table_privs_are('amenity', 'employee', array['SELECT']); +select table_privs_are('amenity', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('amenity', 'authenticator', array[]::text[]); + +select has_column('amenity', 'amenity_id'); +select col_is_pk('amenity', 'amenity_id'); +select col_type_is('amenity', 'amenity_id', 'integer'); +select col_not_null('amenity', 'amenity_id'); +select col_hasnt_default('amenity', 'amenity_id'); + +select has_column('amenity', 'company_id'); +select col_is_fk('amenity', 'company_id'); +select fk_ok('amenity', 'company_id', 'company', 'company_id'); +select col_type_is('amenity', 'company_id', 'integer'); +select col_not_null('amenity', 'company_id'); +select col_hasnt_default('amenity', 'company_id'); + +select has_column('amenity', 'label'); +select col_type_is('amenity', 'label', 'text'); +select col_not_null('amenity', 'label'); +select col_hasnt_default('amenity', 'label'); + +select has_column('amenity', 'name'); +select col_type_is('amenity', 'name', 'text'); +select col_not_null('amenity', 'name'); +select col_hasnt_default('amenity', 'name'); + +select has_column('amenity', 'info1'); +select col_type_is('amenity', 'info1', 'xml'); +select col_not_null('amenity', 'info1'); +select col_has_default('amenity', 'info1'); + +select has_column('amenity', 'info2'); +select col_type_is('amenity', 'info2', 'xml'); +select col_not_null('amenity', 'info2'); +select col_has_default('amenity', 'info2'); + +select has_column('amenity', 'active'); +select col_type_is('amenity', 'active', 'boolean'); +select col_not_null('amenity', 'active'); +select col_has_default('amenity', 'active'); +select col_default_is('amenity', 'active', 'true'); + + +set client_min_messages to warning; +truncate amenity 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, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 'FR', 'USD', 'ca') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'admin') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') +; + +insert into amenity (company_id, label, name) +values (2, 'W1', 'Amenity W1') + , (4, 'B1', 'Amenity B1') +; + +prepare amenity_data as +select company_id, label +from amenity +order by company_id, label; + +set role guest; +select bag_eq( + 'amenity_data', + $$ values (2, 'W1') + , (4, 'B1') + $$, + 'Everyone should be able to list all amenities across all companies' +); +reset role; + +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); + +select lives_ok( + $$ insert into amenity(company_id, label, name) values (2, 'w2', 'Amenity W2') $$, + 'Admin from company 2 should be able to insert a new amenity to that company.' +); + +select bag_eq( + 'amenity_data', + $$ values (2, 'W1') + , (2, 'w2') + , (4, 'B1') + $$, + 'The new row should have been added' +); + +select lives_ok( + $$ update amenity set label = 'W2' where company_id = 2 and label = 'w2' $$, + 'Admin from company 2 should be able to update amenities of that company.' +); + +select bag_eq( + 'amenity_data', + $$ values (2, 'W1') + , (2, 'W2') + , (4, 'B1') + $$, + 'The row should have been updated.' +); + +select lives_ok( + $$ delete from amenity where company_id = 2 and label = 'W2' $$, + 'Admin from company 2 should be able to delete amenities from that company.' +); + +select bag_eq( + 'amenity_data', + $$ values (2, 'W1') + , (4, 'B1') + $$, + 'The row should have been deleted.' +); + +select throws_ok( + $$ insert into amenity (company_id, label) values (4, 'W3' ) $$, + '42501', 'new row violates row-level security policy for table "amenity"', + 'Admin from company 2 should NOT be able to insert new amenities to company 4.' +); + +select lives_ok( + $$ update amenity set label = 'Nope' where company_id = 4 $$, + 'Admin from company 2 should NOT be able to update amenity types of company 4, but no error if company_id is not changed.' +); + +select bag_eq( + 'amenity_data', + $$ values (2, 'W1') + , (4, 'B1') + $$, + 'No row should have been changed.' +); + +select throws_ok( + $$ update amenity set company_id = 4 where company_id = 2 $$, + '42501', 'new row violates row-level security policy for table "amenity"', + 'Admin from company 2 should NOT be able to move amenities to company 4' +); + +select lives_ok( + $$ delete from amenity where company_id = 4 $$, + 'Admin from company 2 should NOT be able to delete amenity types from company 4, but not error is thrown' +); + +select bag_eq( + 'amenity_data', + $$ values (2, 'W1') + , (4, 'B1') + $$, + 'No row should have been changed' +); + +select throws_ok( + $$ insert into amenity (company_id, label, name) values (2, ' ', 'Amenity') $$, + '23514', 'new row for relation "amenity" violates check constraint "label_not_empty"', + 'Should not be able to insert amenities with a blank label.' +); + +select throws_ok( + $$ insert into amenity (company_id, label, name) values (2, 'AB', ' ') $$, + '23514', 'new row for relation "amenity" violates check constraint "name_not_empty"', + 'Should not be able to insert amenities with a blank name.' +); + +reset role; + + +select * +from finish(); + +rollback; + diff --git a/test/amenity_carousel.sql b/test/amenity_carousel.sql new file mode 100644 index 0000000..1c2d1d5 --- /dev/null +++ b/test/amenity_carousel.sql @@ -0,0 +1,230 @@ +-- Test amenity_carousel +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(45); + +set search_path to camper, public; + +select has_table('amenity_carousel'); +select has_pk('amenity_carousel'); +select col_is_pk('amenity_carousel', array['amenity_id', 'media_id']); +select table_privs_are('amenity_carousel', 'guest', array['SELECT']); +select table_privs_are('amenity_carousel', 'employee', array['SELECT']); +select table_privs_are('amenity_carousel', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('amenity_carousel', 'authenticator', array[]::text[]); + +select has_column('amenity_carousel', 'amenity_id'); +select col_is_fk('amenity_carousel', 'amenity_id'); +select fk_ok('amenity_carousel', 'amenity_id', 'amenity', 'amenity_id'); +select col_type_is('amenity_carousel', 'amenity_id', 'integer'); +select col_not_null('amenity_carousel', 'amenity_id'); +select col_hasnt_default('amenity_carousel', 'amenity_id'); + +select has_column('amenity_carousel', 'media_id'); +select col_is_fk('amenity_carousel', 'media_id'); +select fk_ok('amenity_carousel', 'media_id', 'media', 'media_id'); +select col_type_is('amenity_carousel', 'media_id', 'integer'); +select col_not_null('amenity_carousel', 'media_id'); +select col_hasnt_default('amenity_carousel', 'media_id'); + +select has_column('amenity_carousel', 'caption'); +select col_type_is('amenity_carousel', 'caption', 'text'); +select col_not_null('amenity_carousel', 'caption'); +select col_hasnt_default('amenity_carousel', 'caption'); + +select has_column('amenity_carousel', 'position'); +select col_type_is('amenity_carousel', 'position', 'integer'); +select col_not_null('amenity_carousel', 'position'); +select col_has_default('amenity_carousel', 'position'); +select col_default_is('amenity_carousel', 'position', '2147483647'); + + +set client_min_messages to warning; +truncate amenity_carousel cascade; +truncate amenity 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, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 'FR', 'USD', 'ca') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'admin') + , (4, 5, 'admin') +; + +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"};') + , ('text/plain', 'content2') + , ('text/plain', 'content3') + , ('text/plain', 'content4') + , ('text/plain', 'content5') +; + +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"};')) + , ( 7, 2, 'text2.txt', sha256('content2')) + , ( 8, 2, 'text3.txt', sha256('content3')) + , ( 9, 4, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) + , (10, 4, 'text4.txt', sha256('content4')) + , (11, 4, 'text5.txt', sha256('content5')) +; + +insert into amenity (amenity_id, company_id, label, name) +values (23, 2, 'W1', 'Amenity W1') + , (24, 4, 'B1', 'Amenity B1') +; + +insert into amenity_carousel (amenity_id, media_id, caption) +values (23, 7, 'Caption 7') + , (24, 10, 'Caption 10') +; + +prepare carousel_data as +select amenity_id, media_id, caption +from amenity_carousel +; + +set role guest; +select bag_eq( + 'carousel_data', + $$ values (23, 7, 'Caption 7') + , (24, 10, 'Caption 10') + $$, + 'Everyone should be able to list all amenity slides across all companies' +); +reset role; + +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); + +select lives_ok( + $$ insert into amenity_carousel(amenity_id, media_id, caption) values (23, 8, 'Caption 8') $$, + 'Admin from company 2 should be able to insert a new slide to amenitys of that company.' +); + +select bag_eq( + 'carousel_data', + $$ values (23, 7, 'Caption 7') + , (23, 8, 'Caption 8') + , (24, 10, 'Caption 10') + $$, + 'The new row should have been added' +); + +select lives_ok( + $$ update amenity_carousel set caption = 'Caption 8.8' where media_id = 8 $$, + 'Admin from company 2 should be able to update amenity slides of that company.' +); + +select bag_eq( + 'carousel_data', + $$ values (23, 7, 'Caption 7') + , (23, 8, 'Caption 8.8') + , (24, 10, 'Caption 10') + $$, + 'The row should have been updated.' +); + +select lives_ok( + $$ delete from amenity_carousel where media_id = 8 $$, + 'Admin from company 2 should be able to delete amenity slides from that company.' +); + +select bag_eq( + 'carousel_data', + $$ values (23, 7, 'Caption 7') + , (24, 10, 'Caption 10') + $$, + 'The row should have been deleted.' +); + +select throws_ok( + $$ insert into amenity_carousel (amenity_id, media_id, caption) values (23, 10, 'Nope') $$, + '42501', 'new row violates row-level security policy for table "amenity_carousel"', + 'Admin from company 2 should NOT be able to insert a new amenitys slide from a media of company 4.' +); + +select throws_ok( + $$ insert into amenity_carousel (amenity_id, media_id, caption) values (24, 8, 'Nope') $$, + '42501', 'new row violates row-level security policy for table "amenity_carousel"', + 'Admin from company 2 should NOT be able to insert a slide to a amenity of company 4.' +); + +select lives_ok( + $$ update amenity_carousel set caption = 'Nope' where amenity_id = 24 $$, + 'Admin from company 2 should not be able to update slides of amenitys from company 4, but no error if amenity_id or media_id is not changed.' +); + +select lives_ok( + $$ update amenity_carousel set caption = 'Nope' where media_id = 10 $$, + 'Admin from company 2 should not be able to update slides of amenitys from company 4, but no error if amenity_id or media_id is not changed.' +); + +select bag_eq( + 'carousel_data', + $$ values (23, 7, 'Caption 7') + , (24, 10, 'Caption 10') + $$, + 'No row should have been changed.' +); + +select throws_ok( + $$ update amenity_carousel set amenity_id = 24 where amenity_id = 23 $$, + '42501', 'new row violates row-level security policy for table "amenity_carousel"', + 'Admin from company 2 should NOT be able to move slides to amenitys of company 4' +); + +select throws_ok( + $$ update amenity_carousel set media_id = 11 where media_id = 7 $$, + '42501', 'new row violates row-level security policy for table "amenity_carousel"', + 'Admin from company 2 should NOT be able to use media from company 4' +); + +select lives_ok( + $$ delete from amenity_carousel where amenity_id = 24 $$, + 'Admin from company 2 should NOT be able to delete slides of amenitys from company 4, but not error is thrown' +); + +select lives_ok( + $$ delete from amenity_carousel where media_id = 10 $$, + 'Admin from company 2 should NOT be able to delete slides with media from company 4, but not error is thrown' +); + +select bag_eq( + 'carousel_data', + $$ values (23, 7, 'Caption 7') + , (24, 10, 'Caption 10') + $$, + 'No row should have been changed' +); + +reset role; + + +select * +from finish(); + +rollback; + diff --git a/test/amenity_carousel_i18n.sql b/test/amenity_carousel_i18n.sql new file mode 100644 index 0000000..11b0b15 --- /dev/null +++ b/test/amenity_carousel_i18n.sql @@ -0,0 +1,49 @@ +-- Test amenity_carousel_i18n +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(27); + +set search_path to camper, public; + +select has_table('amenity_carousel_i18n'); +select has_pk('amenity_carousel_i18n'); +select col_is_pk('amenity_carousel_i18n', array['amenity_id', 'media_id', 'lang_tag']); +select col_is_fk('amenity_carousel_i18n', array['amenity_id', 'media_id']); +select fk_ok('amenity_carousel_i18n', array['amenity_id', 'media_id'], 'amenity_carousel', array['amenity_id', 'media_id']); +select table_privs_are('amenity_carousel_i18n', 'guest', array['SELECT']); +select table_privs_are('amenity_carousel_i18n', 'employee', array['SELECT']); +select table_privs_are('amenity_carousel_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('amenity_carousel_i18n', 'authenticator', array[]::text[]); + +select has_column('amenity_carousel_i18n', 'amenity_id'); +select col_type_is('amenity_carousel_i18n', 'amenity_id', 'integer'); +select col_not_null('amenity_carousel_i18n', 'amenity_id'); +select col_hasnt_default('amenity_carousel_i18n', 'amenity_id'); + +select has_column('amenity_carousel_i18n', 'media_id'); +select col_type_is('amenity_carousel_i18n', 'media_id', 'integer'); +select col_not_null('amenity_carousel_i18n', 'media_id'); +select col_hasnt_default('amenity_carousel_i18n', 'media_id'); + +select has_column('amenity_carousel_i18n', 'lang_tag'); +select col_is_fk('amenity_carousel_i18n', 'lang_tag'); +select fk_ok('amenity_carousel_i18n', 'lang_tag', 'language', 'lang_tag'); +select col_type_is('amenity_carousel_i18n', 'lang_tag', 'text'); +select col_not_null('amenity_carousel_i18n', 'lang_tag'); +select col_hasnt_default('amenity_carousel_i18n', 'lang_tag'); + +select has_column('amenity_carousel_i18n', 'caption'); +select col_type_is('amenity_carousel_i18n', 'caption', 'text'); +select col_is_null('amenity_carousel_i18n', 'caption'); +select col_hasnt_default('amenity_carousel_i18n', 'caption'); + + +select * +from finish(); + +rollback; + diff --git a/test/amenity_feature.sql b/test/amenity_feature.sql new file mode 100644 index 0000000..d99df37 --- /dev/null +++ b/test/amenity_feature.sql @@ -0,0 +1,199 @@ +-- Test amenity_feature +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(46); + +set search_path to camper, public; + +select has_table('amenity_feature'); +select has_pk('amenity_feature'); +select table_privs_are('amenity_feature', 'guest', array['SELECT']); +select table_privs_are('amenity_feature', 'employee', array['SELECT']); +select table_privs_are('amenity_feature', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('amenity_feature', 'authenticator', array[]::text[]); + +select has_column('amenity_feature', 'amenity_feature_id'); +select col_is_pk('amenity_feature', 'amenity_feature_id'); +select col_type_is('amenity_feature', 'amenity_feature_id', 'integer'); +select col_not_null('amenity_feature', 'amenity_feature_id'); +select col_hasnt_default('amenity_feature', 'amenity_feature_id'); + +select has_column('amenity_feature', 'amenity_id'); +select col_is_fk('amenity_feature', 'amenity_id'); +select fk_ok('amenity_feature', 'amenity_id', 'amenity', 'amenity_id'); +select col_type_is('amenity_feature', 'amenity_id', 'integer'); +select col_not_null('amenity_feature', 'amenity_id'); +select col_hasnt_default('amenity_feature', 'amenity_id'); + +select has_column('amenity_feature', 'icon_name'); +select col_is_fk('amenity_feature', 'icon_name'); +select fk_ok('amenity_feature', 'icon_name', 'icon', 'icon_name'); +select col_type_is('amenity_feature', 'icon_name', 'text'); +select col_not_null('amenity_feature', 'icon_name'); +select col_hasnt_default('amenity_feature', 'icon_name'); + +select has_column('amenity_feature', 'name'); +select col_type_is('amenity_feature', 'name', 'text'); +select col_not_null('amenity_feature', 'name'); +select col_hasnt_default('amenity_feature', 'name'); + +select has_column('amenity_feature', 'position'); +select col_type_is('amenity_feature', 'position', 'integer'); +select col_not_null('amenity_feature', 'position'); +select col_has_default('amenity_feature', 'position'); +select col_default_is('amenity_feature', 'position', '2147483647'); + + +set client_min_messages to warning; +truncate amenity_feature cascade; +truncate amenity 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, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 'FR', 'USD', 'ca') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'admin') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') +; + +insert into amenity (amenity_id, company_id, label, name) +values (26, 2, 'W1', 'Amenity W1') + , (28, 4, 'B1', 'Amenity B1') +; + +insert into amenity_feature (amenity_id, icon_name, name) +values (26, 'information', 'Feature 26.1') + , (28, 'wifi', 'Feature 28.1') +; + +prepare amenity_feature_data as +select amenity_id, name +from amenity_feature +; + +set role guest; +select bag_eq( + 'amenity_feature_data', + $$ values (26, 'Feature 26.1') + , (28, 'Feature 28.1') + $$, + 'Everyone should be able to list all amenity features across all companies' +); +reset role; + +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); + +select lives_ok( + $$ insert into amenity_feature(amenity_id, icon_name, name) values (26, 'castle', 'Feature 26.2') $$, + 'Admin from company 2 should be able to insert a new amenity feature to that company.' +); + +select bag_eq( + 'amenity_feature_data', + $$ values (26, 'Feature 26.1') + , (26, 'Feature 26.2') + , (28, 'Feature 28.1') + $$, + 'The new row should have been added' +); + +select lives_ok( + $$ update amenity_feature set name = 'Feature 26-2' where amenity_id = 26 and name = 'Feature 26.2' $$, + 'Admin from company 2 should be able to update amenity feature of that company.' +); + +select bag_eq( + 'amenity_feature_data', + $$ values (26, 'Feature 26.1') + , (26, 'Feature 26-2') + , (28, 'Feature 28.1') + $$, + 'The row should have been updated.' +); + +select lives_ok( + $$ delete from amenity_feature where amenity_id = 26 and name = 'Feature 26-2' $$, + 'Admin from company 2 should be able to delete amenity feature from that company.' +); + +select bag_eq( + 'amenity_feature_data', + $$ values (26, 'Feature 26.1') + , (28, 'Feature 28.1') + $$, + 'The row should have been deleted.' +); + +select throws_ok( + $$ insert into amenity_feature (amenity_id, icon_name, name) values (28, 'toilet', 'Feature 28.2') $$, + '42501', 'new row violates row-level security policy for table "amenity_feature"', + 'Admin from company 2 should NOT be able to insert new amenity features to company 4.' +); + +select lives_ok( + $$ update amenity_feature set name = 'Feature 28-1' where amenity_id = 28 $$, + 'Admin from company 2 should not be able to update amenitys of company 4, but no error if amenity_id is not changed.' +); + +select bag_eq( + 'amenity_feature_data', + $$ values (26, 'Feature 26.1') + , (28, 'Feature 28.1') + $$, + 'No row should have been changed.' +); + +select throws_ok( + $$ update amenity_feature set amenity_id = 28 where amenity_id = 26 $$, + '42501', 'new row violates row-level security policy for table "amenity_feature"', + 'Admin from company 2 should NOT be able to move amenity feature to one of company 4' +); + +select lives_ok( + $$ delete from amenity_feature where amenity_id = 28 $$, + 'Admin from company 2 should NOT be able to delete amenity from company 4, but not error is thrown' +); + +select bag_eq( + 'amenity_feature_data', + $$ values (26, 'Feature 26.1') + , (28, 'Feature 28.1') + $$, + 'No row should have been changed' +); + +select throws_ok( + $$ insert into amenity_feature (amenity_id, icon_name, name) values (26, 'baby', ' ') $$, + '23514', 'new row for relation "amenity_feature" violates check constraint "name_not_empty"', + 'Should not be able to insert amenity features with a blank name.' +); + +reset role; + + +select * +from finish(); + +rollback; + diff --git a/test/amenity_feature_i18n.sql b/test/amenity_feature_i18n.sql new file mode 100644 index 0000000..b1f560a --- /dev/null +++ b/test/amenity_feature_i18n.sql @@ -0,0 +1,44 @@ +-- Test amenity_feature_i18n +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(23); + +set search_path to camper, public; + +select has_table('amenity_feature_i18n'); +select has_pk('amenity_feature_i18n'); +select col_is_pk('amenity_feature_i18n', array['amenity_feature_id', 'lang_tag']); +select table_privs_are('amenity_feature_i18n', 'guest', array['SELECT']); +select table_privs_are('amenity_feature_i18n', 'employee', array['SELECT']); +select table_privs_are('amenity_feature_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('amenity_feature_i18n', 'authenticator', array[]::text[]); + +select has_column('amenity_feature_i18n', 'amenity_feature_id'); +select col_is_fk('amenity_feature_i18n', 'amenity_feature_id'); +select fk_ok('amenity_feature_i18n', 'amenity_feature_id', 'amenity_feature', 'amenity_feature_id'); +select col_type_is('amenity_feature_i18n', 'amenity_feature_id', 'integer'); +select col_not_null('amenity_feature_i18n', 'amenity_feature_id'); +select col_hasnt_default('amenity_feature_i18n', 'amenity_feature_id'); + +select has_column('amenity_feature_i18n', 'lang_tag'); +select col_is_fk('amenity_feature_i18n', 'lang_tag'); +select fk_ok('amenity_feature_i18n', 'lang_tag', 'language', 'lang_tag'); +select col_type_is('amenity_feature_i18n', 'lang_tag', 'text'); +select col_not_null('amenity_feature_i18n', 'lang_tag'); +select col_hasnt_default('amenity_feature_i18n', 'lang_tag'); + +select has_column('amenity_feature_i18n', 'name'); +select col_type_is('amenity_feature_i18n', 'name', 'text'); +select col_is_null('amenity_feature_i18n', 'name'); +select col_hasnt_default('amenity_feature_i18n', 'name'); + + +select * +from finish(); + +rollback; + diff --git a/test/amenity_i18n.sql b/test/amenity_i18n.sql new file mode 100644 index 0000000..0ad945b --- /dev/null +++ b/test/amenity_i18n.sql @@ -0,0 +1,54 @@ +-- Test amenity_i18n +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(31); + +set search_path to camper, public; + +select has_table('amenity_i18n'); +select has_pk('amenity_i18n'); +select col_is_pk('amenity_i18n', array['amenity_id', 'lang_tag']); +select table_privs_are('amenity_i18n', 'guest', array['SELECT']); +select table_privs_are('amenity_i18n', 'employee', array['SELECT']); +select table_privs_are('amenity_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('amenity_i18n', 'authenticator', array[]::text[]); + +select has_column('amenity_i18n', 'amenity_id'); +select col_is_fk('amenity_i18n', 'amenity_id'); +select fk_ok('amenity_i18n', 'amenity_id', 'amenity', 'amenity_id'); +select col_type_is('amenity_i18n', 'amenity_id', 'integer'); +select col_not_null('amenity_i18n', 'amenity_id'); +select col_hasnt_default('amenity_i18n', 'amenity_id'); + +select has_column('amenity_i18n', 'lang_tag'); +select col_is_fk('amenity_i18n', 'lang_tag'); +select fk_ok('amenity_i18n', 'lang_tag', 'language', 'lang_tag'); +select col_type_is('amenity_i18n', 'lang_tag', 'text'); +select col_not_null('amenity_i18n', 'lang_tag'); +select col_hasnt_default('amenity_i18n', 'lang_tag'); + +select has_column('amenity_i18n', 'name'); +select col_type_is('amenity_i18n', 'name', 'text'); +select col_is_null('amenity_i18n', 'name'); +select col_hasnt_default('amenity_i18n', 'name'); + +select has_column('amenity_i18n', 'info1'); +select col_type_is('amenity_i18n', 'info1', 'xml'); +select col_is_null('amenity_i18n', 'info1'); +select col_hasnt_default('amenity_i18n', 'info1'); + +select has_column('amenity_i18n', 'info2'); +select col_type_is('amenity_i18n', 'info2', 'xml'); +select col_is_null('amenity_i18n', 'info2'); +select col_hasnt_default('amenity_i18n', 'info2'); + + +select * +from finish(); + +rollback; + diff --git a/test/edit_amenity.sql b/test/edit_amenity.sql new file mode 100644 index 0000000..3bef67f --- /dev/null +++ b/test/edit_amenity.sql @@ -0,0 +1,59 @@ +-- Test edit_amenity +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(12); + +set search_path to camper, public; + +select has_function('camper', 'edit_amenity', array['integer', 'text', 'text', 'text', 'text', 'boolean']); +select function_lang_is('camper', 'edit_amenity', array['integer', 'text', 'text', 'text', 'text', 'boolean'], 'sql'); +select function_returns('camper', 'edit_amenity', array['integer', 'text', 'text', 'text', 'text', 'boolean'], 'integer'); +select isnt_definer('camper', 'edit_amenity', array['integer', 'text', 'text', 'text', 'text', 'boolean']); +select volatility_is('camper', 'edit_amenity', array['integer', 'text', 'text', 'text', 'text', 'boolean'], 'volatile'); +select function_privs_are('camper', 'edit_amenity', array ['integer', 'text', 'text', 'text', 'text', 'boolean'], 'guest', array[]::text[]); +select function_privs_are('camper', 'edit_amenity', array ['integer', 'text', 'text', 'text', 'text', 'boolean'], 'employee', array[]::text[]); +select function_privs_are('camper', 'edit_amenity', array ['integer', 'text', 'text', 'text', 'text', 'boolean'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'edit_amenity', array ['integer', 'text', 'text', 'text', 'text', 'boolean'], 'authenticator', array[]::text[]); + +set client_min_messages to warning; +truncate amenity cascade; +truncate company cascade; +reset client_min_messages; + + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into amenity (amenity_id, company_id, label, name, info1, info2, active) +values (21, 1, 'A1', 'Anemity A1', 'A1.1
', 'A1.2
', true) + , (22, 1, 'B1', 'Anemity B1', 'B1.1
', 'B1.2
', false) +; + +select lives_ok( + $$ select edit_amenity(21, 'C1', 'Anemity C1', 'C1.1
', 'C1.2
', false) $$, + 'Should be able to edit the first amenity.' +); + +select lives_ok( + $$ select edit_amenity(22, 'B2', 'Anemity B2', 'B2.1
', 'B2.2
', true) $$, + 'Should be able to edit the second amenity.' +); + +select bag_eq( + $$ select amenity_id, label, name, info1::text, info2::text, active from amenity $$, + $$ values (21, 'C1', 'Anemity C1', 'C1.1
', 'C1.2
', false) + , (22, 'B2', 'Anemity B2', 'B2.1
', 'B2.2
', true) + $$, + 'Should have updated all amenities.' +); + + +select * +from finish(); + +rollback; diff --git a/test/edit_amenity_feature.sql b/test/edit_amenity_feature.sql new file mode 100644 index 0000000..441ad18 --- /dev/null +++ b/test/edit_amenity_feature.sql @@ -0,0 +1,65 @@ +-- Test edit_amenity_feature +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(12); + +set search_path to camper, public; + +select has_function('camper', 'edit_amenity_feature', array['integer', 'text', 'text']); +select function_lang_is('camper', 'edit_amenity_feature', array['integer', 'text', 'text'], 'sql'); +select function_returns('camper', 'edit_amenity_feature', array['integer', 'text', 'text'], 'integer'); +select isnt_definer('camper', 'edit_amenity_feature', array['integer', 'text', 'text']); +select volatility_is('camper', 'edit_amenity_feature', array['integer', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'edit_amenity_feature', array ['integer', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'edit_amenity_feature', array ['integer', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'edit_amenity_feature', array ['integer', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'edit_amenity_feature', array ['integer', 'text', 'text'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_feature cascade; +truncate amenity cascade; +truncate company cascade; +reset client_min_messages; + + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into amenity (amenity_id, company_id, label, name) +values (4, 1, 'A1', 'Amenity A1') +; + +insert into amenity_feature (amenity_feature_id, amenity_id, icon_name, name) +values (5, 4, 'information', 'Feature 1') + , (6, 4, 'wifi', 'Feature 2') +; + +select lives_ok( + $$ select edit_amenity_feature(5, 'toilet', 'Feature A') $$, + 'Should be able to edit the first feature' +); + +select lives_ok( + $$ select edit_amenity_feature(6, 'baby', 'Feature B') $$, + 'Should be able to edit the second feature' +); + +select bag_eq( + $$ select amenity_feature_id, amenity_id, icon_name, name from amenity_feature $$, + $$ values (5, 4, 'toilet', 'Feature A') + , (6, 4, 'baby', 'Feature B') + $$, + 'Should have updated all amenity type features.' +); + + +select * +from finish(); + +rollback; diff --git a/test/order_amenity_carousel.sql b/test/order_amenity_carousel.sql new file mode 100644 index 0000000..221d3cf --- /dev/null +++ b/test/order_amenity_carousel.sql @@ -0,0 +1,101 @@ +-- Test order_amenity_carousel +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(11); + +set search_path to camper, public; + +select has_function('camper', 'order_amenity_carousel', array['text', 'integer', 'integer[]']); +select function_lang_is('camper', 'order_amenity_carousel', array['text', 'integer', 'integer[]'], 'sql'); +select function_returns('camper', 'order_amenity_carousel', array['text', 'integer', 'integer[]'], 'void'); +select isnt_definer('camper', 'order_amenity_carousel', array['text', 'integer', 'integer[]']); +select volatility_is('camper', 'order_amenity_carousel', array['text', 'integer', 'integer[]'], 'volatile'); +select function_privs_are('camper', 'order_amenity_carousel', array ['text', 'integer', 'integer[]'], 'guest', array[]::text[]); +select function_privs_are('camper', 'order_amenity_carousel', array ['text', 'integer', 'integer[]'], 'employee', array[]::text[]); +select function_privs_are('camper', 'order_amenity_carousel', array ['text', 'integer', 'integer[]'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'order_amenity_carousel', array ['text', 'integer', 'integer[]'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_carousel cascade; +truncate amenity cascade; +truncate media cascade; +truncate media_content cascade; +truncate company cascade; +reset client_min_messages; + + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into media_content (media_type, bytes) +values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ff00ff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffff00","a"};') + , ('text/plain', 'hello, world!') + , ('image/svg+xml', '') +; + +insert into media (media_id, company_id, original_filename, content_hash) +values ( 3, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) + , ( 4, 1, 'cover3.xpm', sha256('static char *s[]={"1 1 1 1","a c #ff00ff","a"};')) + , ( 5, 1, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffff00","a"};')) + , ( 6, 1, 'text.txt', sha256('hello, world!')) + , ( 7, 1, 'image.svg', sha256('')) + , ( 8, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) + , ( 9, 1, 'cover3.xpm', sha256('static char *s[]={"1 1 1 1","a c #ff00ff","a"};')) + , (10, 1, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffff00","a"};')) + , (11, 1, 'text.txt', sha256('hello, world!')) + , (12, 1, 'image.svg', sha256('')) +; + +insert into amenity (amenity_id, company_id, label, name) +values (15, 1, 'A1', 'Amenity A1') + , (16, 2, 'A1', 'Amenity A1') +; + +insert into amenity_carousel (amenity_id, media_id, caption) +values (15, 3, '1') + , (15, 4, '2') + , (15, 5, '3') + , (15, 6, '4') + , (15, 7, '5') + , (16, 8, '1') + , (16, 9, '2') + , (16, 10, '3') + , (16, 11, '4') + , (16, 12, '5') +; + +select lives_ok( + $$ select order_amenity_carousel('A1', 1, '{5,7,6,3,4}') $$, + 'Should be able to sort amenity type slides using their media ID' +); + +select bag_eq( + $$ select amenity_id, media_id, position from amenity_carousel $$, + $$ values (15, 5, 1) + , (15, 7, 2) + , (15, 6, 3) + , (15, 3, 4) + , (15, 4, 5) + , (16, 8, 2147483647) + , (16, 9, 2147483647) + , (16, 10, 2147483647) + , (16, 11, 2147483647) + , (16, 12, 2147483647) + $$, + 'Should have sorted all amenity type slides.' +); + + +select * +from finish(); + +rollback; diff --git a/test/order_amenity_features.sql b/test/order_amenity_features.sql new file mode 100644 index 0000000..e43a6ed --- /dev/null +++ b/test/order_amenity_features.sql @@ -0,0 +1,67 @@ +-- Test order_amenity_features +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(11); + +set search_path to camper, public; + +select has_function('camper', 'order_amenity_features', array['integer[]']); +select function_lang_is('camper', 'order_amenity_features', array['integer[]'], 'sql'); +select function_returns('camper', 'order_amenity_features', array['integer[]'], 'void'); +select isnt_definer('camper', 'order_amenity_features', array['integer[]']); +select volatility_is('camper', 'order_amenity_features', array['integer[]'], 'volatile'); +select function_privs_are('camper', 'order_amenity_features', array ['integer[]'], 'guest', array[]::text[]); +select function_privs_are('camper', 'order_amenity_features', array ['integer[]'], 'employee', array[]::text[]); +select function_privs_are('camper', 'order_amenity_features', array ['integer[]'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'order_amenity_features', array ['integer[]'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_feature cascade; +truncate amenity cascade; +truncate company cascade; +reset client_min_messages; + + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into amenity (amenity_id, company_id, label, name) +values (17, 2, 'A1', 'Amenity A1') +; + +insert into amenity_feature (amenity_feature_id, amenity_id, icon_name, name) +values (21, 17, 'information', '1') + , (22, 17, 'ball', '2') + , (23, 17, 'bicycle', '3') + , (24, 17, 'campfire', '4') + , (25, 17, 'castle', '5') +; + + +select lives_ok( + $$ select order_amenity_features('{23,25,24,21,22}') $$, + 'Should be able to sort amenity type features using their ID' +); + +select bag_eq( + $$ select amenity_feature_id, position from amenity_feature $$, + $$ values (23, 1) + , (25, 2) + , (24, 3) + , (21, 4) + , (22, 5) + $$, + 'Should have sorted all amenity type features.' +); + + +select * +from finish(); + +rollback; diff --git a/test/remove_amenity.sql b/test/remove_amenity.sql new file mode 100644 index 0000000..cf71d42 --- /dev/null +++ b/test/remove_amenity.sql @@ -0,0 +1,182 @@ +-- Test remove_amenity +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(17); + +set search_path to camper, public; + +select has_function('camper', 'remove_amenity', array['integer']); +select function_lang_is('camper', 'remove_amenity', array['integer'], 'sql'); +select function_returns('camper', 'remove_amenity', array['integer'], 'void'); +select isnt_definer('camper', 'remove_amenity', array['integer']); +select volatility_is('camper', 'remove_amenity', array['integer'], 'volatile'); +select function_privs_are('camper', 'remove_amenity', array['integer'], 'guest', array[]::text[]); +select function_privs_are('camper', 'remove_amenity', array['integer'], 'employee', array[]::text[]); +select function_privs_are('camper', 'remove_amenity', array['integer'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'remove_amenity', array['integer'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_feature_i18n cascade; +truncate amenity_feature cascade; +truncate amenity_carousel_i18n cascade; +truncate amenity_carousel cascade; +truncate amenity_i18n cascade; +truncate amenity_feature_i18n cascade; +truncate media cascade; +truncate media_content cascade; +truncate company cascade; +reset client_min_messages; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 1', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into media_content (media_type, bytes) +values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ff00ff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffff00","a"};') + , ('text/plain', 'hello, world!') + , ('image/svg+xml', '') +; + +insert into media (media_id, company_id, original_filename, content_hash) +values (3, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) + , (4, 1, 'cover3.xpm', sha256('static char *s[]={"1 1 1 1","a c #ff00ff","a"};')) + , (5, 2, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffff00","a"};')) + , (6, 2, 'text.txt', sha256('hello, world!')) + , (7, 2, 'image.svg', sha256('')) +; + +insert into amenity (amenity_id, company_id, label, name) +values (12, 1, 'A1', 'Amenity A1') + , (13, 2, 'A1', 'Amenity A1') + , (14, 2, 'B1', 'Amenity B1') +; + +insert into amenity_i18n (amenity_id, lang_tag) +values (12, 'ca') + , (12, 'es') + , (13, 'ca') + , (13, 'es') + , (14, 'ca') + , (14, 'es') +; + +insert into amenity_carousel (amenity_id, media_id, caption) +values (12, 3, 'Source caption') + , (12, 4, 'Source caption') + , (13, 5, 'Another caption') + , (13, 6, 'N/A') + , (14, 5, 'Another caption') + , (14, 6, 'N/A') +; + +insert into amenity_carousel_i18n (amenity_id, media_id, lang_tag, caption) +values (12, 3, 'en', 'Target caption') + , (12, 3, 'es', 'Target caption (spanish)') + , (12, 4, 'en', 'Target caption') + , (12, 4, 'es', 'Target caption (spanish)') + , (13, 5, 'en', 'Target caption') + , (13, 5, 'es', 'Target caption (spanish)') + , (13, 6, 'en', 'Target caption') + , (13, 6, 'es', 'Target caption (spanish)') + , (14, 5, 'en', 'Target caption') + , (14, 5, 'es', 'Target caption (spanish)') + , (14, 6, 'en', 'Target caption') + , (14, 6, 'es', 'Target caption (spanish)') +; + +insert into amenity_feature (amenity_feature_id, amenity_id, icon_name, name) +values (15, 12, 'baby', 'Baby') + , (16, 12, 'information', 'Information') + , (17, 13, 'castle', 'Castle') + , (18, 13, 'barbecue', 'Barbecue') + , (19, 14, 'bicycle', 'Bicycle') + , (20, 14, 'puzzle', 'Puzzle') +; + +insert into amenity_feature_i18n (amenity_feature_id, lang_tag, name) +values (15, 'ca', 'Nadó') + , (15, 'es', 'Bebé') + , (16, 'ca', 'Informació') + , (16, 'es', 'Información') + , (17, 'ca', 'Castell') + , (17, 'es', 'Castillo') + , (18, 'ca', 'Barbacoa') + , (18, 'es', 'Barbacoa') + , (19, 'ca', 'Bicicleta') + , (19, 'es', 'Bicicleta') + , (20, 'ca', 'Trencaclosques') + , (20, 'es', 'Rompecabezas') +; + +select lives_ok( + $$ select remove_amenity(12) $$, + 'Should be able to delete an amenity from the first company' +); + +select lives_ok( + $$ select remove_amenity(14) $$, + 'Should be able to delete an amenity from the second company' +); + +select bag_eq( + $$ select amenity_feature_id, lang_tag, name from amenity_feature_i18n $$, + $$ values (17, 'ca', 'Castell') + , (17, 'es', 'Castillo') + , (18, 'ca', 'Barbacoa') + , (18, 'es', 'Barbacoa') + $$, + 'Should have removed features’ translations' +); + +select bag_eq( + $$ select amenity_id, icon_name, name from amenity_feature $$, + $$ values (13, 'castle', 'Castle') + , (13, 'barbecue', 'Barbecue') + $$, + 'Should have removed features' +); + +select bag_eq( + $$ select amenity_id, media_id, lang_tag, caption from amenity_carousel_i18n $$, + $$ values (13, 5, 'en', 'Target caption') + , (13, 5, 'es', 'Target caption (spanish)') + , (13, 6, 'en', 'Target caption') + , (13, 6, 'es', 'Target caption (spanish)') + $$, + 'Should have removed the slides’ translations' +); + +select bag_eq( + $$ select amenity_id, media_id, caption from amenity_carousel $$, + $$ values (13, 5, 'Another caption') + , (13, 6, 'N/A') + $$, + 'Should have removed the slides' +); + +select bag_eq( + $$ select amenity_id, lang_tag from amenity_i18n $$, + $$ values (13, 'ca') + , (13, 'es') + $$, + 'Should have removed the amenities' +); + +select bag_eq( + $$ select amenity_id, label from amenity $$, + $$ values (13, 'A1') $$, + 'Should have removed the amenities' +); + +select * +from finish(); + +rollback; diff --git a/test/remove_amenity_carousel_slide.sql b/test/remove_amenity_carousel_slide.sql new file mode 100644 index 0000000..577afe4 --- /dev/null +++ b/test/remove_amenity_carousel_slide.sql @@ -0,0 +1,108 @@ +-- Test remove_amenity_carousel_slide +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(13); + +set search_path to camper, public; + +select has_function('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer']); +select function_lang_is('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer'], 'plpgsql'); +select function_returns('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer'], 'void'); +select isnt_definer('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer']); +select volatility_is('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer'], 'volatile'); +select function_privs_are('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer'], 'guest', array[]::text[]); +select function_privs_are('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer'], 'employee', array[]::text[]); +select function_privs_are('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'remove_amenity_carousel_slide', array['integer', 'text', 'integer'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_carousel_i18n cascade; +truncate amenity_carousel cascade; +truncate amenity cascade; +truncate media cascade; +truncate media_content cascade; +truncate company cascade; +reset client_min_messages; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into media_content (media_type, bytes) +values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ff00ff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffff00","a"};') + , ('text/plain', 'hello, world!') + , ('image/svg+xml', '') +; + +insert into media (media_id, company_id, original_filename, content_hash) +values (3, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) + , (4, 1, 'cover3.xpm', sha256('static char *s[]={"1 1 1 1","a c #ff00ff","a"};')) + , (5, 2, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffff00","a"};')) + , (6, 2, 'text.txt', sha256('hello, world!')) + , (7, 2, 'image.svg', sha256('')) +; + +insert into amenity (amenity_id, company_id, label, name) +values (12, 1, 'A1', 'Amenity A1') + , (13, 2, 'A1', 'Amenity A1') +; + +insert into amenity_carousel (amenity_id, media_id, caption) +values (12, 3, 'Source caption') + , (12, 4, 'Source caption') + , (13, 5, 'Another caption') + , (13, 6, 'N/A') +; + +insert into amenity_carousel_i18n (amenity_id, media_id, lang_tag, caption) +values (12, 3, 'en', 'Target caption') + , (12, 3, 'es', 'Target caption (spanish)') + , (12, 4, 'en', 'Target caption') + , (12, 4, 'es', 'Target caption (spanish)') + , (13, 5, 'en', 'Target caption') + , (13, 5, 'es', 'Target caption (spanish)') + , (13, 6, 'en', 'Target caption') + , (13, 6, 'es', 'Target caption (spanish)') +; + +select lives_ok( + $$ select remove_amenity_carousel_slide(1, 'A1', 3) $$, + 'Should be able to delete a slide from the first amenity type' +); + +select lives_ok( + $$ select remove_amenity_carousel_slide(2, 'A1', 6) $$, + 'Should be able to delete a slide from the second amenity type' +); + +select bag_eq( + $$ select amenity_id, media_id, caption from amenity_carousel $$, + $$ values (12, 4, 'Source caption') + , (13, 5, 'Another caption') + $$, + 'Should have removed the slides' +); + +select bag_eq( + $$ select amenity_id, media_id, lang_tag, caption from amenity_carousel_i18n $$, + $$ values (12, 4, 'en', 'Target caption') + , (12, 4, 'es', 'Target caption (spanish)') + , (13, 5, 'en', 'Target caption') + , (13, 5, 'es', 'Target caption (spanish)') + $$, + 'Should have removed the slides’ translations' +); + + +select * +from finish(); + +rollback; diff --git a/test/remove_amenity_feature.sql b/test/remove_amenity_feature.sql new file mode 100644 index 0000000..06b9ad0 --- /dev/null +++ b/test/remove_amenity_feature.sql @@ -0,0 +1,89 @@ +-- Test remove_amenity_feature +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(13); + +set search_path to camper, public; + +select has_function('camper', 'remove_amenity_feature', array['integer']); +select function_lang_is('camper', 'remove_amenity_feature', array['integer'], 'sql'); +select function_returns('camper', 'remove_amenity_feature', array['integer'], 'void'); +select isnt_definer('camper', 'remove_amenity_feature', array['integer']); +select volatility_is('camper', 'remove_amenity_feature', array['integer'], 'volatile'); +select function_privs_are('camper', 'remove_amenity_feature', array['integer'], 'guest', array[]::text[]); +select function_privs_are('camper', 'remove_amenity_feature', array['integer'], 'employee', array[]::text[]); +select function_privs_are('camper', 'remove_amenity_feature', array['integer'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'remove_amenity_feature', array['integer'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_feature_i18n cascade; +truncate amenity_feature cascade; +truncate amenity cascade; +truncate company cascade; +reset client_min_messages; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into amenity (amenity_id, company_id, label, name) +values (11, 1, 'A1', 'Amenity A1') + , (12, 1, 'A2', 'Amenity A2') +; + +insert into amenity_feature (amenity_feature_id, amenity_id, icon_name, name) +values (13, 11, 'wifi', 'Feature 1') + , (14, 11, 'baby', 'Feature 2') + , (15, 12, 'castle', 'Feature 3') + , (16, 12, 'ecofriendly', 'Feature 4') +; + +insert into amenity_feature_i18n (amenity_feature_id, lang_tag, name) +values (13, 'ca', 'Característica 1') + , (13, 'es', 'ES 1') + , (14, 'ca', 'Característica 2') + , (14, 'es', 'ES 2') + , (15, 'ca', 'Característica 3') + , (15, 'es', 'ES 3') + , (16, 'ca', 'Característica 4') + , (16, 'es', 'ES 4') +; + +select lives_ok( + $$ select remove_amenity_feature(13) $$, + 'Should be able to delete a feature from the first amenity' +); + +select lives_ok( + $$ select remove_amenity_feature(16) $$, + 'Should be able to delete a feature from the second amenity' +); + +select bag_eq( + $$ select amenity_feature_id, name from amenity_feature $$, + $$ values (14, 'Feature 2') + , (15, 'Feature 3') + $$, + 'Should have removed the features' +); + +select bag_eq( + $$ select amenity_feature_id, lang_tag, name from amenity_feature_i18n $$, + $$ values (14, 'ca', 'Característica 2') + , (14, 'es', 'ES 2') + , (15, 'ca', 'Característica 3') + , (15, 'es', 'ES 3') + $$, + 'Should have removed the features’ translations' +); + + +select * +from finish(); + +rollback; diff --git a/test/translate_amenity.sql b/test/translate_amenity.sql new file mode 100644 index 0000000..ec0b3f9 --- /dev/null +++ b/test/translate_amenity.sql @@ -0,0 +1,77 @@ +-- Test translate_amenity +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(14); + +set search_path to camper, public; + +select has_function('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text']); +select function_lang_is('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text'], 'sql'); +select function_returns('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text'], 'void'); +select isnt_definer('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text']); +select volatility_is('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'translate_amenity', array['integer', 'text', 'text', 'text', 'text'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_i18n cascade; +truncate amenity cascade; +truncate company cascade; +reset client_min_messages; + + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into amenity (amenity_id, company_id, label, name, info1, info2, active) +values (21, 1, 'A1', 'Anemity A1', 'Info 1
', 'Info 2
', true) + , (22, 1, 'B1', 'Anemity B1', 'Info 1
', 'Info 2
', true) +; + +insert into amenity_i18n (amenity_id, lang_tag, name, info1, info2) +values (22, 'ca', 'Insta A1', 'i1
', 'i2
') + , (22, 'en', 'Anemity A1', 'i1
', 'i2
') +; + +select lives_ok( + $$ select translate_amenity(21, 'ca', '', 'Informació 1
', '') $$, + 'Should be able to translate the first type' +); + +select lives_ok( + $$ select translate_amenity(22, 'es', 'Instalación B1', '', 'Información 2
') $$, + 'Should be able to translate the second type' +); + +select lives_ok( + $$ select translate_amenity(22, 'ca', 'Instaŀlació A1', 'Informació 1
', 'Informació 2
') $$, + 'Should be able to overwrite the catalan translation of the second type' +); + +select lives_ok( + $$ select translate_amenity(22, 'en', null, null, null) $$, + 'Should be able to overwrite the english translation of the second type with all empty values' +); + +select bag_eq( + $$ select amenity_id, lang_tag, name, info1::text, info2::text from amenity_i18n $$, + $$ values (21, 'ca', null, 'Informació 1
', null) + , (22, 'ca', 'Instaŀlació A1', 'Informació 1
', 'Informació 2
') + , (22, 'es', 'Instalación B1', null, 'Información 2
') + , (22, 'en', null, null, null) + $$, + 'Should have added and updated all translations.' +); + +select * +from finish(); + +rollback; diff --git a/test/translate_amenity_carousel_slide.sql b/test/translate_amenity_carousel_slide.sql new file mode 100644 index 0000000..a7e417f --- /dev/null +++ b/test/translate_amenity_carousel_slide.sql @@ -0,0 +1,96 @@ +-- Test translate_amenity_carousel_slide +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(13); + +set search_path to camper, public; + +select has_function('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text']); +select function_lang_is('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text'], 'sql'); +select function_returns('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text'], 'void'); +select isnt_definer('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text']); +select volatility_is('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'translate_amenity_carousel_slide', array['integer', 'text', 'integer', 'text', 'text'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_carousel_i18n cascade; +truncate amenity_carousel cascade; +truncate amenity cascade; +truncate media cascade; +truncate media_content cascade; +truncate company cascade; +reset client_min_messages; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') + , (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into media_content (media_type, bytes) +values ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ff00ff","a"};') + , ('image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffff00","a"};') + , ('text/plain', 'hello, world!') + , ('image/svg+xml', '') +; + +insert into media (media_id, company_id, original_filename, content_hash) +values (3, 1, 'cover2.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffffff","a"};')) + , (4, 1, 'cover3.xpm', sha256('static char *s[]={"1 1 1 1","a c #ff00ff","a"};')) + , (5, 1, 'cover4.xpm', sha256('static char *s[]={"1 1 1 1","a c #ffff00","a"};')) + , (6, 1, 'text.txt', sha256('hello, world!')) + , (7, 2, 'image.svg', sha256('')) +; + +insert into amenity (amenity_id, company_id, label, name) +values (12, 1, 'A1', 'Amenity A1') + , (13, 2, 'A1', 'Amenity A1') +; + +insert into amenity_carousel (amenity_id, media_id, caption) +values (13, 7, 'Source caption') + , (12, 5, 'Another caption') + , (12, 6, 'N/A') +; + +insert into amenity_carousel_i18n (amenity_id, media_id, lang_tag, caption) +values (13, 7, 'en', 'Target caption') +; + +select lives_ok( + $$ select translate_amenity_carousel_slide(1, 'A1', 5, 'ca', 'Traducció') $$, + 'Should be able to translate a carousel slide' +); + +select lives_ok( + $$ select translate_amenity_carousel_slide(1, 'A1', 5, 'es', '') $$, + 'Should be able to “translate” a carousel slide to the empty string' +); + +select lives_ok( + $$ select translate_amenity_carousel_slide(2, 'A1', 7, 'en', 'Not anymore') $$, + 'Should be able to overwrite a slide’s translation' +); + +select bag_eq( + $$ select amenity_id, media_id, lang_tag, caption from amenity_carousel_i18n $$, + $$ values (12, 5, 'ca', 'Traducció') + , (12, 5, 'es', null) + , (13, 7, 'en', 'Not anymore') + $$, + 'Should have all three slides' +); + + +select * +from finish(); + +rollback; diff --git a/test/translate_amenity_feature.sql b/test/translate_amenity_feature.sql new file mode 100644 index 0000000..ba50672 --- /dev/null +++ b/test/translate_amenity_feature.sql @@ -0,0 +1,81 @@ +-- Test translate_amenity_feature +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(14); + +set search_path to camper, public; + +select has_function('camper', 'translate_amenity_feature', array['integer', 'text', 'text']); +select function_lang_is('camper', 'translate_amenity_feature', array['integer', 'text', 'text'], 'sql'); +select function_returns('camper', 'translate_amenity_feature', array['integer', 'text', 'text'], 'void'); +select isnt_definer('camper', 'translate_amenity_feature', array['integer', 'text', 'text']); +select volatility_is('camper', 'translate_amenity_feature', array['integer', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'translate_amenity_feature', array['integer', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'translate_amenity_feature', array['integer', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'translate_amenity_feature', array['integer', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'translate_amenity_feature', array['integer', 'text', 'text'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate amenity_feature_i18n cascade; +truncate amenity_feature cascade; +truncate amenity cascade; +truncate company cascade; +reset client_min_messages; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, country_code, currency_code, default_lang_tag) +values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca') +; + +insert into amenity (amenity_id, company_id, label, name) +values (4, 1, 'A1', 'Amenity A1') +; + +insert into amenity_feature (amenity_feature_id, amenity_id, icon_name, name) +values (5, 4, 'toilet', 'Feature 1') + , (6, 4, 'toilet', 'Feature 2') +; + +insert into amenity_feature_i18n (amenity_feature_id, lang_tag, name) +values (6, 'ca', 'carácter2') +; + +select lives_ok( + $$ select translate_amenity_feature(5, 'ca', 'Carácter 1') $$, + 'Should be able to translate the first feature' +); + +select lives_ok( + $$ select translate_amenity_feature(6, 'es', 'Característica 2') $$, + 'Should be able to translate the second feature' +); + +select lives_ok( + $$ select translate_amenity_feature(6, 'en', '') $$, + 'Should be able to “translate” the second feature with an empty translation' +); + +select lives_ok( + $$ select translate_amenity_feature(6, 'ca', 'Carácter 2') $$, + 'Should be able to overwrite the catalan translation of the second feature' +); + +select bag_eq( + $$ select amenity_feature_id, lang_tag, name from amenity_feature_i18n $$, + $$ values (5, 'ca', 'Carácter 1') + , (6, 'ca', 'Carácter 2') + , (6, 'es', 'Característica 2') + , (6, 'en', null) + $$, + 'Should have added and updated all translations.' +); + + +select * +from finish(); + +rollback; diff --git a/test/translate_campsite.sql b/test/translate_campsite.sql index 214b3c9..31e9d94 100644 --- a/test/translate_campsite.sql +++ b/test/translate_campsite.sql @@ -23,7 +23,7 @@ select function_privs_are('camper', 'translate_campsite', array['integer', 'text set client_min_messages to warning; truncate campsite_i18n cascade; truncate campsite cascade; -truncate campsite cascade; +truncate campsite_type cascade; truncate media cascade; truncate media_content cascade; truncate company cascade; diff --git a/verify/add_amenity.sql b/verify/add_amenity.sql new file mode 100644 index 0000000..cdaa357 --- /dev/null +++ b/verify/add_amenity.sql @@ -0,0 +1,7 @@ +-- Verify camper:add_amenity on pg + +begin; + +select has_function_privilege('camper.add_amenity(integer, text, text, text, text)', 'execute'); + +rollback; diff --git a/verify/add_amenity_carousel_slide.sql b/verify/add_amenity_carousel_slide.sql new file mode 100644 index 0000000..2148651 --- /dev/null +++ b/verify/add_amenity_carousel_slide.sql @@ -0,0 +1,7 @@ +-- Verify camper:add_amenity_carousel_slide on pg + +begin; + +select has_function_privilege('camper.add_amenity_carousel_slide(integer, text, integer, text)', 'execute'); + +rollback; diff --git a/verify/add_amenity_feature.sql b/verify/add_amenity_feature.sql new file mode 100644 index 0000000..8c9500b --- /dev/null +++ b/verify/add_amenity_feature.sql @@ -0,0 +1,7 @@ +-- Verify camper:add_amenity_feature on pg + +begin; + +select has_function_privilege('camper.add_amenity_feature(integer, text, text, text)', 'execute'); + +rollback; diff --git a/verify/amenity.sql b/verify/amenity.sql new file mode 100644 index 0000000..734d02f --- /dev/null +++ b/verify/amenity.sql @@ -0,0 +1,21 @@ +-- Verify camper:amenity on pg + +begin; + +select amenity_id + , company_id + , label + , name + , info1 + , info2 + , active +from camper.amenity +where false; + +select 1 / count(*) from pg_class where oid = 'camper.amenity'::regclass and relrowsecurity; +select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.amenity'::regclass; +select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.amenity'::regclass; +select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.amenity'::regclass; +select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.amenity'::regclass; + +rollback; diff --git a/verify/amenity_carousel.sql b/verify/amenity_carousel.sql new file mode 100644 index 0000000..c89bbb9 --- /dev/null +++ b/verify/amenity_carousel.sql @@ -0,0 +1,18 @@ +-- Verify camper:amenity_carousel on pg + +begin; + +select amenity_id + , media_id + , caption + , position +from camper.amenity_carousel +where false; + +select 1 / count(*) from pg_class where oid = 'camper.amenity_carousel'::regclass and relrowsecurity; +select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.amenity_carousel'::regclass; +select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.amenity_carousel'::regclass; +select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.amenity_carousel'::regclass; +select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.amenity_carousel'::regclass; + +rollback; diff --git a/verify/amenity_carousel_i18n.sql b/verify/amenity_carousel_i18n.sql new file mode 100644 index 0000000..7586b21 --- /dev/null +++ b/verify/amenity_carousel_i18n.sql @@ -0,0 +1,12 @@ +-- Verify camper:amenity_carousel_i18n on pg + +begin; + +select amenity_id + , media_id + , lang_tag + , caption +from camper.amenity_carousel_i18n +where false; + +rollback; diff --git a/verify/amenity_feature.sql b/verify/amenity_feature.sql new file mode 100644 index 0000000..ca990e4 --- /dev/null +++ b/verify/amenity_feature.sql @@ -0,0 +1,19 @@ +-- Verify camper:amenity_feature on pg + +begin; + +select amenity_feature_id + , amenity_id + , icon_name + , name + , position +from camper.amenity_feature +where false; + +select 1 / count(*) from pg_class where oid = 'camper.amenity_feature'::regclass and relrowsecurity; +select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.amenity_feature'::regclass; +select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.amenity_feature'::regclass; +select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.amenity_feature'::regclass; +select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.amenity_feature'::regclass; + +rollback; diff --git a/verify/amenity_feature_i18n.sql b/verify/amenity_feature_i18n.sql new file mode 100644 index 0000000..7597872 --- /dev/null +++ b/verify/amenity_feature_i18n.sql @@ -0,0 +1,11 @@ +-- Verify camper:amenity_feature_i18n on pg + +begin; + +select amenity_feature_id + , lang_tag + , name +from camper.amenity_feature_i18n +where false; + +rollback; diff --git a/verify/amenity_i18n.sql b/verify/amenity_i18n.sql new file mode 100644 index 0000000..7aec25c --- /dev/null +++ b/verify/amenity_i18n.sql @@ -0,0 +1,13 @@ +-- Verify camper:amenity_i18n on pg + +begin; + +select amenity_id + , lang_tag + , name + , info1 + , info2 +from camper.amenity_i18n +where false; + +rollback; diff --git a/verify/edit_amenity.sql b/verify/edit_amenity.sql new file mode 100644 index 0000000..aef680b --- /dev/null +++ b/verify/edit_amenity.sql @@ -0,0 +1,7 @@ +-- Verify camper:edit_amenity on pg + +begin; + +select has_function_privilege('camper.edit_amenity(integer, text, text, text, text, boolean)', 'execute'); + +rollback; diff --git a/verify/edit_amenity_feature.sql b/verify/edit_amenity_feature.sql new file mode 100644 index 0000000..e8984a8 --- /dev/null +++ b/verify/edit_amenity_feature.sql @@ -0,0 +1,7 @@ +-- Verify camper:edit_amenity_feature on pg + +begin; + +select has_function_privilege('camper.edit_amenity_feature(integer, text, text)', 'execute'); + +rollback; diff --git a/verify/order_amenity_carousel.sql b/verify/order_amenity_carousel.sql new file mode 100644 index 0000000..d50d249 --- /dev/null +++ b/verify/order_amenity_carousel.sql @@ -0,0 +1,7 @@ +-- Verify camper:order_amenity_carousel on pg + +begin; + +select has_function_privilege('camper.order_amenity_carousel(text, integer, integer[])', 'execute'); + +rollback; diff --git a/verify/order_amenity_features.sql b/verify/order_amenity_features.sql new file mode 100644 index 0000000..39c72ac --- /dev/null +++ b/verify/order_amenity_features.sql @@ -0,0 +1,7 @@ +-- Verify camper:order_amenity_features on pg + +begin; + +select has_function_privilege('camper.order_amenity_features(integer[])', 'execute'); + +rollback; diff --git a/verify/remove_amenity.sql b/verify/remove_amenity.sql new file mode 100644 index 0000000..2118d3b --- /dev/null +++ b/verify/remove_amenity.sql @@ -0,0 +1,7 @@ +-- Verify camper:remove_amenity on pg + +begin; + +select has_function_privilege('camper.remove_amenity(integer)', 'execute'); + +rollback; diff --git a/verify/remove_amenity_carousel_slide.sql b/verify/remove_amenity_carousel_slide.sql new file mode 100644 index 0000000..eae2ef4 --- /dev/null +++ b/verify/remove_amenity_carousel_slide.sql @@ -0,0 +1,7 @@ +-- Verify camper:remove_amenity_carousel_slide on pg + +begin; + +select has_function_privilege('camper.remove_amenity_carousel_slide(integer, text, integer)', 'execute'); + +rollback; diff --git a/verify/remove_amenity_feature.sql b/verify/remove_amenity_feature.sql new file mode 100644 index 0000000..2ad14b4 --- /dev/null +++ b/verify/remove_amenity_feature.sql @@ -0,0 +1,7 @@ +-- Verify camper:remove_amenity_feature on pg + +begin; + +select has_function_privilege('camper.remove_amenity_feature(integer)', 'execute'); + +rollback; diff --git a/verify/translate_amenity.sql b/verify/translate_amenity.sql new file mode 100644 index 0000000..f7b9a41 --- /dev/null +++ b/verify/translate_amenity.sql @@ -0,0 +1,7 @@ +-- Verify camper:translate_amenity on pg + +begin; + +select has_function_privilege('camper.translate_amenity(integer, text, text, text, text)', 'execute'); + +rollback; diff --git a/verify/translate_amenity_carousel_slide.sql b/verify/translate_amenity_carousel_slide.sql new file mode 100644 index 0000000..3307fa9 --- /dev/null +++ b/verify/translate_amenity_carousel_slide.sql @@ -0,0 +1,7 @@ +-- Verify camper:translate_amenity_carousel_slide on pg + +begin; + +select has_function_privilege('camper.translate_amenity_carousel_slide(integer, text, integer, text, text)', 'execute'); + +rollback; diff --git a/verify/translate_amenity_feature.sql b/verify/translate_amenity_feature.sql new file mode 100644 index 0000000..22cab9a --- /dev/null +++ b/verify/translate_amenity_feature.sql @@ -0,0 +1,7 @@ +-- Verify camper:translate_amenity_feature on pg + +begin; + +select has_function_privilege('camper.translate_amenity_feature(integer, text, text)', 'execute'); + +rollback; diff --git a/web/static/map.js b/web/static/map.js index 53a00d4..8032a84 100644 --- a/web/static/map.js +++ b/web/static/map.js @@ -14,8 +14,8 @@ maxZoom: 2, zoom: -1, scrollWheelZoom: false, - maxBounds: latLngBounds, - maxBoundsViscosity: 1.0, + //maxBounds: latLngBounds, + //maxBoundsViscosity: 1.0, zoomSnap: 0, crs: L.CRS.Simple, }); @@ -78,54 +78,60 @@ // XXX: this is from the “parceles” layer. const ctm = [1, 0, 0, 1, 83.2784, 66.1766]; - const transform = function (x, y) { + + function transform(x, y) { return [ctm[0] * x + ctm[2] * y + ctm[4], ctm[1] * x + ctm[3] * y + ctm[5]]; } - const language = document.documentElement.getAttribute('lang'); - const prefix = 'cp_'; - for (const campsite of Array.from(container.querySelectorAll(`[id^="${prefix}"]`))) { - const label = campsite.id.substring(prefix.length); - if (!label) { - continue; - } - const path = campsite.firstElementChild; - const commands = parse(path.getAttribute('d')); - const points = []; - const p0 = [0, 0]; - for (const cmd of commands) { - switch (cmd[0]) { - case 'M': - case 'L': - const cmdM = transform(cmd[1], cmd[2]); - p0[0] = cmdM[0]; - p0[1] = latLngBounds.getNorth() - cmdM[1]; - break; - case 'm': - case 'l': - p0[0] += cmd[1]; - p0[1] -= cmd[2]; - break; - case 'C': - const cmdC = transform(cmd[5], cmd[6]); - p0[0] = cmdC[0]; - p0[1] = latLngBounds.getNorth() - cmdC[1]; - break; - case 'Z': - case 'z': - continue; - default: - console.error(cmd); + function setupFeatures(prefix, baseURI) { + for (const campsite of Array.from(container.querySelectorAll(`[id^="${prefix}"]`))) { + const label = campsite.id.substring(prefix.length); + if (!label) { + continue; } - points.push([p0[1], p0[0]]); + const path = campsite.firstElementChild; + const commands = parse(path.getAttribute('d')); + const points = []; + const p0 = [0, 0]; + for (const cmd of commands) { + switch (cmd[0]) { + case 'M': + case 'L': + const cmdM = transform(cmd[1], cmd[2]); + p0[0] = cmdM[0]; + p0[1] = latLngBounds.getNorth() - cmdM[1]; + break; + case 'm': + case 'l': + p0[0] += cmd[1]; + p0[1] -= cmd[2]; + break; + case 'C': + const cmdC = transform(cmd[5], cmd[6]); + p0[0] = cmdC[0]; + p0[1] = latLngBounds.getNorth() - cmdC[1]; + break; + case 'Z': + case 'z': + continue; + default: + console.error(cmd); + } + points.push([p0[1], p0[0]]); + } + console.log(points); + const feature = L.polygon(points, {color: 'transparent'}).addTo(map); + feature.on({ + mouseover: highlightAccommodation, + mouseout: resetHighlight, + click: function () { + window.location = `${baseURI}/${label}`; + }, + }) } - const accommodation = L.polygon(points, {color: 'transparent'}).addTo(map); - accommodation.on({ - mouseover: highlightAccommodation, - mouseout: resetHighlight, - click: function () { - window.location = `/${language}/campsites/${label}`; - }, - }) } + + const language = document.documentElement.getAttribute('lang'); + setupFeatures('cp_', `/${language}/campsites`) + setupFeatures('cr_', `/${language}/amenities`) })(); diff --git a/web/templates/admin/amenity/carousel/form.gohtml b/web/templates/admin/amenity/carousel/form.gohtml new file mode 100644 index 0000000..23b3493 --- /dev/null +++ b/web/templates/admin/amenity/carousel/form.gohtml @@ -0,0 +1,57 @@ + +{{ define "title" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/amenity.slideForm*/ -}} + {{- if .MediaID -}} + {{( pgettext "Edit Amenity Carousel Slide" "title" )}} + {{- else -}} + {{( pgettext "New Amenity Carousel Slide" "title" )}} + {{- end -}} +{{- end }} + +{{ define "breadcrumb" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/amenity/types.slideForm*/ -}} +{{( gettext "No slides added yet." )}}
+ {{- end }} +{{- end }} diff --git a/web/templates/admin/amenity/feature/form.gohtml b/web/templates/admin/amenity/feature/form.gohtml new file mode 100644 index 0000000..ee29d42 --- /dev/null +++ b/web/templates/admin/amenity/feature/form.gohtml @@ -0,0 +1,78 @@ + +{{ define "title" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/amenity/types.featureForm*/ -}} + {{- if .ID -}} + {{( pgettext "Edit Amenity Feature" "title" )}} + {{- else -}} + {{( pgettext "New Amenity Feature" "title" )}} + {{- end -}} +{{- end }} + +{{ define "breadcrumb" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/amenity.featureForm*/ -}} +{{( gettext "No amenity features added yet." )}}
+ {{- end }} +{{- end }} diff --git a/web/templates/admin/amenity/form.gohtml b/web/templates/admin/amenity/form.gohtml new file mode 100644 index 0000000..ce33932 --- /dev/null +++ b/web/templates/admin/amenity/form.gohtml @@ -0,0 +1,98 @@ + +{{ define "title" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/amenity.amenityForm*/ -}} + {{ if .ID }} + {{( pgettext "Edit Amenity" "title" )}} + {{ else }} + {{( pgettext "New Amenity" "title" )}} + {{ end }} +{{- end }} + +{{ define "breadcrumb" -}} +{{( pgettext "Label" "header" )}} | +{{( pgettext "Name" "header" )}} | +{{( pgettext "Features" "header" )}} | +{{( pgettext "Carousel" "header" )}} | +{{( pgettext "Active" "amenity" )}} | +{{( pgettext "Actions" "header" )}} | +
---|---|---|---|---|---|
{{ .Label }} | +{{ .Name }} | ++ {{( pgettext "Edit Features" "action" )}} + | ++ {{( pgettext "Edit Carousel" "action" )}} + | +{{ if .Active }}{{( gettext "Yes" )}}{{ else }}{{( gettext "No" )}}{{ end }} | ++ + | +
{{( gettext "No amenities added yet." )}}
+ {{- end }} + + +{{- end }} diff --git a/web/templates/admin/layout.gohtml b/web/templates/admin/layout.gohtml index 8e9cda3..763fd32 100644 --- a/web/templates/admin/layout.gohtml +++ b/web/templates/admin/layout.gohtml @@ -45,6 +45,9 @@