Skip to content

Commit ff59189

Browse files
authored
Integration tests: simple send-tx, send-tx, create-tokens, mint and melt tokens (#152)
* docs: minor changes to jsdocs * perf: wallets starting in parallel instead of serially * feat: simple-send-tx tests * feat: send-tx tests with single token and automatic inputs * refactor: logging address samples when starting multiple wallets * test: sending with filter_address query * test: sending with single input * test: sending with multiple inputs * test: sending a custom token * test: sending custom tokens with inputs and "body.token" property * test: send-tx with multiple tokens * refactor: successful transactions changed to use wallet helper * style: applied standardized formatting * test: create-token tests * test: mint-token tests * test: melt-tokens tests * refactor: improving docs and naming for clarity * style: linting * style: fix wrong linting of expect calls * docs: minor fixes to comments and jsdocs * refactor: wallet validation pooling off by default * perf: transactions no longer pause to wait for websocket response This operation should be explicit whenever an operation to get balance is to be done. * docs: jsdocs, comments and variable renaming * feat: new helper functions for wallet txHistory and balance * refactor: improvements to create-token legibility * test: testing all input possibilities on create-token * test: testing all input possibilities on mint-tokens * test: improving assertions and validations on simple-send-tx * test: improving assertions and validations on melt-tokens * test: improving assertions and validations on send-tx * test: multi-token, automatic change address test * style: eslint * test: create-token field size validations * test: new token api tests with a single input and with no inputs * refactor: renaming variables for clarity * fix: made tests more strict and skipped failing ones * fix: removing unnecessary tests and assertions * test: including assertion for change address on basic create token test * style: converted static string templates into single tick strings * perf: wallet starter now working in parallel * refactor: removed irrelevant afterAll() on setupTests-integration * style: typo * refactor: websocket update delay period moved to config file * feat: timeout protection on multiple wallet initialization. * chore: added new timeout variable to config files * docs: added todos on pending assertions These todos should be reviewed together with the skipped tests on a future issue * chore: moved test configurations to separate file also, they are now editable via environment variables
1 parent a8151b2 commit ff59189

18 files changed

+3096
-108
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ config.js
55
node_modules/
66

77
coverage
8+
coverage-integration
89

910
.DS_Store

__tests__/integration/address-info.test.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ describe('address-info routes', () => {
1616
try {
1717
// A random HTR value for the first wallet
1818
wallet1 = new WalletHelper('addinfo-1');
19-
await wallet1.start();
20-
await wallet1.injectFunds(address1balance, 1);
21-
2219
// A fixed custom token amount for the second wallet
2320
wallet2 = new WalletHelper('addinfo-2');
24-
await wallet2.start();
21+
22+
await WalletHelper.startMultipleWalletsForTest([wallet1, wallet2]);
23+
await wallet1.injectFunds(address1balance, 1);
2524
await wallet2.injectFunds(10);
2625
const customToken = await wallet2.createToken({
2726
amount: 500,
@@ -32,6 +31,8 @@ describe('address-info routes', () => {
3231
});
3332
customTokenHash = customToken.hash;
3433

34+
await TestUtils.pauseForWsUpdate();
35+
3536
/*
3637
* The state here should be:
3738
* wallet1[1] with some value between 100 and 200 HTR

__tests__/integration/balance.test.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,16 @@ describe('balance routes', () => {
1212
try {
1313
// First wallet, no balance
1414
wallet1 = new WalletHelper('balance1');
15-
await wallet1.start();
16-
1715
// Second wallet, random balance
1816
wallet2 = new WalletHelper('balance2');
19-
await wallet2.start();
20-
await wallet2.injectFunds(wallet2Balance);
21-
2217
// Third wallet, balance to be used for custom tokens
2318
wallet3 = new WalletHelper('custom3');
24-
await wallet3.start();
19+
20+
await WalletHelper.startMultipleWalletsForTest([wallet1, wallet2, wallet3]);
21+
await wallet2.injectFunds(wallet2Balance);
2522
await wallet3.injectFunds(100);
23+
24+
await TestUtils.pauseForWsUpdate();
2625
} catch (err) {
2726
TestUtils.logError(err.stack);
2827
}
@@ -90,6 +89,8 @@ describe('balance routes', () => {
9089
});
9190
const tokenHash = newToken.hash;
9291

92+
await TestUtils.pauseForWsUpdate();
93+
9394
const balanceResult = await TestUtils.request
9495
.get('/wallet/balance')
9596
.query({ token: tokenHash })

__tests__/integration/configuration/config.js.template

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ module.exports = {
4646
},
4747
*/
4848

49-
// Integration test transaction logging
50-
integrationTestLog: {
51-
outputFolder: 'tmp/', // Should match .github/workflows/integration-test.yml -> upload-artifact
52-
},
53-
5449
// Optional config so you can set the token you want to use in this wallet
5550
// If this parameter is set you don't need to pass your token when getting balance or sending tokens
5651
tokenUid: '',
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* This file contains the configurations specific for the integration tests on the Wallet Headless.
3+
* Those values are also editable via envrionment variables
4+
*/
5+
6+
module.exports = {
7+
// On CI, should match .github/workflows/integration-test.yml -> upload-artifact
8+
logOutputFolder: process.env.TEST_LOG_OUTPUT_FOLDER || 'tmp/',
9+
10+
// Console level used on winston
11+
consoleLevel: process.env.TEST_CONSOLE_LEVEL || 'silly',
12+
13+
// Defines how long tests should wait before consulting balances after transactions
14+
wsUpdateDelay: process.env.TEST_WS_UPDATE_DELAY || 1000,
15+
16+
// Defines for how long the startMultipleWalletsForTest can run
17+
walletStartTimeout: process.env.TEST_WALLET_START_TIMEOUT || 60000,
18+
};
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
import { getRandomInt, TestUtils, WALLET_CONSTANTS } from './utils/test-utils-integration';
2+
import { WalletHelper } from './utils/wallet-helper';
3+
4+
describe('create token', () => {
5+
let wallet1;
6+
let wallet2;
7+
8+
const tokenA = {
9+
name: 'Token A',
10+
symbol: 'TKA',
11+
uid: null
12+
};
13+
14+
beforeAll(async () => {
15+
wallet1 = new WalletHelper('create-token-1');
16+
wallet2 = new WalletHelper('create-token-2');
17+
18+
await WalletHelper.startMultipleWalletsForTest([wallet1, wallet2]);
19+
await wallet1.injectFunds(10, 0);
20+
await wallet1.injectFunds(10, 1);
21+
await wallet2.injectFunds(10, 0);
22+
});
23+
24+
afterAll(async () => {
25+
await wallet1.stop();
26+
await wallet2.stop();
27+
});
28+
29+
// Testing failures first, that do not cause side-effects on the blockchain
30+
31+
it('should reject missing name parameter', async done => {
32+
const response = await TestUtils.request
33+
.post('/wallet/create-token')
34+
.send({
35+
symbol: tokenA.symbol,
36+
amount: 1000
37+
})
38+
.set({ 'x-wallet-id': wallet1.walletId });
39+
40+
expect(response.status).toBe(400);
41+
expect(response.body.success).toBe(false);
42+
done();
43+
});
44+
45+
it('should reject missing symbol parameter', async done => {
46+
const response = await TestUtils.request
47+
.post('/wallet/create-token')
48+
.send({
49+
name: tokenA.name,
50+
amount: 1000
51+
})
52+
.set({ 'x-wallet-id': wallet1.walletId });
53+
54+
expect(response.status).toBe(400);
55+
expect(response.body.success).toBe(false);
56+
done();
57+
});
58+
59+
it.skip('should reject a name with more than 30 characters', async done => {
60+
const response = await TestUtils.request
61+
.post('/wallet/create-token')
62+
.send({
63+
name: 'Name input with more than 30 characters',
64+
symbol: tokenA.symbol,
65+
amount: 2000
66+
})
67+
.set({ 'x-wallet-id': wallet1.walletId });
68+
69+
expect(response.body.success).toBe(false);
70+
expect(response.body.error).toContain('maximum size');
71+
done();
72+
});
73+
74+
// The result is an error with the message "maximum size", but consumes the funds. Must be fixed.
75+
it.skip('should reject a symbol with more than 5 characters', async done => {
76+
const response = await TestUtils.request
77+
.post('/wallet/create-token')
78+
.send({
79+
name: tokenA.name,
80+
symbol: 'TKABCD',
81+
amount: 2000
82+
})
83+
.set({ 'x-wallet-id': wallet1.walletId });
84+
85+
expect(response.body.success).toBe(false);
86+
expect(response.body.error).toContain('maximum size');
87+
done();
88+
});
89+
90+
it('should reject an invalid destination address', async done => {
91+
const response = await TestUtils.request
92+
.post('/wallet/create-token')
93+
.send({
94+
name: tokenA.name,
95+
symbol: tokenA.symbol,
96+
amount: 1000,
97+
address: 'invalidAddress'
98+
})
99+
.set({ 'x-wallet-id': wallet1.walletId });
100+
101+
expect(response.status).toBe(200);
102+
expect(response.body.success).toBe(false);
103+
expect(response.body.error).toContain('base58');
104+
done();
105+
});
106+
107+
it('should reject an invalid change address', async done => {
108+
const response = await TestUtils.request
109+
.post('/wallet/create-token')
110+
.send({
111+
name: tokenA.name,
112+
symbol: tokenA.symbol,
113+
amount: 500,
114+
address: await wallet1.getAddressAt(0),
115+
change_address: 'invalidAddress'
116+
})
117+
.set({ 'x-wallet-id': wallet1.walletId });
118+
119+
expect(response.status).toBe(200);
120+
expect(response.body.success).toBe(false);
121+
expect(response.body.error).toContain('Change address');
122+
done();
123+
});
124+
125+
// The application is incorrectly allowing external addresses to receive the change
126+
it.skip('should reject creating token for change address not in the wallet', async done => {
127+
const response = await TestUtils.request
128+
.post('/wallet/create-token')
129+
.send({
130+
name: tokenA.name,
131+
symbol: tokenA.symbol,
132+
amount: 500,
133+
address: await wallet1.getAddressAt(0),
134+
change_address: WALLET_CONSTANTS.genesis.addresses[3]
135+
})
136+
.set({ 'x-wallet-id': wallet1.walletId });
137+
138+
expect(response.status).toBe(200);
139+
expect(response.body.success).toBe(false);
140+
expect(response.text).toContain('wallet');
141+
done();
142+
});
143+
144+
// insufficient funds
145+
146+
it('should reject for insufficient funds', async done => {
147+
const response = await TestUtils.request
148+
.post('/wallet/create-token')
149+
.send({
150+
name: tokenA.name,
151+
symbol: tokenA.symbol,
152+
amount: 3000
153+
})
154+
.set({ 'x-wallet-id': wallet1.walletId });
155+
156+
expect(response.status).toBe(200);
157+
expect(response.body.success).toBe(false);
158+
expect(response.body.hash).toBeUndefined();
159+
done();
160+
});
161+
162+
it('should not create a token with the reserved HTR symbol', async done => {
163+
const response = await TestUtils.request
164+
.post('/wallet/create-token')
165+
.send({
166+
name: 'Hathor',
167+
symbol: 'HTR',
168+
amount: 100
169+
})
170+
.set({ 'x-wallet-id': wallet1.walletId });
171+
172+
expect(response.status).toBe(200);
173+
expect(response.body.success).toBe(false);
174+
expect(response.body.hash).toBeUndefined();
175+
expect(response.body.error).toContain('Invalid token name');
176+
done();
177+
});
178+
179+
it('should create a token with only required parameters', async done => {
180+
const response = await TestUtils.request
181+
.post('/wallet/create-token')
182+
.send({
183+
name: tokenA.name,
184+
symbol: tokenA.symbol,
185+
amount: 100
186+
})
187+
.set({ 'x-wallet-id': wallet1.walletId });
188+
189+
expect(response.body.success).toBe(true);
190+
expect(response.body.hash).toBeDefined();
191+
192+
await TestUtils.pauseForWsUpdate();
193+
194+
const htrBalance = await wallet1.getBalance();
195+
const tkaBalance = await wallet1.getBalance(response.body.hash);
196+
expect(htrBalance.available).toBe(19); // The initial 20 minus 1
197+
expect(tkaBalance.available).toBe(100); // The newly minted TKA tokens
198+
done();
199+
});
200+
201+
it('should send the created tokens to the correct address', async done => {
202+
const amountTokens = getRandomInt(100, 200);
203+
const response = await TestUtils.request
204+
.post('/wallet/create-token')
205+
.send({
206+
name: 'Token B',
207+
symbol: 'TKB',
208+
amount: amountTokens,
209+
address: await wallet1.getAddressAt(9)
210+
})
211+
.set({ 'x-wallet-id': wallet1.walletId });
212+
213+
const transaction = response.body;
214+
expect(transaction.success).toBe(true);
215+
216+
await TestUtils.pauseForWsUpdate();
217+
218+
const addr9 = await wallet1.getAddressInfo(9, transaction.hash);
219+
expect(addr9.total_amount_received).toBe(amountTokens);
220+
done();
221+
});
222+
223+
it('should send the change to the correct address', async done => {
224+
const response = await TestUtils.request
225+
.post('/wallet/create-token')
226+
.send({
227+
name: 'Token C',
228+
symbol: 'TKC',
229+
amount: 100,
230+
change_address: await wallet2.getAddressAt(5)
231+
})
232+
.set({ 'x-wallet-id': wallet2.walletId });
233+
234+
const transaction = response.body;
235+
expect(transaction.success).toBe(true);
236+
237+
// The only output with token_data equals zero is the one containing the HTR change
238+
const htrOutputIndex = transaction.outputs.findIndex(o => o.token_data === 0);
239+
const htrChange = transaction.outputs[htrOutputIndex].value;
240+
241+
await TestUtils.pauseForWsUpdate();
242+
243+
const addr5 = await wallet2.getAddressInfo(5);
244+
expect(addr5.total_amount_received).toBe(htrChange);
245+
done();
246+
});
247+
248+
it('should create a token with all available inputs', async done => {
249+
const response = await TestUtils.request
250+
.post('/wallet/create-token')
251+
.send({
252+
name: 'Token D',
253+
symbol: 'TKD',
254+
amount: 200,
255+
address: await wallet2.getAddressAt(4),
256+
change_address: await wallet2.getAddressAt(4)
257+
})
258+
.set({ 'x-wallet-id': wallet2.walletId });
259+
260+
const transaction = response.body;
261+
expect(transaction.success).toBe(true);
262+
263+
// The only output with token_data equals zero is the one containing the HTR change
264+
const htrOutputIndex = transaction.outputs.findIndex(o => o.token_data === 0);
265+
const htrChange = transaction.outputs[htrOutputIndex].value;
266+
267+
await TestUtils.pauseForWsUpdate();
268+
269+
const addr4 = await wallet2.getAddressInfo(4);
270+
expect(addr4.total_amount_received).toBe(htrChange);
271+
const addr4C = await wallet2.getAddressInfo(4, transaction.hash);
272+
expect(addr4C.total_amount_available).toBe(200);
273+
done();
274+
});
275+
});

__tests__/integration/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ services:
3333
depends_on:
3434
- fullnode
3535
ports:
36-
- "8034:8034" # Not mandatory to keep these ports open, but helpful for developer machine debugging
36+
- "8034:8034" # Not mandatory to keep this port open, but helpful for developer machine debugging
3737
- "8035:8035"
3838
command: [
3939
"http://fullnode:8080",

0 commit comments

Comments
 (0)