xtablo-source/supabase/tests/database/10_rls_policies_notifications.test.sql
2025-11-16 22:28:07 +01:00

237 lines
8.8 KiB
PL/PgSQL

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
AND length(message->>'en') > 0
AND length(message->>'fr') > 0) > 0,
'Notifications should have non-empty messages in both en and fr'
);
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;