feat: add account change notifications

This commit is contained in:
Michael Vines
2018-10-26 21:37:39 -07:00
parent 9839c087d7
commit e7097340f2
12 changed files with 552 additions and 25 deletions

View File

@ -22,12 +22,17 @@ export const mockRpc: Array<[string, RpcRequest, RpcResponse]> = [];
// identified by `url` instead of using the mock
export const mockRpcEnabled = !process.env.DOITLIVE;
let mockNotice = true;
// Suppress lint: 'JestMockFn' is not defined
// eslint-disable-next-line no-undef
const mock: JestMockFn<any, any> = jest.fn(
(fetchUrl, fetchOptions) => {
if (!mockRpcEnabled) {
console.log(`Note: node-fetch mock is disabled, testing live against ${fetchUrl}`);
if (mockNotice) {
console.log(`Note: node-fetch mock is disabled, testing live against ${fetchUrl}`);
mockNotice = false;
}
return fetch(fetchUrl, fetchOptions);
}

View File

@ -0,0 +1,48 @@
import {Client as RpcWebSocketClient} from 'rpc-websockets';
// Define DOITLIVE in the environment to test against the real full node
// identified by `url` instead of using the mock
export const mockRpcEnabled = !process.env.DOITLIVE;
let mockNotice = true;
export class Client {
client: RpcWebSocketClient;
constructor(url, options) {
//console.log('MockClient', url, options);
if (!mockRpcEnabled) {
if (mockNotice) {
console.log('Note: rpc-websockets mock is disabled, testing live against', url);
mockNotice = false;
}
this.client = new RpcWebSocketClient(url, options);
}
}
connect() {
if (!mockRpcEnabled) {
return this.client.connect();
}
}
close() {
if (!mockRpcEnabled) {
return this.client.close();
}
}
on(event: string, callback: Function) {
if (!mockRpcEnabled) {
return this.client.on(event, callback);
}
//console.log('on', event);
}
async call(method: string, params: Object): Promise<Object> {
if (!mockRpcEnabled) {
return await this.client.call(method, params);
}
throw new Error('call unsupported');
}
}

View File

@ -1,9 +1,11 @@
// @flow
import {
Account,
Connection,
BpfLoader,
Loader,
SystemProgram,
sendAndConfirmTransaction,
} from '../src';
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
import {mockGetLastId} from './mockrpc/getlastid';
@ -410,3 +412,46 @@ test('multi-instruction transaction', async () => {
expect(await connection.getBalance(accountTo.publicKey)).toBe(21);
});
test('account change notification', async () => {
if (mockRpcEnabled) {
console.log('non-live test skipped');
return;
}
const connection = new Connection(url);
const owner = new Account();
const programAccount = new Account();
const mockCallback = jest.fn();
const subscriptionId = connection.onAccountChange(programAccount.publicKey, mockCallback);
await connection.requestAirdrop(owner.publicKey, 42);
const transaction = SystemProgram.createAccount(
owner.publicKey,
programAccount.publicKey,
42,
3,
BpfLoader.programId,
);
await sendAndConfirmTransaction(connection, owner, transaction);
const loader = new Loader(connection, BpfLoader.programId);
await loader.load(programAccount, [1, 2, 3]);
await connection.removeAccountChangeListener(subscriptionId);
// mockCallback should be called twice
expect(mockCallback.mock.calls).toHaveLength(2);
// First mockCallback call is due to SystemProgram.createAccount()
expect(mockCallback.mock.calls[0][0].tokens).toBe(42);
expect(mockCallback.mock.calls[0][0].executable).toBe(false);
expect(mockCallback.mock.calls[0][0].userdata).toEqual(Buffer.from([0, 0, 0]));
expect(mockCallback.mock.calls[0][0].programId).toEqual(BpfLoader.programId);
// Second mockCallback call is due to loader.load()
expect(mockCallback.mock.calls[1][0].userdata).toEqual(Buffer.from([1, 2, 3]));
});

View File

@ -3,7 +3,6 @@
/**
* The connection url to use when running unit tests against a live network
*/
export const url = 'http://localhost:8899';
//export const url = 'http://testnet.solana.com:8899';
//export const url = 'http://master.testnet.solana.com:8899';
export const url = 'http://localhost:8899/';
//export const url = 'http://testnet.solana.com:8899/';