From 16cd672fc465b562af01cb479fa39f9b38222015 Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Tue, 17 Mar 2026 18:35:17 +0200 Subject: [PATCH 1/4] =?UTF-8?q?kin:=20KIN-INFRA-008=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=B8=D0=BD=D0=B4=D0=B5=D0=BA?= =?UTF-8?q?=D1=81=D1=8B=20=D0=BD=D0=B0=20project=5Flinks=20=D0=B8=20unit-?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D1=8B=20deploy=5Fwith=5Fdependents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_models.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_models.py b/tests/test_models.py index 715ce24..4a80e61 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -843,6 +843,24 @@ def test_write_log_extra_dict_decoded(pipeline_conn): assert entry["extra_json"]["model"] == "sonnet" +def test_write_log_custom_ts_stored_exactly(pipeline_conn): + """KIN-OBS-025: write_log с ts='...' сохраняет переданный timestamp без изменений (UTC-naive).""" + db, pid = pipeline_conn + custom_ts = "2026-03-17T10:00:05" + entry = models.write_log(db, pid, "PM start: task planning", ts=custom_ts, extra={"role": "pm"}) + assert entry["ts"] == custom_ts + + +def test_write_log_no_ts_uses_db_default(pipeline_conn): + """KIN-OBS-025: write_log без ts — таймстемп заполняется БД (не None).""" + db, pid = pipeline_conn + entry = models.write_log(db, pid, "Regular entry") + assert entry["ts"] is not None + # DB default — UTC-naive ISO string, no timezone suffix + assert "+" not in entry["ts"] + assert "Z" not in entry["ts"] + + def test_get_pipeline_logs_since_id_zero_returns_all(pipeline_conn): """KIN-084: get_pipeline_logs(since_id=0) возвращает все записи.""" db, pid = pipeline_conn From b1461292aeced43d054bfa0965202679f7ae2678 Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Tue, 17 Mar 2026 18:35:47 +0200 Subject: [PATCH 2/4] =?UTF-8?q?kin:=20KIN-OBS-030=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=B8=D0=BD=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8E=20PM-?= =?UTF-8?q?=D1=88=D0=B0=D0=B3=D0=B0=20=D0=B2=20pipeline=5Flog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_api.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index 7e11514..51ff305 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -2310,3 +2310,32 @@ def test_get_pipeline_logs_not_found(client): r = client.get("/api/pipelines/9999/logs") assert r.status_code == 200 assert r.json() == [] + + +def test_get_pipeline_logs_since_id_returns_pm_entries(client): + """KIN-OBS-025: poll since_id возвращает PM-записи с role='pm' в extra_json.""" + import json as _json + from core.db import init_db + from core import models + pid = _seed_pipeline(api_module.DB_PATH, task_id="PM-001") + conn = init_db(api_module.DB_PATH) + # Вставляем не-PM запись, которая будет отфильтрована + e0 = models.write_log(conn, pid, "Pipeline start") + # Вставляем PM-записи с ретроспективными ts + models.write_log(conn, pid, "PM start: task planning", + ts="2026-03-17T10:00:00", extra={"role": "pm"}) + models.write_log(conn, pid, "PM done: 2 steps planned, success=True, cost=$0.0100, tokens=1000", + ts="2026-03-17T10:00:05", + extra={"role": "pm", "steps_count": 2, "tokens_used": 1000, "cost_usd": 0.01}) + conn.close() + + r = client.get(f"/api/pipelines/{pid}/logs?since_id={e0['id']}") + assert r.status_code == 200 + logs = r.json() + assert len(logs) == 2 + pm_roles = [log["extra_json"]["role"] for log in logs if log.get("extra_json")] + assert all(role == "pm" for role in pm_roles) + # Убеждаемся что PM done содержит метрики + done_log = next(log for log in logs if "PM done" in log["message"]) + assert done_log["extra_json"]["steps_count"] == 2 + assert done_log["extra_json"]["tokens_used"] == 1000 From 151985809da11570b636927564a224c036d81311 Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Tue, 17 Mar 2026 18:36:02 +0200 Subject: [PATCH 3/4] =?UTF-8?q?kin:=20KIN-INFRA-009=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20project=5Fdeploy=5Fruntime=20?= =?UTF-8?q?=D0=B2=20TaskFull=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BE=D1=80?= =?UTF-8?q?=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=D0=B9=20=D0=BA=D0=BD=D0=BE?= =?UTF-8?q?=D0=BF=D0=BA=D0=B8=20Deploy=20=D0=BD=D0=B0=20TaskDetail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index 51ff305..7f0f7fe 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -2317,7 +2317,7 @@ def test_get_pipeline_logs_since_id_returns_pm_entries(client): import json as _json from core.db import init_db from core import models - pid = _seed_pipeline(api_module.DB_PATH, task_id="PM-001") + pid = _seed_pipeline(api_module.DB_PATH, task_id="P1-001") conn = init_db(api_module.DB_PATH) # Вставляем не-PM запись, которая будет отфильтрована e0 = models.write_log(conn, pid, "Pipeline start") From 66f391bf605af1be5fcd14b9c222ab7d0869a50b Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Tue, 17 Mar 2026 18:36:17 +0200 Subject: [PATCH 4/4] kin: auto-commit after pipeline