Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add CLI test to canary flow #1918

Merged
merged 7 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,18 @@ jobs:
- *setup_env
- run:
name: "Test"
command: spot_run_test_script ./scripts/run_tests canary aztec_js_browser.test.test.ts canary docker-compose.yml
command: spot_run_test_script ./scripts/run_tests canary aztec_js_browser.test.ts canary docker-compose.yml

run-deployment-canary-cli:
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: spot_run_test_script ./scripts/run_tests canary cli.test.ts canary docker-compose.yml

# Repeatable config for defining the workflow below.
tag_regex: &tag_regex /^v.*/
Expand Down Expand Up @@ -1403,7 +1414,6 @@ workflows:
- deploy-dockerhub:
requires:
- e2e-end
- aztec-sandbox
<<: *deploy_defaults
- deploy-npm:
requires:
Expand Down Expand Up @@ -1431,3 +1441,9 @@ workflows:
- build-deployment-canary
<<: *deploy_defaults

- run-deployment-canary-cli:
requires:
- build-deployment-canary
<<: *deploy_defaults


2 changes: 2 additions & 0 deletions build_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@
],
"dependencies": [
"aztec.js",
"cli",
"foundation",
"l1-artifacts",
"noir-contracts"
]
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"@types/node": "^18.7.23",
"jest": "^29.5.0",
"jest-mock-extended": "^3.0.5",
"string-argv": "^0.3.2",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/aztec-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
prepTx,
} from './utils.js';

export { cliTestSuite } from './test/cli_test_suite.js';

const accountCreationSalt = Fr.ZERO;

