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

22 lines
6.4 KiB
JavaScript
Raw 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,TARGET,BASE}=require('./portal-service-followup-browser-lib.cjs');
const cp=require('node:child_process'),crypto=require('node:crypto');
const PHP='C:/laragon/bin/php/php-8.5.11-Win32-vs17-x64/php.exe';
void(async()=>{const q=await start('service-followup-web-uploads-qa');let ctx;try{
const a=await q.context('promoter');ctx=a.ctx;const page=a.page;await q.login(page,'promoter');
const runtime=await q.json(page,'/__qa/services-runtime');assert.equal(runtime.status,200);q.proof.web_runtime=runtime.data;
assert.equal(runtime.data.sapi,'apache2handler');assert.equal(runtime.data.upload_max_filesize,'20M');assert.equal(runtime.data.post_max_size,'24M');
async function post(url,data){return await page.evaluate(async({url,data})=>{const token=decodeURIComponent(document.cookie.split('; ').find(v=>v.startsWith('XSRF-TOKEN='))?.slice(11)||'');const r=await fetch(url,{method:'POST',headers:{Accept:'application/json','Content-Type':'application/json','X-XSRF-TOKEN':token},body:JSON.stringify(data)});return {status:r.status,data:await r.json()};},{url,data});}
const r=await post('/minhas-oportunidades/drafts',{creation_key:crypto.randomUUID(),opportunity_category:'real_estate',title:'SIMULAÇÃO QA — upload superior a 2 MiB e recuperação',reason:'SIMULAÇÃO QA isolada: verificar limites PHP web e supervisão sem efeito jurídico ou divulgação.'});assert.equal(r.status,201);const id=r.data.id;q.proof.opportunity_id=id;q.save();
async function props(){const r=await q.open(page,`/minhas-oportunidades/${id}/resources`);assert.equal(r.status(),200);const t=await r.text(),enc=t.match(/data-page="([^"]+)"/);return enc&&enc[1].startsWith('{')?JSON.parse(enc[1].replace(/&quot;/g,'"').replace(/&amp;/g,'&').replace(/&#039;/g,"'")).props:JSON.parse(t.match(/<script[^>]*type="application\/json"[^>]*>([\s\S]*?)<\/script>/)[1]).props;}
// Valid minimal PDF padded with legal comments before xref, no private/reused document.
function pdf(size){let bytes='%PDF-1.4\n',offset=[0];for(const object of ['<< /Type /Catalog /Pages 2 0 R >>','<< /Type /Pages /Kids [3 0 R] /Count 1 >>','<< /Type /Page /Parent 2 0 R /MediaBox [0 0 100 100] >>']){offset.push(Buffer.byteLength(bytes));bytes+=`${offset.length-1} 0 obj\n${object}\nendobj\n`;}bytes+='% SIMULACAO QA - no legal effect\n';const padding=size-Buffer.byteLength(bytes)-300;bytes+=('%'+ 'Q'.repeat(78)+'\n').repeat(Math.ceil(padding/80));const xref=Buffer.byteLength(bytes);bytes+='xref\n0 4\n0000000000 65535 f \n'+offset.slice(1).map(n=>String(n).padStart(10,'0')+' 00000 n \n').join('')+`trailer\n<< /Size 4 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`;return Buffer.from(bytes);}
const bytes=pdf(2621440),fixture=path.join(DIR,'service-followup-large-qa.pdf');fs.writeFileSync(fixture,bytes);q.proof.upload_bytes=bytes.length;q.proof.upload_sha256=crypto.createHash('sha256').update(bytes).digest('hex');
q.phase('Upload >2 MiB through visible document form');await props();await page.getByRole('button',{name:'Documentos',exact:true}).click();await page.getByLabel('Título',{exact:true}).fill('SIMULAÇÃO QA — PDF de 2,5 MiB');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(fixture);await page.getByLabel('Motivo (20–1000 caracteres)',{exact:true}).fill('SIMULAÇÃO QA: validar upload web, scan real e recuperação após interrupção do worker sem duplicações.');const pending=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 upload=await pending;assert.equal(upload.status(),201);const resource=(await upload.json()).id;q.proof.resource_id=resource;await props();await page.getByRole('button',{name:'Documentos',exact:true}).click();await q.shot(page,'upload-large-pending-visible');
function inspect(){return JSON.parse(cp.execFileSync(PHP,[path.join(__dirname,'portal-service-followup-inspect.php'),String(id)],{encoding:'utf8',timeout:30000}));}const before=inspect();assert.equal(before.rows.length,1);assert.equal(before.rows[0].malware_scan_status,'pending');assert.equal(before.jobs.length,1);assert(before.scanner.available);q.proof.before_worker=before;
const url=`/minhas-oportunidades/${id}/resources/documents/${resource}/download`;assert.equal((await page.request.get(BASE+url)).status(),423);q.proof.checks.large_upload_visible_saved_queued_and_quarantined=true;q.save();
q.phase('PHP upload and POST limits, rejection without duplicate or partial document');
async function multipart(size,key){const p=await props();return await page.evaluate(async({url,etag,size,key})=>{const f=new FormData();f.set('etag',etag);f.set('creation_key',key);f.set('reason','SIMULAÇÃO QA: excesso de limite deve ser rejeitado sem persistência parcial ou duplicação.');f.set('payload',JSON.stringify({title:'SIMULAÇÃO QA — excesso',classification:'restricted',visibility:'internal'}));f.set('file',new File([new Uint8Array(size)],'SIMULACAO-QA.pdf',{type:'application/pdf'}));const token=decodeURIComponent(document.cookie.split('; ').find(v=>v.startsWith('XSRF-TOKEN='))?.slice(11)||'');const r=await fetch(url,{method:'POST',headers:{Accept:'application/json','X-XSRF-TOKEN':token},body:f});let data;try{data=await r.json();}catch{data={controlled:false};}return {status:r.status,data};},{url:`/minhas-oportunidades/${id}/resources/documents`,etag:p.etag,size,key});}
const overUpload=await multipart(20*1048576+1,crypto.randomUUID());assert.equal(overUpload.status,422);q.proof.over_upload={bytes:20*1048576+1,status:overUpload.status,errors:overUpload.data.errors};const overPost=await multipart(25*1048576,crypto.randomUUID());assert.equal(overPost.status,413);q.proof.over_post={bytes:25*1048576,status:overPost.status};assert.deepEqual(inspect().rows,before.rows);assert.equal(inspect().jobs.length,1);q.proof.checks.php_web_limits_reject_without_partial_write=true;
q.proof.result='passed_upload_preparation';q.save();console.log(JSON.stringify({result:q.proof.result,target:TARGET,opportunity_id:id,upload_bytes:bytes.length,checks:q.proof.checks,active_writes:0}));
}catch(e){q.proof.result='failed';q.proof.failure=sanitized(e);q.save();console.log(JSON.stringify({result:'failed',phase:q.proof.phase,failure:q.proof.failure}));process.exitCode=1;}finally{if(ctx)await ctx.close();await q.browser.close();}})();