pakmanaged.js
function assertKeys (keys) {
        var obj = flag(this, 'object')
          , str
          , ok = true
          , mixedArgsMsg = 'keys must be given single argument of Array|Object|String, or multiple String arguments';
    
        switch (_.type(keys)) {
          case "array":
            if (arguments.length > 1) throw (new Error(mixedArgsMsg));
            break;
          case "object":
            if (arguments.length > 1) throw (new Error(mixedArgsMsg));
            keys = Object.keys(keys);
            break;
          default:
            keys = Array.prototype.slice.call(arguments);
        }
    
        if (!keys.length) throw new Error('keys required');
    
        var actual = Object.keys(obj)
          , expected = keys
          , len = keys.length
          , any = flag(this, 'any')
          , all = flag(this, 'all');
    
        if (!any && !all) {
          all = true;
        }
    
        // Has any
        if (any) {
          var intersection = expected.filter(function(key) {
            return ~actual.indexOf(key);
          });
          ok = intersection.length > 0;
        }
    
        // Has all
        if (all) {
          ok = keys.every(function(key){
            return ~actual.indexOf(key);
          });
          if (!flag(this, 'negate') && !flag(this, 'contains')) {
            ok = ok && keys.length == actual.length;
          }
        }
    
        // Key string
        if (len > 1) {
          keys = keys.map(function(key){
            return _.inspect(key);
          });
          var last = keys.pop();
          if (all) {
            str = keys.join(', ') + ', and ' + last;
          }
          if (any) {
            str = keys.join(', ') + ', or ' + last;
          }
        } else {
          str = _.inspect(keys[0]);
        }
    
        // Form
        str = (len > 1 ? 'keys ' : 'key ') + str;
    
        // Have / include
        str = (flag(this, 'contains') ? 'contain ' : 'have ') + str;
    
        // Assertion
        this.assert(
            ok
          , 'expected #{this} to ' + str
          , 'expected #{this} to not ' + str
          , expected.slice(0).sort()
          , actual.sort()
          , true
        );
      }
function assertKeys (keys) {
        var obj = flag(this, 'object')
          , str
          , ok = true
          , mixedArgsMsg = 'keys must be given single argument of Array|Object|String, or multiple String arguments';
    
        switch (_.type(keys)) {
          case "array":
            if (arguments.length > 1) throw (new Error(mixedArgsMsg));
            break;
          case "object":
            if (arguments.length > 1) throw (new Error(mixedArgsMsg));
            keys = Object.keys(keys);
            break;
          default:
            keys = Array.prototype.slice.call(arguments);
        }
    
        if (!keys.length) throw new Error('keys required');
    
        var actual = Object.keys(obj)
          , expected = keys
          , len = keys.length
          , any = flag(this, 'any')
          , all = flag(this, 'all');
    
        if (!any && !all) {
          all = true;
        }
    
        // Has any
        if (any) {
          var intersection = expected.filter(function(key) {
            return ~actual.indexOf(key);
          });
          ok = intersection.length > 0;
        }
    
        // Has all
        if (all) {
          ok = keys.every(function(key){
            return ~actual.indexOf(key);
          });
          if (!flag(this, 'negate') && !flag(this, 'contains')) {
            ok = ok && keys.length == actual.length;
          }
        }
    
        // Key string
        if (len > 1) {
          keys = keys.map(function(key){
            return _.inspect(key);
          });
          var last = keys.pop();
          if (all) {
            str = keys.join(', ') + ', and ' + last;
          }
          if (any) {
            str = keys.join(', ') + ', or ' + last;
          }
        } else {
          str = _.inspect(keys[0]);
        }
    
        // Form
        str = (len > 1 ? 'keys ' : 'key ') + str;
    
        // Have / include
        str = (flag(this, 'contains') ? 'contain ' : 'have ') + str;
    
        // Assertion
        this.assert(
            ok
          , 'expected #{this} to ' + str
          , 'expected #{this} to not ' + str
          , expected.slice(0).sort()
          , actual.sort()
          , true
        );
      }
### .keys(key1, [key2], [...])

Asserts that the target contains any or all of the passed-in keys.
Use in combination with `any`, `all`, `contains`, or `have` will affect
what will pass.

When used in conjunction with `any`, at least one key that is passed
in must exist in the target object. This is regardless whether or not
the `have` or `contain` qualifiers are used. Note, either `any` or `all`
should be used in the assertion. If neither are used, the assertion is
defaulted to `all`.

When both `all` and `contain` are used, the target object must have at
least all of the passed-in keys but may have more keys not listed.

When both `all` and `have` are used, the target object must both contain
all of the passed-in keys AND the number of keys in the target object must
match the number of keys passed in (in other words, a target object must
have all and only all of the passed-in keys).

expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz');
expect({ foo: 1, bar: 2 }).to.have.any.keys('foo');
expect({ foo: 1, bar: 2 }).to.contain.any.keys('bar', 'baz');
expect({ foo: 1, bar: 2 }).to.contain.any.keys(['foo']);
expect({ foo: 1, bar: 2 }).to.contain.any.keys({'foo': 6});
expect({ foo: 1, bar: 2 }).to.have.all.keys(['bar', 'foo']);
expect({ foo: 1, bar: 2 }).to.have.all.keys({'bar': 6, 'foo': 7});
expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']);
expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys({'bar': 6});


@name keys
@alias key
@param {String...|Array|Object} keys
@api public