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
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
@@ -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,
@@ -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: [
@@ -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,
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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": [
13 changes: 7 additions & 6 deletions src/command/dappServer/socket.js
Original file line number Diff line number Diff line change
@@ -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 {
@@ -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) {
@@ -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}...`);
4 changes: 2 additions & 2 deletions src/utils/utils.js
Original file line number Diff line number Diff line change
@@ -138,6 +138,7 @@ async function promptTolerateSeveralTimes({ processAfterPrompt = () => {}, patte
askTimes++;
} catch (e) {
oraInstance.fail('Failed');
break;
}
}
if (askTimes >= times && answerInput === null) {
@@ -289,7 +290,6 @@ async function getParams(method) {
} catch (e) {}
let paramValue;
// todo: use recursion

if (
rule !== 'repeated' &&
innerType &&
@@ -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);
}
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 = {
@@ -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');
@@ -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();
@@ -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' });
@@ -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;
@@ -133,7 +131,7 @@ describe('CallCommand', () => {
})
);
expect(logger.info).toHaveBeenCalled();
}, 20000);
});

afterEach(() => {
inquirer.prompt = backup;
2 changes: 1 addition & 1 deletion test/command/config.test.js
Original file line number Diff line number Diff line change
@@ -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(),
9 changes: 3 additions & 6 deletions test/command/console.test.js
Original file line number Diff line number Diff line change
@@ -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');
@@ -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(),
@@ -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');
@@ -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
@@ -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');
@@ -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(),
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