/*
 * 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
 */
describe('cryptoPRNG...', function() {
  'use strict';
  describe('should be able to ..', function() {
    var privateKeyPem =
        '-----BEGIN RSA PRIVATE KEY-----MIIEowIBAAKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQABAoIBAC/tn34Wf3kE9BGeGc1oFLVDaqqdVVz5/oEpeR2J7q0GnzMFYUpAhzC7WvY52cYsUPyll1Q9Jx0TUTmteo/uvKWQQFfz4nVMeS+2PoXabolBDzuWlsv/1eiRo0FOYHa/3siu8YcQN9X0DpAkpbfTmT1uoZOHZ3EuucMmOFu7vGn38Grw8bSxpR0uvTtnb8ygC+aB51y38RMyhzQQanrM8FMeAfDAy6IB0Yo7b0c50Cxa6Ax4nqn9LXyGakr5WeAMkgTIOA/GId9SZD4e5eRpq+628pOeR4O9datFltgl6r1+A4ii2VrJsDqeatGtODlX6KRKqwFHoGIa2TjgSZLuorECgYEAxeSZDOOgFsI5mB7RkRzZaQ9znJ15sgdyZiAFZAOUah4hSGdAXNAnZTlrdacduXEu3EfkpuPToX7xZSv5FRYwfBwMwCLeytlGLPjQzWejZGbo4+KqgzWb9fECDYVtDPlJ/+yLih9nt67BHweJKxYydl18rVigdVyy22X86NijSykCgYEAqKPUrXZAo+TJvmTw4tgsibJgvXBYBhmsej8mGNQw+Nyp2gV28sgm61ifIeXKS8teq+MFwGA6cHQedbsCqhMHokdhESZmlbWxhSFLihQcewBxwvrBwbaxI23yXRzwMewznZFL032PpcbqrmwFmcSSEZ3nmbvTH6ShqLW+pzDNp6MCgYBQLzdgxJ7qedqSa/JohTMG4e7rh9d2rpPJE7J7ewPZF8pOpx+qO+Gqn2COdJ+Ts2vUcAETKn9nEaPIZc/wnmQY9dioxbhWo0FPGaaphBPtq9Ez/XUv4zoFppk5V1X/isdUPsmvttf00oeIBiqrXbwmv+yz5JRn2Z7TTXjz9Ev+OQKBgQCUuoCMRzl1EgcXIqEL/0kwW6BUEqufHa9u1Ri9Vw6lvL8T6DPipMEmWK9nzuid9gtVns/ovTVtDgv7GuabplLaPQePf4WDzY11c0rSyS/hDyBFrK+LL5uEOqhAlJAGB2HyOj1clWVF+GvrTpuV5LZKUS/79pmZU7G7QCaX/0Ow7wKBgC/kDH7cmWQnWvvJ5izrx/7PogQVPOLELeUIGLu/hjsSdDKiFCxCUZ948+9NuG+DnpXDWzw//r8mPBRRGGsqFws5Aipp7yjQ3kRDCCzGelPCVhHyfmKqA+8ewXPulKS3/wIyHIvaXmsuAtTfurHtpRyzjKmCBK1Y6WQ3trIXvo7s-----END RSA PRIVATE KEY-----';

    afterEach(function() {
      cryptoPRNG._privateKeyPem = '';
      cryptoPRNG._pinEntropy = 0;
      cryptoPRNG._usePrivateKeyEntropy = false;
    });

    it('not execute entropy collector if pinEntropy is not a number',
       function() {
         cryptolib('cryptoprng', function() {

           spyOn(cryptoPRNG, '_noWindow').and.returnValue(false);
           spyOn(cryptoPRNG, '_getCrypto').and.returnValue(null);
           spyOn(cryptoPRNG.Collectors, 'getPrivateKeyCollector')
               .and.callThrough();

           cryptoPRNG.setDataForPrivateKeyEntropyCollector(
               privateKeyPem, 'x10');
           cryptoPRNG.startEntropyCollection();
           cryptoPRNG.stopEntropyCollectionAndCreatePRNG();

           expect(cryptoPRNG.Collectors.getPrivateKeyCollector)
               .not.toHaveBeenCalled();
         });
       });

    it('execute entropy collector only if pinEntropy is bigger than 0',
       function() {
         cryptolib('cryptoprng', function() {

           spyOn(cryptoPRNG, '_noWindow').and.returnValue(false);
           spyOn(cryptoPRNG, '_getCrypto').and.returnValue(null);
           spyOn(cryptoPRNG.Collectors, 'getPrivateKeyCollector')
               .and.callThrough();

           cryptoPRNG.setDataForPrivateKeyEntropyCollector(privateKeyPem, 0);
           cryptoPRNG.startEntropyCollection();
           cryptoPRNG.stopEntropyCollectionAndCreatePRNG();

           expect(cryptoPRNG.Collectors.getPrivateKeyCollector)
               .not.toHaveBeenCalled();
         });
       });

    it('execute entropy collector only if private key is not empty',
       function() {
         cryptolib('cryptoprng', function() {

           spyOn(cryptoPRNG, '_noWindow').and.returnValue(false);
           spyOn(cryptoPRNG, '_getCrypto').and.returnValue(null);
           spyOn(cryptoPRNG.Collectors, 'getPrivateKeyCollector')
               .and.callThrough();

           cryptoPRNG.setDataForPrivateKeyEntropyCollector('', 10);
           cryptoPRNG.startEntropyCollection();
           cryptoPRNG.stopEntropyCollectionAndCreatePRNG();

           expect(cryptoPRNG.Collectors.getPrivateKeyCollector)
               .not.toHaveBeenCalled();
         });
       });

    it('add private key entropy collector', function() {
      cryptolib('cryptoprng', function() {

        spyOn(cryptoPRNG, '_noWindow').and.returnValue(false);
        spyOn(cryptoPRNG, '_getCrypto').and.returnValue(null);
        spyOn(cryptoPRNG.Collectors, 'getPrivateKeyCollector')
            .and.callThrough();

        cryptoPRNG.setDataForPrivateKeyEntropyCollector(privateKeyPem, 100);
        cryptoPRNG.startEntropyCollection();
        cryptoPRNG.stopEntropyCollectionAndCreatePRNG();

        expect(cryptoPRNG.Collectors.getPrivateKeyCollector).toHaveBeenCalled();
      });
    });

    it('add entropy from private key pem', function() {
      cryptolib('cryptoprng', function() {

        spyOn(cryptoPRNG, '_noWindow').and.returnValue(false);
        spyOn(cryptoPRNG, '_getCrypto').and.returnValue(null);
        spyOn(cryptoPRNG, '_collectEntropy').and.callThrough();

        cryptoPRNG.setDataForPrivateKeyEntropyCollector(privateKeyPem, 100);
        cryptoPRNG.startEntropyCollection();
        cryptoPRNG.stopEntropyCollectionAndCreatePRNG();

        var privateKey = forge.pki.privateKeyFromPem(privateKeyPem);

        var args = cryptoPRNG._collectEntropy.calls.argsFor(0);
        expect(args[0]).toEqual(privateKey.d);
        expect(args[1]).toEqual(100);
      });
    });

    it('throw exception when private key pem is not correct', function() {
      cryptolib('cryptoprng', function() {

        var privateKeyPemInvalid = ['Invalid Pem'].join('\n');

        spyOn(cryptoPRNG, '_noWindow').and.returnValue(false);
        spyOn(cryptoPRNG, '_getCrypto').and.returnValue(null);
        spyOn(cryptoPRNG, 'startEntropyCollection').and.callThrough();

        cryptoPRNG.setDataForPrivateKeyEntropyCollector(
            privateKeyPemInvalid, 100);

        expect(function() {
          cryptoPRNG.startEntropyCollection();
        }).toThrowError('Invalid PEM formatted message.');
      });
    });

    it('properly handle entropy collection when window is not defined',
       function() {
         cryptolib('cryptoprng', function() {

           spyOn(cryptoPRNG, '_noWindow').and.returnValue(true);
           spyOn(cryptoPRNG, '_addNonWindowEntropyCollectors')
               .and.callThrough();
           spyOn(cryptoPRNG, '_getCrypto').and.callThrough();

           cryptoPRNG.startEntropyCollection();
           cryptoPRNG.stopEntropyCollectionAndCreatePRNG();

           expect(cryptoPRNG._addNonWindowEntropyCollectors).toHaveBeenCalled();
           expect(cryptoPRNG._getCrypto).not.toHaveBeenCalled();
         });
       });

    describe('return percentage of entropy collected ..', function() {
      it('equal to 100 when max entropy number is reached', function() {
        cryptolib('cryptoprng', function() {

          cryptoPRNG._maxEntropyCounter = 300;
          cryptoPRNG._entropyCounter = cryptoPRNG._maxEntropyCounter;

          expect(cryptoPRNG.getEntropyCollectedAsPercentage()).toEqual(100);
        });
      });

      it('equal to 100 when entropy counter is bigger than max entropy number',
         function() {
           cryptolib('cryptoprng', function() {

             cryptoPRNG._entropyCounter = cryptoPRNG._maxEntropyCounter + 1;

             expect(cryptoPRNG.getEntropyCollectedAsPercentage()).toEqual(100);
           });
         });

      it('equal to 0 when entropy counter is set to 0', function() {
        cryptolib('cryptoprng', function() {

          cryptoPRNG._entropyCounter = 0;

          expect(cryptoPRNG.getEntropyCollectedAsPercentage()).toEqual(0);
        });
      });

      it('equal to 50 when entropy counter = max entropy / 2', function() {
        cryptolib('cryptoprng', function() {

          cryptoPRNG._maxEntropyCounter = 300;
          cryptoPRNG._entropyCounter = cryptoPRNG._maxEntropyCounter / 2;

          expect(cryptoPRNG.getEntropyCollectedAsPercentage()).toEqual(50);
        });
      });

      it('equal to 10 when entropy counter is 10% of maximum entropy',
         function() {
           cryptolib('cryptoprng', function() {

             cryptoPRNG._maxEntropyCounter = 300;
             cryptoPRNG._entropyCounter = cryptoPRNG._maxEntropyCounter / 10;

             expect(cryptoPRNG.getEntropyCollectedAsPercentage()).toEqual(10);
           });
         });
    });
  });
});
