214 lines
5.8 KiB
TypeScript
214 lines
5.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { readFileSync } from "node:fs";
|
|
import {
|
|
buildGroupedObjectTreeNodes,
|
|
buildTableTreeNodes,
|
|
expandCachedObjectBrowserNodes,
|
|
objectGroupRefreshParentId,
|
|
} from "../../apps/desktop/src/lib/tableTree.ts";
|
|
import type { ObjectInfo, TableInfo } from "../../apps/desktop/src/types/database.ts";
|
|
|
|
const treeItemSource = readFileSync("apps/desktop/src/components/sidebar/TreeItem.vue", "utf8");
|
|
const connectionStoreSource = readFileSync("apps/desktop/src/stores/connectionStore.ts", "utf8");
|
|
|
|
function table(name: string, tableType: "TABLE" | "VIEW" = "TABLE"): TableInfo {
|
|
return { name, table_type: tableType };
|
|
}
|
|
|
|
function obj(name: string, objectType = "TABLE", schema = "public"): ObjectInfo {
|
|
return {
|
|
name,
|
|
object_type: objectType,
|
|
schema,
|
|
};
|
|
}
|
|
|
|
test("keeps every table as a sidebar node instead of truncating to object browser", () => {
|
|
const tables: TableInfo[] = Array.from({ length: 16 }, (_, index) => table(`table_${index + 1}`));
|
|
|
|
const nodes = buildTableTreeNodes({
|
|
nodeId: "conn:db",
|
|
connectionId: "conn",
|
|
database: "db",
|
|
tables,
|
|
});
|
|
|
|
assert.equal(nodes.length, 16);
|
|
assert.equal(nodes.at(-1)?.label, "table_16");
|
|
assert.equal(
|
|
nodes.some((node) => node.type === "object-browser"),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("preserves table and view node types", () => {
|
|
const nodes = buildTableTreeNodes({
|
|
nodeId: "conn:db:public",
|
|
connectionId: "conn",
|
|
database: "db",
|
|
schema: "public",
|
|
tables: [table("users"), table("user_view", "VIEW")],
|
|
});
|
|
|
|
assert.deepEqual(
|
|
nodes.map((node) => [node.label, node.type, node.schema]),
|
|
[
|
|
["users", "table", "public"],
|
|
["user_view", "view", "public"],
|
|
],
|
|
);
|
|
});
|
|
|
|
test("normalizes padded table names from database drivers", () => {
|
|
const nodes = buildTableTreeNodes({
|
|
nodeId: "conn:db:public",
|
|
connectionId: "conn",
|
|
database: "db",
|
|
schema: "public",
|
|
tables: [table(" users "), table("\norders\t"), table(" ")],
|
|
});
|
|
|
|
assert.deepEqual(
|
|
nodes.map((node) => [node.id, node.label]),
|
|
[
|
|
["conn:db:public:users", "users"],
|
|
["conn:db:public:orders", "orders"],
|
|
],
|
|
);
|
|
});
|
|
|
|
test("object tree groups count unique objects when metadata returns duplicates", () => {
|
|
const nodes = buildGroupedObjectTreeNodes({
|
|
nodeId: "conn:app:public",
|
|
connectionId: "conn",
|
|
database: "app",
|
|
schema: "public",
|
|
objects: [
|
|
obj("orders"),
|
|
obj("orders"),
|
|
obj("customers"),
|
|
obj("active_orders", "VIEW"),
|
|
obj("active_orders", "VIEW"),
|
|
],
|
|
});
|
|
|
|
const tableGroup = nodes.find((node) => node.type === "group-tables");
|
|
assert.equal(tableGroup?.objectCount, 2);
|
|
assert.deepEqual(
|
|
tableGroup?.children?.map((child) => child.label),
|
|
["orders", "customers"],
|
|
);
|
|
|
|
const viewGroup = nodes.find((node) => node.type === "group-views");
|
|
assert.equal(viewGroup?.objectCount, 1);
|
|
assert.deepEqual(
|
|
viewGroup?.children?.map((child) => child.label),
|
|
["active_orders"],
|
|
);
|
|
});
|
|
|
|
test("object tree table nodes keep the schema returned by metadata", () => {
|
|
const nodes = buildGroupedObjectTreeNodes({
|
|
nodeId: "conn:app",
|
|
connectionId: "conn",
|
|
database: "app",
|
|
schema: "app",
|
|
objects: [obj("orders", "TABLE", "sales")],
|
|
});
|
|
|
|
const tableNode = nodes.find((node) => node.type === "group-tables")?.children?.[0];
|
|
|
|
assert.equal(tableNode?.schema, "sales");
|
|
});
|
|
|
|
test("expands cached object-browser nodes back into regular table nodes", () => {
|
|
const nodes = expandCachedObjectBrowserNodes([
|
|
{
|
|
id: "conn:db:table_1",
|
|
label: "table_1",
|
|
type: "table",
|
|
connectionId: "conn",
|
|
database: "db",
|
|
isExpanded: false,
|
|
children: [],
|
|
},
|
|
{
|
|
id: "conn:db:__object_browser",
|
|
label: "tree.objectBrowser",
|
|
type: "object-browser",
|
|
connectionId: "conn",
|
|
database: "db",
|
|
hiddenChildren: [
|
|
{
|
|
id: "conn:db:table_16",
|
|
label: "table_16",
|
|
type: "table",
|
|
connectionId: "conn",
|
|
database: "db",
|
|
isExpanded: false,
|
|
children: [],
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
|
|
assert.deepEqual(
|
|
nodes.map((node) => [node.label, node.type]),
|
|
[
|
|
["table_1", "table"],
|
|
["table_16", "table"],
|
|
],
|
|
);
|
|
});
|
|
|
|
test("resolves grouped object refreshes to the parent schema node", () => {
|
|
assert.equal(
|
|
objectGroupRefreshParentId({
|
|
id: "conn:db:public:__tables",
|
|
label: "tree.tables",
|
|
type: "group-tables",
|
|
connectionId: "conn",
|
|
database: "db",
|
|
schema: "public",
|
|
}),
|
|
"conn:db:public",
|
|
);
|
|
});
|
|
|
|
test("table expander loads groups by the actual tree node id", () => {
|
|
assert.match(
|
|
treeItemSource,
|
|
/loadTableGroups\(node\.connectionId,\s*node\.database,\s*node\.label,\s*node\.schema,\s*node\.id\)/,
|
|
);
|
|
});
|
|
|
|
test("view metadata groups only expose columns", () => {
|
|
assert.match(
|
|
connectionStoreSource,
|
|
/if \(node\.type === "table"\) \{[\s\S]*type: "group-indexes"[\s\S]*type: "group-fkeys"[\s\S]*type: "group-triggers"/,
|
|
);
|
|
});
|
|
|
|
test("table metadata group expanders load by their actual tree node ids", () => {
|
|
for (const [type, loader] of [
|
|
["group-columns", "loadColumns"],
|
|
["group-indexes", "loadIndexes"],
|
|
["group-fkeys", "loadForeignKeys"],
|
|
["group-triggers", "loadTriggers"],
|
|
]) {
|
|
assert.match(
|
|
treeItemSource,
|
|
new RegExp(
|
|
`node\\.type === "${type}"[\\s\\S]*connectionStore\\.${loader}\\(node\\.connectionId,\\s*node\\.database,\\s*node\\.tableName,\\s*node\\.schema,\\s*node\\.id\\)`,
|
|
),
|
|
);
|
|
assert.match(
|
|
connectionStoreSource,
|
|
new RegExp(
|
|
`node\\.type === "${type}"[\\s\\S]*await ${loader}\\(node\\.connectionId,\\s*node\\.database,\\s*node\\.tableName,\\s*node\\.schema,\\s*node\\.id\\)`,
|
|
),
|
|
);
|
|
}
|
|
});
|