harn-cli 0.10.136

CLI for the Harn programming language — run, test, REPL, format, and lint
use super::*;

pub(super) fn assert_only_pick_diagnostic(result: &ApplyResult, source: &str) {
    let temp = tempfile::NamedTempFile::with_suffix(".harn").unwrap();
    fs::write(temp.path(), source).unwrap();
    assert_only_pick_diagnostic_at(result, temp.path());
}

pub(super) fn assert_only_pick_diagnostic_at(result: &ApplyResult, path: &Path) {
    let plan = build_plan(path, None).unwrap();
    let codes: Vec<_> = plan.diagnostics.iter().map(|d| d.code.as_str()).collect();
    assert_eq!(codes, ["HARN-LNT-077"], "{plan:#?}");
    assert_eq!(
        result.post_apply_diagnostics_count,
        codes.len(),
        "{result:#?}"
    );
}

#[test]
fn capability_apply_replaces_bundle_arguments_before_their_receivers() {
    let (result, updated) = apply_single(
        r#"
fn inspect(harness: {runtime: HarnessRuntime}) -> bool {
  return harness.runtime.host_has("workspace", "project_root")
}

fn wrapper(harness: Harness) -> bool {
  return inspect({runtime: harness.runtime})
}

fn main(harness: Harness) {
  wrapper(harness)
}
"#,
    );
    assert!(!result.applied.is_empty(), "the migration must run");
    assert_eq!(result.post_apply_diagnostics_count, 0, "{result:#?}");
    assert_eq!(
        callable_params(&updated, "wrapper"),
        [param("runtime", "HarnessRuntime")]
    );
    assert_eq!(
        call_argument_paths(&updated, "inspect"),
        [vec![Some("runtime".into())]]
    );
    assert_eq!(
        call_argument_paths(&updated, "wrapper"),
        [vec![Some("harness.runtime".into())]]
    );
}

#[test]
fn capability_apply_replaces_existing_projection_for_a_root_parameter() {
    for projection in [
        "pick(harness, [\"env\", \"runtime\"])",
        "{env: harness.env, runtime: harness.runtime}",
        "harness.runtime",
    ] {
        let (result, updated) = apply_single(&format!(
            "import {{ with_scenario }} from \"std/testing\"\n\nfn main(harness: Harness) {{\n  with_scenario({projection}, {{}}, {{ _ -> \"ok\" }})\n}}\n"
        ));
        assert!(!result.applied.is_empty(), "the migration must run");
        assert_eq!(
            result.post_apply_diagnostics_count, 0,
            "{result:#?}\n{updated}"
        );
        assert_eq!(
            call_argument_paths(&updated, "with_scenario"),
            [vec![Some("harness".into()), None, None]],
            "an existing carrier must be replaced rather than shifted: {updated}"
        );
    }
}

#[test]
fn capability_apply_replaces_existing_bundle_for_a_narrow_parameter() {
    for projection in ["pick(harness, [\"fs\"])", "{fs: harness.fs}"] {
        let (result, updated) = apply_single(&format!(
            "import {{ with_temp_dir }} from \"std/testing\"\n\nfn main(harness: Harness) {{\n  with_temp_dir({projection}, {{ dir -> harness.stdio.println(dir) }})\n}}\n"
        ));
        assert!(!result.applied.is_empty(), "the migration must run");
        assert_eq!(
            result.post_apply_diagnostics_count, 0,
            "{result:#?}\n{updated}"
        );
        assert_eq!(
            call_argument_paths(&updated, "with_temp_dir"),
            [vec![Some("harness.fs".into()), None]],
            "an existing carrier must be replaced rather than shifted: {updated}"
        );
    }
}

#[test]
fn capability_apply_preserves_opaque_or_ambiguous_projection_arguments() {
    for projection in [
        "get_runtime(harness.runtime)",
        "{runtime: get_runtime(harness.runtime)}",
        "{env: harness.env, runtime: other.runtime}",
        "pick(harness, [/* retain this explanation */ \"runtime\"])",
    ] {
        let source = format!(
            "import {{ with_scenario }} from \"std/testing\"\n\nfn get_runtime(runtime: HarnessRuntime) -> HarnessRuntime {{\n  runtime.store_set(\"observed\", true)\n  return runtime\n}}\n\nfn main(harness: Harness, other: Harness) {{\n  with_scenario({projection}, {{}}, {{ _ -> \"ok\" }})\n}}\n"
        );
        let (result, updated) = apply_single(&source);
        assert!(
            updated.contains(&format!("with_scenario({projection}, {{}}")),
            "an ambiguous or observable expression must not be dropped or shifted: {updated}"
        );
        assert!(
            result.post_apply_diagnostics_count > 0,
            "the unresolved argument must remain visible: {result:#?}"
        );
    }
}