Files
nanobot/conftest.py
T
李明振andXubin Ren b34f1bd0e8 fix(session): store session history outside the agent workspace
Session files lived under <workspace>/sessions/ (since #713), which is the
on-disk scope of the agent's filesystem tools. With restrict_to_workspace
enabled, an agent could read_file / list_dir every session transcript —
including other users' or channels' conversations — bypassing the scoped
sessions.py access layer entirely.

Move session storage to ~/.nanobot/sessions/<sha256-of-resolved-workspace>[:16]/,
outside the workspace. Per-workspace isolation (the goal of #713) is preserved
via a hash of the resolved workspace path, so different workspaces keep
independent session stores. A one-shot, idempotent migration moves legacy
in-workspace *.jsonl files into the new location at store init.

Scope note: this protects sessions whenever restrict_to_workspace=true. The
default restrict_to_workspace=false leaves read_file unrestricted in general
(not only sessions) and is a separate concern.

Refs #5278
2026-08-13 01:41:10 +09:00

77 lines
2.4 KiB
Python

"""Cross-suite test infrastructure."""
from __future__ import annotations
import os
import ssl
import sys
from collections.abc import Iterator
from pathlib import Path
import certifi
import pytest
from loguru import logger
@pytest.fixture(autouse=True)
def _isolate_nanobot_log_activation() -> Iterator[None]:
"""Keep CLI log settings from leaking into later tests in the same process."""
logger.enable("nanobot")
try:
yield
finally:
logger.enable("nanobot")
@pytest.fixture(autouse=True)
def _isolate_sessions_root(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Iterator[None]:
"""Redirect session storage away from the real ~/.nanobot/sessions.
Session storage lives under get_legacy_sessions_dir() (outside the workspace,
per ADR-0001), so without redirection tests would write into the real home.
"""
root = tmp_path / "sessions-root"
monkeypatch.setattr("nanobot.session.manager.get_legacy_sessions_dir", lambda: root)
monkeypatch.setattr("nanobot.config.paths.get_legacy_sessions_dir", lambda: root)
yield
@pytest.fixture(scope="session", autouse=True)
def _use_windows_system_ca_for_default_http_clients() -> Iterator[None]:
"""Avoid reparsing certifi's CA bundle for every offline HTTP client.
Loading certifi takes roughly 0.7 seconds per client on Windows. The test
suite constructs hundreds of clients while mocking their I/O. System roots
preserve certificate verification for accidental local requests; explicit
``cafile``, ``capath``, and ``cadata`` arguments still use the real loader.
"""
if sys.platform != "win32":
yield
return
original = ssl.create_default_context
certifi_path = os.path.normcase(os.path.abspath(certifi.where()))
def create_default_context(
purpose: ssl.Purpose = ssl.Purpose.SERVER_AUTH,
*,
cafile: str | None = None,
capath: str | None = None,
cadata: str | bytes | None = None,
) -> ssl.SSLContext:
requested_path = os.path.normcase(os.path.abspath(cafile)) if cafile else None
if requested_path == certifi_path and capath is None and cadata is None:
return original(purpose)
return original(
purpose,
cafile=cafile,
capath=capath,
cadata=cadata,
)
ssl.create_default_context = create_default_context
try:
yield
finally:
ssl.create_default_context = original