20 lines
4.7 KiB
JavaScript
20 lines
4.7 KiB
JavaScript
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();}})();
|