# -*- coding: utf-8 -*-
"""
受保护路径黑名单测试套件。

借鉴 zhikuncode-main 的受保护路径机制，防止 Agent 误删用户敏感文件
（.git / .env / .ssh / .gnupg / .aws）。

覆盖场景：
1. 各黑名单项写入 / 编辑被拒绝
2. 路径穿越（../）规范化后命中黑名单
3. Windows 反斜杠路径分隔符兼容
4. 正常路径不受影响
5. is_protected_path 函数单独单元测试
"""
import os

import pytest

from abyss.tools.base import is_protected_path, PROTECTED_PATHS
from abyss.tools.file_edit import FileEditTool
from abyss.tools.file_write import FileWriteTool


# ========== 1. is_protected_path 单元测试 ==========

class TestIsProtectedPath:
    """is_protected_path 函数单元测试。"""

    @pytest.mark.parametrize("path,expected_hit", [
        (".git/config", ".git"),
        (".git", ".git"),
        (".env", ".env"),
        (".ssh/id_rsa", ".ssh"),
        (".gnupg/gpg.conf", ".gnupg"),
        (".aws/credentials", ".aws"),
    ])
    def test_blacklist_items_are_protected(self, path, expected_hit):
        """黑名单中各项应被识别为受保护。"""
        assert is_protected_path(path) is True

    def test_path_traversal_normalized(self):
        """路径穿越 ../ 经规范化后命中黑名单。"""
        assert is_protected_path("../.ssh/id_rsa") is True

    def test_windows_backslash_separator(self):
        """Windows 反斜杠路径分隔符也应被识别。"""
        assert is_protected_path("..\\.ssh\\id_rsa") is True

    def test_deep_nested_traversal(self):
        """深层路径穿越同样命中。"""
        assert is_protected_path("a/b/../../.git/HEAD") is True

    def test_normal_path_not_protected(self):
        """正常业务路径不应被误判。"""
        assert is_protected_path("src/main.py") is False

    def test_absolute_normal_path_not_protected(self, tmp_path):
        """绝对路径下的正常文件不应被误判。"""
        normal = tmp_path / "code.py"
        normal.write_text("print('hi')", encoding="utf-8")
        assert is_protected_path(str(normal)) is False

    def test_protected_paths_constant(self):
        """PROTECTED_PATHS 常量应包含五项敏感目录/文件。"""
        assert set(PROTECTED_PATHS) == {".git", ".env", ".ssh", ".gnupg", ".aws"}


# ========== 2. FileWriteTool 守卫测试 ==========

class TestFileWriteGuard:
    """file_write 工具的受保护路径守卫测试。"""

    @pytest.mark.parametrize("path,expected_name", [
        (".git/config", ".git"),
        (".env", ".env"),
        (".ssh/id_rsa", ".ssh"),
        (".gnupg/gpg.conf", ".gnupg"),
        (".aws/credentials", ".aws"),
    ])
    def test_protected_write_rejected(self, path, expected_name):
        """受保护路径写入应被拒绝，错误信息含命中的黑名单项。"""
        tool = FileWriteTool()

        out = tool.execute(path=path, content="恶意内容")

        assert out["success"] is False
        assert out["error"] == f"受保护路径禁止写入: {expected_name}"

    def test_traversal_write_rejected(self):
        """路径穿越写入 .ssh 应被拒绝。"""
        tool = FileWriteTool()

        out = tool.execute(path="../.ssh/id_rsa", content="泄露密钥")

        assert out["success"] is False
        # 路径穿越检测命中受保护路径时返回路径穿越错误（Phase 6 AP 后行为）
        assert ".ssh" in out["error"]

    def test_windows_backslash_write_rejected(self):
        """Windows 反斜杠路径写入 .ssh 应被拒绝。"""
        tool = FileWriteTool()

        out = tool.execute(path="..\\.ssh\\id_rsa", content="泄露密钥")

        assert out["success"] is False
        assert ".ssh" in out["error"]

    def test_normal_write_succeeds(self, tmp_path):
        """正常路径写入应正常执行，不被守卫拦截。"""
        # 显式设置工作目录为 tmp_path，避免被工作目录边界检测拦截
        from abyss.security.path_security import get_default_service
        get_default_service().set_working_dir(str(tmp_path))
        try:
            f = tmp_path / "main.py"
            tool = FileWriteTool()

            out = tool.execute(path=str(f), content="print('hi')")

            assert out["success"] is True
            assert f.read_text(encoding="utf-8") == "print('hi')"
        finally:
            get_default_service().set_working_dir(None)


# ========== 3. FileEditTool 守卫测试 ==========

class TestFileEditGuard:
    """file_edit 工具的受保护路径守卫测试。"""

    @pytest.mark.parametrize("path,expected_name", [
        (".git/config", ".git"),
        (".env", ".env"),
        (".ssh/id_rsa", ".ssh"),
        (".gnupg/gpg.conf", ".gnupg"),
        (".aws/credentials", ".aws"),
    ])
    def test_protected_edit_rejected(self, path, expected_name):
        """受保护路径编辑应被拒绝。"""
        tool = FileEditTool()

        out = tool.execute(path=path, old_str="a", new_str="b")

        assert out["success"] is False
        assert out["error"] == f"受保护路径禁止编辑: {expected_name}"

    def test_traversal_edit_rejected(self):
        """路径穿越编辑 .ssh 应被拒绝。"""
        tool = FileEditTool()

        out = tool.execute(path="../.ssh/id_rsa", old_str="a", new_str="b")

        assert out["success"] is False
        assert ".ssh" in out["error"]

    def test_windows_backslash_edit_rejected(self):
        """Windows 反斜杠路径编辑 .ssh 应被拒绝。"""
        tool = FileEditTool()

        out = tool.execute(path="..\\.ssh\\id_rsa", old_str="a", new_str="b")

        assert out["success"] is False
        assert ".ssh" in out["error"]

    def test_normal_edit_succeeds(self, tmp_path):
        """正常路径编辑应正常执行，不被守卫拦截。"""
        # 显式设置工作目录为 tmp_path，避免被工作目录边界检测拦截
        from abyss.security.path_security import get_default_service
        get_default_service().set_working_dir(str(tmp_path))
        try:
            f = tmp_path / "main.py"
            f.write_text("hello world\n", encoding="utf-8")
            tool = FileEditTool()

            out = tool.execute(path=str(f), old_str="hello", new_str="goodbye")

            assert out["success"] is True
            assert f.read_text(encoding="utf-8") == "goodbye world\n"
        finally:
            get_default_service().set_working_dir(None)
