Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

scripts/codegen/pi-ext-debug.test.ts

Raw
import {
	chmodSync,
	existsSync,
	lstatSync,
	mkdirSync,
	mkdtempSync,
	readdirSync,
	readFileSync,
	rmSync,
	symlinkSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { sandboxEnv, withProcessEnv } from "../../test/harness";

// The template's own slug placeholder is the extension name under test.
const slug = "{{extension-slug}}";
const enable = `PI_${slug.toUpperCase().replaceAll("-", "_")}_DEBUG`;
const posix = process.platform !== "win32";

let root: string | undefined;
afterEach(() => {
	if (root) rmSync(root, { recursive: true, force: true });
	root = undefined;
	vi.resetModules();
});

function sandbox() {
	root = mkdtempSync(join(tmpdir(), "pi-ext-debug-"));
	const env = sandboxEnv(root, { env: { [enable]: "1" } });
	const directory = join(root, ".test-state", "pi-ext", "debug", slug);
	return { env, state: join(root, ".test-state"), directory };
}

/** Load a fresh template copy, log one event and one span, then close. */
function logOnce(env: NodeJS.ProcessEnv) {
	return withProcessEnv(env, async () => {
		vi.resetModules();
		const { defineDebug } = await import("./templates/pi-ext-debug.ts");
		const { dbg, span, closeDebug } = defineDebug({
			events: ["session.start"],
			spans: ["request"],
			fields: { count: "number" },
		} as const);
		expect(() => {
			dbg?.("session.start", { count: 1 });
			span?.("request")("finish", { count: 2 });
		}).not.toThrow();
		closeDebug();
	});
}

const records = (directory: string) =>
	readdirSync(directory).flatMap((file) =>
		readFileSync(join(directory, file), "utf8")
			.trim()
			.split("\n")
			.map((line) => JSON.parse(line)),
	);

describe("pi-ext-debug template directories", () => {
	it("creates missing private directories and a private file", async () => {
		const { env, directory } = sandbox();

		await logOnce(env);

		expect(records(directory).map((record) => record.event)).toEqual([
			"session.start",
			"request.start",
			"request.finish",
		]);
		if (posix) {
			for (const part of [
				join(directory, "..", ".."),
				join(directory, ".."),
				directory,
			])
				expect(lstatSync(part).mode & 0o777).toBe(0o700);
			const [file] = readdirSync(directory);
			expect(lstatSync(join(directory, file)).mode & 0o777).toBe(0o600);
		}
	});

	it("reuses existing private directories for a new file", async () => {
		const { env, directory } = sandbox();

		await logOnce(env);
		await logOnce(env);

		expect(readdirSync(directory)).toHaveLength(2);
		expect(records(directory)).toHaveLength(6);
	});

	it.runIf(posix)("refuses a directory readable by others", async () => {
		const { env, state, directory } = sandbox();
		mkdirSync(join(state, "pi-ext"), { recursive: true, mode: 0o700 });
		chmodSync(join(state, "pi-ext"), 0o755);

		await logOnce(env);

		expect(existsSync(directory)).toBe(false);
	});

	it.runIf(posix)("refuses a symlinked extension directory", async () => {
		const { env, state, directory } = sandbox();
		const target = join(state, "elsewhere");
		mkdirSync(target, { recursive: true, mode: 0o700 });
		mkdirSync(join(directory, ".."), { recursive: true, mode: 0o700 });
		chmodSync(join(state, "pi-ext"), 0o700);
		symlinkSync(target, directory);

		await logOnce(env);

		expect(readdirSync(target)).toEqual([]);
	});
});