"""Regression tests for DB-file permission hardening (PR #3135).

``DatabaseManager.create_user_database`` tightens the on-disk database file to
owner-only (0o600). The encrypted (SQLCipher) branch chmods right after
creating the file; the unencrypted fallback — which writes PLAINTEXT data and
is therefore the higher-risk path — chmods after ``initialize_database``
materializes the file. This test pins the unencrypted behavior, mirroring the
salt-file permission test in ``test_encryption_constants.py``.
"""

import os
import stat
from unittest.mock import patch

from tests.test_utils import add_src_to_path

add_src_to_path()


class TestDatabaseFilePermissions:
    def test_unencrypted_db_file_has_restrictive_permissions(
        self, tmp_path, monkeypatch
    ):
        """The unencrypted-fallback DB file must be created 0o600.

        It holds plaintext user data, so leaving it at umask-default perms
        (commonly 0o644) would expose it to other local accounts.
        """
        from local_deep_research.database.encrypted_db import DatabaseManager

        monkeypatch.setenv("LDR_BOOTSTRAP_ALLOW_UNENCRYPTED", "true")

        with patch(
            "local_deep_research.database.encrypted_db.get_sqlcipher_module"
        ) as mock_get_sqlcipher:
            mock_get_sqlcipher.side_effect = ImportError(
                "No module named 'sqlcipher3'"
            )
            with patch(
                "local_deep_research.database.encrypted_db.get_data_directory",
                return_value=tmp_path,
            ):
                manager = DatabaseManager()
                assert manager.has_encryption is False, (
                    "Test requires the unencrypted fallback path"
                )

                engine = manager.create_user_database(
                    "permuser", "test-password-123"
                )
                try:
                    db_path = manager._get_user_db_path("permuser")
                    assert db_path.exists(), "DB file was not created"

                    mode = stat.S_IMODE(os.stat(db_path).st_mode)
                    assert mode == 0o600, (
                        f"Unencrypted DB file should be 0o600, got {oct(mode)}"
                    )

                    # The per-user DB directory is owner-only (0o700), so the
                    # plaintext WAL/SHM sidecars and any temp files SQLite may
                    # create alongside the DB are not sibling-readable either.
                    dir_mode = stat.S_IMODE(os.stat(manager.data_dir).st_mode)
                    assert dir_mode == 0o700, (
                        f"DB directory should be 0o700, got {oct(dir_mode)}"
                    )
                finally:
                    engine.dispose()

    def test_db_creation_survives_chmod_failure(self, tmp_path, monkeypatch):
        """Permission hardening must never break DB creation.

        On filesystems that reject POSIX chmod (some Docker bind mounts,
        network/FUSE volumes — e.g. Docker Desktop on macOS/Windows),
        os.chmod can raise OSError. The chmods are best-effort, so
        create_user_database must still succeed and return a usable engine.
        """
        from sqlalchemy import text

        from local_deep_research.database.encrypted_db import DatabaseManager

        monkeypatch.setenv("LDR_BOOTSTRAP_ALLOW_UNENCRYPTED", "true")

        with patch(
            "local_deep_research.database.encrypted_db.get_sqlcipher_module",
            side_effect=ImportError("No module named 'sqlcipher3'"),
        ):
            with patch(
                "local_deep_research.database.encrypted_db.get_data_directory",
                return_value=tmp_path,
            ):
                # Simulate a volume that rejects chmod for every call.
                with patch(
                    "local_deep_research.database.encrypted_db.os.chmod",
                    side_effect=OSError("Operation not permitted"),
                ):
                    manager = DatabaseManager()
                    engine = manager.create_user_database(
                        "chmoduser", "test-password-123"
                    )
                    try:
                        # DB must be created and queryable despite chmod failing.
                        with engine.connect() as conn:
                            assert conn.execute(text("SELECT 1")).scalar() == 1
                    finally:
                        engine.dispose()
