Files
pdf_oralia_vibe/tests/test_sql_executor.py
Bertrand Benjamin d42bfecab4 chore: ajoute ruff (lint) et corrige les violations
- Config ruff dans pyproject.toml : règles E/W/F/I/UP/B, whitelist des appels
  d'injection FastAPI (Depends/File/Form/Query) pour B008, B904 ignoré
  (traduction volontaire des exceptions en réponses HTTP)
- Auto-fixes : tri des imports, suppression d'imports inutilisés, annotations
  PEP 604, f-strings sans placeholder, modes open redondants
- Suppression de variables inutilisées (config.reset_setting, parser locataires)

ruff check . : All checks passed ; 61 tests OK

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 10:20:36 +02:00

94 lines
2.7 KiB
Python

"""Tests de l'exécution SQL read-only de l'assistant IA."""
import sqlite3
import pytest
from plesna_gerance.services.sql_executor import (
_SQLITE_DENY,
_SQLITE_OK,
_SQLITE_PRAGMA,
_SQLITE_READ,
_SQLITE_SELECT,
MAX_ROWS,
_authorizer,
_ensure_limit,
_validate_sql,
execute_readonly_sql,
)
# --- Garde lexicale -------------------------------------------------------
def test_validate_rejects_non_select():
with pytest.raises(ValueError):
_validate_sql("DELETE FROM tags")
def test_validate_rejects_stacked_statements():
with pytest.raises(ValueError):
_validate_sql("SELECT 1; DROP TABLE tags")
def test_validate_rejects_unknown_pragma():
with pytest.raises(ValueError):
_validate_sql("PRAGMA writable_schema = ON")
def test_validate_allows_select_and_with():
_validate_sql("SELECT * FROM tags")
_validate_sql("WITH t AS (SELECT 1) SELECT * FROM t")
_validate_sql("PRAGMA table_info(tags)")
def test_ensure_limit_adds_limit():
assert _ensure_limit("SELECT * FROM tags").endswith(f"LIMIT {MAX_ROWS}")
def test_ensure_limit_preserves_existing():
q = "SELECT * FROM tags LIMIT 5"
assert _ensure_limit(q) == q
# --- Autorisation SQLite (unitaire) ---------------------------------------
def test_authorizer_allows_reads():
assert _authorizer(_SQLITE_SELECT, None, None, None, None) == _SQLITE_OK
assert _authorizer(_SQLITE_READ, "tags", "nom", "main", None) == _SQLITE_OK
def test_authorizer_allows_whitelisted_pragma():
assert _authorizer(_SQLITE_PRAGMA, "table_info", "tags", None, None) == _SQLITE_OK
def test_authorizer_denies_unknown_pragma():
assert _authorizer(_SQLITE_PRAGMA, "writable_schema", "ON", None, None) == _SQLITE_DENY
def test_authorizer_denies_unknown_action():
# 9 = SQLITE_DELETE, doit être refusé
assert _authorizer(9, "tags", None, "main", None) == _SQLITE_DENY
# --- Exécution réelle (nécessite une base) --------------------------------
def test_execute_select_returns_rows(db_session):
# init_db seed des tags prédéfinis
result = execute_readonly_sql("SELECT nom FROM tags ORDER BY nom")
assert "nom" in result["columns"]
assert result["row_count"] >= 1
def test_execute_write_blocked(db_session):
# Bloqué par la garde lexicale (ValueError) ou, à défaut, par mode=ro /
# l'autorisation au niveau SQLite (DatabaseError). Dans tous les cas : refusé.
with pytest.raises((ValueError, sqlite3.DatabaseError)):
execute_readonly_sql("DELETE FROM tags")
def test_execute_attach_blocked(db_session):
with pytest.raises((ValueError, sqlite3.DatabaseError)):
execute_readonly_sql("ATTACH DATABASE 'x.db' AS x")