nu-command 0.115.0

Nushell's built-in commands
Documentation
use nu_experimental::PIPE_FAIL;
use nu_test_support::prelude::*;
use pretty_assertions::assert_matches;
use rstest::rstest;

#[test]
#[deps(TESTBIN_COCOCO)]
fn basic_stdout() -> Result {
    let mut tester = test();
    let without_complete: String = tester.run("cococo test")?;
    let with_complete: CompleteResult = tester.run("cococo test | complete")?;
    assert_eq!(without_complete.trim(), with_complete.stdout.trim());
    Ok(())
}

#[test]
#[deps(TESTBIN_COCOCO)]
fn basic_exit_code() -> Result {
    let result: CompleteResult = test().run("cococo test | complete")?;
    assert_eq!(result.exit_code, 0);
    Ok(())
}

#[test]
fn error() -> Result {
    let err = test().run("not-found | complete").expect_shell_error()?;
    assert_matches!(
        err,
        ShellError::ExternalCommand { label, .. } if label == "Command `not-found` not found"
    );
    Ok(())
}

#[test]
#[cfg(not(windows))]
fn capture_error_with_too_much_stderr_not_hang_nushell() -> Result {
    use nu_test_support::fs::Stub::FileWithContent;
    use nu_test_support::playground::Playground;
    Playground::setup("external with many stderr message", |dirs, sandbox| {
        let bytes: usize = 81920;
        let mut large_file_body = String::with_capacity(bytes);
        for _ in 0..bytes {
            large_file_body.push('a');
        }
        sandbox.with_files(&[FileWithContent("a_large_file.txt", &large_file_body)]);

        let actual: CompleteResult = test()
            .inherit_path()
            .cwd(dirs.test())
            .run("sh -c 'cat a_large_file.txt 1>&2' | complete")?;

        assert_eq!(actual.stdout, "");
        assert_eq!(actual.stderr, large_file_body);
        assert_eq!(actual.exit_code, 0);
        Ok(())
    })
}

#[test]
#[cfg(not(windows))]
fn capture_error_with_too_much_stdout_not_hang_nushell() -> Result {
    use nu_test_support::fs::Stub::FileWithContent;
    use nu_test_support::playground::Playground;
    Playground::setup("external with many stdout message", |dirs, sandbox| {
        let bytes: usize = 81920;
        let mut large_file_body = String::with_capacity(bytes);
        for _ in 0..bytes {
            large_file_body.push('a');
        }
        sandbox.with_files(&[FileWithContent("a_large_file.txt", &large_file_body)]);

        let actual: CompleteResult = test()
            .inherit_path()
            .cwd(dirs.test())
            .run("sh -c 'cat a_large_file.txt' | complete")?;

        assert_eq!(actual.stdout, large_file_body);
        assert_eq!(actual.stderr, "");
        assert_eq!(actual.exit_code, 0);
        Ok(())
    })
}

#[test]
#[cfg(not(windows))]
fn capture_error_with_both_stdout_stderr_messages_not_hang_nushell() -> Result {
    use nu_test_support::fs::Stub::FileWithContent;
    use nu_test_support::playground::Playground;
    Playground::setup(
        "external with many stdout and stderr messages",
        |dirs, sandbox| {
            let script_body = "
        x=$(printf '=%.0s' $(seq 40960))
        echo $x
        echo $x 1>&2
        ";
            let expect_body = "=".repeat(40960);

            sandbox.with_files(&[FileWithContent("test.sh", script_body)]);

            let actual: CompleteResult = test()
                .inherit_path()
                .cwd(dirs.test())
                .run("sh test.sh | complete")?;

            assert_eq!(actual.stdout.trim(), expect_body);
            assert_eq!(actual.stderr.trim(), expect_body);
            assert_eq!(actual.exit_code, 0);
            Ok(())
        },
    )
}

#[test]
#[deps(TESTBIN_ECHO_ENV_MIXED)]
fn combined_pipe_redirection() -> Result {
    let code = "
        $env.FOO = 'hello'; 
        $env.BAR = 'world'; 
        echo_env_mixed out-err FOO BAR o+e>| complete | get stdout
    ";

    test().run(code).expect_value_eq("hello\nworld\n")
}

#[test]
#[deps(TESTBIN_ECHO_ENV_STDERR)]
fn err_pipe_redirection() -> Result {
    let actual: CompleteResult =
        test().run("$env.FOO = 'hello'; echo_env_stderr FOO e>| complete")?;
    assert_eq!(actual.stdout, "hello\n");
    assert_eq!(actual.stderr, "");
    assert_eq!(actual.exit_code, 0);
    Ok(())
}

#[rstest]
#[case::complete_parenthesized(r#"let result = (nu -n -c "exit 1" | complete)"#)]
#[case::complete(r#"let result = nu -n -c "exit 1" | complete"#)]
#[case::into_let(r#"nu -n -c "exit 1" | complete | let result"#)]
#[nu_test_support::test]
#[exp(PIPE_FAIL)]
#[deps(NU)]
fn pipefail_let(#[case] assignment: &str) -> Result {
    let mut tester = test();
    let _: Value = tester.run(assignment)?;
    let outcome: CompleteResult = tester.run("$result")?;
    assert_eq!(outcome.stdout, "");
    assert_eq!(outcome.stderr, "");
    assert_eq!(outcome.exit_code, 1);
    Ok(())
}

#[test]
#[exp(PIPE_FAIL)]
#[deps(NU)]
fn pipefail_parenthesized_pipeline_let_keeps_scope() -> Result {
    let code = r#"
        (nu --no-config-file --commands "exit 1" | complete | let result);
        $result
    "#;

    let err = test().run(code).expect_parse_error()?;
    assert!(matches!(err, ParseError::VariableNotFound { .. }));
    Ok(())
}