Files
roa2web-service-auto/backend/modules/service_auto/tests/test_partener_create.py
Claude Agent fd64cf3f1e test(service-auto): unit tests multi-tenant + lookup + partener + pc_nr
Acoperire 49 tests offline (fără Oracle real):

test_comanda_helpers (16): _build_pc_nr toate prefixele VFP + fallback,
_build_sir_id_operatii csv + limit 4000 chars, _PREFIX_MAP regression.

test_router_authorization (9): _company_id fallback JWT companies[0],
403 firmă neautorizată, 400 companies[] gol, string→int coercion;
_server_id extragere din request.state.

test_lookup_endpoints (15): cache hit/miss per schema pentru tip_deviz,
masini, asiguratori, inspectori (per-asig), operatii; LIKE escape %/_/\;
min 2 chars short-circuit; server_id propagat la get_connection.

test_partener_create (9): 5 Pydantic validation (denumire min 2,
id_firma ge 1, cui opțional), 4 service mocked (happy path, 409
duplicat CUI, fără CUI, lipsă GRANT → 500 log.critical).

Pattern mock Oracle: fake context managers (async get_connection +
sync cursor), monkeypatch pe lookup_service.get_schema (not _context,
din cauza binding copy la import).

Rulare: pytest backend/modules/service_auto/tests/ -q → 62 passed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-05 09:37:10 +00:00

198 lines
6.7 KiB
Python

