face 0.1.0

Fold And Cluster Entries — a Unix-style CLI for grouping, paging, and summarizing structured command output.
Documentation
//! Error-path coverage: conflicting flags and malformed input that the
//! §11 error contract rejects.
//!
//! Acceptance: docs/design.md §11 (errors and recovery), §11.3
//! (conflicting flags).

mod common;

use common::{drive, drive_result};

mod conflicting_flags {
    use super::drive_result;
    use clap::Parser;
    use face_cli::cli::Cli;

    /// Acceptance: docs/design.md §11.3 — `--explain` and `--schema`
    /// are mutually exclusive view modes. Combining them errors.
    #[test]
    fn explain_and_schema_conflicts() {
        let stdin = "{\"kind\":\"bug\"}\n";
        let outcome = drive_result(&["face", "--explain", "--schema"], stdin);
        assert!(
            outcome.is_err(),
            "expected error on `--explain --schema`: outcome={outcome:?}",
        );
    }

    /// Acceptance: docs/design.md §3 OUTPUT, §8.2 — `--color` accepts
    /// only `{auto, always, never}`. An unknown value (e.g.
    /// `--color=banana`) is rejected at parse time by clap, not
    /// silently fallen through to `auto`.
    #[test]
    fn unknown_color_value_errors() {
        let outcome = Cli::try_parse_from(["face", "--color=banana"]);
        assert!(
            outcome.is_err(),
            "unknown --color value should fail clap parse, got Ok({:?})",
            outcome.ok(),
        );
    }

    /// Acceptance: docs/design.md §10.4 — interactive mode owns the
    /// output choice and cannot be combined with explicit formats.
    #[test]
    fn interactive_and_format_conflict() {
        let stdin = "{\"kind\":\"bug\"}\n{\"kind\":\"feat\"}\n";
        let outcome = super::drive_result(&["face", "--interactive", "--format=json"], stdin);
        assert!(
            outcome.is_err(),
            "`--interactive --format=json` should conflict: {outcome:?}",
        );
    }
}

mod malformed_input {
    use super::drive;

    /// Acceptance: docs/design.md §11 — when `--items=PATH` cannot be
    /// resolved on the input, the run errors with `UnknownItemsPath`.
    #[test]
    fn unknown_items_path() {
        let stdin = "{\"weird\":[1,2,3]}";
        let (code, _stdout, stderr) = drive(&["face", "--items=.nope"], stdin);
        assert_ne!(code, 0, "unknown items path is fatal: stderr={stderr:?}");
    }

    /// Acceptance: docs/design.md §11 — when `--score=PATH` resolves to
    /// no numeric value on any sampled item, the run errors with
    /// `UnknownScorePath`.
    #[test]
    fn unknown_score_path() {
        let stdin = "{\"kind\":\"bug\"}\n{\"kind\":\"feat\"}\n";
        let (code, _stdout, stderr) = drive(&["face", "--score=.nope"], stdin);
        assert_ne!(code, 0, "unknown score path is fatal: stderr={stderr:?}");
    }
}