Skip to content

feat: validate Supabase RLS policies with pgTAP and JS tests (#546)#741

Open
Sammy-Samy wants to merge 1 commit into
AnnabelJoe:mainfrom
Sammy-Samy:feat/rls-tests-546
Open

feat: validate Supabase RLS policies with pgTAP and JS tests (#546)#741
Sammy-Samy wants to merge 1 commit into
AnnabelJoe:mainfrom
Sammy-Samy:feat/rls-tests-546

Conversation

@Sammy-Samy

@Sammy-Samy Sammy-Samy commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #546 — Validate Supabase row-level security rules with tests.

What changed

File What
supabase/migrations/20240101000004_rls_tests.sql Replaced the 5-assert PL/pgSQL script with a full 28-test pgTAP suite
supabase/tests/rls_policies.sql New — standalone pgTAP file (28 tests), safe to run on any DB via supabase db test or psql
apps/web/src/__tests__/rls.test.ts New — JS/TS integration harness (32 tests, 7 describe blocks) using a mocked Supabase client
supabase/migrations/rollbacks/20240101000004_rls_tests.down.sql Deletes all fixture rows in dependency order
.github/workflows/supabase.yml Adds supabase db test step after supabase db reset

Test coverage (7 categories, 60 total tests)

  1. Own-data access — operator A can SELECT its own cooperative, meters, readings
  2. Cross-operator SELECT isolation — operator A gets 0 rows for any coop B table
  3. Cross-operator INSERT rejection — RLS error (code 42501) returned for writes into coop B
  4. Admin bypass — admin JWT sees rows from all cooperatives
  5. Unauthenticated denial — empty JWT → 0 rows on every RLS table + INSERT blocked
  6. Readings via meter join — readings scoped through parent meter's cooperative_id
  7. Certificate retirement visibility — retired cert visible only to its owner
  8. UPDATE / DELETE blocked across operators — cross-operator DML silently affects 0 rows

How to test locally

# pgTAP (requires supabase start)
supabase db reset && supabase db test

# JavaScript (no live DB required)
pnpm --filter @solarproof/web test

Checklist

  • pgTAP test file covers all 4 RLS-enabled tables (cooperatives, meters, readings, certificates)
  • Standalone test file usable without the migration process
  • JS/TS tests pass with mocked client (32/32 passing)
  • Rollback script cleans up all fixture data in dependency order
  • CI workflow runs pgTAP after every schema change
  • No pre-existing tests broken by this change

…Joe#546)

- Replace migration 004 with a full pgTAP suite (28 SQL tests)
  covering: own-data access, cross-operator SELECT/INSERT/UPDATE/DELETE
  isolation on cooperatives / meters / readings / certificates, admin
  bypass, unauthenticated denial, readings-via-meter-join scoping, and
  certificate retirement visibility.

- Add supabase/tests/rls_policies.sql — standalone pgTAP file (28 tests)
  wrapped in a BEGIN/ROLLBACK transaction so it is safe to run on any
  database independently of the migration process.

- Add apps/web/src/__tests__/rls.test.ts — JS/TS integration harness
  (32 tests across 7 describe blocks) using a mocked Supabase client;
  runs in CI without a live database.

- Update supabase/migrations/rollbacks/20240101000004_rls_tests.down.sql
  to delete all pgTAP fixture rows in dependency order.

- Update .github/workflows/supabase.yml to run `supabase db test`
  after `supabase db reset`, so pgTAP failures block CI.

Closes AnnabelJoe#546
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate Supabase row-level security rules with tests

1 participant