cargo-changeset 0.1.7

A cargo subcommand for managing changesets
use std::fs;

use changeset_test_helpers::changesets::{write_changeset, write_multi_changeset};
use changeset_test_helpers::git::{create_tag, git_add_and_commit};
use changeset_test_helpers::workspaces::WorkspaceBuilder;
use predicates::str::contains;
use tempfile::TempDir;

fn create_single_package_with_git() -> TempDir {
    WorkspaceBuilder::single_package("my-crate", "1.0.0")
        .with_changeset_dir()
        .with_git()
        .build()
}

fn create_workspace_with_two_crates() -> TempDir {
    WorkspaceBuilder::virtual_workspace()
        .crate_member("crate-a", "1.0.0")
        .crate_member("crate-b", "2.0.0")
        .with_changeset_dir()
        .with_git()
        .build()
}

#[test]
fn release_saga_failure_shows_failed_step_and_rollback_message() {
    let workspace = create_single_package_with_git();
    write_changeset(&workspace, "fix.md", "my-crate", "patch", "Fix a bug");
    git_add_and_commit(&workspace, "Add changeset");

    create_tag(&workspace, "v1.0.1", "Pre-existing conflicting tag");

    assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
        .arg("release")
        .current_dir(workspace.path())
        .assert()
        .failure()
        .stderr(contains("Error: Release failed at step"))
        .stderr(contains("create_tags"))
        .stderr(contains("Rollback completed successfully"))
        .stderr(contains("restored to its original state"));
}

#[test]
fn release_saga_failure_message_includes_step_name() {
    let workspace = create_single_package_with_git();
    write_changeset(&workspace, "fix.md", "my-crate", "patch", "Fix a bug");
    git_add_and_commit(&workspace, "Add changeset");

    create_tag(&workspace, "v1.0.1", "Pre-existing conflicting tag");

    assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
        .arg("release")
        .current_dir(workspace.path())
        .assert()
        .failure()
        .stderr(contains("'create_tags'"));
}

#[test]
fn release_saga_failure_with_rollback_restores_version_in_manifest() {
    let workspace = create_single_package_with_git();
    write_changeset(&workspace, "fix.md", "my-crate", "patch", "Fix a bug");
    git_add_and_commit(&workspace, "Add changeset");

    create_tag(&workspace, "v1.0.1", "Pre-existing conflicting tag");

    assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
        .arg("release")
        .current_dir(workspace.path())
        .assert()
        .failure();

    let manifest_content =
        fs::read_to_string(workspace.path().join("Cargo.toml")).expect("read Cargo.toml");
    assert!(
        manifest_content.contains("version = \"1.0.0\""),
        "version should be restored to original after rollback"
    );
}

#[test]
fn release_saga_failure_multi_package_shows_proper_error_format() {
    let workspace = create_workspace_with_two_crates();
    write_multi_changeset(
        &workspace,
        "multi.md",
        &[("crate-a", "patch"), ("crate-b", "patch")],
        "Fix bugs in both crates",
    );
    git_add_and_commit(&workspace, "Add changeset");

    create_tag(
        &workspace,
        "crate-b@v2.0.1",
        "Pre-existing conflicting tag for crate-b",
    );

    assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
        .arg("release")
        .current_dir(workspace.path())
        .assert()
        .failure()
        .stderr(contains("Error: Release failed at step"))
        .stderr(contains("Rollback completed successfully"));
}

#[test]
fn release_saga_failure_error_includes_cause_chain() {
    let workspace = create_single_package_with_git();
    write_changeset(&workspace, "fix.md", "my-crate", "patch", "Fix a bug");
    git_add_and_commit(&workspace, "Add changeset");

    create_tag(&workspace, "v1.0.1", "Pre-existing conflicting tag");

    assert_cmd::cargo::cargo_bin_cmd!("cargo-changeset")
        .arg("release")
        .current_dir(workspace.path())
        .assert()
        .failure()
        .stderr(contains("->"));
}