"""Contains all unit tests for the execute command helper."""

import subprocess
from io import StringIO
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from voraus_pipeline_utils.methods.shell import execute_command


def test_execute_command_success() -> None:
    exit_code, output = execute_command(["echo", "'Hello pytest!'"])
    assert exit_code == 0
    assert output == ["Hello pytest!"]


def test_execute_command_failure() -> None:
    with pytest.raises(subprocess.CalledProcessError):
        execute_command(["false"])


def test_execute_command_failure_no_raise() -> None:
    exit_code, output = execute_command(["false"], no_raise=True)
    assert exit_code == 1
    assert not output


def test_execute_command_stdout_fn_default(capsys: pytest.CaptureFixture) -> None:
    execute_command(["echo", "'Hello pytest!'"])
    assert capsys.readouterr().out == "Hello pytest!\n"


def test_execute_command_stdout_fn_default_multiline() -> None:
    _, output = execute_command(["echo 'Hello\nHello\nHello\nHello\nHello'"])
    assert output == ["Hello"] * 5


def test_execute_command_stdout_fn_overwrite(capsys: pytest.CaptureFixture) -> None:
    out = StringIO()
    execute_command(["echo", "'Hello pytest!'"], stdout_fn=out.write)
    assert capsys.readouterr().out == ""
    out.seek(0)
    assert out.read() == "Hello pytest!"


def test_execute_command_cwd(tmp_path: Path) -> None:
    (tmp_path / "test.txt").touch()
    _, output = execute_command(["ls"], cwd=tmp_path)
    assert output == ["test.txt"]


def test_execute_command_stdout_fn_none(capsys: pytest.CaptureFixture) -> None:
    _, output = execute_command(["echo", "'Hello pytest!'"], stdout_fn=None)
    assert capsys.readouterr().out == ""
    assert output == ["Hello pytest!"]


@patch("voraus_pipeline_utils.methods.shell.subprocess.Popen")
def test_execute_command_process_stdout_is_none(popen_mock: MagicMock) -> None:
    process_mock = MagicMock()
    process_mock.__enter__.return_value.stdout = None
    popen_mock.return_value = process_mock
    with pytest.raises(ValueError, match="Failed to set stdout for the subprocess"):
        execute_command(["echo", "'Hello pytest!'"])
