263 строки
8.5 KiB
TypeScript
263 строки
8.5 KiB
TypeScript
declare function afterEach(teardownFunc: Function): void;
|
|
declare function beforeEach(setupFunc: Function): void;
|
|
declare function describe(what: string, body: Function): void;
|
|
declare function it(should: string, testFunction: (done?: Function) => void): void;
|
|
declare var mocha: any;
|
|
|
|
import assert = require("assert");
|
|
import cp = require("child_process");
|
|
import fs = require("fs");
|
|
import net = require("net");
|
|
|
|
import adapter = require("../src/adapter");
|
|
|
|
class MockObject {
|
|
onceHandlers: { eventName: string; callback: Function; }[] = [];
|
|
onHandlers: { eventName: string; callback: Function; }[] = [];
|
|
|
|
on(eventName: string, callback: Function) {
|
|
this.onHandlers.push({ eventName: eventName, callback: callback });
|
|
}
|
|
|
|
once(eventName: string, callback: Function) {
|
|
this.onceHandlers.push({ eventName: eventName, callback: callback });
|
|
}
|
|
|
|
removeAllListeners(eventName: string) {
|
|
for (var i = this.onceHandlers.length - 1; i >= 0; i--) {
|
|
var handler = this.onceHandlers[i];
|
|
if (handler.eventName === eventName) {
|
|
this.onceHandlers.splice(i, 1);
|
|
}
|
|
}
|
|
for (var i = this.onHandlers.length - 1; i >= 0; i--) {
|
|
var handler = this.onHandlers[i];
|
|
if (handler.eventName === eventName) {
|
|
this.onHandlers.splice(i, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
_dispatch(eventName: string, eventObj: any) {
|
|
this.onHandlers.forEach(handler => {
|
|
if (handler.eventName !== eventName) {
|
|
return;
|
|
}
|
|
handler.callback(eventObj);
|
|
});
|
|
|
|
for (var i = this.onceHandlers.length - 1; i >= 0; i--) {
|
|
var handler = this.onceHandlers[i];
|
|
if (handler.eventName !== eventName) {
|
|
continue;
|
|
}
|
|
handler.callback(eventObj);
|
|
this.onceHandlers.splice(i, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
class MockSocket extends MockObject {
|
|
private static _kidnapper: (socket: MockSocket) => void = null;
|
|
|
|
destroyed = false;
|
|
|
|
constructor() {
|
|
super();
|
|
if (MockSocket._kidnapper) {
|
|
var kidnapper = MockSocket._kidnapper;
|
|
MockSocket._kidnapper = null;
|
|
kidnapper(this);
|
|
}
|
|
}
|
|
|
|
connect(port: number, host: string, callback: Function) {
|
|
}
|
|
|
|
destroy() {
|
|
this.destroyed = true;
|
|
}
|
|
|
|
write(buffer: Buffer) {
|
|
return true;
|
|
}
|
|
|
|
static kidnapNextInstance(kidnapper: (socket: MockSocket) => void) {
|
|
MockSocket._kidnapper = kidnapper;
|
|
}
|
|
}
|
|
|
|
class SuccessMockSocket extends MockSocket {
|
|
connect(port: number, host: string, callback: Function) {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
class FailMockSocket extends MockSocket {
|
|
connect(port: number, host: string, callback: Function) {
|
|
this._dispatch("error", {});
|
|
}
|
|
}
|
|
|
|
function makeCache(hostname = "1.2.3.4") {
|
|
fs.writeFileSync(adapter._sessionFilePath, hostname, "utf-8");
|
|
}
|
|
|
|
function monkeyPatch(obj: any, funcName: string, callback: Function, dontRestoreAfterFirstCall: boolean) {
|
|
var orig = obj[funcName];
|
|
|
|
var patch = function () {
|
|
var result = callback.apply(obj, arguments);
|
|
if (!dontRestoreAfterFirstCall) {
|
|
(<any>patch).restore();
|
|
}
|
|
return result;
|
|
};
|
|
(<any>patch).restore = function () {
|
|
obj[funcName] = orig;
|
|
};
|
|
(<any>patch).orig = orig;
|
|
obj[funcName] = patch;
|
|
}
|
|
|
|
var _socket = net.Socket;
|
|
|
|
function setup() {
|
|
// Zipping sums up to a significant amount of time over several
|
|
// tests, we will mock it by default and have tests opt-out by
|
|
// calling .restore() on _compressPath if necessary
|
|
monkeyPatch(adapter, "_compressPath", function () {
|
|
return new Buffer("testdata");
|
|
}, true);
|
|
|
|
// execSync is used to launch remote desktop which we don't want
|
|
// to happen during tests. We will no-op it by default and have
|
|
// tests opt-out if necessary
|
|
monkeyPatch(cp, "execSync", function () { }, true);
|
|
|
|
// Disable retries, tests that are interested in testing the retry
|
|
// logic will opt-out
|
|
adapter._numRetries = 0;
|
|
|
|
// Delete cache file
|
|
if (fs.existsSync(adapter._sessionFilePath)) {
|
|
fs.unlinkSync(adapter._sessionFilePath);
|
|
}
|
|
}
|
|
|
|
function teardown() {
|
|
// Restore socket class in case it was mocked
|
|
net.Socket = _socket;
|
|
|
|
// Restore execSync
|
|
(<any>cp).execSync.restore();
|
|
|
|
// Restore any direct APIs on the Adapter
|
|
Object.keys(adapter).forEach(key => {
|
|
var member = (<any>adapter)[key];
|
|
member.restore && member.restore();
|
|
});
|
|
|
|
// Delete cache file
|
|
if (fs.existsSync(adapter._sessionFilePath)) {
|
|
fs.unlinkSync(adapter._sessionFilePath);
|
|
}
|
|
}
|
|
|
|
describe("Adapter", function () {
|
|
beforeEach(setup);
|
|
afterEach(teardown);
|
|
|
|
describe("registerAndLaunchAppxManifest", function () {
|
|
|
|
describe("RDP scenarios", function () {
|
|
it("should launch rdp when no cache file is found", function (done) {
|
|
net.Socket = <any>SuccessMockSocket;
|
|
monkeyPatch(cp, "execSync", function (cmd: string) {
|
|
if (cmd.indexOf(".rdp") === -1) {
|
|
assert.fail();
|
|
}
|
|
done();
|
|
}, false);
|
|
adapter.registerAndLaunchAppxManifest("test");
|
|
});
|
|
|
|
it("should launch rdp when connecting to cached hostname fails", function (done) {
|
|
makeCache();
|
|
net.Socket = <any>FailMockSocket;
|
|
monkeyPatch(cp, "execSync", function (cmd: string) {
|
|
if (cmd.indexOf(".rdp") === -1) {
|
|
assert.fail();
|
|
}
|
|
done();
|
|
}, false);
|
|
adapter.registerAndLaunchAppxManifest("test");
|
|
});
|
|
|
|
it("should not launch rdp when connecting to cached hostname succeeds", function (done) {
|
|
makeCache();
|
|
net.Socket = <any>SuccessMockSocket;
|
|
monkeyPatch(cp, "execSync", function (cmd: string) {
|
|
assert.fail();
|
|
}, false);
|
|
|
|
MockSocket.kidnapNextInstance(socket => {
|
|
socket.write = function (buffer: Buffer) {
|
|
done();
|
|
return true;
|
|
};
|
|
});
|
|
adapter.registerAndLaunchAppxManifest("test");
|
|
|
|
(<any>cp.execSync).restore();
|
|
});
|
|
});
|
|
|
|
describe("Caching scenarios", function () {
|
|
it("should connect to cached hostname if cache file exists", function (done) {
|
|
var testHostname = "testHostname";
|
|
makeCache(testHostname);
|
|
net.Socket = <any>SuccessMockSocket;
|
|
|
|
MockSocket.kidnapNextInstance(socket => {
|
|
socket.connect = function (port: number, host: string, callback: Function) {
|
|
assert.equal(testHostname, host);
|
|
done();
|
|
};
|
|
});
|
|
adapter.registerAndLaunchAppxManifest("test");
|
|
});
|
|
|
|
it("should create cache file if connection was successful", function (done) {
|
|
net.Socket = <any>SuccessMockSocket;
|
|
|
|
MockSocket.kidnapNextInstance(socket => {
|
|
socket.write = function (buffer: Buffer) {
|
|
assert.ok(fs.existsSync(adapter._sessionFilePath));
|
|
done();
|
|
return true;
|
|
};
|
|
});
|
|
adapter.registerAndLaunchAppxManifest("test");
|
|
});
|
|
|
|
it("should delete cache file if connection failed", function (done) {
|
|
net.Socket = <any>FailMockSocket;
|
|
makeCache();
|
|
|
|
MockSocket.kidnapNextInstance(socket => {
|
|
// This is the fail socket, after which we expect the adapter
|
|
// to start over and create another socket
|
|
assert.ok(fs.existsSync(adapter._sessionFilePath));
|
|
MockSocket.kidnapNextInstance(socket => {
|
|
// This is the retry attempt, at this point the cache file
|
|
// should've been deleted
|
|
assert.ok(!fs.existsSync(adapter._sessionFilePath));
|
|
done();
|
|
});
|
|
});
|
|
adapter.registerAndLaunchAppxManifest("test");
|
|
});
|
|
});
|
|
});
|
|
}); |