Skip to content

Commit

Permalink
feat(test/mock) - Refactor Mocking
Browse files Browse the repository at this point in the history
  • Loading branch information
Steven Edouard committed Nov 12, 2015
1 parent 28e4a56 commit 6921cd6
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 20 deletions.
2 changes: 1 addition & 1 deletion lib/native/adv_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var advApi = ffi.Library('Advapi32', {
_Reserved_ LPDWORD lpReserved,
_Out_opt_ LPDWORD lpType,
_Out_opt_ LPBYTE lpData,
_Inout_opt_ LPDWORD lpcbData
_Inout_opt_ LPDWORD lpcbDataRegOpenKeyExA
);
*/
RegQueryValueExA: ['uint64', [types.HKEY, 'string', 'pointer', types.LPDWORD, types.LPBYTE, types.LPDWORD]],
Expand Down
15 changes: 7 additions & 8 deletions lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var api = {
throw 'The key ' + preDefinedKey + ' is not valid. Use the windef module for the list of predefined keys';
}

var pHkey = ref.alloc(types.PHKEY);
var pHkey = ref.alloc(types.PHKEY, new Buffer(ref.sizeof.pointer));
console.log('PHKEY LENGTH: ' + pHkey.deref().length);
var result = advApi.RegOpenKeyExA(preDefinedKey, subKeyName, 0, accessLevel, pHkey);
debug('result:' + result);
if (result !== 0) {
Expand All @@ -25,7 +26,7 @@ var api = {
return new Key(pHkey, subKeyName);
},
openKeyFromKeyObject: function (keyObject, subKeyName, accessLevel) {
var pHkey = ref.alloc(types.PHKEY);
var pHkey = ref.alloc(types.PHKEY, new Buffer(ref.sizeof.pointer));

// RegOpenKeyEx can also take an HKEY in addition to a predefined value
var advApi2 = ffi.Library('Advapi32', {
Expand All @@ -40,11 +41,10 @@ var api = {
return new Key(pHkey, subKeyName);
},
queryValueForKeyObject: function (key, valueName) {
var pKeyDataLength = ref.alloc(types.LPDWORD),
pKeyType = ref.alloc(types.LPDWORD);
var pKeyDataLength = ref.alloc(types.LPDWORD, new Buffer(ref.sizeof.pointer)),
pKeyType = ref.alloc(types.LPDWORD, new Buffer(ref.sizeof.pointer));
// QUERY FOR VALUE SIZE & TYPE
var result = advApi.RegQueryValueExA(key.handle.deref(), valueName, null, pKeyType, null, pKeyDataLength);

// READ VALUE
var value = new Buffer(pKeyDataLength.readUInt32LE()),
valueType = pKeyType.readUInt32LE();
Expand Down Expand Up @@ -121,9 +121,9 @@ var api = {
}
},
createKey: function (key, subKeyName, accessLevel) {
var pHkey = ref.alloc(types.PHKEY);
var pHkey = ref.alloc(types.PHKEY, new Buffer(ref.sizeof.pointer));

var result = advApi.RegCreateKeyExA(key.handle.deref(), subKeyName, null, null, 0 /*REG_OPTION_NON_VOLATILE*/ , accessLevel, null, pHkey, null);
var result = advApi.RegCreateKeyExA(key.handle.deref(), subKeyName, null, null, windef.REG_OPTION_NON_VOLATILE, accessLevel, null, pHkey, null);

if (result !== 0) {
throw 'Failed to open key error: ' + error[result];
Expand All @@ -137,7 +137,6 @@ var api = {
}
},
closeKey: function (key) {
debug('KEY:' + key);
var result = advApi.RegCloseKey(key.handle.deref());

if (result !== 0) {
Expand Down
2 changes: 1 addition & 1 deletion lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var types = {
HWND: ref.refType(ref.types.void),
BYTE: ref.types.uint8,
HKEY: ref.refType(ref.types.void),
PVOID: ref.refType(ref.types.void),
PVOID: ref.refType('pointer'),
HANDLE: ref.refType(ref.types.void),
HINSTANCE: ref.refType(ref.types.void),
LPCTSTR: ref.refType(ref.types.CString),
Expand Down
3 changes: 2 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ var ref = require('ref'),
shell32 = require('./native/shell32'),
windef = require('./windef'),
debug = require('debug')('windows-registry'),
registry = require('./registry');
registry = require('./registry'),
debug = require('debug')('windows-registry');

// pass in default values for members
var lpVerb = 'runas';
Expand Down
1 change: 1 addition & 0 deletions lib/windef.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = {
REG_MULTI_SZ: 7,
REG_RESOURCE_LIST: 8
},
REG_OPTION_NON_VOLATILE: 0,
/*
typedef struct _SHELLEXECUTEINFO {
DWORD cbSize;
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
},
"devDependencies": {
"grunt": "^0.4.5",
"grunt-mocha-cli": "^2.0.0",
"load-grunt-tasks": "^3.3.0",
"grunt-contrib-jshint": "^0.11.3",
"grunt-contrib-watch": "^0.6.1",
"grunt-jsbeautifier": "^0.2.10",
"grunt-jscs": "^2.1.0"
"grunt-jscs": "^2.1.0",
"grunt-mocha-cli": "^2.0.0",
"load-grunt-tasks": "^3.3.0",
"mockery": "^1.4.0"
},
"scripts": {
"test": "grunt test"
Expand Down
4 changes: 4 additions & 0 deletions test/file_association.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* global describe, it */
'use strict';
if (process.env.TEST_MOCKS_ON) {
require('./test_helper');
}

var utils = require('../lib/utils'),
assert = require('assert');

Expand Down
8 changes: 5 additions & 3 deletions test/key.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* global describe, it */
'use strict';
var windef = require('../lib/windef'),
Key = require('../lib/key'),
assert = require('assert');
require('./test_helper');
var assert = require('assert'),
windef = require('../lib/windef'),
Key = require('../lib/key');

describe('Key Open Tests', () => {
it('Should create a key given a subkey', () => {
Expand Down Expand Up @@ -65,6 +66,7 @@ describe('Set / Query Value Tests', function () {
key.setValue('test_value_name', windef.REG_VALUE_TYPE.REG_SZ, 'test_value');

var value = key.getValue('test_value_name');

assert.equal(value, 'test_value');
key.close();
});
Expand Down
220 changes: 220 additions & 0 deletions test/mock/adv_api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/* globals Buffer */
/**
* A very dumb minimal implementation of the Windows Registry.
* Enough to run our tests
*/
'use strict';
var assert = require('assert'),
windef = require('../../lib/windef'),
types = require('../../lib/types'),
debug = require('debug')('windows-registry'),
ref = require('ref');

/*
* Dumb O(n) search for value inside object
*/
function findValueInHash(value, hash) {
var found = false;
for (let k in hash) {
if (hash[k] === value) {
found = true;
break;
}
}
return found;
}

var keys = {
};
keys[windef.HKEY.HKEY_CLASSES_ROOT] = {
predefValue: true,
open: false,
values: {
}
};
var mockIndex = 0x00000001;

var advApi = {
/*
LONG WINAPI RegQueryValueEx(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpValueName,
_Reserved_ LPDWORD lpReserved,
_Out_opt_ LPDWORD lpType,
_Out_opt_ LPBYTE lpData,
_Inout_opt_ LPDWORD lpcbData
);
*/
RegQueryValueExA: function (hKey, valueName, shouldBeNull, lpType, lpData, lpcbData) {
debug('RegQueryValueExA');
if (lpData === null) {
debug(keys[hKey.address()].values.test_value_name);
lpType.writeUInt32LE(windef.REG_VALUE_TYPE.REG_SZ, 0);
lpcbData.writeUInt32LE(keys[hKey.address()].values[valueName].length, 0);
return 0;
}

lpData.write(keys[hKey.address()].values[valueName].value, 'utf8');
lpType.writeUInt16LE(windef.REG_VALUE_TYPE.REG_SZ);
return 0;
},
/*
LONG WINAPI RegOpenKeyEx(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpSubKey,
_In_ DWORD ulOptions,
_In_ REGSAM samDesired,
_Out_ PHKEY phkResult
);
*/
RegOpenKeyExA: function (hKey, subKeyName, shouldBeZero, accessLevel, pHkey) {
var accessLevelFound = findValueInHash(accessLevel, windef.KEY_ACCESS);
debug('Mock: RegOpenKeyExA subkey: ' + subKeyName);
if (hKey.address) {
debug('Mock: hKey address:' + hKey.address());
}
debug('keys:');
debug(keys);
// predefined key
ref.writeUInt64LE(pHkey.deref(), 0, mockIndex);
mockIndex += 1;
if (typeof hKey === 'number') {
assert(findValueInHash(hKey, windef.HKEY), 'Mock: Invalid predefined key specified');

if (!keys[hKey]) {
debug('failed to find key for ' + hKey + ' current keys:');
debug(keys);
// FILE NOT FOUND
return 2;
}
keys[pHkey.deref().address()] = {
opened: true,
values: {

}
};
} else {
assert(hKey.constructor === Buffer);

if (!keys[hKey.address()]) {
debug('failed to find key for ' + hKey.address() + ' current keys:');
debug(keys);
// FILE NOT FOUND
return 2;
}
keys[hKey.address()].open = true;
keys[pHkey.deref().address()] = keys[hKey.address()];
}

assert(typeof subKeyName === 'string');
assert(shouldBeZero === 0);
assert(accessLevelFound, 'Mock: Invalid access level specified');
assert(pHkey.deref().constructor === Buffer);

return 0;
},
/*
LONG WINAPI RegSetValueEx(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpValueName,
_Reserved_ DWORD Reserved,
_In_ DWORD dwType,
_In_ const BYTE *lpData,
_In_ DWORD cbData
);
*/
RegSetValueExA: function (hKey, valueName, shouldBeNull, valueType, valueBuffer, bufferLength) {
debug('Mock: RegSetValueExA');
// predefined key
if (typeof hKey === 'number') {
assert(findValueInHash(hKey, windef.HKEY), 'Mock: Invalid predefined key specified');
} else {
assert(hKey.constructor === Buffer);
}
assert(typeof valueName === 'string');
assert(typeof valueType === 'number');
assert(valueBuffer.constructor === Buffer);
assert(typeof bufferLength === 'number');

keys[hKey.address()].values[valueName] = {
valueType: valueType,
value: ref.readCString(valueBuffer),
length: bufferLength
};
return 0;
},
/**
* LONG WINAPI RegCreateKeyEx(
_In_ HKEY hKey,
_In_ LPCTSTR lpSubKey,
_Reserved_ DWORD Reserved,
_In_opt_ LPTSTR lpClass,
_In_ DWORD dwOptions,
_In_ REGSAM samDesired,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_Out_ PHKEY phkResult,
_Out_opt_ LPDWORD lpdwDisposition
);
*/
RegCreateKeyExA: function (hKey, subKeyName, shouldBeNull, shouldBeNull2, securityAttributes, accessLevel, shouldBeNull3, pHkey, shouldBeNull4) {
debug('Mock: RegCreateKeyExA');
assert(hKey.constructor === Buffer);
assert(typeof subKeyName === 'string');
assert(shouldBeNull === null);
assert(shouldBeNull2 === null);
assert(shouldBeNull3 === null);
assert(shouldBeNull4 === null);
assert.equal(securityAttributes, 0, 'Mock: Security Attributes are not supported yet');
assert(findValueInHash(accessLevel, windef.KEY_ACCESS), 'Mock: Invalid access level specified');
assert(pHkey.deref().constructor === Buffer);
debug('Mock: Writing: ' + mockIndex + ' 64 bit dummy pointer to buffer with length: ' + pHkey.deref().length);

ref.writeUInt64LE(pHkey.deref(), 0, mockIndex);
debug('Mock: Wrote 64 bit dummy pointer');
mockIndex += 1;
debug('Mock: Creating fake key for: ' + hKey.address());
debug('Subkey: ' + subKeyName);
keys[hKey.address()] = {
opened: true,
subkeys: {
subKeyName: ''
},
values: {

}
};

return 0;
},
/*
LONG WINAPI RegDeleteTree(
_In_ HKEY hKey,
_In_opt_ LPCTSTR lpSubKey
);
*/
RegDeleteTreeA: function (hKey, subKeyName) {
if (typeof hKey === 'number') {
assert(findValueInHash(hKey, windef.HKEY), 'Mock: Invalid predefined key specified');
} else {
assert(hKey.constructor === Buffer, 'Mock: hKey should be of type buffer if not a number');
}

assert(typeof subKeyName === 'string' || subKeyName === undefined);
delete keys[hKey.address()];
return 0;
},
/*
LONG WINAPI RegCloseKey(
_In_ HKEY hKey
);
*/
RegCloseKey: function (hKey) {
debug('Mock: RegCloseKey');
assert.equal(hKey.indirections, types.HKEY.indirections);

delete keys[hKey.address()];
return 0;
}
};

module.exports = advApi;
31 changes: 31 additions & 0 deletions test/mock/ffi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
var advApi = require('./adv_api'),
assert = require('assert'),
types = require('../../lib/types');

module.exports = {
Library: function (libFile, funcs) {
switch (libFile) {
case 'Advapi32':
assert(funcs.RegOpenKeyExA.constructor === Array);
if(funcs.RegOpenKeyExA[1][0].indirection === types.HKEY.indirection &&
funcs.RegOpenKeyExA[1][0].name === types.HKEY.name) {
// this is redefition for the library only specifying
// a different key type
break;
}
assert(funcs.RegQueryValueExA.constructor === Array);
assert(funcs.RegCreateKeyExA.constructor === Array);
assert(funcs.RegDeleteTreeA.constructor === Array);
assert(funcs.RegCloseKey.constructor === Array);
assert(funcs.RegSetValueExA.constructor === Array);
assert(typeof funcs === 'object');
break;
case 'Shell32':
// TODO place asserts here
break;
default:
throw 'Please add asserts for this new library file';
}
return advApi;
}
};
Loading

0 comments on commit 6921cd6

Please sign in to comment.