Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/good-job/__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";

function extensionPath(): string {
	return fileURLToPath(new URL("../index.ts", import.meta.url));
}

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

describe("good-job settings resolution", () => {
	let session: PiSession | undefined;
	let root: string | undefined;
	afterEach(async () => {
		await session?.dispose();
		session = undefined;
		vi.restoreAllMocks();
		if (root) rmSync(root, { recursive: true, force: true });
		root = undefined;
	});

	async function status(input: Case): Promise<string> {
		root = mkdtempSync(path.join(tmpdir(), "pi-ext-good-job-settings-"));
		if ("project" in input) {
			const dir = path.join(root, ".pi");
			mkdirSync(dir, { recursive: true });
			writeFileSync(
				path.join(dir, "settings.json"),
				JSON.stringify(input.project),
			);
		}
		session = await createPiSession({
			cwd: root,
			extensionPaths: [extensionPath()],
			...("user" in input
				? { userSettings: input.user as Record<string, unknown> }
				: {}),
		});
		if (input.untrusted)
			session.session.settingsManager.setProjectTrusted(false);
		const runner = session.session.extensionRunner;
		const gj = session.extensions[0]!.commands.get("gj")!;
		const ctx = runner.createContext();
		const notify = vi.spyOn(ctx.ui, "notify").mockImplementation(() => {});
		await gj.handler("status", ctx as never);
		return String(notify.mock.calls.at(-1)?.[0]);
	}

	const user = {
		"good-job": { model: "user/model", thinkingLevel: "low" },
	};

	it.each([
		["defaults", {}, "current", "provider default"],
		["user", { user }, "user/model", "low"],
		[
			"project over user per key",
			{ user, project: { "good-job": { thinkingLevel: "high" } } },
			"user/model",
			"high",
		],
		[
			"untrusted project ignored",
			{
				user,
				project: { "good-job": { model: "project/model" } },
				untrusted: true,
			},
			"user/model",
			"low",
		],
		[
			"old goodJob key ignored",
			{ user: { goodJob: { model: "old/model", thinkingLevel: "max" } } },
			"current",
			"provider default",
		],
	] satisfies [string, Case, string, string][])(
		"%s",
		async (_name, input, model, thinking) => {
			const message = await status(input);
			expect(message).toContain(`analysis model: ${model}`);
			expect(message).toContain(`thinking: ${thinking}`);
		},
	);

	it("resolves directory from settings", async () => {
		const message = await status({
			user: { "good-job": { directory: "  gj-store  " } },
		});
		expect(message).toContain(path.join(root ?? "", "gj-store"));
	});

	it.each([
		[
			"project thinkingLevel",
			{ user, project: { "good-job": { thinkingLevel: "invalid" } } },
			"Invalid good-job.thinkingLevel from project settings: must be minimal, low, medium, high, xhigh, or max",
		],
		[
			"user model",
			{ user: { "good-job": { model: "no-slash" } } },
			"Invalid good-job.model from user settings: must be provider/model",
		],
		[
			"user directory",
			{ user: { "good-job": { directory: " " } } },
			"Invalid good-job.directory from user settings: must be a non-empty path",
		],
	] satisfies [string, Case, string][])(
		"rejects invalid %s without falling through",
		async (_name, input, error) => {
			await expect(status(input)).rejects.toThrow(error);
		},
	);
});