Skip to content

Commit df3c978

Browse files
committed
tests: add unit tests for streaming event handlers
1 parent 9cf02d9 commit df3c978

File tree

10 files changed

+194
-48
lines changed

10 files changed

+194
-48
lines changed

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"!src/types/**/*.{js,ts}",
9191
"!src/i18n/**/*.{js,ts}",
9292
"!src/util/tests/**/*.{ts,tsx}",
93+
"!src/setupProxy.js",
9394
"!src/index.tsx"
9495
]
9596
},

app/src/__mocks__/@improbable-eng/grpc-web.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const grpc = {
99
OK: 0,
1010
Canceled: 1,
1111
},
12+
WebsocketTransport: jest.fn(),
1213
// mock unary function to simulate GRPC requests
1314
unary: jest
1415
.fn()
@@ -32,5 +33,13 @@ export const grpc = {
3233
},
3334
),
3435
// mock client function to simulate server-side streaming
35-
client: jest.fn(),
36+
client: jest.fn().mockImplementation(() => {
37+
return {
38+
onHeaders: jest.fn(func => func()),
39+
onMessage: jest.fn(func => func({ toObject: jest.fn() })),
40+
onEnd: jest.fn(func => func()),
41+
start: jest.fn(),
42+
send: jest.fn(),
43+
};
44+
}),
3645
};

app/src/__stories__/ChannelBalance.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { lndListChannelsOne } from 'util/tests/sampleData';
2+
import { lndChannel } from 'util/tests/sampleData';
33
import { Store, useStore } from 'store';
44
import { Channel } from 'store/models';
55
import ChannelBalance from 'components/loop/ChannelBalance';
@@ -11,7 +11,7 @@ export default {
1111
};
1212

