mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
70 lines
4.1 KiB
TypeScript
70 lines
4.1 KiB
TypeScript
/**
|
|
* HS1 (`plugin-host-state.ts#budgetFor`'s seed read) — Plan 3j Task 3. Converted
|
|
* from a raw `better-sqlite3` connection to `PluginCapabilityAuditRepository`;
|
|
* this covers the seed's grouping (ai.complete + ai.extract vs notify.send),
|
|
* the UTC-day floor, and the `code = 'ok'` filter (a denied call never counts
|
|
* against the budget).
|
|
*/
|
|
import { describe, it, expect, beforeAll, beforeEach, afterAll, afterEach, vi } from 'vitest';
|
|
import { createSnapshotTestDb } from '../../helpers/db-mock';
|
|
import { resetTestDb } from '../../helpers/test-db';
|
|
import { createTestOrm, type TestOrm } from '../../helpers/test-orm';
|
|
import { PluginCapabilityAudit } from '../../../src/db/entities/PluginCapabilityAudit.entity';
|
|
import type { PluginCapabilityAuditRepository } from '../../../src/db/repositories/PluginCapabilityAudit.repository';
|
|
import { appendAudit } from '../../../src/nest/plugins/host/plugin-audit';
|
|
import { budgetFor, pluginBudgetUsage } from '../../../src/nest/plugins/host/plugin-host-state';
|
|
|
|
const testDb = createSnapshotTestDb();
|
|
let t: TestOrm;
|
|
let audit: PluginCapabilityAuditRepository;
|
|
|
|
beforeAll(async () => {
|
|
t = await createTestOrm(testDb);
|
|
audit = t.repo(PluginCapabilityAudit);
|
|
});
|
|
beforeEach(() => { resetTestDb(testDb); t.clear(); });
|
|
afterEach(() => { vi.restoreAllMocks(); });
|
|
afterAll(async () => { await t.close(); testDb.close(); });
|
|
|
|
// `budgetFor`'s `budgets` Map is deliberately module-level (see plugin-host-state.ts's
|
|
// class docstring) — it is NOT reset by `resetTestDb`/`t.clear()`, so each test below
|
|
// uses its own plugin id to avoid one test's seed being served (cached) to the next.
|
|
describe('budgetFor / pluginBudgetUsage seed read', () => {
|
|
it('HS-001: seeds ai (ai.complete + ai.extract) and notify counts separately from today\'s already-audited rows', async () => {
|
|
await appendAudit(audit, { pluginId: 'hs1', actingUserId: 1, method: 'ai.complete', resource: null, code: 'ok' });
|
|
await appendAudit(audit, { pluginId: 'hs1', actingUserId: 1, method: 'ai.extract', resource: null, code: 'ok' });
|
|
await appendAudit(audit, { pluginId: 'hs1', actingUserId: 1, method: 'notify.send', resource: null, code: 'ok' });
|
|
// A denied call must not count against the budget.
|
|
await appendAudit(audit, { pluginId: 'hs1', actingUserId: 1, method: 'ai.complete', resource: null, code: 'RESOURCE_FORBIDDEN' });
|
|
// A different plugin's calls must not bleed into this one's budget.
|
|
await appendAudit(audit, { pluginId: 'hs1-other', actingUserId: 1, method: 'ai.complete', resource: null, code: 'ok' });
|
|
|
|
const usage = await pluginBudgetUsage('hs1', audit);
|
|
expect(usage.ai).toBe(2);
|
|
expect(usage.notify).toBe(1);
|
|
});
|
|
|
|
it('HS-002: seeded only once per plugin per process lifetime — a later append is NOT re-counted on a second read', async () => {
|
|
await appendAudit(audit, { pluginId: 'hs2', actingUserId: 1, method: 'ai.complete', resource: null, code: 'ok' });
|
|
const first = await budgetFor('hs2', audit);
|
|
expect(first.used(Date.now()).ai).toBe(1);
|
|
// A direct .take() call (what a real dispatch does) advances the in-memory
|
|
// counter; the seed read itself is not re-run.
|
|
await appendAudit(audit, { pluginId: 'hs2', actingUserId: 1, method: 'ai.complete', resource: null, code: 'ok' });
|
|
const second = await budgetFor('hs2', audit);
|
|
expect(second).toBe(first); // same cached DailyBudget instance — no re-seed
|
|
expect(second.used(Date.now()).ai).toBe(1); // still 1: the second append was never taken through the budget
|
|
});
|
|
|
|
it("HS-003: ignores yesterday's rows — only today's UTC window seeds the count", async () => {
|
|
await appendAudit(audit, { pluginId: 'hs3', actingUserId: 1, method: 'ai.complete', resource: null, code: 'ok' });
|
|
// Backdate the row to yesterday, bypassing the repository (simulating a
|
|
// real prior day's audit history already on disk).
|
|
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
testDb.prepare("UPDATE plugin_capability_audit SET ts = ? WHERE plugin_id = 'hs3'").run(yesterday);
|
|
|
|
const usage = await pluginBudgetUsage('hs3', audit);
|
|
expect(usage.ai).toBe(0);
|
|
});
|
|
});
|