test(builder): pin exact stake minimums; harden capture-script write ordering
This commit is contained in:
@@ -206,6 +206,24 @@ if (failures.length > 0) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deliberately dereference sampleProposal/goldenTokenPin's fields here,
|
||||||
|
// BEFORE writeFileSync, rather than only embedding them in the fixture
|
||||||
|
// object below: an object literal doesn't dereference its values, so if a
|
||||||
|
// future success path ever left either variable null without also pushing a
|
||||||
|
// fail() (the failures.length check above would then wrongly let a null
|
||||||
|
// slip through), `const fixture = {...}` and JSON.stringify would happily
|
||||||
|
// write "sampleProposal": null / "goldenTokenPin": null to disk with no
|
||||||
|
// error at all. Real property access (.txHash, .lockTxHash, ...) throws a
|
||||||
|
// TypeError on null, so doing it here makes the STOP-before-write guarantee
|
||||||
|
// a structural crash-before-write instead of resting solely on every
|
||||||
|
// success path remembering to keep the failures array in sync.
|
||||||
|
console.log('\nCapture validated, about to write:');
|
||||||
|
console.log(` refScripts: ${refScripts.length}/2, infra: ${infra.length}/3`);
|
||||||
|
console.log(` sampleProposal: ${sampleProposal.txHash}#${sampleProposal.index}`);
|
||||||
|
console.log(` goldenTokenPin.lockTxHash: ${goldenTokenPin.lockTxHash}`);
|
||||||
|
console.log(` goldenTokenPin.lockOutputIndex: ${goldenTokenPin.lockOutputIndex}`);
|
||||||
|
console.log(` goldenTokenPin.expectedTokenName: ${goldenTokenPin.expectedTokenName}`);
|
||||||
|
|
||||||
const fixture = {
|
const fixture = {
|
||||||
asOf: new Date().toISOString(),
|
asOf: new Date().toISOString(),
|
||||||
refScripts,
|
refScripts,
|
||||||
@@ -217,8 +235,3 @@ const fixture = {
|
|||||||
const outPath = resolve(import.meta.dirname, '../test/fixtures/gov-state.fixture.json');
|
const outPath = resolve(import.meta.dirname, '../test/fixtures/gov-state.fixture.json');
|
||||||
writeFileSync(outPath, JSON.stringify(fixture, null, 2) + '\n');
|
writeFileSync(outPath, JSON.stringify(fixture, null, 2) + '\n');
|
||||||
console.log(`\nwrote ${outPath}`);
|
console.log(`\nwrote ${outPath}`);
|
||||||
console.log(` refScripts: ${refScripts.length}/2, infra: ${infra.length}/3`);
|
|
||||||
console.log(` sampleProposal: ${sampleProposal.txHash}#${sampleProposal.index}`);
|
|
||||||
console.log(` goldenTokenPin.lockTxHash: ${goldenTokenPin.lockTxHash}`);
|
|
||||||
console.log(` goldenTokenPin.lockOutputIndex: ${goldenTokenPin.lockOutputIndex}`);
|
|
||||||
console.log(` goldenTokenPin.expectedTokenName: ${goldenTokenPin.expectedTokenName}`);
|
|
||||||
|
|||||||
@@ -216,6 +216,19 @@ describe('buildCritique (offline, FixtureProvider)', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('succeeds at exactly the minimum stake (10 AP3X) - the boundary is inclusive', async () => {
|
||||||
|
const r = await buildCritique(lucid, {
|
||||||
|
agentDid: AGENT_DID, proposalTxHash: PROPOSAL_TX, proposalOutputIndex: 0,
|
||||||
|
critiqueType: 'Supportive', stakeApex: 10,
|
||||||
|
critiqueHash: 'aa'.repeat(32), storageUri: 'ipfs://x',
|
||||||
|
});
|
||||||
|
assert.equal(r.stakeLovelace, '10000000');
|
||||||
|
const tx = decodeTx(r.txCbor);
|
||||||
|
const critiqueOut = findOutputTo(tx, r.scriptAddress);
|
||||||
|
assert.ok(critiqueOut, 'no output to the critique address');
|
||||||
|
assert.equal(critiqueOut!.amount().coin(), 12_000_000n, 'exactly-minimum stake (10 AP3X) must still build: 10 + 2 AP3X');
|
||||||
|
});
|
||||||
|
|
||||||
test('rejects a stake below the minimum', async () => {
|
test('rejects a stake below the minimum', async () => {
|
||||||
await assert.rejects(
|
await assert.rejects(
|
||||||
buildCritique(lucid, {
|
buildCritique(lucid, {
|
||||||
@@ -279,6 +292,17 @@ describe('buildEndorse (offline, FixtureProvider)', () => {
|
|||||||
assert.equal(Number(parsed!.proposalRef.fields[1]), 1);
|
assert.equal(Number(parsed!.proposalRef.fields[1]), 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('succeeds at exactly the minimum stake (5 AP3X) - the boundary is inclusive', async () => {
|
||||||
|
const r = await buildEndorse(lucid, {
|
||||||
|
agentDid: AGENT_DID, proposalTxHash: PROPOSAL_TX, proposalOutputIndex: 0, stakeApex: 5,
|
||||||
|
});
|
||||||
|
assert.equal(r.stakeLovelace, '5000000');
|
||||||
|
const tx = decodeTx(r.txCbor);
|
||||||
|
const endorseOut = findOutputTo(tx, r.scriptAddress);
|
||||||
|
assert.ok(endorseOut, 'no output to the endorsement address');
|
||||||
|
assert.equal(endorseOut!.amount().coin(), 7_000_000n, 'exactly-minimum stake (5 AP3X) must still build: 5 + 2 AP3X');
|
||||||
|
});
|
||||||
|
|
||||||
test('rejects a stake below the minimum', async () => {
|
test('rejects a stake below the minimum', async () => {
|
||||||
await assert.rejects(
|
await assert.rejects(
|
||||||
buildEndorse(lucid, {
|
buildEndorse(lucid, {
|
||||||
|
|||||||
Reference in New Issue
Block a user