js: First testing and improvements
authorMaximilian Krambach <maximilian.krambach@intevation.de>
Wed, 25 Apr 2018 17:45:39 +0000 (19:45 +0200)
committerMaximilian Krambach <maximilian.krambach@intevation.de>
Wed, 25 Apr 2018 17:45:39 +0000 (19:45 +0200)
--

* Introduced Mocha/chai as testsuite. After development build
  'npm test' should run the unit tests. Functionality exclusive to
  Browsers/WebExtensions cannot be run this way, so some other testing
  is still needed.

  - package.json: Added required development packages
  - .babelrc indirect configuration for mocha. ES6 transpiling
    needs some babel configuration, but mocha has no setting for it.
  - test/mocha.opts Vonfiguration for mocha runs

* Fixed errors:
  - Helpers.js toKeyIdArray; isLongId is now exported
  - Key.js Key constructor failed
  - Message.js will not throw an Error during construction, a new
    message is now created with createMessage, which can return an
    Error or a GPGME_Message object

* Tests:
  - test/Helpers: exports from Helpers.js, GPGME_Error handling
  - test/Message: first init test with bad parameters

12 files changed:
lang/js/.babelrc [new file with mode: 0644]
lang/js/package.json
lang/js/src/Connection.js
lang/js/src/Errors.js
lang/js/src/Helpers.js
lang/js/src/Key.js
lang/js/src/Keyring.js
lang/js/src/Message.js
lang/js/src/gpgmejs.js
lang/js/test/Helpers.js [new file with mode: 0644]
lang/js/test/Message.js [new file with mode: 0644]
lang/js/test/mocha.opts [new file with mode: 0644]

diff --git a/lang/js/.babelrc b/lang/js/.babelrc
new file mode 100644 (file)
index 0000000..9d8d516
--- /dev/null
@@ -0,0 +1 @@
+{ "presets": ["es2015"] }
index 2b7dd7e..a794188 100644 (file)
@@ -5,13 +5,15 @@
   "main": "src/index.js",
   "private": true,
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "mocha"
   },
   "keywords": [],
   "author": "",
   "license": "",
   "devDependencies": {
     "webpack": "^4.5.0",
-    "webpack-cli": "^2.0.14"
+    "webpack-cli": "^2.0.14",
+    "chai": "^4.1.2",
+    "mocha": "^5.1.1"
   }
 }
