diff options
| -rw-r--r-- | lang/js/src/Connection.js | 8 | ||||
| -rw-r--r-- | lang/js/src/Errors.js | 4 | ||||
| -rw-r--r-- | lang/js/src/Helpers.js | 5 | ||||
| -rw-r--r-- | lang/js/src/Key.js | 433 | ||||
| -rw-r--r-- | lang/js/src/permittedOperations.js | 95 | ||||
| -rw-r--r-- | lang/js/unittest_inputvalues.js | 14 | ||||
| -rw-r--r-- | lang/js/unittests.js | 67 | 
7 files changed, 448 insertions, 178 deletions
| diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index 07df5def..3b442622 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -215,7 +215,13 @@ class Answer{                          if (!this._response.hasOwnProperty(key)){                              this._response[key] = [];                          } -                        this._response[key].push(msg[key]); +                        if (Array.isArray(msg[key])) { +                            for (let i=0; i< msg[key].length; i++) { +                                this._response[key].push(msg[key][i]); +                            } +                        } else { +                            this._response[key].push(msg[key][i]); +                        }                      }                      else {                          return gpgme_error('CONN_UNEXPECTED_ANSWER'); diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js index 7e98f319..fa8a4efe 100644 --- a/lang/js/src/Errors.js +++ b/lang/js/src/Errors.js @@ -67,6 +67,10 @@ const err_list = {          msg:'Key object is invalid',          type: 'error'      }, +    'KEY_NOKEY': { +        msg:'This key does not exist in GPG', +        type: 'error' +    },      // generic      'PARAM_WRONG':{          msg: 'Invalid parameter was found', diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js index fd0e7200..b26f40fb 100644 --- a/lang/js/src/Helpers.js +++ b/lang/js/src/Helpers.js @@ -90,10 +90,11 @@ function hextest(key, len){  export function isFingerprint(string){      return hextest(string, 40);  }; +  /** - *  TODO no usage; check if the input is a valid Hex string with a length of 16 + *  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);  }; diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 075a190e..7d3d82b1 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -26,13 +26,19 @@   *   */ -import { isFingerprint } from './Helpers' +import { isFingerprint, isLongId } from './Helpers'  import { gpgme_error } from './Errors'  import { createMessage } from './Message';  import { permittedOperations } from './permittedOperations';  import { Connection } from './Connection'; - +/** + * Validates the fingerprint, and checks for tha availability of a connection. + * If both are available, a Key will be returned. + * @param {String} fingerprint + * @param {Object} parent Either a Connection, or the invoking object with a + * Connection (e.g. Keyring) + */  export function createKey(fingerprint, parent){      if (!isFingerprint(fingerprint)){          return gpgme_error('PARAM_WRONG'); @@ -47,6 +53,9 @@ export function createKey(fingerprint, parent){      }  } +/** + * Representing the Keys as stored in GPG + */  export class GPGME_Key {      constructor(fingerprint, connection){ @@ -63,7 +72,7 @@ export class GPGME_Key {      }      get connection(){ -        if (!this._fingerprint){ +        if (!this._data.fingerprint){              return gpgme_error('KEY_INVALID');          }          if (!this._connection instanceof Connection){ @@ -74,171 +83,345 @@ export class GPGME_Key {      }      set fingerprint(fpr){ -        if (isFingerprint(fpr) === true && !this._fingerprint){ -            this._fingerprint = fpr; +        if (isFingerprint(fpr) === true) { +            if (this._data === undefined) { +                this._data = {fingerprint:  fpr}; +            } else { +                if (this._data.fingerprint === undefined){ +                    this._data.fingerprint = fpr; +                } +            }          }      }      get fingerprint(){ -        if (!this._fingerprint){ +        if (!this._data || !this._data.fingerprint){              return gpgme_error('KEY_INVALID');          } -        return this._fingerprint; +        return this._data.fingerprint;      }      /** -     * hasSecret returns true if a secret subkey is included in this Key +     * +     * @param {Object} data Bulk set data for this key, with the Object as sent +     * by gpgme-json. +     * @returns {GPGME_Key|GPGME_Error} The Key object itself after values have +     * been set       */ -    get hasSecret(){ -        return this.checkKey('secret'); -    } - -    get isRevoked(){ -        return this.checkKey('revoked'); -    } - -    get isExpired(){ -        return this.checkKey('expired'); -    } - -    get isDisabled(){ -        return this.checkKey('disabled'); -    } - -    get isInvalid(){ -        return this.checkKey('invalid'); -    } - -    get canEncrypt(){ -        return this.checkKey('can_encrypt'); -    } +    setKeydata(data){ +        if (this._data === undefined) { +            this._data = {}; +        } +        if ( +            typeof(data) !== 'object') { +            return gpgme_error('KEY_INVALID'); +        } +        if (!this._data.fingerprint && isFingerprint(data.fingerprint)){ +            if (data.fingerprint !== this.fingerprint){ +                return gpgme_error('KEY_INVALID'); +            } +            this._data.fingerprint = data.fingerprint; +        } else if (this._data.fingerprint !== data.fingerprint){ +            return gpgme_error('KEY_INVALID'); +        } -    get canSign(){ -        return this.checkKey('can_sign'); -    } +        let booleans = ['expired', 'disabled','invalid','can_encrypt', +            'can_sign','can_certify','can_authenticate','secret', +            'is_qualified']; +        for (let b =0; b < booleans.length; b++) { +            if ( +                !data.hasOwnProperty(booleans[b]) || +                typeof(data[booleans[b]]) !== 'boolean' +            ){ +                return gpgme_error('KEY_INVALID'); +            } +            this._data[booleans[b]] = data[booleans[b]]; +        } +        if (typeof(data.protocol) !== 'string'){ +            return gpgme_error('KEY_INVALID'); +        } +        // TODO check valid protocols? +        this._data.protocol = data.protocol; -    get canCertify(){ -        return this.checkKey('can_certify'); -    } +        if (typeof(data.owner_trust) !== 'string'){ +            return gpgme_error('KEY_INVALID'); +        } +        // TODO check valid values? +        this._data.owner_trust = data.owner_trust; -    get canAuthenticate(){ -        return this.checkKey('can_authenticate'); -    } +        // TODO: what about origin ? +        if (!Number.isInteger(data.last_update)){ +            return gpgme_error('KEY_INVALID'); +        } +        this._data.last_update = data.last_update; -    get isQualified(){ -        return this.checkKey('is_qualified'); -    } +        this._data.subkeys = []; +        if (data.hasOwnProperty('subkeys')){ +            if (!Array.isArray(data.subkeys)){ +                return gpgme_error('KEY_INVALID'); +            } +            for (let i=0; i< data.subkeys.length; i++) { +                this._data.subkeys.push( +                    new GPGME_Subkey(data.subkeys[i])); +            } +        } -    get armored(){ -        let msg = createMessage ('export_key'); -        msg.setParameter('armor', true); -        if (msg instanceof Error){ -            return gpgme_error('KEY_INVALID'); +        this._data.userids = []; +        if (data.hasOwnProperty('userids')){ +            if (!Array.isArray(data.userids)){ +                return gpgme_error('KEY_INVALID'); +            } +            for (let i=0; i< data.userids.length; i++) { +                this._data.userids.push( +                    new GPGME_UserId(data.userids[i])); +            }          } -        this.connection.post(msg).then(function(result){ -            return result.data; -        }); -        // TODO return value not yet checked. Should result in an armored block -        // in correct encoding +        return this;      }      /** -     * TODO returns true if this is the default key used to sign +     * Query any property of the Key list +     * (TODO: armor not in here, might be unexpected) +     * @param {String} property Key property to be retreived +     * @param {*} cached (optional) if false, the data will be directly queried +     * from gnupg. +     *  @returns {*|Promise<*>} the value, or if not cached, a Promise +     * resolving on the value       */ -    get isDefault(){ -        throw('NOT_YET_IMPLEMENTED'); +    get(property, cached=true) { +        if (cached === false) { +            let me = this; +            return new Promise(function(resolve, reject) { +                me.refreshKey().then(function(key){ +                    resolve(key.get(property, true)); +                }, function(error){ +                    reject(error); +                }); +            }); +         } else { +            if (!this._data.hasOwnProperty(property)){ +                return gpgme_error('PARAM_WRONG'); +            } else { +                return (this._data[property]); +            } +        }      }      /** -     * get the Key's subkeys as GPGME_Key objects -     * @returns {Array<GPGME_Key>} +     * Reloads the Key from gnupg       */ -    get subkeys(){ -        return this.checkKey('subkeys').then(function(result){ -            // TBD expecting a list of fingerprints -            if (!Array.isArray(result)){ -                result = [result]; +    refreshKey() { +        let me = this; +        return new Promise(function(resolve, reject) { +            if (!me._data.fingerprint){ +                reject(gpgme_error('KEY_INVALID'));              } -            let resultset = []; -            for (let i=0; i < result.length; i++){ -                let subkey = new GPGME_Key(result[i], this.connection); -                if (subkey instanceof GPGME_Key){ -                    resultset.push(subkey); +            let msg = createMessage('keylist'); +            msg.setParameter('sigs', true); +            msg.setParameter('keys', me._data.fingerprint); +            me.connection.post(msg).then(function(result){ +                if (result.keys.length === 1){ +                    me.setKeydata(result.keys[0]); +                    resolve(me); +                } else { +                    reject(gpgme_error('KEY_NOKEY'));                  } -            } -            return Promise.resolve(resultset); -        }, function(error){ -            //TODO this.checkKey fails +            }, function (error) { +                reject(gpgme_error('GNUPG_ERROR'), error); +            })          });      } +    //TODO:      /** -     * creation time stamp of the key -     * @returns {Date|null} TBD +     * Get the armored block of the non- secret parts of the Key. +     * @returns {String} the armored Key block. +     * Notice that this may be outdated cached info. Use the async getArmor if +     * you need the most current info       */ -    get timestamp(){ -        return this.checkKey('timestamp'); -        //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available. -    } +    // get armor(){ TODO }      /** -     * The expiration timestamp of this key TBD -     *  @returns {Date|null} TBD +     * Query the armored block of the non- secret parts of the Key directly +     * from gpg. +     * Async, returns Promise<String>       */ -    get expires(){ -        return this.checkKey('expires'); -        // TODO convert to Date; check for 0 +    // getArmor(){ TODO } +    // + +    // get hasSecret(){TODO} // confusing difference to Key.get('secret')! +    // getHasSecret(){TODO async version} +} + +/** + * The subkeys of a Key. Currently, they cannot be refreshed separately + */ +class GPGME_Subkey { + +    constructor(data){ +        let keys = Object.keys(data); +        for (let i=0; i< keys.length; i++) { +            this.setProperty(keys[i], data[keys[i]]); +        } +    } + +    setProperty(property, value){ +        if (!this._data){ +            this._data = {}; +        } +        if (validSubKeyProperties.hasOwnProperty(property)){ +            if (validSubKeyProperties[property](value) === true) { +                this._data[property] = value; +            } +        }      }      /** -     * getter name TBD -     * @returns {String|Array<String>} The user ids associated with this key +     * +     * @param {String} property Information to request +     * @returns {String | Number} +     * TODO: date properties are numbers with Date in seconds       */ -    get userIds(){ -        return this.checkKey('uids'); +    get(property) { +        if (this._data.hasOwnProperty(property)){ +            return (this._data[property]); +        } +    } +} + +class GPGME_UserId { + +    constructor(data){ +        let keys = Object.keys(data); +        for (let i=0; i< keys.length; i++) { +            this.setProperty(keys[i], data[keys[i]]); +        } +    } + +    setProperty(property, value){ +        if (!this._data){ +            this._data = {}; +        } +        if (validUserIdProperties.hasOwnProperty(property)){ +            if (validUserIdProperties[property](value) === true) { +                this._data[property] = value; +            } +        }      }      /** -     * @returns {String} The public key algorithm supported by this subkey +     * +     * @param {String} property Information to request +     * @returns {String | Number} +     * TODO: date properties are numbers with Date in seconds       */ -    get pubkey_algo(){ -        return this.checkKey('pubkey_algo'); +    get(property) { +        if (this._data.hasOwnProperty(property)){ +            return (this._data[property]); +        }      } +} -    /** -    * generic function to query gnupg information on a key. -    * @param {*} property The gpgme-json property to check. -    * TODO: check if Promise.then(return) -    */ -    checkKey(property){ -        if (!this._fingerprint){ -            return gpgme_error('KEY_INVALID'); +const validUserIdProperties = { +    'revoked': function(value){ +        return typeof(value) === 'boolean'; +    }, +    'invalid':  function(value){ +        return typeof(value) === 'boolean'; +    }, +    'uid': function(value){ +        if (typeof(value) === 'string' || value === ''){ +            return true;;          } -        return gpgme_error('NOT_YET_IMPLEMENTED'); -        // TODO: async is not what is to be ecpected from Key information :( -        if (!property || typeof(property) !== 'string' || -            !permittedOperations['keyinfo'].hasOwnProperty(property)){ -            return gpgme_error('PARAM_WRONG'); -        } -        let msg = createMessage ('keyinfo'); -        if (msg instanceof Error){ -            return gpgme_error('PARAM_WRONG'); -        } -        msg.setParameter('fingerprint', this.fingerprint); -        this.connection.post(msg).then(function(result, error){ -            if (error){ -                return gpgme_error('GNUPG_ERROR',error.msg); -            } else if (result.hasOwnProperty(property)){ -                return result[property]; -            } -            else if (property == 'secret'){ -                // TBD property undefined means "not true" in case of secret? -                return false; -            } else { -                return gpgme_error('CONN_UNEXPECTED_ANSWER'); -            } -        }, function(error){ -            return gpgme_error('GENERIC_ERROR'); -        }); +        return false; +    }, +    'validity': function(value){ +        if (typeof(value) === 'string'){ +            return true;; +        } +        return false; +    }, +    'name': function(value){ +        if (typeof(value) === 'string' || value === ''){ +        return true;; +        } +        return false; +    }, +    'email': function(value){ +        if (typeof(value) === 'string' || value === ''){ +            return true;; +        } +        return false; +    }, +    'address': function(value){ +        if (typeof(value) === 'string' || value === ''){ +            return true;; +        } +        return false; +    }, +    'comment': function(value){ +        if (typeof(value) === 'string' || value === ''){ +            return true;; +        } +        return false; +    }, +    'origin':  function(value){ +        return Number.isInteger(value); +    }, +    'last_update':  function(value){ +        return Number.isInteger(value);      } -};
\ No newline at end of file +}; + +const validSubKeyProperties = { +    'invalid': function(value){ +        return typeof(value) === 'boolean'; +    }, +    'can_encrypt': function(value){ +        return typeof(value) === 'boolean'; +    }, +    'can_sign': function(value){ +        return typeof(value) === 'boolean'; +    }, +    'can_certify':  function(value){ +        return typeof(value) === 'boolean'; +    }, +    'can_authenticate':  function(value){ +        return typeof(value) === 'boolean'; +    }, +    'secret': function(value){ +        return typeof(value) === 'boolean'; +    }, +    'is_qualified': function(value){ +        return typeof(value) === 'boolean'; +    }, +    'is_cardkey':  function(value){ +        return typeof(value) === 'boolean'; +    }, +    'is_de_vs':  function(value){ +        return typeof(value) === 'boolean'; +    }, +    'pubkey_algo_name': function(value){ +            return typeof(value) === 'string'; +            // TODO: check against list of known?[''] +    }, +    'pubkey_algo_string': function(value){ +        return typeof(value) === 'string'; +        // TODO: check against list of known?[''] +    }, +    'keyid': function(value){ +        return isLongId(value); +    }, +    'pubkey_algo': function(value) { +        return (Number.isInteger(value) && value >= 0); +    }, +    'length': function(value){ +        return (Number.isInteger(value) && value > 0); +    }, +    'timestamp': function(value){ +        return (Number.isInteger(value) && value > 0); +    }, +    'expires': function(value){ +        return (Number.isInteger(value) && value > 0); +    } +} diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js index aa02a8bc..42213ec3 100644 --- a/lang/js/src/permittedOperations.js +++ b/lang/js/src/permittedOperations.js @@ -172,49 +172,57 @@ export const permittedOperations = {          }      }, - -    /** TBD: querying the Key's information (keyinfo) -    TBD name: { -        required: { -            'fingerprint': { -                allowed: ['string'] -            }, -        }, -        answer: { -            type: ['TBD'], -            data: [], -            params: ['hasSecret','isRevoked','isExpired','armored', -                'timestamp','expires','pubkey_algo'], -            infos: ['subkeys', 'userIds'] -            // {'hasSecret': <Boolean>, -            //  'isRevoked': <Boolean>, -            //  'isExpired': <Boolean>, -            //  'armored': <String>, // armored public Key block -            //  'timestamp': <Number>, // -            //  'expires': <Number>, -            //  'pubkey_algo': TBD // TBD (optional?), -            //  'userIds': Array<String>, -            //  'subkeys': Array<String> Fingerprints of Subkeys -            // } -    }*/ - -    /** -    listkeys:{ -        required: {}; +    keylist:{ +        required: {},          optional: { -            'with-secret':{ +            'protocol': { +                allowed: ['string'], +                allowed_data: ['cms', 'openpgp'] +            }, +            'chunksize': { +                allowed: ['number'], +            }, +            // note: For the meaning of the flags, refer to +            // https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html +            'secret': {                  allowed: ['boolean'] -            },{ -            'pattern': { -                allowed: ['string'] +            }, +            'extern': { +                allowed: ['boolean'] +            }, +            'local':{ +                allowed: ['boolean'] +            }, +            'sigs':{ +                allowed: ['boolean'] +            }, +            'notations':{ +                allowed: ['boolean'] +            }, +            'tofu': { +                allowed: ['boolean'] +            }, +            'ephemeral': { +                allowed: ['boolean'] +            }, +            'validate': { +                allowed: ['boolean'] +            }, +            // 'pattern': { TODO +            //     allowed: ['string'] +            // }, +            'keys': { +                allowed: ['string'], +                array_allowed: true              }          }, -    answer: { -        type: ['TBD'], -        infos: ['TBD'] -    // keys: Array<String> Fingerprints representing the results +        answer: { +            type: [], +            data: [], +            params: [], +            infos: ['keys'] +        }      }, -    */      /**      importkey: { @@ -256,4 +264,15 @@ export const permittedOperations = {       * TBD key modification?       * encryptsign: TBD       */ + +    version: { +        required: {}, +        optional: {}, +        answer: { +            type:  [''], +            data: ['gpgme'], +            infos: ['info'], +            params:[] +        } +    }  } diff --git a/lang/js/unittest_inputvalues.js b/lang/js/unittest_inputvalues.js index 3450afd2..ca51f4ae 100644 --- a/lang/js/unittest_inputvalues.js +++ b/lang/js/unittest_inputvalues.js @@ -6,7 +6,7 @@ let conn = new Connection;  export const helper_params = {      validLongId: '0A0A0A0A0A0A0A0A',      validKeys: ['A1E3BC45BDC8E87B74F4392D53B151A1368E50F3', -        createKey('ADDBC303B6D31026F5EB4591A27EABDF283121BB', conn), +        createKey('D41735B91236FDB882048C5A2301635EEFF0CB05', conn),          'EE17AEE730F88F1DE7713C54BBE0A4FF7851650A'],      validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',      validFingerprints: ['9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A', @@ -15,11 +15,11 @@ export const helper_params = {      invalidFingerprints: [{hello:'World'}, ['kekekeke'], new Uint32Array(40)],      invalidKeyArray: {curiosity:'uncat'},      invalidKeyArray_OneBad: [ -        createKey('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08', conn), +        createKey('D41735B91236FDB882048C5A2301635EEFF0CB05', conn),          'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',          '3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],      invalidErrorCode: 'Please type in all your passwords.', -    validGPGME_Key: createKey('ADDBC303B6D31026F5EB4591A27EABDF283121BB', conn), +    validGPGME_Key: createKey('D41735B91236FDB882048C5A2301635EEFF0CB05', conn),      valid_openpgplike: { primaryKey: {          getFingerprint: function(){              return '85DE2A8BA5A5AB3A8A7BE2000B8AED24D7534BC2';} @@ -41,5 +41,11 @@ export const message_params = {  }  export const whatever_params = { -    four_invalid_params: ['<(((-<', '>°;==;~~', '^^', '{{{{o}}}}'] +    four_invalid_params: ['<(((-<', '>°;==;~~', '^^', '{{{{o}}}}'], +} +export const key_params = { +    validKeyFingerprint: 'D41735B91236FDB882048C5A2301635EEFF0CB05', +    invalidKeyFingerprint: 'CDC3A2B2860625CCBFC5AAAAAC6D1B604967FC4A', +    validKeyProperties: ['expired', 'disabled','invalid','can_encrypt', +    'can_sign','can_certify','can_authenticate','secret','is_qualified']  } diff --git a/lang/js/unittests.js b/lang/js/unittests.js index 06b2b23a..bb06309d 100644 --- a/lang/js/unittests.js +++ b/lang/js/unittests.js @@ -22,6 +22,7 @@ import "./node_modules/chai/chai";  import { helper_params as hp } from "./unittest_inputvalues";  import { message_params as mp } from "./unittest_inputvalues";  import { whatever_params as wp } from "./unittest_inputvalues"; +import { key_params as kp } from "./unittest_inputvalues";  import { Connection } from "./src/Connection";  import { gpgme_error } from "./src/Errors";  import { toKeyIdArray , isFingerprint } from "./src/Helpers"; @@ -46,6 +47,7 @@ function unittests (){                  expect(answer.info).to.be.an('Array');                  expect(conn0.disconnect).to.be.a('function');                  expect(conn0.post).to.be.a('function'); +                conn0.disconnect();                  done();              }); @@ -65,7 +67,7 @@ function unittests (){      });      describe('Error Object handling', function(){ - +        // TODO: new GPGME_Error codes          it('check the Timeout error', function(){              let test0 = gpgme_error('CONN_TIMEOUT'); @@ -169,13 +171,55 @@ function unittests (){          it('correct Key initialization', function(){              let conn = new Connection; -            let key = createKey(hp.validFingerprint, conn); - +            let key = createKey(kp.validKeyFingerprint, conn);              expect(key).to.be.an.instanceof(GPGME_Key);              expect(key.connection).to.be.an.instanceof(Connection); -            // TODO not implemented yet: Further Key functionality +            conn.disconnect(); +        }); +        it('Key has data after a first refresh', function(done) { +            let conn = new Connection; +            let key = createKey(kp.validKeyFingerprint, conn); +            key.refreshKey().then(function(key2){ +                expect(key2).to.be.an.instanceof(GPGME_Key); +                expect(key2.get).to.be.a('function'); +                for (let i=0; i < kp.validKeyProperties.length; i++) { +                    let prop = key2.get(kp.validKeyProperties[i]); +                    expect(prop).to.not.be.undefined; +                    expect(prop).to.be.a('boolean'); +                } +                expect(isFingerprint(key2.get('fingerprint'))).to.be.true; +                expect( +                    key2.get('fingerprint')).to.equal(kp.validKeyFingerprint); +                expect( +                    key2.get('fingerprint')).to.equal(key.fingerprint); +                conn.disconnect(); +                done(); +            });          }); +        it('Non-cached key async data retrieval', function (done){ +            let conn = new Connection; +            let key = createKey(kp.validKeyFingerprint, conn); +            key.get('can_authenticate',false).then(function(result){ +                expect(result).to.be.a('boolean'); +                conn.disconnect(); +                done(); +            }); +        }) + +        it('Querying non-existing Key returns an error', function(done) { +            let conn = new Connection; +            let key = createKey(kp.invalidKeyFingerprint, conn); +            key.refreshKey().then(function(){}, +                function(error){ +                    expect(error).to.be.an.instanceof(Error); +                    expect(error.code).to.equal('KEY_NOKEY'); +                    conn.disconnect(); +                    done(); +            }); +        }); + +          it('Key can use the connection', function(done){              let conn = new Connection;              let key = createKey(hp.validFingerprint, conn); @@ -184,6 +228,7 @@ function unittests (){                  key.connection.disconnect();                  key.connection.checkConnection(false).then(function(result2){                      expect(result2).to.be.false; +                    conn.disconnect();                      done();                  });              }); @@ -204,16 +249,22 @@ function unittests (){                  expect(key0).to.be.an.instanceof(Error);                  expect(key0.code).to.equal('PARAM_WRONG');              } +            conn.disconnect();          }); -        it('bad GPGME_Key returns Error if used', function(){ + +        it('malformed GPGME_Key cannot be used', function(){              let conn = new Connection;              for (let i=0; i < 4; i++){                  let key = new GPGME_Key(wp.four_invalid_params[i], conn); - -                expect(key.connection).to.be.an.instanceof(Error); -                expect(key.connection.code).to.equal('KEY_INVALID'); +                expect(key.fingerprint).to.be.an.instanceof(Error); +                expect(key.fingerprint.code).to.equal('KEY_INVALID');              } +            conn.disconnect();          }); + +        // TODO: tests for subkeys +        // TODO: tests for userids +        // TODO: some invalid tests for key/keyring      });      describe('GPGME_Keyring', function(){ | 
