Skip to content

Commit

Permalink
test(a3p): migrate wallet and vaults related tests from `a3p-proposal…
Browse files Browse the repository at this point in the history
…s` to the `z:acceptance` (#10123)

closes: https://github.com/Agoric/BytePitchPartnerEng/issues/5
closes: https://github.com/Agoric/BytePitchPartnerEng/issues/7
refs: #10049

## Description


This PR is part of an ongoing effort to migrate some of the selected test cases from `a3p-proposals` to the `z:acceptance` test phase. 

This particular PR new tests focus on:
- invitations and payments behavior within the smart wallet
- actions that can be executed on vaults

The source code of the migrated tests are:
- [send invitation via namesByAddress](https://github.com/Agoric/agoric-3-proposals/blob/main/proposals/75%3Aupgrade-16/provisioning.test.js#L34-L51)
- [exitOffer tool reclaims stuck payment](https://github.com/Agoric/agoric-3-proposals/blob/main/proposals/74%3Aupgrade-15/exit-reclaim.test.js#L9-L38)
- [ante handler sends fee only to vbank/reserve](https://github.com/Agoric/agoric-3-proposals/blob/main/proposals/71%3Aupgrade-14/ante-fees.test.js#L5-L74)
- Vault actions executed on [actions.sh](https://github.com/Agoric/agoric-sdk/blob/mainnet1B-rc3/packages/deployment/upgrade-test/upgrade-test-scripts/agoric-upgrade-10/actions.sh#L105-L176)


### Security Considerations


n/a

### Scaling Considerations


n/a

### Documentation Considerations


One commit included in this PR fixes a small typo on `a3p-integration/proposals/README.md` 

### Testing Considerations


The new test files included in this PR are invoked through `test.sh`, so no changes to the current testing workflow are necessary.

However, as pointed out by @dckc in this [comment](bytepitch@6afaba3#r146887047), using `waitForBlock()` can lead to unexpected behavior. To address this, we plan to update the `z:acceptance` tests to mitigate this issue.

As a solution, we propose extending the `@agoric/synthetic-chain` package to export a method like [makeRetryUntilCondition](https://github.com/Agoric/agoric-sdk/blob/f291362e4bc62ec31552cef29ec1a5f5cfbf7bd7/multichain-testing/tools/sleep.ts#L62) or a similar function. This would provide a more reliable alternative to `waitForBlock()`.

Additionally, there is already an [test-vaults.mts](https://github.com/Agoric/agoric-sdk/blob/master/a3p-integration/proposals/z%3Aacceptance/scripts/test-vaults.mts) file that test operations related to changes on auctions parameters.
To avoid confusion with `vaults.test` due to the similarity in names, I propose renaming this file to `auction.test` and expanding its scope to cover all auction-related tests.

### Upgrade Considerations


n/a
  • Loading branch information
mergify[bot] authored Oct 8, 2024
2 parents b61865e + e43bcd7 commit feae722
Show file tree
Hide file tree
Showing 8 changed files with 565 additions and 1 deletion.
2 changes: 1 addition & 1 deletion a3p-integration/proposals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Steps:
1. Get the upgrade branch
2. Build the submissions
3. Migrate the proposal
4. Update the `latest` iamge
4. Update the `latest` image

### Get the proposal's branch

Expand Down
97 changes: 97 additions & 0 deletions a3p-integration/proposals/z:acceptance/exitOffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Note: limit imports to node modules for portability
import { parseArgs, promisify } from 'node:util';
import { execFile } from 'node:child_process';
import { writeFile, mkdtemp, rm } from 'node:fs/promises';
import { join } from 'node:path';

const options = /** @type {const} */ ({
id: { type: 'string' },
from: { type: 'string' },
bin: { type: 'string', default: '/usr/src/agoric-sdk/node_modules/.bin' },
});

const Usage = `
Try to exit an offer, reclaiming any associated payments.
node exitOffer.js --id ID --from FROM [--bin PATH]
Options:
--id <offer id>
--from <address or key name>
--bin <path to agoric and agd> default: ${options.bin.default}
`;

const badUsage = () => {
const reason = new Error(Usage);
reason.name = 'USAGE';
throw reason;
};

const { stringify: jq } = JSON;
// limited to JSON data: no remotables/promises; no undefined.
const toCapData = data => ({ body: `#${jq(data)}`, slots: [] });

const { entries } = Object;
/**
* @param {Record<string, string>} obj - e.g. { color: 'blue' }
* @returns {string[]} - e.g. ['--color', 'blue']
*/
const flags = obj =>
entries(obj)
.map(([k, v]) => [`--${k}`, v])
.flat();

const execP = promisify(execFile);

const showAndRun = (file, args) => {
console.log('$', file, ...args);
return execP(file, args);
};

const withTempFile = async (tail, fn) => {
const tmpDir = await mkdtemp('offers-');
const tmpFile = join(tmpDir, tail);
try {
const result = await fn(tmpFile);
return result;
} finally {
await rm(tmpDir, { recursive: true, force: true }).catch(err =>
console.error(err),
);
}
};

const doAction = async (action, from) => {
await withTempFile('offer.json', async tmpOffer => {
await writeFile(tmpOffer, jq(toCapData(action)));

const out = await showAndRun('agoric', [
'wallet',
...flags({ 'keyring-backend': 'test' }),
'send',
...flags({ offer: tmpOffer, from }),
]);
return out.stdout;
});
};

const main = async (argv, env) => {
const { values } = parseArgs({ args: argv.slice(2), options });
const { id: offerId, from, bin } = values;
(offerId && from) || badUsage();

env.PATH = `${bin}:${env.PATH}`;
const action = { method: 'tryExitOffer', offerId };
const out = await doAction(action, from);
console.log(out);
};

main(process.argv, process.env).catch(e => {
if (e.name === 'USAGE' || e.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') {
console.error(e.message);
} else {
console.error(e);
}
process.exit(1);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* global E */

/// <reference types="@agoric/vats/src/core/core-eval-env"/>
/// <reference types="@agoric/vats/src/core/types-ambient"/>

/**
* The primary purpose of this script is to access the depositFacet of a given address
* via the namesByAddress and then send a payment to it.
*
* The {{ADDRESS}} placeholder should be replaced with the desired address before use.
*
* The payment in this case is an invitation to add collateral to the reserve.
* However, the use of the reserve is incidental and simply provides an easy payment to construct.
*
* see a3p-integration/proposals/z:acceptance/wallet.test.js
*
* @param {BootstrapPowers} powers
*/
const sendIt = async powers => {
const addr = '{{ADDRESS}}';
const {
consume: { namesByAddress, zoe },
instance: {
consume: { reserve },
},
} = powers;
const pf = E(zoe).getPublicFacet(reserve);
const anInvitation = await E(pf).makeAddCollateralInvitation();
const addressDepositFacet = E(namesByAddress).lookup(addr, 'depositFacet');
await E(addressDepositFacet).receive(anInvitation);
};

sendIt;
49 changes: 49 additions & 0 deletions a3p-integration/proposals/z:acceptance/test-lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { makeAgd, agops } from '@agoric/synthetic-chain';
import { execFileSync } from 'node:child_process';
import { readFile, writeFile } from 'node:fs/promises';

/**
* @param {string} fileName base file name without .tjs extension
* @param {Record<string, string>} replacements
*/
export const replaceTemplateValuesInFile = async (fileName, replacements) => {
let script = await readFile(`${fileName}.tjs`, 'utf-8');
for (const [template, value] of Object.entries(replacements)) {
script = script.replaceAll(`{{${template}}}`, value);
}
await writeFile(`${fileName}.js`, script);
};

const showAndExec = (file, args, opts) => {
console.log('$', file, ...args);
return execFileSync(file, args, opts);
};

// @ts-expect-error string is not assignable to Buffer
export const agd = makeAgd({ execFileSync: showAndExec }).withOpts({
keyringBackend: 'test',
});

/**
* @param {string[]} addresses
* @param {string} [targetDenom]
*/
export const getBalances = async (addresses, targetDenom = undefined) => {
const balancesList = await Promise.all(
addresses.map(async address => {
const { balances } = await agd.query(['bank', 'balances', address]);

if (targetDenom) {
const balance = balances.find(({ denom }) => denom === targetDenom);
return balance ? BigInt(balance.amount) : undefined;
}

return balances;
}),
);

return addresses.length === 1 ? balancesList[0] : balancesList;
};

export const agopsVaults = async addr =>
await agops.vaults('list', '--from', addr);
6 changes: 6 additions & 0 deletions a3p-integration/proposals/z:acceptance/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ yarn ava valueVow.test.js
echo ACCEPTANCE TESTING state sync
./state-sync-snapshots-test.sh
./genesis-test.sh

echo ACCEPTANCE TESTING wallet
yarn ava wallet.test.js

echo ACCEPTANCE TESTING vaults
yarn ava vaults.test.js
Loading

0 comments on commit feae722

Please sign in to comment.