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
163 lines
6.0 KiB
Python
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)
|