"""
Unit tests pentru creare partener nou:
- Validare PartnerCreateRequest (denumire min_length=2, id_firma ge=1)
- LookupService.create_partener — happy path + duplicat CUI (409) + lipsă GRANT (500)
Folosește mock pentru oracle_pool și _context.get_schema (fără DB).
"""
from unittest.mock import AsyncMock, MagicMock, patch
import oracledb
import pytest
from fastapi import HTTPException
from pydantic import ValidationError
from backend.modules.service_auto.schemas.comanda import PartnerCreateRequest
from backend.modules.service_auto.services.lookup_service import LookupService
# ---- PartnerCreateRequest validation ----
def test_partner_request_denumire_too_short_raises():
"""denumire cu 1 caracter → ValidationError (min_length=2)."""
with pytest.raises(ValidationError) as exc:
PartnerCreateRequest(denumire="X", id_firma=167)
assert "denumire" in str(exc.value).lower()
def test_partner_request_denumire_empty_raises():
"""denumire goală → ValidationError."""
with pytest.raises(ValidationError):
PartnerCreateRequest(denumire="", id_firma=167)
def test_partner_request_minimal_valid():
"""Doar denumire + id_firma → CUI și adresa optionale = None."""
req = PartnerCreateRequest(denumire="ACME SRL", id_firma=167)
assert req.denumire == "ACME SRL"
assert req.cui is None
assert req.adresa is None
assert req.id_firma == 167
def test_partner_request_full():
req = PartnerCreateRequest(
denumire="ACME SRL",
cui="RO12345678",
adresa="Str. Exemplu nr. 1, București",
id_firma=167,
)
assert req.cui == "RO12345678"
assert req.adresa is not None and req.adresa.startswith("Str.")
def test_partner_request_id_firma_zero_raises():
"""id_firma=0 → ValidationError (ge=1)."""
with pytest.raises(ValidationError):
PartnerCreateRequest(denumire="ACME", id_firma=0)
# ---- LookupService.create_partener (mocked) ----
def _make_pool_ctx(cursor_mock):
"""
Construiește un context manager async pentru oracle_pool.get_connection.
Returnează: pool_mock cu .get_connection() → async ctx → conn cu .cursor()
sync ctx care returnează cursor_mock.
"""
conn_mock = MagicMock()
conn_mock.cursor.return_value.__enter__.return_value = cursor_mock
conn_mock.cursor.return_value.__exit__.return_value = None
conn_mock.commit = MagicMock()
async_ctx = MagicMock()
async_ctx.__aenter__ = AsyncMock(return_value=conn_mock)
async_ctx.__aexit__ = AsyncMock(return_value=None)
pool_mock = MagicMock()
pool_mock.get_connection = MagicMock(return_value=async_ctx)
return pool_mock, conn_mock
@pytest.mark.asyncio
async def test_create_partener_happy_path():
"""
Cazul nominal:
- Pre-check CUI: nicio coliziune (fetchone() → None)
- SELECT MAX(id_part)+1 → 4242
- INSERT reușește; conn.commit() apelat; întoarce PartenerItem.
"""
cursor = MagicMock()
# fetchone secvență: pre-check CUI (None), SELECT MAX (4242,)
cursor.fetchone.side_effect = [None, (4242,)]
cursor.execute = MagicMock()
pool_mock, conn_mock = _make_pool_ctx(cursor)
with patch(
"backend.modules.service_auto.services.lookup_service.oracle_pool",
pool_mock,
), patch(
"backend.modules.service_auto.services.lookup_service.get_schema",
new=AsyncMock(return_value="MARIUSM_AUTO"),
):
req = PartnerCreateRequest(
denumire="ACME SRL", cui="RO12345678", adresa="Str. X", id_firma=167,
)
result = await LookupService.create_partener(req, server_id="mariusm_test")
assert result.id_part == 4242
assert result.denumire == "ACME SRL"
conn_mock.commit.assert_called_once()
@pytest.mark.asyncio
async def test_create_partener_duplicate_cui_raises_409():
"""Pre-check CUI găsește rând existent → HTTPException 409, NU INSERT."""
cursor = MagicMock()
cursor.fetchone.return_value = (1,) # CUI deja există
cursor.execute = MagicMock()
pool_mock, conn_mock = _make_pool_ctx(cursor)
with patch(
"backend.modules.service_auto.services.lookup_service.oracle_pool",
pool_mock,
), patch(
"backend.modules.service_auto.services.lookup_service.get_schema",
new=AsyncMock(return_value="MARIUSM_AUTO"),
):
req = PartnerCreateRequest(
denumire="ACME SRL", cui="RO12345678", id_firma=167,
)
with pytest.raises(HTTPException) as exc:
await LookupService.create_partener(req, server_id="mariusm_test")
assert exc.value.status_code == 409
assert "CUI" in exc.value.detail
conn_mock.commit.assert_not_called()
@pytest.mark.asyncio
async def test_create_partener_no_cui_skips_precheck():
"""Fără CUI → pre-check sărit, doar SELECT MAX + INSERT."""
cursor = MagicMock()
cursor.fetchone.side_effect = [(99,)] # doar SELECT MAX
cursor.execute = MagicMock()
pool_mock, conn_mock = _make_pool_ctx(cursor)
with patch(
"backend.modules.service_auto.services.lookup_service.oracle_pool",
pool_mock,
), patch(
"backend.modules.service_auto.services.lookup_service.get_schema",
new=AsyncMock(return_value="MARIUSM_AUTO"),
):
req = PartnerCreateRequest(denumire="Persoană fizică", id_firma=167)
result = await LookupService.create_partener(req, server_id=None)
assert result.id_part == 99
conn_mock.commit.assert_called_once()
@pytest.mark.asyncio
async def test_create_partener_missing_grant_raises_500():
"""ORA-01031 (lipsă INSERT privilege) → HTTPException 500 cu mesaj clar."""
cursor = MagicMock()
# CUI furnizat → fetchone secvență: pre-check (None=fără duplicat), SELECT MAX (1,)
cursor.fetchone.side_effect = [None, (1,)]
# INSERT primește ORA-01031
err = oracledb.DatabaseError()
err.args = (MagicMock(code=1031, message="ORA-01031: insufficient privileges"),)
def execute_side_effect(sql, *args, **kw):
del args, kw
if "INSERT" in sql.upper():
raise err
cursor.execute.side_effect = execute_side_effect
pool_mock, conn_mock = _make_pool_ctx(cursor)
with patch(
"backend.modules.service_auto.services.lookup_service.oracle_pool",
pool_mock,
), patch(
"backend.modules.service_auto.services.lookup_service.get_schema",
new=AsyncMock(return_value="MARIUSM_AUTO"),
):
req = PartnerCreateRequest(
denumire="ACME SRL", cui="RO99999999", id_firma=167,
)
with pytest.raises(HTTPException) as exc:
await LookupService.create_partener(req, server_id="mariusm_test")
assert exc.value.status_code == 500
assert "privilegii" in exc.value.detail.lower()
conn_mock.commit.assert_not_called()