"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.runTestsWithCoverage = runTestsWithCoverage;
/**
 * Shared spawn helper for per-pack `runTests()` implementations (D021).
 *
 * Each language pack's `coverage` capability declares an optional
 * `runTests()` method that materializes the on-disk artifact its
 * `gather()` later reads. The actual mechanics — spawn a shell
 * command, bracket with Date.now() for duration, surface exit code +
 * post-run artifact check, format the `RunTestsOutcome` discriminated
 * union — are identical across packs. This module owns those mechanics
 * so per-pack code stays compact (just "what command + what artifact").
 *
 * Stdio is inherited so the user sees test output stream live —
 * `vyuh-dxkit coverage` is a side-effecting CLI command, the user is
 * watching their test suite run, not consuming JSON.
 */
const child_process_1 = require("child_process");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
/**
 * Spawn a test-with-coverage command, time it, classify the outcome.
 *
 * Outcome rules:
 *   - `preflight` returned a reason → `unavailable`
 *   - spawn signals ENOENT (binary missing)   → `unavailable`
 *   - exit non-zero (test fail / compile err) → `failed`
 *   - exit zero AND artifact present         → `success`
 *   - exit zero BUT artifact missing          → `failed`
 *     (the user ran the right command but it didn't produce coverage —
 *     usually means simplecov / coverage-py / similar isn't actually
 *     wired into the test setup. The hint they need is "your test
 *     run succeeded but produced no coverage report" not "no test
 *     runner found.")
 */
function runTestsWithCoverage(args) {
    const { pack, cmd, cwd, artifact, timeoutMs = 600_000, preflight } = args;
    if (preflight) {
        const reason = preflight(cwd);
        if (reason) {
            return { kind: 'unavailable', reason };
        }
    }
    const start = Date.now();
    const result = (0, child_process_1.spawnSync)('/bin/bash', ['-c', cmd], {
        cwd,
        stdio: 'inherit',
        timeout: timeoutMs,
        // Some test runners parse TTY-ness for colorized output. Inheriting
        // stdio already plumbs TTY status through naturally.
    });
    const durationMs = Date.now() - start;
    // spawn-level failure: usually means /bin/bash is missing, or the
    // command's first token isn't on PATH. We treat these as "unavailable"
    // because they describe an environment problem the user can fix —
    // distinct from "tests ran and failed."
    if (result.error) {
        const err = result.error;
        if (err.code === 'ENOENT') {
            return {
                kind: 'unavailable',
                reason: `command not found: ${cmd.split(/\s+/)[0]}`,
            };
        }
        return {
            kind: 'failed',
            reason: `spawn error: ${err.message}`,
            durationMs,
        };
    }
    // Test runner returned non-zero. Could be compile failure, test
    // failure, or coverage-config errors. The user already saw the
    // output (inherited stdio); we just record the disposition.
    //
    // Special cases by bash convention: 127 = "command not found",
    // 126 = "found but not executable". These describe an environment
    // problem (a binary is missing from PATH) rather than a test failure,
    // so they get the `unavailable` framing — same as the direct-spawn
    // ENOENT path above. Without this re-mapping, the user sees
    // "test command exited with status 127" which is opaque; routing
    // through `unavailable` surfaces the actual binary name in the
    // CLI table.
    if (typeof result.status === 'number' && result.status !== 0) {
        const firstWord = cmd.trim().split(/\s+/)[0];
        if (result.status === 127) {
            return { kind: 'unavailable', reason: `command not found: ${firstWord}` };
        }
        if (result.status === 126) {
            return { kind: 'unavailable', reason: `command not executable: ${firstWord}` };
        }
        return {
            kind: 'failed',
            reason: `${pack}: test command exited with status ${result.status}`,
            durationMs,
        };
    }
    // Signal-terminated (timeout, SIGKILL, ...).
    if (result.signal) {
        return {
            kind: 'failed',
            reason: `${pack}: test command killed by signal ${result.signal}`,
            durationMs,
        };
    }
    // Locate the artifact. Function form takes precedence over string
    // form so packs with non-deterministic output paths can implement
    // arbitrary discovery logic.
    const artifactPath = typeof artifact === 'function' ? artifact(cwd) : artifact;
    if (!artifactPath || !fs.existsSync(path.join(cwd, artifactPath))) {
        return {
            kind: 'failed',
            reason: `${pack}: test command succeeded but no coverage artifact was produced. ` +
                `Expected ${typeof artifact === 'function' ? '<computed at runtime>' : artifact}. ` +
                `If this is a Ruby project, simplecov must be required + started in spec_helper.rb. ` +
                `If TypeScript, the test script may not be passing --coverage to the runner. ` +
                `If Python, ensure pytest --cov is configured.`,
            durationMs,
        };
    }
    return { kind: 'success', artifact: artifactPath, durationMs };
}
//# sourceMappingURL=run-tests-helper.js.map