1313
const getChannel = (store: Store, ratio: number) => {
14-
const channel = new Channel(store, lndListChannelsOne.channelsList[0]);
14+
const channel = new Channel(store, lndChannel);
1515
channel.localBalance = channel.capacity.mul(ratio);
1616
channel.remoteBalance = channel.capacity.mul(1 - ratio);
1717
return channel;

app/src/__tests__/components/loop/ChannelBalance.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { renderWithProviders } from 'util/tests';
3-
import { lndListChannelsOne } from 'util/tests/sampleData';
3+
import { lndChannel } from 'util/tests/sampleData';
44
import { createStore } from 'store';
55
import { Channel } from 'store/models';
66
import ChannelBalance from 'components/loop/ChannelBalance';
@@ -13,7 +13,7 @@ describe('ChannelBalance component', () => {
1313

1414
const render = (ratio: number, active = true) => {
1515
const store = createStore();
16-
channel = new Channel(store, lndListChannelsOne.channelsList[0]);
16+
channel = new Channel(store, lndChannel);
1717
channel.localBalance = channel.capacity.mul(ratio);
1818
channel.remoteBalance = channel.capacity.mul(1 - ratio);
1919
channel.active = active;

app/src/__tests__/store/authStore.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { grpc } from '@improbable-eng/grpc-web';
2+
import { waitFor } from '@testing-library/react';
23
import AppStorage from 'util/appStorage';
4+
import { lndListChannels } from 'util/tests/sampleData';
35
import { AuthStore, createStore, Store } from 'store';
46
import { PersistentSettings } from 'store/stores/settingsStore';
57

@@ -65,4 +67,16 @@ describe('AuthStore', () => {
6567
await store.init();
6668
expect(store.credentials).toBe('');
6769
});
70+
71+
it('should fetch data after authentication succeeds', async () => {
72+
await rootStore.init();
73+
expect(rootStore.channelStore.channels.size).toBe(0);
74+
await store.login('test-pw');
75+
expect(store.authenticated).toBe(true);
76+
await waitFor(() => {
77+
expect(rootStore.channelStore.channels.size).toBe(
78+
lndListChannels.channelsList.length,
79+
);
80+
});
81+
});
6882
});

app/src/__tests__/store/channelStore.spec.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { observable, ObservableMap, values } from 'mobx';
2+
import * as LND from 'types/generated/lnd_pb';
23
import { grpc } from '@improbable-eng/grpc-web';
34
import { waitFor } from '@testing-library/react';
45
import Big from 'big.js';
56
import { BalanceMode } from 'util/constants';
6-
import { lndListChannels } from 'util/tests/sampleData';
7+
import { lndChannelEvent, lndListChannels } from 'util/tests/sampleData';
78
import { createStore, Store } from 'store';
89
import Channel from 'store/models/channel';
910
import ChannelStore from 'store/stores/channelStore';
@@ -118,4 +119,60 @@ describe('ChannelStore', () => {
118119

119120
expect(+store.totalOutbound).toBe(outbound);
120121
});
122+
123+
describe('onChannelEvent', () => {
124+
const {
125+
OPEN_CHANNEL,
126+
CLOSED_CHANNEL,
127+
ACTIVE_CHANNEL,
128+
INACTIVE_CHANNEL,
129+
} = LND.ChannelEventUpdate.UpdateType;
130+
131+
beforeEach(async () => {
132+
await store.fetchChannels();
133+
});
134+
135+
it('should handle inactive channel event', async () => {
136+
const event = { ...lndChannelEvent, type: INACTIVE_CHANNEL };
137+
const len = lndListChannels.channelsList.length;
138+
expect(store.activeChannels).toHaveLength(len);
139+
store.onChannelEvent(event);
140+
expect(store.activeChannels).toHaveLength(len - 1);
141+
});
142+
143+
it('should handle active channel event', async () => {
144+
await store.fetchChannels();
145+
const chan = store.channels.get(lndListChannels.channelsList[0].chanId) as Channel;
146+
chan.active = false;
147+
const event = { ...lndChannelEvent, type: ACTIVE_CHANNEL };
148+
const len = lndListChannels.channelsList.length;
149+
expect(store.activeChannels).toHaveLength(len - 1);
150+
store.onChannelEvent(event);
151+
expect(store.activeChannels).toHaveLength(len);
152+
});
153+
154+
it('should handle open channel event', async () => {
155+
const event = { ...lndChannelEvent, type: OPEN_CHANNEL };
156+
event.openChannel.chanId = '12345';
157+
expect(store.channels.get('12345')).toBeUndefined();
158+
store.onChannelEvent(event);
159+
expect(store.channels.get('12345')).toBeDefined();
160+
});
161+
162+
it('should handle close channel event', async () => {
163+
const event = { ...lndChannelEvent, type: CLOSED_CHANNEL };
164+
const chanId = event.closedChannel.chanId;
165+
expect(store.channels.get(chanId)).toBeDefined();
166+
store.onChannelEvent(event);
167+
expect(store.channels.get(chanId)).toBeUndefined();
168+
});
169+
170+
it('should do nothing for unknown channel event type', async () => {
171+
const event = { ...lndChannelEvent, type: 99 };
172+
const len = lndListChannels.channelsList.length;
173+
expect(store.activeChannels).toHaveLength(len);
174+
store.onChannelEvent(event as any);
175+
expect(store.activeChannels).toHaveLength(len);
176+
});
177+
});
121178
});

app/src/__tests__/store/nodeStore.spec.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { values } from 'mobx';
22
import { grpc } from '@improbable-eng/grpc-web';
33
import { waitFor } from '@testing-library/react';
4-
import { lndChannelBalance, lndGetInfo, lndWalletBalance } from 'util/tests/sampleData';
4+
import {
5+
lndChannelBalance,
6+
lndGetInfo,
7+
lndTransaction,
8+
lndWalletBalance,
9+
} from 'util/tests/sampleData';
510
import { createStore, NodeStore, Store } from 'store';
611

712
const grpcMock = grpc as jest.Mocked<typeof grpc>;
@@ -60,4 +65,18 @@ describe('NodeStore', () => {
6065
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
6166
});
6267
});
68+
69+
it('should handle a transaction event', () => {
70+
expect(+store.wallet.walletBalance).toBe(0);
71+
store.onTransaction(lndTransaction);
72+
expect(+store.wallet.walletBalance).toBe(lndTransaction.amount);
73+
});
74+
75+
it('should handle duplicate transaction events', () => {
76+
expect(+store.wallet.walletBalance).toBe(0);
77+
store.onTransaction(lndTransaction);
78+
expect(+store.wallet.walletBalance).toBe(lndTransaction.amount);
79+
store.onTransaction(lndTransaction);
80+
expect(+store.wallet.walletBalance).toBe(lndTransaction.amount);
81+
});
6382
});

