Files
SGF/scripts/a-http-proof.mjs

51 lines
4.5 KiB
JavaScript

import fs from 'node:fs/promises';
import assert from 'node:assert/strict';
import { randomUUID } from 'node:crypto';
const fixture = JSON.parse(await fs.readFile('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json','utf8'));
const base = 'http://127.0.0.1:8001';
function client() {
const cookies = new Map(); let csrf = ''; let context = '';
return {
cookies,
async request(path, method='GET', body, {key=randomUUID(), token=context, useCsrf=true}={}) {
const headers={Accept:'application/json','Content-Type':'application/json', Cookie:[...cookies].map(([k,v])=>`${k}=${v}`).join('; '),'X-SGF-Context':token,'Idempotency-Key':key};
if(useCsrf) headers['X-CSRF-TOKEN']=csrf;
const response=await fetch(base+path,{method,headers,...(body===undefined?{}:{body:JSON.stringify(body)})});
for(const cookie of response.headers.getSetCookie()){const pair=cookie.split(';',1)[0], split=pair.indexOf('=');cookies.set(pair.slice(0,split),pair.slice(split+1));}
const data=await response.json().catch(()=>({}));
if(data.csrf)csrf=data.csrf;if(data.context?.token)context=data.context.token;
return {status:response.status,data};
},
async login(email){await this.request('/api/session');assert.equal((await this.request('/api/login','POST',{email,password:fixture.password})).status,200);},
async select(unit=fixture.unit){const result=await this.request('/api/context','POST',{tenant_id:fixture.tenant,company_id:fixture.company,unit_id:unit});assert.equal(result.status,200);return result.data.context.token;}
};
}
const manager=client(),reader=client(); await manager.login('gestor@example.test');const oldToken=await manager.select();await reader.login('consulta@example.test');await reader.select();
const results=[];
const key=randomUUID();const created=await manager.request('/api/a/clients','POST',{name:'Cliente sintético HTTP A'},{key});assert.equal(created.status,201);
const replay=await manager.request('/api/a/clients','POST',{name:'Cliente sintético HTTP A'},{key});assert.equal(replay.status,201);assert.equal(replay.data.id,created.data.id);assert.equal(replay.data.event_id,created.data.event_id);assert.equal(replay.data.replayed,true);
assert.equal((await manager.request('/api/a/clients','POST',{name:'Conteúdo alterado'},{key})).status,409);
results.push('Criação repetida recupera o mesmo registo/evento; conteúdo diferente com a mesma chave é recusado (409).');
const path=`/api/a/clients/${created.data.id}`;
assert.equal((await reader.request(path)).status,200);
assert.equal((await reader.request(path,'PATCH',{name:'Não autorizado',version:1})).status,403);
assert.equal((await reader.request('/api/a/clients/access')).status,403);
results.push('Consulta por credenciais reais sintéticas: leitura 200, edição e gestão 403.');
const grant={email:'consulta@example.test',action:'edit',grant:true,reason:'Prova HTTP A, exclusivamente sintética'};
assert.equal((await manager.request('/api/a/clients/access','POST',grant)).status,200);
assert.equal((await reader.request(path,'PATCH',{name:'Edição sintética autorizada',version:1})).status,200);
assert.equal((await manager.request('/api/a/clients/access','POST',{...grant,grant:false})).status,200);
assert.equal((await reader.request(path,'PATCH',{name:'Direito revogado',version:2})).status,403);
results.push('Concessão/revogação de edição têm efeito no pedido seguinte, mantendo a mesma sessão.');
assert.equal((await manager.request(path,'PATCH',{name:'Versão antiga',version:1})).status,409);
await manager.select(null);
assert.equal((await manager.request(path,'PATCH',{name:'Contexto antigo',version:2},{token:oldToken})).status,409);
assert.equal((await manager.request('/api/a/clients','POST',{name:'Sem CSRF'},{useCsrf:false})).status,419);
results.push('Versão antiga e contexto antigo recusados (409); alteração sem protecção CSRF/origem recusada (419).');
const previousCookie=new Map(reader.cookies);assert.equal((await reader.request('/api/logout','POST')).status,200);reader.cookies.clear();for(const [k,v] of previousCookie)reader.cookies.set(k,v);
assert.equal((await reader.request('/api/a/clients')).status,401);await manager.request('/api/logout','POST');
results.push('Cookie anterior à saída não recupera acesso aos dados de A (401).');
await fs.writeFile(new URL('../docs/proofs/a-http-results.json',import.meta.url),JSON.stringify({environment:'browser-testing / sgf_tempo_test only',results},null,2));
console.log(results.join('\n'));