kin: auto-commit after pipeline
This commit is contained in:
parent
61212b6605
commit
1e8253d6e8
2 changed files with 349 additions and 0 deletions
162
tests/test_kin_110_regression.py
Normal file
162
tests/test_kin_110_regression.py
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
"""Regression tests for KIN-110 — Followup button for blocked tasks.
|
||||
|
||||
Verifies:
|
||||
1. POST /api/tasks/{id}/followup endpoint exists and responds for blocked task
|
||||
2. Endpoint returns 404 for non-existent task
|
||||
3. Endpoint works for any task status (not just blocked)
|
||||
4. Response contains expected fields: created, pending_actions, needs_decision
|
||||
"""
|
||||
|
||||
import json
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
import web.api as api_module
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(tmp_path):
|
||||
db_path = tmp_path / "test.db"
|
||||
api_module.DB_PATH = db_path
|
||||
from web.api import app
|
||||
from fastapi.testclient import TestClient
|
||||
c = TestClient(app)
|
||||
c.post("/api/projects", json={"id": "p1", "name": "P1", "path": "/p1"})
|
||||
c.post("/api/tasks", json={"project_id": "p1", "title": "Fix auth"})
|
||||
return c
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def blocked_client(tmp_path):
|
||||
"""Client with a blocked task seeded."""
|
||||
db_path = tmp_path / "test.db"
|
||||
api_module.DB_PATH = db_path
|
||||
from web.api import app
|
||||
from fastapi.testclient import TestClient
|
||||
from core.db import init_db
|
||||
from core import models
|
||||
c = TestClient(app)
|
||||
c.post("/api/projects", json={"id": "p1", "name": "P1", "path": "/p1"})
|
||||
c.post("/api/tasks", json={"project_id": "p1", "title": "Blocked task"})
|
||||
# Set task to blocked status
|
||||
conn = init_db(db_path)
|
||||
models.update_task(conn, "P1-001", status="blocked")
|
||||
conn.close()
|
||||
return c
|
||||
|
||||
|
||||
class TestFollowupEndpoint:
|
||||
"""Tests for POST /api/tasks/{id}/followup endpoint."""
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_followup_blocked_task_returns_200(self, mock_claude, blocked_client):
|
||||
"""POST /followup на blocked-задаче должен вернуть 200."""
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([
|
||||
{"title": "Fix dependency", "type": "hotfix", "priority": 2,
|
||||
"brief": "Add missing dep"},
|
||||
]),
|
||||
"returncode": 0,
|
||||
}
|
||||
r = blocked_client.post("/api/tasks/P1-001/followup", json={})
|
||||
assert r.status_code == 200
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_followup_returns_created_tasks(self, mock_claude, blocked_client):
|
||||
"""Ответ /followup содержит список created с созданными задачами."""
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([
|
||||
{"title": "Fix dependency", "type": "hotfix", "priority": 2,
|
||||
"brief": "Add missing dep"},
|
||||
]),
|
||||
"returncode": 0,
|
||||
}
|
||||
r = blocked_client.post("/api/tasks/P1-001/followup", json={})
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert "created" in data
|
||||
assert len(data["created"]) == 1
|
||||
assert data["created"][0]["title"] == "Fix dependency"
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_followup_response_has_required_fields(self, mock_claude, blocked_client):
|
||||
"""Ответ /followup содержит поля created, pending_actions, needs_decision."""
|
||||
mock_claude.return_value = {"output": "[]", "returncode": 0}
|
||||
r = blocked_client.post("/api/tasks/P1-001/followup", json={})
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert "created" in data
|
||||
assert "pending_actions" in data
|
||||
assert "needs_decision" in data
|
||||
|
||||
def test_followup_nonexistent_task_returns_404(self, blocked_client):
|
||||
"""POST /followup на несуществующую задачу → 404."""
|
||||
r = blocked_client.post("/api/tasks/NOPE/followup", json={})
|
||||
assert r.status_code == 404
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_followup_dry_run_does_not_create_tasks(self, mock_claude, blocked_client):
|
||||
"""dry_run=true не создаёт задачи в БД."""
|
||||
from core.db import init_db
|
||||
from core import models
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([
|
||||
{"title": "Dry run task", "type": "hotfix", "priority": 3,
|
||||
"brief": "should not be created"},
|
||||
]),
|
||||
"returncode": 0,
|
||||
}
|
||||
r = blocked_client.post("/api/tasks/P1-001/followup", json={"dry_run": True})
|
||||
assert r.status_code == 200
|
||||
|
||||
# No tasks should be created in DB (only original P1-001 remains)
|
||||
conn = init_db(api_module.DB_PATH)
|
||||
tasks = models.list_tasks(conn, project_id="p1")
|
||||
conn.close()
|
||||
assert len(tasks) == 1 # Only P1-001, no new tasks
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_followup_creates_child_tasks_in_db(self, mock_claude, blocked_client):
|
||||
"""Созданные followup-задачи сохраняются в БД с parent_task_id."""
|
||||
from core.db import init_db
|
||||
from core import models
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([
|
||||
{"title": "New dep task", "type": "feature", "priority": 4,
|
||||
"brief": "Install dependency"},
|
||||
]),
|
||||
"returncode": 0,
|
||||
}
|
||||
r = blocked_client.post("/api/tasks/P1-001/followup", json={})
|
||||
assert r.status_code == 200
|
||||
|
||||
conn = init_db(api_module.DB_PATH)
|
||||
tasks = models.list_tasks(conn, project_id="p1")
|
||||
conn.close()
|
||||
# Original task + 1 followup
|
||||
assert len(tasks) == 2
|
||||
followup = next((t for t in tasks if t["id"] != "P1-001"), None)
|
||||
assert followup is not None
|
||||
assert followup["parent_task_id"] == "P1-001"
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_followup_pending_actions_for_permission_blocked_items(
|
||||
self, mock_claude, blocked_client
|
||||
):
|
||||
"""Элементы с permission-блокером попадают в pending_actions, не в created."""
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([
|
||||
{"title": "Normal task", "type": "hotfix", "priority": 2,
|
||||
"brief": "Just a fix"},
|
||||
{"title": "Ручное применение .env",
|
||||
"type": "hotfix", "priority": 3,
|
||||
"brief": "Не получили разрешение на запись"},
|
||||
]),
|
||||
"returncode": 0,
|
||||
}
|
||||
r = blocked_client.post("/api/tasks/P1-001/followup", json={})
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert len(data["created"]) == 1
|
||||
assert len(data["pending_actions"]) == 1
|
||||
assert data["needs_decision"] is True
|
||||
Loading…
Add table
Add a link
Reference in a new issue