Files
2026-07-13 13:12:33 +08:00

123 lines
4.0 KiB
Python

"""skill_list / skill_view honor coding-mode gating (codex BLOCKER #2).
The gate now lives in ``skills.eligibility`` (single source of truth shared by
the skill tools, the pre-turn filter, and the meta-skill executors), so these
tests drive it through ``set_live_skills_config_getter``.
"""
from __future__ import annotations
from types import SimpleNamespace
import pytest
from opensquilla.skills import eligibility
from opensquilla.tools.builtin import skill_tools
@pytest.fixture(autouse=True)
def _reset_module_state():
saved_loader = skill_tools._loader
saved_getter = eligibility._live_skills_cfg_getter
yield
skill_tools._loader = saved_loader
eligibility.set_live_skills_config_getter(saved_getter)
def _install(coding_mode: bool, disabled=None):
eligibility.set_live_skills_config_getter(
lambda: SimpleNamespace(disabled=disabled or [], coding_mode=coding_mode)
)
def test_skill_available_off_gates_codetask():
_install(coding_mode=False)
assert skill_tools._skill_available("code-task") is False
assert skill_tools._skill_available("git-diff") is True
def test_skill_available_on_allows_codetask():
_install(coding_mode=True)
assert skill_tools._skill_available("code-task") is True
def test_skill_available_no_getter_fails_closed_for_codetask():
# Un-wired gate: coding-mode skills fail CLOSED (OFF is the safe default);
# ordinary skills stay available.
eligibility.set_live_skills_config_getter(None)
assert skill_tools._skill_available("code-task") is False
assert skill_tools._skill_available("git-diff") is True
class _FakeLoader:
def __init__(self, specs):
self._specs = specs
def load_all(self):
return self._specs
def get_by_name(self, name):
for s in self._specs:
if s.name == name:
return s
return None
def _spec(name):
return SimpleNamespace(name=name, description=f"{name} desc", content=f"body of {name}")
def _skill_view_handler():
from opensquilla.tools.registry import get_default_registry
return get_default_registry().get("skill_view").handler
@pytest.mark.asyncio
async def test_skill_view_blocks_codetask_when_off():
# skill_view must NOT return code-task content when coding mode is off,
# even though it exists in the loader (the bypass codex flagged).
skill_tools.create_skill_tools(
_FakeLoader([_spec("code-task"), _spec("git-diff")]),
skills_cfg_getter=lambda: SimpleNamespace(disabled=[], coding_mode=False),
)
view = _skill_view_handler()
out = await view("code-task")
assert "body of code-task" not in out
assert "not found" in out.lower()
# A normal skill still works.
assert "body of git-diff" in await view("git-diff")
@pytest.mark.asyncio
async def test_skill_view_allows_codetask_when_on():
skill_tools.create_skill_tools(
_FakeLoader([_spec("code-task")]),
skills_cfg_getter=lambda: SimpleNamespace(disabled=[], coding_mode=True),
)
out = await _skill_view_handler()("code-task")
assert "body of code-task" in out
def test_create_skill_tools_wires_shared_gate():
# create_skill_tools must publish its getter to the shared eligibility gate
# so the meta-skill executors see the same live config.
skill_tools.create_skill_tools(
_FakeLoader([_spec("code-task")]),
skills_cfg_getter=lambda: SimpleNamespace(disabled=[], coding_mode=False),
)
assert eligibility.is_skill_available_live("code-task") is False
def test_find_install_spec_refuses_gated_codetask():
# install_skill_deps resolves the skill via _find_install_spec; a gated
# code-task must report not-found before any install metadata is touched.
from opensquilla.tools.types import ToolError
skill_tools._loader = _FakeLoader([_spec("code-task")])
eligibility.set_live_skills_config_getter(
lambda: SimpleNamespace(disabled=[], coding_mode=False)
)
with pytest.raises(ToolError, match="not found"):
skill_tools._find_install_spec("code-task", "x")