55aee7cf22
CI / lint (pull_request) Successful in 18s
CI / quality (pull_request) Successful in 21s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 14s
CI / security (pull_request) Successful in 49s
CI / typecheck (pull_request) Successful in 56s
CI / integration_tests (pull_request) Successful in 2m35s
CI / unit_tests (pull_request) Successful in 14m30s
CI / docker (pull_request) Successful in 55s
CI / benchmark-regression (pull_request) Successful in 21m11s
CI / coverage (pull_request) Successful in 34m22s
CI / lint (push) Successful in 12s
CI / build (push) Successful in 16s
CI / quality (push) Successful in 17s
CI / security (push) Successful in 29s
CI / typecheck (push) Successful in 1m0s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 4m54s
CI / benchmark-publish (push) Successful in 11m48s
CI / unit_tests (push) Successful in 16m30s
CI / docker (push) Successful in 28s
CI / coverage (push) Successful in 25m45s
The step_register_skills_table step called add_skill in a loop but only committed once at the end. Because SkillRepository.create() obtains a new session per call and only flushes (never commits), the intermediate sessions could be garbage-collected before the final commit, rolling back their transactions on the shared SQLite :memory: connection. Moving _commit_pending inside the loop ensures each skill is durably committed before the next session is created. ISSUES CLOSED: #418
202 lines
6.5 KiB
Python
202 lines
6.5 KiB
Python
# pyright: reportRedeclaration=false
|
|
"""Step definitions for skill CLI uncovered-lines coverage."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
from behave import given, then, when
|
|
from typer.testing import CliRunner
|
|
|
|
from cleveragents.cli.commands.skill import (
|
|
_get_skill_service,
|
|
_reset_skill_service,
|
|
)
|
|
from cleveragents.cli.commands.skill import (
|
|
app as skill_app,
|
|
)
|
|
from cleveragents.domain.models.core.skill import (
|
|
Skill,
|
|
SkillAgentSource,
|
|
SkillInclude,
|
|
SkillMcpSource,
|
|
)
|
|
|
|
# ── helpers ─────────────────────────────────────────────────
|
|
|
|
|
|
def _make_skill(
|
|
name: str,
|
|
description: str = "test skill",
|
|
tool_refs: list[str] | None = None,
|
|
includes: list[SkillInclude] | None = None,
|
|
mcp_servers: list[SkillMcpSource] | None = None,
|
|
agent_skills: list[SkillAgentSource] | None = None,
|
|
) -> Skill:
|
|
return Skill(
|
|
name=name,
|
|
description=description,
|
|
tool_refs=tool_refs or [],
|
|
includes=includes or [],
|
|
mcp_servers=mcp_servers or [],
|
|
agent_skills=agent_skills or [],
|
|
)
|
|
|
|
|
|
# ── Given ───────────────────────────────────────────────────
|
|
|
|
|
|
@given("a clean skill service")
|
|
def step_clean_skill_service(context):
|
|
_reset_skill_service()
|
|
context.runner = CliRunner()
|
|
context.service = _get_skill_service()
|
|
|
|
|
|
@given("a skill with an include pointing to a non-registered skill")
|
|
def step_skill_with_unregistered_include(context):
|
|
skill = _make_skill(
|
|
name="local/inc-parent",
|
|
includes=[SkillInclude(name="local/missing-child")],
|
|
)
|
|
context.service._skills[skill.name] = skill
|
|
context.skill_name = skill.name
|
|
|
|
|
|
@given("a registered skill for capability error testing")
|
|
def step_skill_for_capability_error(context):
|
|
skill = _make_skill(
|
|
name="local/cap-error",
|
|
tool_refs=["builtin/read-file"],
|
|
)
|
|
context.service._skills[skill.name] = skill
|
|
context.skill_name = skill.name
|
|
|
|
|
|
@given("a registered skill for dependent actors testing")
|
|
def step_skill_for_dependent_actors(context):
|
|
skill = _make_skill(
|
|
name="local/dep-actors",
|
|
tool_refs=["builtin/write-file"],
|
|
)
|
|
context.service._skills[skill.name] = skill
|
|
context.skill_name = skill.name
|
|
|
|
|
|
@given("a registered skill with agent_skill sources")
|
|
def step_skill_with_agent_skills(context):
|
|
skill = _make_skill(
|
|
name="local/with-agents",
|
|
agent_skills=[SkillAgentSource(path="./skills/my-agent")],
|
|
)
|
|
context.service._skills[skill.name] = skill
|
|
context.skill_name = skill.name
|
|
|
|
|
|
@given("a registered skill for removal with actor dependents")
|
|
def step_skill_for_removal_actors(context):
|
|
skill = _make_skill(
|
|
name="local/removable-actors",
|
|
tool_refs=["builtin/exec"],
|
|
)
|
|
context.service._skills[skill.name] = skill
|
|
context.skill_name = skill.name
|
|
|
|
|
|
# ── When ────────────────────────────────────────────────────
|
|
|
|
|
|
@when("I run skill show for the skill with unregistered include")
|
|
def step_run_show_unregistered_include(context):
|
|
context.result = context.runner.invoke(skill_app, ["show", context.skill_name])
|
|
|
|
|
|
@when("I run skill show with capability summary raising ValueError")
|
|
def step_run_show_capability_error(context):
|
|
with patch.object(
|
|
context.service,
|
|
"compute_capability_summary",
|
|
side_effect=ValueError("boom"),
|
|
):
|
|
context.result = context.runner.invoke(skill_app, ["show", context.skill_name])
|
|
|
|
|
|
@when("I run skill show with dependents returning actors")
|
|
def step_run_show_dependent_actors(context):
|
|
with patch.object(
|
|
context.service,
|
|
"get_dependents",
|
|
return_value={"skills": [], "actors": ["actor/alpha", "actor/beta"]},
|
|
):
|
|
context.result = context.runner.invoke(skill_app, ["show", context.skill_name])
|
|
|
|
|
|
@when("I run skill tools for the agent_skill skill")
|
|
def step_run_tools_agent_skill(context):
|
|
context.result = context.runner.invoke(skill_app, ["tools", context.skill_name])
|
|
|
|
|
|
@when("I run skill remove with dependents returning actors")
|
|
def step_run_remove_with_actor_dependents(context):
|
|
with patch.object(
|
|
context.service,
|
|
"get_dependents",
|
|
return_value={
|
|
"skills": [],
|
|
"actors": ["actor/gamma", "actor/delta"],
|
|
},
|
|
):
|
|
context.result = context.runner.invoke(
|
|
skill_app,
|
|
["remove", context.skill_name, "--yes"],
|
|
)
|
|
|
|
|
|
# ── Then ────────────────────────────────────────────────────
|
|
|
|
|
|
@then("the show output contains the not registered marker")
|
|
def step_show_not_registered(context):
|
|
assert context.result.exit_code == 0, (
|
|
f"exit_code={context.result.exit_code}\n{context.result.output}"
|
|
)
|
|
assert "not registered" in context.result.output
|
|
|
|
|
|
@then("the show output contains OK and no capability panel")
|
|
def step_show_ok_no_capability(context):
|
|
assert context.result.exit_code == 0, (
|
|
f"exit_code={context.result.exit_code}\n{context.result.output}"
|
|
)
|
|
assert "OK" in context.result.output
|
|
# The capability panel should be absent because the exception was caught
|
|
assert "Capability Summary" not in context.result.output
|
|
|
|
|
|
@then("the show output contains the actors reference line")
|
|
def step_show_actors_reference(context):
|
|
assert context.result.exit_code == 0, (
|
|
f"exit_code={context.result.exit_code}\n{context.result.output}"
|
|
)
|
|
assert "actor/alpha" in context.result.output
|
|
assert "actor/beta" in context.result.output
|
|
assert "Actors" in context.result.output
|
|
|
|
|
|
@then("the tools output contains agent_skill source type")
|
|
def step_tools_agent_skill_source(context):
|
|
assert context.result.exit_code == 0, (
|
|
f"exit_code={context.result.exit_code}\n{context.result.output}"
|
|
)
|
|
assert "agent_skill" in context.result.output
|
|
|
|
|
|
@then("the remove output contains the actor dependency warning")
|
|
def step_remove_actor_warning(context):
|
|
assert context.result.exit_code == 0, (
|
|
f"exit_code={context.result.exit_code}\n{context.result.output}"
|
|
)
|
|
assert "actor(s) reference this skill" in context.result.output
|
|
assert "actor/gamma" in context.result.output
|
|
assert "actor/delta" in context.result.output
|