Files
2026-07-13 12:25:07 +08:00

491 lines
20 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";
const _query = async (should_cache, table, config = {}, callback) => {
let cached_view;
if (should_cache) {
cached_view = await table.view(config);
}
const data_getter = async () => {
if (should_cache) {
return await cached_view.to_columns();
} else {
const view = await table.view(config);
const data = await view.to_columns();
await view.delete();
return data;
}
};
const v = await callback(data_getter);
if (cached_view) {
cached_view.delete();
}
return v;
};
const SCHEMA = {
str: "string",
int: "integer",
float: "float",
};
test.describe("Removes", () => {
for (const cond of [true, false]) {
const query = _query.bind(undefined, cond);
for (const idx of ["str", "int"]) {
const string_pkey = idx === "str";
test.describe(`View cached? ${cond}, index: ${idx}`, () => {
test("Output consistent with filter", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
await table.update(data);
await query(
table,
{
filter: [["str", "!=", "2"]],
},
async (getter) => {
const pkey = 3;
await table.remove([
string_pkey ? pkey.toString() : pkey,
]);
expect(await getter()).toEqual({
str: ["1", "4", "5", "6"],
int: [1, 4, 5, 6],
float: [0.5, 2, 2.5, 3],
});
},
);
await table.delete();
});
test("Output consistent with filter, empty string", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
data.str.push("");
data.int.push(7);
data.float.push(3.5);
await table.update(data);
await query(
table,
{
filter: [["str", "!=", "2"]],
},
async (getter) => {
const pkey = 3;
await table.remove([
string_pkey ? pkey.toString() : pkey,
]);
if (string_pkey) {
expect(await getter()).toEqual({
str: ["", "1", "4", "5", "6"],
int: [7, 1, 4, 5, 6],
float: [3.5, 0.5, 2, 2.5, 3],
});
} else {
expect(await getter()).toEqual({
str: ["1", "4", "5", "6", ""],
int: [1, 4, 5, 6, 7],
float: [0.5, 2, 2.5, 3, 3.5],
});
}
},
);
await table.delete();
});
test("Output consistent with filter, null", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
data.str.push(null);
data.int.push(7);
data.float.push(3.5);
await table.update(data);
await query(
table,
{
filter: [["str", "!=", "2"]],
},
async (getter) => {
const pkey = 3;
await table.remove([
string_pkey ? pkey.toString() : pkey,
]);
if (string_pkey) {
expect(await getter()).toEqual({
str: [null, "1", "4", "5", "6"],
int: [7, 1, 4, 5, 6],
float: [3.5, 0.5, 2, 2.5, 3],
});
} else {
expect(await getter()).toEqual({
str: ["1", "4", "5", "6", null],
int: [1, 4, 5, 6, 7],
float: [0.5, 2, 2.5, 3, 3.5],
});
}
},
);
await table.delete();
});
test("Output consistent with filter, remove before view construction, sorted", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
await table.update(data);
await table.remove(
[1, 3, 5].map((x) => (string_pkey ? x.toString() : x)),
);
await query(
table,
{
sort: [["str", "desc"]],
},
async (getter) => {
expect(await getter()).toEqual({
str: ["6", "4", "2"],
int: [6, 4, 2],
float: [3, 2, 1],
});
await table.update({
str: ["7", "9", "8"],
int: [7, 9, 8],
float: [3.5, 4.5, 4],
});
expect(await getter()).toEqual({
str: ["9", "8", "7", "6", "4", "2"],
int: [9, 8, 7, 6, 4, 2],
float: [4.5, 4, 3.5, 3, 2, 1],
});
},
);
await table.delete();
});
test("Output consistent with filter, remove and add identical", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
await table.update(data);
await query(
table,
{
filter: [["str", "!=", "2"]],
},
async (getter) => {
const pkey = 3;
await table.remove([
string_pkey ? pkey.toString() : pkey,
]);
expect(await getter()).toEqual({
str: ["1", "4", "5", "6"],
int: [1, 4, 5, 6],
float: [0.5, 2, 2.5, 3],
});
await table.update({
str: ["7", "9", "8"],
int: [7, 9, 8],
float: [3.5, 4.5, 4],
});
expect(await getter()).toEqual({
str: ["1", "4", "5", "6", "7", "8", "9"],
int: [1, 4, 5, 6, 7, 8, 9],
float: [0.5, 2, 2.5, 3, 3.5, 4, 4.5],
});
},
);
await table.delete();
});
test("Output consistent with filter, add null and remove other", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
await table.update(data);
await query(table, {}, async (getter) => {
expect(await getter()).toEqual(data);
await table.update({
str: [string_pkey ? null : "7"],
int: [string_pkey ? 7 : null],
float: [3.5],
});
if (string_pkey) {
expect(await getter()).toEqual({
str: [null, 1, 2, 3, 4, 5, 6].map((x) =>
x ? x.toString() : x,
),
int: [7, 1, 2, 3, 4, 5, 6],
float: [7, 1, 2, 3, 4, 5, 6].map(
(x) => x * 0.5,
),
});
} else {
expect(await getter()).toEqual({
str: [7, 1, 2, 3, 4, 5, 6].map((x) =>
x.toString(),
),
int: [null, 1, 2, 3, 4, 5, 6],
float: [7, 1, 2, 3, 4, 5, 6].map(
(x) => x * 0.5,
),
});
}
await table.remove([string_pkey ? "3" : 3]);
if (string_pkey) {
expect(await getter()).toEqual({
str: [null, 1, 2, 4, 5, 6].map((x) =>
x ? x.toString() : x,
),
int: [7, 1, 2, 4, 5, 6],
float: [7, 1, 2, 4, 5, 6].map((x) => x * 0.5),
});
} else {
expect(await getter()).toEqual({
str: [7, 1, 2, 4, 5, 6].map((x) =>
x.toString(),
),
int: [null, 1, 2, 4, 5, 6],
float: [7, 1, 2, 4, 5, 6].map((x) => x * 0.5),
});
}
});
await table.delete();
});
test("Output consistent with filter, remove and add new dataset", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
await table.update(data);
await query(
table,
{
filter: [["int", "!=", 3]],
},
async (getter) => {
await table.remove(
[1, 2, 3, 4, 5, 6].map((x) =>
string_pkey ? x.toString() : x,
),
);
expect(await getter()).toEqual({
float: [],
int: [],
str: [],
});
await table.update({
str: ["def", "abc", "deff"],
int: [1, 2, 4],
float: [100.5, 200.5, 300.5],
});
if (string_pkey) {
expect(await getter()).toEqual({
str: ["abc", "def", "deff"],
int: [2, 1, 4],
float: [200.5, 100.5, 300.5],
});
} else {
expect(await getter()).toEqual({
str: ["def", "abc", "deff"],
int: [1, 2, 4],
float: [100.5, 200.5, 300.5],
});
}
},
);
await table.delete();
});
test.skip("Output consistent with filter, remove and add null", async () => {
// FIXME
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
const splice_idx = Math.floor(Math.random() * 6);
data.str.splice(splice_idx, 0, string_pkey ? null : "7");
data.int.splice(splice_idx, 0, string_pkey ? 7 : null);
data.float.splice(splice_idx, 0, 3.5);
await table.update(data);
await query(
table,
{
filter: [["float", "==", 3.5]],
},
async (getter) => {
await table.remove([null]);
const v = await table.view();
console.log(
"str?",
string_pkey,
await v.to_columns(),
);
await v.delete();
expect(await getter()).toEqual({});
await table.update({
str: [string_pkey ? null : "7"],
int: [string_pkey ? 7 : null],
float: [3.5],
});
expect(await getter()).toEqual({
str: [string_pkey ? null : "7"],
int: [string_pkey ? 7 : null],
float: [3.5],
});
},
);
await table.delete();
});
test.skip("Output consistent with filter, update null", async () => {
const table = await perspective.table(SCHEMA, {
index: idx,
});
const data = {
str: [1, 2, 3, 4, 5, 6].map((x) => x.toString()),
int: [1, 2, 3, 4, 5, 6],
float: [1, 2, 3, 4, 5, 6].map((x) => x * 0.5),
};
const splice_idx = Math.floor(Math.random() * 6);
data.str.splice(splice_idx, 0, string_pkey ? null : "7");
data.int.splice(splice_idx, 0, string_pkey ? 7 : null);
data.float.splice(splice_idx, 0, 3.5);
await table.update(data);
await query(table, {}, async (getter) => {
const expected = {
str: data.str.slice(),
int: data.int.slice(),
float: data.float.slice(),
};
expect(await getter()).toEqual(expected);
await table.update([
{
str: string_pkey ? null : "7",
int: string_pkey ? 7 : null,
float: 4.5,
},
]);
expected.float[splice_idx] = 4.5;
expect(await getter()).toEqual(expected);
});
await table.delete();
});
});
}
}
});