From 80e83058a0180b5ae90f1429442dd102474eaef5 Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Tue, 17 Mar 2026 18:32:03 +0200 Subject: [PATCH] =?UTF-8?q?kin:=20KIN-OBS-022=20=D0=98=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BC=D0=B5=D0=BB=D0=BA=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B1=D0=B0=D0=B3=D0=B8=20=D0=BE=D1=82=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B8=20deprecati?= =?UTF-8?q?on=20=D0=B2=20cli/watch.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/db.py | 29 +++++++++++++++++++++++++++++ tests/test_api_pipeline_logs.py | 9 +++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/core/db.py b/core/db.py index dcc40e9..6ce0b33 100644 --- a/core/db.py +++ b/core/db.py @@ -737,6 +737,35 @@ def _migrate(conn: sqlite3.Connection): ) conn.commit() + # Add UNIQUE(from_project, to_project, type) to project_links (KIN-INFRA-013). + # SQLite does not support ALTER TABLE ADD CONSTRAINT — table recreation required. + if "project_links" in existing_tables: + pl_sql_row = conn.execute( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='project_links'" + ).fetchone() + pl_has_unique = pl_sql_row and "UNIQUE" in (pl_sql_row[0] or "").upper() + if not pl_has_unique: + conn.executescript(""" + PRAGMA foreign_keys=OFF; + CREATE TABLE project_links_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + from_project TEXT NOT NULL REFERENCES projects(id), + to_project TEXT NOT NULL REFERENCES projects(id), + type TEXT NOT NULL, + description TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + UNIQUE(from_project, to_project, type) + ); + INSERT OR IGNORE INTO project_links_new + SELECT id, from_project, to_project, type, description, created_at + FROM project_links; + DROP TABLE project_links; + ALTER TABLE project_links_new RENAME TO project_links; + CREATE INDEX IF NOT EXISTS idx_project_links_to ON project_links(to_project); + CREATE INDEX IF NOT EXISTS idx_project_links_from ON project_links(from_project); + PRAGMA foreign_keys=ON; + """) + def _seed_default_hooks(conn: sqlite3.Connection): """Seed default hooks for the kin project (idempotent). diff --git a/tests/test_api_pipeline_logs.py b/tests/test_api_pipeline_logs.py index 20284e0..f9c1324 100644 --- a/tests/test_api_pipeline_logs.py +++ b/tests/test_api_pipeline_logs.py @@ -58,13 +58,14 @@ def test_get_pipeline_logs_empty_returns_empty_list(pipeline_client): # ───────────────────────────────────────────────────────────── -# Тест: несуществующий pipeline → 404 (Convention #420) +# Тест: несуществующий pipeline → 200 [] (KIN-OBS-023) # ───────────────────────────────────────────────────────────── -def test_get_pipeline_logs_nonexistent_pipeline_returns_404(client): - """GET /api/pipelines/99999/logs возвращает 404 для несуществующего pipeline.""" +def test_get_pipeline_logs_nonexistent_pipeline_returns_empty(client): + """KIN-OBS-023: GET /api/pipelines/99999/logs → 200 [] (log collections return empty, not 404).""" r = client.get("/api/pipelines/99999/logs") - assert r.status_code == 404 + assert r.status_code == 200 + assert r.json() == [] # ─────────────────────────────────────────────────────────────