feat: add shared notification, communication, and export services for bookings, courses, and events; introduce btree_gist extension and new booking atomic function
This commit is contained in:
121
apps/web/supabase/tests/database/member-audit.test.sql
Normal file
121
apps/web/supabase/tests/database/member-audit.test.sql
Normal file
@@ -0,0 +1,121 @@
|
||||
begin;
|
||||
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
-- =====================================================
|
||||
-- Audit Trigger & Version Tests
|
||||
-- Verifies triggers fire correctly on member changes
|
||||
-- =====================================================
|
||||
|
||||
-- Setup
|
||||
select tests.create_supabase_user('audit_owner', 'audit_owner@test.com');
|
||||
select makerkit.set_identifier('audit_owner', 'audit_owner@test.com');
|
||||
|
||||
set local role service_role;
|
||||
select public.create_team_account('Audit Verein', tests.get_supabase_uid('audit_owner'));
|
||||
|
||||
set local role postgres;
|
||||
insert into public.role_permissions (role, permission)
|
||||
values ('owner', 'members.write')
|
||||
on conflict do nothing;
|
||||
|
||||
-- Get account ID
|
||||
select makerkit.authenticate_as('audit_owner');
|
||||
|
||||
-- Insert a member (triggers audit INSERT)
|
||||
set local role service_role;
|
||||
insert into public.members (
|
||||
account_id, first_name, last_name, status, entry_date, member_number,
|
||||
created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'audit-verein' limit 1),
|
||||
'Audit', 'Test', 'active', current_date, '0001',
|
||||
tests.get_supabase_uid('audit_owner'),
|
||||
tests.get_supabase_uid('audit_owner')
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: INSERT creates audit entry
|
||||
-- -------------------------------------------------------
|
||||
select isnt_empty(
|
||||
$$ select * from public.member_audit_log
|
||||
where member_id = (select id from public.members where first_name = 'Audit' limit 1)
|
||||
and action = 'created' $$,
|
||||
'Member INSERT creates audit entry with action=created'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Version starts at 1
|
||||
-- -------------------------------------------------------
|
||||
select is(
|
||||
(select version from public.members where first_name = 'Audit' limit 1),
|
||||
1,
|
||||
'Initial version is 1'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: UPDATE increments version
|
||||
-- -------------------------------------------------------
|
||||
update public.members
|
||||
set first_name = 'AuditUpdated'
|
||||
where first_name = 'Audit';
|
||||
|
||||
select is(
|
||||
(select version from public.members where first_name = 'AuditUpdated' limit 1),
|
||||
2,
|
||||
'Version incremented to 2 after update'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: UPDATE creates audit entry with field diff
|
||||
-- -------------------------------------------------------
|
||||
select isnt_empty(
|
||||
$$ select * from public.member_audit_log
|
||||
where member_id = (select id from public.members where first_name = 'AuditUpdated' limit 1)
|
||||
and action = 'updated'
|
||||
and changes ? 'first_name' $$,
|
||||
'Member UPDATE creates audit entry with first_name change diff'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Status change creates status_changed audit entry
|
||||
-- -------------------------------------------------------
|
||||
update public.members
|
||||
set status = 'inactive'
|
||||
where first_name = 'AuditUpdated';
|
||||
|
||||
select isnt_empty(
|
||||
$$ select * from public.member_audit_log
|
||||
where member_id = (select id from public.members where first_name = 'AuditUpdated' limit 1)
|
||||
and action = 'status_changed' $$,
|
||||
'Status change creates audit entry with action=status_changed'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Archive creates archived audit entry
|
||||
-- -------------------------------------------------------
|
||||
update public.members
|
||||
set is_archived = true
|
||||
where first_name = 'AuditUpdated';
|
||||
|
||||
select isnt_empty(
|
||||
$$ select * from public.member_audit_log
|
||||
where member_id = (select id from public.members where first_name = 'AuditUpdated' limit 1)
|
||||
and action = 'archived' $$,
|
||||
'Archive creates audit entry with action=archived'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Multiple updates increment version correctly
|
||||
-- -------------------------------------------------------
|
||||
select is(
|
||||
(select version from public.members where first_name = 'AuditUpdated' limit 1),
|
||||
4,
|
||||
'Version is 4 after 3 updates (initial insert + 3 updates)'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
186
apps/web/supabase/tests/database/member-constraints.test.sql
Normal file
186
apps/web/supabase/tests/database/member-constraints.test.sql
Normal file
@@ -0,0 +1,186 @@
|
||||
begin;
|
||||
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
-- =====================================================
|
||||
-- CHECK Constraint Tests
|
||||
-- =====================================================
|
||||
|
||||
-- Setup
|
||||
select tests.create_supabase_user('constraint_owner', 'constraint_owner@test.com');
|
||||
select makerkit.set_identifier('constraint_owner', 'constraint_owner@test.com');
|
||||
|
||||
set local role service_role;
|
||||
select public.create_team_account('Constraint Verein', tests.get_supabase_uid('constraint_owner'));
|
||||
|
||||
set local role postgres;
|
||||
insert into public.role_permissions (role, permission)
|
||||
values ('owner', 'members.write')
|
||||
on conflict do nothing;
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: DOB in future rejected
|
||||
-- -------------------------------------------------------
|
||||
set local role service_role;
|
||||
|
||||
select throws_ok(
|
||||
$test$ insert into public.members (
|
||||
account_id, first_name, last_name, date_of_birth, status, entry_date, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'Future', 'Baby', current_date + interval '1 day', 'active', current_date,
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
) $test$,
|
||||
'new row for relation "members" violates check constraint "chk_members_dob_not_future"',
|
||||
'Future date of birth is rejected'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Exit date before entry date rejected
|
||||
-- -------------------------------------------------------
|
||||
select throws_ok(
|
||||
$test$ insert into public.members (
|
||||
account_id, first_name, last_name, status, entry_date, exit_date, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'Wrong', 'Dates', 'resigned', '2024-06-01', '2024-01-01',
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
) $test$,
|
||||
'new row for relation "members" violates check constraint "chk_members_exit_after_entry"',
|
||||
'Exit date before entry date is rejected'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Entry date in future rejected
|
||||
-- -------------------------------------------------------
|
||||
select throws_ok(
|
||||
$test$ insert into public.members (
|
||||
account_id, first_name, last_name, status, entry_date, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'Future', 'Entry', 'active', current_date + interval '2 days',
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
) $test$,
|
||||
'new row for relation "members" violates check constraint "chk_members_entry_not_future"',
|
||||
'Future entry date is rejected'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Valid member insert succeeds
|
||||
-- -------------------------------------------------------
|
||||
select lives_ok(
|
||||
$test$ insert into public.members (
|
||||
account_id, first_name, last_name, status, entry_date,
|
||||
date_of_birth, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'Valid', 'Member', 'active', '2024-01-15', '1990-05-20',
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
) $test$,
|
||||
'Valid member with correct dates succeeds'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Duplicate email in same account rejected
|
||||
-- -------------------------------------------------------
|
||||
insert into public.members (
|
||||
account_id, first_name, last_name, email, status, entry_date, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'First', 'Email', 'duplicate@test.com', 'active', current_date,
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$test$ insert into public.members (
|
||||
account_id, first_name, last_name, email, status, entry_date, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'Second', 'Email', 'duplicate@test.com', 'active', current_date,
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
) $test$,
|
||||
'duplicate key value violates unique constraint "uix_members_email_per_account"',
|
||||
'Duplicate email in same account is rejected'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: NULL emails allowed (multiple)
|
||||
-- -------------------------------------------------------
|
||||
select lives_ok(
|
||||
$test$ insert into public.members (
|
||||
account_id, first_name, last_name, email, status, entry_date, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'No', 'Email1', null, 'active', current_date,
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
) $test$,
|
||||
'NULL email is allowed'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$test$ insert into public.members (
|
||||
account_id, first_name, last_name, email, status, entry_date, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'No', 'Email2', null, 'active', current_date,
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
) $test$,
|
||||
'Multiple NULL emails allowed'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Invalid IBAN rejected on sepa_mandates
|
||||
-- -------------------------------------------------------
|
||||
insert into public.members (
|
||||
account_id, first_name, last_name, status, entry_date, member_number, created_by, updated_by
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'SEPA', 'Test', 'active', current_date, 'SEPA01',
|
||||
tests.get_supabase_uid('constraint_owner'), tests.get_supabase_uid('constraint_owner')
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$test$ insert into public.sepa_mandates (
|
||||
member_id, account_id, mandate_reference, iban, account_holder, mandate_date, status
|
||||
) values (
|
||||
(select id from public.members where first_name = 'SEPA' limit 1),
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'MANDATE-001', 'invalid-iban', 'Test Holder', current_date, 'active'
|
||||
) $test$,
|
||||
'new row for relation "sepa_mandates" violates check constraint "chk_sepa_iban_format"',
|
||||
'Invalid IBAN format is rejected'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Valid IBAN accepted
|
||||
-- -------------------------------------------------------
|
||||
select lives_ok(
|
||||
$test$ insert into public.sepa_mandates (
|
||||
member_id, account_id, mandate_reference, iban, account_holder, mandate_date, status
|
||||
) values (
|
||||
(select id from public.members where first_name = 'SEPA' limit 1),
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'MANDATE-002', 'DE89370400440532013000', 'Test Holder', current_date, 'active'
|
||||
) $test$,
|
||||
'Valid German IBAN is accepted'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: Negative dues amount rejected
|
||||
-- -------------------------------------------------------
|
||||
select throws_ok(
|
||||
$test$ insert into public.dues_categories (
|
||||
account_id, name, amount
|
||||
) values (
|
||||
(select id from public.accounts where slug = 'constraint-verein' limit 1),
|
||||
'Negative Fee', -50
|
||||
) $test$,
|
||||
'new row for relation "dues_categories" violates check constraint "chk_dues_amount_non_negative"',
|
||||
'Negative dues amount is rejected'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
211
apps/web/supabase/tests/database/member-functions.test.sql
Normal file
211
apps/web/supabase/tests/database/member-functions.test.sql
Normal file
@@ -0,0 +1,211 @@
|
||||
begin;
|
||||
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
-- =====================================================
|
||||
-- Member Management Function Tests
|
||||
-- Tests PG functions for correctness, auth, atomicity
|
||||
-- =====================================================
|
||||
|
||||
-- Setup: create test users and account
|
||||
select tests.create_supabase_user('owner', 'owner@test.com');
|
||||
select tests.create_supabase_user('member_user', 'member@test.com');
|
||||
select tests.create_supabase_user('outsider', 'outsider@test.com');
|
||||
|
||||
select makerkit.set_identifier('owner', 'owner@test.com');
|
||||
select makerkit.set_identifier('member_user', 'member@test.com');
|
||||
select makerkit.set_identifier('outsider', 'outsider@test.com');
|
||||
|
||||
-- Create a team account owned by 'owner'
|
||||
set local role service_role;
|
||||
select public.create_team_account('Test Verein', tests.get_supabase_uid('owner'));
|
||||
|
||||
-- Get account ID
|
||||
select makerkit.authenticate_as('owner');
|
||||
\set test_account_id '(select id from public.accounts where slug = ''test-verein'' limit 1)'
|
||||
|
||||
-- Grant members.write permission to owner
|
||||
set local role postgres;
|
||||
insert into public.role_permissions (role, permission)
|
||||
values ('owner', 'members.write')
|
||||
on conflict do nothing;
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: get_next_member_number
|
||||
-- -------------------------------------------------------
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
select is(
|
||||
public.get_next_member_number(:test_account_id),
|
||||
'0001',
|
||||
'First member number should be 0001'
|
||||
);
|
||||
|
||||
-- Insert a member to test incrementing
|
||||
set local role service_role;
|
||||
insert into public.members (account_id, first_name, last_name, member_number, status, entry_date, created_by, updated_by)
|
||||
values (:test_account_id, 'Max', 'Mustermann', '0001', 'active', current_date,
|
||||
tests.get_supabase_uid('owner'), tests.get_supabase_uid('owner'));
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
select is(
|
||||
public.get_next_member_number(:test_account_id),
|
||||
'0002',
|
||||
'Second member number should be 0002'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: get_member_quick_stats
|
||||
-- -------------------------------------------------------
|
||||
select isnt_empty(
|
||||
$$ select * from public.get_member_quick_stats((select id from public.accounts where slug = 'test-verein' limit 1)) $$,
|
||||
'Quick stats returns data for account with members'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: check_duplicate_member
|
||||
-- -------------------------------------------------------
|
||||
select isnt_empty(
|
||||
$$ select * from public.check_duplicate_member(
|
||||
(select id from public.accounts where slug = 'test-verein' limit 1),
|
||||
'Max', 'Mustermann', null
|
||||
) $$,
|
||||
'Duplicate check finds existing member by name'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.check_duplicate_member(
|
||||
(select id from public.accounts where slug = 'test-verein' limit 1),
|
||||
'Nonexistent', 'Person', null
|
||||
) $$,
|
||||
'Duplicate check returns empty for non-matching name'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: approve_application
|
||||
-- -------------------------------------------------------
|
||||
|
||||
-- Create a test application
|
||||
set local role service_role;
|
||||
insert into public.membership_applications (
|
||||
account_id, first_name, last_name, email, status
|
||||
) values (
|
||||
:test_account_id, 'Anna', 'Bewerberin', 'anna@test.com', 'submitted'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
-- Approve it
|
||||
select lives_ok(
|
||||
$$ select public.approve_application(
|
||||
(select id from public.membership_applications where email = 'anna@test.com'),
|
||||
tests.get_supabase_uid('owner')
|
||||
) $$,
|
||||
'Owner can approve application'
|
||||
);
|
||||
|
||||
-- Verify member was created
|
||||
select isnt_empty(
|
||||
$$ select * from public.members where first_name = 'Anna' and last_name = 'Bewerberin' $$,
|
||||
'Approved application creates a member'
|
||||
);
|
||||
|
||||
-- Verify application status changed
|
||||
select is(
|
||||
(select status from public.membership_applications where email = 'anna@test.com'),
|
||||
'approved'::public.application_status,
|
||||
'Application status is approved'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: reject_application
|
||||
-- -------------------------------------------------------
|
||||
set local role service_role;
|
||||
insert into public.membership_applications (
|
||||
account_id, first_name, last_name, email, status
|
||||
) values (
|
||||
:test_account_id, 'Bob', 'Abgelehnt', 'bob@test.com', 'submitted'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
select lives_ok(
|
||||
$$ select public.reject_application(
|
||||
(select id from public.membership_applications where email = 'bob@test.com'),
|
||||
tests.get_supabase_uid('owner'),
|
||||
'Nicht qualifiziert'
|
||||
) $$,
|
||||
'Owner can reject application'
|
||||
);
|
||||
|
||||
select is(
|
||||
(select status from public.membership_applications where email = 'bob@test.com'),
|
||||
'rejected'::public.application_status,
|
||||
'Application status is rejected'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: approve_application — already approved should fail
|
||||
-- -------------------------------------------------------
|
||||
-- Verify the re-approval throws with status message
|
||||
prepare approve_again as select public.approve_application(
|
||||
(select id from public.membership_applications where email = 'anna@test.com'),
|
||||
tests.get_supabase_uid('owner')
|
||||
);
|
||||
select throws_ok(
|
||||
'approve_again',
|
||||
'P0001',
|
||||
'Application is not in a reviewable state (current: approved)',
|
||||
'Cannot approve already-approved application'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: get_member_timeline
|
||||
-- -------------------------------------------------------
|
||||
-- The member creation via approve_application should have generated an audit entry
|
||||
select isnt_empty(
|
||||
$$ select * from public.get_member_timeline(
|
||||
(select id from public.members where first_name = 'Anna' limit 1),
|
||||
1, 50, null
|
||||
) $$,
|
||||
'Member timeline has entries after creation'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: log_member_audit_event
|
||||
-- -------------------------------------------------------
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
select lives_ok(
|
||||
$$ select public.log_member_audit_event(
|
||||
(select id from public.members where first_name = 'Max' limit 1),
|
||||
(select id from public.accounts where slug = 'test-verein' limit 1),
|
||||
'note_added',
|
||||
'{"note": "Test note"}'::jsonb,
|
||||
'{}'::jsonb
|
||||
) $$,
|
||||
'Owner can log audit event for member'
|
||||
);
|
||||
|
||||
-- -------------------------------------------------------
|
||||
-- Test: outsider cannot access functions
|
||||
-- -------------------------------------------------------
|
||||
select makerkit.authenticate_as('outsider');
|
||||
|
||||
-- Outsider should get an error when calling get_next_member_number
|
||||
prepare outsider_member_number as select public.get_next_member_number(
|
||||
(select id from public.accounts where slug = 'test-verein' limit 1)
|
||||
);
|
||||
select throws_ok(
|
||||
'outsider_member_number',
|
||||
'P0001',
|
||||
null,
|
||||
'Outsider cannot call get_next_member_number'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
105
apps/web/supabase/tests/database/member-tables.test.sql
Normal file
105
apps/web/supabase/tests/database/member-tables.test.sql
Normal file
@@ -0,0 +1,105 @@
|
||||
begin;
|
||||
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
-- =====================================================
|
||||
-- Member Management Schema Tests
|
||||
-- Verifies all tables, columns, and RLS settings
|
||||
-- =====================================================
|
||||
|
||||
-- 1. Core tables exist
|
||||
select has_table('public', 'members', 'members table exists');
|
||||
select has_table('public', 'dues_categories', 'dues_categories table exists');
|
||||
select has_table('public', 'membership_applications', 'membership_applications table exists');
|
||||
select has_table('public', 'member_cards', 'member_cards table exists');
|
||||
select has_table('public', 'member_departments', 'member_departments table exists');
|
||||
select has_table('public', 'member_department_assignments', 'member_department_assignments table exists');
|
||||
select has_table('public', 'member_roles', 'member_roles table exists');
|
||||
select has_table('public', 'member_honors', 'member_honors table exists');
|
||||
select has_table('public', 'sepa_mandates', 'sepa_mandates table exists');
|
||||
select has_table('public', 'member_portal_invitations', 'member_portal_invitations table exists');
|
||||
select has_table('public', 'member_transfers', 'member_transfers table exists');
|
||||
|
||||
-- 2. New Phase 1-4 tables exist
|
||||
select has_table('public', 'member_audit_log', 'member_audit_log table exists');
|
||||
select has_table('public', 'member_communications', 'member_communications table exists');
|
||||
select has_table('public', 'member_tags', 'member_tags table exists');
|
||||
select has_table('public', 'member_tag_assignments', 'member_tag_assignments table exists');
|
||||
select has_table('public', 'member_merges', 'member_merges table exists');
|
||||
select has_table('public', 'gdpr_retention_policies', 'gdpr_retention_policies table exists');
|
||||
select has_table('public', 'member_notification_rules', 'member_notification_rules table exists');
|
||||
select has_table('public', 'scheduled_job_configs', 'scheduled_job_configs table exists');
|
||||
select has_table('public', 'scheduled_job_runs', 'scheduled_job_runs table exists');
|
||||
select has_table('public', 'pending_member_notifications', 'pending_member_notifications table exists');
|
||||
|
||||
-- 3. New columns on members table
|
||||
select has_column('public', 'members', 'primary_mandate_id', 'members has primary_mandate_id column');
|
||||
select has_column('public', 'members', 'version', 'members has version column');
|
||||
|
||||
-- 4. New column on event_registrations
|
||||
select has_column('public', 'event_registrations', 'member_id', 'event_registrations has member_id FK');
|
||||
|
||||
-- 5. RLS enabled on all member tables
|
||||
select is(
|
||||
(select relrowsecurity from pg_class where relname = 'members' and relnamespace = 'public'::regnamespace),
|
||||
true, 'RLS enabled on members'
|
||||
);
|
||||
select is(
|
||||
(select relrowsecurity from pg_class where relname = 'member_audit_log' and relnamespace = 'public'::regnamespace),
|
||||
true, 'RLS enabled on member_audit_log'
|
||||
);
|
||||
select is(
|
||||
(select relrowsecurity from pg_class where relname = 'member_communications' and relnamespace = 'public'::regnamespace),
|
||||
true, 'RLS enabled on member_communications'
|
||||
);
|
||||
select is(
|
||||
(select relrowsecurity from pg_class where relname = 'member_tags' and relnamespace = 'public'::regnamespace),
|
||||
true, 'RLS enabled on member_tags'
|
||||
);
|
||||
select is(
|
||||
(select relrowsecurity from pg_class where relname = 'member_tag_assignments' and relnamespace = 'public'::regnamespace),
|
||||
true, 'RLS enabled on member_tag_assignments'
|
||||
);
|
||||
select is(
|
||||
(select relrowsecurity from pg_class where relname = 'member_notification_rules' and relnamespace = 'public'::regnamespace),
|
||||
true, 'RLS enabled on member_notification_rules'
|
||||
);
|
||||
select is(
|
||||
(select relrowsecurity from pg_class where relname = 'scheduled_job_configs' and relnamespace = 'public'::regnamespace),
|
||||
true, 'RLS enabled on scheduled_job_configs'
|
||||
);
|
||||
|
||||
-- 6. Key indexes exist
|
||||
select is(
|
||||
(select count(*) > 0 from pg_indexes where tablename = 'members' and indexname = 'ix_members_active_account_status'),
|
||||
true, 'Active members composite index exists'
|
||||
);
|
||||
select is(
|
||||
(select count(*) > 0 from pg_indexes where tablename = 'member_audit_log' and indexname = 'ix_member_audit_member'),
|
||||
true, 'Audit log member index exists'
|
||||
);
|
||||
|
||||
-- 7. Check constraints exist on members
|
||||
select is(
|
||||
(select count(*) > 0 from information_schema.check_constraints
|
||||
where constraint_name = 'chk_members_dob_not_future'),
|
||||
true, 'DOB not-future constraint exists'
|
||||
);
|
||||
select is(
|
||||
(select count(*) > 0 from information_schema.check_constraints
|
||||
where constraint_name = 'chk_members_exit_after_entry'),
|
||||
true, 'Exit-after-entry constraint exists'
|
||||
);
|
||||
|
||||
-- 8. Version column has correct default
|
||||
select is(
|
||||
(select column_default from information_schema.columns
|
||||
where table_name = 'members' and column_name = 'version'),
|
||||
'1', 'Version column defaults to 1'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
Reference in New Issue
Block a user