mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-31 04:07:52 +00:00
New sync-delta + sync-cost-estimate unit suites; rewritten cost-gate serial tests (auto-defer instead of exit 2, posture, off-switch, format split, single-source); parseUsdLimit/posture-aware shouldBlockSync; backfill cap-off + tokenmax-bypass + cooldown-still-refuses; config known-key acceptance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
223 lines
8.4 KiB
TypeScript
223 lines
8.4 KiB
TypeScript
/**
|
|
* Tests for src/core/embed-backfill-submit.ts (v0.40 D19).
|
|
*
|
|
* Validates the submission gate layer:
|
|
* - Default path: submits with priority 5 + idempotency bucket
|
|
* - Cooldown: refuses re-submission inside the window
|
|
* - Active-job: refuses while a same-source job is active/waiting
|
|
* - 24h spend cap: refuses when accumulated spend >= cap
|
|
* - Config overrides honored (per-test cap + cooldown)
|
|
* - Override knobs in opts honored (test seam)
|
|
*/
|
|
import { describe, test, expect, beforeAll, afterAll, beforeEach } from 'bun:test';
|
|
import { PGLiteEngine } from '../src/core/pglite-engine.ts';
|
|
import { resetPgliteState } from './helpers/reset-pglite.ts';
|
|
import {
|
|
submitEmbedBackfill,
|
|
COOLDOWN_CONFIG_KEY,
|
|
SPEND_CAP_CONFIG_KEY,
|
|
} from '../src/core/embed-backfill-submit.ts';
|
|
import { MinionQueue } from '../src/core/minions/queue.ts';
|
|
|
|
let engine: PGLiteEngine;
|
|
|
|
beforeAll(async () => {
|
|
engine = new PGLiteEngine();
|
|
await engine.connect({});
|
|
await engine.initSchema();
|
|
}, 30000); // 30s — PGLite WASM cold-start + 89 migrations exceeds 5s default
|
|
|
|
afterAll(async () => {
|
|
await engine.disconnect();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
// Surgical reset (mirrors test/minions.test.ts) — full TRUNCATE wipes the
|
|
// config table's `version` key that MinionQueue.ensureSchema() reads.
|
|
await engine.executeRaw('DELETE FROM minion_jobs');
|
|
});
|
|
|
|
describe('submitEmbedBackfill — happy path', () => {
|
|
test('submits with priority 5 + idempotency key on a clean source', async () => {
|
|
const result = await submitEmbedBackfill(engine, 'default', { reason: 'unit' });
|
|
expect(result.status).toBe('submitted');
|
|
expect(result.jobId).toBeDefined();
|
|
|
|
const queue = new MinionQueue(engine);
|
|
const job = await queue.getJob(result.jobId!);
|
|
expect(job).not.toBeNull();
|
|
expect(job!.name).toBe('embed-backfill');
|
|
expect(job!.priority).toBe(5);
|
|
expect((job!.data as { sourceId: string }).sourceId).toBe('default');
|
|
});
|
|
|
|
test('respects opts.priority override', async () => {
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
priority: -10,
|
|
});
|
|
expect(result.status).toBe('submitted');
|
|
const queue = new MinionQueue(engine);
|
|
const job = await queue.getJob(result.jobId!);
|
|
expect(job!.priority).toBe(-10);
|
|
});
|
|
});
|
|
|
|
describe('submitEmbedBackfill — cooldown gate', () => {
|
|
test('blocks re-submission while a same-source job is active', async () => {
|
|
const queue = new MinionQueue(engine);
|
|
// Seed an active job manually
|
|
await queue.add('embed-backfill', { sourceId: 'default' }, {});
|
|
await engine.executeRaw(
|
|
`UPDATE minion_jobs SET status='active' WHERE name='embed-backfill'`,
|
|
);
|
|
|
|
const result = await submitEmbedBackfill(engine, 'default', { reason: 'unit' });
|
|
expect(result.status).toBe('cooldown');
|
|
expect(result.cooldownRemainingSeconds).toBeUndefined();
|
|
});
|
|
|
|
test('blocks re-submission inside the cooldown window after recent finish', async () => {
|
|
const queue = new MinionQueue(engine);
|
|
const job = await queue.add('embed-backfill', { sourceId: 'default' }, {});
|
|
// Mark completed 1 minute ago
|
|
await engine.executeRaw(
|
|
`UPDATE minion_jobs SET status='completed', finished_at=NOW() - INTERVAL '1 minute' WHERE id=$1`,
|
|
[job.id],
|
|
);
|
|
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
cooldownMinOverride: 10, // 10min cooldown; 1min elapsed → blocked
|
|
});
|
|
expect(result.status).toBe('cooldown');
|
|
expect(result.cooldownRemainingSeconds).toBeGreaterThan(0);
|
|
expect(result.cooldownRemainingSeconds).toBeLessThanOrEqual(10 * 60);
|
|
});
|
|
|
|
test('allows re-submission after cooldown elapses', async () => {
|
|
const queue = new MinionQueue(engine);
|
|
const job = await queue.add('embed-backfill', { sourceId: 'default' }, {});
|
|
// Mark completed 11 minutes ago — past the 10-min cooldown
|
|
await engine.executeRaw(
|
|
`UPDATE minion_jobs SET status='completed', finished_at=NOW() - INTERVAL '11 minutes' WHERE id=$1`,
|
|
[job.id],
|
|
);
|
|
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
cooldownMinOverride: 10,
|
|
});
|
|
expect(result.status).toBe('submitted');
|
|
});
|
|
|
|
test('config-overridable cooldown (via embed.backfill_cooldown_min)', async () => {
|
|
await engine.setConfig(COOLDOWN_CONFIG_KEY, '60'); // 60min cooldown
|
|
const queue = new MinionQueue(engine);
|
|
const job = await queue.add('embed-backfill', { sourceId: 'default' }, {});
|
|
await engine.executeRaw(
|
|
`UPDATE minion_jobs SET status='completed', finished_at=NOW() - INTERVAL '30 minutes' WHERE id=$1`,
|
|
[job.id],
|
|
);
|
|
|
|
const result = await submitEmbedBackfill(engine, 'default', { reason: 'unit' });
|
|
expect(result.status).toBe('cooldown');
|
|
});
|
|
});
|
|
|
|
describe('submitEmbedBackfill — 24h spend cap', () => {
|
|
test('refuses when spend24hFn returns >= cap', async () => {
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
spendCapUsdOverride: 25,
|
|
spend24hFn: async () => 25,
|
|
});
|
|
expect(result.status).toBe('spend_capped');
|
|
expect(result.spend24hUsd).toBe(25);
|
|
expect(result.spendCapUsd).toBe(25);
|
|
});
|
|
|
|
test('admits when spend24hFn returns < cap', async () => {
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
spendCapUsdOverride: 25,
|
|
spend24hFn: async () => 24.99,
|
|
});
|
|
expect(result.status).toBe('submitted');
|
|
});
|
|
|
|
test('config-overridable spend cap (via embed.backfill_max_usd_per_source_24h)', async () => {
|
|
await engine.setConfig(SPEND_CAP_CONFIG_KEY, '5');
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
spend24hFn: async () => 5,
|
|
});
|
|
expect(result.status).toBe('spend_capped');
|
|
expect(result.spendCapUsd).toBe(5);
|
|
});
|
|
|
|
// v0.42.42.0 (#2139): off-switch + tokenmax bypass.
|
|
test('cap "off" → submits even at huge spend (Infinity cap never tripped)', async () => {
|
|
await engine.setConfig(SPEND_CAP_CONFIG_KEY, 'off');
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
spend24hFn: async () => 1e9,
|
|
});
|
|
expect(result.status).toBe('submitted');
|
|
});
|
|
|
|
test('0 falls back to the default cap (off semantics ≠ 0)', async () => {
|
|
await engine.setConfig(SPEND_CAP_CONFIG_KEY, '0');
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
spend24hFn: async () => 25, // == default $25 → capped
|
|
});
|
|
expect(result.status).toBe('spend_capped');
|
|
expect(result.spendCapUsd).toBe(25);
|
|
});
|
|
|
|
test('spend.posture=tokenmax bypasses the cap, marks spendCapBypassed', async () => {
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
postureOverride: 'tokenmax',
|
|
spendCapUsdOverride: 25,
|
|
spend24hFn: async () => 100, // way over cap
|
|
});
|
|
expect(result.status).toBe('submitted');
|
|
expect(result.spendCapBypassed).toBe(true);
|
|
expect(result.spend24hUsd).toBe(100);
|
|
});
|
|
|
|
test('tokenmax does NOT bypass the cooldown (axis split — churn protection stays)', async () => {
|
|
const queue = new MinionQueue(engine);
|
|
const job = await queue.add('embed-backfill', { sourceId: 'default' }, {});
|
|
await engine.executeRaw(
|
|
`UPDATE minion_jobs SET status='completed', finished_at=NOW() - INTERVAL '1 minute' WHERE id=$1`,
|
|
[job.id],
|
|
);
|
|
const result = await submitEmbedBackfill(engine, 'default', {
|
|
reason: 'unit',
|
|
postureOverride: 'tokenmax',
|
|
cooldownMinOverride: 10,
|
|
spend24hFn: async () => 1e9,
|
|
});
|
|
expect(result.status).toBe('cooldown'); // posture lifts the cap, NOT the cooldown
|
|
});
|
|
});
|
|
|
|
describe('submitEmbedBackfill — source isolation', () => {
|
|
test('cooldown is per-source, not global', async () => {
|
|
await engine.executeRaw(
|
|
`INSERT INTO sources (id, name, config) VALUES ('other', 'other', '{"federated":true}') ON CONFLICT (id) DO NOTHING`,
|
|
);
|
|
const queue = new MinionQueue(engine);
|
|
// Active job on 'default'
|
|
await queue.add('embed-backfill', { sourceId: 'default' }, {});
|
|
await engine.executeRaw(`UPDATE minion_jobs SET status='active' WHERE name='embed-backfill'`);
|
|
|
|
// Submit for 'other' — should NOT be blocked
|
|
const result = await submitEmbedBackfill(engine, 'other', { reason: 'unit' });
|
|
expect(result.status).toBe('submitted');
|
|
});
|
|
});
|