Skip to content

Commit

Permalink
fix: services unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Kpoke committed Jul 22, 2022
1 parent 38cd568 commit 0542d47
Show file tree
Hide file tree
Showing 16 changed files with 606 additions and 276 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"eslint": "eslint --report-unused-disable-directives .",
"eslint:fix": "npm run eslint -- --fix",
"test": "npm run test-unit; npm run test-integration;npm run test-repository",
"test-unit": "NODE_ENV=test DOTENV_CONFIG_PATH=.env.test mocha -r dotenv/config --exit --ignore './server/repositories/**/*.spec.js' './server/setup.js' './server/**/*.spec.js'",
"test-unit": "NODE_ENV=test DOTENV_CONFIG_PATH=.env.test mocha -r dotenv/config --exit --ignore './server/repositories/**/*.spec.js' './server/setup.js' './server/services/*.spec.js'",
"test-unit-ci": "NODE_ENV=test DOTENV_CONFIG_PATH=.env.ci mocha -r dotenv/config --exit --ignore './server/repositories/**/*.spec.js' './server/setup.js' './server/**/*.spec.js'",
"test-repository": "NODE_ENV=test DOTENV_CONFIG_PATH=.env.test mocha -r dotenv/config --exit ./server/repositories/**/*.spec.js",
"server-test": "DEBUG=loopback:*,express:* NODE_LOG_LEVEL=debug nodemon server/serverTest.js",
Expand Down Expand Up @@ -57,7 +57,7 @@
"@commitlint/config-conventional": "^11.0.0",
"assert": "1.5.0",
"chai": "^4.0.0",
"chai-as-promised": "7.1.1",
"chai-as-promised": "^7.1.1",
"chai-http": "^4.3.0",
"database-cleaner": "^1.3.0",
"db-migrate": "^0.11.12",
Expand Down
4 changes: 2 additions & 2 deletions server/models/Token.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ class Token {
return transactions;
}

