Files
cleveragents-core/features/steps/skill_cli_coverage_steps.py
freemo 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
fix(test): commit after each add_skill to prevent session GC rollback, and improved coverage.
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
2026-02-24 12:19:04 -05:00

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