const stripLeadingHex = (hex: string) => {
Expand Down
175 changes: 175 additions & 0 deletions yarn-project/aztec-cli/src/test/cli_test_suite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { AztecAddress } from '@aztec/aztec.js';
import { DebugLogger } from '@aztec/foundation/log';
import { AztecRPC, CompleteAddress } from '@aztec/types';

import stringArgv from 'string-argv';
import { format } from 'util';

import { getProgram } from '../index.js';

const INITIAL_BALANCE = 33000;
const TRANSFER_BALANCE = 3000;

export const cliTestSuite = (
testName: string,
aztecRpcSetup: () => Promise<AztecRPC>,
cleanup: () => Promise<void>,
rpcUrl: string,
debug: DebugLogger,
) => {
return describe(testName, () => {
let cli: ReturnType<typeof getProgram>;
let aztecRpcClient: AztecRPC;
let existingAccounts: CompleteAddress[];
let contractAddress: AztecAddress;
let log: (...args: any[]) => void;

// All logs emitted by the cli will be collected here, and reset between tests
const logs: string[] = [];

beforeAll(async () => {
aztecRpcClient = await aztecRpcSetup();
log = (...args: any[]) => {
logs.push(format(...args));
debug(...args);
};
});

afterAll(async () => {
await cleanup();
});

// in order to run the same command twice, we need to create a new CLI instance
const resetCli = () => {
cli = getProgram(log, debug);
};

beforeEach(() => {
logs.splice(0);
resetCli();
});

// Run a command on the CLI
const run = (cmd: string, addRpcUrl = true) => {
const args = stringArgv(cmd, 'node', 'dest/bin/index.js');
if (addRpcUrl) {
args.push('--rpc-url', rpcUrl);
}
return cli.parseAsync(args);
};

// Returns first match across all logs collected so far
const findInLogs = (regex: RegExp) => {
for (const log of logs) {
const match = regex.exec(log);
if (match) return match;
}
};

const findMultipleInLogs = (regex: RegExp) => {
const matches = [];
for (const log of logs) {
const match = regex.exec(log);
if (match) matches.push(match);
}
return matches;
};

const clearLogs = () => {
logs.splice(0);
};

it('creates & retrieves an account', async () => {
existingAccounts = await aztecRpcClient.getAccounts();
debug('Create an account');
await run(`create-account`);
const foundAddress = findInLogs(/Address:\s+(?<address>0x[a-fA-F0-9]+)/)?.groups?.address;
expect(foundAddress).toBeDefined();
const newAddress = AztecAddress.fromString(foundAddress!);

const accountsAfter = await aztecRpcClient.getAccounts();
const expectedAccounts = [...existingAccounts.map(a => a.address), newAddress];
expect(accountsAfter.map(a => a.address)).toEqual(expectedAccounts);
const newCompleteAddress = accountsAfter[accountsAfter.length - 1];

// Test get-accounts
debug('Check that account was added to the list of accs in RPC');
await run('get-accounts');
const fetchedAddresses = findMultipleInLogs(/Address:\s+(?<address>0x[a-fA-F0-9]+)/);
const foundFetchedAddress = fetchedAddresses.find(match => match.groups?.address === newAddress.toString());
expect(foundFetchedAddress).toBeDefined();

// Test get-account
debug('Check we can retrieve the specific account');
clearLogs();
await run(`get-account ${newAddress.toString()}`);
const fetchedAddress = findInLogs(/Public Key:\s+(?<address>0x[a-fA-F0-9]+)/)?.groups?.address;
expect(fetchedAddress).toEqual(newCompleteAddress.publicKey.toString());
});

it('deploys a contract & sends transactions', async () => {
// generate a private key
debug('Create an account using a private key');
await run('generate-private-key', false);
const privKey = findInLogs(/Private\sKey:\s+(?<privKey>[a-fA-F0-9]+)/)?.groups?.privKey;
expect(privKey).toHaveLength(64);
await run(`create-account --private-key ${privKey}`);
const foundAddress = findInLogs(/Address:\s+(?<address>0x[a-fA-F0-9]+)/)?.groups?.address;
expect(foundAddress).toBeDefined();
const ownerAddress = AztecAddress.fromString(foundAddress!);

debug('Deploy Private Token Contract using created account.');
await run(`deploy PrivateTokenContractAbi --args ${INITIAL_BALANCE} ${ownerAddress} --salt 0`);
const loggedAddress = findInLogs(/Contract\sdeployed\sat\s+(?<address>0x[a-fA-F0-9]+)/)?.groups?.address;
expect(loggedAddress).toBeDefined();
contractAddress = AztecAddress.fromString(loggedAddress!);

const deployedContract = await aztecRpcClient.getContractData(contractAddress);
expect(deployedContract?.contractAddress).toEqual(contractAddress);

debug('Check contract can be found in returned address');
await run(`check-deploy -ca ${loggedAddress}`);
const checkResult = findInLogs(/Contract\sfound\sat\s+(?<address>0x[a-fA-F0-9]+)/)?.groups?.address;
expect(checkResult).toEqual(deployedContract?.contractAddress.toString());

// clear logs
clearLogs();
await run(`get-contract-data ${loggedAddress}`);
const contractDataAddress = findInLogs(/Address:\s+(?<address>0x[a-fA-F0-9]+)/)?.groups?.address;
expect(contractDataAddress).toEqual(deployedContract?.contractAddress.toString());

debug("Check owner's balance");
await run(
`call getBalance --args ${ownerAddress} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`,
);
const balance = findInLogs(/View\sresult:\s+(?<data>\S+)/)?.groups?.data;
expect(balance!).toEqual(`${BigInt(INITIAL_BALANCE).toString()}n`);

debug('Transfer some tokens');
const existingAccounts = await aztecRpcClient.getAccounts();
// ensure we pick a different acc
const receiver = existingAccounts.find(acc => acc.address.toString() !== ownerAddress.toString());

await run(
`send transfer --args ${TRANSFER_BALANCE} ${receiver?.address.toString()} --contract-address ${contractAddress.toString()} --contract-abi PrivateTokenContractAbi --private-key ${privKey}`,
);
const txHash = findInLogs(/Transaction\shash:\s+(?<txHash>\S+)/)?.groups?.txHash;

debug('Check the transfer receipt');
await run(`get-tx-receipt ${txHash}`);
const txResult = findInLogs(/Transaction receipt:\s*(?<txHash>[\s\S]*?\})/)?.groups?.txHash;
const parsedResult = JSON.parse(txResult!);
expect(parsedResult.txHash).toEqual(txHash);
expect(parsedResult.status).toEqual('mined');
debug("Check Receiver's balance");
// Reset CLI as we're calling getBalance again
resetCli();
clearLogs();
await run(
`call getBalance --args ${receiver?.address.toString()} --contract-abi PrivateTokenContractAbi --contract-address ${contractAddress.toString()}`,
);
const receiverBalance = findInLogs(/View\sresult:\s+(?<data>\S+)/)?.groups?.data;
expect(receiverBalance).toEqual(`${BigInt(TRANSFER_BALANCE).toString()}n`);
});
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@aztec/types';

export const aztecRpcTestSuite = (testName: string, aztecRpcSetup: () => Promise<AztecRPC>) => {
describe(testName, function () {
describe(testName, () => {
let rpc: AztecRPC;

beforeAll(async () => {
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/canary/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
},
"dependencies": {
"@aztec/aztec.js": "workspace:^",
"@aztec/cli": "workspace:^",
"@aztec/foundation": "workspace:^",
"@aztec/l1-artifacts": "workspace:^",
"@aztec/noir-contracts": "workspace:^",
"@jest/globals": "^29.5.0",
Expand All @@ -32,6 +34,7 @@
"koa": "^2.14.2",
"koa-static": "^5.0.0",
"puppeteer": "^21.1.0",
"string-argv": "^0.3.2",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",
Expand Down
13 changes: 13 additions & 0 deletions yarn-project/canary/src/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createAztecRpcClient, createDebugLogger, makeFetch } from '@aztec/aztec.js';
import { cliTestSuite } from '@aztec/cli';

const { SANDBOX_URL = 'http://localhost:8080' } = process.env;

const debug = createDebugLogger('aztec:e2e_cli');

const setupRPC = () => {
const aztecRpcClient = createAztecRpcClient(SANDBOX_URL, makeFetch([1, 2, 3], true));
return Promise.resolve(aztecRpcClient);
};

cliTestSuite('CLI canary', setupRPC, () => Promise.resolve(), SANDBOX_URL, debug);
6 changes: 6 additions & 0 deletions yarn-project/canary/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
{
"path": "../aztec.js"
},
{
"path": "../aztec-cli"
},
{
"path": "../foundation"
},
{
"path": "../l1-artifacts"
},
Expand Down
Loading