import pytest

from atlas_runner.errors import ErrorCode, RunnerError

EXPECTED_CODES = [
    "IMAGE_PULL_FAILED",
    "DOCKER_DAEMON_UNAVAILABLE",
    "CONTAINER_CREATE_FAILED",
    "CONTAINER_START_FAILED",
    "CONTAINER_NOT_FOUND",
    "INSUFFICIENT_RUNTIME_PERMISSIONS",
    "LOGIN_FAILED",
    "BACKEND_ERROR",
]


class TestErrorCode:
    def test_all_members_exist(self) -> None:
        member_names = [m.name for m in ErrorCode]
        assert sorted(member_names) == sorted(EXPECTED_CODES)

    @pytest.mark.parametrize("code_name", EXPECTED_CODES)
    def test_strenum_value_matches_name(self, code_name: str) -> None:
        code = ErrorCode[code_name]
        assert code.value == code_name


class TestRunnerError:
    def test_stores_fields(self) -> None:
        err = RunnerError(
            code=ErrorCode.IMAGE_PULL_FAILED,
            message="pull failed",
            details={"image": "alpine"},
        )
        assert err.code == ErrorCode.IMAGE_PULL_FAILED
        assert err.message == "pull failed"
        assert err.details == {"image": "alpine"}

    def test_details_defaults_to_none(self) -> None:
        err = RunnerError(code=ErrorCode.BACKEND_ERROR, message="oops")
        assert err.details is None

    def test_to_dict(self) -> None:
        err = RunnerError(
            code=ErrorCode.CONTAINER_NOT_FOUND,
            message="not found",
            details={"name": "foo"},
        )
        assert err.to_dict() == {
            "code": "CONTAINER_NOT_FOUND",
            "message": "not found",
            "details": {"name": "foo"},
        }

    def test_to_dict_without_details(self) -> None:
        err = RunnerError(code=ErrorCode.LOGIN_FAILED, message="bad creds")
        assert err.to_dict() == {
            "code": "LOGIN_FAILED",
            "message": "bad creds",
            "details": None,
        }

    def test_is_exception(self) -> None:
        err = RunnerError(code=ErrorCode.BACKEND_ERROR, message="fail")
        assert isinstance(err, Exception)

    def test_catchable(self) -> None:
        with pytest.raises(RunnerError) as exc_info:
            raise RunnerError(code=ErrorCode.BACKEND_ERROR, message="boom")
        assert exc_info.value.message == "boom"

    def test_str(self) -> None:
        err = RunnerError(code=ErrorCode.BACKEND_ERROR, message="something broke")
        assert str(err) == "something broke"
