kin/tests/test_no_connection_artifacts.py
Gros Frumos 01b269e2b8 feat(KIN-010): implement rebuild-frontend post-pipeline hook
- scripts/rebuild-frontend.sh: builds Vue 3 frontend and restarts uvicorn API
- cli/main.py: hook group with add/list/remove/logs/setup commands;
  `hook setup` idempotently registers rebuild-frontend for a project
- agents/runner.py: call run_hooks(event="pipeline_completed") after
  successful pipeline; wrap in try/except so hook errors never block results
- tests: 3 tests for hook_setup CLI + 3 tests for pipeline→hooks integration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 19:17:42 +02:00

112 lines
5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Regression test — KIN-009.
Проверяет, что в рабочей директории проекта НЕ создаются файлы с именами,
содержащими 'sqlite3.Connection'. Такие артефакты появляются, если путь к БД
формируется передачей объекта sqlite3.Connection вместо строки/Path в
sqlite3.connect().
"""
import os
import sqlite3
from pathlib import Path
import pytest
# Корень проекта — три уровня вверх от этого файла (tests/ → kin/)
PROJECT_ROOT = Path(__file__).parent.parent
def _find_connection_artifacts(search_dir: Path) -> list[Path]:
"""Рекурсивно ищет файлы, чьё имя содержит 'sqlite3.Connection'."""
found = []
try:
for entry in search_dir.rglob("*"):
if entry.is_file() and "sqlite3.Connection" in entry.name:
found.append(entry)
except PermissionError:
pass
return found
# ---------------------------------------------------------------------------
# Тест 1: статическая проверка — артефактов нет прямо сейчас
# ---------------------------------------------------------------------------
def test_no_connection_artifact_files_in_project():
"""В рабочей директории проекта не должно быть файлов с 'sqlite3.Connection' в имени."""
artifacts = _find_connection_artifacts(PROJECT_ROOT)
assert artifacts == [], (
f"Найдены файлы-артефакты sqlite3.Connection:\n"
+ "\n".join(f" {p}" for p in artifacts)
)
def test_no_connection_artifact_files_in_kin_home():
"""В ~/.kin/ тоже не должно быть таких файлов."""
kin_home = Path.home() / ".kin"
if not kin_home.exists():
pytest.skip("~/.kin не существует")
artifacts = _find_connection_artifacts(kin_home)
assert artifacts == [], (
f"Найдены файлы-артефакты sqlite3.Connection в ~/.kin:\n"
+ "\n".join(f" {p}" for p in artifacts)
)
# ---------------------------------------------------------------------------
# Тест 2: динамическая проверка — init_db не создаёт артефактов в tmp_path
# ---------------------------------------------------------------------------
def test_init_db_does_not_create_connection_artifact(tmp_path):
"""init_db() должен создавать файл с нормальным именем, а не 'sqlite3.Connection ...'."""
from core.db import init_db
db_file = tmp_path / "test.db"
conn = init_db(db_file)
conn.close()
artifacts = _find_connection_artifacts(tmp_path)
assert artifacts == [], (
"init_db() создал файл с именем, содержащим 'sqlite3.Connection':\n"
+ "\n".join(f" {p}" for p in artifacts)
)
# Убедимся, что файл БД реально создан с правильным именем
assert db_file.exists(), "Файл БД должен существовать"
# ---------------------------------------------------------------------------
# Тест 3: воспроизводит сценарий утечки — connect(conn) вместо connect(path)
# ---------------------------------------------------------------------------
def test_init_db_passes_string_to_sqlite_connect(tmp_path, monkeypatch):
"""core/db.init_db() должен вызывать sqlite3.connect() со строкой пути, а НЕ с объектом Connection.
Баг-сценарий: если где-то в коде путь к БД перепутан с объектом conn,
sqlite3.connect(str(conn)) создаст файл с именем '<sqlite3.Connection object at 0x...>'.
Этот тест перехватывает вызов и проверяет тип аргумента напрямую.
"""
import core.db as db_module
connect_calls: list = []
real_connect = sqlite3.connect
def mock_connect(database, *args, **kwargs):
connect_calls.append(database)
return real_connect(database, *args, **kwargs)
monkeypatch.setattr(db_module.sqlite3, "connect", mock_connect)
db_file = tmp_path / "test.db"
conn = db_module.init_db(db_file)
conn.close()
assert connect_calls, "sqlite3.connect() должен быть вызван хотя бы один раз"
for call_arg in connect_calls:
assert isinstance(call_arg, str), (
f"sqlite3.connect() получил не строку: {type(call_arg).__name__!r} = {call_arg!r}"
)
assert "sqlite3.Connection" not in call_arg, (
f"sqlite3.connect() получил строку объекта Connection: {call_arg!r}\n"
"Баг: str(conn) передаётся вместо пути к файлу — это создаёт файл-артефакт!"
)