app/src/__tests__/store/swapStore.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,12 @@ describe('SwapStore', () => {
7575
swap.type = type;
7676
expect(swap.typeName).toEqual(label);
7777
});
78+
79+
it('should handle swap events', () => {
80+
const swap = loopListSwaps.swapsList[0];
81+
swap.id += 'test';
82+
expect(store.sortedSwaps).toHaveLength(0);
83+
store.onSwapUpdate(swap);
84+
expect(store.sortedSwaps).toHaveLength(1);
85+
});
7886
});

app/src/store/stores/swapStore.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,6 @@ export default class SwapStore {
4242
);
4343
}
4444

45-
/** swaps that are currently pending */
46-
@computed get pendingSwaps() {
47-
return this.sortedSwaps.filter(s => s.isPending);
48-
}
49-
5045
@action.bound
5146
dismissSwap(swapId: string) {
5247
this.dismissedSwapIds.push(swapId);

app/src/util/tests/sampleData.ts

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -47,55 +47,54 @@ export const lndWalletBalance: LND.WalletBalanceResponse.AsObject = {
4747
unconfirmedBalance: 0,
4848
};
4949

50-
export const lndListChannelsOne: LND.ListChannelsResponse.AsObject = {
51-
channelsList: [
50+
const txId = '6ee4e45870ac6191e25173f29804851e9f4bcf10f65f8b63100f488989e1e7a8';
51+
const outIndex = 0;
52+
export const lndChannel: LND.Channel.AsObject = {
53+
active: true,
54+
remotePubkey: '037136742c67e24681f36542f7c8916aa6f6fdf665c1dca2a107425503cff94501',
55+
channelPoint: `${txId}:${outIndex}`,
56+
chanId: '124244814004224',
57+
capacity: 15000000,
58+
localBalance: 9988660,
59+
remoteBalance: 4501409,
60+
commitFee: 11201,
61+
commitWeight: 896,
62+
feePerKw: 12500,
63+
unsettledBalance: 498730,
64+
totalSatoshisSent: 1338,
65+
totalSatoshisReceived: 499929,
66+
numUpdates: 6,
67+
pendingHtlcsList: [
5268
{
53-
active: true,
54-
remotePubkey: '037136742c67e24681f36542f7c8916aa6f6fdf665c1dca2a107425503cff94501',
55-
channelPoint: '0ef6a4ae3d8f800f4eb736f0776f5d3a72571615a1b7218ab17c9a43f85d8949:0',
56-
chanId: '124244814004224',
57-
capacity: 15000000,
58-
localBalance: 9988660,
59-
remoteBalance: 4501409,
60-
commitFee: 11201,
61-
commitWeight: 896,
62-
feePerKw: 12500,
63-
unsettledBalance: 498730,
64-
totalSatoshisSent: 1338,
65-
totalSatoshisReceived: 499929,
66-
numUpdates: 6,
67-
pendingHtlcsList: [
68-
{
69-
incoming: false,
70-
amount: 498730,
71-
hashLock: 'pl8fmsyoSqEQFQCw6Zu9e1aIlFnMz5H+hW2mmh3kRlI=',
72-
expirationHeight: 285,
73-
},
74-
],
75-
csvDelay: 1802,
76-
pb_private: false,
77-
initiator: true,
78-
chanStatusFlags: 'ChanStatusDefault',
79-
localChanReserveSat: 150000,
80-
remoteChanReserveSat: 150000,
81-
staticRemoteKey: true,
82-
lifetime: 21802,
83-
uptime: 21802,
84-
closeAddress: '',
69+
incoming: false,
70+
amount: 498730,
71+
hashLock: 'pl8fmsyoSqEQFQCw6Zu9e1aIlFnMz5H+hW2mmh3kRlI=',
72+
expirationHeight: 285,
8573
},
8674
],
75+
csvDelay: 1802,
76+
pb_private: false,
77+
initiator: true,
78+
chanStatusFlags: 'ChanStatusDefault',
79+
localChanReserveSat: 150000,
80+
remoteChanReserveSat: 150000,
81+
staticRemoteKey: true,
82+
lifetime: 21802,
83+
uptime: 21802,
84+
closeAddress: '',
8785
};
8886

8987
export const lndListChannels: LND.ListChannelsResponse.AsObject = {
9088
channelsList: [...Array(500)].map((_, i) => {
91-
const c = lndListChannelsOne.channelsList[0];
89+
const c = lndChannel;
9290
// pick a random capacity between 0.5 and 1 BTC
9391
const cap = Math.floor(Math.random() * 50000000) + 50000000;
9492
// pick a local balance that is at least 100K sats
9593
const local = Math.max(100000, Math.floor(Math.random() * cap - 100000));
9694
return {
9795
...c,
98-
chanId: `${i}${c.chanId}`,
96+
chanId: `${i || ''}${c.chanId}`,
97+
channelPoint: `${c.channelPoint.substring(0, c.channelPoint.length - 2)}:${i}`,
9998
remotePubkey: `${i}${c.remotePubkey}`,
10099
localBalance: local,
101100
remoteBalance: cap - local,
@@ -105,6 +104,50 @@ export const lndListChannels: LND.ListChannelsResponse.AsObject = {
105104
}),
106105
};
107106

107+
const txIdBytes = Buffer.from(txId, 'hex').reverse().toString('base64');
108+
export const lndChannelEvent: Required<LND.ChannelEventUpdate.AsObject> = {
109+
type: LND.ChannelEventUpdate.UpdateType.OPEN_CHANNEL,
110+
openChannel: lndChannel,
111+
closedChannel: {
112+
capacity: 15000000,
113+
chainHash: '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206',
114+
chanId: lndChannel.chanId,
115+
channelPoint: lndChannel.channelPoint,
116+
closeHeight: 191,
117+
closeType: 0,
118+
closingTxHash: '1f765f45f2a6d33837a203e3fc911915c891e9b86f9c9d91a1931b92efdedf5b',
119+
remotePubkey: '030e98fdacf2464bdfb027b866a018d6cdc5108514208988873abea7eff59afd91',
120+
settledBalance: 12990950,
121+
timeLockedBalance: 0,
122+
},
123+
activeChannel: {
124+
fundingTxidBytes: txIdBytes,
125+
fundingTxidStr: '',
126+
outputIndex: outIndex,
127+
},
128+
inactiveChannel: {
129+
fundingTxidBytes: txIdBytes,
130+
fundingTxidStr: '',
131+
outputIndex: outIndex,
132+
},
133+
};
134+
135+
export const lndTransaction: LND.Transaction.AsObject = {
136+
amount: 12990950,
137+
blockHash: '',
138+
blockHeight: 0,
139+
destAddressesList: [
140+
'bcrt1qgrvqm263gra5t02cvvkxmp9520rkann0cedzz8',
141+
'bcrt1qkggx6pzd768hn6psc5tmwuvv4c2nzvpd3ax9a9',
142+
],
143+
numConfirmations: 0,
144+
rawTxHex:
145+
'02000000000101a8e7e18989480f10638b5ff610cf4b9f1e850498f27351e29161ac7058e4e46e0000000000ffffffff0280841e000000000016001440d80dab5140fb45bd58632c6d84b453c76ece6fe639c60000000000160014b2106d044df68f79e830c517b7718cae1531302d040047304402207e17f9938f04a2379300a5c0f37305c902855fa000726bb7f0ad78d084acfcee02206d3da5edd73624d6ecfa27ae61e994e75bd0ad8cca6c9b7dda087bcf34b2bbbc0148304502210086d0b7e77b1d81f210d55bc13f9eef975774ac1509a22ff649bd2baac85b3fd702203bb272d6372450159b89ca41d97efbf6bdac076bc271696a1bd556efc31b5cda01475221028d084ada5554c83421bfac35bc78332f3c1f6ae980dea1e0eb3220411b7b83972103c60b39c8558f280fe2f0dfa7cb6a04f016470c4670e631458b400774a667610052ae00000000',
146+
timeStamp: 1591226124,
147+
totalFees: 0,
148+
txHash: '1f765f45f2a6d33837a203e3fc911915c891e9b86f9c9d91a1931b92efdedf5b',
149+
};
150+
108151
//
109152
// Loop API Responses
110153
//

0 commit comments

Comments
 (0)