# tests/test_input_validation.py
"""Comprehensive tests for input validation functions."""
import pytest


class TestInputValidation:
    """Tests for _validate_string_input() function."""

    def test_rejects_null_bytes(self):
        """Should reject strings containing null bytes."""
        from second_opinion_mcp.server import _validate_string_input
        err, _ = _validate_string_input("test\x00evil", 100, "field")
        assert err is not None
        assert "null" in err.lower()

    def test_rejects_excessive_length(self):
        """Should reject strings exceeding max_length."""
        from second_opinion_mcp.server import _validate_string_input
        err, _ = _validate_string_input("a" * 5000, 100, "field")
        assert err is not None
        assert "exceeds maximum length" in err

    def test_accepts_valid_input(self):
        """Should accept valid strings and return them unchanged."""
        from second_opinion_mcp.server import _validate_string_input
        err, val = _validate_string_input("valid input", 100, "field")
        assert err is None
        assert val == "valid input"

    def test_rejects_non_string_input(self):
        """Should reject non-string inputs with type error."""
        from second_opinion_mcp.server import _validate_string_input
        err, _ = _validate_string_input(12345, 100, "field")  # type: ignore
        assert err is not None
        assert "must be a string" in err

    def test_accepts_empty_string(self):
        """Should accept empty strings (they may be valid for optional fields)."""
        from second_opinion_mcp.server import _validate_string_input
        err, val = _validate_string_input("", 100, "field")
        assert err is None
        assert val == ""

    def test_accepts_exact_max_length(self):
        """Should accept strings at exactly max_length."""
        from second_opinion_mcp.server import _validate_string_input
        err, val = _validate_string_input("a" * 100, 100, "field")
        assert err is None
        assert len(val) == 100

    def test_rejects_one_over_max_length(self):
        """Should reject strings one character over max_length."""
        from second_opinion_mcp.server import _validate_string_input
        err, _ = _validate_string_input("a" * 101, 100, "field")
        assert err is not None

    def test_handles_whitespace_only_strings(self):
        """Should accept whitespace-only strings (validation, not sanitization)."""
        from second_opinion_mcp.server import _validate_string_input
        err, val = _validate_string_input("   ", 100, "field")
        assert err is None
        assert val == "   "

    def test_handles_unicode_characters(self):
        """Should handle unicode/multibyte characters correctly."""
        from second_opinion_mcp.server import _validate_string_input
        # Unicode string with emoji and CJK characters
        unicode_str = "Hello 世界 🌍 café"
        err, val = _validate_string_input(unicode_str, 100, "field")
        assert err is None
        assert val == unicode_str

    def test_unicode_length_is_char_count_not_bytes(self):
        """Length check should use character count, not byte count."""
        from second_opinion_mcp.server import _validate_string_input
        # This string is 5 characters but more bytes in UTF-8
        unicode_str = "日本語中文"
        err, val = _validate_string_input(unicode_str, 5, "field")
        assert err is None  # 5 chars should fit in max_length=5
        assert val == unicode_str

    def test_null_byte_at_start(self):
        """Should detect null byte at start of string."""
        from second_opinion_mcp.server import _validate_string_input
        err, _ = _validate_string_input("\x00start", 100, "field")
        assert err is not None
        assert "null" in err.lower()

    def test_null_byte_at_end(self):
        """Should detect null byte at end of string."""
        from second_opinion_mcp.server import _validate_string_input
        err, _ = _validate_string_input("end\x00", 100, "field")
        assert err is not None
        assert "null" in err.lower()

    def test_multiple_null_bytes(self):
        """Should detect multiple null bytes."""
        from second_opinion_mcp.server import _validate_string_input
        err, _ = _validate_string_input("a\x00b\x00c", 100, "field")
        assert err is not None
        assert "null" in err.lower()


class TestPathValidation:
    """Tests for _validate_path_input() function."""

    def test_validates_normal_path(self):
        """Should accept normal file paths."""
        from second_opinion_mcp.server import _validate_path_input
        err, val = _validate_path_input("/home/user/project")
        assert err is None
        assert val == "/home/user/project"

    def test_validates_windows_path(self):
        """Should accept Windows-style paths."""
        from second_opinion_mcp.server import _validate_path_input
        err, val = _validate_path_input("C:\\Users\\test\\project")
        assert err is None

    def test_rejects_excessively_long_path(self):
        """Should reject paths exceeding MAX_PATH_LENGTH."""
        from second_opinion_mcp.server import _validate_path_input, MAX_PATH_LENGTH
        long_path = "/" + "a" * (MAX_PATH_LENGTH + 1)
        err, _ = _validate_path_input(long_path)
        assert err is not None


class TestPromptValidation:
    """Tests for _validate_prompt_input() function."""

    def test_validates_normal_prompt(self):
        """Should accept normal prompts."""
        from second_opinion_mcp.server import _validate_prompt_input
        err, val = _validate_prompt_input("Review this code for security issues")
        assert err is None
        assert val == "Review this code for security issues"

    def test_rejects_excessively_long_prompt(self):
        """Should reject prompts exceeding MAX_PROMPT_LENGTH."""
        from second_opinion_mcp.server import _validate_prompt_input, MAX_PROMPT_LENGTH
        long_prompt = "a" * (MAX_PROMPT_LENGTH + 1)
        err, _ = _validate_prompt_input(long_prompt)
        assert err is not None
