Skip to content

Commit

Permalink
feat: call onNewNftEvent when a new NFT tx is received (#150)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
andreabadesso authored Apr 19, 2024
1 parent 8604db6 commit c19c2c3
Show file tree
Hide file tree
Showing 33 changed files with 708 additions and 2,016 deletions.
1 change: 1 addition & 0 deletions .codebuild/buildspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ env:
CONFIRM_FIRST_ADDRESS: true
VOIDED_TX_OFFSET: 20
TX_HISTORY_MAX_COUNT: 50
CREATE_NFT_MAX_RETRIES: 3
dev_DEFAULT_SERVER: "https://wallet-service.private-nodes.testnet.hathor.network/v1a/"
dev_WS_DOMAIN: "ws.dev.wallet-service.testnet.hathor.network"
dev_NETWORK: "testnet"
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
nix_path: nixpkgs=channel:nixos-unstable
extra_nix_config: |
experimental-features = nix-command flakes
- name: Cache Nix
uses: DeterminateSystems/magic-nix-cache-action@v2

Expand All @@ -59,6 +59,10 @@ jobs:
CI_DB_HOST: 127.0.0.1
CI_DB_PORT: 3306

- name: Run tests on the common modules project
run: |
nix develop . -c yarn workspace @wallet-service/common run test
- name: Run tests on the daemon
run: |
nix develop . -c yarn workspace sync-daemon run test
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"author": "André Abadesso <[email protected]>",
"private": true,
"devDependencies": {
"@types/jest": "^29.5.12",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"dotenv": "^16.4.5",
Expand All @@ -36,6 +37,7 @@
"bip32": "^4.0.0",
"bitcoinjs-lib": "^6.1.5",
"bitcoinjs-message": "^2.2.0",
"jest": "^29.7.0",
"tiny-secp256k1": "^2.2.3",
"winston": "3.13.0"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/* eslint-disable @typescript-eslint/no-empty-function */

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

/**
* A sample transaction for a NFT creation, as obtained by a wallet's history methods
Expand Down Expand Up @@ -149,11 +149,12 @@ export function getTransaction(): Transaction {
spent_by: o.spent_by,
token_data: o.token_data,
locked: false,
})),
})) as TxOutput[],
height: 8,
token_name: nftCreationTx.token_name,
token_symbol: nftCreationTx.token_symbol,
};

return result;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/common/__tests__/utils/alerting.utils.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const mockedAddAlert = jest.fn();
export default jest.mock('@src/utils/alerting.utils', () => ({
addAlert: mockedAddAlert.mockReturnValue(Promise.resolve()),
}));
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
import { Logger } from 'winston';
// @ts-ignore: Using old wallet-lib version, no types exported
import hathorLib from '@hathor/wallet-lib';
import { mockedAddAlert } from '@tests/utils/alerting.utils.mock';
import { Severity } from '@wallet-service/common/src/types';
import { MAX_METADATA_UPDATE_RETRIES, NftUtils } from '@src/utils/nft.utils';
import { getHandlerContext, getTransaction } from '@events/nftCreationTx';
import { mockedAddAlert } from './alerting.utils.mock';
import { Severity } from '@src/types';
import { NftUtils } from '@src/utils/nft.utils';
import { getHandlerContext, getTransaction } from '../events/nftCreationTx';
import {
LambdaClient as LambdaClientMock,
InvokeCommandOutput,
} from '@aws-sdk/client-lambda';
import { Logger } from 'winston';

jest.mock('winston', () => {
class FakeLogger {
warn() {
return jest.fn();
}
error() {
return jest.fn();
}
info() {
return jest.fn();
}
};

return {
Logger: FakeLogger,
}
});

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

const network = new hathorLib.Network('testnet');
const logger = new Logger();

describe('shouldInvokeNftHandlerForTx', () => {
it('should return false for a NFT transaction if the feature is disabled', () => {
expect.hasAssertions();

// Preparation
const tx = getTransaction();
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx);
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(isNftTransaction).toStrictEqual(true);

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

// Execution
const result = NftUtils.shouldInvokeNftHandlerForTx(tx);
// @ts-ignore
const result = NftUtils.shouldInvokeNftHandlerForTx(tx, network, logger);

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

// Preparation
const tx = getTransaction();
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx);
const isNftTransaction = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(isNftTransaction).toStrictEqual(true);

const oldValue = process.env.NFT_AUTO_REVIEW_ENABLED;
process.env.NFT_AUTO_REVIEW_ENABLED = 'true';

// Execution
const result = NftUtils.shouldInvokeNftHandlerForTx(tx);
const result = NftUtils.shouldInvokeNftHandlerForTx(tx, network, logger);

// Assertion
expect(result).toBe(true);
Expand All @@ -71,21 +94,21 @@ describe('isTransactionNFTCreation', () => {
// Incorrect version
tx = getTransaction();
tx.version = hathorLib.constants.DEFAULT_TX_VERSION;
result = NftUtils.isTransactionNFTCreation(tx);
result = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(result).toBe(false);
expect(spyCreateTx).not.toHaveBeenCalled();

// Missing name
tx = getTransaction();
tx.token_name = undefined;
result = NftUtils.isTransactionNFTCreation(tx);
result = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(result).toBe(false);
expect(spyCreateTx).not.toHaveBeenCalled();

// Missing symbol
tx = getTransaction();
tx.token_symbol = undefined;
result = NftUtils.isTransactionNFTCreation(tx);
result = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(result).toBe(false);
expect(spyCreateTx).not.toHaveBeenCalled();

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

// Validation
const tx = getTransaction();
const result = NftUtils.isTransactionNFTCreation(tx);
const result = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(result).toBe(true);

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

// Validation
const tx = getTransaction();
const result = NftUtils.isTransactionNFTCreation(tx);
const result = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(result).toBe(true);
});

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

// Validation
const tx = getTransaction();
const result = NftUtils.isTransactionNFTCreation(tx);
const result = NftUtils.isTransactionNFTCreation(tx, network, logger);
expect(result).toBe(false);

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

spyUpdateMetadata.mockImplementation(async () => expectedUpdateResponse);
const result = await NftUtils.createOrUpdateNftMetadata('sampleUid');
const result = await NftUtils.createOrUpdateNftMetadata('sampleUid', 5, logger);

expect(spyUpdateMetadata).toHaveBeenCalledTimes(1);

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

const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' });
const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' }, 5, logger);
expect(result).toStrictEqual(expectedLambdaResponse);
process.env.STAGE = oldStage;
});
Expand All @@ -198,7 +221,7 @@ describe('_updateMetadata', () => {
};
const mLambdaClient = new LambdaClientMock({});
(mLambdaClient.send as jest.Mocked<any>).mockImplementation(async () => {
if (failureCount < MAX_METADATA_UPDATE_RETRIES - 1) {
if (failureCount < 4) {
++failureCount;
return {
StatusCode: 500,
Expand All @@ -208,7 +231,7 @@ describe('_updateMetadata', () => {
return expectedLambdaResponse;
});

const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' });
const result = await NftUtils._updateMetadata('sampleUid', { sampleData: 'fake' }, 5, logger);
expect(result).toStrictEqual(expectedLambdaResponse);
});

Expand All @@ -219,7 +242,7 @@ describe('_updateMetadata', () => {
let failureCount = 0;
const mLambdaClient = new LambdaClientMock({});
(mLambdaClient.send as jest.Mocked<any>).mockImplementation(() => {
if (failureCount < MAX_METADATA_UPDATE_RETRIES) {
if (failureCount < 5) {
++failureCount;
return {
StatusCode: 500,
Expand All @@ -233,7 +256,7 @@ describe('_updateMetadata', () => {
});

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

await expect(NftUtils.invokeNftHandlerLambda('sampleUid')).resolves.toBeUndefined();
await expect(NftUtils.invokeNftHandlerLambda('sampleUid', 'local', logger)).resolves.toBeUndefined();
});

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

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

expect(mockedAddAlert).toHaveBeenCalledWith(
'Error on NFTHandler lambda',
'Erroed on invokeNftHandlerLambda invocation',
Severity.MINOR,
{ TxId: 'sampleUid' },
expect.any(Logger),
logger,
);
});
});
Expand Down
18 changes: 18 additions & 0 deletions packages/common/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
roots: ["<rootDir>/__tests__"],
testRegex: ".*\\.test\\.ts$",
moduleNameMapper: {
'^@src/(.*)$': '<rootDir>/src/$1',
'^@tests/(.*)$': '<rootDir>/__tests__/$1',
'^@events/(.*)$': '<rootDir>/__tests__/events/$1',
},
transform: {
"^.+\\.ts$": ["ts-jest", {
tsconfig: "./tsconfig.json",
babelConfig: {
sourceMaps: true,
}
}]
},
moduleFileExtensions: ["ts", "js", "json", "node"]
};
9 changes: 8 additions & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
{
"name": "@wallet-service/common",
"packageManager": "[email protected]",
"scripts": {
"test": "jest --runInBand --collectCoverage --detectOpenHandles --forceExit"
},
"peerDependencies": {
"@aws-sdk/client-lambda": "3.540.0",
"@hathor/wallet-lib": "0.39.0",
"winston": "^3.13.0"
},
"devDependencies": {
"@types/node": "^20.11.30"
"@types/aws-lambda": "^8.10.136",
"@types/node": "^20.11.30",
"jest": "^29.6.4",
"ts-jest": "^29.1.2",
"typescript": "^5.4.3"
}
}
Loading

0 comments on commit c19c2c3

Please sign in to comment.