gpii-windows
Version:
Components of the GPII personalization infrastructure for use on Microsoft's "Windows" ™
313 lines (255 loc) • 9.41 kB
JavaScript
/*
* Windows service handler tests
*
* Copyright 2017 Raising the Floor - International
*
* Licensed under the New BSD license. You may not use this file except in
* compliance with this License.
*
* The R&D leading to these results received funding from the
* Department of Education - Grant H421A150005 (GPII-APCP). However,
* these results do not necessarily represent the policy of the
* Department of Education, and you should not assume endorsement by the
* Federal Government.
*
* You may obtain a copy of the License at
* https://github.com/GPII/universal/blob/master/LICENSE.txt
*/
;
var fluid = require("gpii-universal"),
ffi = require("ffi-napi"),
net = require("net");
var jqUnit = fluid.require("node-jqunit");
var gpii = fluid.registerNamespace("gpii");
fluid.registerNamespace("gpii.tests.serviceHandler");
process.env.GPII_SERVICE_PIPE_DISABLED = true;
require("../index.js");
jqUnit.module("gpii.tests.serviceHandler");
gpii.tests.serviceHandler.kernel32 = ffi.Library("kernel32", {
// https://msdn.microsoft.com/library/ms687032
"WaitForSingleObject": [
"ulong", ["uint", "ulong"]
],
// https://msdn.microsoft.com/library/ms682396
"CreateEventW": [
"ulong", [ "int", "int", "int", "int" ]
]
});
jqUnit.asyncTest("serviceChallenge tests", function () {
var badHandles = [
null,
"",
0,
"0",
123,
"123",
"text"
];
// Try some invalid data. Nothing is expected to happen, it shouldn't crash.
badHandles.forEach(function (handle) {
fluid.log("serviceChallenge - trying: ", handle);
gpii.windows.service.serviceChallenge(handle);
});
// Try a real event handle.
var eventHandle = gpii.tests.serviceHandler.kernel32.CreateEventW(0, false, false, 0);
var timeout = 5000;
gpii.tests.serviceHandler.kernel32.WaitForSingleObject.async(eventHandle, timeout, function (err, result) {
if (err) {
jqUnit.fail(err);
} else {
var WAIT_OBJECT_0 = 0;
jqUnit.assertEquals("WaitForSingleObject should return WAIT_OBJECT_0", WAIT_OBJECT_0, result);
jqUnit.start();
}
});
gpii.windows.service.serviceChallenge(eventHandle);
});
jqUnit.asyncTest("connectToService failure tests", function () {
var tests = [
"aa",
123,
"a:",
":a",
"pipe:",
"pipe:a/b",
"pipe:a\\b",
"pipe:aa bb",
"pipe:aa/bb",
"pipe:aa\nbb",
"pipe:201-valid-characters-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
];
jqUnit.expect(tests.length * 3 + 4);
var serviceHandler = gpii.windows.service.serviceHandler({
listeners: {
// Don't auto-connect.
"onCreate.connectToService": "fluid.identity"
}
});
var runTest = function (testIndex) {
if (testIndex >= tests.length) {
jqUnit.start();
return;
}
var test = tests[testIndex];
process.env.GPII_SERVICE_PIPE = test;
var promise = serviceHandler.connectToService();
jqUnit.assertNotNull("connectToService must return non-null", promise);
jqUnit.assertTrue("connectToService must return a promise", fluid.isPromise(promise));
promise.then(function () {
jqUnit.fail("connectToService should have rejected");
}, function (e) {
jqUnit.assertUndefined("connectToService should reject with undefined", e);
runTest(testIndex + 1);
});
};
// Test a valid name, but doesn't exist.
process.env.GPII_SERVICE_PIPE = "pipe:test-not-exist" + Math.random().toString(36);
var promise = serviceHandler.connectToService();
jqUnit.assertNotNull("connectToService must return non-null", promise);
jqUnit.assertTrue("connectToService must return a promise", fluid.isPromise(promise));
promise.then(function () {
jqUnit.fail("connectToService should have rejected");
}, function (e) {
jqUnit.assertTrue("connectToService should reject with an error", e.isError);
jqUnit.assertEquals("connectToService should reject with ENOENT", "ENOENT", e.error && e.error.code);
runTest(0);
});
});
jqUnit.asyncTest("connectToService tests", function () {
var tests = [
{
data: "challenge:none\nOK\n",
expect: "resolve"
},
{
data: "challenge:1\nOK\n",
expect: "resolve"
},
{
data: "challenge:2\nOK\n",
expect: "resolve"
},
{
data: "challenge:A\nOK\n",
expect: "reject"
},
{
data: "challenge:1\nchallenge:2\nOK\n",
expect: "reject"
},
{
data: "stupid value\n",
expect: "reject"
},
{
data: "OK\n",
expect: "resolve"
},
{
data: null,
disconnect: true,
expect: "reject"
},
{
data: "challenge:1\n",
disconnect: true,
expect: "reject"
},
{
data: "challenge:none\n",
disconnect: true,
expect: "reject"
},
{
data: "challenge:none\nOK\n",
disconnect: true,
expect: "resolve"
}
];
var serviceHandler = gpii.windows.service.serviceHandler({
listeners: {
// Don't auto-connect.
"onCreate.connectToService": "fluid.identity"
},
reconnect: false
});
var pipeIdPrefix = "test-connectToService-" + Math.random().toString(36);
var runTest = function (testIndex) {
if (testIndex >= tests.length) {
jqUnit.start();
return;
}
var test = tests[testIndex];
var pipeId = pipeIdPrefix + testIndex;
var pipeName = serviceHandler.options.pipePrefix + pipeId;
var pipeServer = net.createServer();
var closed = 0;
pipeServer.on("error", jqUnit.fail);
pipeServer.listen(pipeName, function () {
process.env.GPII_SERVICE_PIPE = "pipe:" + pipeId;
var promise = serviceHandler.connectToService();
jqUnit.assertNotNull("connectToService must return non-null", promise);
jqUnit.assertTrue("connectToService must return a promise", fluid.isPromise(promise));
promise.then(function () {
jqUnit.assertEquals("connectToService must only resolve if expected", test.expect, "resolve");
runTest(testIndex + 1);
}, function () {
jqUnit.assertEquals("connectToService must only reject if expected", test.expect, "reject");
// Wait for the pipe to close.
gpii.windows.waitForCondition(function () {
return closed;
}, {
timeout: 5000,
dontReject: true
}).then(function (value) {
jqUnit.assertNotEquals("connectToService should have closed the pipe", "timeout", value);
runTest(testIndex + 1);
}, jqUnit.fail);
});
});
pipeServer.on("connection", function (pipe) {
fluid.log("pipeServer.connection");
pipe.on("data", function (data) {
fluid.log("pipeServer.data", data.toString());
});
pipe.on("close", function () {
closed = true;
});
if (test.data) {
fluid.log("Sending ", test.data.replace(/\n/g, "\\n"));
pipe.write(test.data);
}
if (test.disconnect) {
fluid.log("Disconnecting");
pipe.destroy();
}
});
};
runTest(0);
});
jqUnit.test("status request tests", function () {
var requestHandler = gpii.windows.service.requestHandler();
var result = requestHandler.status();
jqUnit.assertTrue("status should return an object", fluid.isPlainObject(result));
jqUnit.assertTrue("status should be 'running'", result.isRunning);
requestHandler.destroy();
});
jqUnit.test("shutdown request tests", function () {
var requestHandler = gpii.windows.service.requestHandler();
var sendMessageCalled = false;
// Mock the sendMessage, instead of sending the real shutdown message.
var sendMessageOrig = gpii.windows.messages.sendMessage;
gpii.windows.messages.sendMessage = function (window, msg) {
jqUnit.assertEquals("sendMessage should be called with the correct window", "gpii-message-window", window);
jqUnit.assertEquals("sendMessage should be called with the correct message",
gpii.windows.API_constants.WM_QUERYENDSESSION, msg);
sendMessageCalled = true;
};
try {
requestHandler.shutdown();
jqUnit.assertTrue("sendMessage should have been invoked", sendMessageCalled);
} finally {
gpii.windows.messages.sendMessage = sendMessageOrig;
}
});