Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ENV NODE_ENV=production
# We will only use the node_modules folder from this step

COPY package.json package-lock.json ./
COPY patches/ ./patches/
RUN apk add --no-cache --virtual .gyp python3 make g++ &&\
npm ci --only=production

Expand All @@ -50,11 +51,12 @@ COPY --from=builder /usr/src/app/dist/ ./dist/
COPY --from=builder /usr/src/app/dist-scripts/ ./dist-scripts/
COPY --from=deps /usr/src/app/node_modules/ ./node_modules/
COPY --from=deps /usr/src/app/package.json ./
COPY lavamoat/ ./lavamoat/
COPY Makefile ./

# Install the process supervisor
RUN apk add --no-cache dumb-init make &&\
rm -rf /tmp/* /var/cache/apk/*

EXPOSE 8000
ENTRYPOINT ["dumb-init", "node", "dist/index.js"]
ENTRYPOINT ["dumb-init", "./node_modules/.bin/lavamoat", "dist/index.js"]
49 changes: 49 additions & 0 deletions __tests__/helpers/wallet.helper.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (c) Hathor Labs and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import { getWalletConfigFromSeed } from '../../src/helpers/wallet.helper';
import { WalletStartError } from '../../src/errors';
import * as loggerModule from '../../src/logger';

const STUB_SEED = 'upon tennis increase embark dismiss diamond monitor face magnet jungle scout salute rural master shoulder cry juice jeans radar present close meat antenna mind';

describe('getWalletConfigFromSeed', () => {
it('should return a wallet config for a valid seed', () => {
const config = getWalletConfigFromSeed({ seed: STUB_SEED });
expect(config).toHaveProperty('seed');
expect(config).toHaveProperty('password');
expect(config).toHaveProperty('pinCode');
});

it('should throw WalletStartError for an invalid seed', () => {
expect(() => getWalletConfigFromSeed({ seed: 'invalid seed words' })).toThrow(WalletStartError);
});

it('should throw WalletStartError when passphrase is provided but not allowed', () => {
const mockError = jest.fn();
jest.spyOn(loggerModule, 'buildAppLogger').mockReturnValue({ error: mockError });

expect(() => getWalletConfigFromSeed({
seed: STUB_SEED,
passphrase: 'my-passphrase',
allowPassphrase: false,
})).toThrow(WalletStartError);

expect(mockError).toHaveBeenCalledWith(
expect.stringContaining('passphrase is not allowed')
);
});

it('should set passphrase when allowed', () => {
const config = getWalletConfigFromSeed({
seed: STUB_SEED,
passphrase: 'my-passphrase',
allowPassphrase: true,
});
expect(config.passphrase).toBe('my-passphrase');
});
});
31 changes: 30 additions & 1 deletion __tests__/plugins/child.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { bigIntUtils } from '@hathor/wallet-lib';
import { handleMessage } from '../../src/plugins/child';
import { handleMessage, loadPlugins } from '../../src/plugins/child';
import { notificationBus, EVENTBUS_EVENT_NAME } from '../../src/services/notification.service';
import * as loggerModule from '../../src/logger';

jest.mock('../../src/services/notification.service', () => ({
notificationBus: {
Expand All @@ -9,6 +10,34 @@ jest.mock('../../src/services/notification.service', () => ({
EVENTBUS_EVENT_NAME: 'eventbus_event',
}));

describe('loadPlugins', () => {
it('should warn and skip unknown plugins', async () => {
const mockWarn = jest.fn();
jest.spyOn(loggerModule, 'buildAppLogger').mockReturnValue({ warn: mockWarn });

const plugins = await loadPlugins(['nonexistent_plugin'], {});

expect(mockWarn).toHaveBeenCalledWith('Unable to find plugin nonexistent_plugin, skipping.');
expect(plugins).toEqual([]);
});

it('should load known hathor plugins', async () => {
const plugins = await loadPlugins(['debug'], {});

expect(plugins).toHaveLength(1);
expect(plugins[0]).toHaveProperty('eventHandler');
});

it('should load custom plugins from config', async () => {
const plugins = await loadPlugins(['my_plugin'], {
my_plugin: { name: 'debug', file: 'hathor_debug.js' },
});

expect(plugins).toHaveLength(1);
expect(plugins[0]).toHaveProperty('eventHandler');
});
});

describe('handleMessage', () => {
afterEach(() => {
jest.clearAllMocks();
Expand Down
45 changes: 23 additions & 22 deletions __tests__/plugins/debug_plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* LICENSE file in the root directory of this source tree.
*/

