// acceptance grid for apt-resolve.ts.
//
// Locks the four resolver branches plus the os-release parser
// and Candidate-line extractor. Inputs are passed directly so no mocking is
// needed — every test exercises pure logic with concrete fixtures captured
// from real devices (Noble `apt-cache policy chromium`, Bookworm ditto,
// Pop!_OS `/etc/os-release`).
//
// Runs via Node's built-in test runner; no vitest dependency. Compiles to
// dist/__tests__/apt-resolve.test.js alongside the rest of the package so
// `node --test dist/__tests__/*.test.js` picks it up after build.
import test from "node:test";
import assert from "node:assert/strict";
import { UBUNTU_ALIASES, parseOsRelease, isUbuntuLike, parseAptCacheCandidate, decideAptResolution, } from "../apt-resolve.js";
// ---------------------------------------------------------------------------
// Real fixtures — captured from devices the installer actually targets.
// ---------------------------------------------------------------------------
const UBUNTU_NOBLE_OS_RELEASE = `PRETTY_NAME="Ubuntu 24.04 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
UBUNTU_CODENAME=noble`;
const DEBIAN_BOOKWORM_OS_RELEASE = `PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"`;
const POP_OS_OS_RELEASE = `NAME="Pop!_OS"
VERSION="22.04 LTS"
ID=pop
ID_LIKE="ubuntu debian"
PRETTY_NAME="Pop!_OS 22.04 LTS"
VERSION_ID="22.04"
VERSION_CODENAME=jammy
UBUNTU_CODENAME=jammy`;
// `apt-cache policy chromium` on Ubuntu Noble (the alias case).
const NOBLE_CHROMIUM_POLICY = `chromium:
  Installed: (none)
  Candidate: (none)
  Version table:`;
// `apt-cache policy chromium` on Debian Bookworm (concrete .deb, no alias).
const BOOKWORM_CHROMIUM_POLICY = `chromium:
  Installed: (none)
  Candidate: 120.0.6099.224-1~deb12u1
  Version table:
     120.0.6099.224-1~deb12u1 500
        500 http://deb.debian.org/debian bookworm-security/main arm64 Packages`;
