Skip to content

Commit

Permalink
Merge pull request #63 from AElfProject/feature/unit-test
Browse files Browse the repository at this point in the history
Feature/unit test
  • Loading branch information
hzz780 authored Jun 26, 2024
2 parents 629bafc + 55c08a6 commit 1350684
Show file tree
Hide file tree
Showing 34 changed files with 2,051 additions and 105 deletions.
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

0 comments on commit 1350684

Please sign in to comment.