Skip to content

Commit 16ba61f

Browse files
committed
feat: call onNewNftEvent when a new NFT tx is received (#150)
* feat: added nft utils * tests: added tests for common utils * chore: added common module to CI * refactor: moved used types to common package * tests: removed nft utils * tests: fixed nft tests on txProcessor * tests: mocking network on getconfig * tests: fixed nft tests on txProcessor * refactor: passing logger to invoke nft handler * refactor: no need to ignore ts * chore: removed jest script, instead using only test * chore: added hathor header * refactor: using common utils on txProcessor * tests: nft utils using old syntax * tests: skipped txProcessor tests * tests: fixed txProcessor tests * refactor: using isAuthority from common utils * refactor: using isAuthority from common types * refactor: using assertEnvVariablesExistance from common project * refactor: getting CREATE_NFT_MAX_RETRIES from env * docs: updated docstrings on nft utils * chore: removed events/nftCreationTx.ts * refactor: invalid import locations * refactor: remove unused lambdas (#155) * tests: fixed nft tests on txProcessor * refactor: removed all methods related to the old wallet-service txProcessor
1 parent a142dcb commit 16ba61f

33 files changed

+708
-2016
lines changed

.codebuild/buildspec.yml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ env:
1414
CONFIRM_FIRST_ADDRESS: true
1515
VOIDED_TX_OFFSET: 20
1616
TX_HISTORY_MAX_COUNT: 50
17+
CREATE_NFT_MAX_RETRIES: 3
1718
dev_DEFAULT_SERVER: "https://wallet-service.private-nodes.testnet.hathor.network/v1a/"
1819
dev_WS_DOMAIN: "ws.dev.wallet-service.testnet.hathor.network"
1920
dev_NETWORK: "testnet"

.github/workflows/main.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
nix_path: nixpkgs=channel:nixos-unstable
4141
extra_nix_config: |
4242
experimental-features = nix-command flakes
43-
43+
4444
- name: Cache Nix
4545
uses: DeterminateSystems/magic-nix-cache-action@v2
4646

@@ -59,6 +59,10 @@ jobs:
5959
CI_DB_HOST: 127.0.0.1
6060
CI_DB_PORT: 3306
6161

62+
- name: Run tests on the common modules project
63+
run: |
64+
nix develop . -c yarn workspace @wallet-service/common run test
65+
6266
- name: Run tests on the daemon
6367
run: |
6468
nix develop . -c yarn workspace sync-daemon run test

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"author": "André Abadesso <[email protected]>",
1717
"private": true,
1818
"devDependencies": {
19+
"@types/jest": "^29.5.12",
1920
"@typescript-eslint/eslint-plugin": "^7.4.0",
2021
"@typescript-eslint/parser": "^7.4.0",
2122
"dotenv": "^16.4.5",
@@ -36,6 +37,7 @@
3637
"bip32": "^4.0.0",
3738
"bitcoinjs-lib": "^6.1.5",
3839
"bitcoinjs-message": "^2.2.0",
40+
"jest": "^29.7.0",
3941
"tiny-secp256k1": "^2.2.3",
4042
"winston": "3.13.0"
4143
}

packages/wallet-service/events/nftCreationTx.ts renamed to packages/common/__tests__/events/nftCreationTx.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/* eslint-disable @typescript-eslint/no-empty-function */
1313

1414
import { Context } from 'aws-lambda';
15-
import { Transaction } from '@wallet-service/common/src/types';
15+
import { Transaction, TxOutput } from '../../src/types';
1616

1717
/**
1818
* A sample transaction for a NFT creation, as obtained by a wallet's history methods
@@ -149,11 +149,12 @@ export function getTransaction(): Transaction {
149149
spent_by: o.spent_by,
150150
token_data: o.token_data,
151151
locked: false,
152-
})),
152+
})) as TxOutput[],
153153
height: 8,
154154
token_name: nftCreationTx.token_name,
155155
token_symbol: nftCreationTx.token_symbol,
156156
};
157+
157158
return result;
158159
}
159160

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const mockedAddAlert = jest.fn();
2+
export default jest.mock('@src/utils/alerting.utils', () => ({
3+
addAlert: mockedAddAlert.mockReturnValue(Promise.resolve()),
4+
}));

packages/wallet-service/tests/utils/nft.utils.test.ts renamed to packages/common/__tests__/utils/nft.utils.test.ts

+48-25
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
1-
import { Logger } from 'winston';
1+
// @ts-ignore: Using old wallet-lib version, no types exported
22
import hathorLib from '@hathor/wallet-lib';
3-
import { mockedAddAlert } from '@tests/utils/alerting.utils.mock';
4-
import { Severity } from '@wallet-service/common/src/types';
5-
import { MAX_METADATA_UPDATE_RETRIES, NftUtils } from '@src/utils/nft.utils';
6-
import { getHandlerContext, getTransaction } from '@events/nftCreationTx';
3+
import { mockedAddAlert } from './alerting.utils.mock';
4+
import { Severity } from '@src/types';
5+
import { NftUtils } from '@src/utils/nft.utils';
6+
import { getHandlerContext, getTransaction } from '../events/nftCreationTx';
77
import {
88
LambdaClient as LambdaClientMock,
99
InvokeCommandOutput,
1010
} from '@aws-sdk/client-lambda';
11+
import { Logger } from 'winston';
12+
13+
jest.mock('winston', () => {
14+
class FakeLogger {
15+
warn() {
16+
return jest.fn();
17+
}
18+
error() {
19+
return jest.fn();
20+
}
21+
info() {
22+
return jest.fn();
23+
}
24+
};
25+
26+
return {
27+
Logger: FakeLogger,
28+
}
29+
});
1130

1231
jest.mock('@aws-sdk/client-lambda', () => {
1332
const mLambda = { send: jest.fn() };
@@ -18,19 +37,23 @@ jest.mock('@aws-sdk/client-lambda', () => {
1837
};
1938
});
2039

40+
const network = new hathorLib.Network('testnet');
41+
const logger = new Logger();
42+
2143
describe('shouldInvokeNftHandlerForTx', () => {
2244
it('should return false for a NFT transaction if the feature is disabled', () => {
2345
expect.hasAssertions();
2446

2547
// Preparation
2648
const tx = getTransaction();
27-
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx);
49+
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx, network, logger);
2850
expect(isNftTransaction).toStrictEqual(true);
2951

3052
expect(process.env.NFT_AUTO_REVIEW_ENABLED).not.toStrictEqual('true');
3153

3254
// Execution
33-
const result = NftUtils.shouldInvokeNftHandlerForTx(tx);
55+
// @ts-ignore
56+
const result = NftUtils.shouldInvokeNftHandlerForTx(tx, network, logger);
3457

3558
// Assertion
3659
expect(result).toBe(false);
@@ -41,14 +64,14 @@ describe('shouldInvokeNftHandlerForTx', () => {
4164

4265
// Preparation
4366
const tx = getTransaction();
44-
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx);
67+
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx, network, logger);
4568
expect(isNftTransaction).toStrictEqual(true);
4669

4770
const oldValue = process.env.NFT_AUTO_REVIEW_ENABLED;
4871
process.env.NFT_AUTO_REVIEW_ENABLED = 'true';
4972

5073
// Execution
51-
const result = NftUtils.shouldInvokeNftHandlerForTx(tx);
74+
const result = NftUtils.shouldInvokeNftHandlerForTx(tx, network, logger);
5275

5376
// Assertion
5477
expect(result).toBe(true);
@@ -71,21 +94,21 @@ describe('isTransactionNFTCreation', () => {
7194
// Incorrect version
7295
tx = getTransaction();
7396
tx.version = hathorLib.constants.DEFAULT_TX_VERSION;
74-
result = NftUtils.isTransactionNFTCreation(tx);
97+
result = NftUtils.isTransactionNFTCreation(tx, network, logger);
7598
expect(result).toBe(false);
7699
expect(spyCreateTx).not.toHaveBeenCalled();
77100

78101
// Missing name
79102
tx = getTransaction();
80103
tx.token_name = undefined;
81-
result = NftUtils.isTransactionNFTCreation(tx);
104+
result = NftUtils.isTransactionNFTCreation(tx, network, logger);
82105
expect(result).toBe(false);
83106
expect(spyCreateTx).not.toHaveBeenCalled();
84107

85108
// Missing symbol
86109
tx = getTransaction();
87110
tx.token_symbol = undefined;
88-
result = NftUtils.isTransactionNFTCreation(tx);
111+
result = NftUtils.isTransactionNFTCreation(tx, network, logger);
89112
expect(result).toBe(false);
90113
expect(spyCreateTx).not.toHaveBeenCalled();
91114

@@ -102,7 +125,7 @@ describe('isTransactionNFTCreation', () => {
102125

103126
// Validation
104127
const tx = getTransaction();
105-
const result = NftUtils.isTransactionNFTCreation(tx);
128+
const result = NftUtils.isTransactionNFTCreation(tx, network, logger);
106129
expect(result).toBe(true);
107130

108131
// Reverting mocks
@@ -114,7 +137,7 @@ describe('isTransactionNFTCreation', () => {
114137

115138
// Validation
116139
const tx = getTransaction();
117-
const result = NftUtils.isTransactionNFTCreation(tx);
140+
const result = NftUtils.isTransactionNFTCreation(tx, network, logger);
118141
expect(result).toBe(true);
119142
});
120143

@@ -129,7 +152,7 @@ describe('isTransactionNFTCreation', () => {
129152

130153
// Validation
131154
const tx = getTransaction();
132-
const result = NftUtils.isTransactionNFTCreation(tx);
155+
const result = NftUtils.isTransactionNFTCreation(tx, network, logger);
133156
expect(result).toBe(false);
134157

135158
// Reverting mocks
@@ -155,11 +178,11 @@ describe('createOrUpdateNftMetadata', () => {
155178
const expectedUpdateResponse = { updated: 'ok' };
156179

157180
spyUpdateMetadata.mockImplementation(async () => expectedUpdateResponse);
158-
const result = await NftUtils.createOrUpdateNftMetadata('sampleUid');
181+
const result = await NftUtils.createOrUpdateNftMetadata('sampleUid', 5, logger);
159182

160183
expect(spyUpdateMetadata).toHaveBeenCalledTimes(1);
161184

162-
expect(spyUpdateMetadata).toHaveBeenCalledWith('sampleUid', expectedUpdateRequest);
185+
expect(spyUpdateMetadata).toHaveBeenCalledWith('sampleUid', expectedUpdateRequest, 5, logger);
163186
expect(result).toBeUndefined(); // The method returns void
164187
});
165188
});
@@ -182,7 +205,7 @@ describe('_updateMetadata', () => {
182205
const oldStage = process.env.STAGE;
183206
process.env.STAGE = 'dev'; // Testing all code branches, including the developer ones, for increased coverage
184207

185-
const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' });
208+
const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' }, 5, logger);
186209
expect(result).toStrictEqual(expectedLambdaResponse);
187210
process.env.STAGE = oldStage;
188211
});
@@ -198,7 +221,7 @@ describe('_updateMetadata', () => {
198221
};
199222
const mLambdaClient = new LambdaClientMock({});
200223
(mLambdaClient.send as jest.Mocked<any>).mockImplementation(async () => {
201-
if (failureCount < MAX_METADATA_UPDATE_RETRIES - 1) {
224+
if (failureCount < 4) {
202225
++failureCount;
203226
return {
204227
StatusCode: 500,
@@ -208,7 +231,7 @@ describe('_updateMetadata', () => {
208231
return expectedLambdaResponse;
209232
});
210233

211-
const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' });
234+
const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' }, 5, logger);
212235
expect(result).toStrictEqual(expectedLambdaResponse);
213236
});
214237

@@ -219,7 +242,7 @@ describe('_updateMetadata', () => {
219242
let failureCount = 0;
220243
const mLambdaClient = new LambdaClientMock({});
221244
(mLambdaClient.send as jest.Mocked<any>).mockImplementation(() => {
222-
if (failureCount < MAX_METADATA_UPDATE_RETRIES) {
245+
if (failureCount < 5) {
223246
++failureCount;
224247
return {
225248
StatusCode: 500,
@@ -233,7 +256,7 @@ describe('_updateMetadata', () => {
233256
});
234257

235258
// eslint-disable-next-line jest/valid-expect
236-
expect(NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' }))
259+
expect(NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' }, network, logger))
237260
.rejects.toThrow(new Error('Metadata update failed for tx_id: sampleUid.'));
238261
});
239262
});
@@ -250,7 +273,7 @@ describe('invokeNftHandlerLambda', () => {
250273
const mLambdaClient = new LambdaClientMock({});
251274
(mLambdaClient.send as jest.Mocked<any>).mockImplementationOnce(async () => expectedLambdaResponse);
252275

253-
await expect(NftUtils.invokeNftHandlerLambda('sampleUid')).resolves.toBeUndefined();
276+
await expect(NftUtils.invokeNftHandlerLambda('sampleUid', 'local', logger)).resolves.toBeUndefined();
254277
});
255278

256279
it('should throw when payload response status is invalid', async () => {
@@ -264,15 +287,15 @@ describe('invokeNftHandlerLambda', () => {
264287
};
265288
(mLambdaClient.send as jest.Mocked<any>).mockImplementation(() => expectedLambdaResponse);
266289

267-
await expect(NftUtils.invokeNftHandlerLambda('sampleUid'))
290+
await expect(NftUtils.invokeNftHandlerLambda('sampleUid', 'local', logger))
268291
.rejects.toThrow(new Error('onNewNftEvent lambda invoke failed for tx: sampleUid'));
269292

270293
expect(mockedAddAlert).toHaveBeenCalledWith(
271294
'Error on NFTHandler lambda',
272295
'Erroed on invokeNftHandlerLambda invocation',
273296
Severity.MINOR,
274297
{ TxId: 'sampleUid' },
275-
expect.any(Logger),
298+
logger,
276299
);
277300
});
278301
});

packages/common/jest.config.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
roots: ["<rootDir>/__tests__"],
3+
testRegex: ".*\\.test\\.ts$",
4+
moduleNameMapper: {
5+
'^@src/(.*)$': '<rootDir>/src/$1',
6+
'^@tests/(.*)$': '<rootDir>/__tests__/$1',
7+
'^@events/(.*)$': '<rootDir>/__tests__/events/$1',
8+
},
9+
transform: {
10+
"^.+\\.ts$": ["ts-jest", {
11+
tsconfig: "./tsconfig.json",
12+
babelConfig: {
13+
sourceMaps: true,
14+
}
15+
}]
16+
},
17+
moduleFileExtensions: ["ts", "js", "json", "node"]
18+
};

packages/common/package.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
{
22
"name": "@wallet-service/common",
33
"packageManager": "[email protected]",
4+
"scripts": {
5+
"test": "jest --runInBand --collectCoverage --detectOpenHandles --forceExit"
6+
},
47
"peerDependencies": {
58
"@aws-sdk/client-lambda": "3.540.0",
69
"@hathor/wallet-lib": "0.39.0",
710
"winston": "^3.13.0"
811
},
912
"devDependencies": {
10-
"@types/node": "^20.11.30"
13+
"@types/aws-lambda": "^8.10.136",
14+
"@types/node": "^20.11.30",
15+
"jest": "^29.6.4",
16+
"ts-jest": "^29.1.2",
17+
"typescript": "^5.4.3"
1118
}
1219
}

0 commit comments

Comments
 (0)