modde-cli 0.2.1

CLI interface for modde
mod common;

use common::Fixture;

fn add_game(fx: &Fixture, id: &str) -> std::process::Output {
    fx.cmd()
        .args([
            "game",
            "add",
            id,
            "--display-name",
            "Custom Game",
            "--executable-dir",
            "bin/x64",
        ])
        .output()
        .expect("spawn modde game add")
}

#[test]
fn add_writes_toml_to_user_data_dir() {
    let fx = Fixture::new();
    let out = add_game(&fx, "custom-game");
    assert!(
        out.status.success(),
        "add failed: {}",
        String::from_utf8_lossy(&out.stderr)
    );

    let spec_path = fx.data_dir().join("games/custom-game.toml");
    let content = std::fs::read_to_string(&spec_path).expect("read spec");
    assert!(content.contains(r#"id = "custom-game""#));
    assert!(content.contains(r#"display_name = "Custom Game""#));
    assert!(content.contains(r#"executable_dir = "bin/x64""#));
}

#[test]
fn add_rejects_built_in_id() {
    let fx = Fixture::new();
    let out = fx
        .cmd()
        .args([
            "game",
            "add",
            "skyrim-se",
            "--display-name",
            "Skyrim",
            "--executable-dir",
            "bin",
        ])
        .output()
        .expect("spawn modde game add");
    assert!(!out.status.success(), "built-in add should fail");
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(
        stderr.contains("collides with a built-in game"),
        "unexpected stderr:\n{stderr}"
    );
}

#[test]
fn add_then_list_round_trip() {
    let fx = Fixture::new();
    let out = add_game(&fx, "round-trip");
    assert!(
        out.status.success(),
        "add failed: {}",
        String::from_utf8_lossy(&out.stderr)
    );

    let list = fx
        .cmd()
        .args(["game", "list"])
        .output()
        .expect("spawn modde game list");
    assert!(list.status.success());
    let stdout = String::from_utf8_lossy(&list.stdout);
    assert!(stdout.contains("round-trip"), "missing game id:\n{stdout}");
}

#[test]
fn remove_deletes_toml() {
    let fx = Fixture::new();
    let out = add_game(&fx, "remove-me");
    assert!(
        out.status.success(),
        "add failed: {}",
        String::from_utf8_lossy(&out.stderr)
    );

    let rm = fx
        .cmd()
        .args(["game", "remove", "remove-me", "--yes"])
        .output()
        .expect("spawn modde game remove");
    assert!(
        rm.status.success(),
        "remove failed: {}",
        String::from_utf8_lossy(&rm.stderr)
    );

    assert!(!fx.data_dir().join("games/remove-me.toml").exists());

    let list = fx
        .cmd()
        .args(["game", "list"])
        .output()
        .expect("spawn modde game list");
    assert!(list.status.success());
    let stdout = String::from_utf8_lossy(&list.stdout);
    assert!(
        !stdout.contains("remove-me"),
        "removed game should not appear:\n{stdout}"
    );
}

#[test]
fn detect_lists_exe_bearing_dirs() {
    let fx = Fixture::new();
    let install_root = fx.root().join("install");
    std::fs::create_dir_all(install_root.join("Game")).unwrap();
    std::fs::create_dir_all(install_root.join("Binaries/Win64")).unwrap();
    std::fs::write(install_root.join("Game/foo.exe"), b"foo").unwrap();
    std::fs::write(
        install_root.join("Binaries/Win64/launcher.exe"),
        b"launcher",
    )
    .unwrap();

    let out = fx
        .cmd()
        .args(["game", "detect", install_root.to_str().unwrap()])
        .output()
        .expect("spawn modde game detect");
    assert!(
        out.status.success(),
        "detect failed: {}",
        String::from_utf8_lossy(&out.stderr)
    );

    let stdout = String::from_utf8_lossy(&out.stdout);
    assert!(stdout.contains("Game"), "missing Game dir:\n{stdout}");
    assert!(
        stdout.contains("Binaries/Win64"),
        "missing Binaries/Win64 dir:\n{stdout}"
    );
    assert!(stdout.contains("foo.exe"), "missing foo.exe:\n{stdout}");
    assert!(
        stdout.contains("launcher.exe"),
        "missing launcher.exe:\n{stdout}"
    );
}

#[test]
fn show_prints_resolved_registration() {
    let fx = Fixture::new();
    let out = fx
        .cmd()
        .args([
            "game",
            "add",
            "show-me",
            "--display-name",
            "Show Me",
            "--executable-dir",
            "Game",
            "--proxy-dll",
            "dxgi",
        ])
        .output()
        .expect("spawn modde game add");
    assert!(
        out.status.success(),
        "add failed: {}",
        String::from_utf8_lossy(&out.stderr)
    );

    let show = fx
        .cmd()
        .args(["game", "show", "show-me"])
        .output()
        .expect("spawn modde game show");
    assert!(
        show.status.success(),
        "show failed: {}",
        String::from_utf8_lossy(&show.stderr)
    );
    let stdout = String::from_utf8_lossy(&show.stdout);
    assert!(stdout.contains("dxgi"), "missing proxy dll:\n{stdout}");
}