romm-cli 0.35.0

Rust-based CLI and TUI for the ROMM API
Documentation
#![allow(deprecated)]

use assert_cmd::Command;
use httpmock::Method::POST;
use httpmock::MockServer;
use std::fs;

fn test_config_dir(prefix: &str) -> std::path::PathBuf {
    let ts = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_nanos();
    let dir = std::env::temp_dir().join(format!("romm-cli-{prefix}-test-{ts}"));
    fs::create_dir_all(&dir).unwrap();
    dir
}

fn write_minimal_config(dir: &std::path::Path) {
    let config_json = r#"{
        "base_url": "https://disk.example",
        "download_dir": "/tmp/downloads",
        "use_https": true,
        "auth": null
    }"#;
    fs::write(dir.join("config.json"), config_json).unwrap();
}

#[tokio::test]
async fn auth_status_reports_effective_bearer_from_env() {
    let config_dir = test_config_dir("auth-status");
    write_minimal_config(&config_dir);

    let mut cmd = Command::cargo_bin("romm-cli").expect("binary");
    cmd.env("ROMM_TEST_CONFIG_DIR", config_dir.as_os_str())
        .env("API_BASE_URL", "http://example.test")
        .env("API_USE_HTTPS", "false")
        .env("API_TOKEN", "test-token")
        .args(["--json", "auth", "status"]);

    cmd.assert()
        .success()
        .stdout(predicates::str::contains(r#""mode": "bearer""#));

    let _ = fs::remove_dir_all(config_dir);
}

#[tokio::test]
async fn auth_login_persists_bearer_token_to_disk() {
    let config_dir = test_config_dir("auth-login");
    write_minimal_config(&config_dir);

    let mut cmd = Command::cargo_bin("romm-cli").expect("binary");
    cmd.env("ROMM_TEST_CONFIG_DIR", config_dir.as_os_str())
        .env("API_BASE_URL", "http://example.test")
        .env("API_USE_HTTPS", "false")
        .args(["auth", "login", "--token", "saved-token"]);

    cmd.assert()
        .success()
        .stdout(predicates::str::contains("Auth updated: bearer"));

    let saved = fs::read_to_string(config_dir.join("config.json")).unwrap();
    assert!(saved.contains("saved-token"));

    let _ = fs::remove_dir_all(config_dir);
}

#[tokio::test]
async fn auth_logout_clears_disk_auth() {
    let config_dir = test_config_dir("auth-logout");
    let config_json = r#"{
        "base_url": "https://disk.example",
        "download_dir": "/tmp/downloads",
        "use_https": true,
        "auth": { "Bearer": { "token": "old-token" } }
    }"#;
    fs::write(config_dir.join("config.json"), config_json).unwrap();

    let mut cmd = Command::cargo_bin("romm-cli").expect("binary");
    cmd.env("ROMM_TEST_CONFIG_DIR", config_dir.as_os_str())
        .env("API_BASE_URL", "http://example.test")
        .env("API_USE_HTTPS", "false")
        .args(["auth", "logout"]);

    cmd.assert().success();

    let saved = fs::read_to_string(config_dir.join("config.json")).unwrap();
    assert!(!saved.contains("old-token"));
    assert!(saved.contains(r#""auth": null"#) || saved.contains(r#""auth":null"#));

    let _ = fs::remove_dir_all(config_dir);
}

#[tokio::test]
async fn auth_login_exchanges_pairing_code() {
    let server = MockServer::start_async().await;
    let config_dir = test_config_dir("auth-pairing");

    let config_json = format!(
        r#"{{
        "base_url": "{}",
        "download_dir": "/tmp/downloads",
        "use_https": false,
        "auth": null
    }}"#,
        server.base_url()
    );
    fs::write(config_dir.join("config.json"), config_json).unwrap();

    let _mock = server
        .mock_async(|when, then| {
            when.method(POST).path("/api/client-tokens/exchange");
            then.status(200)
                .header("content-type", "application/json")
                .body(
                    r#"{
                        "id": 1,
                        "name": "cli",
                        "scopes": [],
                        "expires_at": null,
                        "last_used_at": null,
                        "created_at": "2020-01-01T00:00:00Z",
                        "user_id": 1,
                        "raw_token": "paired-token"
                    }"#,
                );
        })
        .await;

    let mut cmd = Command::cargo_bin("romm-cli").expect("binary");
    cmd.env("ROMM_TEST_CONFIG_DIR", config_dir.as_os_str())
        .args(["auth", "login", "--pairing-code", "ABCD1234"]);

    cmd.assert().success();

    let saved = fs::read_to_string(config_dir.join("config.json")).unwrap();
    assert!(saved.contains("paired-token"));

    let _ = fs::remove_dir_all(config_dir);
}