Skip to content

Commit a8151b2

Browse files
Integration Tests: balance, tx-history, address-info and transaction routes (#149)
* test: Implementing first integrated tests with the fullnode * test: Working proof-of-concept * refactor: moved integration test to separate folder + config file * feat: first draft for logging test network transactions * test: first balance tests * feat: log filename indicates the suite it tested * test: implementing private network on docker images pending: include a miner * test: mining private network on docker images * test: testing on github workflow * fix: uploading artifacts despite failure for debugging * feat: single command to build network environment, test and cleanup * fix: delaying injectFundsIntoAddress to avoid workflow failures * test: create token test * test: testing failure, but did not work with local cache * test: tx history tests without error handling * test: address info tests without error handling * test: transaction route tests without error handling * chore: changed integration test workflow to run on dev and master * docs: Adds various comments and documentations to the Integration Tests * fix: lgtm raised issues * fix: mocked tests ignore paths * style: integration tests linting * fix: getRandomInt function had incorrect logic * chore: removing proof of concept file * style: eslint fixes * docs: clarifying jsdocs and comments * fix: minor failures on log helpers * test: new tests for locked balances * test: new assertions for transaction order * style: fix eslint parens * refactor: improve wallet startup's "status" validation * refactor: using the WalletHelper.createToken instead of a direct http * fix: locked amount on addresses is not fixed as supposed * refactor: improving lib code reuse * Update __tests__/integration/utils/test-utils-integration.js Co-authored-by: André Abadesso <[email protected]> * Update __tests__/integration/utils/wallet-helper.js Co-authored-by: André Abadesso <[email protected]> * refactor: error handling on logger * refactor: txLogger now using winston instead of direct fs * refactor: txLogger adapter to better use of winston logs * refactor: logs folder now fetched from config file Note: When on Github, the config "outputFolder" property should match the one specified on the integration-test.yml workflow. Otherwise, the test artifacts will not be correctly uploaded Co-authored-by: André Abadesso <[email protected]>
1 parent fd1ebaa commit a8151b2

16 files changed

+1279
-1
lines changed

.eslintrc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"extends": "airbnb-base",
3+
"parser": "@babel/eslint-parser",
4+
"rules": {
5+
"arrow-parens": ["error", "as-needed"],
6+
"import/no-named-as-default": 0,
7+
"import/prefer-default-export": 0,
8+
"quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
9+
"comma-dangle": 0,
10+
"object-curly-newline": ["error", {"ObjectPattern": { "multiline": true }}],
11+
"eqeqeq": [1, "allow-null"],
12+
"no-continue": 0,
13+
"no-cond-assign": 1,
14+
"no-constant-condition": 0,
15+
"no-control-regex": 1,
16+
"no-debugger": 1,
17+
"no-dupe-keys": 1,
18+
"no-ex-assign": 1,
19+
"no-extra-boolean-cast": 1,
20+
"no-func-assign": 1,
21+
"no-regex-spaces": 1,
22+
"no-unreachable": 1,
23+
"no-fallthrough": 1,
24+
"no-lone-blocks": 1,
25+
"no-delete-var": 1,
26+
"no-shadow": 1,
27+
"no-shadow-restricted-names": 1,
28+
"no-undef": 2,
29+
"no-undef-init": 1,
30+
"no-use-before-define": 0,
31+
"no-unused-vars": [1, {"vars": "all", "args": "none"}],
32+
"no-underscore-dangle": 0,
33+
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
34+
"no-await-in-loop": "off",
35+
"no-plusplus": "off",
36+
"guard-for-in": "off"
37+
},
38+
"overrides": [
39+
{
40+
"files": [
41+
"**/*.test.js"
42+
],
43+
"env": {
44+
"jest": true
45+
}
46+
}
47+
]
48+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: integration-test
2+
on:
3+
push:
4+
branches:
5+
- master
6+
- dev
7+
tags:
8+
- v*
9+
pull_request:
10+
branches:
11+
- dev
12+
- master
13+
jobs:
14+
itest:
15+
runs-on: ubuntu-20.04
16+
timeout-minutes: 20
17+
18+
strategy:
19+
matrix:
20+
node-version: [14.x]
21+
22+
steps:
23+
- uses: actions/checkout@v2
24+
25+
- name: Use Node.js ${{ matrix.node-version }}
26+
uses: actions/setup-node@v1
27+
with:
28+
node-version: ${{ matrix.node-version }}
29+
30+
- name: Install dependencies
31+
run: npm install
32+
33+
- name: Copy config file
34+
run: cp ./__tests__/integration/configuration/config.js.template ./src/config.js
35+
36+
- name: Run the tests
37+
run: npm run test_integration
38+
39+
- name: Upload debug transaction logs
40+
if: always()
41+
uses: actions/upload-artifact@v2
42+
with:
43+
name: test-transacion-logs
44+
path: tmp
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import {
2+
getRandomInt,
3+
HATHOR_TOKEN_ID,
4+
TestUtils,
5+
WALLET_CONSTANTS
6+
} from './utils/test-utils-integration';
7+
import { WalletHelper } from './utils/wallet-helper';
8+
9+
describe('address-info routes', () => {
10+
let wallet1;
11+
let wallet2;
12+
const address1balance = getRandomInt(200, 100);
13+
let customTokenHash;
14+
15+
beforeAll(async () => {
16+
try {
17+
// A random HTR value for the first wallet
18+
wallet1 = new WalletHelper('addinfo-1');
19+
await wallet1.start();
20+
await wallet1.injectFunds(address1balance, 1);
21+
22+
// A fixed custom token amount for the second wallet
23+
wallet2 = new WalletHelper('addinfo-2');
24+
await wallet2.start();
25+
await wallet2.injectFunds(10);
26+
const customToken = await wallet2.createToken({
27+
amount: 500,
28+
name: 'AddInfo Token',
29+
symbol: 'AIT',
30+
address: await wallet2.getAddressAt(1),
31+
change_address: await wallet2.getAddressAt(0)
32+
});
33+
customTokenHash = customToken.hash;
34+
35+
/*
36+
* The state here should be:
37+
* wallet1[1] with some value between 100 and 200 HTR
38+
* wallet2[0] with 5 HTR
39+
* wallet2[1] with 500 AIT
40+
*/
41+
} catch (err) {
42+
TestUtils.logError(err.stack);
43+
}
44+
});
45+
46+
afterAll(async () => {
47+
await wallet1.stop();
48+
await wallet2.stop();
49+
});
50+
51+
it('should return results for an address (empty)', async done => {
52+
const response = await TestUtils.request
53+
.get('/wallet/address-info')
54+
.query({ address: await wallet1.getAddressAt(0) })
55+
.set({ 'x-wallet-id': wallet1.walletId });
56+
57+
expect(response.status).toBe(200);
58+
59+
const results = response.body;
60+
expect(results.success).toBeTruthy();
61+
expect(results.token).toBe(HATHOR_TOKEN_ID);
62+
expect(results.index).toBe(0);
63+
expect(results.total_amount_received).toBe(0);
64+
expect(results.total_amount_sent).toBe(0);
65+
expect(results.total_amount_available).toBe(0);
66+
expect(results.total_amount_locked).toBe(0);
67+
done();
68+
});
69+
70+
it('should return results for an address with a single receiving transaction', async done => {
71+
const response = await TestUtils.request
72+
.get('/wallet/address-info')
73+
.query({ address: await wallet1.getAddressAt(1) })
74+
.set({ 'x-wallet-id': wallet1.walletId });
75+
76+
expect(response.status).toBe(200);
77+
78+
const results = response.body;
79+
expect(results.success).toBeTruthy();
80+
expect(results.token).toBe(HATHOR_TOKEN_ID);
81+
expect(results.index).toBe(1);
82+
expect(results.total_amount_received).toBe(address1balance);
83+
expect(results.total_amount_sent).toBe(0);
84+
expect(results.total_amount_available).toBe(address1balance);
85+
expect(results.total_amount_locked).toBe(0);
86+
done();
87+
});
88+
89+
it('should return correct locked balance for an address with miner rewards', async done => {
90+
const response = await TestUtils.request
91+
.get('/wallet/address-info')
92+
.query({ address: WALLET_CONSTANTS.genesis.addresses[1] }) // Miner rewards address
93+
.set({ 'x-wallet-id': WALLET_CONSTANTS.genesis.walletId });
94+
95+
expect(response.status).toBe(200);
96+
97+
const results = response.body;
98+
expect(results.success).toBeTruthy();
99+
expect(results.token).toBe(HATHOR_TOKEN_ID);
100+
expect(results.index).toBe(2);
101+
expect(results.total_amount_received).toBeGreaterThan(0);
102+
103+
/*
104+
* According to the REWARD_SPEND_MIN_BLOCKS variable in the ./configuration/privnet.py file
105+
* the miner rewards are locked for exactly one block. Since we have only one miner reward
106+
* address, this value should be always 6400 or greater.
107+
*
108+
* Should another miner reward address be included later, this assertion must be recalculated.
109+
*/
110+
expect(results.total_amount_locked).toBeGreaterThanOrEqual(6400);
111+
done();
112+
});
113+
114+
it('should return results for an address with send/receive transactions', async done => {
115+
const response = await TestUtils.request
116+
.get('/wallet/address-info')
117+
.query({ address: await wallet2.getAddressAt(0) })
118+
.set({ 'x-wallet-id': wallet2.walletId });
119+
120+
expect(response.status).toBe(200);
121+
122+
const results = response.body;
123+
expect(results.success).toBeTruthy();
124+
expect(results.token).toBe(HATHOR_TOKEN_ID);
125+
expect(results.index).toBe(0);
126+
expect(results.total_amount_received).toBe(15); // 10 from genesis, 5 from token creation change
127+
expect(results.total_amount_sent).toBe(10); // token creation tx
128+
expect(results.total_amount_available).toBe(5); // change
129+
expect(results.total_amount_locked).toBe(0);
130+
done();
131+
});
132+
133+
it('should return results for custom token for an address (empty)', async done => {
134+
const response = await TestUtils.request
135+
.get('/wallet/address-info')
136+
.query({
137+
address: await wallet2.getAddressAt(0),
138+
token: customTokenHash,
139+
})
140+
.set({ 'x-wallet-id': wallet2.walletId });
141+
142+
expect(response.status).toBe(200);
143+
144+
const results = response.body;
145+
expect(results.success).toBeTruthy();
146+
expect(results.token).toBe(customTokenHash);
147+
expect(results.index).toBe(0);
148+
expect(results.total_amount_received).toBe(0);
149+
expect(results.total_amount_sent).toBe(0);
150+
expect(results.total_amount_available).toBe(0);
151+
expect(results.total_amount_locked).toBe(0);
152+
done();
153+
});
154+
155+
it('should return results for custom token on an address with a single transaction', async done => {
156+
const response = await TestUtils.request
157+
.get('/wallet/address-info')
158+
.query({
159+
address: await wallet2.getAddressAt(1),
160+
token: customTokenHash,
161+
})
162+
.set({ 'x-wallet-id': wallet2.walletId });
163+
164+
expect(response.status).toBe(200);
165+
166+
const results = response.body;
167+
expect(results.success).toBeTruthy();
168+
expect(results.token).toBe(customTokenHash);
169+
expect(results.index).toBe(1);
170+
expect(results.total_amount_received).toBe(500);
171+
expect(results.total_amount_sent).toBe(0);
172+
expect(results.total_amount_available).toBe(500);
173+
expect(results.total_amount_locked).toBe(0);
174+
done();
175+
});
176+
});

__tests__/integration/balance.test.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { getRandomInt, TestUtils, WALLET_CONSTANTS } from './utils/test-utils-integration';
2+
import { WalletHelper } from './utils/wallet-helper';
3+
4+
describe('balance routes', () => {
5+
/** @type WalletHelper */
6+
let wallet1;
7+
let wallet2;
8+
let wallet3;
9+
const wallet2Balance = getRandomInt(200);
10+
11+
beforeAll(async () => {
12+
try {
13+
// First wallet, no balance
14+
wallet1 = new WalletHelper('balance1');
15+
await wallet1.start();
16+
17+
// Second wallet, random balance
18+
wallet2 = new WalletHelper('balance2');
19+
await wallet2.start();
20+
await wallet2.injectFunds(wallet2Balance);
21+
22+
// Third wallet, balance to be used for custom tokens
23+
wallet3 = new WalletHelper('custom3');
24+
await wallet3.start();
25+
await wallet3.injectFunds(100);
26+
} catch (err) {
27+
TestUtils.logError(err.stack);
28+
}
29+
});
30+
31+
afterAll(async () => {
32+
await wallet1.stop();
33+
await wallet2.stop();
34+
await wallet3.stop();
35+
});
36+
37+
it('should return zero for an empty wallet', async done => {
38+
const balanceResult = await TestUtils.request
39+
.get('/wallet/balance')
40+
.set({ 'x-wallet-id': wallet1.walletId });
41+
42+
expect(balanceResult.body.available).toBe(0);
43+
expect(balanceResult.body.locked).toBe(0);
44+
done();
45+
});
46+
47+
it('should return correct balance for a wallet with one transaction', async done => {
48+
const balanceResult = await TestUtils.request
49+
.get('/wallet/balance')
50+
.set({ 'x-wallet-id': wallet2.walletId });
51+
52+
expect(balanceResult.body.available).toBe(wallet2Balance);
53+
expect(balanceResult.body.locked).toBe(0);
54+
done();
55+
});
56+
57+
it('should return some locked balance for the miner wallet', async done => {
58+
const balanceResult = await TestUtils.request
59+
.get('/wallet/balance')
60+
.set({ 'x-wallet-id': WALLET_CONSTANTS.genesis.walletId });
61+
62+
/*
63+
* According to the REWARD_SPEND_MIN_BLOCKS variable in the ./configuration/privnet.py file
64+
* the miner rewards are locked for exactly one block. Since we have only one miner reward
65+
* address, this value should be always 6400 or greater.
66+
*
67+
* Should another miner reward address be included later, this assertion must be recalculated.
68+
*/
69+
expect(balanceResult.body.locked).toBeGreaterThanOrEqual(6400);
70+
done();
71+
});
72+
73+
it('should return correct balance for a custom token (empty)', async done => {
74+
const balanceResult = await TestUtils.request
75+
.get('/wallet/balance')
76+
.query({ token: 'TST' })
77+
.set({ 'x-wallet-id': wallet1.walletId });
78+
79+
expect(balanceResult.body.available).toBe(0);
80+
expect(balanceResult.body.locked).toBe(0);
81+
done();
82+
});
83+
84+
it('should return correct balance for a custom token', async done => {
85+
const tokenAmount = getRandomInt(200, 100);
86+
const newToken = await wallet3.createToken({
87+
name: 'Test Token',
88+
symbol: 'TST',
89+
amount: tokenAmount,
90+
});
91+
const tokenHash = newToken.hash;
92+
93+
const balanceResult = await TestUtils.request
94+
.get('/wallet/balance')
95+
.query({ token: tokenHash })
96+
.set({ 'x-wallet-id': wallet3.walletId });
97+
98+
expect(balanceResult.body.available).toBe(tokenAmount);
99+
expect(balanceResult.body.locked).toBe(0);
100+
done();
101+
});
102+
});

0 commit comments

Comments
 (0)