kin: auto-commit after pipeline
This commit is contained in:
parent
938f7e3a57
commit
4f1dfbf10f
1 changed files with 135 additions and 0 deletions
|
|
@ -285,3 +285,138 @@ class TestApiFollowupEmptyArrayNeedsDecision:
|
||||||
)
|
)
|
||||||
assert data["created"] == []
|
assert data["created"] == []
|
||||||
assert data["pending_actions"] == []
|
assert data["pending_actions"] == []
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Additional edge cases — deeper investigation
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestRunPipelineNoneSteps:
|
||||||
|
"""run_pipeline with steps=None — must also return empty_pipeline, not crash."""
|
||||||
|
|
||||||
|
@patch("agents.runner.check_claude_auth")
|
||||||
|
def test_none_steps_returns_empty_pipeline_error(self, mock_auth, conn):
|
||||||
|
"""run_pipeline(steps=None) must return {success: False, error: 'empty_pipeline'}."""
|
||||||
|
from agents.runner import run_pipeline
|
||||||
|
result = run_pipeline(conn, "PROJ-001", None)
|
||||||
|
assert result["success"] is False
|
||||||
|
assert result.get("error") == "empty_pipeline", (
|
||||||
|
f"Expected error='empty_pipeline' for None steps, got: {result}"
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch("agents.runner.check_claude_auth")
|
||||||
|
def test_none_steps_does_not_mutate_task(self, mock_auth, conn):
|
||||||
|
"""run_pipeline(steps=None) must not change task status."""
|
||||||
|
from agents.runner import run_pipeline
|
||||||
|
before = models.get_task(conn, "PROJ-001")["status"]
|
||||||
|
run_pipeline(conn, "PROJ-001", None)
|
||||||
|
after = models.get_task(conn, "PROJ-001")["status"]
|
||||||
|
assert after == before, (
|
||||||
|
f"Task status changed from '{before}' to '{after}' after None steps"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunPipelineEmptyStepsDryRun:
|
||||||
|
"""run_pipeline(steps=[], dry_run=True) — must bail before auth check."""
|
||||||
|
|
||||||
|
def test_empty_steps_dry_run_returns_error_without_auth(self, conn):
|
||||||
|
"""run_pipeline(steps=[], dry_run=True) must return early without auth check."""
|
||||||
|
from agents.runner import run_pipeline
|
||||||
|
# No @patch for check_claude_auth — if auth is called, it may raise; empty guard must fire first
|
||||||
|
result = run_pipeline(conn, "PROJ-001", [], dry_run=True)
|
||||||
|
assert result["success"] is False
|
||||||
|
assert result.get("error") == "empty_pipeline"
|
||||||
|
|
||||||
|
|
||||||
|
class TestCliRunTaskNonListPipeline:
|
||||||
|
"""PM returns pipeline as a non-list non-null value (dict or string) — CLI must exit(1)."""
|
||||||
|
|
||||||
|
@patch("agents.runner.run_pipeline")
|
||||||
|
@patch("agents.runner.run_agent")
|
||||||
|
def test_dict_pipeline_exits_1(self, mock_run_agent, mock_run_pipeline, tmp_path):
|
||||||
|
"""PM returning pipeline={} (dict) → CLI exits 1."""
|
||||||
|
from cli.main import cli as kin_cli
|
||||||
|
db_path = _seed_db(tmp_path)
|
||||||
|
mock_run_agent.return_value = {
|
||||||
|
"success": True,
|
||||||
|
"output": json.dumps({"pipeline": {"steps": []}, "analysis": "..."}),
|
||||||
|
}
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(kin_cli, ["--db", db_path, "run", "PROJ-001"])
|
||||||
|
assert result.exit_code == 1, (
|
||||||
|
f"Expected exit_code=1 for dict pipeline, got {result.exit_code}"
|
||||||
|
)
|
||||||
|
mock_run_pipeline.assert_not_called()
|
||||||
|
|
||||||
|
@patch("agents.runner.run_pipeline")
|
||||||
|
@patch("agents.runner.run_agent")
|
||||||
|
def test_string_pipeline_exits_1(self, mock_run_agent, mock_run_pipeline, tmp_path):
|
||||||
|
"""PM returning pipeline='[]' (JSON-string-encoded) → CLI exits 1."""
|
||||||
|
from cli.main import cli as kin_cli
|
||||||
|
db_path = _seed_db(tmp_path)
|
||||||
|
mock_run_agent.return_value = {
|
||||||
|
"success": True,
|
||||||
|
"output": json.dumps({"pipeline": "[]", "analysis": "..."}),
|
||||||
|
}
|
||||||
|
runner = CliRunner()
|
||||||
|
result = runner.invoke(kin_cli, ["--db", db_path, "run", "PROJ-001"])
|
||||||
|
assert result.exit_code == 1, (
|
||||||
|
f"Expected exit_code=1 for string pipeline, got {result.exit_code}"
|
||||||
|
)
|
||||||
|
mock_run_pipeline.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
class TestGenerateFollowupsNullAndDict:
|
||||||
|
"""Additional generate_followups edge cases: null output, dict with empty tasks."""
|
||||||
|
|
||||||
|
@patch("agents.runner._run_claude")
|
||||||
|
def test_agent_returns_null_gives_empty_result(self, mock_claude, conn):
|
||||||
|
"""generate_followups: agent returning 'null' → {created: [], pending_actions: []}."""
|
||||||
|
from core.followup import generate_followups
|
||||||
|
mock_claude.return_value = {"output": "null", "returncode": 0}
|
||||||
|
result = generate_followups(conn, "PROJ-001")
|
||||||
|
assert result["created"] == [], f"Expected created=[], got: {result['created']}"
|
||||||
|
assert result["pending_actions"] == []
|
||||||
|
|
||||||
|
@patch("agents.runner._run_claude")
|
||||||
|
def test_agent_returns_null_creates_no_tasks(self, mock_claude, conn):
|
||||||
|
"""generate_followups: agent returning 'null' must not create any tasks."""
|
||||||
|
from core.followup import generate_followups
|
||||||
|
mock_claude.return_value = {"output": "null", "returncode": 0}
|
||||||
|
tasks_before = conn.execute("SELECT COUNT(*) FROM tasks").fetchone()[0]
|
||||||
|
generate_followups(conn, "PROJ-001")
|
||||||
|
tasks_after = conn.execute("SELECT COUNT(*) FROM tasks").fetchone()[0]
|
||||||
|
assert tasks_after == tasks_before
|
||||||
|
|
||||||
|
@patch("agents.runner._run_claude")
|
||||||
|
def test_agent_returns_dict_with_empty_tasks_list(self, mock_claude, conn):
|
||||||
|
"""generate_followups: agent returning {"tasks": []} → empty result, no tasks created."""
|
||||||
|
from core.followup import generate_followups
|
||||||
|
mock_claude.return_value = {"output": '{"tasks": []}', "returncode": 0}
|
||||||
|
tasks_before = conn.execute("SELECT COUNT(*) FROM tasks").fetchone()[0]
|
||||||
|
result = generate_followups(conn, "PROJ-001")
|
||||||
|
tasks_after = conn.execute("SELECT COUNT(*) FROM tasks").fetchone()[0]
|
||||||
|
assert result["created"] == []
|
||||||
|
assert tasks_after == tasks_before
|
||||||
|
|
||||||
|
@patch("agents.runner._run_claude")
|
||||||
|
def test_agent_returns_empty_string_gives_empty_result(self, mock_claude, conn):
|
||||||
|
"""generate_followups: agent returning '' (empty string) → {created: [], pending_actions: []}."""
|
||||||
|
from core.followup import generate_followups
|
||||||
|
mock_claude.return_value = {"output": "", "returncode": 0}
|
||||||
|
result = generate_followups(conn, "PROJ-001")
|
||||||
|
assert result["created"] == [], (
|
||||||
|
f"Expected created=[] for empty string output, got: {result['created']}"
|
||||||
|
)
|
||||||
|
assert result["pending_actions"] == []
|
||||||
|
|
||||||
|
@patch("agents.runner._run_claude")
|
||||||
|
def test_agent_returns_whitespace_wrapped_empty_array(self, mock_claude, conn):
|
||||||
|
"""generate_followups: agent returning ' [] ' (whitespace-wrapped) → no tasks created."""
|
||||||
|
from core.followup import generate_followups
|
||||||
|
mock_claude.return_value = {"output": " [] ", "returncode": 0}
|
||||||
|
result = generate_followups(conn, "PROJ-001")
|
||||||
|
assert result["created"] == [], (
|
||||||
|
f"Expected created=[] for whitespace-wrapped '[]', got: {result['created']}"
|
||||||
|
)
|
||||||
|
assert result["pending_actions"] == []
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue