import cockpit from "cockpit";
import QUnit from "qunit-tests";
function MockPeer() {
* Events triggered here:
* open(event, args)
* recv(event, payload)
* close(event, problem)
* get(event, path)
*/
cockpit.event_target(this);
this.onopened = function(event, channel, options) {
};
this.onclosed = function(event, channel, options) {
};
this.oncontrol = function(event, channel, options) {
};
this.send = function(channel, payload) {
if (typeof (payload) != "string")
payload = String(payload);
window.setTimeout(function() {
if (channel.valid)
channel.dispatchEvent("message", payload);
else
console.log("dropping message after close from MockPeer");
}, 5);
};
this.close = function(channel, options) {
console.assert(channel);
window.setTimeout(function() {
if (channel.valid) {
channel.valid = false;
channel.dispatchEvent("close", options || { });
}
}, 5);
};
const peer = this;
let last_channel = 0;
function MockChannel(options) {
cockpit.event_target(this);
this.number = last_channel++;
this.options = options;
this.valid = true;
const channel = this;
function Transport() {
this.close = function(problem) { console.assert(arguments.length === 1) };
}
this.transport = new Transport();
this.send = function(payload) {
console.assert(arguments.length === 1);
console.assert(this.valid);
window.setTimeout(function() { peer.dispatchEvent("recv", channel, payload) }, 5);
};
this.control = function(options) {
console.assert(typeof options.command === 'string');
console.assert(options !== null && typeof options === 'object');
console.assert(arguments.length === 1);
window.setTimeout(function() { peer.dispatchEvent("control", channel, options) }, 5);
};
this.close = function(options) {
console.assert(arguments.length <= 1);
this.valid = false;
window.setTimeout(function() { peer.dispatchEvent("closed", channel, options || { }) }, 5);
this.dispatchEvent("close", options || { });
};
this.buffer = function(callback) {
const buffers = [];
buffers.callback = callback;
buffers.squash = function squash() {
return buffers.join("");
};
this.onmessage = function(event, data) {
buffers.push(data);
if (buffers.callback) {
const block = buffers.squash();
if (block.length > 0) {
const consumed = buffers.callback.call(this, block);
if (typeof consumed !== "number" || consumed === block.length) {
buffers.length = 0;
} else {
buffers.length = 1;
buffers[0] = block.substring(consumed);
}
}
}
};
return buffers;
};
QUnit.testDone(function() {
channel.valid = false;
});
peer.dispatchEvent("opened", channel, options);
}
cockpit.channel = function(options) {
return new MockChannel(options);
};
}
QUnit.test("public api", function (assert) {
assert.equal(typeof cockpit.spawn, "function", "spawn is a function");
});
QUnit.test("simple request", async assert => {
const peer = new MockPeer();
peer.addEventListener("opened", function(event, channel, options) {
assert.deepEqual(channel.options.spawn, ["/the/path", "arg1", "arg2"], "passed spawn correctly");
assert.equal(channel.options.host, undefined, "had no host");
});
peer.addEventListener("recv", function(event, channel, payload) {
assert.equal(payload, "input", "had input");
this.send(channel, "output");
this.close(channel);
});
const resp = await cockpit.spawn(["/the/path", "arg1", "arg2"])
.input("input", true);
assert.deepEqual(resp, "output", "returned right json");
});
QUnit.test("input large", function (assert) {
const done = assert.async();
assert.expect(25);
const str = new Array(128 * 1024).join('abcdef12345');
let output = "";
let count = 0;
const peer = new MockPeer();
peer.addEventListener("recv", function(event, channel, payload) {
assert.ok(typeof (payload) === "string", "got payload");
output += payload;
count += 1;
});
peer.addEventListener("control", function(event, channel, options) {
if (options.command === "done")
this.close(channel);
});
cockpit.spawn(["/path/to/command"])
.input(str)
.always(function() {
assert.equal(this.state(), "resolved", "didn't fail");
assert.equal(str, output, "right output");
assert.ok(count > 1, "broken into multiple blocks");
done();
});
});
QUnit.test("binary large", function (assert) {
const done = assert.async();
assert.expect(10);
const data = new Uint8Array(249 * 1023);
const len = data.byteLength;
for (let i = 0; i < len; i++)
data[i] = i % 233;
let count = 0;
const peer = new MockPeer();
peer.addEventListener("recv", function(event, channel, payload) {
console.log(typeof (payload), payload.constructor);
assert.equal(typeof (payload), "object", "got payload");
assert.equal(payload.constructor, Uint8Array, "right binary array");
count += 1;
});
peer.addEventListener("control", function(event, channel, options) {
console.log("control", options);
if (options.command === "done")
this.close(channel);
});
cockpit.spawn(["/ptah/to/command"])
.input(data)
.always(function() {
assert.equal(this.state(), "resolved", "didn't fail");
assert.ok(count > 1, "broken into multiple blocks");
done();
});
});
QUnit.test("string command", function (assert) {
const done = assert.async();
assert.expect(2);
const peer = new MockPeer();
peer.addEventListener("opened", function(event, channel, options) {
assert.deepEqual(channel.options.spawn, ["/the/path"], "passed spawn correctly");
assert.equal(channel.options.host, "hostname", "had host");
done();
});
cockpit.spawn("/the/path", { host: "hostname" });
});
QUnit.test("channel options", function (assert) {
const done = assert.async();
assert.expect(1);
const peer = new MockPeer();
peer.addEventListener("opened", function(event, channel) {
assert.deepEqual(channel.options, {
spawn: ["/the/path", "arg"],
host: "the-other-host.example.com",
"extra-option": "zerogjuggs",
payload: "stream"
}, "sent correctly");
done();
});
const options = { "extra-option": "zerogjuggs", host: "the-other-host.example.com" };
cockpit.spawn(["/the/path", "arg"], options);
});
QUnit.test("streaming", assert => {
const done = assert.async();
assert.expect(15);
const peer = new MockPeer();
peer.addEventListener("opened", function(event, channel) {
for (let i = 0; i < 10; i++)
this.send(channel, String(i));
this.close(channel);
});
let at = 0;
const promise = cockpit.spawn(["/unused"])
.stream(function(resp) {
assert.equal(String(at), resp, "stream got right data");
if (at === 0)
assert.strictEqual(this, promise, "stream got right this");
at++;
})
.done(function(resp) {
assert.ok(!resp, "stream didn't send data to done");
assert.strictEqual(this, promise, "done got right this");
})
.always(function() {
assert.equal(this.state(), "resolved", "split response didn't fail");
assert.strictEqual(this, promise, "always got right this");
done();
});
});
QUnit.test("with problem", async assert => {
const peer = new MockPeer();
peer.addEventListener("opened", (_event, channel) => {
peer.close(channel, { problem: "not-found" });
});
try {
await cockpit.spawn("/unused");
assert.ok(false, "should not be reached");
} catch (ex) {
assert.equal(ex.problem, "not-found", "got problem");
assert.strictEqual(ex.exit_signal, null, "got no signal");
assert.strictEqual(ex.exit_status, null, "got no status");
}
});
QUnit.test("with status", function (assert) {
const done = assert.async();
assert.expect(5);
const peer = new MockPeer();
peer.addEventListener("opened", function(event, channel) {
peer.send(channel, "the data");
peer.close(channel, { "exit-status": 5 });
});
cockpit.spawn("/unused")
.fail(function(ex, data) {
assert.strictEqual(ex.problem, null, "got null problem");
assert.strictEqual(ex.exit_signal, null, "got no signal");
assert.strictEqual(ex.exit_status, 5, "got status");
assert.equal(data, "the data", "got data even with exit status");
})
.always(function() {
assert.equal(this.state(), "rejected", "should fail");
done();
});
});
QUnit.test("with signal", function (assert) {
const done = assert.async();
assert.expect(5);
const peer = new MockPeer();
peer.addEventListener("opened", function(event, channel) {
peer.send(channel, "signal data here");
peer.close(channel, { "exit-signal": "TERM" });
});
cockpit.spawn("/unused")
.fail(function(ex, data) {
assert.strictEqual(ex.problem, null, "got null problem");
assert.strictEqual(ex.exit_signal, "TERM", "got signal");
assert.strictEqual(ex.exit_status, null, "got no status");
assert.equal(data, "signal data here", "got data even with signal");
})
.always(function() {
assert.equal(this.state(), "rejected", "should fail");
done();
});
});
QUnit.test("spawn promise recursive", function (assert) {
assert.expect(7);
const promise = cockpit.spawn(["/the/path", "arg1", "arg2"]);
const target = { };
const promise2 = promise.promise(target);
assert.strictEqual(promise2, target, "used target");
assert.equal(typeof promise2.done, "function", "promise2.done()");
assert.equal(typeof promise2.promise, "function", "promise2.promise()");
assert.equal(typeof promise2.input, "function", "promise2.input()");
const promise3 = promise2.promise();
assert.equal(typeof promise3.done, "function", "promise3.done()");
assert.equal(typeof promise3.promise, "function", "promise3.promise()");
assert.equal(typeof promise3.input, "function", "promise3.input()");
});
QUnit.start();