presolve-cli 0.1.0-alpha.8

The Presolve compiler and application command-line interface.
Documentation
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::process::Command;

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct BaselineDocument {
    suite: String,
    fixtures: Vec<FixtureBaseline>,
}

#[derive(Debug, Deserialize)]
struct FixtureBaseline {
    name: String,
    input: String,
    artifacts: BTreeMap<String, u64>,
    resume: ResumeBaseline,
}

#[derive(Debug, Deserialize)]
struct ResumeBaseline {
    boundaries: usize,
    slot_schemas: usize,
    capture_programs: usize,
    restore_programs: usize,
    anchors: usize,
    events: usize,
    activations: usize,
    root_kinds: Vec<String>,
    root_ids: Vec<String>,
    provided_program_ids: Vec<String>,
}

fn repo_root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../..")
        .canonicalize()
        .expect("failed to resolve repository root")
}

fn baseline() -> BaselineDocument {
    serde_json::from_str(include_str!("../../../fixtures/production-baseline.json"))
        .expect("production baseline fixture should be valid JSON")
}

fn logical_artifact_name(path: &Path) -> String {
    let name = path
        .file_name()
        .and_then(|value| value.to_str())
        .expect("artifact name should be UTF-8");
    if name.starts_with("boot.application.")
        && Path::new(name)
            .extension()
            .is_some_and(|extension| extension.eq_ignore_ascii_case("js"))
    {
        "resume.eager.module.js".to_string()
    } else {
        name.to_string()
    }
}

fn build_fixture(
    repo_root: &Path,
    fixture: &FixtureBaseline,
    suffix: &str,
    production: bool,
) -> PathBuf {
    let output = repo_root
        .join("target/presolve-test-output")
        .join(format!("production-{}-{suffix}", fixture.name));
    if output.exists() {
        std::fs::remove_dir_all(&output).expect("failed to clean prior production output");
    }
    let mut arguments = vec![
        "build".to_string(),
        fixture.input.clone(),
        "--out".to_string(),
        output.to_string_lossy().into_owned(),
    ];
    if production {
        arguments.push("--production".to_string());
    }
    let result = Command::new(env!("CARGO_BIN_EXE_presolve"))
        .current_dir(repo_root)
        .args(arguments)
        .output()
        .expect("failed to run production baseline build");
    assert!(
        result.status.success(),
        "baseline build failed for {}: {}",
        fixture.name,
        String::from_utf8_lossy(&result.stderr)
    );
    output
}

fn artifact_sizes(output: &Path) -> BTreeMap<String, u64> {
    let mut actual = BTreeMap::new();
    for entry in std::fs::read_dir(output).expect("baseline output should be readable") {
        let path = entry.expect("output entry should be readable").path();
        if path.is_file() {
            actual.insert(
                logical_artifact_name(&path),
                std::fs::metadata(path)
                    .expect("artifact metadata should be readable")
                    .len(),
            );
        }
    }
    actual
}

fn values(document: &serde_json::Value, key: &str) -> usize {
    document[key]
        .as_array()
        .unwrap_or_else(|| panic!("resume field {key} should be an array"))
        .len()
}

fn assert_resume_matches_baseline(resume: &serde_json::Value, fixture: &FixtureBaseline) {
    assert_eq!(values(resume, "boundaries"), fixture.resume.boundaries);
    assert_eq!(values(resume, "slot_schemas"), fixture.resume.slot_schemas);
    assert_eq!(
        values(resume, "capture_programs"),
        fixture.resume.capture_programs
    );
    assert_eq!(
        values(resume, "restore_programs"),
        fixture.resume.restore_programs
    );
    assert_eq!(values(resume, "anchors"), fixture.resume.anchors);
    assert_eq!(values(resume, "events"), fixture.resume.events);
    assert_eq!(values(resume, "activations"), fixture.resume.activations);
    let chunks = resume["chunks"]
        .as_array()
        .expect("chunks should be an array");
    assert_eq!(
        chunks
            .iter()
            .map(|chunk| chunk["root_kind"].as_str().expect("root kind"))
            .collect::<Vec<_>>(),
        fixture.resume.root_kinds
    );
    assert_eq!(
        chunks
            .iter()
            .map(|chunk| chunk["root_id"].as_str().expect("root ID"))
            .collect::<Vec<_>>(),
        fixture.resume.root_ids
    );
    let mut programs = chunks
        .iter()
        .flat_map(|chunk| {
            chunk["provided_program_ids"]
                .as_array()
                .expect("provided programs")
                .iter()
                .map(|program| program.as_str().expect("program ID").to_string())
                .collect::<Vec<_>>()
        })
        .collect::<Vec<_>>();
    programs.sort();
    assert_eq!(programs, fixture.resume.provided_program_ids);
    assert!(chunks.iter().all(|chunk| chunk["dependency_chunk_ids"]
        .as_array()
        .is_some_and(Vec::is_empty)));
}

