2025-11-16 11:55:30 +00:00
|
|
|
begin;
|
|
|
|
|
select plan(19); -- Total number of tests
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- RLS Enabled Test
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
SELECT is(
|
|
|
|
|
(SELECT relrowsecurity FROM pg_class WHERE relname = 'notifications' AND relnamespace = 'public'::regnamespace),
|
|
|
|
|
true,
|
|
|
|
|
'RLS should be enabled on notifications table'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Notifications Table RLS Policies
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM pg_policies WHERE tablename = 'notifications' AND policyname = 'Users can view their own notifications') > 0,
|
|
|
|
|
'Policy for viewing own notifications should exist'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM pg_policies WHERE tablename = 'notifications' AND policyname = 'Users can mark their own notifications as read') > 0,
|
|
|
|
|
'Policy for marking notifications as read should exist'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Test policy commands
|
|
|
|
|
SELECT is(
|
|
|
|
|
(SELECT cmd FROM pg_policies WHERE tablename = 'notifications' AND policyname = 'Users can view their own notifications' LIMIT 1),
|
|
|
|
|
'SELECT',
|
|
|
|
|
'View notifications policy should be for SELECT'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SELECT is(
|
|
|
|
|
(SELECT cmd FROM pg_policies WHERE tablename = 'notifications' AND policyname = 'Users can mark their own notifications as read' LIMIT 1),
|
|
|
|
|
'UPDATE',
|
|
|
|
|
'Mark as read policy should be for UPDATE'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Test policy uses auth.uid() for isolation
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT qual::text FROM pg_policies
|
|
|
|
|
WHERE tablename = 'notifications'
|
|
|
|
|
AND policyname = 'Users can view their own notifications'
|
|
|
|
|
LIMIT 1) LIKE '%auth.uid()%',
|
|
|
|
|
'SELECT policy should use auth.uid() for user isolation'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Test UPDATE policy allows updating read_at
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT with_check::text FROM pg_policies
|
|
|
|
|
WHERE tablename = 'notifications'
|
|
|
|
|
AND policyname = 'Users can mark their own notifications as read'
|
|
|
|
|
LIMIT 1) LIKE '%read_at%',
|
|
|
|
|
'UPDATE policy should check read_at field'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Notifications Behavior Tests with Mock Data
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
-- Create test users and generate some notifications
|
|
|
|
|
DO $$
|
|
|
|
|
DECLARE
|
|
|
|
|
user1_id uuid := gen_random_uuid();
|
|
|
|
|
user2_id uuid := gen_random_uuid();
|
|
|
|
|
test_tablo_id text;
|
|
|
|
|
BEGIN
|
|
|
|
|
-- Insert test users
|
|
|
|
|
INSERT INTO auth.users (id, instance_id, aud, role, email, encrypted_password, email_confirmed_at, created_at, updated_at)
|
|
|
|
|
VALUES
|
|
|
|
|
(user1_id, '00000000-0000-0000-0000-000000000000', 'authenticated', 'authenticated', 'notifrlsuser1_' || user1_id::text || '@test.com', 'encrypted', now(), now(), now()),
|
|
|
|
|
(user2_id, '00000000-0000-0000-0000-000000000000', 'authenticated', 'authenticated', 'notifrlsuser2_' || user2_id::text || '@test.com', 'encrypted', now(), now(), now())
|
|
|
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
|
|
|
|
|
|
-- Insert test profiles
|
|
|
|
|
INSERT INTO public.profiles (id, email, first_name, last_name, short_user_id)
|
|
|
|
|
VALUES
|
|
|
|
|
(user1_id, 'notifrlsuser1_' || user1_id::text || '@test.com', 'NotifRLS', 'User1', substring(user1_id::text from 1 for 8)),
|
|
|
|
|
(user2_id, 'notifrlsuser2_' || user2_id::text || '@test.com', 'NotifRLS', 'User2', substring(user2_id::text from 1 for 8))
|
|
|
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
|
|
|
|
|
|
-- Set auth context to user1 for creating test data
|
|
|
|
|
PERFORM set_config('request.jwt.claims', json_build_object('sub', user1_id::text)::text, true);
|
|
|
|
|
|
|
|
|
|
-- Create a tablo as user1
|
|
|
|
|
INSERT INTO public.tablos (owner_id, name, status, position)
|
|
|
|
|
VALUES (user1_id, 'RLS Test Tablo', 'todo', 0)
|
|
|
|
|
RETURNING id INTO test_tablo_id;
|
|
|
|
|
|
|
|
|
|
-- Grant access to user2
|
|
|
|
|
INSERT INTO public.tablo_access (tablo_id, user_id, granted_by, is_active, is_admin)
|
|
|
|
|
VALUES (test_tablo_id, user2_id, user1_id, true, false);
|
|
|
|
|
|
|
|
|
|
-- Update tablo to trigger notification for user2
|
|
|
|
|
UPDATE public.tablos
|
|
|
|
|
SET name = 'RLS Test Tablo Updated'
|
|
|
|
|
WHERE id = test_tablo_id;
|
|
|
|
|
|
|
|
|
|
-- Store test IDs
|
|
|
|
|
PERFORM set_config('test.rls_user1_id', user1_id::text, true);
|
|
|
|
|
PERFORM set_config('test.rls_user2_id', user2_id::text, true);
|
|
|
|
|
PERFORM set_config('test.rls_tablo_id', test_tablo_id, true);
|
|
|
|
|
END $$;
|
|
|
|
|
|
|
|
|
|
-- Test: Notifications exist for test users
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM public.notifications
|
|
|
|
|
WHERE user_id = current_setting('test.rls_user2_id')::uuid) > 0,
|
|
|
|
|
'Notifications should exist for test user2'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Test: User2 received notification about tablo update
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM public.notifications
|
|
|
|
|
WHERE user_id = current_setting('test.rls_user2_id')::uuid
|
|
|
|
|
AND entity_type = 'tablos'
|
|
|
|
|
AND entity_id = current_setting('test.rls_tablo_id')) > 0,
|
|
|
|
|
'User2 should have notification for tablo update'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Test: Actor (user1) did not receive notification for their own action
|
|
|
|
|
SELECT is(
|
|
|
|
|
(SELECT COUNT(*)::integer FROM public.notifications
|
|
|
|
|
WHERE user_id = current_setting('test.rls_user1_id')::uuid
|
|
|
|
|
AND actor_id = current_setting('test.rls_user1_id')::uuid
|
|
|
|
|
AND entity_id = current_setting('test.rls_tablo_id')),
|
|
|
|
|
0,
|
|
|
|
|
'User should not receive notifications for their own actions'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Test: Notifications have proper structure
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM public.notifications
|
|
|
|
|
WHERE user_id = current_setting('test.rls_user2_id')::uuid
|
|
|
|
|
AND message IS NOT NULL
|
2025-11-16 21:28:07 +00:00
|
|
|
AND length(message->>'en') > 0
|
|
|
|
|
AND length(message->>'fr') > 0) > 0,
|
|
|
|
|
'Notifications should have non-empty messages in both en and fr'
|
2025-11-16 11:55:30 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM public.notifications
|
|
|
|
|
WHERE user_id = current_setting('test.rls_user2_id')::uuid
|
|
|
|
|
AND metadata IS NOT NULL
|
|
|
|
|
AND jsonb_typeof(metadata) = 'object') > 0,
|
|
|
|
|
'Notifications should have valid JSONB metadata'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM public.notifications
|
|
|
|
|
WHERE user_id = current_setting('test.rls_user2_id')::uuid
|
|
|
|
|
AND action_type IN ('created', 'updated')) > 0,
|
|
|
|
|
'Notifications should have valid action_type'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Test Notification Updates (Mark as Read)
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
-- Test: Update read_at on a notification
|
|
|
|
|
DO $$
|
|
|
|
|
DECLARE
|
|
|
|
|
test_notif_id uuid;
|
|
|
|
|
BEGIN
|
|
|
|
|
-- Get an unread notification for user2
|
|
|
|
|
SELECT id INTO test_notif_id
|
|
|
|
|
FROM public.notifications
|
|
|
|
|
WHERE user_id = current_setting('test.rls_user2_id')::uuid
|
|
|
|
|
AND read_at IS NULL
|
|
|
|
|
LIMIT 1;
|
|
|
|
|
|
|
|
|
|
IF test_notif_id IS NOT NULL THEN
|
|
|
|
|
-- Update the notification
|
|
|
|
|
UPDATE public.notifications
|
|
|
|
|
SET read_at = now()
|
|
|
|
|
WHERE id = test_notif_id;
|
|
|
|
|
|
|
|
|
|
PERFORM set_config('test.updated_notif_id', test_notif_id::text, true);
|
|
|
|
|
PERFORM set_config('test.notif_was_updated', 'true', true);
|
|
|
|
|
ELSE
|
|
|
|
|
PERFORM set_config('test.notif_was_updated', 'false', true);
|
|
|
|
|
PERFORM set_config('test.updated_notif_id', '00000000-0000-0000-0000-000000000000', true);
|
|
|
|
|
END IF;
|
|
|
|
|
END $$;
|
|
|
|
|
|
|
|
|
|
-- Verify the update worked
|
|
|
|
|
SELECT ok(
|
|
|
|
|
CASE
|
|
|
|
|
WHEN current_setting('test.notif_was_updated', true) = 'true' THEN
|
|
|
|
|
(SELECT read_at IS NOT NULL
|
|
|
|
|
FROM public.notifications
|
|
|
|
|
WHERE id = current_setting('test.updated_notif_id', true)::uuid
|
|
|
|
|
LIMIT 1)
|
|
|
|
|
ELSE true -- Skip if no notification was found
|
|
|
|
|
END,
|
|
|
|
|
'Notification should be updatable with read_at timestamp'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Test Data Isolation
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
-- Test: Count notifications for user1 vs user2
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM public.notifications WHERE user_id = current_setting('test.rls_user1_id')::uuid) >= 0,
|
|
|
|
|
'User1 notifications count should be valid'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SELECT ok(
|
|
|
|
|
(SELECT COUNT(*) FROM public.notifications WHERE user_id = current_setting('test.rls_user2_id')::uuid) > 0,
|
|
|
|
|
'User2 should have at least one notification'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
-- Foreign Key Constraints Tests
|
|
|
|
|
-- ============================================================================
|
|
|
|
|
|
|
|
|
|
SELECT has_fk('public', 'notifications', 'notifications should have foreign key constraints');
|
|
|
|
|
|
|
|
|
|
-- Test that notifications.user_id references auth.users.id
|
|
|
|
|
SELECT fk_ok(
|
|
|
|
|
'public', 'notifications', 'user_id',
|
|
|
|
|
'auth', 'users', 'id',
|
|
|
|
|
'notifications.user_id should reference auth.users.id'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Test that notifications.actor_id references auth.users.id
|
|
|
|
|
SELECT fk_ok(
|
|
|
|
|
'public', 'notifications', 'actor_id',
|
|
|
|
|
'auth', 'users', 'id',
|
|
|
|
|
'notifications.actor_id should reference auth.users.id'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
select * from finish();
|
|
|
|
|
rollback;
|