Files
cowork-local/tests/routing/test_per_workspace_modes.py
T
2026-08-09 20:12:05 +07:00

126 lines
4.5 KiB
Python

"""Per-workspace mode resolution: each workspace keeps its own routing /
auto-run mode, falling back to the global default when unset.
Exercises AppContext.project_routing_mode / set_project_routing_mode /
project_confirm_commands / set_project_auto_run against a temp projects dir.
"""
from __future__ import annotations
import copy
import pytest
from cowork_local.config import DEFAULT_CONFIG, AppConfig
from cowork_local.core import projects as projects_mod
from cowork_local.core.projects import Project, new_project, save_project
from cowork_local.state import AppContext
@pytest.fixture()
def ctx(tmp_path, monkeypatch):
# Redirect the projects store to a temp dir so load/save hit tmp, not $HOME.
monkeypatch.setattr(projects_mod, "PROJECTS_DIR", tmp_path / "projects")
data = copy.deepcopy(DEFAULT_CONFIG)
cfg = AppConfig(data=data, path=tmp_path / "config.json")
return AppContext(cfg)
def _mk(ctx, name):
return new_project(name, directory=projects_mod.PROJECTS_DIR)
def test_defaults_follow_global_when_no_override(ctx):
a = _mk(ctx, "Alpha")
ctx.active_project_id = a.project_id
# Global default switch_mode is "off".
assert ctx.project_routing_mode("cowork") == "off"
# Change the GLOBAL default → project with no override follows it.
ctx.config.data["routing"]["switch_mode"] = "auto"
assert ctx.project_routing_mode("cowork") == "auto"
def test_per_workspace_routing_is_isolated(ctx):
a = _mk(ctx, "Alpha")
b = _mk(ctx, "Beta")
ctx.active_project_id = a.project_id
ctx.set_project_routing_mode("cowork", "auto")
assert ctx.project_routing_mode("cowork") == "auto"
# Switching to workspace B must NOT see A's override (falls back to global).
ctx.active_project_id = b.project_id
assert ctx.project_routing_mode("cowork") == "off"
# B sets its own, independently.
ctx.set_project_routing_mode("cowork", "manual")
assert ctx.project_routing_mode("cowork") == "manual"
# A is unchanged.
ctx.active_project_id = a.project_id
assert ctx.project_routing_mode("cowork") == "auto"
def test_per_surface_isolated_within_a_workspace(ctx):
a = _mk(ctx, "Alpha")
ctx.active_project_id = a.project_id
ctx.set_project_routing_mode("cowork", "auto")
ctx.set_project_routing_mode("ai_edit", "manual")
# co4e untouched → global default.
assert ctx.project_routing_mode("cowork") == "auto"
assert ctx.project_routing_mode("ai_edit") == "manual"
assert ctx.project_routing_mode("co4e") == "off"
def test_routing_mode_persists_to_disk(ctx):
a = _mk(ctx, "Alpha")
ctx.active_project_id = a.project_id
ctx.set_project_routing_mode("co4e", "auto")
# Reload the project from disk — the override survived.
reloaded = projects_mod.load_project(a.project_id, projects_mod.PROJECTS_DIR)
assert reloaded.routing_modes.get("co4e") == "auto"
def test_auto_run_per_workspace(ctx):
a = _mk(ctx, "Alpha")
b = _mk(ctx, "Beta")
# Global default: cowork_confirm_commands is False → auto-run (no confirm).
ctx.active_project_id = a.project_id
assert ctx.project_confirm_commands() is False
assert ctx.project_auto_run() is True
# A: require confirm (auto_run=False). B stays on the global default.
ctx.set_project_auto_run(False)
assert ctx.project_confirm_commands() is True
ctx.active_project_id = b.project_id
assert ctx.project_confirm_commands() is False # B unaffected by A
def test_auto_run_none_follows_global(ctx):
a = _mk(ctx, "Alpha")
ctx.active_project_id = a.project_id
# Turn the GLOBAL confirm setting on; project override is None → follows it.
ctx.config.data["agent_security"]["cowork_confirm_commands"] = True
assert ctx.project_confirm_commands() is True
# Explicit per-project auto-run overrides the global.
ctx.set_project_auto_run(True) # auto-approve
assert ctx.project_confirm_commands() is False
def test_no_active_project_uses_global(ctx):
# active_project_id points at a non-existent project → global fallback.
ctx.active_project_id = "does-not-exist"
ctx.config.data["routing"]["switch_mode"] = "manual"
assert ctx.project_routing_mode("cowork") == "manual"
# Setting a mode with no real project writes the GLOBAL setting.
ctx.set_project_routing_mode("cowork", "auto")
assert ctx.config.routing_mode_for("cowork") == "auto"
def test_project_dataclass_defaults():
# New fields have safe defaults and round-trip through asdict/load.
p = Project(project_id="x", name="X")
assert p.routing_modes == {}
assert p.auto_run is None