#[test]
fn production_artifact_is_deterministic_and_preserves_the_committed_baseline() {
    let repo_root = repo_root();
    let baseline = baseline();
    assert_eq!(baseline.suite, "production-baseline");
    for fixture in &baseline.fixtures {
        let first = build_fixture(&repo_root, fixture, "first", false);
        let second = build_fixture(&repo_root, fixture, "second", false);
        let mut first_artifacts = artifact_sizes(&first);
        let production_size = first_artifacts
            .remove("production.runtime.json")
            .expect("production artifact should be emitted");
        let optimization_size = first_artifacts
            .remove("optimization-report.json")
            .expect("production optimization report should be emitted");
        let cost_size = first_artifacts
            .remove("runtime-cost-report.json")
            .expect("K15 runtime cost report should be emitted");
        assert!(production_size > 0, "production artifact must not be empty");
        assert_eq!(
            first_artifacts, fixture.artifacts,
            "{} byte baseline",
            fixture.name
        );
        let mut second_artifacts = artifact_sizes(&second);
        assert_eq!(
            second_artifacts.remove("production.runtime.json"),
            Some(production_size),
            "{} repeated production artifact size",
            fixture.name
        );
        assert_eq!(
            second_artifacts.remove("optimization-report.json"),
            Some(optimization_size)
        );
        assert_eq!(
            second_artifacts.remove("runtime-cost-report.json"),
            Some(cost_size)
        );
        assert_eq!(
            first_artifacts, second_artifacts,
            "{} repeated build",
            fixture.name
        );
        assert!(optimization_size > 0 && cost_size > 0);
        let resume: serde_json::Value = serde_json::from_slice(
            &std::fs::read(first.join("resume.runtime.json"))
                .expect("resume manifest should be emitted"),
        )
        .expect("resume manifest should be valid JSON");
        let production: serde_json::Value = serde_json::from_slice(
            &std::fs::read(first.join("production.runtime.json"))
                .expect("production artifact should be emitted"),
        )
        .expect("production artifact should be valid JSON");
        assert_eq!(production["schemaVersion"], 1);
        assert_eq!(production["buildId"], resume["build_id"]);
        assert_eq!(production["runtimeProtocolVersion"], 1);
        assert_eq!(
            std::fs::read(first.join("production.runtime.json"))
                .expect("first production artifact bytes"),
            std::fs::read(second.join("production.runtime.json"))
                .expect("second production artifact bytes"),
            "{} production artifact bytes",
            fixture.name
        );
        assert_resume_matches_baseline(&resume, fixture);
    }
}

#[test]
fn k9_cli_production_layout_is_syntax_safe_and_deterministic() {
    let repo_root = repo_root();
    let fixture = &baseline().fixtures[0];
    let first = build_fixture(&repo_root, fixture, "production-first", true);
    let second = build_fixture(&repo_root, fixture, "production-second", true);
    assert!(std::fs::read_to_string(first.join("index.html"))
        .expect("production page")
        .contains("id=\"presolve-production-runtime\""));
    let read_layout = |output: &Path| {
        let mut modules = std::fs::read_dir(output.join("production"))
            .expect("production module directory")
            .map(|entry| entry.expect("module entry").path())
            .collect::<Vec<_>>();
        modules.sort();
        modules
            .into_iter()
            .map(|path| {
                let source = std::fs::read_to_string(&path).expect("module source");
                assert!(!source.contains("eval("));
                assert!(!source.contains("Function("));
                assert!(!source.contains("import("));
                assert!(Command::new("node")
                    .arg("--check")
                    .arg(&path)
                    .status()
                    .expect("node syntax check")
                    .success());
                (
                    path.file_name()
                        .expect("module filename")
                        .to_string_lossy()
                        .into_owned(),
                    source,
                )
            })
            .collect::<Vec<_>>()
    };
    assert_eq!(read_layout(&first), read_layout(&second));
}