#!/usr/bin/env node // Controlul negativ al probelor clientului Go: fiecare paznic se strica intr-o COPIE a pachetului (sursa din depozit nu se // atinge), `go test` ruleaza pe copie si proba tinta TREBUIE sa pice; o plantare care nu se aplica sau nu compileaza e STRICAT, // nu rosu. La urma, pachetul neatins trebuie sa treaca intreg. // node aerenew/sdk/go/control-negativ.mjs import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const AICI = path.dirname(fileURLToPath(import.meta.url)); const FISIERE = ['go.mod', 'aerecloud.go', 'aerecloud_test.go']; const sursa = fs.readFileSync(path.join(AICI, 'aerecloud.go'), 'utf8'); const PLANTARI = [ ['exportul jsonl declarat verificat fara hash', 'ok := e.Digest != "" && e.Digest == sha256Hex(r.body)', 'ok := true', 'TestExportulZilei'], ['piatra zilei sterse acceptata fara comparatie', 'Matches: local == t.Expired.Digest,', 'Matches: true,', 'TestExportulPastrat'], ['ziua tinuta acceptata fara comparatie', 'Matches: local == d}', 'Matches: true}', 'TestExportulPastrat'], ['cheia trimisa si fara attest', 'return c.jsonReq(ctx, "POST", "/pq/readiness", b, attest)', 'return c.jsonReq(ctx, "POST", "/pq/readiness", b, true)', 'TestReadinessCheiaNumaiLaAttest'], ['redirectarile urmate (cheia ar pleca spre alta gazda)', 'CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }', 'CheckRedirect: nil', 'TestRedirectareaNuEUrmata'], ['un 429 intors ca nepotrivire', 'return nil, &Error{Status: r.status, Body: r.body, Path: "/account/audit"}\n\t}\n\td := sha256Hex(r.body)', 'return &KeptCheck{Day: day, State: "held", Local: local}, nil\n\t}\n\td := sha256Hex(r.body)', 'TestExportulPastrat'], // 2026-09-29: plicul trecut printr-un map de Go (cheile sortate = alt text canonic, un plic valid ar iesi INVALID la Trust API) ['Verify trece plicul printr-un map', 'return c.jsonReq(ctx, "POST", p, envelope, true)', 'var m map[string]any\n\t_ = json.Unmarshal(envelope, &m)\n\treturn c.jsonReq(ctx, "POST", p, m, true)', 'TestVerifyPastreazaOrdinea'], // 1.7.0: prezentarea trecuta printr-un map (numerele mari devin float64) si ruta de conformitate trimisa la identitate ['IdentityVerify trece prezentarea printr-un map', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'var m map[string]any\n\t_ = json.Unmarshal(req.Presentation, &m)\n\tb, _ := json.Marshal(m)\n\treq.Presentation = b\n\treturn c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'], ['ComplianceCheck pe ruta de identitate', 'return c.jsonReq(ctx, "POST", "/compliance/check", req, true)', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'], // 1.8.0: intrarile de risc trecute printr-un map (suma peste 2^53 devine float64) si chitanta trimisa pe ruta riscului ['PaymentsRisk trece intrarile printr-un map', 'return c.jsonReq(ctx, "POST", "/payments/risk", req, true)', 'var m map[string]any\n\t_ = json.Unmarshal(req.Inputs, &m)\n\tb, _ := json.Marshal(m)\n\treq.Inputs = b\n\treturn c.jsonReq(ctx, "POST", "/payments/risk", req, true)', 'TestPaymentsPastreazaOctetii'], ['PaymentsReceipt pe ruta riscului', 'return c.jsonReq(ctx, "POST", "/payments/receipt", req, true)', 'return c.jsonReq(ctx, "POST", "/payments/risk", req, true)', 'TestPaymentsPastreazaOctetii'], ]; function ruleaza(src) { const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-go-cn-')); try { for (const f of FISIERE) fs.copyFileSync(path.join(AICI, f), path.join(d, f)); fs.writeFileSync(path.join(d, 'aerecloud.go'), src); const r = spawnSync('go', ['test', '-count=1', '-v', './...'], { cwd: d, encoding: 'utf8' }); const out = (r.stdout || '') + (r.stderr || ''); if (/\[build failed\]|cannot use|undefined:|syntax error/.test(out)) return { stricat: 'nu compileaza: ' + out.split('\n').find((l) => /error|undefined|cannot/.test(l)) }; const picate = [...out.matchAll(/--- FAIL: (\w+)/g)].map((m) => m[1]); const trecute = [...out.matchAll(/--- PASS: (\w+)/g)].map((m) => m[1]); if (!picate.length && !trecute.length) return { stricat: 'probele nu au rulat' }; return { picate, trecute }; } finally { fs.rmSync(d, { recursive: true, force: true }); } } let rele = 0; for (const [nume, vechi, nou, tinta] of PLANTARI) { if (sursa.split(vechi).length !== 2) { console.log(` STRICAT ${nume}: ancora apare de ${sursa.split(vechi).length - 1} ori`); rele++; continue; } const r = ruleaza(sursa.replace(vechi, nou)); if (r.stricat) { console.log(` STRICAT ${nume}: ${r.stricat}`); rele++; continue; } if (r.picate.includes(tinta)) console.log(` ROSU cum trebuia ${nume} (pica ${r.picate.join(', ')})`); else { console.log(` CONTROL CAZUT ${nume}: ${tinta} a ramas verde`); rele++; } } const m = ruleaza(sursa); if (m.stricat || m.picate.length) { console.log(' MARTORUL nu e verde: ' + JSON.stringify(m)); rele++; } else console.log(` martor: sursa neatinsa, ${m.trecute.length} probe verzi`); console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `CONTROL NEGATIV TRECUT (${PLANTARI.length} din ${PLANTARI.length} rosii, martorul verde)`); process.exitCode = rele ? 1 : 0;