import { bigIntUtils } from '@hathor/wallet-lib';
import { eventHandler, getSettings } from '../../src/plugins/hathor_debug';
import * as logger from '../../src/logger';

test('settings', () => {
const oldArgs = process.argv;
Expand All @@ -27,10 +27,11 @@ test('settings', () => {

test('event handler', () => {
const oldArgs = process.argv;
const logSpy = jest.spyOn(console, 'log');
const smallMsg = { type: 'small', walletId: 'default', foo: 'bar', bigInt: BigInt(Number.MAX_SAFE_INTEGER) + 1n };
const mockLoggerInfo = jest.fn();
const buildAppLoggerSpy = jest.spyOn(logger, 'buildAppLogger').mockReturnValue({ info: mockLoggerInfo });
const smallMsg = { type: 'small', walletId: 'default', foo: 'bar' };
const bigMsg = { type: 'big', walletId: 'default' };
const bigCompleteMsg = { ...bigMsg, message: '', bigInt: BigInt(Number.MAX_SAFE_INTEGER) + 1n };
const bigCompleteMsg = { ...bigMsg, message: '' };
for (let i = 0; i < 200; i++) {
// 200 * 'aaaaa'(length of 5) -> lenght of 1000
bigCompleteMsg.message += 'aaaaa';
Expand All @@ -46,30 +47,30 @@ test('event handler', () => {
'--plugin_debug_long', 'off',
];
getSettings(); // set debugLong value
logSpy.mockReset();
mockLoggerInfo.mockReset();
// small message: always log
eventHandler(smallMsg);
expect(logSpy).toHaveBeenCalledWith(toDebugMessage(bigIntUtils.JSONBigInt.stringify(smallMsg)));
logSpy.mockReset();
expect(mockLoggerInfo).toHaveBeenCalledWith(toDebugMessage(JSON.stringify(smallMsg)));
mockLoggerInfo.mockReset();
// big message: should not log
eventHandler(bigCompleteMsg);
expect(logSpy).not.toHaveBeenCalled();
expect(mockLoggerInfo).not.toHaveBeenCalled();

// debugLong: all
process.argv = [
'node', 'a_script_file.js', // not used but a value is required
'--plugin_debug_long', 'all',
];
getSettings(); // set debugLong value
logSpy.mockReset();
mockLoggerInfo.mockReset();
// small message: always log
eventHandler(smallMsg);
expect(logSpy).toHaveBeenCalledWith(toDebugMessage(bigIntUtils.JSONBigInt.stringify(smallMsg)));
logSpy.mockReset();
expect(mockLoggerInfo).toHaveBeenCalledWith(toDebugMessage(JSON.stringify(smallMsg)));
mockLoggerInfo.mockReset();
// big message: should log the entire message
eventHandler(bigCompleteMsg);
expect(logSpy).toHaveBeenCalledWith(
toDebugMessage(bigIntUtils.JSONBigInt.stringify(bigCompleteMsg))
expect(mockLoggerInfo).toHaveBeenCalledWith(
toDebugMessage(JSON.stringify(bigCompleteMsg))
);

// debugLong: unexpected value
Expand All @@ -78,30 +79,30 @@ test('event handler', () => {
'--plugin_debug_long', 'any-unexpected-value',
];
getSettings(); // set debugLong value
logSpy.mockReset();
mockLoggerInfo.mockReset();
// small message: always log
eventHandler(smallMsg);
expect(logSpy).toHaveBeenCalledWith(toDebugMessage(bigIntUtils.JSONBigInt.stringify(smallMsg)));
logSpy.mockReset();
expect(mockLoggerInfo).toHaveBeenCalledWith(toDebugMessage(JSON.stringify(smallMsg)));
mockLoggerInfo.mockReset();
// big message: should log partially
eventHandler(bigCompleteMsg);
expect(logSpy).toHaveBeenCalledWith(toDebugMessage(bigIntUtils.JSONBigInt.stringify(bigMsg)));
expect(mockLoggerInfo).toHaveBeenCalledWith(toDebugMessage(JSON.stringify(bigMsg)));

// debugLong: default (should be the same as unexpected)
process.argv = [
'node', 'a_script_file.js', // not used but a value is required
];
getSettings(); // set debugLong value
logSpy.mockReset();
mockLoggerInfo.mockReset();
// small message: always log
eventHandler(smallMsg);
expect(logSpy).toHaveBeenCalledWith(toDebugMessage(bigIntUtils.JSONBigInt.stringify(smallMsg)));
logSpy.mockReset();
expect(mockLoggerInfo).toHaveBeenCalledWith(toDebugMessage(JSON.stringify(smallMsg)));
mockLoggerInfo.mockReset();
// big message: should log partially
eventHandler(bigCompleteMsg);
expect(logSpy).toHaveBeenCalledWith(toDebugMessage(bigIntUtils.JSONBigInt.stringify(bigMsg)));
expect(mockLoggerInfo).toHaveBeenCalledWith(toDebugMessage(JSON.stringify(bigMsg)));

// Restore original argv state
process.argv = oldArgs;
logSpy.mockRestore();
buildAppLoggerSpy.mockRestore();
});
19 changes: 19 additions & 0 deletions __tests__/plugins/sqs_plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,22 @@ test('event handler', () => {
MessageBody: bigIntUtils.JSONBigInt.stringify(data),
}, expect.anything());
});

test('event handler logs error on SQS failure', () => {
const mockError = jest.fn();
// eslint-disable-next-line global-require
const loggerModule = require('../../src/logger');
jest.spyOn(loggerModule, 'buildAppLogger').mockReturnValue({ error: mockError });

const sqsMock = {
sendMessage: jest.fn((params, cb) => cb(new Error('SQS send failed'))),
};
const mockedSettings = { queueUrl: 'test-queue' };
const evHandler = eventHandlerFactory(sqsMock, mockedSettings);

evHandler({ test: 'event' });

expect(mockError).toHaveBeenCalledWith(
expect.stringContaining('plugin[sqs] error sending to sqs:')
);
});
14 changes: 14 additions & 0 deletions __tests__/plugins/ws_plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@

import { bigIntUtils } from '@hathor/wallet-lib';
import { getSockets, eventHandler, connectionHandler, getSettings } from '../../src/plugins/hathor_websocket';
import * as loggerModule from '../../src/logger';

beforeEach(() => {
jest.spyOn(loggerModule, 'buildAppLogger').mockReturnValue({
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
});
});

afterEach(() => {
jest.restoreAllMocks();
});

test('settings', () => {
const oldArgs = process.argv;
Expand Down
41 changes: 41 additions & 0 deletions lavamoat/node/policy-override.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"resources": {
"depd": {
"globals": {
"Error.prepareStackTrace": "write",
"Error.captureStackTrace": true,
"Error.stackTraceLimit": "write"
}
},

"axios": {
"globals": {
"document": false,
"importScripts": false,
"location.href": false,
"navigator": false,
"WorkerGlobalScope": false,
"XMLHttpRequest": false
}
},

"morgan>debug": {
"globals": {
"chrome": false,
"document": false,
"localStorage": false,
"navigator": false
}
},

"winston": {
"builtin": {
"http": false,
"https": false
},
"globals": {
"process.exit": false
}
}
}
}
Loading