@@ -0,0 +1,254 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs/promises';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
const base = 'http://127.0.0.1:8005';
|
||||
function client() {
|
||||
const cookies = new Map();
|
||||
let csrf;
|
||||
let version;
|
||||
async function request(path, method = 'GET', data, inertia = true) {
|
||||
const response = await fetch(base + path, {
|
||||
method,
|
||||
redirect: 'manual',
|
||||
headers: {
|
||||
Accept: inertia ? 'application/json' : 'text/html',
|
||||
Cookie: [...cookies].map(([k, v]) => `${k}=${v}`).join('; '),
|
||||
...(inertia
|
||||
? {
|
||||
'X-Inertia': 'true',
|
||||
'X-Inertia-Version': version ?? '',
|
||||
}
|
||||
: {}),
|
||||
...(data
|
||||
? {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrf,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
...(data ? { body: JSON.stringify(data) } : {}),
|
||||
});
|
||||
for (const cookie of response.headers.getSetCookie()) {
|
||||
const pair = cookie.split(';', 1)[0],
|
||||
i = pair.indexOf('=');
|
||||
cookies.set(pair.slice(0, i), pair.slice(i + 1));
|
||||
}
|
||||
const text = await response.text();
|
||||
return {
|
||||
status: response.status,
|
||||
location: response.headers.get('location'),
|
||||
text,
|
||||
page: response.headers
|
||||
.get('content-type')
|
||||
?.includes('application/json')
|
||||
? JSON.parse(text)
|
||||
: null,
|
||||
};
|
||||
}
|
||||
return {
|
||||
request,
|
||||
async enter(role) {
|
||||
let page = await request('/avaliar', 'GET', undefined, false);
|
||||
assert.equal(page.status, 200);
|
||||
csrf = page.text.match(/name="_token" value="([^"]+)"/)[1];
|
||||
assert.equal(
|
||||
(await request('/avaliar/entrar', 'POST', { role }, false))
|
||||
.status,
|
||||
302,
|
||||
);
|
||||
page = await request('/avaliar', 'GET', undefined, false);
|
||||
csrf = page.text.match(/name="_token" value="([^"]+)"/)[1];
|
||||
const dashboard = await request(
|
||||
'/dashboard',
|
||||
'GET',
|
||||
undefined,
|
||||
false,
|
||||
);
|
||||
const encoded = dashboard.text.match(
|
||||
/<script data-page="app" type="application\/json">([\s\S]*?)<\/script>/,
|
||||
);
|
||||
assert.ok(encoded, 'Expected Inertia bootstrap data');
|
||||
version = JSON.parse(encoded[1]).version;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const author = client(),
|
||||
reviewer = client();
|
||||
await author.enter('admin');
|
||||
await reviewer.enter('finance');
|
||||
const path = '/fornecedores/avaliacoes/1',
|
||||
responseBase = path + '/contraditorios';
|
||||
const before = await author.request(path);
|
||||
assert.equal(before.status, 200);
|
||||
assert.equal(before.page.props.evaluation.status, 'approved');
|
||||
assert.equal(before.page.props.responses.ready, true);
|
||||
const original = JSON.stringify(before.page.props.evaluation);
|
||||
const operation_key = randomUUID();
|
||||
let payload = {
|
||||
title: 'QA — contraditório do percurso integrado',
|
||||
received_on: '',
|
||||
source_reference: '',
|
||||
statement: '',
|
||||
};
|
||||
assert.equal(
|
||||
(await author.request(responseBase, 'POST', { operation_key, payload }))
|
||||
.status,
|
||||
302,
|
||||
);
|
||||
assert.equal(
|
||||
(await author.request(responseBase, 'POST', { operation_key, payload }))
|
||||
.status,
|
||||
302,
|
||||
);
|
||||
let page = await author.request(path);
|
||||
const record = page.page.props.responses.records
|
||||
.filter((r) => r.payload.title === payload.title)
|
||||
.sort((a, b) => b.id - a.id)[0];
|
||||
assert.ok(record);
|
||||
const url = responseBase + '/' + record.id;
|
||||
assert.equal(
|
||||
(await author.request(url + '/submit', 'POST', { version: 1 })).status,
|
||||
422,
|
||||
);
|
||||
payload = {
|
||||
...payload,
|
||||
received_on: '2026-10-29',
|
||||
source_reference:
|
||||
'Documento fictício QA-CTR — ensaio sem comunicação externa.',
|
||||
statement:
|
||||
'Argumentos fictícios do fornecedor destinados a conferir o fluxo e o histórico.',
|
||||
};
|
||||
assert.equal(
|
||||
(await author.request(url + '/save', 'POST', { version: 1, payload }))
|
||||
.status,
|
||||
302,
|
||||
);
|
||||
assert.equal(
|
||||
(await author.request(url + '/save', 'POST', { version: 1, payload }))
|
||||
.status,
|
||||
422,
|
||||
);
|
||||
assert.equal(
|
||||
(await author.request(url + '/submit', 'POST', { version: 2 })).status,
|
||||
302,
|
||||
);
|
||||
const decision = {
|
||||
outcome: 'partially_accepted',
|
||||
reason: 'Parte dos argumentos do exemplo está sustentada pelas referências fictícias.',
|
||||
follow_up:
|
||||
'Conferir o âmbito afectado numa nova revisão, sem alterar esta avaliação.',
|
||||
response_reference: 'Minuta fictícia QA-RESPOSTA — não enviada.',
|
||||
};
|
||||
assert.equal(
|
||||
(await author.request(url + '/decide', 'POST', { version: 3, ...decision }))
|
||||
.status,
|
||||
403,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await reviewer.request(url + '/return', 'POST', {
|
||||
version: 3,
|
||||
reason: 'Completar a referência do documento recebido para o ensaio.',
|
||||
})
|
||||
).status,
|
||||
302,
|
||||
);
|
||||
payload = {
|
||||
...payload,
|
||||
source_reference: payload.source_reference + ' Anexo fictício A.',
|
||||
};
|
||||
assert.equal(
|
||||
(await author.request(url + '/save', 'POST', { version: 4, payload }))
|
||||
.status,
|
||||
302,
|
||||
);
|
||||
assert.equal(
|
||||
(await author.request(url + '/submit', 'POST', { version: 5 })).status,
|
||||
302,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await reviewer.request(url + '/decide', 'POST', {
|
||||
version: 6,
|
||||
...decision,
|
||||
follow_up: '',
|
||||
})
|
||||
).status,
|
||||
422,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await reviewer.request(url + '/decide', 'POST', {
|
||||
version: 6,
|
||||
...decision,
|
||||
})
|
||||
).status,
|
||||
302,
|
||||
);
|
||||
assert.equal(
|
||||
(await author.request(url + '/save', 'POST', { version: 7, payload }))
|
||||
.status,
|
||||
409,
|
||||
);
|
||||
page = await author.request(path);
|
||||
assert.equal(JSON.stringify(page.page.props.evaluation), original);
|
||||
const final = page.page.props.responses.records.find((r) => r.id === record.id);
|
||||
assert.equal(final.status, 'decided');
|
||||
assert.equal(final.version, 7);
|
||||
assert.equal(final.decision.outcome, 'partially_accepted');
|
||||
const events = page.page.props.responses.history.data.filter(
|
||||
(e) => e.after.id === record.id,
|
||||
);
|
||||
assert.equal(events.length, 7);
|
||||
assert.equal(
|
||||
events.find((e) => e.action === 'supplier_response.create').after.payload
|
||||
.source_reference,
|
||||
'',
|
||||
);
|
||||
assert.ok(
|
||||
events
|
||||
.find((e) => e.action === 'supplier_response.return')
|
||||
.after.review_reason.includes('Completar'),
|
||||
);
|
||||
const report = await author.request(path + '/relatorio');
|
||||
assert.equal(report.status, 200);
|
||||
const printed = report.page.props.responses.records.find(
|
||||
(r) => r.id === record.id,
|
||||
);
|
||||
assert.equal(printed.status, 'decided');
|
||||
assert.equal(printed.canEdit, false);
|
||||
assert.equal(printed.canReview, false);
|
||||
for (const c of [author, reviewer])
|
||||
assert.equal((await c.request('/logout', 'POST', {}, false)).status, 302);
|
||||
await fs.writeFile(
|
||||
'docs/proofs/supplier-responses-http.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
checked_at: new Date().toISOString(),
|
||||
base: 'http://127.0.0.1:8005',
|
||||
evaluation_path: path,
|
||||
response_id: record.id,
|
||||
idempotent_creation: true,
|
||||
incomplete_submit_refused: true,
|
||||
stale_save_refused: true,
|
||||
self_decision_refused: true,
|
||||
return_correction_independent_conclusion: true,
|
||||
accepted_follow_up_required: true,
|
||||
concluded_edit_refused: true,
|
||||
parent_evaluation_unchanged: true,
|
||||
history_events: events.length,
|
||||
individual_report: true,
|
||||
synthetic_example_only: true,
|
||||
external_messages_sent: 0,
|
||||
financial_commands: 0,
|
||||
visual_review: false,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + '\n',
|
||||
);
|
||||
console.log(
|
||||
'Supplier response HTTP flow passed: independent conclusion, preserved statements, history and report.',
|
||||
);
|
||||
Reference in New Issue
Block a user