sync SVN r18175

This commit is contained in:
2026-09-17 22:44:24 +03:00
parent 04a78f99db
commit 30fee191f3
9 changed files with 1541 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
# 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!".