Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/quota/__tests__/settings.test.ts

Raw
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
	createPiSession,
	type PiSession,
} from "../../../test/fixture/create-pi-session.ts";

const extensionPath = fileURLToPath(new URL("../index.ts", import.meta.url));

type Case = {
	user?: unknown;
	project?: unknown;
	env?: string;
	untrusted?: boolean;
};

describe("quota.refreshIntervalMs resolution", () => {
	let t: PiSession | undefined;
	let root: string | undefined;
	afterEach(async () => {
		await t?.dispose();
		t = undefined;
		vi.restoreAllMocks();
		if (root) rmSync(root, { recursive: true, force: true });
		root = undefined;
	});

	async function start(input: Case) {
		root = mkdtempSync(path.join(tmpdir(), "pi-ext-quota-settings-"));
		if ("project" in input) {
			mkdirSync(path.join(root, ".pi"), { recursive: true });
			writeFileSync(
				path.join(root, ".pi", "settings.json"),
				JSON.stringify({ quota: { refreshIntervalMs: input.project } }),
			);
		}
		t = await createPiSession({
			cwd: root,
			extensionPaths: [extensionPath],
			env: { PI_QUOTA_REFRESH_INTERVAL_MS: input.env },
			...("user" in input
				? { userSettings: { quota: { refreshIntervalMs: input.user } } }
				: {}),
		});
		if (input.untrusted) t.session.settingsManager.setProjectTrusted(false);

		// Install a recording UI so `hasUI` is true and `mode` is tui (interactive
		// UI), capturing `ctx.ui.notify` calls.
		const uiCalls: { args: unknown[] }[] = [];
		t.session.extensionRunner.setUIContext(
			{
				notify: (...args: unknown[]) => uiCalls.push({ args }),
				setStatus: () => {},
				setWidget: () => {},
				theme: {
					fg: (_color: unknown, text: string) => text,
					bold: (text: string) => text,
				},
			} as never,
			"tui",
		);

		const runner = t.session.extensionRunner;
		await runner.emit({ type: "session_start", reason: "startup" });
		const setInterval = vi.spyOn(globalThis, "setInterval");
		await runner.emit({ type: "agent_start" });
		const warnings = uiCalls
			.filter((call) => call.args[1] === "warning")
			.map((call) => call.args[0]);
		return { interval: setInterval.mock.calls.at(-1)?.[1], warnings };
	}

	it.each([
		["default unchanged", {}, 300_000],
		["user value", { user: 60_000 }, 60_000],
		["env over settings", { user: 60_000, env: "20000" }, 20_000],
		["trusted project over user", { user: 60_000, project: 30_000 }, 30_000],
		[
			"untrusted project ignored",
			{ user: 60_000, project: 30_000, untrusted: true },
			60_000,
		],
	] satisfies [string, Case, number][])(
		"%s",
		async (_name, input, expected) => {
			const { interval, warnings } = await start(input);
			expect(interval).toBe(expected);
			expect(warnings).toEqual([]);
		},
	);

	it.each([
		[
			"string user",
			{ user: "60000" },
			"Invalid quota.refreshIntervalMs from user settings",
		],
		[
			"too small project",
			{ user: 60_000, project: 5_000 },
			"Invalid quota.refreshIntervalMs from project settings: expected an integer from 10000 to 2147483647",
		],
		[
			"fractional user",
			{ user: 10_000.5 },
			"Invalid quota.refreshIntervalMs from user settings: expected an integer from 10000 to 2147483647",
		],
		[
			"non-numeric env",
			{ user: 60_000, env: "soon" },
			"Invalid quota.refreshIntervalMs from environment variable PI_QUOTA_REFRESH_INTERVAL_MS",
		],
	] satisfies [string, Case, string][])(
		"rejects %s without falling through",
		async (_name, input, error) => {
			const { interval, warnings } = await start(input);
			expect(interval).toBe(300_000);
			expect(warnings).toEqual([`${error}; using 300000 ms`]);
		},
	);
});