/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
/* jshint node:true, jasmine:true */
'use strict';

var messageDigest = require('../lib/index');
var cryptoPolicy = require('scytl-cryptopolicy');
var bitwise = require('scytl-bitwise');
var codec = require('scytl-codec');

describe('The message digest module should be able to ...', function() {
  var DATA = 'Ox2fUJq1gAbX';
  var OTHER_DATA_1 = DATA + 'a';
  var OTHER_DATA_2 = DATA + 'b';

  var data_;
  var otherData1_;
  var otherData2_;
  var service_;

  beforeAll(function() {
    data_ = codec.utf8Encode(DATA);
    otherData1_ = codec.utf8Encode(OTHER_DATA_1);
    otherData2_ = codec.utf8Encode(OTHER_DATA_2);
    service_ = messageDigest.newService();
  });

  describe('create a message digest service that should be able to ..', function() {
    describe('create a message digester that should be able to', function() {
      it('generate digests that are deterministic', function() {
        // Pass the digester some data.
        var digester = service_.newDigester();

        var firstMsgDigest = digester.digest(data_);

        // Pass another digester the same data.
        digester = service_.newDigester();
        var secondMsgDigest = digester.digest(data_);

        // Verify that both processes have produced the same result.
        expect(secondMsgDigest).toEqual(firstMsgDigest);

        // Pass yet another digester slightly altered data.
        digester = service_.newDigester();
        var thirdMsgDigest = digester.digest(otherData1_);

        // Verify that new message digest does not match the first two.
        expect(thirdMsgDigest).not.toEqual(firstMsgDigest);
      });

      it('generate different digests from the same data, when using different hash algorithms',
         function() {
           // Generate a SHA256 digest.
           var sha256Policy = cryptoPolicy.newInstance();
           sha256Policy.primitives.messageDigest.algorithm =
               cryptoPolicy.options.primitives.messageDigest.algorithm.SHA256;
           var service = messageDigest.newService({policy: sha256Policy});
           var digester = service.newDigester();
           var sha256Digest = digester.digest(data_);

           // Generate a SHA512 digest.
           var sha512Policy = cryptoPolicy.newInstance();
           sha512Policy.primitives.messageDigest.algorithm =
               cryptoPolicy.options.primitives.messageDigest.algorithm
                   .SHA512_224;
           service = messageDigest.newService({policy: sha512Policy});
           digester = service.newDigester();
           var sha512Digest = digester.digest(data_);

           // Verify that the digests do not match.
           expect(sha256Digest).not.toEqual(sha512Digest);
         });

      it('generate a digest from multiple data parts', function() {
        var dataParts = [data_, otherData1_, otherData2_];

        var digester = service_.newDigester();
        for (var i = 0; i < dataParts.length; i++) {
          digester.update(dataParts[i]);
        }
        var firstMsgDigest = digester.digest();

        digester = service_.newDigester();
        var secondMsgDigest = digester.digest(
            bitwise.concatenate(data_, otherData1_, otherData2_));
        expect(secondMsgDigest).toEqual(firstMsgDigest);

        digester = service_.newDigester();
        for (var j = 0; j < (dataParts.length - 1); j++) {
          digester.update(dataParts[j]);
        }
        firstMsgDigest = digester.digest(otherData2_);

        digester = service_.newDigester();
        secondMsgDigest = digester.digest(
            bitwise.concatenate(data_, otherData1_, otherData2_));
        expect(secondMsgDigest).toEqual(firstMsgDigest);
      });

      it('generate a digest from multiple data parts, using method chaining',
         function() {
           var digester = service_.newDigester();
           var firstMsgDigest = digester.digest(
               bitwise.concatenate(data_, otherData1_, otherData2_));

           var secondMsgDigest = service_.newDigester()
                                     .update(data_)
                                     .update(otherData1_)
                                     .update(otherData2_)
                                     .digest();
           expect(secondMsgDigest).toEqual(firstMsgDigest);

           secondMsgDigest = service_.newDigester()
                                 .update(data_)
                                 .update(otherData1_)
                                 .digest(otherData2_);
           expect(secondMsgDigest).toEqual(firstMsgDigest);
         });

      it('generate a digest with the expected value when using data of type string',
         function() {
           var digester = service_.newDigester();

           var firstMsgDigest =
               digester.update(data_).update(otherData1_).digest(otherData2_);

           var secondMsgDigest =
               digester.update(DATA).update(OTHER_DATA_1).digest(OTHER_DATA_2);

           expect(secondMsgDigest).toEqual(firstMsgDigest);
         });

      it('be automatically reinitialized after digesting', function() {
        var digester = service_.newDigester();

        var firstMsgDigest = digester.digest(data_);
        var secondMsgDigest = digester.digest(data_);

        expect(secondMsgDigest).toEqual(firstMsgDigest);
      });

      it('throw an error upon creation if an unsupported crytographic policy was provided to the service',
         function() {
           var policy = cryptoPolicy.newInstance();
           policy.primitives.messageDigest.algorithm = 'SHA0';
           var service = messageDigest.newService({policy: policy});

           expect(function() {
             service.newDigester();
           }).toThrow();
         });

      it('throw an error when generating a digest without either providing data or having previously updated the digester with some data',
         function() {
           var digester = service_.newDigester();

           expect(function() {
             digester.digest();
           }).toThrow();
         });
    });
  });
});
