diff --git a/backend/schema.sql b/backend/schema.sql index e475b76..48957b8 100644 --- a/backend/schema.sql +++ b/backend/schema.sql @@ -885,3 +885,10 @@ revoke all on public.user_mcp_connector_tools from anon, authenticated; revoke all on public.user_mcp_tool_audit_logs from anon, authenticated; revoke all on public.courtlistener_citation_index from anon, authenticated; revoke all on public.courtlistener_opinion_cluster_index from anon, authenticated; + +-- Tables created by this file are owned by the database bootstrap role. The +-- backend connects as service_role, so grant it the privileges that the direct +-- browser roles above intentionally do not have. RLS is still enabled as +-- defense in depth; service_role bypasses it for the backend data path. +grant all privileges on all tables in schema public to service_role; +grant all privileges on all sequences in schema public to service_role; diff --git a/backend/scripts/test-stack.sh b/backend/scripts/test-stack.sh index 2f647a7..f75deae 100755 --- a/backend/scripts/test-stack.sh +++ b/backend/scripts/test-stack.sh @@ -9,6 +9,10 @@ # npm run test:stack (from backend/) set -euo pipefail +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +BACKEND_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" +SCHEMA_FILE="$BACKEND_DIR/schema.sql" + if ! command -v supabase >/dev/null 2>&1; then echo "supabase CLI not found. Install: brew install supabase/tap/supabase" >&2 exit 1 @@ -24,14 +28,35 @@ read_key() { node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>pro SUPABASE_TEST_URL="$(read_key API_URL)" SUPABASE_TEST_SERVICE_ROLE_KEY="$(read_key SERVICE_ROLE_KEY)" SUPABASE_TEST_ANON_KEY="$(read_key ANON_KEY)" +SUPABASE_TEST_DB_URL="$(read_key DB_URL)" -if [[ -z "$SUPABASE_TEST_URL" || -z "$SUPABASE_TEST_SERVICE_ROLE_KEY" || -z "$SUPABASE_TEST_ANON_KEY" ]]; then - echo "Could not read API_URL/SERVICE_ROLE_KEY/ANON_KEY from 'supabase status'." >&2 +if [[ -z "$SUPABASE_TEST_URL" || -z "$SUPABASE_TEST_SERVICE_ROLE_KEY" || -z "$SUPABASE_TEST_ANON_KEY" || -z "$SUPABASE_TEST_DB_URL" ]]; then + echo "Could not read API_URL/DB_URL/SERVICE_ROLE_KEY/ANON_KEY from 'supabase status'." >&2 exit 1 fi export SUPABASE_TEST_URL SUPABASE_TEST_SERVICE_ROLE_KEY SUPABASE_TEST_ANON_KEY +if ! command -v psql >/dev/null 2>&1; then + echo "psql not found. Install PostgreSQL's client tools before running stack tests." >&2 + exit 1 +fi + +# A newly started local stack contains Supabase's system schemas but none of +# Mike's application tables. Initialize only an empty stack: silently resetting +# or modifying an existing application database would be surprising. +PROJECTS_TABLE="$( + psql "$SUPABASE_TEST_DB_URL" -XAtq \ + -c "select to_regclass('public.projects');" +)" +if [[ "$PROJECTS_TABLE" != "projects" ]]; then + echo "Mike schema not found; loading $SCHEMA_FILE" + psql "$SUPABASE_TEST_DB_URL" -X \ + --set ON_ERROR_STOP=1 \ + --file "$SCHEMA_FILE" +fi + echo "Running stack integration tests against $SUPABASE_TEST_URL" +cd "$BACKEND_DIR" exec npx vitest run \ src/__tests__/integration/stack.supabase.test.ts \ src/__tests__/integration/access.supabase.test.ts \ diff --git a/backend/src/__tests__/integration/access.supabase.test.ts b/backend/src/__tests__/integration/access.supabase.test.ts index 65b9244..edfea6d 100644 --- a/backend/src/__tests__/integration/access.supabase.test.ts +++ b/backend/src/__tests__/integration/access.supabase.test.ts @@ -28,7 +28,7 @@ maybeDescribe("Supabase access integration", () => { const privateDocId = crypto.randomUUID(); try { - await admin.from("projects").insert([ + const projectsInsert = await admin.from("projects").insert([ { id: sharedProjectId, user_id: ownerId, @@ -42,9 +42,16 @@ maybeDescribe("Supabase access integration", () => { shared_with: [], }, ]); + if (projectsInsert.error) { + throw new Error( + `Could not seed projects: ${projectsInsert.error.message}`, + { cause: projectsInsert.error }, + ); + } + // filename/file_type live on document_versions in this schema — // the documents rows only need identity + ownership columns. - await admin.from("documents").insert([ + const documentsInsert = await admin.from("documents").insert([ { id: sharedDocId, user_id: ownerId, @@ -56,6 +63,12 @@ maybeDescribe("Supabase access integration", () => { project_id: privateProjectId, }, ]); + if (documentsInsert.error) { + throw new Error( + `Could not seed documents: ${documentsInsert.error.message}`, + { cause: documentsInsert.error }, + ); + } await expect( listAccessibleProjectIds(