/*
 * 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 */
'use strict';

var OrProofTestData = require('../data/or-proof-data');
var ProgressMeterTestData = require('../data/progress-meter-data');
var ValidationTestData = require('../data/validation-data');
var zkProof = require('../../lib/index');
var elGamal = require('scytl-elgamal');

describe('The zero-knowledge proof module should be able to ...', function() {
  var proofService_;

  var group_;
  var anotherGroup_;
  var secret_;
  var ciphertext_;
  var secretFromAnotherGroup_;
  var ciphertextFromAnotherGroup_;
  var ciphertextWithTooManyElements_;
  var publicKey_;
  var publicKeyFromAnotherGroup_;
  var index_;
  var tooSmallIndex_;
  var tooLargeIndex_;
  var numElements_;
  var elements_;
  var tooFewElements_;
  var elementsFromAnotherGroup_;
  var callback_;
  var nonObject_;
  var emptyObject_;
  var nonNumber_;
  var nonPositiveNumber_;
  var nonString_;
  var nonArray_;
  var emptyArray_;
  var nonObjectArray_;
  var nonFunction_;
  var nonAuxiliaryData_;
  var nonProofObject_;
  var proofHandler_;
  var proof_;

  beforeAll(function() {
    proofService_ = zkProof.newService();

    var testData = new OrProofTestData();
    var elGamalService = elGamal.newService();
    group_ = testData.getGroup();
    anotherGroup_ = testData.getAnotherGroup();
    var encryptedElements = testData.getEncryptedElements();
    secret_ = encryptedElements.secret;
    ciphertext_ = elGamalService.newEncryptedElements(
        encryptedElements.gamma, encryptedElements.phis);
    var encryptedElementsFromAnotherGroup =
        testData.getEncryptedElementsFromAnotherGroup();
    secretFromAnotherGroup_ = encryptedElementsFromAnotherGroup.secret;
    ciphertextFromAnotherGroup_ = elGamalService.newEncryptedElements(
        encryptedElementsFromAnotherGroup.gamma,
        encryptedElementsFromAnotherGroup.phis);
    var encryptedElementsWithTooManyElements =
        testData.getEncryptedElementsWithTooManyElements();
    ciphertextWithTooManyElements_ = elGamalService.newEncryptedElements(
        encryptedElementsWithTooManyElements.gamma,
        encryptedElementsWithTooManyElements.phis);
    publicKey_ = testData.getPublicKey();
    publicKeyFromAnotherGroup_ = testData.getPublicKeyFromAnotherGroup();
    index_ = testData.getIndex();
    tooSmallIndex_ = testData.getTooSmallIndex();
    tooLargeIndex_ = testData.getTooLargeIndex();
    numElements_ = testData.getNumElements();
    elements_ = testData.getElements();
    tooFewElements_ = testData.getTooFewElements();
    elementsFromAnotherGroup_ = testData.getElementsFromAnotherGroup();

    var progressMeterTestData = new ProgressMeterTestData();
    callback_ = progressMeterTestData.progressCallback;

    var validationTestData = new ValidationTestData();
    nonObject_ = validationTestData.getNonObject();
    emptyObject_ = validationTestData.getEmptyObject();
    nonNumber_ = validationTestData.getNonNumber();
    nonPositiveNumber_ = validationTestData.getNonPositiveNumber();
    nonString_ = validationTestData.getNonString();
    nonArray_ = validationTestData.getNonArray();
    emptyArray_ = validationTestData.getEmptyArray();
    nonObjectArray_ = validationTestData.getNonObjectArray();
    nonFunction_ = validationTestData.getNonFunction();
    nonAuxiliaryData_ = validationTestData.getNonAuxiliaryData();
    nonProofObject_ = validationTestData.getNonProofObject();

    proofHandler_ = proofService_.newOrProofHandler(group_).init(publicKey_);
    proof_ = proofHandler_.generate(secret_, elements_, index_, ciphertext_);
  });

  beforeEach(function() {
    proofHandler_ = proofService_.newOrProofHandler(group_).init(publicKey_);
  });

  describe('create a zero-knowledge proof service that should be able to ..', function() {
    describe('create an OR proof handler that should be able to', function() {
      it('throw an error when being created, using invalid input data',
         function() {
           expect(function() {
             proofService_.newOrProofHandler();
           }).toThrow();

           expect(function() {
             proofService_.newOrProofHandler(undefined);
           }).toThrow();

           expect(function() {
             proofService_.newOrProofHandler(null);
           }).toThrow();

           expect(function() {
             proofService_.newOrProofHandler(nonObject_);
           }).toThrow();

           expect(function() {
             proofService_.newOrProofHandler(emptyObject_);
           }).toThrow();
         });

      it('throw an error when being initialized, using invalid input data',
         function() {
           expect(function() {
             proofHandler_.init();
           }).toThrow();

           expect(function() {
             proofHandler_.init(undefined);
           }).toThrow();

           expect(function() {
             proofHandler_.init(null);
           }).toThrow();

           expect(function() {
             proofHandler_.init(nonObject_);
           }).toThrow();

           expect(function() {
             proofHandler_.init(emptyObject_);
           }).toThrow();

           expect(function() {
             proofHandler_.init(publicKeyFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when generating an OR proof, using an invalid secret',
         function() {
           expect(function() {
             proofHandler_.generate(undefined, elements_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(null, elements_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(nonObject_, elements_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 emptyObject_, elements_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secretFromAnotherGroup_, elements_, index_, ciphertext_);
           }).toThrow();
         });

      it('throw an error when generating an OR proof, using invalid elements',
         function() {
           expect(function() {
             proofHandler_.generate(secret_, undefined, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(secret_, null, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(secret_, nonArray_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(secret_, emptyArray_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, nonObjectArray_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, tooFewElements_, index_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elementsFromAnotherGroup_, index_, ciphertext_);
           }).toThrow();
         });

      it('throw an error when generating an OR proof, using invalid index',
         function() {
           expect(function() {
             proofHandler_.generate(secret_, elements_, undefined, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(secret_, elements_, null, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, nonNumber_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, tooSmallIndex_, ciphertext_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, tooLargeIndex_, ciphertext_);
           }).toThrow();
         });

      it('throw an error when generating an OR proof, using an invalid ciphertext',
         function() {
           expect(function() {
             proofHandler_.generate(secret_, elements_, index_, undefined);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(secret_, elements_, index_, null);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(secret_, elements_, index_, nonObject_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(secret_, elements_, index_, emptyObject_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, index_, ciphertextWithTooManyElements_);
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, index_, ciphertextFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when generating an OR proof, using invalid optional data',
         function() {
           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, index_, ciphertext_,
                 {data: nonAuxiliaryData_});
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, index_, ciphertext_,
                 {preComputation: nonObject_});
           }).toThrow();

           expect(function() {
             proofHandler_.generate(
                 secret_, elements_, index_, ciphertext_,
                 {preComputation: nonProofObject_});
           }).toThrow();
         });

      it('throw an error when pre-computing an OR proof, using invalid input data',
         function() {
           expect(function() {
             proofHandler_.preCompute();
           }).toThrow();

           expect(function() {
             proofHandler_.preCompute(undefined);
           }).toThrow();

           expect(function() {
             proofHandler_.preCompute(null);
           }).toThrow();

           expect(function() {
             proofHandler_.preCompute(nonNumber_);
           }).toThrow();

           expect(function() {
             proofHandler_.preCompute(nonPositiveNumber_);
           }).toThrow();

           expect(function() {
             proofHandler_.preCompute(1);
           }).toThrow();
         });

      it('throw an error when verifying an OR proof, using an invalid proof',
         function() {
           expect(function() {
             var verified =
                 proofHandler_.verify(undefined, elements_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(null, elements_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified =
                 proofHandler_.verify(nonProofObject_, elements_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();
         });

      it('throw an error when verifying an OR proof, using invalid elements',
         function() {
           expect(function() {
             var verified =
                 proofHandler_.verify(proof_, undefined, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(proof_, null, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified =
                 proofHandler_.verify(proof_, nonArray_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified =
                 proofHandler_.verify(proof_, emptyArray_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified =
                 proofHandler_.verify(proof_, nonObjectArray_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified =
                 proofHandler_.verify(proof_, tooFewElements_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(
                 proof_, elementsFromAnotherGroup_, ciphertext_);
             expect(verified).toBeTruthy();
           }).toThrow();
         });

      it('throw an error when verifying an OR proof, using an invalid ciphertext',
         function() {
           expect(function() {
             var verified = proofHandler_.verify(proof_, elements_, undefined);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(proof_, elements_, null);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(proof_, elements_, nonObject_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(emptyObject_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(
                 proof_, elements_, ciphertextWithTooManyElements_);
             expect(verified).toBeTruthy();
           }).toThrow();

           expect(function() {
             var verified = proofHandler_.verify(
                 proof_, elements_, ciphertextFromAnotherGroup_);
             expect(verified).toBeTruthy();
           }).toThrow();
         });

      it('throw an error when verifying an OR proof, using invalid optional data',
         function() {
           expect(function() {
             proofHandler_.verify(
                 proof_, elements_, ciphertext_, {data: nonAuxiliaryData_});
           }).toThrow();
         });

      it('throw an error when measuring OR proof progress, using invalid input data',
         function() {
           expect(function() {
             proofHandler_.measureProgress();
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(undefined);
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(null);
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(nonFunction_);
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(callback_, null);
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(callback_, nonNumber_);
           }).toThrow();

           expect(function() {
             proofHandler_.measureProgress(callback_, nonPositiveNumber_);
           }).toThrow();
         });
    });
  });
});
