# Test headless — `citeste_curs_bnr` (arhiva 10 zile + fisier anual BNR) Tinta: `COMUN\clase\onom_curs.vcx`, clasa `actualizare_curs_bnr`, metoda `citeste_curs_bnr` (patch v3, deja scris in binar — confirmat inainte de test, vezi sectiunea 0). Metoda a fost apelata **DIRECT**, niciodata prin `initializeaza` — niciun apel la `initializeaza()` sau `scrie_curs_bnr()` in tot scriptul, deci **nicio scriere in Oracle**. Conexiunea Oracle deschisa de mediul de test (`test_init_env_auto_roafacturare.prg`) face doar `SELECT`-uri (firma, calendar, `pack_sesiune.set*`) pentru initializarea sesiunii — nimic legat de cursuri BNR. ## 0. Confirmare inainte de test `COMUN\clase\onom_curs.vc2` are deja proprietatile `clinkan`/`clinkarhiva` si noua `citeste_curs_bnr` cu parametrul `tnSursa` (linia 106+), identice cu blocul `NOU` din `docs\patch_curs_bnr_arhiva.md` — verificat cu grep inainte de a scrie testul. `.vcx`/`.VCT` au acelasi mtime cu `.vc2` (22:11, 17.09.2026) — write-back-ul din `docs\writeback_curs_bnr.md` confirmat aplicat, nu s-a atins din nou. ## 1. Artefact de harness gasit si ocolit (NU e defect de cod) Apeland `citeste_curs_bnr` direct (fara `initializeaza`), primul apel a agatat testul (90s timeout, 0 dialoguri detectate de `watchdog_vfp.ps1`). Cauza, confirmata prin proba minimala (`test_curs_bnr_probe2.prg`, sters dupa confirmare): - `citeste_curs_bnr` incepe cu `creeaza_backup_cursoare(This.cCursor)` (`onom_curs.vc2:118`), care cheama `copiaza_structura_cursor()` din `COMUN\programe\oproceduri_comune.prg:3024`. - Acolo, `If Used(tcSursa)` (`oproceduri_comune.prg:3027`) — daca `ccrsbnr` nu e deja deschis (cazul cand chemi metoda direct, fara sa treci prin `initializeaza`, care il creeaza via `goExecutor.oExecute`), cade pe `Else` si cheama `amessagebox("Eroare interna 2 - copiaza structura cursor",...)` (`oproceduri_comune.prg:3036`). - `amessagebox` (functia REALA, `oproceduri_comune.prg:1205`) e apelata **din acelasi fisier** ca `copiaza_structura_cursor` — `mock_amessagebox.prg` documenteaza explicit aceasta limita ("LIMITA 1: nu acopera sigur apelurile INTRA-FISIER") — deci mock-ul nu intercepteaza, se deschide `messagebox_form` real (`MessageBox.vcx`), modal, headless agata. - In productie nu se intampla: `initializeaza()` creeaza `This.cCursor` cu `goExecutor.oExecute(lcSql, This.cCursor)` (`onom_curs.vc2:159`) INAINTE de a chema `citeste_curs_bnr`. **Reparatia e in test, nu in cod**: inainte de FIECARE apel, testul precreaza `ccrsbnr` gol (`CREATE CURSOR ccrsbnr (Currency C(10), multiplier N(10,4), Curs N(14,6))` — campurile confirmate din `scrie_curs_bnr`, `onom_curs.vc2:282-283`, care citeste exact `Currency`/`multiplier`/`Curs` din cursor). Metoda oricum inchide cursorul (`Use In`) si il reconstruieste din raspunsul HTTP (`Xmltocursor`), deci structura precreata conteaza doar pentru backup, nu pentru rezultat. Confirmat cu proba minimala: dupa precreare, Return=0, Reccount=37, EUR curs=5.2637 — fara agatare. Niciun fisier de productie nu a fost atins pentru asta. ## 2. Rezultate — 7 cazuri cerute + 1 caz suplimentar (cascada) | # | Caz | Parametri | Return asteptat | Return obtinut | Durata | Rezultat | |---|---|---|---|---|---|---| | 1 | sursa 1, azi (zi lucratoare) | `dData=17.09.2026` (joi) | 0 | 0 | 0.238s | **PASS** — Reccount=37, EUR curs=5.2637 | | 2 | sursa 1, luni recenta | `dData=14.09.2026` (luni) | 0 | 0 | 0.048s | **PASS** — cade pe publicarea de vineri, Reccount=37, EUR curs=5.2557 | | 3 | sursa 2, martie an curent | `dData=15.03.2026` | 0 | 0 | 0.084s | **PASS** — Reccount=37, EUR curs=5.0947 | | 4 | sursa 2, 2 ianuarie an curent | `dData=02.01.2026` | 0 | 0 | 0.202s | **PASS** — trecere in 2025, Reccount=38, EUR curs=5.0985 | | 5 | sursa 1, data mult mai veche decat arhiva | `dData=01.01.2026` | 2 | 2 | 0.047s | **PASS** — "data lipsa" distinct de "BNR picat" | | 6 | sursa 1, host invalid (indisponibilitate) | `cLinkArhiva`=host `.invalid` inexistent, `dData`=azi | 1 | **2** | 0.181s | **FAIL** — vezi sectiunea 3, defect real in cod | | 7 | fara parametru (compatibilitate inapoi) | `dData=18.08.2026` | 0 (comportament sursa 0) | 0 | 0.041s | **PASS** — `This.dData` s-a suprascris la 18.09.2026 (publicare+1), ca inainte | | 8 (suplimentar, cerut de coordonator) | cascada reala: acelasi obiect/`dData` din caz 5 (`Return 2` pe sursa 1), apoi sursa 2 | `dData=01.01.2026` | 0 | 0 | 0.120s | **PASS** — Reccount=38, EUR curs=5.0985, FARA "Eroare interna 2" la al doilea apel | **Sumar: 7 PASS, 1 FAIL** (cazul 6). ## 3. Cazul 5 — eroare tranzitorie auto-vindecata, nu afecteaza Return La finalul cazului 5 (Return 2, data prea veche), logul a prins: ``` [ON ERROR] 13 Alias 'CCRSBNR' is not found. in REPUNE_BACKUP_CURSOARE linia 6363 ``` Cauza: `citeste_curs_bnr` inchide `This.cCursor` (`Use In`) inainte de apelul HTTP; pe ramura `Return 2` cheama `repune_backup_cursoare(This.cCursor)` (`onom_curs.vc2:175`), a carei prima linie utila e `Select (tcOriginal)` (`oproceduri_comune.prg:6363`) pe un alias deja inchis — **risc preexistent, deja documentat** in `docs\patch_curs_bnr_arhiva.md` sectiunea 5, punctul 3 ("`repune_backup_cursoare` pe cursor deja inchis"), nu introdus de acest patch. Confirmat acum ca se reproduce efectiv. Efect: eroarea e prinsa de `ON ERROR`, executia continua la linia urmatoare (`lnRecno = Recno()`), iar liniile de dupa (`Select * From bckpccrsbnr Into Cursor ccrsbnr`) reconstruiesc singure alias-ul `ccrsbnr` — **auto-vindecare in aceeasi rulare**, `Return 2` final e corect si cazul 8 (cascada imediat urmatoare, sursa 2 pe acelasi obiect) confirma ca `ccrsbnr` ramane intr-o stare corecta (Used, fara sa mai loveasca "Eroare interna 2" la `creeaza_backup_cursoare` din apelul urmator). Nu s-a reparat codul — doar raportat, cum s-a cerut. ## 4. Cazul 6 — defect REAL, nu artefact de test **Return obtinut 2, asteptat 1.** Nu e problema harnessului — e un gol real in `citeste_curs_bnr` pentru cazul specific "host complet nerezolvabil" (nu doar HTTP non-200 sau timeout pe conexiune). Secventa exacta din log (prima eroare e cea reala, restul e cascada, exact ca in nota de procedura despre `ON ERROR`): ``` [ON ERROR] 1429 OLE IDispatch exception ... WinHttp.WinHttpRequest: The server name or address could not be resolved. in ACTUALIZARE_CURS_BNR.CITESTE_CURS_BNR (linia loHTTP.Send(), onom_curs.vc2:137) [ON ERROR] 1429 ... The data necessary to complete this operation is not yet available. (citirea ulterioara a lui loHTTP.Status, onom_curs.vc2:139) ... (cascada: 8x eroare 11 "Function argument value, type, or count is invalid" in bucla Do While de la onom_curs.vc2:151-173, cu lcFisier ramas needefinit) [ON ERROR] 13 Alias 'CCRSBNR' is not found. in REPUNE_BACKUP_CURSOARE linia 6363 ``` Explicatie: `loHTTP.Send()` (`onom_curs.vc2:137`) NU e in `TRY/CATCH` si NU verifica intai daca request-ul a esuat la nivel de retea/DNS — arunca direct o exceptie OLE cand hostul nu se rezolva. Fara `TRY/CATCH` in metoda, `ON ERROR` global preia eroarea si, conform semanticii VFP (executia continua la linia URMATOARE, nu la ramura `Else` corecta), programul "sare" peste blocul `If loHTTP.Status = 200 ... Else ... Return 1 ... Endif` fara sa execute nici ramura de succes, nici `Return 1` — continua direct in bucla de cautare `Cube` cu `lcFisier` gol/nedefinit, epuizeaza cele 10 incercari (cascada de erori 11), si iese pe ramura "nimic gasit" -> `Return 2`. Rezultatul final e gresit: un host complet indisponibil (DNS esuat) e raportat ca "data lipsa" (2), nu ca "indisponibilitate" (1) — exact confuzia pe care patch-ul trebuia sa o elimine, dar doar pentru raspunsuri HTTP non-200 de la un server care RASPUNDE, nu pentru esecul de rezolvare DNS. **Durata: 0.181s** — nu agata deloc (departe de cele 5s/5s/5s/15s din `SetTimeouts`); esecul de rezolvare DNS e instant, nu trece prin timeout-uri. Conform instructiunii explicite: **nu am reparat codul**. Raportez cu dovada de mai sus. ## 5. Cum s-a rulat Script: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg` (helper-e proprii: `crea_cursor_gol`, `log_txt`, `log_eroare`, `verifica_caz` — toate in acelasi fisier). Precompilare izolata: ``` powershell -ExecutionPolicy Bypass -File D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1 -Prg D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg ``` Rulare (proces separat, timeout 240s, in PowerShell): ```powershell $p = Start-Process -FilePath 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' ` -ArgumentList '-A','-T','"D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg"' -PassThru if (-not $p.WaitForExit(240000)) { $p.Kill() } ``` Log complet (marca unica de rulare in prima linie, `SYS(2015)`): `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_test_curs_bnr_out.txt` — ultima rulare confirmata: mtime 17.09.2026 22:26, marca `_7KA1C340N`. Retea: toate cele 8 sub-cazuri au ajuns efectiv la `curs.bnr.ro` (sau la hostul invalid pentru cazul 6) — reteaua a functionat, niciun rezultat nu e raportat fals din lipsa de conectivitate. ## 6. Confirmare: nicio scriere in Oracle Scriptul apeleaza EXCLUSIV `loCurs.citeste_curs_bnr(...)` pe obiecte `actualizare_curs_bnr` instantiate direct — niciun apel la `initializeaza()` (care ar citi din Oracle si, la `Reccount=0`, ar declansa cascada de UI) si niciun apel la `scrie_curs_bnr()` (singura metoda a clasei care scrie in `pack_curs.scrie_cotatii_curs`). Singurele interactiuni cu Oracle din intreaga rulare sunt cele din `test_init_env_auto_roafacturare.prg` (conectare + `SELECT`-uri de sesiune), neschimbate fata de orice alt test headless din acest proiect. ## 7. Fisiere temporare de test, sterse dupa confirmare `test_curs_bnr_probe.prg` / `.fxp` si `test_curs_bnr_probe2.prg` / `.fxp` (folosite doar pentru diagnostic, sterse din `COMUN\utile\Teste\` dupa ce artefactul de harness a fost confirmat si reparat in scriptul final). Raman pe disc doar `test_curs_bnr_headless.prg` / `.fxp` si logul `_test_curs_bnr_out.txt`. ## 8. Rerulare dupa reparatia `Try/Catch` (v4) Data/ora: 17.09.2026 22:36. Reparatia din `docs\patch_curs_bnr_arhiva.md` sectiunea 7 (`Try/Catch` in jurul ambelor `loHTTP.Send()` din `citeste_curs_bnr`) e scrisa in binar (write-back confirmat, fidelity-check pe round-trip identic). `.FXP` vechi al testului sters, precompilat din nou, o singura rulare vfp9 (`-A -T`), log nou confirmat pe mtime si marca (`_7KA1CGZRZ`, 22:36:54). | # | Caz | Return inainte (sectiunea 2) | Return dupa | Rezultat | |---|---|---|---|---| | 1-4, 7, 8 | neschimbate | 0 | 0 | PASS (neschimbat) | | 5 | data mult mai veche decat arhiva | 2 | 2 | PASS (neschimbat) | | 6 | host invalid (indisponibilitate) | **2 (FAIL, asteptat 1)** | **1** | **PASS** | **Sumar nou: 8 PASS, 0 FAIL** (fata de 7 PASS, 1 FAIL inainte de reparatie). Nicio regresie pe cazurile 1-5, 7, 8. Cazul 6 intoarce acum `1` ("BNR picat") in loc de `2` ("data lipsa"), deci `initializeaza` ofera acum linkul `https://www.cursbnr.ro/` in loc de mesajul gresit "Nu exista cursul BNR pentru data specificata!".