diff --git a/web3.js/.eslintignore b/web3.js/.eslintignore index e17bf5e6ed..be0db153b3 100644 --- a/web3.js/.eslintignore +++ b/web3.js/.eslintignore @@ -1,3 +1,4 @@ /doc /flow-typed /lib +/coverage diff --git a/web3.js/src/connection.js b/web3.js/src/connection.js index 510f622d90..4d8a5de522 100644 --- a/web3.js/src/connection.js +++ b/web3.js/src/connection.js @@ -54,6 +54,7 @@ function createRpcRequest(url): RpcRequest { }; } + /** * Expected JSON RPC response for the "getBalance" message */ @@ -237,9 +238,9 @@ export class Connection { // TODO: This is not the correct transaction payload `Transaction ${from.publicKey} ${to} ${amount}` ); + const signedTransaction = nacl.sign.detached(transaction, from.secretKey); - console.log(typeof signedTransaction); - console.log([...signedTransaction]); + const unsafeRes = await this._rpcRequest('sendTransaction', [[...signedTransaction]]); const res = SendTokensRpcResult(unsafeRes); if (res.error) { diff --git a/web3.js/test/__mocks__/node-fetch.js b/web3.js/test/__mocks__/node-fetch.js new file mode 100644 index 0000000000..77589a5280 --- /dev/null +++ b/web3.js/test/__mocks__/node-fetch.js @@ -0,0 +1,64 @@ +// @flow + +type RpcRequest = { + method: string; + params: Array; +}; + +type RpcResponseError = { + message: string; +} +type RpcResponseResult = boolean | string | number; +type RpcResponse = { + error: ?RpcResponseError; + result: ?RpcResponseResult; +}; + +export const mockRpc: Array<[string, RpcRequest, RpcResponse]> = []; + +const mock: JestMockFn = jest.fn( + (fetchUrl, fetchOptions) => { + expect(mockRpc.length).toBeGreaterThanOrEqual(1); + const [mockUrl, mockRequest, mockResponse] = mockRpc.shift(); + + expect(fetchUrl).toBe(mockUrl); + expect(fetchOptions).toMatchObject({ + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + }); + expect(fetchOptions.body).toBeDefined(); + + const body = JSON.parse(fetchOptions.body); + expect(body).toMatchObject(Object.assign( + {}, + { + jsonrpc: '2.0', + method: 'invalid', + params: ['invalid', 'params'], + }, + mockRequest + )); + + const response = Object.assign( + {}, + { + jsonrpc: '2.0', + id: body.id, + error: { + message: 'invalid error message', + }, + result: 'invalid response', + }, + mockResponse, + ); + return { + text: () => { + return Promise.resolve(JSON.stringify(response)); + }, + }; + } +); + +export default mock; diff --git a/web3.js/test/connection.test.js b/web3.js/test/connection.test.js index da208d0028..8b0e13677d 100644 --- a/web3.js/test/connection.test.js +++ b/web3.js/test/connection.test.js @@ -1,66 +1,204 @@ // @flow + import {Connection} from '../src/connection'; import {Account} from '../src/account'; +import {mockRpc} from './__mocks__/node-fetch'; +const url = 'http://master.testnet.solana.com:8899'; -const url = 'http://localhost:8899'; -//const url = 'http://master.testnet.solana.com:8899'; +// Define DOITLIVE in the environment to test against the real full node +// identified by `url` instead of using the mock +if (process.env.DOITLIVE) { + console.log(`Note: node-fetch mock is disabled, testing live against ${url}`); +} else { + jest.mock('node-fetch'); +} -test.skip('get balance', async () => { +test('get balance', async () => { const account = new Account(); const connection = new Connection(url); + mockRpc.push([ + url, + { + method: 'getBalance', + params: [account.publicKey], + }, + { + error: null, + result: 0, + } + ]); + const balance = await connection.getBalance(account.publicKey); expect(balance).toBeGreaterThanOrEqual(0); }); -test.skip('throws on bad transaction confirmation', () => { +test('throws on bad transaction confirmation', () => { const connection = new Connection(url); - expect(connection.confirmTransaction('bad transaction signature')) - .rejects.toThrow('Invalid request'); + const badTransactionSignature = 'bad transaction signature'; + const errorMessage = 'Invalid request'; + + mockRpc.push([ + url, + { + method: 'confirmTransaction', + params: [badTransactionSignature], + }, + { + error: { + message: errorMessage, + }, + result: undefined, + } + ] + ); + + expect(connection.confirmTransaction(badTransactionSignature)) + .rejects.toThrow(errorMessage); }); -test.skip('get transaction count', async () => { +test('get transaction count', async () => { const connection = new Connection(url); + mockRpc.push([ + url, + { + method: 'getTransactionCount', + params: [], + }, + { + error: null, + result: 1000000, + } + ] + ); + const count = await connection.getTransactionCount(); expect(count).toBeGreaterThanOrEqual(0); }); -test.skip('get last Id', async () => { +test('get last Id', async () => { const connection = new Connection(url); + mockRpc.push([ + url, + { + method: 'getLastId', + params: [], + }, + { + error: null, + result: '1111111111111111111111111111111111111111111111', + } + ] + ); + const lastId = await connection.getLastId(); expect(lastId.length).toBeGreaterThanOrEqual(43); }); -test.skip('get finality', async () => { +test('get finality', async () => { const connection = new Connection(url); + mockRpc.push([ + url, + { + method: 'getFinality', + params: [], + }, + { + error: null, + result: 123, + } + ] + ); + const finality = await connection.getFinality(); expect(finality).toBeGreaterThanOrEqual(0); }); -test.skip('request airdrop', async () => { +test('request airdrop', async () => { const account = new Account(); const connection = new Connection(url); - await Promise.all([ - connection.requestAirdrop(account.publicKey, 40), - connection.requestAirdrop(account.publicKey, 2), + mockRpc.push([ + url, + { + method: 'requestAirdrop', + params: [account.publicKey, 40], + }, + { + error: null, + result: true, + } ]); + mockRpc.push([ + url, + { + method: 'requestAirdrop', + params: [account.publicKey, 2], + }, + { + error: null, + result: true, + } + ]); + mockRpc.push([ + url, + { + method: 'getBalance', + params: [account.publicKey], + }, + { + error: null, + result: 42, + } + ]); + + await connection.requestAirdrop(account.publicKey, 40); + await connection.requestAirdrop(account.publicKey, 2); const balance = await connection.getBalance(account.publicKey); expect(balance).toBe(42); }); -test.skip('throws on bad transaction', () => { - const account = new Account(); +test('throws on bad transaction', () => { + const secretKey = Buffer.from([ + 153, 218, 149, 89, 225, 94, 145, 62, 233, 171, 46, 83, 227, + 223, 173, 87, 93, 163, 59, 73, 190, 17, 37, 187, 146, 46, 51, + 73, 79, 73, 136, 40, 27, 47, 73, 9, 110, 62, 93, 189, 15, 207, + 169, 192, 192, 205, 146, 217, 171, 59, 33, 84, 75, 52, 213, 221, + 74, 101, 217, 139, 135, 139, 153, 34 + ]); + const account = new Account(secretKey); const connection = new Connection(url); + const errorMessage = 'Invalid request'; + mockRpc.push([ + url, + { + method: 'sendTransaction', + params: [[ + 78, 52, 48, 146, 162, 213, 83, 169, 128, 10, 82, 26, 145, 238, + 1, 130, 16, 44, 249, 99, 121, 55, 217, 72, 77, 41, 73, 227, 5, + 15, 125, 212, 186, 157, 182, 100, 232, 232, 39, 84, 5, 121, 172, + 137, 177, 248, 188, 224, 196, 102, 204, 43, 128, 243, 170, 157, + 134, 216, 209, 8, 211, 209, 44, 1 + ]], + }, + { + error: { + message: errorMessage, + }, + result: undefined, + } + ]); + + expect(connection.sendTokens(account, account.publicKey, 123)) - .rejects.toThrow('Invalid request'); + .rejects.toThrow(errorMessage); });