Ctrl K
ComunidadGuías…

test/verification-engine.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"use strict";

const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { EventEmitter } = require("events");
const { PassThrough } = require("stream");
const VerificationEngine = require("../lib/verification-engine");
const WorkspaceIndex = require("../lib/workspace-index");

function write(root, rel, text) {
  const file = path.join(root, rel);
  fs.mkdirSync(path.dirname(file), { recursive: true });
  fs.writeFileSync(file, text, "utf8");
}

function nodeCommand(source) {
  return "node -e " + JSON.stringify(source);
}

async function withWorkspace(fn) {
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "vrcloud-verify-"));
  const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "vrcloud-verify-index-"));
  try {
    write(root, "package.json", JSON.stringify({
      name: "fixture",
      scripts: { test: nodeCommand("process.exit(0)") },
    }));
    write(root, "lib/math.js", "exports.add = (a, b) => a + b;\n");
    write(root, "test/math.test.js", '"use strict";\nconst assert = require("assert");\nconst { add } = require("../lib/math");\nassert.strictEqual(add(1, 2), 3);\n');
    const index = new WorkspaceIndex({ workspace: root, dataDir });
    await index.ready;
    await fn(root, index);
    await index.close();
  } finally {
    fs.rmSync(root, { recursive: true, force: true });
    fs.rmSync(dataDir, { recursive: true, force: true });
  }
}

async function selectionAndReconciliation() {
  await withWorkspace(async (workspace, index) => {
    const selection = VerificationEngine.selectCommands({
      workspace,
      index,
      touchedFiles: ["lib/math.js"],
    });
    assert.strictEqual(selection.source, "manifest");
    assert.deepStrictEqual(selection.commands, ["npm run test"],
      "non-node:test files must use the package runner");

    write(workspace, "test/math.test.js",
      '"use strict";\nconst test = require("node:test");\nconst assert = require("assert");\n' +
      'const { add } = require("../lib/math");\ntest("add", () => assert.strictEqual(add(1, 2), 3));\n');
    await index.rescan();
    const standalone = VerificationEngine.selectCommands({
      workspace,
      index,
      touchedFiles: ["lib/math.js"],
    });
    const quotedMath = process.platform === "win32" ? '"test/math.test.js"' : "'test/math.test.js'";
    const expectedTestCommand = "node --test " + quotedMath;
    assert.deepStrictEqual(standalone.commands, [expectedTestCommand]);

    write(workspace, "package.json", JSON.stringify({
      name: "fixture",
      scripts: { test: "jest" },
    }));
    write(workspace, "test/jest.test.js", 'test("jest case", () => expect(true).toBe(true));\n');
    const framework = VerificationEngine.selectCommands({
      workspace,
      index,
      touchedFiles: ["test/jest.test.js"],
    });
    const quotedJest = process.platform === "win32" ? '"test/jest.test.js"' : "'test/jest.test.js'";
    assert.deepStrictEqual(framework.commands, ["npm run test -- --runTestsByPath " + quotedJest]);

    const engine = new VerificationEngine({ workspace, index, timeoutMs: 3000 });
    await engine.prepare({ runId: "selection" });
    write(workspace, "lib/math.js", "exports.add = (a, b) => Number(a) + Number(b);\n");
    write(workspace, "lib/new-file.js", "module.exports = true;\n");
    const result = await engine.verify({ runId: "selection", touchedFiles: [], attempt: 0 });
    assert.strictEqual(result.status, "passed", JSON.stringify(result, null, 2));
    assert.ok(result.touchedFiles.includes("lib/math.js"));
    assert.ok(result.touchedFiles.includes("lib/new-file.js"));
    assert.strictEqual(result.command, expectedTestCommand);
    engine.complete({ runId: "selection" });
  });
}

