840 lines
32 KiB
JavaScript
840 lines
32 KiB
JavaScript
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
|
|
import { test, expect } from "@perspective-dev/test";
|
|
import perspective from "./perspective_client";
|
|
import _ from "lodash";
|
|
|
|
const data = {
|
|
w: [1.5, 2.5, 3.5, 4.5],
|
|
x: [1, 2, 3, 4],
|
|
y: ["a", "b", "c", "d"],
|
|
z: [true, false, true, false],
|
|
};
|
|
|
|
const get_random_int = function (min, max) {
|
|
// liberally copied from stack
|
|
min = Math.ceil(min);
|
|
max = Math.floor(max);
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
};
|
|
|
|
function it_old_behavior(name, capture) {
|
|
test(name, async function () {
|
|
let done;
|
|
let result = new Promise((x) => {
|
|
done = x;
|
|
});
|
|
|
|
await capture(done);
|
|
await result;
|
|
});
|
|
}
|
|
|
|
((perspective) => {
|
|
test.describe("ports", function () {
|
|
test("Should create port IDs in incremental order", async function () {
|
|
const table = await perspective.table(data);
|
|
const port_ids = [];
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
table.delete();
|
|
});
|
|
|
|
test("Should create port IDs in incremental order and allow updates on each port", async function () {
|
|
const table = await perspective.table(data);
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
for (const port_id of port_ids) {
|
|
await table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [port_id],
|
|
y: ["d"],
|
|
z: [true],
|
|
},
|
|
{ port_id },
|
|
);
|
|
}
|
|
|
|
const view = await table.view();
|
|
const output = await view.to_columns();
|
|
|
|
expect(await table.size()).toEqual(14);
|
|
|
|
const expected = {
|
|
w: [
|
|
1.5, 2.5, 3.5, 4.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5,
|
|
1.5, 1.5,
|
|
],
|
|
x: [1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
|
y: [
|
|
"a",
|
|
"b",
|
|
"c",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
],
|
|
z: [
|
|
true,
|
|
false,
|
|
true,
|
|
false,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
],
|
|
};
|
|
|
|
expect(output).toEqual(expected);
|
|
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("Should create port IDs in incremental order and allow updates on each port, indexed", async function () {
|
|
const table = await perspective.table(data, { index: "w" });
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
for (const port_id of port_ids) {
|
|
await table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [1],
|
|
y: ["a"],
|
|
z: [true],
|
|
},
|
|
{ port_id },
|
|
);
|
|
}
|
|
|
|
expect(await table.size()).toEqual(4);
|
|
|
|
const view = await table.view();
|
|
const output = await view.to_columns();
|
|
expect(output).toEqual(data);
|
|
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("Should create port IDs in incremental order and allow random arbitrary updates on each port, indexed", async function () {
|
|
const table = await perspective.table(data, { index: "w" });
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
for (const port_id of port_ids) {
|
|
await table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [1],
|
|
y: ["a"],
|
|
z: [true],
|
|
},
|
|
{ port_id },
|
|
);
|
|
}
|
|
|
|
expect(await table.size()).toEqual(4);
|
|
|
|
const view = await table.view();
|
|
const output = await view.to_columns();
|
|
expect(output).toEqual(data);
|
|
|
|
// arbitarily update from a random port, force append with new pkey
|
|
const port = get_random_int(1, 9);
|
|
const update_data = {
|
|
w: [5.5],
|
|
x: [5],
|
|
y: ["e"],
|
|
z: [true],
|
|
};
|
|
|
|
await table.update(update_data, { port_id: 0 });
|
|
|
|
const output2 = await view.to_columns();
|
|
expect(output2).toEqual({
|
|
w: [1.5, 2.5, 3.5, 4.5, 5.5],
|
|
x: [1, 2, 3, 4, 5],
|
|
y: ["a", "b", "c", "d", "e"],
|
|
z: [true, false, true, false, true],
|
|
});
|
|
|
|
// and do it again but this time with null as pkey
|
|
let port2 = get_random_int(1, 9);
|
|
|
|
while (port2 === port) {
|
|
port2 = get_random_int(1, 9);
|
|
}
|
|
|
|
const update_data2 = {
|
|
w: [null],
|
|
x: [6],
|
|
y: ["f"],
|
|
z: [true],
|
|
};
|
|
|
|
await table.update(update_data2, { port_id: port2 });
|
|
const output3 = await view.to_columns();
|
|
expect(output3).toEqual({
|
|
w: [null, 1.5, 2.5, 3.5, 4.5, 5.5],
|
|
x: [6, 1, 2, 3, 4, 5],
|
|
y: ["f", "a", "b", "c", "d", "e"],
|
|
z: [true, true, false, true, false, true],
|
|
});
|
|
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test.describe("View notifications from different ports", function () {
|
|
test("All views should be notified by appends on all ports", async function () {
|
|
const table = await perspective.table(data);
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
const view = await table.view();
|
|
|
|
const view2 = await table.view({
|
|
group_by: ["w"],
|
|
});
|
|
|
|
const view3 = await table.view({
|
|
group_by: ["w"],
|
|
split_by: ["x"],
|
|
});
|
|
|
|
for (const port_id of port_ids) {
|
|
await table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [port_id],
|
|
y: ["d"],
|
|
z: [true],
|
|
},
|
|
{ port_id },
|
|
);
|
|
}
|
|
|
|
const output = await view.to_columns();
|
|
|
|
expect(await table.size()).toEqual(14);
|
|
|
|
const expected = {
|
|
w: [
|
|
1.5, 2.5, 3.5, 4.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5,
|
|
1.5, 1.5, 1.5,
|
|
],
|
|
x: [1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
|
y: [
|
|
"a",
|
|
"b",
|
|
"c",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
"d",
|
|
],
|
|
z: [
|
|
true,
|
|
false,
|
|
true,
|
|
false,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
],
|
|
};
|
|
|
|
expect(output).toEqual(expected);
|
|
|
|
const output2 = await view2.to_columns();
|
|
expect(output2).toEqual({
|
|
__ROW_PATH__: [[], [1.5], [2.5], [3.5], [4.5]],
|
|
w: [27, 16.5, 2.5, 3.5, 4.5],
|
|
x: [65, 56, 2, 3, 4],
|
|
y: [14, 11, 1, 1, 1],
|
|
z: [14, 11, 1, 1, 1],
|
|
});
|
|
|
|
const output3 = await view3.to_columns();
|
|
expect(output3).toEqual({
|
|
__ROW_PATH__: [[], [1.5], [2.5], [3.5], [4.5]],
|
|
"1|w": [3, 3, null, null, null],
|
|
"1|x": [2, 2, null, null, null],
|
|
"1|y": [2, 2, null, null, null],
|
|
"1|z": [2, 2, null, null, null],
|
|
"2|w": [4, 1.5, 2.5, null, null],
|
|
"2|x": [4, 2, 2, null, null],
|
|
"2|y": [2, 1, 1, null, null],
|
|
"2|z": [2, 1, 1, null, null],
|
|
"3|w": [5, 1.5, null, 3.5, null],
|
|
"3|x": [6, 3, null, 3, null],
|
|
"3|y": [2, 1, null, 1, null],
|
|
"3|z": [2, 1, null, 1, null],
|
|
"4|w": [6, 1.5, null, null, 4.5],
|
|
"4|x": [8, 4, null, null, 4],
|
|
"4|y": [2, 1, null, null, 1],
|
|
"4|z": [2, 1, null, null, 1],
|
|
"5|w": [1.5, 1.5, null, null, null],
|
|
"5|x": [5, 5, null, null, null],
|
|
"5|y": [1, 1, null, null, null],
|
|
"5|z": [1, 1, null, null, null],
|
|
"6|w": [1.5, 1.5, null, null, null],
|
|
"6|x": [6, 6, null, null, null],
|
|
"6|y": [1, 1, null, null, null],
|
|
"6|z": [1, 1, null, null, null],
|
|
"7|w": [1.5, 1.5, null, null, null],
|
|
"7|x": [7, 7, null, null, null],
|
|
"7|y": [1, 1, null, null, null],
|
|
"7|z": [1, 1, null, null, null],
|
|
"8|w": [1.5, 1.5, null, null, null],
|
|
"8|x": [8, 8, null, null, null],
|
|
"8|y": [1, 1, null, null, null],
|
|
"8|z": [1, 1, null, null, null],
|
|
"9|w": [1.5, 1.5, null, null, null],
|
|
"9|x": [9, 9, null, null, null],
|
|
"9|y": [1, 1, null, null, null],
|
|
"9|z": [1, 1, null, null, null],
|
|
"10|w": [1.5, 1.5, null, null, null],
|
|
"10|x": [10, 10, null, null, null],
|
|
"10|y": [1, 1, null, null, null],
|
|
"10|z": [1, 1, null, null, null],
|
|
});
|
|
|
|
view3.delete();
|
|
view2.delete();
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("All views should be notified by partial updates on all ports", async function () {
|
|
const table = await perspective.table(data, { index: "w" });
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
const view = await table.view();
|
|
|
|
const view2 = await table.view({
|
|
group_by: ["w"],
|
|
});
|
|
|
|
const view3 = await table.view({
|
|
group_by: ["w"],
|
|
split_by: ["x"],
|
|
});
|
|
|
|
for (const port_id of port_ids) {
|
|
await table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [port_id],
|
|
y: ["d"],
|
|
z: [false],
|
|
},
|
|
{ port_id },
|
|
);
|
|
}
|
|
|
|
const output = await view.to_columns();
|
|
|
|
expect(await table.size()).toEqual(4);
|
|
|
|
const expected = {
|
|
w: [1.5, 2.5, 3.5, 4.5],
|
|
x: [10, 2, 3, 4],
|
|
y: ["d", "b", "c", "d"],
|
|
z: [false, false, true, false],
|
|
};
|
|
|
|
expect(output).toEqual(expected);
|
|
|
|
const output2 = await view2.to_columns();
|
|
expect(output2).toEqual({
|
|
__ROW_PATH__: [[], [1.5], [2.5], [3.5], [4.5]],
|
|
w: [12, 1.5, 2.5, 3.5, 4.5],
|
|
x: [19, 10, 2, 3, 4],
|
|
y: [4, 1, 1, 1, 1],
|
|
z: [4, 1, 1, 1, 1],
|
|
});
|
|
|
|
const output3 = await view3.to_columns();
|
|
expect(output3).toEqual({
|
|
__ROW_PATH__: [[], [1.5], [2.5], [3.5], [4.5]],
|
|
"2|w": [2.5, null, 2.5, null, null],
|
|
"2|x": [2, null, 2, null, null],
|
|
"2|y": [1, null, 1, null, null],
|
|
"2|z": [1, null, 1, null, null],
|
|
"3|w": [3.5, null, null, 3.5, null],
|
|
"3|x": [3, null, null, 3, null],
|
|
"3|y": [1, null, null, 1, null],
|
|
"3|z": [1, null, null, 1, null],
|
|
"4|w": [4.5, null, null, null, 4.5],
|
|
"4|x": [4, null, null, null, 4],
|
|
"4|y": [1, null, null, null, 1],
|
|
"4|z": [1, null, null, null, 1],
|
|
"10|w": [1.5, 1.5, null, null, null],
|
|
"10|x": [10, 10, null, null, null],
|
|
"10|y": [1, 1, null, null, null],
|
|
"10|z": [1, 1, null, null, null],
|
|
});
|
|
|
|
view3.delete();
|
|
view2.delete();
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
});
|
|
|
|
test.describe("on_update notifications from different ports", function () {
|
|
it_old_behavior(
|
|
"All calls to on_update should contain the port ID",
|
|
async function (done) {
|
|
const table = await perspective.table(data);
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
const view = await table.view();
|
|
|
|
// because no updates were called in port 0, start at 1
|
|
let last_port_id = 1;
|
|
view.on_update(function (updated) {
|
|
expect(updated.port_id).toEqual(last_port_id);
|
|
if (last_port_id == 10) {
|
|
view.delete();
|
|
table.delete();
|
|
done();
|
|
} else {
|
|
last_port_id++;
|
|
}
|
|
});
|
|
|
|
for (const port_id of port_ids) {
|
|
await table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [1],
|
|
y: ["a"],
|
|
z: [true],
|
|
},
|
|
{ port_id },
|
|
);
|
|
}
|
|
},
|
|
);
|
|
|
|
it_old_behavior(
|
|
"Only the port that was updated should be notified in on_update",
|
|
async function (done) {
|
|
const table = await perspective.table(data);
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
const view = await table.view();
|
|
|
|
const port_id = get_random_int(1, 9);
|
|
|
|
view.on_update(function (updated) {
|
|
expect(updated.port_id).toEqual(port_id);
|
|
view.delete();
|
|
table.delete();
|
|
done();
|
|
});
|
|
|
|
await table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [1],
|
|
y: ["a"],
|
|
z: [true],
|
|
},
|
|
{ port_id },
|
|
);
|
|
},
|
|
);
|
|
|
|
it_old_behavior(
|
|
"All ports should be notified in creation order, regardless of what order the update is called.",
|
|
async function (done) {
|
|
const table = await perspective.table(data);
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
const view = await table.view();
|
|
|
|
let last_port_id = 0;
|
|
let num_updates = 0;
|
|
|
|
view.on_update(function (updated) {
|
|
expect(updated.port_id).toEqual(last_port_id);
|
|
if (num_updates === 10) {
|
|
view.delete();
|
|
table.delete();
|
|
done();
|
|
} else {
|
|
num_updates++;
|
|
last_port_id++;
|
|
}
|
|
});
|
|
|
|
const update_order = _.shuffle([
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
]);
|
|
|
|
for (const port_id of update_order) {
|
|
table.update(
|
|
{
|
|
w: [1.5],
|
|
x: [1],
|
|
y: ["a"],
|
|
z: [true],
|
|
},
|
|
{ port_id },
|
|
);
|
|
}
|
|
},
|
|
);
|
|
});
|
|
|
|
test.describe("deltas", function () {
|
|
it_old_behavior(
|
|
"Deltas should be unique to each port",
|
|
async function (done) {
|
|
const table = await perspective.table(data);
|
|
const port_ids = [];
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
port_ids.push(await table.make_port());
|
|
}
|
|
|
|
expect(port_ids).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
|
|
|
const view = await table.view();
|
|
|
|
let update_count = 0;
|
|
|
|
view.on_update(
|
|
async function (updated) {
|
|
const _t = await perspective.table(updated.delta);
|
|
const _v = await _t.view();
|
|
const result = await _v.to_columns();
|
|
|
|
if (updated.port_id == first_port) {
|
|
expect(result).toEqual({
|
|
w: [100],
|
|
x: [first_port],
|
|
y: ["first port"],
|
|
z: [false],
|
|
});
|
|
} else if (updated.port_id == second_port) {
|
|
expect(result).toEqual({
|
|
w: [200],
|
|
x: [second_port],
|
|
y: ["second port"],
|
|
z: [true],
|
|
});
|
|
}
|
|
|
|
update_count++;
|
|
|
|
if (update_count === 2) {
|
|
_v.delete();
|
|
_t.delete();
|
|
view.delete();
|
|
table.delete();
|
|
done();
|
|
}
|
|
},
|
|
{ mode: "row" },
|
|
);
|
|
|
|
const first_port = get_random_int(0, 10);
|
|
let second_port = get_random_int(0, 10);
|
|
|
|
while (second_port === first_port) {
|
|
second_port = get_random_int(0, 10);
|
|
}
|
|
|
|
await table.update(
|
|
{
|
|
w: [100],
|
|
x: [first_port],
|
|
y: ["first port"],
|
|
z: [false],
|
|
},
|
|
{ port_id: first_port },
|
|
);
|
|
|
|
await table.update(
|
|
{
|
|
w: [200],
|
|
x: [second_port],
|
|
y: ["second port"],
|
|
z: [true],
|
|
},
|
|
{ port_id: second_port },
|
|
);
|
|
},
|
|
);
|
|
});
|
|
|
|
test.describe("Cross-table port operations", function () {
|
|
it_old_behavior(
|
|
"Should allow a client-server round trip without loops",
|
|
async function (done) {
|
|
const client_table = await perspective.table(data);
|
|
const server_table = await perspective.table(data);
|
|
const client_port = await client_table.make_port();
|
|
|
|
// "get rid" of a random number of ports by creating them
|
|
for (let i = 0; i < get_random_int(5, 20); i++) {
|
|
await server_table.make_port();
|
|
}
|
|
|
|
const server_port = await server_table.make_port();
|
|
|
|
expect(client_port).toBeLessThan(server_port);
|
|
|
|
const client_view = await client_table.view();
|
|
|
|
let CLIENT_TO_SERVER_UPDATES = 0;
|
|
|
|
client_view.on_update(
|
|
async (updated) => {
|
|
if (updated.port_id === client_port) {
|
|
await server_table.update(updated.delta, {
|
|
port_id: server_port,
|
|
});
|
|
CLIENT_TO_SERVER_UPDATES++;
|
|
} else {
|
|
// Should not pass forward that update, and break
|
|
// the chain.
|
|
expect(CLIENT_TO_SERVER_UPDATES).toEqual(1);
|
|
}
|
|
},
|
|
{ mode: "row" },
|
|
);
|
|
|
|
const server_view = await server_table.view();
|
|
|
|
server_view.on_update(
|
|
async (updated) => {
|
|
if (updated.port_id !== server_port) {
|
|
await client_table.update(updated.delta);
|
|
} else {
|
|
// update is trapped in this state - assert correct
|
|
// state and exit the test.
|
|
expect(await client_table.size()).toEqual(8);
|
|
expect(await server_table.size()).toEqual(8);
|
|
client_view.delete();
|
|
server_view.delete();
|
|
client_table.delete();
|
|
server_table.delete();
|
|
done();
|
|
}
|
|
},
|
|
{ mode: "row" },
|
|
);
|
|
|
|
// this should go to the server and round trip back to the
|
|
// client, but stop when it gets to the client again.
|
|
await client_table.update(data, { port_id: client_port });
|
|
},
|
|
);
|
|
|
|
it_old_behavior(
|
|
"Should allow multi client-server round trips without loops",
|
|
async function (done) {
|
|
const client_table = await perspective.table(data, {
|
|
index: "w",
|
|
});
|
|
const client_table2 = await perspective.table(data, {
|
|
index: "w",
|
|
});
|
|
const server_table = await perspective.table(data, {
|
|
index: "w",
|
|
});
|
|
const client_port = await client_table.make_port();
|
|
const client_port2 = await client_table2.make_port();
|
|
|
|
// "get rid" of a random number of ports by creating them
|
|
for (let i = 0; i < get_random_int(5, 20); i++) {
|
|
await server_table.make_port();
|
|
}
|
|
|
|
const server_port = await server_table.make_port();
|
|
const server_port2 = await server_table.make_port();
|
|
|
|
expect(client_port).toBeLessThan(server_port);
|
|
expect(client_port2).toBeLessThan(server_port);
|
|
expect(client_port).toBeLessThan(server_port2);
|
|
expect(client_port2).toBeLessThan(server_port2);
|
|
|
|
// port numbers can be equal between clients
|
|
expect(client_port2).toEqual(client_port);
|
|
|
|
const client_view = await client_table.view();
|
|
const client_view2 = await client_table2.view();
|
|
|
|
client_view.on_update(
|
|
async (updated) => {
|
|
// client1 and server handlers are as minimal as can be
|
|
if (updated.port_id === client_port) {
|
|
await server_table.update(updated.delta, {
|
|
port_id: server_port,
|
|
});
|
|
}
|
|
},
|
|
{ mode: "row" },
|
|
);
|
|
|
|
client_view2.on_update(
|
|
async (updated) => {
|
|
// client2 only reads updates from other clients
|
|
const results = await client_view2.to_columns();
|
|
expect(results).toEqual({
|
|
w: [1.5, 2.5, 3.5, 4.5],
|
|
x: [1, 2, 300, 4],
|
|
y: ["a", "b", "ccc", "d"],
|
|
z: [true, false, true, false],
|
|
});
|
|
expect(await client_view.to_columns()).toEqual(
|
|
results,
|
|
);
|
|
expect(await server_view.to_columns()).toEqual(
|
|
results,
|
|
);
|
|
client_view.delete();
|
|
client_view2.delete();
|
|
server_view.delete();
|
|
client_table.delete();
|
|
client_table2.delete();
|
|
server_table.delete();
|
|
done();
|
|
},
|
|
{ mode: "row" },
|
|
);
|
|
|
|
const server_view = await server_table.view();
|
|
|
|
// Simulate multiple connections to the server
|
|
server_view.on_update(
|
|
async (updated) => {
|
|
if (updated.port_id !== server_port) {
|
|
await client_table.update(updated.delta);
|
|
}
|
|
},
|
|
{ mode: "row" },
|
|
);
|
|
|
|
server_view.on_update(
|
|
async (updated) => {
|
|
if (updated.port_id !== server_port2) {
|
|
await client_table2.update(updated.delta);
|
|
}
|
|
},
|
|
{ mode: "row" },
|
|
);
|
|
|
|
// this should go to the server and round trip back to the
|
|
// client, but stop when it gets to the client again. It should
|
|
// reflect on the client2 table though.
|
|
await client_table.update(
|
|
[
|
|
{
|
|
w: 3.5,
|
|
x: 300,
|
|
y: "ccc",
|
|
},
|
|
],
|
|
{ port_id: client_port },
|
|
);
|
|
},
|
|
);
|
|
});
|
|
});
|
|
})(perspective);
|