Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

163 lines
6.0 KiB
Python

"""Tests for paths module."""
import hashlib
from pathlib import Path
from unittest.mock import patch
import pytest
from local_deep_research.config.paths import (
get_data_directory,
get_research_outputs_directory,
get_cache_directory,
get_logs_directory,
get_encrypted_database_path,
get_user_database_filename,
get_data_dir,
)
class TestGetDataDirectory:
"""Tests for get_data_directory function."""
def test_uses_env_var_override(self, tmp_path, monkeypatch):
"""Should use LDR_DATA_DIR if set."""
monkeypatch.setenv("LDR_DATA_DIR", str(tmp_path))
result = get_data_directory()
assert result == tmp_path
def test_uses_platformdirs_when_no_env_var(self, monkeypatch):
"""Should use platformdirs when no env var set."""
monkeypatch.delenv("LDR_DATA_DIR", raising=False)
with patch("platformdirs.user_data_dir", return_value="/mock/path"):
result = get_data_directory()
assert result == Path("/mock/path")
def test_returns_path_object(self, mock_env_data_dir):
"""Should return Path object."""
result = get_data_directory()
assert isinstance(result, Path)
def test_apostrophe_in_path_is_allowed(self, tmp_path, monkeypatch):
"""Regression for #3090: a legitimate POSIX path containing an
apostrophe (e.g. /home/O'Brien/ldr) must NOT crash startup. The
over-broad quote rejection was removed; the actual ATTACH-DATABASE
sink validates the interpolated path separately."""
data_dir = tmp_path / "O'Brien" / "ldr"
monkeypatch.setenv("LDR_DATA_DIR", str(data_dir))
result = get_data_directory()
assert result == data_dir.resolve()
def test_relative_path_rejected(self, monkeypatch):
"""A relative LDR_DATA_DIR is rejected (must be absolute)."""
monkeypatch.setenv("LDR_DATA_DIR", "relative/dir")
with pytest.raises(ValueError, match="absolute"):
get_data_directory()
def test_control_characters_rejected(self, tmp_path, monkeypatch):
"""A newline in LDR_DATA_DIR is still rejected (could break a SQL/
ATTACH statement), and the offending value is not echoed."""
monkeypatch.setenv("LDR_DATA_DIR", str(tmp_path) + "\nDROP")
with pytest.raises(ValueError, match="control character") as exc_info:
get_data_directory()
assert "DROP" not in str(exc_info.value)
class TestGetResearchOutputsDirectory:
"""Tests for get_research_outputs_directory function."""
def test_returns_subdirectory_of_data_dir(self, mock_env_data_dir):
"""Should return research_outputs subdirectory."""
result = get_research_outputs_directory()
assert result.name == "research_outputs"
assert result.parent == mock_env_data_dir
def test_creates_directory(self, mock_env_data_dir):
"""Should create directory if it doesn't exist."""
result = get_research_outputs_directory()
assert result.exists()
assert result.is_dir()
class TestGetCacheDirectory:
"""Tests for get_cache_directory function."""
def test_returns_cache_subdirectory(self, mock_env_data_dir):
"""Should return cache subdirectory."""
result = get_cache_directory()
assert result.name == "cache"
assert result.parent == mock_env_data_dir
def test_creates_directory(self, mock_env_data_dir):
"""Should create directory if it doesn't exist."""
result = get_cache_directory()
assert result.exists()
class TestGetLogsDirectory:
"""Tests for get_logs_directory function."""
def test_returns_logs_subdirectory(self, mock_env_data_dir):
"""Should return logs subdirectory."""
result = get_logs_directory()
assert result.name == "logs"
assert result.parent == mock_env_data_dir
def test_creates_directory(self, mock_env_data_dir):
"""Should create directory if it doesn't exist."""
result = get_logs_directory()
assert result.exists()
class TestGetEncryptedDatabasePath:
"""Tests for get_encrypted_database_path function."""
def test_returns_encrypted_databases_subdirectory(self, mock_env_data_dir):
"""Should return encrypted_databases subdirectory."""
result = get_encrypted_database_path()
assert result.name == "encrypted_databases"
assert result.parent == mock_env_data_dir
def test_creates_directory(self, mock_env_data_dir):
"""Should create directory if it doesn't exist."""
result = get_encrypted_database_path()
assert result.exists()
class TestGetUserDatabaseFilename:
"""Tests for get_user_database_filename function."""
def test_generates_hashed_filename(self):
"""Should generate filename with username hash."""
result = get_user_database_filename("testuser")
expected_hash = hashlib.sha256("testuser".encode()).hexdigest()[:16]
assert result == f"ldr_user_{expected_hash}.db"
def test_consistent_for_same_username(self):
"""Should return same filename for same username."""
result1 = get_user_database_filename("testuser")
result2 = get_user_database_filename("testuser")
assert result1 == result2
def test_different_for_different_usernames(self):
"""Should return different filenames for different usernames."""
result1 = get_user_database_filename("user1")
result2 = get_user_database_filename("user2")
assert result1 != result2
def test_handles_special_characters(self):
"""Should handle special characters in username."""
result = get_user_database_filename("user@domain.com")
assert result.startswith("ldr_user_")
assert result.endswith(".db")
class TestGetDataDir:
"""Tests for get_data_dir backward compat function."""
def test_returns_string(self, mock_env_data_dir):
"""Should return string instead of Path."""
result = get_data_dir()
assert isinstance(result, str)
assert result == str(mock_env_data_dir)