/* eslint-disable no-empty */ /** * Unit testing file for encryption */ 'use strict'; /* eslint max-nested-callbacks: ["error", 7] */ // eslint-disable-next-line no-unused-vars const testGlobals = require('../../tools/test/testGlobals.js'); const _ = require('lodash'); const chai = require('chai'); const sinon = require('sinon'); const sinonChai = require('sinon-chai'); const rewire = require('rewire'); /** * Use `rewire` instead of require so that we can access private functions for test */ const encryption = rewire('../encryption.js'); const utilsStub = encryption.__get__('utils'); const expect = chai.expect; const sandbox = sinon.createSandbox(); chai.use(sinonChai); const ENCRYPTION_KEY = 'go3rn2ofno2'; const USER_ID = 'o5oij5oioj23oij'; const FAKE_ENCRYPTED_DETAILS = '4j3nrkj23b4rk'; const FAKE_ERROR = {error: 'This is an error'}; const CARD_PAN = '0000000000000000'; const CARD_EXPIRY = '01-20'; const CARD_VALID_FROM = '01-15'; const CARD_ISSUE_NO = '00'; const UNENCRYPTED_DETAILS = { FirstName: 'Joe', LastName: 'Bloggs' }; const MIN_ACCOUNT = { CreditDebitCardInfo: _.defaults( { CardPanToBeEncrypted: CARD_PAN, CardExpiryToBeEncrypted: CARD_EXPIRY, CardPANEncrypted: '', CardExpiryEncrypted: '', CardValidFromEncrypted: '', IssueNumberEncrypted: '' }, UNENCRYPTED_DETAILS ) }; const MAX_ACCOUNT = _.defaultsDeep( { CreditDebitCardInfo: { CardValidFromToBeEncrypted: CARD_VALID_FROM, IssueNumberToBeEncrypted: CARD_ISSUE_NO } }, MIN_ACCOUNT ); const MIN_DATA = { Account: MIN_ACCOUNT }; const MAX_DATA = { Account: MAX_ACCOUNT }; const MIN_ENCRYPTED_ACCOUNT = { CreditDebitCardInfo: _.defaultsDeep( { CardExpiryEncrypted: FAKE_ENCRYPTED_DETAILS, CardPANEncrypted: FAKE_ENCRYPTED_DETAILS }, UNENCRYPTED_DETAILS ) }; const MAX_ENCRYPTED_ACCOUNT = _.defaultsDeep( { CreditDebitCardInfo: { CardValidFromEncrypted: FAKE_ENCRYPTED_DETAILS, IssueNumberEncrypted: FAKE_ENCRYPTED_DETAILS } }, MIN_ENCRYPTED_ACCOUNT ); const MIN_DECRYPTED_RETURN_OBJECT = { expiryMonth: '01', expiryYear: '2020', cardNumber: CARD_PAN }; const MAX_DECRYPTED_RETURN_OBJECT = _.defaultsDeep( { IssueNumber: 0, startMonth: '01', startYear: '2015' }, MIN_DECRYPTED_RETURN_OBJECT ); const MIN_DECRYPTED_FULL_ACCOUNT = { CreditDebitCardInfo: _.defaultsDeep( {}, UNENCRYPTED_DETAILS, MIN_DECRYPTED_RETURN_OBJECT ) }; const MAX_DECRYPTED_FULL_ACCOUNT = { CreditDebitCardInfo: _.defaultsDeep( {}, UNENCRYPTED_DETAILS, MAX_DECRYPTED_RETURN_OBJECT ) }; const MIN_ENCRYPTED_RETURN_OBJECT = { CardPANEncrypted: FAKE_ENCRYPTED_DETAILS, CardExpiryEncrypted: FAKE_ENCRYPTED_DETAILS }; const MAX_ENCRYPTED_RETURN_OBJECT = _.defaultsDeep( { CardValidFromEncrypted: FAKE_ENCRYPTED_DETAILS, IssueNumberEncrypted: FAKE_ENCRYPTED_DETAILS }, MIN_ENCRYPTED_RETURN_OBJECT ); const MIN_ENCRYPTED_FULL_ACCOUNT = { Account: { CreditDebitCardInfo: _.defaultsDeep( { CardValidFromEncrypted: '', IssueNumberEncrypted: '' }, MIN_ENCRYPTED_RETURN_OBJECT, UNENCRYPTED_DETAILS ) } }; const MAX_ENCRYPTED_FULL_ACCOUNT = { Account: { CreditDebitCardInfo: _.defaultsDeep( {}, MAX_ENCRYPTED_RETURN_OBJECT, UNENCRYPTED_DETAILS ) } }; const INVALID_ACCOUNT = {}; describe('encryption function', () => { /** * Stub the functions that will be used for the "happy path" * The responses are specifically overriden below for testing the error cases */ beforeEach(() => { sandbox.spy(encryption, 'encryptCard'); sandbox.spy(encryption, 'decryptCard'); sandbox.spy(encryption, 'encryptCardMaintainingAccount'); sandbox.spy(encryption, 'decryptCardMaintainingAccount'); sandbox.stub(utilsStub, 'decryptDataV3') .onCall(0).returns(CARD_EXPIRY) .onCall(1).returns(CARD_PAN) .onCall(2).returns(CARD_ISSUE_NO) .onCall(3).returns(CARD_VALID_FROM); sandbox.stub(utilsStub, 'encryptDataV3') .onCall(0).returns(FAKE_ENCRYPTED_DETAILS) .onCall(1).returns(FAKE_ENCRYPTED_DETAILS) .onCall(2).returns(FAKE_ENCRYPTED_DETAILS) .onCall(3).returns(FAKE_ENCRYPTED_DETAILS); }); afterEach(() => { sandbox.restore(); }); describe('calls encryptCard', () => { describe('successfully', () => { describe('with required card fields set', () => { beforeEach(() => { encryption.encryptCard(MIN_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); }); it('encrypting cardPAN and expiry date', () => { return expect(utilsStub.encryptDataV3).to.have.been .calledTwice .calledWith(CARD_PAN, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_EXPIRY, ENCRYPTION_KEY, USER_ID); }); it('returning encrypted details ', () => { return expect(encryption.encryptCard).to.have.returned(MIN_ENCRYPTED_RETURN_OBJECT); }); }); describe('with all fields set', () => { beforeEach(() => { encryption.encryptCard(MAX_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); }); it('encrypting valid from, issue no., cardPAN and expiry date', () => { return expect(utilsStub.encryptDataV3).to.have.been .callCount(4) .calledWith(CARD_PAN, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_EXPIRY, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_VALID_FROM, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_ISSUE_NO, ENCRYPTION_KEY, USER_ID); }); it('returning encrypted details ', () => { return expect(encryption.encryptCard).to.have.returned(MAX_ENCRYPTED_RETURN_OBJECT); }); }); }); describe('with a failure', () => { describe('to encrypt the data', () => { beforeEach(() => { utilsStub.encryptDataV3 .onCall(0).returns(FAKE_ERROR); try { encryption.encryptCard(MIN_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); } catch (error) {} }); it('fails to encrypt cardPAN', () => { return expect(utilsStub.encryptDataV3).to.have.been .calledOnce .calledWith(CARD_PAN, ENCRYPTION_KEY, USER_ID); }); it('throwing an error', () => { return expect(encryption.encryptCard).to.have.thrown(); }); }); describe('to send invalid data to encrypt', () => { beforeEach(() => { try { encryption.encryptCard(INVALID_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); } catch (error) {} }); it('does not try to encrypt anything', () => { return expect(utilsStub.encryptDataV3).to.not.have.been.called; }); it('throwing an error', () => { return expect(encryption.encryptCard).to.have.thrown(); }); }); }); }); describe('calls decryptCard', () => { describe('successfully', () => { describe('with required card fields set', () => { beforeEach(() => { encryption.decryptCard(MIN_ENCRYPTED_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); }); it('decrypting cardPAN and expiry date', () => { return expect(utilsStub.decryptDataV3).to.have.been .calledTwice .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID); }); it('returning decrypted details ', () => { return expect(encryption.decryptCard).to.have.returned(MIN_DECRYPTED_RETURN_OBJECT); }); }); describe('with all fields set', () => { beforeEach(() => { encryption.decryptCard(MAX_ENCRYPTED_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); }); it('decrypting valid from, issue no., cardPAN and expiry date', () => { return expect(utilsStub.decryptDataV3).to.have.been .callCount(4) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID); }); it('returning decrypted details ', () => { return expect(encryption.decryptCard).to.have.returned(MAX_DECRYPTED_RETURN_OBJECT); }); }); }); describe('with a failure', () => { describe('to decrypt the data', () => { beforeEach(() => { utilsStub.decryptDataV3 .onCall(0).returns(FAKE_ERROR); try { encryption.decryptCard(MIN_ENCRYPTED_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); } catch (error) {} }); it('fails to decrypt cardPAN', () => { return expect(utilsStub.decryptDataV3).to.have.been .calledOnce .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID); }); it('throwing an error', () => { return expect(encryption.decryptCard).to.have.thrown(); }); }); describe('with an invalid encryption key', () => { beforeEach(() => { utilsStub.decryptDataV3 .onCall(0).returns({ info: 'invalid encryption key.', code: 9}); try { encryption.decryptCard(MIN_ENCRYPTED_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); } catch (error) {} }); it('fails to decrypt cardPAN', () => { return expect(utilsStub.decryptDataV3).to.have.been .calledOnce .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID); }); it('throwing an error', () => { return expect(encryption.decryptCard).to.have.returned(null); }); }); describe('to send invalid data to decrypt', () => { beforeEach(() => { try { encryption.decryptCard(INVALID_ACCOUNT.CreditDebitCardInfo, ENCRYPTION_KEY, USER_ID); } catch (error) {} }); it('does not try to decrypt anything', () => { return expect(utilsStub.decryptDataV3).to.not.have.been.called; }); it('throwing an error', () => { return expect(encryption.decryptCard).to.have.thrown(); }); }); }); }); describe('calls decryptCardMaintainingAccount', () => { describe('successfully', () => { describe('with required card fields set', () => { beforeEach(() => { encryption.decryptCardMaintainingAccount(MIN_ENCRYPTED_ACCOUNT, ENCRYPTION_KEY, USER_ID); }); it('decrypting cardPAN and expiry date', () => { return expect(utilsStub.decryptDataV3).to.have.been .calledTwice .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID); }); it('returning decrypted details ', () => { return expect(encryption.decryptCardMaintainingAccount).to.have.returned(MIN_DECRYPTED_FULL_ACCOUNT); }); }); describe('with all fields set', () => { beforeEach(() => { encryption.decryptCardMaintainingAccount(MAX_ENCRYPTED_ACCOUNT, ENCRYPTION_KEY, USER_ID); }); it('decrypting valid from, issue no., cardPAN and expiry date', () => { return expect(utilsStub.decryptDataV3).to.have.been .callCount(4) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID) .calledWith(FAKE_ENCRYPTED_DETAILS, ENCRYPTION_KEY, USER_ID); }); it('returning decrypted details ', () => { return expect(encryption.decryptCardMaintainingAccount).to.have.returned(MAX_DECRYPTED_FULL_ACCOUNT); }); }); }); describe('with wrong key', () => { it('returns null', () => { utilsStub.decryptDataV3.onCall(0).returns( utilsStub.createError(9, 'Decryption error.') ); return expect(encryption.decryptCardMaintainingAccount( MIN_ENCRYPTED_ACCOUNT, 'WRONG KEY', USER_ID )).to.be.null; }); }); }); describe('calls encryptCardMaintainingAccount', () => { describe('successfully', () => { describe('with required card fields set', () => { beforeEach(() => { encryption.encryptCardMaintainingAccount(_.clone(MIN_DATA), ENCRYPTION_KEY, USER_ID); }); it('encrypting cardPAN and expiry date', () => { return expect(utilsStub.encryptDataV3).to.have.been .calledTwice .calledWith(CARD_PAN, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_EXPIRY, ENCRYPTION_KEY, USER_ID); }); it('returning encrypted details ', () => { return expect(encryption.encryptCardMaintainingAccount).to.have.returned(MIN_ENCRYPTED_FULL_ACCOUNT); }); }); describe('with all fields set', () => { beforeEach(() => { encryption.encryptCardMaintainingAccount(_.clone(MAX_DATA), ENCRYPTION_KEY, USER_ID); }); it('encrypting valid from, issue no., cardPAN and expiry date', () => { return expect(utilsStub.encryptDataV3).to.have.been .callCount(4) .calledWith(CARD_PAN, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_EXPIRY, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_VALID_FROM, ENCRYPTION_KEY, USER_ID) .calledWith(CARD_ISSUE_NO, ENCRYPTION_KEY, USER_ID); }); it('returning encrypted details ', () => { return expect(encryption.encryptCardMaintainingAccount).to.have.returned(MAX_ENCRYPTED_FULL_ACCOUNT); }); }); }); }); });