Files
Investidor-V2/tools/portal-functional-documents-qa.cjs
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

20 lines
4.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const {start,assert,DIR,path,fs,sanitized}=require('./portal-functional-qa-lib.cjs');const crypto=require('node:crypto');
void(async()=>{const q=await start('functional-documents-chromium-20261011');let page,ctx;const route='/minhas-oportunidades/15/resources',title='Documento sintético QA22 — ciclo documental 11/10/2026',file=path.join(DIR,'functional-synthetic-document-20261011.pdf'),sha=crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
async function actor(role){if(ctx)await ctx.close();({page,ctx}=await q.context(role));await q.login(page,role);}
async function open(){const r=await q.open(page,route);assert.equal(r.status(),200);await page.getByRole('button',{name:'Documentos',exact:true}).click();}
async function binary(url){return page.evaluate(async u=>{const r=await fetch(u);const bytes=await r.arrayBuffer();const hash=await crypto.subtle.digest('SHA-256',bytes);return {status:r.status,final_path:new URL(r.url).pathname,sha256:[...new Uint8Array(hash)].map(b=>b.toString(16).padStart(2,'0')).join(''),type:r.headers.get('content-type'),cache:r.headers.get('cache-control')};},url);}
async function selected(){await open();await page.getByRole('article').filter({hasText:title}).getByRole('button',{name:'Ver / editar / restaurar',exact:true}).click();}
async function lifecycle(name){await selected();await page.getByLabel('Motivo (20–1000 caracteres)',{exact:true}).fill('Ensaio sintético QA de '+name+' documental: preservar os bytes cifrados, hash e auditoria.');await page.getByLabel('Confirmar exclusão lógica / restauração').check();const wait=page.waitForResponse(r=>r.request().method()==='POST'&&new URL(r.url()).pathname.endsWith('/lifecycle'));await page.getByRole('button',{name,exact:true}).click();const r=await wait;assert.equal(r.status(),200);}
try{await actor('colleague');await open();q.phase('QA22 uploads an explicitly synthetic private PDF');
await page.getByLabel('Título',{exact:true}).fill(title);await page.getByLabel('Classificação documental',{exact:true}).selectOption('restricted');await page.getByLabel('Âmbito de consulta',{exact:true}).selectOption('internal');await page.locator('input[type="file"]').setInputFiles(file);await page.getByLabel('Motivo (20–1000 caracteres)',{exact:true}).fill('Original PDF sintético QA para provar scan próprio, coedição e recuperação sem exposição pública.');
let id=q.prior.resource_id;if(!id){const saving=page.waitForResponse(r=>r.request().method()==='POST'&&new URL(r.url()).pathname.endsWith('/resources/documents'));await page.getByRole('button',{name:'Guardar recurso',exact:true}).click();const saved=await saving;assert.equal(saved.status(),201);id=(await saved.json()).id;}const url=route+'/documents/'+id+'/download';q.proof.resource_id=id;q.proof.sha256=sha;q.save();
let downloaded;for(let i=0;i<35;i++){downloaded=await binary(url);if(downloaded.status===200)break;assert.equal(downloaded.status,423);await page.waitForTimeout(1000);}assert.equal(downloaded.status,200);assert.equal(downloaded.sha256,sha);q.proof.scan_download={...downloaded,real_pipeline:true};q.save();
await open();await page.reload({waitUntil:'domcontentloaded'});await page.getByRole('button',{name:'Documentos',exact:true}).click();await page.getByRole('article').filter({hasText:title}).waitFor();await actor('promoter');await open();assert.equal((await binary(url)).sha256,sha);q.proof.owner_recovery=true;
await actor('colleague');await lifecycle('Excluir recurso logicamente');assert.equal((await binary(url)).status,404);q.proof.old_url_revoked_same_session=true;
await actor('promoter');await open();assert.equal((await binary(url)).status,404);q.proof.old_url_revoked_other_session=true;
await actor('colleague');await lifecycle('Restaurar recurso');assert.equal((await binary(url)).sha256,sha);q.proof.restored_same_original=true;
await actor('outsider');assert.equal((await q.open(page,route)).status(),403);const denied=await binary(url);assert([403,404].includes(denied.status));q.proof.other_organization_denied=denied.status;
await ctx.close();({ctx,page}=await q.context('anonymous'));await q.open(page,'/login');const publicResult=await binary(url);assert([401,403,404].includes(publicResult.status)||publicResult.final_path==='/login');assert.notEqual(publicResult.sha256,sha);q.proof.public_result={...publicResult,private_file_denied:true};
q.proof.result='passed';q.save();console.log(JSON.stringify({result:'passed',resource:id,private_file_denied:true,active_writes:0}));
}catch(e){q.proof.result='failed';q.proof.failure=sanitized(e);q.save();console.log(JSON.stringify({result:'failed',failure:q.proof.failure,active_writes:0}));process.exitCode=1;}finally{if(ctx)await ctx.close();await q.browser.close();}})();