"""Unit tests for the shared file-area helpers (navi/tools/_internal/areas.py)."""
from pathlib import Path
import pytest
from navi.config import Settings
from navi.tools._internal.areas import (
allowed_areas,
is_within,
resolve_in_areas,
session_area,
user_sandbox,
)
@pytest.fixture(autouse=True)
def _areas(monkeypatch, tmp_path):
import navi.session_files as _sf_mod
monkeypatch.setattr(_sf_mod, "settings", Settings(session_files_dir=str(tmp_path / "sessions")))
monkeypatch.chdir(tmp_path)
return tmp_path
class TestAllowedAreas:
def test_sandbox_is_created_and_comes_first(self, tmp_path):
areas = allowed_areas("7", "s1")
assert areas[0] == (tmp_path / "user_data" / "7").resolve()
assert areas[0].is_dir()
def test_session_area_only_with_a_session(self, tmp_path):
assert session_area(None) is None
assert session_area("") is None
assert session_area("s1") == (tmp_path / "sessions" / "s1").resolve()
assert len(allowed_areas("7")) == 1
assert allowed_areas("7", "s1")[1] == (tmp_path / "sessions" / "s1").resolve()
def test_user_sandbox_is_per_user(self, tmp_path):
assert user_sandbox("7") != user_sandbox("8")
class TestResolveInAreas:
def test_relative_resolves_into_sandbox(self, tmp_path):
areas = allowed_areas("7", "s1")
assert resolve_in_areas(Path("a.txt"), areas) == (tmp_path / "user_data" / "7" / "a.txt").resolve()
def test_relative_is_not_redirected_into_the_session_area(self, tmp_path):
areas = allowed_areas("7", "s1")
resolved = resolve_in_areas(Path("a.txt"), areas)
assert not is_within(resolved, [areas[1]])
def test_absolute_inside_session_area_allowed(self, tmp_path):
session = tmp_path / "sessions" / "s1"
session.mkdir(parents=True)
areas = allowed_areas("7", "s1")
assert resolve_in_areas(session / "upload.wav", areas) == (session / "upload.wav").resolve()
def test_absolute_outside_every_area_denied(self, tmp_path):
areas = allowed_areas("7", "s1")
assert resolve_in_areas(tmp_path / "outside.txt", areas) is None
class TestIsWithin:
def test_sibling_prefix_is_not_inside(self, tmp_path):
areas = allowed_areas("7")
# user_data/70 is not user_data/7 — containment is per path component.
assert not is_within(tmp_path / "user_data" / "70" / "f.txt", areas)
def test_dotdot_escape_is_not_inside(self, tmp_path):
areas = allowed_areas("7")
assert not is_within(tmp_path / "user_data" / "7" / ".." / "8", areas)
def test_the_root_itself_is_inside(self, tmp_path):
areas = allowed_areas("7")
assert is_within(areas[0], areas)