#!/usr/bin/env node // Controlul negativ al probelor clientului Java (1.5.0; 1.6.1 adauga redirectarea si poarta --release 8): fiecare paznic se strica intr-o COPIE a clasei (sursa din depozit nu se // atinge), probele se ruleaza pe copie si proba tinta TREBUIE sa pice; o plantare care nu se aplica sau nu compileaza e un ESEC al // controlului (STRICAT), nu un rosu. La urma, clasa neatinsa trebuie sa treaca intreaga. // node aerenew/sdk/java/control-negativ.mjs import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const AICI = path.dirname(fileURLToPath(import.meta.url)); const SRC = path.join(AICI, 'src', 'main', 'java', 'network', 'aere', 'cloud', 'AereCloud.java'); const TEST = path.join(AICI, 'src', 'test', 'java', 'network', 'aere', 'cloud', 'TestAereCloud.java'); const original = fs.readFileSync(SRC, 'utf8'); const PLANTARI = [ ['piatra zilei sterse acceptata fara comparatie', 'return new KeptCheck(day, "expired", dig, local, local.equals(dig), cand);', 'return new KeptCheck(day, "expired", dig, local, true, cand);', 'auditVerifyKept'], ['ziua tinuta acceptata fara comparatie', 'return new KeptCheck(day, "held", dig, local, local.equals(dig), null);', 'return new KeptCheck(day, "held", dig, local, true, null);', 'auditVerifyKept'], ['exportul jsonl declarat verificat fara hash', 'Boolean ok = "jsonl".equals(f) ? (digest != null && digest.equals(sha256Hex(r.octeti))) : null;', 'Boolean ok = "jsonl".equals(f) ? Boolean.TRUE : null;', 'auditExport'], ['cheia trimisa si fara attest', 'return req("POST", "/pq/readiness", json(m), attest);', 'return req("POST", "/pq/readiness", json(m), true);', 'readiness fara attest'], ['un 429 intors ca nepotrivire', 'if (r.status < 200 || r.status >= 300) throw new AereCloudException(r.status, r.text(), "/account/audit");\n String dig = sha256Hex(r.octeti);', 'if (r.status < 200 || r.status >= 300) return new KeptCheck(day, "held", null, local, false, null);\n String dig = sha256Hex(r.octeti);', 'auditVerifyKept'], // 1.6.1: HttpURLConnection urmeaza redirectarile implicit; fara oprire, cheia pleaca spre gazda din Location ['redirectarea urmata (cheia pleaca spre alta gazda)', 'c.setInstanceFollowRedirects(false);', 'c.setInstanceFollowRedirects(true);', '302'], // 1.7.0: conformitatea trimisa pe ruta identitatii; identitatea fara cheie ['complianceCheck pe ruta identitatii', 'return req("POST", "/compliance/check", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, true);', '1.7.0'], ['identityVerify fara cheie', 'return req("POST", "/identity/verify", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, false);', '1.7.0'], // 1.8.0: chitanta pe ruta riscului; verificarea fara cheie ['paymentsReceipt pe ruta riscului', 'return req("POST", "/payments/receipt", requestJson, true);', 'return req("POST", "/payments/risk", requestJson, true);', '1.8.0'], ['paymentsVerify fara cheie', 'return req("POST", "/payments/verify", requestJson, true);', 'return req("POST", "/payments/verify", requestJson, false);', '1.8.0'], ]; // 1.6.1: poarta Android = clasa compileaza cu `javac --release 8`. Un API de dupa Java 8 trece probele pe JDK 21 si trebuie prins // numai de poarta; plantarea NU e o proba, e o compilare care TREBUIE sa cada. const PLANTARI_JAVA8 = [ ['InputStream.readAllBytes (Java 9)', 'while ((n = in.read(buf)) != -1) b.write(buf, 0, n);\n return b.toByteArray();', 'return in.readAllBytes();'], ['URLEncoder.encode cu Charset (Java 10)', 'try { return URLEncoder.encode(s, "UTF-8"); }', 'try { if (s == null) throw new java.io.UnsupportedEncodingException(); return URLEncoder.encode(s, StandardCharsets.UTF_8); }'], ]; function compileazaJava8(sursa) { const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-java8-')); try { fs.writeFileSync(path.join(d, 'AereCloud.java'), sursa); execFileSync('javac', ['--release', '8', '-Xlint:-options', '-d', path.join(d, 'out'), path.join(d, 'AereCloud.java')], { stdio: 'pipe' }); return { ok: true }; } catch (e) { return { ok: false, err: String(e.stderr || e.message).split('\n').find((l) => /error/.test(l)) || 'eroare' }; } finally { fs.rmSync(d, { recursive: true, force: true }); } } function ruleaza(sursa) { const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-java-cn-')); try { fs.writeFileSync(path.join(d, 'AereCloud.java'), sursa); try { execFileSync('javac', ['-d', path.join(d, 'out'), path.join(d, 'AereCloud.java'), TEST], { stdio: 'pipe' }); } catch (e) { return { stricat: 'nu compileaza: ' + String(e.stderr || e.message).split('\n')[0] }; } let out; try { out = execFileSync('java', ['-cp', path.join(d, 'out'), 'network.aere.cloud.TestAereCloud'], { encoding: 'utf8' }); } catch (e) { out = String(e.stdout || ''); } const m = /(\d+) treceri, (\d+) esecuri/.exec(out); if (!m) return { stricat: 'probele nu au terminat' }; return { treceri: +m[1], esecuri: +m[2], picate: out.split('\n').filter((l) => l.startsWith(' PICA')) }; } finally { fs.rmSync(d, { recursive: true, force: true }); } } let rele = 0; for (const [nume, vechi, nou, tinta] of PLANTARI) { if (original.split(vechi).length !== 2) { console.log(` STRICAT ${nume}: ancora apare de ${original.split(vechi).length - 1} ori`); rele++; continue; } const r = ruleaza(original.replace(vechi, nou)); if (r.stricat) { console.log(` STRICAT ${nume}: ${r.stricat}`); rele++; continue; } if (r.picate.some((l) => l.includes(tinta))) console.log(` ROSU cum trebuia ${nume} (${r.esecuri} esec, intre ele proba '${tinta}')`); else { console.log(` CONTROL CAZUT ${nume}: proba '${tinta}' a ramas verde`); rele++; } } for (const [nume, vechi, nou] of PLANTARI_JAVA8) { if (original.split(vechi).length !== 2) { console.log(` STRICAT ${nume}: ancora apare de ${original.split(vechi).length - 1} ori`); rele++; continue; } const r = compileazaJava8(original.replace(vechi, nou)); if (!r.ok) console.log(` ROSU cum trebuia ${nume}: --release 8 refuza (${r.err.trim().slice(0, 90)})`); else { console.log(` CONTROL CAZUT ${nume}: --release 8 a compilat`); rele++; } } const j8 = compileazaJava8(original); if (!j8.ok) { console.log(' MARTORUL nu compileaza cu --release 8: ' + j8.err); rele++; } else console.log(' martor: sursa neatinsa compileaza cu --release 8 (API Java 8, deci si Android)'); const martor = ruleaza(original); if (martor.stricat || martor.esecuri !== 0) { console.log(' MARTORUL nu e verde: ' + JSON.stringify(martor)); rele++; } else console.log(` martor: sursa neatinsa ${martor.treceri}/${martor.treceri} verde`); console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `CONTROL NEGATIV TRECUT (${PLANTARI.length + PLANTARI_JAVA8.length} din ${PLANTARI.length + PLANTARI_JAVA8.length} rosii, martorul verde si pe --release 8)`); process.exitCode = rele ? 1 : 0;