async getByOwner(wallet, limit, offset) {
async getByOwner(walletId, limit, offset) {
const tokensObject = await this._tokenRepository.getByFilter(
{ wallet_id: wallet.id },
{ wallet_id: walletId },
{ limit, offset },
);
return tokensObject;
Expand Down
51 changes: 36 additions & 15 deletions server/services/ApiKeyService.spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
const jestExpect = require('expect');
const { expect } = require('chai');
const sinon = require('sinon');
const ApiKeyService = require('./ApiKeyService');
const ApiKeyRepository = require('../repositories/ApiKeyRepository');
const HttpError = require('../utils/HttpError');
const Session = require('../infra/database/Session');
const ApiKey = require('../models/ApiKey');

describe('ApiKey', () => {
let apiKey;
Expand All @@ -13,24 +12,46 @@ describe('ApiKey', () => {
});

it('empty key should throw error', async () => {
await jestExpect(async () => {
await apiKey.check(undefined);
}).rejects.toThrow(/no API key/);
let error;
try {
await apiKey.check();
} catch (e) {
error = e;
}
expect(error.message).eql('Invalid access - no API key');

This comment has been minimized.

Copy link
@dadiorchen

dadiorchen Jul 22, 2022

Collaborator

If we want to stick with chai, I rememeber there is a plugin for chai to support better API for checking error

This comment has been minimized.

Copy link
@Kpoke

Kpoke Jul 22, 2022

Author Collaborator

I also found a package for that, chai-as-promised but I think I am fine with the current approach

});

it('key which do not exist should throw error', async () => {
sinon
.stub(ApiKeyRepository.prototype, 'getByApiKey')
.throws(new HttpError(404));
await jestExpect(async () => {
await apiKey.check('not_exist');
}).rejects.toThrow(/Invalid/);
ApiKeyRepository.prototype.getByApiKey.restore();
let error;
const getApiKeyStub = sinon
.stub(ApiKey.prototype, 'getByApiKey')
.resolves();
try {
await apiKey.check('api key');
} catch (e) {
error = e;
}
expect(error.message).eql('Invalid API access');
getApiKeyStub.restore();
});

it('key with false tree_token_api_access should not pass', async () => {
let error;
const getApiKeyStub = sinon
.stub(ApiKey.prototype, 'getByApiKey')
.resolves({ tree_token_api_access: false });
try {
await apiKey.check('api key');
} catch (e) {
error = e;
}
expect(error.message).eql('Invalid API access, apiKey was deprecated');
getApiKeyStub.restore();
});

it('good key should pass', async () => {
sinon.stub(ApiKeyRepository.prototype, 'getByApiKey').returns({});
sinon.stub(ApiKey.prototype, 'getByApiKey').returns({});
await apiKey.check('not_exist');
ApiKeyRepository.prototype.getByApiKey.restore();
ApiKey.prototype.getByApiKey.restore();
});
});
4 changes: 2 additions & 2 deletions server/services/AuthService.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const WalletService = require('./WalletService');
const JWTService = require('./JWTService');
const { sha512 } = require('./HashService');
const HashService = require('./HashService');

class AuthService {
static async signIn({ wallet, password }) {
const walletService = new WalletService();
const walletObject = await walletService.getByIdOrName(wallet);

const hash = sha512(password, walletObject.salt);
const hash = HashService.sha512(password, walletObject.salt);

if (hash === walletObject.password) {
const token = JWTService.sign(walletObject);
Expand Down
50 changes: 50 additions & 0 deletions server/services/AuthService.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { expect } = require('chai');
const Sinon = require('sinon');
const AuthService = require('./AuthService');
const HashService = require('./HashService');
const JWTService = require('./JWTService');
const WalletService = require('./WalletService');

describe('AuthService', () => {
it('signin', async () => {
const walletObject = { salt: 'salt', password: 'hash' };
const getByIdOrNameStub = Sinon.stub(
WalletService.prototype,
'getByIdOrName',
).resolves(walletObject);
const sha512Stub = Sinon.stub(HashService, 'sha512').returns('hash');
const jwtSignStub = Sinon.stub(JWTService, 'sign').resolves('token');
const details = { wallet: 'wallet', password: 'password' };
const token = await AuthService.signIn(details);
expect(getByIdOrNameStub.calledOnceWithExactly(details.wallet)).eql(true);
expect(sha512Stub.calledOnceWithExactly(details.password, 'salt')).eql(
true,
);
expect(jwtSignStub.calledOnceWithExactly(walletObject)).eql(true);
expect(token).eql('token');
getByIdOrNameStub.restore();
sha512Stub.restore();
jwtSignStub.restore();
});

it('failed signin', async () => {
const walletObject = { salt: 'salt', password: 'password' };
const getByIdOrNameStub = Sinon.stub(
WalletService.prototype,
'getByIdOrName',
).resolves(walletObject);
const sha512Stub = Sinon.stub(HashService, 'sha512').returns('hash');
const jwtSignStub = Sinon.stub(JWTService, 'sign').resolves('token');
const details = { wallet: 'wallet', password: 'password' };
const token = await AuthService.signIn(details);
expect(getByIdOrNameStub.calledOnceWithExactly(details.wallet)).eql(true);
expect(sha512Stub.calledOnceWithExactly(details.password, 'salt')).eql(
true,
);
expect(jwtSignStub.notCalled).eql(true);
expect(token).eql(false);
getByIdOrNameStub.restore();
sha512Stub.restore();
jwtSignStub.restore();
});
});
22 changes: 12 additions & 10 deletions server/services/HashService.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
const Crypto = require('crypto');

const sha512 = (password, salt) => {
const hash = Crypto.createHmac(
'sha512',
salt,
); /** Hashing algorithm sha512 */
hash.update(password);
const value = hash.digest('hex');
return value;
};
class HashService {
static sha512(password, salt) {
const hash = Crypto.createHmac(
'sha512',
salt,
); /** Hashing algorithm sha512 */
hash.update(password);
const value = hash.digest('hex');
return value;
}
}

module.exports = { sha512 };
module.exports = HashService;
21 changes: 21 additions & 0 deletions server/services/HashService.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { expect } = require('chai');
const Sinon = require('sinon');
const Crypto = require('crypto');
const HashService = require('./HashService');

describe('HashService', () => {
it('sha512', async () => {
const hashUpdateStub = Sinon.stub();
const hashDigestStub = Sinon.stub().returns('hash');
const cryptoStub = Sinon.stub(Crypto, 'createHmac').returns({
update: hashUpdateStub,
digest: hashDigestStub,
});
const hash = HashService.sha512('password', 'salt');
expect(cryptoStub.calledOnceWithExactly('sha512', 'salt')).eql(true);
expect(hashUpdateStub.calledOnceWithExactly('password')).eql(true);
expect(hashDigestStub.calledOnceWithExactly('hex')).eql(true);
expect(hash).eql('hash');
cryptoStub.restore();
});
});
20 changes: 8 additions & 12 deletions server/services/JWTService.spec.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
const {expect} = require("chai");
const JWTService = require("./JWTService");
const { expect } = require('chai');
const JWTService = require('./JWTService');



describe("JWTService", () => {

it("signed payload should be able to be verified", () => {
const payload = {id: 1};
const jwtService = new JWTService();
const token = jwtService.sign(payload);
describe('JWTService', () => {
it('signed payload should be able to be verified', () => {
const payload = { id: 1 };
const token = JWTService.sign(payload);
expect(token).match(/\S+/);
const result = jwtService.verify(`Bearer ${token}`);
expect(result).property("id").eq(1);
const result = JWTService.verify(`Bearer ${token}`);
expect(result).property('id').eq(1);
});
});
10 changes: 6 additions & 4 deletions server/services/TokenService.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ class TokenService {
}

async getTokens({ wallet, limit, offset, walletLoginId }) {
const walletLogin = await this._walletService.getById(walletLoginId);
let tokens = [];

if (wallet) {
Expand All @@ -21,11 +20,14 @@ class TokenService {
walletInstance.id,
);
if (!isSub) {
throw new HttpError(403, 'Wallet does not belong to wallet logged in');
throw new HttpError(
403,
'Wallet does not belong to the logged in wallet',
);
}
tokens = await this._token.getByOwner(walletInstance, limit, offset);
tokens = await this._token.getByOwner(walletInstance.id, limit, offset);
} else {
tokens = await this._token.getByOwner(walletLogin, limit, offset);
tokens = await this._token.getByOwner(walletLoginId, limit, offset);
}

return tokens;
Expand Down
Loading

0 comments on commit 0542d47

Please sign in to comment.