d3ro-voice/server/supabase/tests/meeting-document-llm-quota-lease.integration.sql
Yun Chan ba9ef9741e fix: red-team round 3 hardening across desktop, mobile, core and server
Batch of red-team r3 fixes that were in the working tree before the
2026-09-28 design overhaul, committed as one unit with their tests.

- desktop main: STT timeouts and sidecar, voice recording store, sync
  (credentials, audio, knowledge reindex, push gates), runtime
  provisioner, update policy, AltGr keybindings, voice-command policy,
  dictionary file codec/limits, meeting transcript condensing and a
  local recording ledger so interrupted-session recovery only closes
  meetings this device recorded (a phone's live meeting is left alone).
- mobile: login CSRF via implicit token callbacks rejected, account
  deletion/retention, durable queue retention, knowledge realtime
  without unfiltered DELETE, meeting re-record failure paths, cloud STT
  client, preferences store/resync.
- core: text chunking splits long unbroken transcripts to fit, template
  field policy, dictionary limits, meeting markdown inline handling.
- server: payple webhook policy and cancellation order scope, meeting
  document generation quota, team RPC null-role guard, unified LLM
  quota in-flight accounting, knowledge chunk vector index, meeting
  re-record failure paths (migrations 20260929*).
- ci: portable/runtime feed gates, update-policy schema, Forgejo file
  delete and alias planning.

Four older tests are updated to the new contracts rather than the old
behavior: token-pair auth callbacks are rejected, knowledge realtime no
longer subscribes to DELETE, long transcript lines are split, and
meeting recovery requires the local recording ledger for empty rows.
2026-09-28 20:45:52 +09:00

341 lines
17 KiB
PL/PgSQL

\set ON_ERROR_STOP on
-- Regression (red-team r3-10): meeting-document claims and llm-proxy
-- reservations used two separate quota ledgers. A claim held its unit only as
-- a 'processing' row in meeting_document_generation_requests (advisory lock
-- seed 0), while reserve_llm_quota counted only daily_usage (seed 20260928).
-- An llm-proxy request could therefore take the unit a running document
-- generation already held; the document was paid for and then rejected by
-- commit with generation_quota_exceeded.
--
-- Since 20260929020000_unify_llm_quota_inflight a claim takes a
-- reserve_llm_quota lease, so both paths share one ledger (daily_usage +
-- llm_quota_reservations) and one lock (daily_usage_lock_v1). This file covers
-- the cross-path interleavings (document <-> llm-proxy in both orders, a mixed
-- burst at the limit, late commit after lease reclaim, legacy claims);
-- meeting-document-generation-quota.integration.sql covers the single path.
--
-- Local only: psql against the local Supabase stack. Runs in a transaction and
-- rolls back.
BEGIN;
CREATE OR REPLACE FUNCTION pg_temp.assert_true(condition boolean, message text)
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
IF condition IS NOT TRUE THEN
RAISE EXCEPTION 'assertion_failed: %', message;
END IF;
END;
$$;
-- Returns the claim payload, or {"error": SQLERRM} when the claim raises.
CREATE OR REPLACE FUNCTION pg_temp.try_claim(p_actor uuid, p_key uuid, p_meeting uuid, p_template uuid, p_model text)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
BEGIN
RETURN public.claim_meeting_document_generation_v1(
p_actor, p_key, p_meeting, p_template, 'Lease fixture document', p_model
);
EXCEPTION WHEN OTHERS THEN
RETURN jsonb_build_object('error', SQLERRM);
END;
$$;
-- Returns the commit payload, or {"error": SQLERRM} when the commit raises.
CREATE OR REPLACE FUNCTION pg_temp.try_commit(p_actor uuid, p_key uuid)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
BEGIN
RETURN public.commit_meeting_document_generation_v1(p_actor, p_key, 'Generated body', 10, 1, 1);
EXCEPTION WHEN OTHERS THEN
RETURN jsonb_build_object('error', SQLERRM);
END;
$$;
CREATE OR REPLACE FUNCTION pg_temp.usage_of(p_actor uuid, p_feature text)
RETURNS integer
LANGUAGE sql
AS $$
SELECT coalesce(sum(count), 0)::integer FROM public.daily_usage
WHERE user_id = p_actor AND feature = p_feature AND date = CURRENT_DATE;
$$;
INSERT INTO auth.users (
id, aud, role, email, encrypted_password, email_confirmed_at,
raw_app_meta_data, raw_user_meta_data, created_at, updated_at
) VALUES
(
'38000000-0000-4000-8000-000000000001', 'authenticated', 'authenticated',
'meeting-doc-lease-pro@example.invalid', crypt('fixture-password', gen_salt('bf')), now(),
'{"provider":"email","providers":["email"]}'::jsonb, '{}'::jsonb, now(), now()
),
(
'38000000-0000-4000-8000-000000000002', 'authenticated', 'authenticated',
'meeting-doc-lease-free@example.invalid', crypt('fixture-password', gen_salt('bf')), now(),
'{"provider":"email","providers":["email"]}'::jsonb, '{}'::jsonb, now(), now()
),
(
'38000000-0000-4000-8000-000000000003', 'authenticated', 'authenticated',
'meeting-doc-lease-unlimited@example.invalid', crypt('fixture-password', gen_salt('bf')), now(),
'{"provider":"email","providers":["email"]}'::jsonb, '{}'::jsonb, now(), now()
);
UPDATE public.subscriptions SET tier = 'pro', status = 'active', overage_credits = 0
WHERE user_id = '38000000-0000-4000-8000-000000000001';
UPDATE public.subscriptions SET tier = 'free', status = 'active', overage_credits = 0
WHERE user_id = '38000000-0000-4000-8000-000000000002';
UPDATE public.subscriptions SET tier = 'pro_plus', status = 'active', overage_credits = 0
WHERE user_id = '38000000-0000-4000-8000-000000000003';
INSERT INTO public.meetings (id, user_id, title, status, raw_transcript)
VALUES
('38100000-0000-4000-8000-000000000001', '38000000-0000-4000-8000-000000000001',
'Lease fixture meeting', 'completed', 'Speaker one talked about the roadmap.'),
('38100000-0000-4000-8000-000000000002', '38000000-0000-4000-8000-000000000002',
'Lease fixture meeting', 'completed', 'Speaker two talked about hiring.'),
('38100000-0000-4000-8000-000000000003', '38000000-0000-4000-8000-000000000003',
'Lease fixture meeting', 'completed', 'Speaker three talked about budgets.');
INSERT INTO public.user_templates (
id, user_id, template_kind, name, template_type, system_prompt, is_builtin
) VALUES
('38200000-0000-4000-8000-000000000001', '38000000-0000-4000-8000-000000000001',
'meeting_document', 'Lease fixture template', 'custom', 'Summarize the meeting.', false),
('38200000-0000-4000-8000-000000000002', '38000000-0000-4000-8000-000000000002',
'meeting_document', 'Lease fixture template', 'custom', 'Summarize the meeting.', false),
('38200000-0000-4000-8000-000000000003', '38000000-0000-4000-8000-000000000003',
'meeting_document', 'Lease fixture template', 'custom', 'Summarize the meeting.', false);
-- Pro user, one daily Opus unit left (49/50).
INSERT INTO public.daily_usage (user_id, date, feature, count)
VALUES ('38000000-0000-4000-8000-000000000001', CURRENT_DATE, 'llm_opus', 49);
-- 1. A running document holds the last unit against llm-proxy, and its commit
-- is not thrown away afterwards (the reported bug).
DO $$
DECLARE
actor constant uuid := '38000000-0000-4000-8000-000000000001';
meeting constant uuid := '38100000-0000-4000-8000-000000000001';
template constant uuid := '38200000-0000-4000-8000-000000000001';
opus constant text := 'claude-opus-4-6';
claim jsonb;
proxy jsonb;
committed jsonb;
request_row public.meeting_document_generation_requests;
BEGIN
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000001', meeting, template, opus);
PERFORM pg_temp.assert_true((claim->>'claimed')::boolean, 'last Opus unit can be claimed: ' || claim::text);
SELECT * INTO request_row FROM public.meeting_document_generation_requests
WHERE user_id = actor AND idempotency_key = '38300000-0000-4000-8000-000000000001';
PERFORM pg_temp.assert_true(request_row.llm_reservation_id IS NOT NULL,
'the claim records the LLM quota lease it holds');
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_opus') = 50,
'the in-flight claim is visible in the shared ledger');
-- The bug: llm-proxy used to see 49/50 here and reserve a second paid call.
proxy := public.reserve_llm_quota(actor, gen_random_uuid(), 'llm_opus', 50, 'daily');
PERFORM pg_temp.assert_true(NOT (proxy->>'allowed')::boolean,
'llm-proxy cannot take the unit a running document holds: ' || proxy::text);
committed := pg_temp.try_commit(actor, '38300000-0000-4000-8000-000000000001');
PERFORM pg_temp.assert_true(committed->>'error' IS NULL AND committed->'document' IS NOT NULL,
'the paid document is committed, not rejected: ' || committed::text);
PERFORM pg_temp.assert_true(committed->>'consumedFrom' = 'base', 'commit reports the base allowance');
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_opus') = 50,
'commit completes the lease without charging a second unit');
PERFORM pg_temp.assert_true(
(SELECT status FROM public.llm_quota_reservations WHERE id = request_row.llm_reservation_id) = 'completed',
'commit completes the lease');
-- Idempotent commit replay does not charge again.
committed := pg_temp.try_commit(actor, '38300000-0000-4000-8000-000000000001');
PERFORM pg_temp.assert_true((committed->>'idempotent')::boolean, 'commit replay is idempotent');
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_opus') = 50, 'commit replay charges nothing');
END;
$$;
-- 2. The reverse order: an llm-proxy reservation holds the last unit, so the
-- document claim is rejected before any provider work.
UPDATE public.daily_usage SET count = 49
WHERE user_id = '38000000-0000-4000-8000-000000000001' AND date = CURRENT_DATE AND feature = 'llm_opus';
DO $$
DECLARE
actor constant uuid := '38000000-0000-4000-8000-000000000001';
meeting constant uuid := '38100000-0000-4000-8000-000000000001';
template constant uuid := '38200000-0000-4000-8000-000000000001';
proxy_id constant uuid := '38500000-0000-4000-8000-000000000001';
proxy jsonb;
claim jsonb;
BEGIN
proxy := public.reserve_llm_quota(actor, proxy_id, 'llm_opus', 50, 'daily');
PERFORM pg_temp.assert_true((proxy->>'allowed')::boolean, 'llm-proxy takes the last unit');
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000002', meeting, template, 'claude-opus-4-6');
PERFORM pg_temp.assert_true(claim->>'error' = 'generation_quota_exceeded',
'a document claim cannot take the unit llm-proxy holds: ' || claim::text);
PERFORM pg_temp.assert_true(NOT EXISTS (
SELECT 1 FROM public.meeting_document_generation_requests
WHERE user_id = actor AND idempotency_key = '38300000-0000-4000-8000-000000000002'
), 'a rejected claim leaves no request row');
-- Once llm-proxy releases its lease, the document can be claimed.
PERFORM public.finalize_llm_quota(proxy_id, false);
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000002', meeting, template, 'claude-opus-4-6');
PERFORM pg_temp.assert_true((claim->>'claimed')::boolean, 'released unit can be claimed: ' || claim::text);
-- Replaying the in-flight key neither reserves again nor errors.
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000002', meeting, template, 'claude-opus-4-6');
PERFORM pg_temp.assert_true(
claim->>'error' IS NULL AND NOT (claim->>'claimed')::boolean AND claim->>'status' = 'processing',
'replaying the in-flight key reports processing: ' || claim::text);
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_opus') = 50, 'a replay holds no extra unit');
-- Failure releases the unit back to the shared ledger, once.
PERFORM public.fail_meeting_document_generation_v1(actor, '38300000-0000-4000-8000-000000000002', 'provider_timeout');
PERFORM public.fail_meeting_document_generation_v1(actor, '38300000-0000-4000-8000-000000000002', 'provider_timeout');
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_opus') = 49,
'failing a claim releases exactly one unit');
proxy := public.reserve_llm_quota(actor, gen_random_uuid(), 'llm_opus', 50, 'daily');
PERFORM pg_temp.assert_true((proxy->>'allowed')::boolean, 'the released unit is usable by llm-proxy');
END;
$$;
-- 3. Interleaved burst: document claims and llm-proxy reservations together
-- never exceed the remaining allowance, and one overage credit is spent once.
UPDATE public.daily_usage SET count = 45
WHERE user_id = '38000000-0000-4000-8000-000000000001' AND date = CURRENT_DATE AND feature = 'llm_opus';
UPDATE public.llm_quota_reservations SET status = 'completed'
WHERE user_id = '38000000-0000-4000-8000-000000000001' AND status = 'reserved';
UPDATE public.subscriptions SET overage_credits = 1
WHERE user_id = '38000000-0000-4000-8000-000000000001';
DO $$
DECLARE
actor constant uuid := '38000000-0000-4000-8000-000000000001';
meeting constant uuid := '38100000-0000-4000-8000-000000000001';
template constant uuid := '38200000-0000-4000-8000-000000000001';
granted integer := 0;
outcome jsonb;
i integer;
BEGIN
FOR i IN 1..10 LOOP
IF i % 2 = 0 THEN
outcome := public.reserve_llm_quota(actor, gen_random_uuid(), 'llm_opus', 50, 'daily');
IF (outcome->>'allowed')::boolean THEN granted := granted + 1; END IF;
ELSE
outcome := pg_temp.try_claim(actor, gen_random_uuid(), meeting, template, 'claude-opus-4-6');
IF coalesce((outcome->>'claimed')::boolean, false) THEN granted := granted + 1; END IF;
END IF;
END LOOP;
PERFORM pg_temp.assert_true(granted = 6,
'5 base units + 1 overage credit admit exactly 6 paid calls, got ' || granted);
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_opus') = 51, 'shared ledger records every admitted call');
PERFORM pg_temp.assert_true(
(SELECT overage_credits FROM public.subscriptions WHERE user_id = actor) = 0,
'the overage credit is spent once');
END;
$$;
-- 4. Lease handling: an expired claim lease stops holding its unit, and a late
-- commit re-checks the allowance instead of charging past it.
DO $$
DECLARE
actor constant uuid := '38000000-0000-4000-8000-000000000002';
meeting constant uuid := '38100000-0000-4000-8000-000000000002';
template constant uuid := '38200000-0000-4000-8000-000000000002';
haiku constant text := 'claude-haiku-4-5-20251001';
claim jsonb;
proxy jsonb;
committed jsonb;
late_id uuid;
BEGIN
INSERT INTO public.daily_usage (user_id, date, feature, count)
VALUES (actor, CURRENT_DATE - 3, 'llm_haiku', 249);
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000010', meeting, template, haiku);
PERFORM pg_temp.assert_true((claim->>'claimed')::boolean, 'last weekly Haiku unit can be claimed');
-- Crashed worker: its lease expires, and the next reservation reclaims it.
SELECT llm_reservation_id INTO late_id FROM public.meeting_document_generation_requests
WHERE user_id = actor AND idempotency_key = '38300000-0000-4000-8000-000000000010';
UPDATE public.llm_quota_reservations SET lease_expires_at = now() - interval '1 minute' WHERE id = late_id;
proxy := public.reserve_llm_quota(actor, gen_random_uuid(), 'llm_haiku', 250, 'weekly');
PERFORM pg_temp.assert_true((proxy->>'allowed')::boolean,
'an expired document lease no longer holds a unit: ' || proxy::text);
-- The late commit finds its lease released and the allowance spent: reject
-- rather than charge a unit that is no longer there.
committed := pg_temp.try_commit(actor, '38300000-0000-4000-8000-000000000010');
PERFORM pg_temp.assert_true(committed->>'error' = 'generation_quota_exceeded',
'a late commit past the allowance is rejected: ' || committed::text);
-- With an overage credit the late commit is charged again and succeeds.
UPDATE public.subscriptions SET overage_credits = 1 WHERE user_id = actor;
committed := pg_temp.try_commit(actor, '38300000-0000-4000-8000-000000000010');
PERFORM pg_temp.assert_true(committed->>'error' IS NULL AND committed->>'consumedFrom' = 'overage',
'a late commit re-charges through the shared ledger: ' || committed::text);
PERFORM pg_temp.assert_true(
(SELECT overage_credits FROM public.subscriptions WHERE user_id = actor) = 0,
'the late commit spent the overage credit');
PERFORM pg_temp.assert_true(
(SELECT sum(count) FROM public.daily_usage WHERE user_id = actor AND feature = 'llm_haiku') = 251,
'the late commit is recorded once');
END;
$$;
-- 5. Claims made before this migration (no lease) still commit through the
-- shared ledger, and fail without touching it.
DO $$
DECLARE
actor constant uuid := '38000000-0000-4000-8000-000000000003';
meeting constant uuid := '38100000-0000-4000-8000-000000000003';
template constant uuid := '38200000-0000-4000-8000-000000000003';
lease uuid;
claim jsonb;
committed jsonb;
BEGIN
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000020', meeting, template, 'claude-sonnet-4-6');
PERFORM pg_temp.assert_true((claim->>'claimed')::boolean, 'pro_plus Sonnet claim');
-- Turn it into a legacy claim: no lease, nothing recorded yet.
SELECT llm_reservation_id INTO lease FROM public.meeting_document_generation_requests
WHERE user_id = actor AND idempotency_key = '38300000-0000-4000-8000-000000000020';
UPDATE public.meeting_document_generation_requests SET llm_reservation_id = NULL
WHERE user_id = actor AND idempotency_key = '38300000-0000-4000-8000-000000000020';
PERFORM public.finalize_llm_quota(lease, false);
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_sonnet') = 0, 'legacy claim holds nothing');
committed := pg_temp.try_commit(actor, '38300000-0000-4000-8000-000000000020');
PERFORM pg_temp.assert_true(committed->>'error' IS NULL AND committed->>'consumedFrom' = 'base',
'a legacy claim commits: ' || committed::text);
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_sonnet') = 1, 'a legacy commit records one unit');
-- Legacy failure is a no-op on the ledger.
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000021', meeting, template, 'claude-sonnet-4-6');
SELECT llm_reservation_id INTO lease FROM public.meeting_document_generation_requests
WHERE user_id = actor AND idempotency_key = '38300000-0000-4000-8000-000000000021';
UPDATE public.meeting_document_generation_requests SET llm_reservation_id = NULL
WHERE user_id = actor AND idempotency_key = '38300000-0000-4000-8000-000000000021';
PERFORM public.finalize_llm_quota(lease, false);
PERFORM public.fail_meeting_document_generation_v1(actor, '38300000-0000-4000-8000-000000000021', 'provider_timeout');
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_sonnet') = 1, 'a legacy failure changes nothing');
-- Unlimited allowances are never throttled and still release on failure.
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000022', meeting, template, 'claude-haiku-4-5-20251001');
PERFORM pg_temp.assert_true((claim->>'claimed')::boolean, 'unlimited Haiku claim');
claim := pg_temp.try_claim(actor, '38300000-0000-4000-8000-000000000023', meeting, template, 'claude-haiku-4-5-20251001');
PERFORM pg_temp.assert_true((claim->>'claimed')::boolean, 'unlimited allowances are not throttled');
PERFORM public.fail_meeting_document_generation_v1(actor, '38300000-0000-4000-8000-000000000023', 'provider_timeout');
PERFORM pg_temp.assert_true(pg_temp.usage_of(actor, 'llm_haiku') = 1, 'unlimited failure releases its unit');
END;
$$;
ROLLBACK;