Refactored provider test cases to more reliable CI.
This commit is contained in:
parent
86e0269a86
commit
de8a717b4c
@ -8,8 +8,6 @@ import { ethers } from "ethers";
|
|||||||
|
|
||||||
const bnify = ethers.BigNumber.from;
|
const bnify = ethers.BigNumber.from;
|
||||||
|
|
||||||
type Dictionary = { [ key: string ]: any };
|
|
||||||
|
|
||||||
type TestCases = {
|
type TestCases = {
|
||||||
addresses: Array<any>;
|
addresses: Array<any>;
|
||||||
blocks: Array<any>;
|
blocks: Array<any>;
|
||||||
@ -70,7 +68,7 @@ const blockchainData: { [ network: string ]: TestCases } = {
|
|||||||
s: "0x269c3e5b3558267ad91b0a887d51f9f10098771c67b82ea6cb74f29638754f54",
|
s: "0x269c3e5b3558267ad91b0a887d51f9f10098771c67b82ea6cb74f29638754f54",
|
||||||
v: 38,
|
v: 38,
|
||||||
creates: null,
|
creates: null,
|
||||||
raw: "0xf8d2808504a817c8008303d090946fc21092da55b392b045ed78f4732bff3c580e2c880186cc6acd4b0000b864f2c298be000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000067269636d6f6f000000000000000000000000000000000000000000000000000026a01e5605197a03e3f0a168f14749168dfeefc44c9228312dacbffdcbbb13263265a0269c3e5b3558267ad91b0a887d51f9f10098771c67b82ea6cb74f29638754f54",
|
//raw: "0xf8d2808504a817c8008303d090946fc21092da55b392b045ed78f4732bff3c580e2c880186cc6acd4b0000b864f2c298be000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000067269636d6f6f000000000000000000000000000000000000000000000000000026a01e5605197a03e3f0a168f14749168dfeefc44c9228312dacbffdcbbb13263265a0269c3e5b3558267ad91b0a887d51f9f10098771c67b82ea6cb74f29638754f54",
|
||||||
chainId: 1
|
chainId: 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -375,6 +373,8 @@ blockchainData["default"] = blockchainData.homestead;
|
|||||||
function equals(name: string, actual: any, expected: any): void {
|
function equals(name: string, actual: any, expected: any): void {
|
||||||
if (expected && expected.eq) {
|
if (expected && expected.eq) {
|
||||||
if (actual == null) { assert.ok(false, name + " - actual big number null"); }
|
if (actual == null) { assert.ok(false, name + " - actual big number null"); }
|
||||||
|
expected = ethers.BigNumber.from(expected);
|
||||||
|
actual = ethers.BigNumber.from(actual);
|
||||||
assert.ok(expected.eq(actual), name + " matches");
|
assert.ok(expected.eq(actual), name + " matches");
|
||||||
|
|
||||||
} else if (Array.isArray(expected)) {
|
} else if (Array.isArray(expected)) {
|
||||||
@ -383,6 +383,7 @@ function equals(name: string, actual: any, expected: any): void {
|
|||||||
for (let i = 0; i < expected.length; i++) {
|
for (let i = 0; i < expected.length; i++) {
|
||||||
equals("(" + name + " - item " + i + ")", actual[i], expected[i]);
|
equals("(" + name + " - item " + i + ")", actual[i], expected[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (typeof(expected) === "object") {
|
} else if (typeof(expected) === "object") {
|
||||||
if (actual == null) {
|
if (actual == null) {
|
||||||
if (expected === actual) { return; }
|
if (expected === actual) { return; }
|
||||||
@ -405,298 +406,371 @@ function equals(name: string, actual: any, expected: any): void {
|
|||||||
|
|
||||||
function waiter(duration: number): Promise<void> {
|
function waiter(duration: number): Promise<void> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(resolve, duration);
|
const timer = setTimeout(resolve, duration);
|
||||||
|
if (timer.unref) { timer.unref(); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testProvider(providerName: string, networkName: string) {
|
|
||||||
|
|
||||||
// Delay (ms) after each test case to prevent the backends from throttling
|
type ProviderDescription = {
|
||||||
const delay = 1000;
|
name: string;
|
||||||
|
networks: Array<string>;
|
||||||
|
create: (network: string) => ethers.providers.Provider;
|
||||||
|
};
|
||||||
|
|
||||||
describe(("Read-Only " + providerName + " (" + networkName + ")"), function() {
|
type CheckSkipFunc = (provider: string, network: string, test: TestDescription) => boolean;
|
||||||
this.retries(3);
|
|
||||||
|
|
||||||
// Get the Provider based on the name of the provider we are testing and the network
|
type TestDescription = {
|
||||||
let provider: ethers.providers.Provider = null;
|
name: string;
|
||||||
if (networkName === "default") {
|
networks: Array<string>;
|
||||||
if (providerName === "getDefaultProvider") {
|
execute: (provider: ethers.providers.Provider) => Promise<void>;
|
||||||
provider = ethers.getDefaultProvider();
|
|
||||||
} else {
|
attempts?: number;
|
||||||
provider = new (<any>(ethers.providers))[providerName]();
|
timeout?: number;
|
||||||
|
extras?: Array<"nowait" | "funding">;
|
||||||
|
checkSkip?: CheckSkipFunc;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const allNetworks = [ "default", "homestead", "ropsten", "rinkeby", "kovan", "goerli" ];
|
||||||
|
|
||||||
|
const providerFunctions: Array<ProviderDescription> = [
|
||||||
|
{
|
||||||
|
name: "getDefaultProvider",
|
||||||
|
networks: allNetworks,
|
||||||
|
create: (network: string) => {
|
||||||
|
if (network == "default") {
|
||||||
|
return ethers.getDefaultProvider();
|
||||||
}
|
}
|
||||||
} else {
|
return ethers.getDefaultProvider(network);
|
||||||
if (providerName === "getDefaultProvider") {
|
}
|
||||||
provider = ethers.getDefaultProvider(networkName);
|
},
|
||||||
} else {
|
{
|
||||||
provider = new (<any>(ethers.providers))[providerName](networkName);
|
name: "AlchemyProvider",
|
||||||
|
networks: allNetworks,
|
||||||
|
create: (network: string) => {
|
||||||
|
if (network == "default") {
|
||||||
|
return new ethers.providers.AlchemyProvider();
|
||||||
}
|
}
|
||||||
|
return new ethers.providers.AlchemyProvider(network);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CloudflareProvider",
|
||||||
|
networks: [ "homestead" ],
|
||||||
|
create: (network: string) => {
|
||||||
|
return new ethers.providers.AlchemyProvider(network);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "InfuraProvider",
|
||||||
|
networks: allNetworks,
|
||||||
|
create: (network: string) => {
|
||||||
|
if (network == "default") {
|
||||||
|
return new ethers.providers.InfuraProvider();
|
||||||
|
}
|
||||||
|
return new ethers.providers.InfuraProvider(network);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "EtherscanProvider",
|
||||||
|
networks: allNetworks,
|
||||||
|
create: (network: string) => {
|
||||||
|
if (network == "default") {
|
||||||
|
return new ethers.providers.EtherscanProvider();
|
||||||
|
}
|
||||||
|
return new ethers.providers.EtherscanProvider(network);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NodesmithProvider",
|
||||||
|
networks: [ ],
|
||||||
|
create: (network: string) => {
|
||||||
|
throw new Error("not tested");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Web3Provider",
|
||||||
|
networks: [ ],
|
||||||
|
create: (network: string) => {
|
||||||
|
throw new Error("not tested");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// This wallet can be funded and used for various test cases
|
||||||
|
const fundWallet = ethers.Wallet.createRandom();
|
||||||
|
|
||||||
|
|
||||||
|
const testFunctions: Array<TestDescription> = [ ];
|
||||||
|
|
||||||
|
Object.keys(blockchainData).forEach((network) => {
|
||||||
|
function addSimpleTest(name: string, func: (provider: ethers.providers.Provider) => Promise<any>, expected: any) {
|
||||||
|
testFunctions.push({
|
||||||
|
name: name,
|
||||||
|
networks: [ network ],
|
||||||
|
execute: async (provider: ethers.providers.Provider) => {
|
||||||
|
const value = await func(provider);
|
||||||
|
equals(name, expected, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addObjectTest(name: string, func: (provider: ethers.providers.Provider) => Promise<any>, expected: any, checkSkip?: CheckSkipFunc) {
|
||||||
|
testFunctions.push({
|
||||||
|
name,
|
||||||
|
networks: [ network ],
|
||||||
|
checkSkip,
|
||||||
|
execute: async (provider: ethers.providers.Provider) => {
|
||||||
|
const value = await func(provider);
|
||||||
|
Object.keys(expected).forEach((key) => {
|
||||||
|
equals(`${ name }.${ key }`, value[key], expected[key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const tests: TestCases = blockchainData[network];
|
||||||
|
|
||||||
|
// And address test case can have any of the following:
|
||||||
|
// - balance
|
||||||
|
// - code
|
||||||
|
// - storage
|
||||||
|
// - ENS name
|
||||||
|
tests.addresses.forEach((test) => {
|
||||||
|
if (test.balance) {
|
||||||
|
addSimpleTest(`fetches account balance: ${ test.address }`, (provider: ethers.providers.Provider) => {
|
||||||
|
return provider.getBalance(test.address);
|
||||||
|
}, test.balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tests: TestCases = blockchainData[networkName];
|
if (test.code) {
|
||||||
|
addSimpleTest(`fetches account code: ${ test.address }`, (provider: ethers.providers.Provider) => {
|
||||||
|
return provider.getCode(test.address);
|
||||||
|
}, test.code);
|
||||||
|
}
|
||||||
|
|
||||||
// And address test case can have any of the following:
|
if (test.storage) {
|
||||||
// - balance
|
Object.keys(test.storage).forEach((position) => {
|
||||||
// - code
|
addSimpleTest(`fetches storage: ${ test.address }:${ position }`, (provider: ethers.providers.Provider) => {
|
||||||
// - storage
|
return provider.getStorageAt(test.address, bnify(position));
|
||||||
// - ENS name
|
}, test.storage[position]);
|
||||||
tests.addresses.forEach((test) => {
|
|
||||||
if (test.balance) {
|
|
||||||
it(`fetches address balance: ${ test.address }`, function() {
|
|
||||||
// Note: These tests could be fiddled with if someone sends ether
|
|
||||||
// to our address; we just have to live with jerks sending us
|
|
||||||
// money. *smile emoji*
|
|
||||||
|
|
||||||
this.timeout(60000);
|
|
||||||
return provider.getBalance(test.address).then((balance) => {
|
|
||||||
equals("Balance", test.balance, balance);
|
|
||||||
return waiter(delay);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test.code) {
|
|
||||||
it(`fetches address code: ${ test.address }`, function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
return provider.getCode(test.address).then((code) => {
|
|
||||||
equals("Code", test.code, code);
|
|
||||||
return waiter(delay);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test.storage) {
|
|
||||||
Object.keys(test.storage).forEach((position) => {
|
|
||||||
it(`fetches storage: ${ test.address }:${ position }`, function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
return provider.getStorageAt(test.address, bnify(position)).then((value) => {
|
|
||||||
equals("Storage", test.storage[position], value);
|
|
||||||
return waiter(delay);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test.name) {
|
|
||||||
it(`fetches the ENS name: ${ test.name }`, function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
return provider.resolveName(test.name).then((address) => {
|
|
||||||
equals("ENS Name", test.address, address);
|
|
||||||
return waiter(delay);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tests.blocks.forEach((test) => {
|
|
||||||
function checkBlock(promise: Promise<any>): Promise<any> {
|
|
||||||
return promise.then((block) => {
|
|
||||||
for (let key in test) {
|
|
||||||
equals("Block " + key, block[key], test[key]);
|
|
||||||
}
|
|
||||||
return waiter(delay);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it(`fetches block (by number) #${ test.number }`, function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
return checkBlock(provider.getBlock(test.number));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Etherscan does not support getBlockByBlockhash... *sad emoji*
|
|
||||||
if (providerName === "EtherscanProvider") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
it(`fetches block (by hash) ${ test.hash }`, function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
return checkBlock(provider.getBlock(test.hash));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
tests.transactions.forEach((test) => {
|
|
||||||
function testTransaction(expected: Dictionary): Promise<void> {
|
|
||||||
const title = ("Transaction " + expected.hash.substring(0, 10) + " - ");
|
|
||||||
return provider.getTransaction(expected.hash).then((tx) => {
|
|
||||||
|
|
||||||
// This changes with every block
|
|
||||||
assert.equal(typeof(tx.confirmations), "number", "confirmations is a number");
|
|
||||||
delete tx.confirmations;
|
|
||||||
|
|
||||||
assert.equal(typeof(tx.wait), "function", "wait is a function");
|
|
||||||
delete tx.wait
|
|
||||||
|
|
||||||
for (const key in tx) {
|
|
||||||
equals((title + key), (<any>tx)[key], expected[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return waiter(delay);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it(`fetches transaction: ${ test.hash }`, function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
return testTransaction(test);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
tests.transactionReceipts.forEach((test) => {
|
|
||||||
function testTransactionReceipt(expected: Dictionary): Promise<void> {
|
|
||||||
const title = ("Receipt " + expected.transactionHash.substring(0, 10) + " - ");
|
|
||||||
return provider.getTransactionReceipt(expected.transactionHash).then(function(receipt) {
|
|
||||||
|
|
||||||
// This changes with every block; so just make sure it is a number
|
|
||||||
assert.equal(typeof(receipt.confirmations), "number", "confirmations is a number");
|
|
||||||
delete receipt.confirmations;
|
|
||||||
|
|
||||||
for (const key in receipt) {
|
|
||||||
equals((title + key), (<any>receipt)[key], expected[key]);
|
|
||||||
}
|
|
||||||
//equals(("Receipt " + expected.transactionHash.substring(0, 10)), receipt, expected);
|
|
||||||
|
|
||||||
return waiter(delay);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it(`fetches transaction receipt: ${ test.transactionHash }`, function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
return testTransactionReceipt(test);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (networkName === "ropsten") {
|
|
||||||
|
|
||||||
it("throws correct NONCE_EXPIRED errors", async function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const tx = await provider.sendTransaction("0xf86480850218711a0082520894000000000000000000000000000000000000000002801ba038aaddcaaae7d3fa066dfd6f196c8348e1bb210f2c121d36cb2c24ef20cea1fba008ae378075d3cd75aae99ab75a70da82161dffb2c8263dabc5d8adecfa9447fa");
|
|
||||||
console.log(tx);
|
|
||||||
assert.ok(false);
|
|
||||||
} catch (error) {
|
|
||||||
assert.equal(error.code, ethers.utils.Logger.errors.NONCE_EXPIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
await waiter(delay);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws correct INSUFFICIENT_FUNDS errors", async function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
|
|
||||||
const txProps = {
|
|
||||||
to: "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
|
|
||||||
gasPrice: 9000000000,
|
|
||||||
gasLimit: 21000,
|
|
||||||
value: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const wallet = ethers.Wallet.createRandom();
|
|
||||||
const tx = await wallet.signTransaction(txProps);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await provider.sendTransaction(tx);
|
|
||||||
assert.ok(false);
|
|
||||||
} catch (error) {
|
|
||||||
assert.equal(error.code, ethers.utils.Logger.errors.INSUFFICIENT_FUNDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
await waiter(delay);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws correct INSUFFICIENT_FUNDS errors (signer)", async function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
|
|
||||||
const txProps = {
|
|
||||||
to: "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
|
|
||||||
gasPrice: 9000000000,
|
|
||||||
gasLimit: 21000,
|
|
||||||
value: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const wallet = ethers.Wallet.createRandom().connect(provider);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await wallet.sendTransaction(txProps);
|
|
||||||
assert.ok(false);
|
|
||||||
} catch (error) {
|
|
||||||
assert.equal(error.code, ethers.utils.Logger.errors.INSUFFICIENT_FUNDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
await waiter(delay);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws correct UNPREDICTABLE_GAS_LIMIT errors", async function() {
|
|
||||||
this.timeout(60000);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await provider.estimateGas({
|
|
||||||
to: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e" // ENS; no payable fallback
|
|
||||||
});
|
|
||||||
assert.ok(false);
|
|
||||||
} catch (error) {
|
|
||||||
assert.equal(error.code, ethers.utils.Logger.errors.UNPREDICTABLE_GAS_LIMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
await waiter(delay);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sends a transaction", async function() {
|
|
||||||
this.timeout(360000);
|
|
||||||
|
|
||||||
const wallet = ethers.Wallet.createRandom().connect(provider);
|
|
||||||
const funder = await ethers.utils.fetchJson(`https:/\/api.ethers.io/api/v1/?action=fundAccount&address=${ wallet.address.toLowerCase() }`);
|
|
||||||
await provider.waitForTransaction(funder.hash);
|
|
||||||
|
|
||||||
const addr = "0x8210357f377E901f18E45294e86a2A32215Cc3C9";
|
|
||||||
const gasPrice = 9000000000;
|
|
||||||
|
|
||||||
let balance = await provider.getBalance(wallet.address);
|
|
||||||
assert.ok(balance.eq(ethers.utils.parseEther("3.141592653589793238")), "balance is pi after funding");
|
|
||||||
|
|
||||||
const tx = await wallet.sendTransaction({
|
|
||||||
to: addr,
|
|
||||||
gasPrice: gasPrice,
|
|
||||||
value: balance.sub(21000 * gasPrice)
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.wait();
|
|
||||||
|
|
||||||
balance = await provider.getBalance(wallet.address);
|
|
||||||
assert.ok(balance.eq(ethers.constants.Zero), "balance is zero after after sweeping");
|
|
||||||
|
|
||||||
await waiter(delay);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (test.name) {
|
||||||
// Obviously many more cases to add here
|
addSimpleTest(`fetches ENS name: ${ test.address }`, (provider: ethers.providers.Provider) => {
|
||||||
// - getTransactionCount
|
return provider.resolveName(test.name);
|
||||||
// - getBlockNumber
|
}, test.address);
|
||||||
// - getGasPrice
|
}
|
||||||
// - estimateGas
|
|
||||||
// - sendTransaction
|
|
||||||
// - call
|
|
||||||
// - getLogs
|
|
||||||
//
|
|
||||||
// Many of these are tLegacyParametersested in run-providers, which uses nodeunit, but
|
|
||||||
// also creates a local private key which must then be funded to
|
|
||||||
// execute the tests. I am working on a better test contract to deploy
|
|
||||||
// to all the networks to help test these.
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
["default", "homestead", "ropsten", "rinkeby", "kovan", "goerli"].forEach(function(networkName) {
|
tests.blocks.forEach((test) => {
|
||||||
["getDefaultProvider", "AlchemyProvider", "CloudflareProvider", "InfuraProvider", "EtherscanProvider", "NodesmithProvider", "Web3Provider"].forEach(function(providerName) {
|
addObjectTest(`fetches block (by number) #${ test.number }`, (provider: ethers.providers.Provider) => {
|
||||||
if (providerName === "NodesmithProvider") { return; }
|
return provider.getBlock(test.number);
|
||||||
if (providerName === "CloudflareProvider") { return; }
|
}, test);
|
||||||
if (providerName === "Web3Provider") { return; }
|
|
||||||
|
|
||||||
if ((networkName !== "homestead" && networkName !== "default") && providerName === "CloudflareProvider") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testProvider(providerName, networkName);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tests.blocks.forEach((test) => {
|
||||||
|
addObjectTest(`fetches block (by hash) ${ test.hash }`, (provider: ethers.providers.Provider) => {
|
||||||
|
return provider.getBlock(test.hash);
|
||||||
|
}, test, (provider: string, network: string, test: TestDescription) => {
|
||||||
|
return (provider === "EtherscanProvider");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tests.transactions.forEach((test) => {
|
||||||
|
addObjectTest(`fetches transaction ${ test.hash }`, async (provider: ethers.providers.Provider) => {
|
||||||
|
const tx = await provider.getTransaction(test.hash);
|
||||||
|
|
||||||
|
// This changes with every block
|
||||||
|
assert.equal(typeof(tx.confirmations), "number", "confirmations is a number");
|
||||||
|
delete tx.confirmations;
|
||||||
|
|
||||||
|
assert.equal(typeof(tx.wait), "function", "wait is a function");
|
||||||
|
delete tx.wait
|
||||||
|
|
||||||
|
return tx;
|
||||||
|
}, test, (provider: string, network: string, test: TestDescription) => {
|
||||||
|
return (provider === "EtherscanProvider");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tests.transactionReceipts.forEach((test) => {
|
||||||
|
addObjectTest(`fetches transaction receipt ${ test.transactionHash }`, async (provider: ethers.providers.Provider) => {
|
||||||
|
const receipt = await provider.getTransactionReceipt(test.transactionHash);
|
||||||
|
|
||||||
|
if (test.status === null) {
|
||||||
|
assert.ok(receipt.status === undefined, "no status");
|
||||||
|
receipt.status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This changes with every block; so just make sure it is a number
|
||||||
|
assert.equal(typeof(receipt.confirmations), "number", "confirmations is a number");
|
||||||
|
delete receipt.confirmations;
|
||||||
|
|
||||||
|
return receipt;
|
||||||
|
}, test);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function addErrorTest(code: string, func: (provider: ethers.providers.Provider) => Promise<any>) {
|
||||||
|
testFunctions.push({
|
||||||
|
name: `throws correct ${ code } error`,
|
||||||
|
networks: [ "ropsten" ],
|
||||||
|
execute: async (provider: ethers.providers.Provider) => {
|
||||||
|
try {
|
||||||
|
const value = await func(provider);
|
||||||
|
console.log(value);
|
||||||
|
assert.ok(false, "did not throw");
|
||||||
|
} catch (error) {
|
||||||
|
assert.equal(error.code, code, "incorrect error thrown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addErrorTest(ethers.utils.Logger.errors.NONCE_EXPIRED, async (provider: ethers.providers.Provider) => {
|
||||||
|
return provider.sendTransaction("0xf86480850218711a0082520894000000000000000000000000000000000000000002801ba038aaddcaaae7d3fa066dfd6f196c8348e1bb210f2c121d36cb2c24ef20cea1fba008ae378075d3cd75aae99ab75a70da82161dffb2c8263dabc5d8adecfa9447fa");
|
||||||
|
});
|
||||||
|
|
||||||
|
addErrorTest(ethers.utils.Logger.errors.INSUFFICIENT_FUNDS, async (provider: ethers.providers.Provider) => {
|
||||||
|
|
||||||
|
const txProps = {
|
||||||
|
to: "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
|
||||||
|
gasPrice: 9000000000,
|
||||||
|
gasLimit: 21000,
|
||||||
|
value: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const wallet = ethers.Wallet.createRandom();
|
||||||
|
const tx = await wallet.signTransaction(txProps);
|
||||||
|
return provider.sendTransaction(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
addErrorTest(ethers.utils.Logger.errors.INSUFFICIENT_FUNDS, async (provider: ethers.providers.Provider) => {
|
||||||
|
const txProps = {
|
||||||
|
to: "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
|
||||||
|
gasPrice: 9000000000,
|
||||||
|
gasLimit: 21000,
|
||||||
|
value: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const wallet = ethers.Wallet.createRandom().connect(provider);
|
||||||
|
return wallet.sendTransaction(txProps);
|
||||||
|
});
|
||||||
|
|
||||||
|
addErrorTest(ethers.utils.Logger.errors.UNPREDICTABLE_GAS_LIMIT, async (provider: ethers.providers.Provider) => {
|
||||||
|
return provider.estimateGas({
|
||||||
|
to: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e" // ENS contract
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
testFunctions.push({
|
||||||
|
name: "sends a transaction",
|
||||||
|
extras: [ "funding" ], // We need funding to the funWallet
|
||||||
|
timeout: 300, // 5 minutes
|
||||||
|
networks: [ "ropsten" ], // Only test on Ropsten
|
||||||
|
execute: async (provider: ethers.providers.Provider) => {
|
||||||
|
const wallet = fundWallet.connect(provider);
|
||||||
|
|
||||||
|
const addr = "0x8210357f377E901f18E45294e86a2A32215Cc3C9";
|
||||||
|
|
||||||
|
const b0 = await provider.getBalance(wallet.address);
|
||||||
|
assert.ok(b0.gt(ethers.constants.Zero), "balance is non-zero");
|
||||||
|
|
||||||
|
const tx = await wallet.sendTransaction({
|
||||||
|
to: addr,
|
||||||
|
value: 123
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.wait();
|
||||||
|
|
||||||
|
const b1 = await provider.getBalance(wallet.address);
|
||||||
|
assert.ok(b0.gt(b1), "balance is decreased");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test Provider Methods", function() {
|
||||||
|
let fundReceipt: Promise<ethers.providers.TransactionReceipt> = null;
|
||||||
|
const faucet = "0x8210357f377E901f18E45294e86a2A32215Cc3C9";
|
||||||
|
|
||||||
|
before(async function() {
|
||||||
|
// Get some ether from the faucet
|
||||||
|
const provider = ethers.getDefaultProvider("ropsten");
|
||||||
|
const funder = await ethers.utils.fetchJson(`https:/\/api.ethers.io/api/v1/?action=fundAccount&address=${ fundWallet.address.toLowerCase() }`);
|
||||||
|
fundReceipt = provider.waitForTransaction(funder.hash);
|
||||||
|
fundReceipt.then((receipt) => {
|
||||||
|
console.log(`*** Funded: ${ fundWallet.address }`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async function() {
|
||||||
|
// Wait until the funding is complete
|
||||||
|
await fundReceipt;
|
||||||
|
|
||||||
|
// Refund all unused ether to the faucet
|
||||||
|
const provider = ethers.getDefaultProvider("ropsten");
|
||||||
|
const gasPrice = await provider.getGasPrice();
|
||||||
|
const balance = await provider.getBalance(fundWallet.address);
|
||||||
|
fundWallet.connect(provider).sendTransaction({
|
||||||
|
to: faucet,
|
||||||
|
gasLimit: 21000,
|
||||||
|
gasPrice: gasPrice,
|
||||||
|
value: balance.sub(gasPrice.mul(21000))
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
providerFunctions.forEach(({ name, networks, create}) => {
|
||||||
|
|
||||||
|
networks.forEach((network) => {
|
||||||
|
const provider = create(network);
|
||||||
|
|
||||||
|
testFunctions.forEach((test) => {
|
||||||
|
|
||||||
|
// Skip tests not supported on this network
|
||||||
|
if (test.networks.indexOf(network) === -1) { return; }
|
||||||
|
if (test.checkSkip && test.checkSkip(name, network, test)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// How many attempts to try?
|
||||||
|
const attempts = (test.attempts != null) ? test.attempts: 3;
|
||||||
|
const timeout = (test.timeout != null) ? test.timeout: 60;
|
||||||
|
const extras = (test.extras || []).reduce((accum, key) => {
|
||||||
|
accum[key] = true;
|
||||||
|
return accum;
|
||||||
|
}, <{ [ key: string ]: boolean }>{ });
|
||||||
|
|
||||||
|
it(`${ name }.${ network ? network: "default" } ${ test.name}`, async function() {
|
||||||
|
this.timeout(timeout * 1000 * attempts);
|
||||||
|
|
||||||
|
// Wait for the funding transaction to be mined
|
||||||
|
if (extras.funding) { await fundReceipt; }
|
||||||
|
|
||||||
|
// We wait at least 1 seconds between tests
|
||||||
|
if (!extras.nowait) { await waiter(1000); }
|
||||||
|
|
||||||
|
let error: Error = null;
|
||||||
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
||||||
|
try {
|
||||||
|
return await Promise.race([
|
||||||
|
test.execute(provider),
|
||||||
|
waiter(timeout * 1000).then((resolve) => { throw new Error("timeout"); })
|
||||||
|
]);
|
||||||
|
} catch (attemptError) {
|
||||||
|
console.log(`*** Failed attempt ${ attempt + 1 }: ${ attemptError.message }`);
|
||||||
|
error = attemptError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -720,7 +794,7 @@ describe("Test extra Etherscan operations", function() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
describe("Test Basic Authentication", function() {
|
describe("Test Basic Authentication", function() {
|
||||||
this.retries(3);
|
//this.retries(3);
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/6509278/authentication-test-servers#16756383
|
// https://stackoverflow.com/questions/6509278/authentication-test-servers#16756383
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user