// ---------------------------------------------------------------------------
// parseOsRelease — every key/value pair lands, quoted values get unquoted.
// ---------------------------------------------------------------------------
test("parseOsRelease unquotes values and captures all keys", () => {
    const noble = parseOsRelease(UBUNTU_NOBLE_OS_RELEASE);
    assert.equal(noble.ID, "ubuntu");
    assert.equal(noble.ID_LIKE, "debian");
    assert.equal(noble.VERSION_CODENAME, "noble");
    assert.equal(noble.NAME, "Ubuntu", "double-quoted values must be unquoted");
    assert.equal(noble.PRETTY_NAME, "Ubuntu 24.04 LTS");
});
test("parseOsRelease handles space-separated ID_LIKE (Pop!_OS)", () => {
    const pop = parseOsRelease(POP_OS_OS_RELEASE);
    assert.equal(pop.ID, "pop");
    assert.equal(pop.ID_LIKE, "ubuntu debian", "quoted multi-value must be preserved as a single string");
});
// ---------------------------------------------------------------------------
// isUbuntuLike — Ubuntu, Pop!_OS pick up; Debian does not.
// ---------------------------------------------------------------------------
test("isUbuntuLike accepts Ubuntu directly and via ID_LIKE", () => {
    assert.equal(isUbuntuLike(parseOsRelease(UBUNTU_NOBLE_OS_RELEASE)), true, "Ubuntu Noble: ID=ubuntu");
    assert.equal(isUbuntuLike(parseOsRelease(POP_OS_OS_RELEASE)), true, "Pop!_OS: ID_LIKE contains ubuntu");
    assert.equal(isUbuntuLike(parseOsRelease(DEBIAN_BOOKWORM_OS_RELEASE)), false, "Debian Bookworm: not Ubuntu-like");
    assert.equal(isUbuntuLike({}), false, "empty os-release (read failed): not Ubuntu-like");
});
// ---------------------------------------------------------------------------
// parseAptCacheCandidate — three states: real candidate, "(none)", absent.
// ---------------------------------------------------------------------------
test("parseAptCacheCandidate distinguishes real candidate, (none), and missing", () => {
    assert.equal(parseAptCacheCandidate(BOOKWORM_CHROMIUM_POLICY), "120.0.6099.224-1~deb12u1");
    assert.equal(parseAptCacheCandidate(NOBLE_CHROMIUM_POLICY), "(none)");
    assert.equal(parseAptCacheCandidate(""), null, "empty stdout (spawn failed) → null");
    assert.equal(parseAptCacheCandidate("totally unrelated text"), null, "no Candidate line → null");
});
// ---------------------------------------------------------------------------
// decideAptResolution — the four resolver branches.
// ---------------------------------------------------------------------------
test("decideAptResolution: dpkg-installed package returns pkg, no log", () => {
    // Branch (1): dpkg -s exits 0 — name is concrete and installed.
    const r = decideAptResolution({
        pkg: "curl",
        dpkgInstalled: true,
        aptCandidate: null,
        ubuntuLike: true,
        distro: "ubuntu-noble",
    });
    assert.equal(r.resolved, "curl");
    assert.equal(r.log, null);
});
test("decideAptResolution: real apt candidate returns pkg, no log (Bookworm chromium)", () => {
    // Branch (2): apt-cache policy says there IS a real candidate — leave name
    // alone, the post-check will use the same name.
    const r = decideAptResolution({
        pkg: "chromium",
        dpkgInstalled: false,
        aptCandidate: "120.0.6099.224-1~deb12u1",
        ubuntuLike: false,
        distro: "debian-bookworm",
    });
    assert.equal(r.resolved, "chromium");
    assert.equal(r.log, null);
});
test("decideAptResolution: Noble chromium → chromium-browser with log line", () => {
    // Branch (3): the load-bearing alias case.
    const r = decideAptResolution({
        pkg: "chromium",
        dpkgInstalled: false,
        aptCandidate: "(none)",
        ubuntuLike: true,
        distro: "ubuntu-noble",
    });
    assert.equal(r.resolved, "chromium-browser");
    assert.match(r.log ?? "", /^  apt-resolve chromium → chromium-browser \(reason=candidate-none, distro=ubuntu-noble\)$/);
});
test("decideAptResolution: Bookworm + alias-keyed pkg + candidate-none returns pkg unchanged", () => {
    // Branch (4) variant: not Ubuntu-like, so even an alias-keyed pkg falls
    // through. Preserves the loud-failure contract — apt-get install
    // will fail, post-check throws, operator sees the real diagnostic.
    const r = decideAptResolution({
        pkg: "chromium",
        dpkgInstalled: false,
        aptCandidate: "(none)",
        ubuntuLike: false,
        distro: "debian-bookworm",
    });
    assert.equal(r.resolved, "chromium");
    assert.equal(r.log, null);
});
test("decideAptResolution: Ubuntu-like + non-aliased pkg + candidate-none returns pkg unchanged", () => {
    // Branch (4) variant: Ubuntu-like host but the package isn't in the alias
    // map. No invented mapping — fall through and let the post-check fail loudly.
    const r = decideAptResolution({
        pkg: "some-package-with-no-alias",
        dpkgInstalled: false,
        aptCandidate: "(none)",
        ubuntuLike: true,
        distro: "ubuntu-noble",
    });
    assert.equal(r.resolved, "some-package-with-no-alias");
    assert.equal(r.log, null);
});
test("decideAptResolution: aptCandidate null (policy spawn failed) still allows alias resolution", () => {
    // Edge case: if `apt-cache policy` itself fails, we still want
    // alias resolution on Ubuntu-like hosts. Treat null-candidate the same as
    // candidate-none for the alias decision.
    const r = decideAptResolution({
        pkg: "chromium",
        dpkgInstalled: false,
        aptCandidate: null,
        ubuntuLike: true,
        distro: "ubuntu-noble",
    });
    assert.equal(r.resolved, "chromium-browser");
    assert.match(r.log ?? "", /apt-resolve chromium → chromium-browser/);
});
// ---------------------------------------------------------------------------
// UBUNTU_ALIASES — the alias map is the authoritative source. Locking its
// shape so adding a new alias is a deliberate one-line PR, not a surprise.
// ---------------------------------------------------------------------------
test("UBUNTU_ALIASES contains chromium → chromium-browser and is the only entry", () => {
    assert.equal(UBUNTU_ALIASES.chromium, "chromium-browser");
    // If a future change adds another alias, this test fails loudly — the
    // intent is "every new alias gets a deliberate test grid case", not silent
    // expansion. Bump the count and add cases when adding a new alias.
    assert.equal(Object.keys(UBUNTU_ALIASES).length, 1);
});
