407 lines
11 KiB
TypeScript
Vendored
407 lines
11 KiB
TypeScript
Vendored
import { strict as assert } from "node:assert";
|
|
import { test } from "vitest";
|
|
import { restoreOpenTabsState, serializeOpenTabs } from "../../apps/desktop/src/lib/app/openTabsPersistence.ts";
|
|
import type { QueryTab } from "../../apps/desktop/src/types/database.ts";
|
|
|
|
function queryTab(overrides: Partial<QueryTab> = {}): QueryTab {
|
|
return {
|
|
id: "tab-1",
|
|
title: "Query 1",
|
|
connectionId: "conn-1",
|
|
database: "app",
|
|
schema: "public",
|
|
sql: "select * from users",
|
|
pinned: true,
|
|
isExecuting: false,
|
|
isCancelling: false,
|
|
isExplaining: false,
|
|
mode: "query",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test("serializes unsaved query tabs with editor context", () => {
|
|
const saved = serializeOpenTabs([queryTab()]);
|
|
|
|
assert.deepEqual(saved, [
|
|
{
|
|
id: "tab-1",
|
|
title: "Query 1",
|
|
connectionId: "conn-1",
|
|
database: "app",
|
|
schema: "public",
|
|
sql: "select * from users",
|
|
savedSqlId: undefined,
|
|
externalSqlPath: undefined,
|
|
pinned: true,
|
|
mode: "query",
|
|
objectBrowser: undefined,
|
|
objectSource: undefined,
|
|
tableMeta: undefined,
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("serializes object source query tabs with save context", () => {
|
|
const saved = serializeOpenTabs([
|
|
queryTab({
|
|
title: "fn_add",
|
|
customTitle: true,
|
|
objectSource: {
|
|
schema: "public",
|
|
name: "fn_add",
|
|
objectType: "FUNCTION",
|
|
},
|
|
}),
|
|
]);
|
|
|
|
assert.deepEqual(saved[0]?.objectSource, {
|
|
schema: "public",
|
|
name: "fn_add",
|
|
objectType: "FUNCTION",
|
|
});
|
|
assert.equal(saved[0]?.customTitle, true);
|
|
});
|
|
|
|
test("serializes table tabs with reload context", () => {
|
|
const saved = serializeOpenTabs([
|
|
queryTab({
|
|
mode: "data",
|
|
lastExecutedSql: "select * from users limit 100 offset 100",
|
|
resultPageLimit: 100,
|
|
resultPageOffset: 100,
|
|
whereInput: "active = true",
|
|
orderByInput: "created_at DESC",
|
|
tableMeta: { schema: "public", tableName: "users", columns: [], primaryKeys: [] },
|
|
}),
|
|
]);
|
|
|
|
assert.deepEqual(
|
|
{
|
|
mode: saved[0]?.mode,
|
|
lastExecutedSql: saved[0]?.lastExecutedSql,
|
|
resultPageLimit: saved[0]?.resultPageLimit,
|
|
resultPageOffset: saved[0]?.resultPageOffset,
|
|
whereInput: saved[0]?.whereInput,
|
|
orderByInput: saved[0]?.orderByInput,
|
|
tableMeta: saved[0]?.tableMeta,
|
|
},
|
|
{
|
|
mode: "data",
|
|
lastExecutedSql: "select * from users limit 100 offset 100",
|
|
resultPageLimit: 100,
|
|
resultPageOffset: 100,
|
|
whereInput: "active = true",
|
|
orderByInput: "created_at DESC",
|
|
tableMeta: { schema: "public", tableName: "users", columns: [], primaryKeys: [] },
|
|
},
|
|
);
|
|
});
|
|
|
|
test("serializes MQ tabs with selected tenant context", () => {
|
|
const saved = serializeOpenTabs([
|
|
queryTab({
|
|
mode: "mq",
|
|
database: "",
|
|
mqTenant: "public",
|
|
}),
|
|
]);
|
|
|
|
assert.equal(saved[0]?.mqTenant, "public");
|
|
});
|
|
|
|
test("serializes Nacos admin tabs", () => {
|
|
const saved = serializeOpenTabs([
|
|
queryTab({
|
|
mode: "nacos",
|
|
database: "",
|
|
nacosNamespace: "dev",
|
|
nacosNamespaceName: "Development",
|
|
}),
|
|
]);
|
|
|
|
assert.equal(saved[0]?.mode, "nacos");
|
|
assert.equal(saved[0]?.nacosNamespace, "dev");
|
|
assert.equal(saved[0]?.nacosNamespaceName, "Development");
|
|
});
|
|
|
|
test("serializes evicted result cache handles", () => {
|
|
const saved = serializeOpenTabs([
|
|
queryTab({
|
|
resultEvicted: true,
|
|
resultCacheKey: "tab:tab-1:result",
|
|
}),
|
|
]);
|
|
|
|
assert.equal(saved[0]?.resultEvicted, true);
|
|
assert.equal(saved[0]?.resultCacheKey, "tab:tab-1:result");
|
|
});
|
|
|
|
test("does not persist table data result cache handles across restarts", () => {
|
|
const saved = serializeOpenTabs([
|
|
queryTab({
|
|
mode: "data",
|
|
resultEvicted: true,
|
|
resultCacheKey: "tab:tab-1:result",
|
|
}),
|
|
]);
|
|
|
|
assert.equal(saved[0]?.resultEvicted, undefined);
|
|
assert.equal(saved[0]?.resultCacheKey, undefined);
|
|
});
|
|
|
|
test("restores evicted result cache handles as disk-backed runtime state", () => {
|
|
const raw = JSON.stringify([queryTab({ resultEvicted: true, resultCacheKey: "tab:tab-1:result" })]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "tab-1");
|
|
|
|
assert.equal(restored.tabs[0]?.resultEvicted, true);
|
|
assert.equal(restored.tabs[0]?.resultCacheKey, "tab:tab-1:result");
|
|
assert.equal(restored.tabs[0]?.resultCacheState, "disk");
|
|
});
|
|
|
|
test("serializes query result run metadata without row payloads", () => {
|
|
const saved = serializeOpenTabs([
|
|
queryTab({
|
|
activeResultRunId: "run-2",
|
|
resultRuns: [
|
|
{
|
|
id: "run-1",
|
|
title: "Run 1",
|
|
sequence: 1,
|
|
sql: "select 1",
|
|
createdAt: 100,
|
|
result: {
|
|
columns: ["id"],
|
|
rows: [[1]],
|
|
affected_rows: 0,
|
|
execution_time_ms: 1,
|
|
},
|
|
resultCacheKey: "tab:tab-1:run:run-1",
|
|
resultCacheState: "disk",
|
|
resultEvicted: true,
|
|
},
|
|
],
|
|
}),
|
|
]);
|
|
|
|
assert.deepEqual(saved[0]?.resultRuns, [
|
|
{
|
|
id: "run-1",
|
|
title: "Run 1",
|
|
sequence: 1,
|
|
sql: "select 1",
|
|
createdAt: 100,
|
|
activeResultIndex: undefined,
|
|
resultCacheKey: "tab:tab-1:run:run-1",
|
|
resultEvicted: true,
|
|
},
|
|
]);
|
|
assert.equal(JSON.stringify(saved).includes("[[1]]"), false);
|
|
assert.equal(saved[0]?.activeResultRunId, "run-2");
|
|
});
|
|
|
|
test("restores query result run metadata as disk-backed runtime state", () => {
|
|
const raw = JSON.stringify([
|
|
{
|
|
...queryTab(),
|
|
activeResultRunId: "run-1",
|
|
resultRuns: [
|
|
{
|
|
id: "run-1",
|
|
title: "Run 1",
|
|
sequence: 1,
|
|
sql: "select 1",
|
|
createdAt: 100,
|
|
resultCacheKey: "tab:tab-1:run:run-1",
|
|
resultEvicted: true,
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "tab-1");
|
|
|
|
assert.equal(restored.tabs[0]?.activeResultRunId, "run-1");
|
|
assert.equal(restored.tabs[0]?.resultRuns?.[0]?.id, "run-1");
|
|
assert.equal(restored.tabs[0]?.resultRuns?.[0]?.resultCacheState, "disk");
|
|
assert.equal(restored.tabs[0]?.resultRuns?.[0]?.result, undefined);
|
|
});
|
|
|
|
test("ignores legacy table data result cache handles on restore", () => {
|
|
const raw = JSON.stringify([queryTab({ mode: "data", resultEvicted: true, resultCacheKey: "tab:tab-1:result" })]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "tab-1");
|
|
|
|
assert.equal(restored.tabs[0]?.resultEvicted, undefined);
|
|
assert.equal(restored.tabs[0]?.resultCacheKey, undefined);
|
|
assert.equal(restored.tabs[0]?.resultCacheState, undefined);
|
|
});
|
|
|
|
test("restores unsaved query tabs and active tab after restart", () => {
|
|
const raw = JSON.stringify([queryTab({ id: "tab-1", sql: "select 1" }), queryTab({ id: "tab-2", title: "Query 2", sql: "select 2" })]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "tab-2");
|
|
|
|
assert.deepEqual(
|
|
restored.tabs.map((tab) => ({ id: tab.id, sql: tab.sql, originalSql: tab.originalSql, isExecuting: tab.isExecuting })),
|
|
[
|
|
{ id: "tab-1", sql: "select 1", originalSql: "", isExecuting: false },
|
|
{ id: "tab-2", sql: "select 2", originalSql: "", isExecuting: false },
|
|
],
|
|
);
|
|
assert.equal(restored.activeTabId, "tab-2");
|
|
});
|
|
|
|
test("restores only pinned tabs when requested", () => {
|
|
const raw = JSON.stringify([queryTab({ id: "tab-1", pinned: true }), queryTab({ id: "tab-2", pinned: false }), queryTab({ id: "tab-3", pinned: true })]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "tab-2", { filter: "pinned" });
|
|
|
|
assert.deepEqual(
|
|
restored.tabs.map((tab) => tab.id),
|
|
["tab-1", "tab-3"],
|
|
);
|
|
assert.equal(restored.activeTabId, "tab-1");
|
|
});
|
|
|
|
test("restores object source save context", () => {
|
|
const raw = JSON.stringify([
|
|
queryTab({
|
|
customTitle: true,
|
|
objectSource: {
|
|
schema: "public",
|
|
name: "fn_add",
|
|
objectType: "FUNCTION",
|
|
},
|
|
}),
|
|
]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "tab-1");
|
|
|
|
assert.deepEqual(restored.tabs[0]?.objectSource, {
|
|
schema: "public",
|
|
name: "fn_add",
|
|
objectType: "FUNCTION",
|
|
});
|
|
assert.equal(restored.tabs[0]?.customTitle, true);
|
|
});
|
|
|
|
test("restores data and structure tabs with table state", () => {
|
|
const raw = JSON.stringify([
|
|
queryTab({
|
|
id: "data",
|
|
title: "public.users",
|
|
mode: "data",
|
|
sql: 'SELECT * FROM "public"."users" LIMIT 50 OFFSET 50;',
|
|
lastExecutedSql: 'SELECT * FROM "public"."users" LIMIT 50 OFFSET 50;',
|
|
resultPageLimit: 50,
|
|
resultPageOffset: 50,
|
|
whereInput: "id > 10",
|
|
orderByInput: "id DESC",
|
|
tableMeta: {
|
|
schema: "public",
|
|
tableName: "users",
|
|
columns: [
|
|
{
|
|
name: "id",
|
|
data_type: "integer",
|
|
is_nullable: false,
|
|
column_default: null,
|
|
is_primary_key: true,
|
|
extra: null,
|
|
},
|
|
],
|
|
primaryKeys: ["id"],
|
|
},
|
|
}),
|
|
queryTab({
|
|
id: "structure",
|
|
title: "Edit users",
|
|
mode: "structure",
|
|
sql: "",
|
|
structureTableName: "users",
|
|
}),
|
|
]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "data");
|
|
|
|
assert.deepEqual(
|
|
restored.tabs.map((tab) => ({ id: tab.id, mode: tab.mode })),
|
|
[
|
|
{ id: "data", mode: "data" },
|
|
{ id: "structure", mode: "structure" },
|
|
],
|
|
);
|
|
assert.equal(restored.activeTabId, "data");
|
|
assert.equal(restored.tabs[0]?.tableMeta?.tableName, "users");
|
|
assert.equal(restored.tabs[0]?.resultPageLimit, 50);
|
|
assert.equal(restored.tabs[0]?.resultPageOffset, 50);
|
|
assert.equal(restored.tabs[0]?.whereInput, "id > 10");
|
|
assert.equal(restored.tabs[0]?.orderByInput, "id DESC");
|
|
assert.equal(restored.tabs[1]?.structureTableName, "users");
|
|
});
|
|
|
|
test("restores MQ tabs with selected tenant context", () => {
|
|
const raw = JSON.stringify([
|
|
queryTab({
|
|
mode: "mq",
|
|
database: "",
|
|
mqTenant: "public",
|
|
}),
|
|
]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "tab-1");
|
|
|
|
assert.equal(restored.tabs[0]?.mqTenant, "public");
|
|
});
|
|
|
|
test("restores GridFS manager tabs with their mode intact", () => {
|
|
const raw = JSON.stringify([
|
|
queryTab({
|
|
id: "gridfs",
|
|
title: "GridFS",
|
|
mode: "mongo-gridfs" as QueryTab["mode"],
|
|
sql: "",
|
|
database: "amazon",
|
|
}),
|
|
]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "gridfs");
|
|
|
|
assert.equal(restored.tabs[0]?.mode, "mongo-gridfs");
|
|
assert.equal(restored.tabs[0]?.title, "GridFS");
|
|
assert.equal(restored.activeTabId, "gridfs");
|
|
});
|
|
|
|
test("query-only restore keeps legacy query tabs without a mode", () => {
|
|
const raw = JSON.stringify([
|
|
{
|
|
id: "legacy",
|
|
title: "Query 1",
|
|
connectionId: "conn-1",
|
|
database: "app",
|
|
sql: "select now()",
|
|
},
|
|
queryTab({ id: "data", title: "users", mode: "data" }),
|
|
]);
|
|
|
|
const restored = restoreOpenTabsState(raw, "data", { queryOnly: true });
|
|
|
|
assert.deepEqual(
|
|
restored.tabs.map((tab) => ({ id: tab.id, mode: tab.mode })),
|
|
[{ id: "legacy", mode: "query" }],
|
|
);
|
|
assert.equal(restored.activeTabId, "legacy");
|
|
});
|
|
|
|
test("ignores invalid persisted tab payloads", () => {
|
|
const restored = restoreOpenTabsState(JSON.stringify([{ id: "missing-fields" }, queryTab()]), "missing-fields");
|
|
|
|
assert.deepEqual(
|
|
restored.tabs.map((tab) => tab.id),
|
|
["tab-1"],
|
|
);
|
|
assert.equal(restored.activeTabId, "tab-1");
|
|
});
|