import assert from "node:assert";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, it } from "node:test";
import { findCliModulesYaml, formatCommand, getBlockedCriticalDeps, isPnpmDlx, readBlockedBuilds, } from "./pnpm-lifecycle.js";
describe("pnpm-lifecycle", () => {
    let tmp;
    beforeEach(() => {
        tmp = mkdtempSync(path.join(os.tmpdir(), "zup-pnpm-"));
    });
    afterEach(() => {
        rmSync(tmp, { recursive: true, force: true });
    });
    function writeModulesYaml(contents, dir = tmp) {
        const nm = path.join(dir, "node_modules");
        mkdirSync(nm, { recursive: true });
        const file = path.join(nm, ".modules.yaml");
        writeFileSync(file, contents);
        return file;
    }
    describe("findCliModulesYaml", () => {
        it("finds .modules.yaml in the start directory", () => {
            const file = writeModulesYaml("ignoredBuilds: []\n");
            assert.strictEqual(findCliModulesYaml(tmp), file);
        });
        it("walks up to find .modules.yaml in an ancestor", () => {
            const file = writeModulesYaml("ignoredBuilds: []\n");
            const nested = path.join(tmp, "a", "b", "c");
            mkdirSync(nested, { recursive: true });
            assert.strictEqual(findCliModulesYaml(nested), file);
        });
        it("returns null when no .modules.yaml exists in tree", () => {
            const nested = path.join(tmp, "a", "b");
            mkdirSync(nested, { recursive: true });
            assert.strictEqual(findCliModulesYaml(nested), null);
        });
    });
    describe("readBlockedBuilds", () => {
        it("returns [] when file is missing", () => {
            const missing = path.join(tmp, "does-not-exist.yaml");
            assert.deepStrictEqual(readBlockedBuilds(missing), []);
        });
        it("returns [] for malformed YAML", () => {
            const file = writeModulesYaml("not: valid: yaml: [\n");
            assert.deepStrictEqual(readBlockedBuilds(file), []);
        });
        it("strips @version suffix from package names", () => {
            const file = writeModulesYaml("ignoredBuilds:\n  - deno@2.3.1\n  - workerd@1.20241230.0\n");
            const result = readBlockedBuilds(file).sort();
            assert.deepStrictEqual(result, ["deno", "workerd"]);
        });
        it("preserves leading @ on scoped packages", () => {
            const file = writeModulesYaml("ignoredBuilds:\n  - '@swc/core@1.10.18'\n");
            assert.deepStrictEqual(readBlockedBuilds(file), ["@swc/core"]);
        });
        it("merges and dedupes ignoredBuilds and pendingBuilds", () => {
            const file = writeModulesYaml([
                "ignoredBuilds:",
                "  - deno@2.3.1",
                "  - esbuild@0.25.2",
                "pendingBuilds:",
                "  - deno@2.3.1",
                "  - workerd@1.20241230.0",
                "",
            ].join("\n"));
            const result = readBlockedBuilds(file).sort();
            assert.deepStrictEqual(result, ["deno", "esbuild", "workerd"]);
        });
        it("returns [] when both lists are empty", () => {
            const file = writeModulesYaml("ignoredBuilds: []\npendingBuilds: []\n");
            assert.deepStrictEqual(readBlockedBuilds(file), []);
        });
    });
    describe("isPnpmDlx", () => {
        it("detects macOS pnpm dlx cache paths", () => {
            assert.strictEqual(isPnpmDlx("/Users/me/Library/Caches/pnpm/dlx/abc/node_modules/foo"), true);
        });
        it("detects Linux pnpm dlx cache paths", () => {
            assert.strictEqual(isPnpmDlx("/home/me/.local/share/pnpm/dlx/abc/node_modules/foo"), true);
        });
        it("detects Windows pnpm-cache dlx paths", () => {
            assert.strictEqual(isPnpmDlx("C:\\Users\\me\\AppData\\Local\\pnpm-cache\\dlx\\abc\\node_modules\\foo"), true);
        });
        it("returns false for ordinary project paths", () => {
            assert.strictEqual(isPnpmDlx("/Users/me/projects/foo/node_modules/.pnpm/bar"), false);
        });
        it("returns false for paths that contain dlx without pnpm", () => {
            assert.strictEqual(isPnpmDlx("/Users/me/dlx/something/node_modules"), false);
        });
    });
    describe("getBlockedCriticalDeps", () => {
        it("returns [] when no .modules.yaml is found", () => {
            assert.deepStrictEqual(getBlockedCriticalDeps(tmp), []);
        });
        it("returns all matching critical deps", () => {
            writeModulesYaml([
                "ignoredBuilds:",
                "  - deno@2.3.1",
                "  - protobufjs@7.5.6",
                "  - workerd@1.20241230.0",
                "  - esbuild@0.25.2",
                "  - '@swc/core@1.10.18'",
                "  - esbuild@0.25.12",
                "pendingBuilds: []",
                "",
            ].join("\n"));
            const result = getBlockedCriticalDeps(tmp);
            assert.deepStrictEqual(result, [
                "deno",
                "workerd",
                "esbuild",
                "@swc/core",
                "protobufjs",
            ]);
        });
        it("returns subset when only some criticals are blocked", () => {
            writeModulesYaml([
                "ignoredBuilds:",
                "  - '@swc/core@1.10.18'",
                "  - protobufjs@7.5.6",
                "pendingBuilds: []",
                "",
            ].join("\n"));
            assert.deepStrictEqual(getBlockedCriticalDeps(tmp), [
                "@swc/core",
                "protobufjs",
            ]);
        });
        it("ignores unrelated packages", () => {
            writeModulesYaml([
                "ignoredBuilds:",
                "  - some-other-pkg@1.0.0",
                "pendingBuilds: []",
                "",
            ].join("\n"));
            assert.deepStrictEqual(getBlockedCriticalDeps(tmp), []);
        });
    });
    describe("formatCommand", () => {
        it("returns <cmd> for empty positionals", () => {
            assert.strictEqual(formatCommand({ _: [] }), "<cmd>");
        });
        it("returns <cmd> when _ is missing entirely", () => {
            assert.strictEqual(formatCommand({}), "<cmd>");
        });
        it("returns the single command unchanged", () => {
            assert.strictEqual(formatCommand({ _: ["deploy"] }), "deploy");
        });
        it("preserves the full subcommand path", () => {
            assert.strictEqual(formatCommand({ _: ["project", "create", "my-app"] }), "project create my-app");
        });
        it("stringifies non-string entries", () => {
            assert.strictEqual(formatCommand({ _: [123, "create"] }), "123 create");
        });
        it("double-quotes positionals containing whitespace", () => {
            assert.strictEqual(formatCommand({ _: ["init", "my project"] }), 'init "my project"');
        });
        it("double-quotes positionals with shell metacharacters", () => {
            assert.strictEqual(formatCommand({
                _: ["delete", "https://api.example.com/foo?bar=1&baz=2"],
            }), 'delete "https://api.example.com/foo?bar=1&baz=2"');
        });
        it("escapes embedded double quotes via JSON encoding", () => {
            assert.strictEqual(formatCommand({ _: ["init", 'say "hi"'] }), 'init "say \\"hi\\""');
        });
        it("leaves embedded single quotes alone (JSON doesn't need to escape them)", () => {
            assert.strictEqual(formatCommand({ _: ["init", "don't"] }), `init "don't"`);
        });
        it("leaves dotted/URL-safe tokens unquoted", () => {
            assert.strictEqual(formatCommand({
                _: ["init", "./my-dir", "v1.2.3", "user@example.com"],
            }), "init ./my-dir v1.2.3 user@example.com");
        });
    });
});
//# sourceMappingURL=pnpm-lifecycle.test.js.map