121 lines
5.1 KiB
JavaScript
121 lines
5.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { randomUUID } from 'node:crypto';
|
|
import { createClient } from '@supabase/supabase-js';
|
|
|
|
const url = process.env.SUPABASE_URL;
|
|
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
const publishableKey = process.env.SUPABASE_ANON_KEY ?? process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
assert(url && serviceKey && publishableKey, 'SUPABASE_URL, service role key and public key are required');
|
|
|
|
const service = createClient(url, serviceKey, {
|
|
auth: { persistSession: false, autoRefreshToken: false },
|
|
});
|
|
const email = `stt-quota-${randomUUID()}@example.invalid`;
|
|
const password = `Fixture-${randomUUID()}!`;
|
|
let userId = null;
|
|
let assertions = 0;
|
|
|
|
function ok(condition, message) {
|
|
assert(condition, message);
|
|
assertions += 1;
|
|
}
|
|
|
|
async function reserve(id) {
|
|
const { data, error } = await service.rpc('reserve_stt_quota', {
|
|
p_user_id: userId,
|
|
p_reservation_id: id,
|
|
});
|
|
assert.equal(error, null, error?.message);
|
|
return data;
|
|
}
|
|
|
|
async function finalize(id, succeeded) {
|
|
const { data, error } = await service.rpc('finalize_stt_quota', {
|
|
p_reservation_id: id,
|
|
p_succeeded: succeeded,
|
|
});
|
|
assert.equal(error, null, error?.message);
|
|
return data;
|
|
}
|
|
|
|
try {
|
|
const created = await service.auth.admin.createUser({ email, password, email_confirm: true });
|
|
assert.equal(created.error, null, created.error?.message);
|
|
userId = created.data.user.id;
|
|
|
|
const subscription = await service.from('subscriptions')
|
|
.update({ tier: 'free', status: 'active', overage_credits: 0 })
|
|
.eq('user_id', userId);
|
|
assert.equal(subscription.error, null, subscription.error?.message);
|
|
const usage = await service.from('daily_usage').insert({
|
|
user_id: userId,
|
|
date: new Date().toISOString().slice(0, 10),
|
|
feature: 'stt_transcribe',
|
|
count: 249,
|
|
});
|
|
assert.equal(usage.error, null, usage.error?.message);
|
|
|
|
const competingIds = Array.from({ length: 20 }, () => randomUUID());
|
|
const competing = await Promise.all(competingIds.map(reserve));
|
|
const winners = competing.filter((item) => item.allowed === true);
|
|
ok(winners.length === 1, 'twenty concurrent requests reserve exactly one remaining base unit');
|
|
ok(competing.filter((item) => item.status === 'denied').length === 19, 'all excess work is denied before provider use');
|
|
await finalize(winners[0].reservation_id, false);
|
|
|
|
const replayId = randomUUID();
|
|
const replay = await Promise.all(Array.from({ length: 10 }, () => reserve(replayId)));
|
|
ok(replay.every((item) => item.allowed && item.reservation_id === replayId), 'same request replay is idempotent');
|
|
const usageAfterReplay = await service.from('daily_usage')
|
|
.select('count')
|
|
.eq('user_id', userId)
|
|
.eq('feature', 'stt_transcribe')
|
|
.single();
|
|
assert.equal(usageAfterReplay.error, null, usageAfterReplay.error?.message);
|
|
ok(usageAfterReplay.data.count === 250, 'idempotent replay consumes one unit only');
|
|
await finalize(replayId, true);
|
|
|
|
const creditsUpdate = await service.from('subscriptions')
|
|
.update({ overage_credits: 3 })
|
|
.eq('user_id', userId);
|
|
assert.equal(creditsUpdate.error, null, creditsUpdate.error?.message);
|
|
const overage = await Promise.all(Array.from({ length: 10 }, () => reserve(randomUUID())));
|
|
const overageWinners = overage.filter((item) => item.allowed === true);
|
|
ok(overageWinners.length === 3, 'concurrent requests cannot overspend three overage credits');
|
|
ok(overageWinners.every((item) => item.consumed_from === 'overage'), 'base exhaustion uses only overage');
|
|
await finalize(overageWinners[0].reservation_id, true);
|
|
await Promise.all(overageWinners.slice(1).map((item) => finalize(item.reservation_id, false)));
|
|
|
|
const finalSubscription = await service.from('subscriptions')
|
|
.select('overage_credits')
|
|
.eq('user_id', userId)
|
|
.single();
|
|
const finalUsage = await service.from('daily_usage')
|
|
.select('count')
|
|
.eq('user_id', userId)
|
|
.eq('feature', 'stt_transcribe')
|
|
.single();
|
|
assert.equal(finalSubscription.error, null, finalSubscription.error?.message);
|
|
assert.equal(finalUsage.error, null, finalUsage.error?.message);
|
|
ok(finalSubscription.data.overage_credits === 2, 'two failed providers refund exactly two overage credits');
|
|
ok(finalUsage.data.count === 251, 'only the successful overage provider remains charged');
|
|
|
|
const userClient = createClient(url, publishableKey, {
|
|
auth: { persistSession: false, autoRefreshToken: false },
|
|
});
|
|
const signedIn = await userClient.auth.signInWithPassword({ email, password });
|
|
assert.equal(signedIn.error, null, signedIn.error?.message);
|
|
const forbiddenLedger = await userClient.from('stt_quota_reservations').select('id').limit(1);
|
|
ok(forbiddenLedger.error !== null, 'authenticated users cannot read the reservation ledger');
|
|
const forbiddenReserve = await userClient.rpc('reserve_stt_quota', {
|
|
p_user_id: userId,
|
|
p_reservation_id: randomUUID(),
|
|
});
|
|
ok(forbiddenReserve.error !== null, 'authenticated users cannot invoke reservation RPCs');
|
|
|
|
console.log(`stt quota integration GREEN: ${assertions} assertions`);
|
|
} finally {
|
|
if (userId) {
|
|
const removed = await service.auth.admin.deleteUser(userId);
|
|
assert.equal(removed.error, null, removed.error?.message);
|
|
}
|
|
}
|