-- pgTAP: auth.users role/aud defaulting (migrations 013 + 014)
-- Run with: psql -U postgres -d postgres -f supabase/tests/008_auth_user_role_default.sql
--
-- Covers two bugs observed on ambrosio:
--  * 2026-04-15 — GoTrue v2.158.1 inserts OIDC-created users into auth.users
--    with empty-string `role` and `aud`, later making PostgREST's
--    `SET LOCAL role = ''` fail with `role "" does not exist`.
--    Migration 013 adds a BEFORE INSERT trigger + backfills.
--  * 2026-04-20 — GoTrue additionally emits an UPDATE right after the
--    insert (pop ORM resends the whole struct incl. role='') which clobbers
--    the role that 013's trigger just filled in. Migration 014 adds a
--    matching BEFORE UPDATE trigger.

CREATE EXTENSION IF NOT EXISTS pgtap;

BEGIN;
SELECT plan(11);

-- ── Existence assertions ────────────────────────────────────────────────
SELECT has_function(
    'auth', 'ensure_user_role_default', ARRAY[]::text[],
    'ensure_user_role_default trigger function exists in the auth schema'
);

SELECT has_trigger(
    'auth', 'users', 'ensure_role_default',
    'BEFORE INSERT trigger ensure_role_default exists on auth.users'
);

SELECT has_trigger(
    'auth', 'users', 'ensure_role_default_update',
    'BEFORE UPDATE trigger ensure_role_default_update exists on auth.users'
);

-- ── Backfill assertion ──────────────────────────────────────────────────
SELECT ok(
    (SELECT count(*) FROM auth.users WHERE role IS NULL OR role = '' OR aud IS NULL OR aud = '') = 0,
    'No pre-existing auth.users row has an empty role or aud'
);

-- ── Trigger behavior ────────────────────────────────────────────────────
-- Insert a synthetic user with empty role/aud; the trigger should fill both.
-- Drop the AFTER trigger locally so we don't exercise handle_new_user's
-- side-effects (tested elsewhere, not the focus here).
ALTER TABLE auth.users DISABLE TRIGGER on_auth_user_created;

-- NEW row with empty strings
INSERT INTO auth.users (instance_id, id, email, role, aud)
VALUES (
    '00000000-0000-0000-0000-000000000000',
    'f0000000-0000-0000-0000-000000000001',
    'empty-role@test.local',
    '',
    ''
);
SELECT is(
    (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000001'),
    'authenticated',
    'Empty role on INSERT is defaulted to authenticated'
);
SELECT is(
    (SELECT aud FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000001'),
    'authenticated',
    'Empty aud on INSERT is defaulted to authenticated'
);

-- NEW row with NULL values
INSERT INTO auth.users (instance_id, id, email, role, aud)
VALUES (
    '00000000-0000-0000-0000-000000000000',
    'f0000000-0000-0000-0000-000000000002',
    'null-role@test.local',
    NULL,
    NULL
);
SELECT is(
    (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000002'),
    'authenticated',
    'NULL role on INSERT is defaulted to authenticated'
);

-- Explicit non-empty role is preserved (trigger should only fill when empty).
INSERT INTO auth.users (instance_id, id, email, role, aud)
VALUES (
    '00000000-0000-0000-0000-000000000000',
    'f0000000-0000-0000-0000-000000000003',
    'custom-role@test.local',
    'service_role',
    'authenticated'
);
SELECT is(
    (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000003'),
    'service_role',
    'Explicit role is preserved by the trigger'
);

-- ── UPDATE trigger behavior (migration 014) ────────────────────────────
-- Simulate GoTrue's pop-ORM resending role='' on the follow-up UPDATE.
-- The BEFORE UPDATE trigger must rewrite it to 'authenticated'.
UPDATE auth.users
SET role = '', aud = ''
WHERE id = 'f0000000-0000-0000-0000-000000000001';
SELECT is(
    (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000001'),
    'authenticated',
    'UPDATE clearing role to empty is reverted to authenticated'
);
SELECT is(
    (SELECT aud FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000001'),
    'authenticated',
    'UPDATE clearing aud to empty is reverted to authenticated'
);

-- And a legitimate UPDATE to some other field (e.g. raw_user_meta_data)
-- does not change role/aud.
UPDATE auth.users
SET raw_user_meta_data = '{"locale":"es"}'::jsonb
WHERE id = 'f0000000-0000-0000-0000-000000000003';
SELECT is(
    (SELECT role FROM auth.users WHERE id = 'f0000000-0000-0000-0000-000000000003'),
    'service_role',
    'Unrelated UPDATE leaves an explicit role untouched'
);

SELECT * FROM finish();
ROLLBACK;