index 5b092ab..a10f9d9 100644 (file)
@@ -83,7 +83,7 @@ export class Connection{
      */
     post(message){
         if (!this.isConnected){
-            return Promise.reject(gpgme_error('CONN_NO_CONNECT'));
+            return Promise.reject(gpgme_error('CONN_DISCONNECTED'));
         }
         if (!message || !message instanceof GPGME_Message){
             return Promise.reject(gpgme_error('PARAM_WRONG'), message);
index 2f53aa8..d26aab1 100644 (file)
@@ -55,14 +55,9 @@ const err_list = {
         msg: 'The Message is empty.',
         type: 'error'
     },
-    'MSG_OP_PENDING': {
-        msg: 'There is no operation specified yet. The parameter cannot'
-            + ' be set',
-        type: 'warning'
-    },
     'MSG_WRONG_OP': {
         msg: 'The operation requested could not be found',
-        type: 'warning'
+        type: 'error'
     },
     'MSG_NO_KEYS' : {
         msg: 'There were no valid keys provided.',
@@ -78,7 +73,7 @@ const err_list = {
     },
     // generic
     'PARAM_WRONG':{
-        msg: 'invalid parameter was found',
+        msg: 'Invalid parameter was found',
         type: 'error'
     },
     'PARAM_IGNORED': {
@@ -111,7 +106,7 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){
             return new GPGME_Error(code);
         }
         if (err_list[code].type === 'warning'){
-            console.log(new GPGME_Error(code));
+            console.warn(code + ': ' + err_list[code].msg);
         }
         return null;
     } else if (code === 'GNUPG_ERROR'){
index 841c0ed..9a69f85 100644 (file)
@@ -18,6 +18,7 @@
  * SPDX-License-Identifier: LGPL-2.1+
  */
 import { gpgme_error } from "./Errors";
+import { GPGME_Key } from "./Key";
 
 /**
  * Tries to return an array of fingerprints, either from input fingerprints or
@@ -26,7 +27,7 @@ import { gpgme_error } from "./Errors";
  * @returns {Array<String>} Array of fingerprints.
  */
 
-export function toKeyIdArray(input, nocheck){
+export function toKeyIdArray(input){
     if (!input){
         gpgme_error('MSG_NO_KEYS');
         return [];
@@ -46,7 +47,7 @@ export function toKeyIdArray(input, nocheck){
             let fpr = '';
             if (input[i] instanceof GPGME_Key){
                 fpr = input[i].fingerprint;
-            } else if (input[i].hasOwnProperty(primaryKey) &&
+            } else if (input[i].hasOwnProperty('primaryKey') &&
                 input[i].primaryKey.hasOwnProperty(getFingerprint)){
                     fpr = input[i].primaryKey.getFingerprint();
             }
@@ -92,7 +93,7 @@ export function isFingerprint(string){
 /**
  * check if the input is a valid Hex string with a length of 16
  */
-function isLongId(string){
+export function isLongId(string){
     return hextest(string, 16);
 };
 
index f6fa7ae..0b44b24 100644 (file)
@@ -26,9 +26,9 @@
  *
  */
 
-import {isFingerprint} from './Helpers'
-import {gpgme_error} from './Errors'
-import { GPGME_Message } from './Message';
+import { isFingerprint } from './Helpers'
+import { gpgme_error } from './Errors'
+import { createMessage } from './Message';
 import { permittedOperations } from './permittedOperations';
 
 export class GPGME_Key {
@@ -39,7 +39,7 @@ export class GPGME_Key {
 
     set fingerprint(fpr){
         if (isFingerprint(fpr) === true && !this._fingerprint){
-            this._fingerprint = fingerprint;
+            this._fingerprint = fpr;
         }
     }
 
@@ -181,9 +181,12 @@ function checkKey(fingerprint, property){
     }
     return new Promise(function(resolve, reject){
         if (!isFingerprint(fingerprint)){
-            reject('KEY_INVALID');
+            reject(gpgme_error('KEY_INVALID'));
+        }
+        let msg = createMessage ('keyinfo');
+        if (msg instanceof Error){
+            reject(gpgme_error('PARAM_WRONG'));
         }
-        let msg = new GPGME_Message('keyinfo');
         msg.setParameter('fingerprint', this.fingerprint);
         return (this.connection.post(msg)).then(function(result){
             if (result.hasOwnProperty(property)){
index e1f0a50..470eeee 100644 (file)
@@ -18,7 +18,7 @@
  * SPDX-License-Identifier: LGPL-2.1+
  */
 
-import {GPGME_Message} from './Message'
+import {createMessage} from './Message'
 import {GPGME_Key} from './Key'
 import { isFingerprint, isLongId } from './Helpers';
 import { gpgme_error } from './Errors';
@@ -50,7 +50,10 @@ export class GPGME_Keyring {
      *
      */
     getKeys(pattern, include_secret){
-        let msg = new GPGME_Message('listkeys');
+        let msg = createMessage('listkeys');
+        if (msg instanceof Error){
+            return Promise.reject(msg);
+        }
         if (pattern && typeof(pattern) === 'string'){
             msg.setParameter('pattern', pattern);
         }
index 06ac8db..4d24227 100644 (file)
  */
 import { permittedOperations } from './permittedOperations'
 import { gpgme_error } from './Errors'
-export class GPGME_Message {
+
+export function createMessage(operation){
+    if (typeof(operation) !== 'string'){
+        return gpgme_error('PARAM_WRONG');
+    }
+    if (permittedOperations.hasOwnProperty(operation)){
+        return new GPGME_Message(operation);
+    } else {
+        return gpgme_error('MSG_WRONG_OP');
+    }
+}
+
+/**
+ * Prepares a communication request. It checks operations and parameters in
+ * ./permittedOperations.
+ * @param {String} operation
+ */
+class GPGME_Message {
     //TODO getter
 
     constructor(operation){
-        setOperation(this, operation);
+        this.operation = operation;
     }
 
+    set operation (op){
+
+
+    }
     get operation(){
         return this._msg.op;
     }
@@ -41,9 +62,6 @@ export class GPGME_Message {
         if (!param || typeof(param) !== 'string'){
             return gpgme_error('PARAM_WRONG');
         }
-        if (!this._msg || !this._msg.op){
-            return gpgme_error('MSG_OP_PENDING');
-        }
         let po = permittedOperations[this._msg.op];
         if (!po){
             return gpgme_error('MSG_WRONG_OP');
@@ -90,22 +108,3 @@ export class GPGME_Message {
 
     }
 }
-
-/**
- * Defines the operation this message will have
- * @param {String} operation Must be defined in permittedOperations
- *  TODO: move to constructor?
- */
-function setOperation (scope, operation){
-    if (!operation || typeof(operation) !== 'string'){
-        return gpgme_error('PARAM_WRONG');
-    }
-    if (permittedOperations.hasOwnProperty(operation)){
-        if (!scope._msg){
-            scope._msg = {};
-        }
-        scope._msg.op = operation;
-    } else {
-        return gpgme_error('MSG_WRONG_OP');
-    }
-}
\ No newline at end of file
index b504a45..2ddf296 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 import {Connection} from "./Connection"
-import {GPGME_Message} from './Message'
+import {GPGME_Message, createMessage} from './Message'
 import {toKeyIdArray} from "./Helpers"
 import { gpgme_error } from "./Errors"
 import { GPGME_Keyring } from "./Keyring";
@@ -71,8 +71,10 @@ export class GpgME {
      */
     encrypt(data, publicKeys, wildcard=false){
 
-        let msg = new GPGME_Message('encrypt');
-
+        let msg = createMessage('encrypt');
+        if (msg instanceof Error){
+            return Promise.reject(msg)
+        }
         // TODO temporary
         msg.setParameter('armor', true);
         msg.setParameter('always-trust', true);
@@ -101,7 +103,10 @@ export class GpgME {
         if (data === undefined){
             return Promise.reject(gpgme_error('MSG_EMPTY'));
         }
-        let msg = new GPGME_Message('decrypt');
+        let msg = createMessage('decrypt');
+        if (msg instanceof Error){
+            return Promise.reject(msg);
+        }
         putData(msg, data);
         return this.connection.post(msg);
 
@@ -109,21 +114,27 @@ export class GpgME {
 
     deleteKey(key, delete_secret = false, no_confirm = false){
         return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
-        let msg = new GPGME_Message('deletekey');
+        let msg = createMessage('deletekey');
+        if (msg instanceof Error){
+            return Promise.reject(msg);
+        }
         let key_arr = toKeyIdArray(key);
         if (key_arr.length !== 1){
-            throw('TODO');
-            //should always be ONE key
+            return Promise.reject(
+                gpgme_error('GENERIC_ERROR'));
+            // TBD should always be ONE key?
         }
         msg.setParameter('key', key_arr[0]);
         if (delete_secret === true){
-            msg.setParameter('allow_secret', true); //TBD
+            msg.setParameter('allow_secret', true);
+            // TBD
         }
         if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
-            msg.setParameter('delete_force', true); //TBD
+            msg.setParameter('delete_force', true);
+            // TBD
         }
         this.connection.post(msg).then(function(success){
-            //TODO: it seems that there is always errors coming back:
+            // TODO: it seems that there is always errors coming back:
         }, function(error){
             switch (error.msg){
             case 'ERR_NO_ERROR':
diff --git a/lang/js/test/Helpers.js b/lang/js/test/Helpers.js
new file mode 100644 (file)
index 0000000..590f9f6
--- /dev/null
@@ -0,0 +1,110 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+import { expect } from "../node_modules/chai/chai";
+import { gpgme_error} from "../src/Errors";
+import { GPGME_Key } from "../src/Key";
+import { isLongId, isFingerprint, toKeyIdArray } from "../src/Helpers"
+
+const helper_params = {
+    validLongId: '0A0A0A0A0A0A0A0A',
+    validGPGME_Key: new GPGME_Key('ADDBC303B6D31026F5EB4591A27EABDF283121BB'),
+    validKeys: [new GPGME_Key('A1E3BC45BDC8E87B74F4392D53B151A1368E50F3'),
+        'ADDBC303B6D31026F5EB4591A27EABDF283121BB',
+        new GPGME_Key('EE17AEE730F88F1DE7713C54BBE0A4FF7851650A')],
+    validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+    invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
+    invalidFingerprint: [{hello:'World'}],
+    invalidKeyArray: {curiosity:'uncat'},
+    invalidKeyArray_OneBad: [
+        new GPGME_Key('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'),
+        'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
+        '3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
+    invalidErrorCode: 'Please type in all your passwords.'
+}
+
+describe('Error Object handling', function(){
+    it('check the Timeout error', function(){
+        let test0 = gpgme_error('CONN_TIMEOUT');
+        expect(test0).to.be.an.instanceof(Error);
+        expect(test0.code).to.equal('CONN_TIMEOUT');
+    });
+    it('Error Object returns generic code if code is not listed', function(){
+        let test0 = gpgme_error(helper_params.invalidErrorCode);
+        expect(test0).to.be.an.instanceof(Error);
+        expect(test0.code).to.equal('GENERIC_ERROR');
+    });
+
+    it('Warnings like PARAM_IGNORED should not return errors', function(){
+        let test0 = gpgme_error('PARAM_IGNORED');
+        expect(test0).to.be.null;
+    });
+});
+
+describe('Fingerprint checking', function(){
+    it('isFingerprint(): valid Fingerprint', function(){
+        let test0  = isFingerprint(helper_params.validFingerprint);
+        expect(test0).to.be.true;
+    });
+    it('isFingerprint(): invalid Fingerprint', function(){
+        let test0 = isFingerprint(helper_params.invalidFingerprint);
+        expect(test0).to.be.false;
+    });
+});
+describe('Converting to Fingerprint', function(){
+    it('Correct Inputs', function(){
+        it('Fingerprint string', function(){
+            let test0 = toKeyIdArray(helper_params.validFingerprint);
+            expect(test0).to.be.an('array');
+            expect(test0).to.include(helper_params.validFingerprint);
+        });
+        it('GPGME_Key', function(){
+            expect(helper_params.validGPGME_Key).to.be.an.instanceof(GPGME_Key);
+            let test0 = toKeyIdArray(helper_params.validGPGME_Key);
+            expect(test0).to.be.an('array');
+            expect(test0).to.include(helper_params.validGPGME_Key.fingerprint);
+        });
+        it('Array of valid inputs', function(){
+            let test0 = toKeyIdArray(helper_params.validKeys);
+            expect(test0).to.be.an('array');
+            expect(test0).to.have.lengthOf(helper_params.validKeys.length);
+        });
+    });
+    describe('Incorrect inputs', function(){
+        it('valid Long ID', function(){
+            let test0 = toKeyIdArray(helper_params.validLongId);
+            expect(test0).to.be.empty;
+        });
+        it('invalidFingerprint', function(){
+            let test0 = toKeyIdArray(helper_params.invalidFingerprint);
+            expect(test0).to.be.empty;
+        });
+        it('invalidKeyArray', function(){
+            let test0 = toKeyIdArray(helper_params.invalidKeyArray);
+            expect(test0).to.be.empty;
+        });
+        it('Partially invalid array', function(){
+            let test0 = toKeyIdArray(helper_params.invalidKeyArray_OneBad);
+            expect(test0).to.be.an('array');
+            expect(test0).to.have.lengthOf(
+                helper_params.invalidKeyArray_OneBad.length - 1);
+        });
+    });
+});
diff --git a/lang/js/test/Message.js b/lang/js/test/Message.js
new file mode 100644 (file)
index 0000000..454b8ca
--- /dev/null
@@ -0,0 +1,42 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+import { expect } from "../node_modules/chai/chai";
+import { GPGME_Message, createMessage } from "../src/Message";
+
+const message_params = {
+    invalid_op_action : 'dance',
+    invalid_op_type : [234, 34, '<>'],
+}
+
+describe('Message Object', function(){
+    describe('incorrect initialization', function(){
+        it('non-allowed operation', function(){
+            let test0 = createMessage(message_params.invalid_op_action);
+            expect(test0).to.be.an.instanceof(Error);
+            expect(test0.code).to.equal('MSG_WRONG_OP');
+        });
+        it('wrong parameter type in constructor', function(){
+            let test0 = createMessage(message_params.invalid_op_type);
+            expect(test0).to.be.an.instanceof(Error);
+            expect(test0.code).to.equal('PARAM_WRONG');
+        });
+    });
+});
diff --git a/lang/js/test/mocha.opts b/lang/js/test/mocha.opts
new file mode 100644 (file)
index 0000000..65adc1c
--- /dev/null
@@ -0,0 +1,4 @@
+--require babel-register
+--reporter spec
+--ui bdd
+--colors