mirror of https://github.com/status-im/nimplay.git
Add basic test layout.
This commit is contained in:
parent
5de34f913c
commit
0a3990465c
10
Makefile
10
Makefile
|
@ -81,8 +81,16 @@ ee-examples:
|
|||
$(WASM32_NIMC) --out:examples/ee/helloworld.wasm examples/ee/helloworld.nim
|
||||
$(WASM32_NIMC) --out:examples/ee/block_echo.wasm examples/ee/block_echo.nim
|
||||
|
||||
|
||||
.PHONY: test-ee
|
||||
test-ee: ee-examples
|
||||
cd tests/ee/; \
|
||||
./test.sh
|
||||
|
||||
.PHONY: substrate-examples
|
||||
substrate-examples:
|
||||
$(WASM32_NIMC) --out:examples/substrate/hello_world.wasm examples/substrate/hello_world.nim
|
||||
|
||||
.PHONY: test-substrate
|
||||
test-substrate: substrate-examples
|
||||
cd tests/substrate; \
|
||||
SUBSTRATE_PATH="${HOME}/.cargo/bin/substrate" ./test.sh
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
import utils
|
||||
import macros
|
||||
|
||||
{.push cdecl, importc.}
|
||||
|
||||
proc ext_address*()
|
||||
proc ext_block_number*()
|
||||
proc ext_gas_left*()
|
||||
proc ext_gas_price*()
|
||||
proc ext_get_storage*(key_ptr: pointer): int32
|
||||
proc ext_println*(str_ptr: pointer, str_len: int32)
|
||||
proc ext_now*()
|
||||
proc ext_println*(str_ptr: pointer, str_len: int32) # experimental; will be removed.
|
||||
proc ext_random_seed*()
|
||||
proc ext_scratch_read*(dest_ptr: pointer, offset: int32, len: int32)
|
||||
proc ext_scratch_size*(): int32
|
||||
proc ext_scratch_write(src_ptr: pointer, len: int32)
|
||||
proc ext_scratch_write*(src_ptr: pointer, len: int32)
|
||||
proc ext_set_rent_allowance*(value_ptr: pointer, value_len: int32)
|
||||
proc ext_set_storage*(key_ptr: pointer, value_non_null: int32, value_ptr: int32, value_len: int32)
|
||||
proc ext_value_transferred*()
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ -z "$SUBSTRATE_PATH" ]; then
|
||||
echo "Please specify the path to substrate in the SUBSTRATE_PATH environment variable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$SUBSTRATE_PATH" ]; then
|
||||
echo "$SUBSTRATE_PATH doesn't exist"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Purge dev chain and then spin up the substrate node in background
|
||||
$SUBSTRATE_PATH purge-chain --dev -y
|
||||
$SUBSTRATE_PATH --dev &
|
||||
SUBSTRATE_PID=$!
|
||||
|
||||
# # Execute tests
|
||||
yarn && yarn test --verbose
|
||||
|
||||
# # Kill the spawned substrate node
|
||||
kill -9 $SUBSTRATE_PID
|
|
@ -0,0 +1,9 @@
|
|||
import BN from "bn.js";
|
||||
|
||||
export const WSURL = "ws://127.0.0.1:9944";
|
||||
export const DOT: BN = new BN("1000000000000000");
|
||||
export const CREATION_FEE: BN = DOT.muln(200);
|
||||
export const GAS_REQUIRED = 50000;
|
||||
export const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
|
||||
export const BOB = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// Adapted from https://github.com/paritytech/srml-contracts-waterfall/
|
||||
|
||||
import { ApiPromise, SubmittableResult, WsProvider } from "@polkadot/api";
|
||||
import { Abi } from '@polkadot/api-contract';
|
||||
import testKeyring from "@polkadot/keyring/testing";
|
||||
import { u8aToHex } from "@polkadot/util";
|
||||
import { randomAsU8a } from "@polkadot/util-crypto";
|
||||
import { KeyringPair } from "@polkadot/keyring/types";
|
||||
import { Option } from "@polkadot/types";
|
||||
import { Address, ContractInfo, Hash } from "@polkadot/types/interfaces";
|
||||
|
||||
import { ALICE, CREATION_FEE, WSURL } from "./consts";
|
||||
import {
|
||||
callContract,
|
||||
instantiate,
|
||||
getContractStorage,
|
||||
putCode
|
||||
} from "./utils";
|
||||
|
||||
// This is a test account that is going to be created and funded each test.
|
||||
const keyring = testKeyring({ type: "sr25519" });
|
||||
const alicePair = keyring.getPair(ALICE);
|
||||
let testAccount: KeyringPair;
|
||||
let api: ApiPromise;
|
||||
|
||||
beforeAll((): void => {
|
||||
jest.setTimeout(30000);
|
||||
});
|
||||
|
||||
beforeEach(
|
||||
async (done): Promise<() => void> => {
|
||||
api = await ApiPromise.create({ provider: new WsProvider(WSURL) });
|
||||
testAccount = keyring.addFromSeed(randomAsU8a(32));
|
||||
|
||||
return api.tx.balances
|
||||
.transfer(testAccount.address, CREATION_FEE.muln(3))
|
||||
.signAndSend(alicePair, (result: SubmittableResult): void => {
|
||||
if (
|
||||
result.status.isFinalized &&
|
||||
result.findRecord("system", "ExtrinsicSuccess")
|
||||
) {
|
||||
console.log("New test account has been created.");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
describe("Nimplay Hello World", () => {
|
||||
test("Raw Flipper contract", async (done): Promise<void> => {
|
||||
// See https://github.com/paritytech/srml-contracts-waterfall/issues/6 for info about
|
||||
// how to get the STORAGE_KEY of an instantiated contract
|
||||
|
||||
const STORAGE_KEY = (new Uint8Array(32)).fill(2);
|
||||
// Deploy contract code on chain and retrieve the code hash
|
||||
const codeHash = await putCode(
|
||||
api,
|
||||
testAccount,
|
||||
"../../../examples/substrate/hello_world.wasm"
|
||||
);
|
||||
expect(codeHash).toBeDefined();
|
||||
|
||||
// Instantiate a new contract instance and retrieve the contracts address
|
||||
// Call contract with Action: 0x00 = Action::Flip()
|
||||
const address: Address = await instantiate(
|
||||
api,
|
||||
testAccount,
|
||||
codeHash,
|
||||
"0x00",
|
||||
CREATION_FEE
|
||||
);
|
||||
expect(address).toBeDefined();
|
||||
|
||||
const initialValue: Uint8Array = await getContractStorage(
|
||||
api,
|
||||
address,
|
||||
STORAGE_KEY
|
||||
);
|
||||
expect(initialValue).toBeDefined();
|
||||
expect(initialValue.toString()).toEqual("0x00");
|
||||
|
||||
await callContract(api, testAccount, address, "0x00");
|
||||
|
||||
const newValue = await getContractStorage(api, address, STORAGE_KEY);
|
||||
expect(newValue.toString()).toEqual("0x01");
|
||||
|
||||
await callContract(api, testAccount, address, "0x00");
|
||||
|
||||
const flipBack = await getContractStorage(api, address, STORAGE_KEY);
|
||||
expect(flipBack.toString()).toEqual("0x00");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
});
|
|
@ -0,0 +1,109 @@
|
|||
import { ApiPromise, SubmittableResult } from "@polkadot/api";
|
||||
import { KeyringPair } from "@polkadot/keyring/types";
|
||||
import { Option, StorageData } from "@polkadot/types";
|
||||
import { Address, ContractInfo, Hash } from "@polkadot/types/interfaces";
|
||||
import BN from "bn.js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
const blake = require('blakejs')
|
||||
|
||||
import { GAS_REQUIRED } from "./consts";
|
||||
|
||||
export async function sendAndReturnFinalized(signer: KeyringPair, tx: any) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
tx.signAndSend(signer, (result: SubmittableResult) => {
|
||||
if (result.status.isFinalized) {
|
||||
// Return result of the submittable extrinsic after the transfer is finalized
|
||||
resolve(result as SubmittableResult);
|
||||
}
|
||||
if (
|
||||
result.status.isDropped ||
|
||||
result.status.isInvalid ||
|
||||
result.status.isUsurped
|
||||
) {
|
||||
reject(result as SubmittableResult);
|
||||
console.error("ERROR: Transaction could not be finalized.");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function putCode(
|
||||
api: ApiPromise,
|
||||
signer: KeyringPair,
|
||||
fileName: string,
|
||||
gasRequired: number = GAS_REQUIRED
|
||||
): Promise<Hash> {
|
||||
const wasmCode = fs
|
||||
.readFileSync(path.join(__dirname, fileName))
|
||||
.toString("hex");
|
||||
const tx = api.tx.contracts.putCode(gasRequired, `0x${wasmCode}`);
|
||||
const result: any = await sendAndReturnFinalized(signer, tx);
|
||||
console.log('result', result)
|
||||
const record = result.findRecord("contracts", "CodeStored");
|
||||
|
||||
if (!record) {
|
||||
console.error("ERROR: No code stored after executing putCode()");
|
||||
}
|
||||
// Return code hash.
|
||||
console.log(record);
|
||||
return record.event.data[0];
|
||||
}
|
||||
|
||||
export async function instantiate(
|
||||
api: ApiPromise,
|
||||
signer: KeyringPair,
|
||||
codeHash: Hash,
|
||||
inputData: any,
|
||||
endowment: BN,
|
||||
gasRequired: number = GAS_REQUIRED
|
||||
): Promise<Address> {
|
||||
const tx = api.tx.contracts.instantiate(
|
||||
endowment,
|
||||
gasRequired,
|
||||
codeHash,
|
||||
inputData
|
||||
);
|
||||
const result: any = await sendAndReturnFinalized(signer, tx);
|
||||
const record = result.findRecord("contracts", "Instantiated");
|
||||
|
||||
if (!record) {
|
||||
console.error("ERROR: No new instantiated contract");
|
||||
}
|
||||
// Return the address of instantiated contract.
|
||||
return record.event.data[1];
|
||||
}
|
||||
|
||||
export async function callContract(
|
||||
api: ApiPromise,
|
||||
signer: KeyringPair,
|
||||
contractAddress: Address,
|
||||
inputData: any,
|
||||
gasRequired: number = GAS_REQUIRED,
|
||||
endowment: number = 0
|
||||
): Promise<void> {
|
||||
const tx = api.tx.contracts.call(
|
||||
contractAddress,
|
||||
endowment,
|
||||
gasRequired,
|
||||
inputData
|
||||
);
|
||||
await sendAndReturnFinalized(signer, tx);
|
||||
}
|
||||
|
||||
export async function getContractStorage(
|
||||
api: ApiPromise,
|
||||
contractAddress: Address,
|
||||
storageKey: Uint8Array
|
||||
): Promise<StorageData> {
|
||||
const contractInfo = await api.query.contracts.contractInfoOf(
|
||||
contractAddress
|
||||
);
|
||||
// Return the value of the contracts storage
|
||||
const storageKeyBlake2b = blake.blake2bHex(storageKey, null, 32);
|
||||
return await api.rpc.state.getChildStorage(
|
||||
(contractInfo as Option<ContractInfo>).unwrap().asAlive.trieId,
|
||||
'0x' + storageKeyBlake2b
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "commonjs",
|
||||
"lib": ["dom", "es2018"],
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": [
|
||||
"contracts/rust",
|
||||
"lib",
|
||||
"node_modules",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import BN from "bn.js";
|
||||
|
||||
export const WSURL = "ws://127.0.0.1:9944";
|
||||
export const DOT: BN = new BN("1000000000000000");
|
||||
export const CREATION_FEE: BN = DOT.muln(200);
|
||||
export const GAS_REQUIRED = 50000;
|
||||
export const ALICE = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
|
||||
export const BOB = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
|
||||
|
Loading…
Reference in New Issue