Newer
Older
navi-1 / tests / unit / tools / test_areas.py
"""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)