async function overridePassAndFail() {
  await withWorkspace(async (workspace, index) => {
    const engine = new VerificationEngine({ workspace, index, timeoutMs: 3000 });
    await engine.prepare({ runId: "override-pass" });
    const passCommand = nodeCommand("process.stdout.write('verified'); process.exit(0)");
    const passed = await engine.verify({
      runId: "override-pass", touchedFiles: ["lib/math.js"], verifyCmd: passCommand,
    });
    assert.strictEqual(passed.selection, "override");
    assert.strictEqual(passed.command, passCommand);
    assert.strictEqual(passed.exitCode, 0);
    assert.strictEqual(passed.stdout, "verified");
    assert.strictEqual(passed.status, "passed");

    await engine.prepare({ runId: "override-fail" });
    const failed = await engine.verify({
      runId: "override-fail",
      touchedFiles: ["lib/math.js"],
      verifyCmd: nodeCommand("process.stderr.write('broken'); process.exit(7)"),
    });
    assert.strictEqual(failed.passed, false);
    assert.strictEqual(failed.status, "failed");
    assert.strictEqual(failed.exitCode, 7);
    assert.strictEqual(failed.stderr, "broken");
    assert.ok(failed.issues[0].includes("broken"));
  });
}

async function skippedTimeoutCancelAndBounds() {
  await withWorkspace(async (workspace, index) => {
    const engine = new VerificationEngine({ workspace, index, timeoutMs: 120, maxOutput: 1024 });
    await engine.prepare({ runId: "skip" });
    const skipped = await engine.verify({ runId: "skip", touchedFiles: ["README.md"] });
    assert.strictEqual(skipped.passed, true);
    assert.strictEqual(skipped.status, "skipped");

    await engine.prepare({ runId: "timeout" });
    const timeout = await engine.verify({
      runId: "timeout", touchedFiles: ["lib/math.js"],
      verifyCmd: nodeCommand("setTimeout(() => {}, 5000)"),
    });
    assert.strictEqual(timeout.passed, false);
    assert.strictEqual(timeout.status, "timeout");
    assert.strictEqual(timeout.commands[0].timedOut, true);

    await engine.prepare({ runId: "cancel" });
    const controller = new AbortController();
    const pending = engine.verify({
      runId: "cancel", touchedFiles: ["lib/math.js"], signal: controller.signal,
      verifyCmd: nodeCommand("setTimeout(() => {}, 5000)"),
    });
    setTimeout(() => controller.abort(), 50);
    const cancelled = await pending;
    assert.strictEqual(cancelled.passed, false);
    assert.strictEqual(cancelled.status, "cancelled");
    assert.strictEqual(cancelled.commands[0].cancelled, true);

    const alreadyAborted = new AbortController();
    alreadyAborted.abort();
    const preCancelled = await VerificationEngine.runCommand(nodeCommand("process.exit(0)"), {
      workspace, signal: alreadyAborted.signal, timeoutMs: 3000,
    });
    assert.strictEqual(preCancelled.status, "cancelled");
    assert.strictEqual(preCancelled.cancelled, true);

    const fakeChild = new EventEmitter();
    fakeChild.stdout = new PassThrough();
    fakeChild.stderr = new PassThrough();
    fakeChild.exitCode = null;
    let terminationRequested = false;
    let closeSynchronized = false;
    const closeController = new AbortController();
    const closePending = VerificationEngine.runCommand("fake command", {
      workspace,
      signal: closeController.signal,
      timeoutMs: 3000,
      spawn: () => fakeChild,
      terminate: () => {
        terminationRequested = true;
        return () => {};
      },
    }).then((result) => {
      closeSynchronized = true;
      return result;
    });
    closeController.abort();
    await new Promise((resolve) => setImmediate(resolve));
    assert.strictEqual(terminationRequested, true);
    assert.strictEqual(closeSynchronized, false, "abort must wait for the child close event");
    fakeChild.emit("close", null);
    const closeResult = await closePending;
    assert.strictEqual(closeResult.status, "cancelled");

    const bounded = await VerificationEngine.runCommand(nodeCommand("process.stdout.write('x'.repeat(10000))"), {
      workspace, maxOutput: 1024, timeoutMs: 3000,
    });
    assert.strictEqual(bounded.passed, true);
    assert.ok(bounded.stdout.length < 1100);
    assert.ok(bounded.stdout.includes("truncated"));
  });
}

(async () => {
  await selectionAndReconciliation();
  await overridePassAndFail();
  await skippedTimeoutCancelAndBounds();
  console.log("verification-engine: ok");
})().catch((error) => {
  console.error(error);
  process.exit(1);
});