/** * F-series: Eva has no collective membership. She must see zero rows * and receive errors on every write attempt. */ import { describe, it, expect } from 'vitest'; import { createClientAs, createAdminClient } from '../src/supabase-clients.js'; import { EVA_ID, COLLECTIVE_ID, SEED_LIST_ID } from '../src/seed-constants.js'; describe('RLS isolation — non-member user (Eva)', () => { it('F-01: cannot read any shopping lists', async () => { const eva = await createClientAs(EVA_ID); const { data, error } = await eva.from('shopping_lists').select('id'); expect(error).toBeNull(); expect(data).toHaveLength(0); }); it('F-02: cannot read any shopping items', async () => { const eva = await createClientAs(EVA_ID); const { data, error } = await eva.from('shopping_items').select('id'); expect(error).toBeNull(); expect(data).toHaveLength(0); }); it('F-03: cannot create a shopping list', async () => { const eva = await createClientAs(EVA_ID); const { data, error } = await eva .from('shopping_lists') .insert({ collective_id: COLLECTIVE_ID, name: 'Eva sneaky list', created_by: EVA_ID }) .select() .single(); expect(data).toBeNull(); expect(error).not.toBeNull(); }); it('F-04: cannot add an item to the seed list', async () => { const eva = await createClientAs(EVA_ID); const { data, error } = await eva .from('shopping_items') .insert({ list_id: SEED_LIST_ID, name: 'Spy item', sort_order: 99, created_by: EVA_ID }) .select() .single(); expect(data).toBeNull(); expect(error).not.toBeNull(); }); it('F-05: cannot read any collectives', async () => { const eva = await createClientAs(EVA_ID); const { data, error } = await eva.from('collectives').select('id'); expect(error).toBeNull(); expect(data).toHaveLength(0); }); it('F-06: cannot read collective members', async () => { const eva = await createClientAs(EVA_ID); const { data, error } = await eva.from('collective_members').select('user_id'); expect(error).toBeNull(); expect(data).toHaveLength(0); }); it('F-07: cannot read item_frequency suggestions', async () => { const eva = await createClientAs(EVA_ID); const { data, error } = await eva .from('item_frequency') .select('name') .eq('collective_id', COLLECTIVE_ID); expect(error).toBeNull(); expect(data).toHaveLength(0); }); it('F-08: cannot create a collective on behalf of another user', async () => { const eva = await createClientAs(EVA_ID); // Eva creating a collective with her own user id as created_by — this IS allowed // (any authenticated user can create a collective). But she cannot create one // and pretend it was created by someone else. const admin = createAdminClient(); const { data: evaCollective } = await eva .from('collectives') .insert({ name: 'Eva collective', emoji: '🐱', created_by: EVA_ID }) .select() .single(); // Clean up if somehow inserted if (evaCollective) { await admin.from('collectives').delete().eq('id', evaCollective.id); } // The INSERT policy requires created_by = auth.uid(), so inserting with a // different user ID is blocked. Inserting with her own ID is allowed. // Here we verify that inserting with someone else's ID fails. const { data: spoofed, error: spoofError } = await eva .from('collectives') .insert({ name: 'Spoof collective', emoji: '😈', created_by: 'aaaaaaaa-0000-0000-0000-000000000000' }) .select() .single(); expect(spoofed).toBeNull(); expect(spoofError).not.toBeNull(); }); });