kin: KIN-OBS-027 Исправить catch(e: any) и setTimeout cleanup в LiveConsole.vue

This commit is contained in:
Gros Frumos 2026-03-17 18:33:42 +02:00
parent 79757a3120
commit b58da600d4
2 changed files with 111 additions and 2 deletions

View file

@ -740,3 +740,112 @@ class TestProjectLinksIndexMigration:
assert "idx_project_links_from" in after
assert before == after
conn.close()
# ---------------------------------------------------------------------------
# 12. Migration: UNIQUE(from_project, to_project, type) (KIN-INFRA-013)
# Convention #433: set-assert unique constraint after fresh init
# Convention #434: negative test — ALTER TABLE cannot add UNIQUE in SQLite
# ---------------------------------------------------------------------------
class TestProjectLinksUniqueMigration:
"""KIN-INFRA-013: UNIQUE(from_project, to_project, type) на project_links."""
# --- fresh schema ---
def test_fresh_schema_project_links_has_unique_constraint(self):
"""Свежая схема должна иметь UNIQUE-ограничение на (from_project, to_project, type)."""
conn = init_db(db_path=":memory:")
unique_indexes = [
r for r in conn.execute("PRAGMA index_list(project_links)").fetchall()
if r[2] == 1 # unique == 1
]
assert len(unique_indexes) >= 1
conn.close()
# --- модельный уровень ---
def test_create_duplicate_link_raises_integrity_error(self, conn):
"""Дублирующая вставка должна вызывать IntegrityError."""
import sqlite3 as _sqlite3
models.create_project(conn, "dup_a", "A", "/a")
models.create_project(conn, "dup_b", "B", "/b")
models.create_project_link(conn, "dup_a", "dup_b", "depends_on")
with pytest.raises(_sqlite3.IntegrityError):
models.create_project_link(conn, "dup_a", "dup_b", "depends_on")
# --- migration guard: 3 кейса (Convention #384) ---
# Кейс 1: без таблицы — guard не падает
def test_migrate_without_project_links_table_no_error_unique(self):
conn = _old_schema_no_deploy() # project_links отсутствует
_migrate(conn) # не должно упасть
conn.close()
# Кейс 2: таблица без UNIQUE → _migrate() добавляет ограничение
def test_migrate_adds_unique_constraint_to_old_schema(self):
conn = _schema_with_project_links_no_indexes() # без UNIQUE
_migrate(conn)
pl_sql = conn.execute(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='project_links'"
).fetchone()
assert "UNIQUE" in (pl_sql[0] or "").upper()
conn.close()
# Кейс 3: таблица уже с UNIQUE → _migrate() идемпотентен
def test_migrate_unique_constraint_is_idempotent(self):
conn = init_db(db_path=":memory:")
before_sql = conn.execute(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='project_links'"
).fetchone()[0]
_migrate(conn)
after_sql = conn.execute(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='project_links'"
).fetchone()[0]
assert before_sql == after_sql
conn.close()
# Convention #434: документируем, почему ALTER TABLE нельзя использовать
def test_alter_table_cannot_add_unique_constraint(self):
"""SQLite не поддерживает ALTER TABLE ADD CONSTRAINT.
Именно поэтому _migrate() пересоздаёт таблицу вместо ALTER TABLE.
"""
import sqlite3 as _sqlite3
_conn = _sqlite3.connect(":memory:")
_conn.execute("CREATE TABLE t (a TEXT, b TEXT)")
with pytest.raises(_sqlite3.OperationalError):
_conn.execute("ALTER TABLE t ADD CONSTRAINT uq UNIQUE (a, b)")
_conn.close()
# ---------------------------------------------------------------------------
# 13. API: POST /api/project-links возвращает 409 при дублировании
# ---------------------------------------------------------------------------
class TestProjectLinksDuplicateAPI:
def _create_projects(self, client):
client.post("/api/projects", json={"id": "dup_p2", "name": "P2", "path": "/p2"})
def test_create_duplicate_link_returns_409(self, client):
self._create_projects(client)
client.post("/api/project-links", json={
"from_project": "p1", "to_project": "dup_p2", "type": "depends_on"
})
r = client.post("/api/project-links", json={
"from_project": "p1", "to_project": "dup_p2", "type": "depends_on"
})
assert r.status_code == 409
assert "already exists" in r.json()["detail"].lower()
def test_same_projects_different_type_not_duplicate(self, client):
"""Одна пара проектов с разными type — не дубликат."""
self._create_projects(client)
r1 = client.post("/api/project-links", json={
"from_project": "p1", "to_project": "dup_p2", "type": "depends_on"
})
r2 = client.post("/api/project-links", json={
"from_project": "p1", "to_project": "dup_p2", "type": "references"
})
assert r1.status_code == 201
assert r2.status_code == 201