import test from 'node:test'; import assert from 'node:assert/strict'; import { harness, props, text } from './helpers/partnership-room.mjs'; test('an uncertain agreement action exposes recovery of the frozen attempt', () => { const h = harness(props()); h.change('agreement', 'Motivo da ação', 'SYNTHETIC reason'); const request = h.click('Confirmar pela minha parte'); h.fail(request); assert.ok(h.button('Recuperar a mesma acção')); }); function ack(h, request, patch = {}) { const command = request.data; const statuses = { record: 'agreement_recorded', confirm: 'agreement_recorded', start: 'execution', complete: 'closed', cancel: 'cancelled', renew: 'negotiation', }; const agreement = { ...h.props.room.agreement }; if (command.action === 'record') { agreement.title = command.title.trim(); agreement.terms = command.terms.trim(); agreement.confirmations = {}; } else if (command.action === 'confirm') { agreement.confirmations = { ...agreement.confirmations, [h.props.agreementParty]: { actor_id: h.props.agreementActorId, date: '2026-11-03', reason: command.reason.trim(), }, }; } return { ...h.props, room: { ...h.props.room, agreement, version: command.version + 1, status: statuses[command.action], ...patch, }, agreementCommands: [ { key: command.client_key, action: command.action, version: command.version + 1, status: patch.status ?? statuses[command.action], party: command.action === 'confirm' ? h.props.agreementParty : null, }, ], }; } const adopt = 'Usar este contexto e conservar as alterações escolhidas'; const recover = 'Recuperar a mesma acção'; const refresh = 'Comparar com os dados actuais da sala'; function prepared() { const h = harness(props()); h.change('agreement', 'Motivo da ação', 'SYNTHETIC decision'); return h; } test('confirmation recovery freezes context and reason while preserving later preparation', () => { const h = prepared(); h.change( 'agreement', 'Título do instrumento', 'SYNTHETIC unrecorded title', ); const first = h.click('Confirmar pela minha parte'); const frozen = { ...first.data }; assert.equal(first.data.partnership_version, 4); assert.equal(first.data.interest_version, 3); assert.equal(first.data.title, undefined); h.fail(first); h.change('agreement', 'Motivo da ação', 'SYNTHETIC next reason'); const repeated = h.click(recover); assert.deepEqual(repeated.data, frozen); h.success(ack(h, repeated), repeated); assert.equal( h.field('agreement', 'Motivo da ação').props.value, 'SYNTHETIC next reason', ); assert.equal( h.field('agreement', 'Título do instrumento').props.value, 'SYNTHETIC unrecorded title', ); assert.equal(h.button(recover), undefined); }); test('record recovery preserves terms written after the original command', () => { const h = prepared(); h.change( 'agreement', 'Condições e plano de valor', 'SYNTHETIC submitted terms', ); const first = h.click('Registar versão do acordo'); h.fail(first); h.change('agreement', 'Condições e plano de valor', 'SYNTHETIC next terms'); const repeated = h.click(recover); assert.equal(repeated.data.terms, 'SYNTHETIC submitted terms'); h.success(ack(h, repeated), repeated); assert.equal( h.field('agreement', 'Condições e plano de valor').props.value, 'SYNTHETIC next terms', ); assert.equal(h.field('agreement', 'Motivo da ação').props.value, ''); }); test('a terminal cancellation still permits recovery of the same command', () => { const h = prepared(); const first = h.click('Cancelar conexão com motivo'); h.fail(first); const terminal = ack(h, first); h.render(terminal); assert.equal(h.button(recover).props.disabled, false); const repeated = h.click(recover); assert.deepEqual(repeated.data, first.data); h.success(terminal, repeated); assert.equal(h.button(recover), undefined); }); test('a fresh read can acknowledge a committed attempt without another write', () => { const h = prepared(); const first = h.click('Confirmar pela minha parte'); h.fail(first); h.click(refresh); h.refreshed(ack(h, first)); assert.equal(h.sent.length, 1); assert.equal(h.button(recover), undefined); assert.equal(h.field('agreement', 'Motivo da ação').props.value, ''); }); test('a recovered command followed by a bilateral decision requires explicit review', () => { const h = prepared(); const first = h.click('Confirmar pela minha parte'); h.fail(first); const repeated = h.click(recover); const later = ack(h, repeated, { version: first.data.version + 2, status: 'agreed', }); later.room.agreement.confirmations.candidate = { actor_id: 2, date: '2026-11-03', reason: 'SYNTHETIC other decision', }; later.agreementCommands[0].status = 'agreement_recorded'; h.success(later, repeated); assert.ok(h.button(adopt)); assert.equal(h.button('Iniciar acompanhamento').props.disabled, true); h.click(adopt); h.change('agreement', 'Motivo da ação', 'SYNTHETIC start after review'); assert.equal(h.button('Iniciar acompanhamento').props.disabled, false); }); test('a changed proposal with the same room version requires context adoption', () => { const h = prepared(); h.render({ ...h.props, partnership: { ...h.props.partnership, version: 5 }, }); assert.equal(h.button('Confirmar pela minha parte').props.disabled, true); h.click(adopt); const request = h.click('Confirmar pela minha parte'); assert.equal(request.data.partnership_version, 5); assert.equal(request.data.version, 2); }); test('an exact command receipt with a newer parent context still requires review', () => { const h = prepared(); const first = h.click('Confirmar pela minha parte'); const next = ack(h, first); next.interest = { ...next.interest, version: 4 }; h.success(next, first); assert.ok(h.button(adopt)); assert.equal( h.button('Actualizar confirmação da minha parte').props.disabled, true, ); h.click(adopt); h.change('agreement', 'Motivo da ação', 'SYNTHETIC updated context'); assert.equal( h.button('Actualizar confirmação da minha parte').props.disabled, false, ); }); test('missing own receipt or an incompatible confirmation stamp never clears a draft', () => { for (const wrong of ['receipt', 'stamp']) { const h = prepared(); const first = h.click('Confirmar pela minha parte'); const next = ack(h, first); if (wrong === 'receipt') next.agreementCommands = []; else next.room.agreement.confirmations.proponent.actor_id = 9; h.success(next, first); assert.equal( h.field('agreement', 'Motivo da ação').props.value, 'SYNTHETIC decision', ); assert.ok(h.button(recover)); assert.equal(h.button(adopt).props.disabled, true); } }); test('lost write authority blocks recovery and another actor does not inherit its draft', () => { const h = prepared(); const first = h.click('Confirmar pela minha parte'); h.fail(first); h.fail(first, 403); assert.equal(h.button(recover).props.disabled, true); h.render({ ...h.props, actorId: 2, agreementActorId: 2, agreementParty: 'candidate', agreementCommands: [], }); h.success(ack(h, first), first); assert.equal(h.field('agreement', 'Motivo da ação').props.value, ''); assert.equal(h.button(recover), undefined); }); test('a current confirmation is identified as an update rather than another party', () => { const initial = props(); initial.room.agreement.confirmations.proponent = { actor_id: 1, reason: 'SYNTHETIC old', date: '2026-11-02', }; const h = harness(initial); assert.ok(h.button('Actualizar confirmação da minha parte')); assert.equal(h.button('Confirmar pela minha parte'), undefined); assert.match(text(h.tree), /proponente/i); }); test('a callback from an abandoned attempt does not freeze its replacement', () => { const h = prepared(); const first = h.click('Confirmar pela minha parte'); h.fail(first); h.click(refresh); h.refreshed(h.props); h.click(adopt); const second = h.click('Confirmar pela minha parte'); assert.notEqual(second.data.client_key, first.data.client_key); h.fail(first); h.success(ack(h, second), second); assert.equal(h.button(recover), undefined); assert.equal(h.field('agreement', 'Motivo da ação').props.value, ''); });