"""Tests for the check-weak-tests pre-commit hook."""

import importlib.util
from pathlib import Path

# Load the hook module by path (hyphenated filename isn't importable directly).
_HOOK_PATH = (
    Path(__file__).resolve().parents[2]
    / ".pre-commit-hooks"
    / "check-weak-tests.py"
)
_spec = importlib.util.spec_from_file_location("check_weak_tests", _HOOK_PATH)
check_weak_tests = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(check_weak_tests)


def _write(tmp_path, body: str) -> str:
    fp = tmp_path / "test_sample.py"
    fp.write_text(body)
    return str(fp)


class TestAssertTrueOnly:
    def test_flags_assert_true_only(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    do_thing()\n    assert True\n",
        )
        issues = check_weak_tests.check_file(fp)
        assert len(issues) == 1
        assert "no real assertion" in issues[0][1]

    def test_real_assertion_not_flagged(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    assert compute() == 42\n",
        )
        assert check_weak_tests.check_file(fp) == []

    def test_assert_true_alongside_real_assert_not_flagged(self, tmp_path):
        # If a real assertion exists, the test is not a pure no-op.
        fp = _write(
            tmp_path,
            "def test_x():\n    assert compute() == 1\n    assert True\n",
        )
        assert check_weak_tests.check_file(fp) == []


class TestImportExistence:
    def test_flags_import_existence(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    from mymod import thing\n"
            "    assert thing is not None\n",
        )
        issues = check_weak_tests.check_file(fp)
        assert len(issues) == 1
        assert "import-existence tautology" in issues[0][1]

    def test_import_then_real_use_not_flagged(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    from mymod import thing\n"
            "    assert thing() == 5\n",
        )
        assert check_weak_tests.check_file(fp) == []


class TestOrTrueTautology:
    def test_flags_or_true(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    result = run()\n    assert result or True\n",
        )
        issues = check_weak_tests.check_file(fp)
        assert len(issues) == 1
        assert "always true" in issues[0][1]


class TestNullcheckTautology:
    def test_flags_is_none_or_is_not_none(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    r = run()\n"
            "    assert r is None or r is not None\n",
        )
        issues = check_weak_tests.check_file(fp)
        assert len(issues) == 1
        assert "accepts any value" in issues[0][1]

    def test_flags_reversed_ordering(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    r = run()\n"
            "    assert r is not None or r is None\n",
        )
        assert len(check_weak_tests.check_file(fp)) == 1

    def test_different_operands_not_flagged(self, tmp_path):
        # `a is None or b is not None` is a real (if odd) condition.
        fp = _write(
            tmp_path,
            "def test_x():\n    assert a is None or b is not None\n",
        )
        assert check_weak_tests.check_file(fp) == []


class TestOptOut:
    def test_allow_comment_on_def_line(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():  # allow: weak-test\n    assert True\n",
        )
        assert check_weak_tests.check_file(fp) == []

    def test_allow_comment_on_assert_line(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    r = run()\n"
            "    assert r or True  # allow: weak-test\n",
        )
        assert check_weak_tests.check_file(fp) == []


class TestSkipMarkers:
    def test_skip_decorated_test_not_flagged(self, tmp_path):
        fp = _write(
            tmp_path,
            "import pytest\n\n"
            "@pytest.mark.skip(reason='wip')\n"
            "def test_x():\n    assert True\n",
        )
        assert check_weak_tests.check_file(fp) == []

    def test_xfail_decorated_test_not_flagged(self, tmp_path):
        fp = _write(
            tmp_path,
            "import pytest\n\n"
            "@pytest.mark.xfail(reason='not yet')\n"
            "def test_x():\n    assert True\n",
        )
        assert check_weak_tests.check_file(fp) == []


class TestEdgeCases:
    def test_non_test_function_not_flagged(self, tmp_path):
        # Helper functions (not test_*) are out of scope.
        fp = _write(
            tmp_path,
            "def helper():\n    assert True\n",
        )
        assert check_weak_tests.check_file(fp) == []

    def test_docstring_stripped_before_analysis(self, tmp_path):
        fp = _write(
            tmp_path,
            'def test_x():\n    """A docstring."""\n    assert True\n',
        )
        assert len(check_weak_tests.check_file(fp)) == 1

    def test_syntax_error_returns_no_issues(self, tmp_path):
        fp = _write(tmp_path, "def test_x(:\n    assert True\n")
        # Syntax errors are left to ruff/py_compile, not this hook.
        assert check_weak_tests.check_file(fp) == []

    def test_main_exit_code_clean(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    assert compute() == 1\n",
        )
        assert check_weak_tests.main([fp]) == 0

    def test_main_exit_code_dirty(self, tmp_path):
        fp = _write(
            tmp_path,
            "def test_x():\n    assert True\n",
        )
        assert check_weak_tests.main([fp]) == 1
