From 2b3caf844f61179b34444847199081e202c7beff Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Tue, 17 Mar 2026 16:38:37 +0200 Subject: [PATCH] =?UTF-8?q?kin:=20KIN-FIX-011=20=D0=9F=D1=80=D0=B8=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=82=D1=8C=20glob-=D0=B4=D0=B5=D1=82=D0=B5?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D1=8E=20SSH=5FAUTH=5FSOCK=20=D0=B2=20web/api?= =?UTF-8?q?.py=20(=5Flaunch=5Fpipeline=5Fsubprocess)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_db.py | 109 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/tests/test_db.py b/tests/test_db.py index 6514bbf..07ff8bd 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -283,3 +283,112 @@ def test_migrate_acceptance_criteria_is_nullable_after_migration(): row = conn.execute("SELECT acceptance_criteria FROM tasks WHERE id='tm'").fetchone() assert row["acceptance_criteria"] is None conn.close() + + +# --------------------------------------------------------------------------- +# Regression KIN-FIX-014: guard "if pipelines in existing_tables" +# Финальный UPDATE pipelines в _migrate не должен падать на схемах без этой таблицы. +# --------------------------------------------------------------------------- + +def _old_schema_with_pipelines_conn() -> sqlite3.Connection: + """Минимальная схема С таблицей pipelines (dept_sub + running записи).""" + conn = sqlite3.connect(":memory:") + conn.row_factory = sqlite3.Row + conn.executescript(""" + CREATE TABLE projects ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + path TEXT, + status TEXT DEFAULT 'active', + language TEXT DEFAULT 'ru', + execution_mode TEXT NOT NULL DEFAULT 'review' + ); + CREATE TABLE tasks ( + id TEXT PRIMARY KEY, + project_id TEXT NOT NULL, + title TEXT NOT NULL, + status TEXT DEFAULT 'pending', + execution_mode TEXT + ); + CREATE TABLE pipelines ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id TEXT NOT NULL, + project_id TEXT NOT NULL, + route_type TEXT NOT NULL, + steps TEXT NOT NULL DEFAULT '[]', + status TEXT DEFAULT 'running' + ); + """) + conn.commit() + return conn + + +def test_migrate_without_pipelines_table_does_not_raise(): + """Regression KIN-FIX-014: _migrate на схеме без таблицы pipelines не бросает исключение. + + До фикса: безусловный UPDATE pipelines падал с 'no such table: pipelines' + на тест-фикстурах с минимальной схемой (только projects + tasks). + """ + conn = _old_schema_conn() + try: + _migrate(conn) + except Exception as exc: + pytest.fail( + f"_migrate raised {type(exc).__name__} при отсутствии таблицы pipelines: {exc}" + ) + conn.close() + + +def test_migrate_sets_orphaned_dept_sub_running_to_failed(): + """_migrate обновляет dept_sub pipelines со статусом 'running' → 'failed'. + + Исправляет призрачные записи от двойного создания пайплайна (KIN-ARCH-012). + """ + conn = _old_schema_with_pipelines_conn() + conn.execute( + "INSERT INTO projects (id, name) VALUES ('p1', 'P')" + ) + conn.execute( + "INSERT INTO tasks (id, project_id, title) VALUES ('t1', 'p1', 'T')" + ) + conn.execute( + "INSERT INTO pipelines (task_id, project_id, route_type, status)" + " VALUES ('t1', 'p1', 'dept_sub', 'running')" + ) + conn.commit() + + _migrate(conn) + + row = conn.execute("SELECT status FROM pipelines WHERE route_type = 'dept_sub'").fetchone() + assert row["status"] == "failed", ( + "dept_sub pipeline со статусом 'running' должен стать 'failed' после миграции" + ) + conn.close() + + +def test_migrate_preserves_non_dept_sub_and_non_running_pipelines(): + """_migrate не трогает pipelines, которые не являются dept_sub+running.""" + conn = _old_schema_with_pipelines_conn() + conn.execute("INSERT INTO projects (id, name) VALUES ('p2', 'P')") + conn.execute("INSERT INTO tasks (id, project_id, title) VALUES ('t2', 'p2', 'T')") + # dept_sub но уже completed — не трогать + conn.execute( + "INSERT INTO pipelines (task_id, project_id, route_type, status)" + " VALUES ('t2', 'p2', 'dept_sub', 'completed')" + ) + # обычный running pipeline другого типа — не трогать + conn.execute( + "INSERT INTO pipelines (task_id, project_id, route_type, status)" + " VALUES ('t2', 'p2', 'direct', 'running')" + ) + conn.commit() + + _migrate(conn) + + rows = { + r["route_type"]: r["status"] + for r in conn.execute("SELECT route_type, status FROM pipelines").fetchall() + } + assert rows["dept_sub"] == "completed", "completed pipeline не должен меняться" + assert rows["direct"] == "running", "non-dept_sub running pipeline не должен меняться" + conn.close()