Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/unit test #63

Merged
merged 12 commits into from
Jun 26, 2024
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
25 changes: 25 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Coverage Diff

on:
push:
branches:
- master
pull_request: {}

jobs:
test:
name: Coverage Diff
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 20
cache: yarn
- run: yarn install
- run: yarn run test:coverage
- name: Coverage Diff
uses: greatwizard/coverage-diff-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
24 changes: 10 additions & 14 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,10 @@ export default {
coverageDirectory: 'coverage',

// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
coveragePathIgnorePatterns: ['/node_modules/', '/src/utils/constants.js', '/src/command/index.js'],

// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
coverageReporters: ['text', 'json-summary'],

// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: null,
Expand Down Expand Up @@ -143,10 +136,13 @@ export default {
// testLocationInResults: false,

// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
testMatch: [
// '**/test/utils/Logger.test.js'
'**/test/command/dappServer/socket-sign.test.js',
'**/test/**/?(*.)+(spec|test).[jt]s?(x)'
// "**/?(*.)+(spec|test).[tj]s?(x)"
],
testTimeout: 20000,

// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
Expand Down Expand Up @@ -176,7 +172,7 @@ export default {
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
transformIgnorePatterns: [
// "/node_modules/(?!chalk|update-notifier|configstore|xdg-basedir|unique-string|crypto-random-string|semver-diff|latest-version|package-json|got|@sindresorhus|p-cancelable|@szmarczak/http-timer|cacheable-request|normalize-url|responselike|lowercase-keys|mimic-response|form-data-encoder)"
],
]

// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"jest": "^29.7.0",
"lint-staged": "^15.2.5",
"prettier": "^3.3.2",
"socket.io-client": "^4.7.5",
"standard-version": "^9.5.0"
},
"keywords": [
Expand Down
13 changes: 7 additions & 6 deletions src/command/dappServer/socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,11 @@ class Socket {
}

serializeResult(appId, result) {
if (!this.clientConfig[appId]) {
throw new Error(`AppId ${appId} has not connected`);
}
if (this.clientConfig[appId].encryptWay === 'sign') {
// delete next line as function deserializeParams already has the logic
// if (!this.clientConfig[appId]) {
// throw new Error(`AppId ${appId} has not connected`);
// }
if (this.clientConfig[appId]?.encryptWay === 'sign') {
const originalResult = serializeMessage(result);
const signature = this.clientConfig[appId].encrypt.sign(Buffer.from(originalResult, 'base64'));
return {
Expand All @@ -263,7 +264,7 @@ class Socket {
};
}
const originalResult = serializeMessage(result);
return this.clientConfig[appId].encrypt.encrypt(originalResult);
return this.clientConfig[appId]?.encrypt.encrypt(originalResult);
}

async handleConnect(message) {
Expand Down Expand Up @@ -362,7 +363,7 @@ class Socket {
};
}

async handleInvoke(message, isReadOnly = false) {
async handleInvoke(message, isReadOnly) {
const params = await this.deserializeParams(message);
const { endpoint = this.defaultEndpoint, contractAddress, contractMethod, arguments: contractArgs } = params;
logger.info(`${isReadOnly ? 'Calling' : 'Sending'} contract ${contractAddress} method ${contractMethod}...`);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ async function promptTolerateSeveralTimes({ processAfterPrompt = () => {}, patte
askTimes++;
} catch (e) {
oraInstance.fail('Failed');
break;
}
}
if (askTimes >= times && answerInput === null) {
Expand Down Expand Up @@ -289,7 +290,6 @@ async function getParams(method) {
} catch (e) {}
let paramValue;
// todo: use recursion

if (
rule !== 'repeated' &&
innerType &&
Expand Down Expand Up @@ -366,7 +366,7 @@ async function deserializeLogs(aelf, logs = []) {
let results = await Promise.all(logs.map(v => getProto(aelf, v.Address)));
results = results.map((proto, index) => {
const { Name, NonIndexed, Indexed = [] } = logs[index];
const serializedData = [...(Indexed || [])];
const serializedData = [...Indexed];
if (NonIndexed) {
serializedData.push(NonIndexed);
}
Expand Down
20 changes: 9 additions & 11 deletions test/command/call.test.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import { Command } from 'commander';
import path from 'path';
import AElf from 'aelf-sdk';
import inquirer from 'inquirer';
import { CallCommand } from '../../src/command';
import { callCommandUsages, callCommandParameters } from '../../src/utils/constants.js';
import { getContractInstance } from '../../src/utils/utils.js';
import { userHomeDir } from '../../src/utils/userHomeDir.js';
import { logger } from '../../src/utils/myLogger.js';
import inquirer from 'inquirer';
import { endpoint as endPoint, account, password, dataDir } from '../constants.js';

const sampleRc = { getConfigs: jest.fn() };
jest.mock('../../src/utils/myLogger');

describe('CallCommand', () => {
let callCommand;
let mockOraInstance;
const endPoint = 'https://tdvw-test-node.aelf.io/';
const aelf = new AElf(new AElf.providers.HttpProvider(endPoint));
const wallet = AElf.wallet.getWalletByPrivateKey('943df6d39fd1e1cc6ae9813e54f7b9988cf952814f9c31e37744b52594cb4096');
const address = 'ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx';
const account = 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk';
const password = '1234*Qwer';
const dataDir = path.resolve(__dirname, '../datadir/aelf');

beforeEach(() => {
backup = inquirer.prompt;
mockOraInstance = {
Expand Down Expand Up @@ -48,12 +46,12 @@ describe('CallCommand', () => {
const result = await callCommand.callMethod(method, params);
expect(mockOraInstance.start).toHaveBeenCalledWith('Calling method...');
expect(mockOraInstance.succeed).toHaveBeenCalledWith('Calling method successfully!');
}, 20000);
});
test('should process address after prompt', async () => {
const answerInput = { contractAddress: address };
const result = await callCommand.processAddressAfterPrompt(aelf, wallet, answerInput);
expect(result.address).toBe(address);
}, 20000);
});
test('should run with valid inputs', async () => {
const commander = new Command();
commander.option('-e, --endpoint <URI>', 'The URI of an AElf node. Eg: http://127.0.0.1:8000');
Expand All @@ -73,7 +71,7 @@ describe('CallCommand', () => {
})
);
expect(logger.info).toHaveBeenCalled();
}, 20000);
});
test('should run without contractAddress', async () => {
inquirer.prompt = questions => Promise.resolve('');
const commander = new Command();
Expand All @@ -87,7 +85,7 @@ describe('CallCommand', () => {
commander.parse([process.argv[0], '', 'call', '-e', endPoint, '-a', account, '-p', password, '-d', dataDir]);
await callCommand.run(commander);
expect(logger.fatal).toHaveBeenCalled();
}, 5000);
});

test('should run without params', async () => {
inquirer.prompt = questions => Promise.resolve({ symbol: 'ELF' });
Expand All @@ -102,7 +100,7 @@ describe('CallCommand', () => {
commander.parse([process.argv[0], '', 'call', '-e', endPoint, '-a', account, '-p', password, '-d', dataDir]);
await callCommand.run(commander, 'AElf.ContractNames.Token', 'GetTokenInfo');
expect(logger.info).toHaveBeenCalled();
}, 20000);
});

test('should run with invalid parameters', async () => {
inquirer.prompt = backup;
Expand Down Expand Up @@ -133,7 +131,7 @@ describe('CallCommand', () => {
})
);
expect(logger.info).toHaveBeenCalled();
}, 20000);
});

afterEach(() => {
inquirer.prompt = backup;
Expand Down
2 changes: 1 addition & 1 deletion test/command/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('ConfigCommand', () => {
deleteConfig: jest.fn()
};
const endPoint = 'https://tdvw-test-node.aelf.io/';
const dataDir = path.resolve(__dirname, '../datadir/aelf');
const dataDir = path.resolve(__dirname, '../dataDir/aelf');
beforeEach(() => {
mockOraInstance = {
start: jest.fn(),
Expand Down
9 changes: 3 additions & 6 deletions test/command/console.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Command } from 'commander';
import ConsoleCommand from '../../src/command/console.js';
import { userHomeDir } from '../../src/utils/userHomeDir.js';
import { logger } from '../../src/utils/myLogger';
import { endpoint as endPoint, account, password, dataDir } from '../constants.js';

jest.mock('boxen');
jest.mock('repl');
Expand All @@ -16,10 +17,6 @@ describe('ConsoleCommand', () => {
const sampleRc = {
getConfigs: jest.fn()
};
const endPoint = 'https://tdvw-test-node.aelf.io/';
const account = 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk';
const password = '1234*Qwer';
const dataDir = path.resolve(__dirname, '../datadir/aelf');
beforeEach(() => {
oraInstance = {
succeed: jest.fn(),
Expand All @@ -46,7 +43,7 @@ describe('ConsoleCommand', () => {
await consoleCommand.run(commander);
expect(oraInstance.succeed).toHaveBeenCalledWith('Succeed!');
expect(logger.info).toHaveBeenCalledTimes(2);
}, 20000);
});
test('should handle errors correctly', async () => {
const commander = new Command();
commander.option('-e, --endpoint <URI>', 'The URI of an AElf node. Eg: http://127.0.0.1:8000');
Expand All @@ -63,5 +60,5 @@ describe('ConsoleCommand', () => {
await consoleCommand.run(commander);
expect(oraInstance.fail).toHaveBeenCalledWith('Failed!');
expect(logger.error).toHaveBeenCalled();
}, 20000);
});
});
5 changes: 1 addition & 4 deletions test/command/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CreateCommand from '../../src/command/create.js';
import { saveKeyStore } from '../../src/utils/wallet';
import { logger } from '../../src/utils/myLogger';
import { userHomeDir } from '../../src/utils/userHomeDir.js';
import { endpoint as endPoint, account, password, dataDir } from '../constants.js';

jest.mock('../../src/utils/wallet');
jest.mock('../../src/utils/myLogger');
Expand All @@ -12,10 +13,6 @@ describe('CreateCommand', () => {
let createCommand;
let oraInstance;
const sampleRc = { getConfigs: jest.fn() };
const endPoint = 'https://tdvw-test-node.aelf.io/';
const account = 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk';
const password = '1234*Qwer';
const dataDir = path.resolve(__dirname, '../datadir/aelf');
beforeEach(() => {
oraInstance = {
succeed: jest.fn(),
Expand Down
29 changes: 29 additions & 0 deletions test/command/dappServer/HKDF.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import HKDF from '../../../src/command/dappServer/HKDF';

describe('HKDF', () => {
const hash = 'sha256';
const random = '52695e07c0c545e8a279a71b1f47b9b7';
const salt = Buffer.from(random, 'hex');
console.log(salt, 'salt');
const initialKey = '667a8937f4da939b93e716211e83f787741be3fe0c938bea9e333d37c779b5';
afterEach(() => {
jest.clearAllMocks();
});
test('should initialize with correct values', () => {
const hkdf = new HKDF(hash, salt, initialKey);
expect(hkdf.hashMethod).toBe(hash);
expect(hkdf.hashLength).toBe(32);
expect(hkdf.salt).toBe(salt);
expect(hkdf.initialKey).toBe(initialKey);
console.log(hkdf.prk, 'hkdf.prk');
expect(hkdf.prk.toString('hex')).toEqual('6269eb093d3ed47cd698158c8f404b6380cd3222f3889323ea7814ea341456f2');
});
test('should throw an error for unsupported hash method', () => {
expect(() => new HKDF('sha1', Buffer.from('salt'), 'initialKey')).toThrow('not supported hash method');
});
test('should expand correctly', () => {
const hkdf = new HKDF(hash, salt, initialKey);
const result = hkdf.expand();
expect(result.toString('hex')).toBe('50649277a8ec2090de2c15af4aab197a7baa3c09accb755d3fafa829c370ba62');
});
});
65 changes: 65 additions & 0 deletions test/command/dappServer/encrypt.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable max-len */
import elliptic from 'elliptic';
import Encrypt from '../../../src/command/dappServer/encrypt';
import { randomId } from '../../../src/utils/utils.js';

jest.mock('../../../src/utils/utils.js', () => {
return {
randomId: () => '1ef39a09ce4f415da335dcb408989116'
};
});

describe('Encrypt', () => {
const algorithm = 'curve25519';
const remotePublicKey = '75d5e81323eecc4e887ef0934a52d8de1a2785ca04f4a4e9a39359c4bfd8cc9d';
const random = 'e44ab14618724a80be03f4565e7ed668';
const ecInstance = new Encrypt(algorithm, remotePublicKey, random);
const data =
'JTdCJTIyY29kZSUyMiUzQTAlMkMlMjJtc2clMjIlM0ElMjJzdWNjZXNzJTIyJTJDJTIyZXJyb3IlMjIlM0ElNUIlNUQlMkMlMjJkYXRhJTIyJTNBJTdCJTIyYWNjb3VudHMlMjIlM0ElNUIlN0IlMjJuYW1lJTIyJTNBJTIyYWVsZi1jb21tYW5kJTIyJTJDJTIyYWRkcmVzcyUyMiUzQSUyMkd5UVg2dDE4a3B3YUQ5WEhYZTFUb0t4Zm92OG1TZVRMRTlxOU53VUFlVEU4dFVMWmslMjIlMkMlMjJwdWJsaWNLZXklMjIlM0ElMjIwNDcwM2JiZTk1ZTk4NmM5ZDkwMWYyOGVkZDYwOTc1YTdhNmMzYjJkY2U0MWRmZWMyZTc5ODNkMjkzYzYwMGU4MjQ5NjQyYTNkYTM3OWM0MTk0YTZkNjJiZDg5YWZlNjc1M2U4MWFjZmMyYjZiYmYzYjQwNzM2ZWUwOTQ5MTAyMDcxJTIyJTdEJTVEJTJDJTIyY2hhaW5zJTIyJTNBJTVCJTdCJTIydXJsJTIyJTNBJTIyaHR0cHMlM0ElMkYlMkZ0ZHZ3LXRlc3Qtbm9kZS5hZWxmLmlvJTJGJTIyJTJDJTIyaXNNYWluQ2hhaW4lMjIlM0F0cnVlJTJDJTIyY2hhaW5JZCUyMiUzQSUyMkFFTEYlMjIlN0QlNUQlN0QlN0Q=';
const encrypted =
'U5E3LMWOVAintuE4zJf+8O4XSHgAtZTeQX831sc53r9yZJGnh6fiwNR3tF8zCORnwtVUaYh+pnzPgxeQz8b1tKEUhc/VYoy2dtqMubMDq+WUeF5tFnFhv6kuhHkMSDYQnpwOpwSvzJ4kWr+cxdHGOg+qoWe1nPujQlc/gQ/7Z4MeEHWXDagtKXzKGfE/rYJhnlAN+K1xHhq//tS4g4izEuHe2J2RK5xYa91e5p1qzLTEQyqax2q1pyYFPbaRyrP/6yAlUPOkdyuIGdMXwEp7BdfPXF3xPemaj6w+WgOkmAUcKk35blQvNLuJAWux6ZhLl+P+w6sfinT3Mk1ymrz2SEdThjB/LEPbbBlx+in80y0S8bSgUpsXbW1mfMPX3svDks0cK3Thjf/o+sGed5Ej4cFMFbgeCI4JdDnKeG7RWPWN9lL77kzPq0q60zXr/70jyRElktzSWzGfcOEr+TQiEx3MfiRHgAoqz5glc/ml7VlUP9ouLvRQfmrWcYIsbkf9WnBac/G68rSGOEqR/qbugsQCclO+V6yJt3o4NL94Nbn8V/+gQo430zdefkljGum84c0e2vVYxXyRbrFIAROJeO4IRiVWAaLrnFc0h/Shtt8EupaWGYJps+aYODUpfulJjDm6pZoDE+bp5h02XV0C/akhIUclLveNJc14qYc7tGpSRBDNXC/z6PfZgPlV3OL+';
const iv = '1ef39a09ce4f415da335dcb408989116';
afterEach(() => {
jest.clearAllMocks();
});
test('should initialize correctly', () => {
expect(ecInstance).toHaveProperty('keyPair');
expect(ecInstance).toHaveProperty('cipher');
expect(ecInstance).toHaveProperty('remoteKeyPair');
expect(ecInstance).toHaveProperty('sharedKey');
expect(ecInstance).toHaveProperty('derivedKey');
});

test('should ecInstance data correctly', () => {
// mock
ecInstance.sharedKey = Buffer.from('7f8e6db3591d76846d8b6ffefe5e0a19f85feee2cb5d41131dac2b9a668b41ca', 'hex');
ecInstance.derivedKey = Buffer.from('0369788fa050c720131722efb25281f444857db4b833ea89ede08a8fdf6117c0', 'hex');
const result = ecInstance.encrypt(data);
expect(result).toEqual({
encryptedResult: encrypted,
iv
});
});
test('should decrypt data correctly', () => {
const decrypt = ecInstance.decrypt(encrypted, iv);
expect(decrypt).toBe(data);
});
test('should return correct public key', () => {
const result = ecInstance.getPublicKey();
expect(result.length).toBe(64);
});

test(`should initialize with not default algorithm`, () => {
// another algorithm
const ecInstanceNotDefault = new Encrypt(
'secp256k1',
'04695fb2e8ce837d5b9e79df046dd1947a558b884165c8f83a9e9b01e47a37135fc4ff42256e4a79f25f740a840b58f47f79a3bf934857c7397e545163cddf663e',
'beae60451b554891bb8967d0c2bdaa4e'
);
expect(ecInstance).toHaveProperty('keyPair');
expect(ecInstance).toHaveProperty('cipher');
expect(ecInstance).toHaveProperty('remoteKeyPair');
expect(ecInstance).toHaveProperty('sharedKey');
expect(ecInstance).toHaveProperty('derivedKey');
});
});
Loading
Loading