cargo-nidus 1.0.4

Command-line project generator and inspection tooling for Nidus applications.
mod support;

use std::{fs, process::Command};

use support::temp_project_root;

#[test]
fn cargo_nidus_openapi_generates_document_from_controllers() {
    let root = temp_project_root("openapi_generates_document_from_controllers");
    let status = Command::new(env!("CARGO_BIN_EXE_cargo-nidus"))
        .args(["nidus", "generate", "controller", "users", "--path"])
        .arg(&root)
        .status()
        .unwrap();
    assert!(status.success());
    let controller_path = root.join("src/controllers/users.rs");
    let controller = fs::read_to_string(&controller_path)
        .unwrap()
        .replace(
            "pub struct UsersController;",
            r#"pub struct UsersController;

pub struct CreateUserDto {
    email: String,
    age: Option<u16>,
}

pub struct UserDto {
    #[serde(rename = "user_id")]
    id: u64,
    email: String,
    profile: UserProfile,
    #[serde(default)]
    display_name: String,
    #[serde(skip)]
    internal_notes: String,
    roles: Vec<String>,
}

pub struct UserProfile {
    display_name: String,
}"#,
        )
        .replace(
            "#[get(\"/\")]",
            r#"#[get("/:id")]
    #[guard(AuthGuard)]
    #[pipe(ValidationPipe)]
    #[validate]
    #[openapi(
        summary = "Find user",
        tags = ["users", "read"],
        status = 201,
        request = CreateUserDto,
        response = UserDto
    )]"#,
        );
    fs::write(controller_path, controller).unwrap();

    let openapi = Command::new(env!("CARGO_BIN_EXE_cargo-nidus"))
        .args(["nidus", "openapi", "--path"])
        .arg(&root)
        .output()
        .unwrap();
    assert!(openapi.status.success());
    let stdout = String::from_utf8(openapi.stdout).unwrap();
    let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
    assert_eq!(json["openapi"], "3.1.0");
    assert_eq!(json["paths"]["/users/{id}"]["get"]["summary"], "Find user");
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["operationId"],
        "get_users_by_id"
    );
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["tags"],
        serde_json::json!(["users", "read"])
    );
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["x-nidus-guards"],
        serde_json::json!(["AuthGuard"])
    );
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["x-nidus-pipes"],
        serde_json::json!(["ValidationPipe"])
    );
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["x-nidus-validates"],
        true
    );
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["parameters"],
        serde_json::json!([
            {
                "name": "id",
                "in": "path",
                "required": true,
                "schema": {
                    "type": "string"
                }
            }
        ])
    );
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["requestBody"]["content"]["application/json"]["schema"]
            ["$ref"],
        "#/components/schemas/CreateUserDto"
    );
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["responses"]["201"]["content"]["application/json"]["schema"]
            ["$ref"],
        "#/components/schemas/UserDto"
    );
    assert!(json["paths"]["/users/{id}"]["get"]["responses"]["200"].is_null());
    assert_eq!(
        json["components"]["schemas"]["CreateUserDto"]["properties"]["email"]["type"],
        "string"
    );
    assert_eq!(
        json["components"]["schemas"]["CreateUserDto"]["properties"]["age"]["type"],
        "integer"
    );
    assert_eq!(
        json["components"]["schemas"]["CreateUserDto"]["required"],
        serde_json::json!(["email"])
    );
    assert_eq!(
        json["components"]["schemas"]["UserDto"]["properties"]["user_id"]["type"],
        "integer"
    );
    assert!(json["components"]["schemas"]["UserDto"]["properties"]["internal_notes"].is_null());
    assert_eq!(
        json["components"]["schemas"]["UserDto"]["required"],
        serde_json::json!(["user_id", "email", "profile", "roles"])
    );
    assert_eq!(
        json["components"]["schemas"]["UserDto"]["properties"]["roles"]["type"],
        "array"
    );
    assert_eq!(
        json["components"]["schemas"]["UserDto"]["properties"]["roles"]["items"]["type"],
        "string"
    );
    assert_eq!(
        json["components"]["schemas"]["UserDto"]["properties"]["profile"]["$ref"],
        "#/components/schemas/UserProfile"
    );
    assert_eq!(
        json["components"]["schemas"]["UserProfile"]["properties"]["display_name"]["type"],
        "string"
    );
}

#[test]
fn cargo_nidus_openapi_accepts_document_title_and_version() {
    let root = temp_project_root("openapi_accepts_document_title_and_version");
    let status = Command::new(env!("CARGO_BIN_EXE_cargo-nidus"))
        .args(["nidus", "generate", "controller", "users", "--path"])
        .arg(&root)
        .status()
        .unwrap();
    assert!(status.success());

    let openapi = Command::new(env!("CARGO_BIN_EXE_cargo-nidus"))
        .args([
            "nidus",
            "openapi",
            "--title",
            "Users API",
            "--version",
            "2026.6",
            "--path",
        ])
        .arg(&root)
        .output()
        .unwrap();

    assert!(openapi.status.success());
    let stdout = String::from_utf8(openapi.stdout).unwrap();
    let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
    assert_eq!(json["info"]["title"], "Users API");
    assert_eq!(json["info"]["version"], "2026.6");
}

#[test]
fn cargo_nidus_openapi_ignores_tags_word_in_summary() {
    let root = temp_project_root("openapi_ignores_tags_word_in_summary");
    let status = Command::new(env!("CARGO_BIN_EXE_cargo-nidus"))
        .args(["nidus", "generate", "controller", "users", "--path"])
        .arg(&root)
        .status()
        .unwrap();
    assert!(status.success());
    let controller_path = root.join("src/controllers/users.rs");
    let controller = fs::read_to_string(&controller_path).unwrap().replace(
        "#[get(\"/\")]",
        "#[get(\"/:id\")]\n    #[openapi(summary = \"Find user tags\")]",
    );
    fs::write(controller_path, controller).unwrap();

    let openapi = Command::new(env!("CARGO_BIN_EXE_cargo-nidus"))
        .args(["nidus", "openapi", "--path"])
        .arg(&root)
        .output()
        .unwrap();

    assert!(openapi.status.success());
    let stdout = String::from_utf8(openapi.stdout).unwrap();
    let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
    assert_eq!(
        json["paths"]["/users/{id}"]["get"]["summary"],
        "Find user tags"
    );
    assert!(json["paths"]["/users/{id}"]["get"].get("tags").is_none());
}