Backend: - service_auto module complet: router, service, schemas, 5 teste suites (22/22 passed) - 5 endpoints: GET /ping, /firme, /tip-deviz, /masini, POST /comenzi - SP_CREEAZA_COMANDA_PROTOTIP creat în MARIUSM_AUTO (VALID, 5.9ms) - oracle_pool.py: session_callback backward-compat patch - ROA_WEB user: grants SP-only confirmate (H3), mariusm_test pool switchat - pyproject.toml: integration pytest marker înregistrat Frontend: - ComandaNoua.vue: date reale din Oracle (firme/tip-deviz/masini), nu hardcodate - src/modules/service-auto/services/api.js: axios service cu Bearer token - src/router/index.js: rută /service-auto/comanda-noua Docs: - decision-log.md: verdict MERGE, toate 6 ipoteze CONFIRMED - learnings.md: 7 patterns reutilizabile - grants-audit.md: arhitectura multi-tenant + proxy auth analysis + V_NOM_FIRME loop - template-modul-oracle.md: rețetă completă pentru module Oracle noi - TODO-phase2.md: 7 items concrete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
140 lines
4.8 KiB
Python
140 lines
4.8 KiB
Python
"""
|
|
Integration test: SP_CREEAZA_COMANDA_PROTOTIP persist + reconnect verify — Săpt 9-10
|
|
|
|
Verifies that:
|
|
1. callproc SP_CREEAZA_COMANDA_PROTOTIP writes a row to DEV_ORDL
|
|
2. After commit + disconnect, a NEW connection sees the row (true durability)
|
|
3. Cleanup removes all prototype rows from DEV_ORDL and NOM_LUCRARI
|
|
|
|
Run:
|
|
cd /workspace/roa2web
|
|
python -m pytest backend/modules/service_auto/tests/test_comanda_persist.py -v -m integration
|
|
"""
|
|
import os
|
|
import sys
|
|
import time
|
|
|
|
import pytest
|
|
import oracledb
|
|
|
|
# Add backend to path so secrets/ is accessible
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
|
|
|
|
HOST = "10.0.20.121"
|
|
PORT = 1521
|
|
SERVICE_NAME = "ROA"
|
|
USER = "CONTAFIN_ORACLE"
|
|
SECRETS_FILE = os.path.join(
|
|
os.path.dirname(__file__), '..', '..', '..', 'secrets', 'central.oracle_pass'
|
|
)
|
|
|
|
|
|
def _read_password() -> str:
|
|
with open(SECRETS_FILE) as f:
|
|
return f.read().strip()
|
|
|
|
|
|
def _connect() -> oracledb.Connection:
|
|
return oracledb.connect(
|
|
user=USER,
|
|
password=_read_password(),
|
|
host=HOST,
|
|
port=PORT,
|
|
service_name=SERVICE_NAME,
|
|
)
|
|
|
|
|
|
@pytest.mark.integration
|
|
def test_comanda_persist_and_reconnect():
|
|
"""
|
|
Full round-trip: callproc → commit → close → NEW connection → SELECT → assert exists.
|
|
Cleans up all inserted rows after verification.
|
|
"""
|
|
# --- Phase 1: Call SP and commit ---
|
|
conn1 = _connect()
|
|
id_ordl = None
|
|
id_lucrare = None
|
|
try:
|
|
with conn1.cursor() as cursor:
|
|
out_id_ordl = cursor.var(oracledb.NUMBER)
|
|
out_nrord = cursor.var(oracledb.STRING)
|
|
|
|
t0 = time.perf_counter()
|
|
cursor.callproc(
|
|
"MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP",
|
|
[
|
|
3, # p_tip IN NUMBER
|
|
200, # p_id_masiniclient IN NUMBER
|
|
'Test pytest persist', # p_solicitari IN VARCHAR2
|
|
110, # p_id_firma IN NUMBER
|
|
out_id_ordl, # p_id_ordl OUT NUMBER
|
|
out_nrord, # p_nrord OUT VARCHAR2
|
|
],
|
|
)
|
|
t_callproc = time.perf_counter() - t0
|
|
|
|
id_ordl = int(out_id_ordl.getvalue())
|
|
nrord = out_nrord.getvalue() or ""
|
|
print(f"\n[PERSIST] callproc OK in {t_callproc*1000:.1f}ms → id_ordl={id_ordl} nrord={nrord}")
|
|
|
|
assert id_ordl > 0, f"Expected positive id_ordl, got {id_ordl}"
|
|
assert nrord.startswith("P"), f"Expected nrord starting with 'P', got {nrord!r}"
|
|
|
|
conn1.commit()
|
|
print(f"[PERSIST] commit OK on connection 1")
|
|
finally:
|
|
conn1.close()
|
|
print("[PERSIST] connection 1 closed")
|
|
|
|
# --- Phase 2: Open NEW connection and verify row exists ---
|
|
assert id_ordl is not None, "id_ordl must be set before reconnect phase"
|
|
|
|
conn2 = _connect()
|
|
try:
|
|
with conn2.cursor() as cursor:
|
|
cursor.execute(
|
|
"SELECT id_ordl, id_lucrare FROM MARIUSM_AUTO.DEV_ORDL WHERE id_ordl = :id",
|
|
{"id": id_ordl},
|
|
)
|
|
row = cursor.fetchone()
|
|
|
|
print(f"[PERSIST] NEW connection SELECT → row={row}")
|
|
assert row is not None, f"Row id_ordl={id_ordl} not found after reconnect — durability FAILED"
|
|
assert int(row[0]) == id_ordl, f"id_ordl mismatch: got {row[0]}, expected {id_ordl}"
|
|
id_lucrare = int(row[1])
|
|
print(f"[PERSIST] ASSERT PASSED: row exists in new connection ✅ (id_lucrare={id_lucrare})")
|
|
finally:
|
|
conn2.close()
|
|
print("[PERSIST] connection 2 closed")
|
|
|
|
# --- Phase 3: Cleanup — delete child then parent ---
|
|
assert id_lucrare is not None, "id_lucrare must be set for cleanup"
|
|
|
|
conn3 = _connect()
|
|
try:
|
|
with conn3.cursor() as cursor:
|
|
# Child first (FK constraint: DEV_ORDL → NOM_LUCRARI)
|
|
cursor.execute(
|
|
"DELETE FROM MARIUSM_AUTO.DEV_ORDL WHERE id_ordl = :id",
|
|
{"id": id_ordl},
|
|
)
|
|
deleted_ordl = cursor.rowcount
|
|
print(f"[PERSIST] DELETE DEV_ORDL id_ordl={id_ordl} → {deleted_ordl} row(s)")
|
|
|
|
# Parent second
|
|
cursor.execute(
|
|
"DELETE FROM MARIUSM_AUTO.NOM_LUCRARI WHERE id_lucrare = :id",
|
|
{"id": id_lucrare},
|
|
)
|
|
deleted_nom = cursor.rowcount
|
|
print(f"[PERSIST] DELETE NOM_LUCRARI id_lucrare={id_lucrare} → {deleted_nom} row(s)")
|
|
|
|
conn3.commit()
|
|
print("[PERSIST] cleanup commit OK ✅")
|
|
|
|
assert deleted_ordl == 1, f"Expected 1 DEV_ORDL row deleted, got {deleted_ordl}"
|
|
assert deleted_nom == 1, f"Expected 1 NOM_LUCRARI row deleted, got {deleted_nom}"
|
|
finally:
|
|
conn3.close()
|
|
print("[PERSIST] connection 3 (cleanup) closed")
|