Files
dbx/apps/desktop/src/lib/__tests__/app/openTabsPersistence.spec.ts
T

239 lines
8.6 KiB
TypeScript
Vendored

import { describe, expect, it } from "vitest";
import { serializeOpenTabs, restoreOpenTabsPayload } from "@/lib/app/openTabsPersistence";
import type { QueryTab } from "@/types/database";
function queryTab(overrides: Partial<QueryTab>): QueryTab {
return {
id: "t1",
title: "query_1",
connectionId: "c1",
database: "db",
mode: "query",
sql: "",
isExecuting: false,
...overrides,
} as QueryTab;
}
function roundTrip(tabs: QueryTab[]) {
const saved = serializeOpenTabs(tabs);
return restoreOpenTabsPayload({ tabs: saved, activeTabId: tabs[0]?.id ?? null }).tabs;
}
describe("openTabsPersistence originalSql round-trip", () => {
it("preserves per-tab output view state across a round-trip", () => {
const [restored] = roundTrip([queryTab({ uiState: { activeOutputView: "chart", resultPaneOpen: false } })]);
expect(restored.uiState).toEqual({ activeOutputView: "chart", resultPaneOpen: false });
});
it("preserves namespaced special-page state across a round-trip", () => {
const uiState = {
page: {
etcd: {
EtcdKeyBrowser: { mode: "search", searchQuery: "orders" },
KvKeyBrowser: { selectedKey: "/orders/42", expandedGroupIds: ["group:/orders"] },
},
},
};
const [restored] = roundTrip([queryTab({ mode: "etcd", uiState })]);
expect(restored.uiState).toEqual(uiState);
});
it("drops invalid per-tab output view state while restoring", () => {
const [restored] = restoreOpenTabsPayload({
tabs: [{ id: "t1", title: "query_1", connectionId: "c1", database: "db", mode: "query", sql: "", uiState: { activeOutputView: "invalid", resultPaneOpen: "false" } }],
activeTabId: "t1",
}).tabs;
expect(restored.uiState).toBeUndefined();
});
it("keeps clean saved SQL tabs eligible for file hydration", () => {
const [restored] = roundTrip([queryTab({ savedSqlId: "saved", sql: "SELECT 1", originalSql: "SELECT 1" })]);
expect(restored.sql).toBe("");
expect(restored.originalSql).toBeUndefined();
});
it("preserves read-only source intent without adding editable source metadata", () => {
const [restored] = roundTrip([queryTab({ sourceView: true, sql: "CREATE SEQUENCE seq_users" })]);
expect(restored.sourceView).toBe(true);
expect(restored.objectSource).toBeUndefined();
});
it("keeps legacy query tabs without source intent compatible", () => {
const [restored] = roundTrip([queryTab({ sql: "SELECT 1" })]);
expect(restored.sourceView).toBeUndefined();
});
it("restores a clean prefilled query tab as clean (sql === originalSql)", () => {
const sql = 'SELECT * FROM "public"."users"';
const [restored] = roundTrip([queryTab({ sql, originalSql: sql })]);
expect(restored.sql).toBe(sql);
expect(restored.originalSql).toBe(sql);
expect(restored.sql === restored.originalSql).toBe(true);
});
it("restores a user-edited scratch query tab as dirty (originalSql stays empty)", () => {
const [restored] = roundTrip([queryTab({ sql: "SELECT 1", originalSql: "" })]);
expect(restored.sql).toBe("SELECT 1");
expect(restored.originalSql).toBe("");
expect(restored.sql === restored.originalSql).toBe(false);
});
it("restores an empty new query tab as clean", () => {
const [restored] = roundTrip([queryTab({ sql: "", originalSql: "" })]);
expect(restored.sql).toBe("");
expect(restored.originalSql).toBe("");
});
it("falls back to empty originalSql for old saved state without the field (backward compat)", () => {
const [restored] = restoreOpenTabsPayload({
tabs: [{ id: "t1", title: "query_1", connectionId: "c1", database: "db", mode: "query", sql: "SELECT 1" }],
activeTabId: "t1",
}).tabs;
expect(restored.sql).toBe("SELECT 1");
expect(restored.originalSql).toBe("");
});
it("does not persist the transient result execution target", () => {
const tab = queryTab({ activeResultRunId: "run-1", executingResultRunId: "run-1", isExecuting: true });
const [saved] = serializeOpenTabs([tab]);
const [restored] = restoreOpenTabsPayload({
tabs: [{ ...saved, executingResultRunId: "run-1" }],
activeTabId: tab.id,
}).tabs;
expect(saved).not.toHaveProperty("executingResultRunId");
expect(restored.executingResultRunId).toBeUndefined();
expect(restored.isExecuting).toBe(false);
});
it("does not resume a MONITOR stream when restoring tabs", () => {
const tab = queryTab({ sql: "MONITOR", redisMonitorActive: true, isExecuting: true });
const [saved] = serializeOpenTabs([tab]);
const [restored] = roundTrip([tab]);
expect(saved).not.toHaveProperty("redisMonitorActive");
expect(restored.redisMonitorActive).toBe(false);
expect(restored.isExecuting).toBe(false);
});
it("preserves an external Doris catalog across tab restore", () => {
const [restored] = roundTrip([queryTab({ database: "dbx_catalog_completion", catalog: "dbx_mysql_catalog" })]);
expect(restored.database).toBe("dbx_catalog_completion");
expect(restored.catalog).toBe("dbx_mysql_catalog");
});
it("preserves external file versions and acknowledged state across tab restore", () => {
const version = { sizeBytes: 9, modifiedNs: "100", contentHash: "original" };
const ignoredVersion = { sizeBytes: 9, modifiedNs: "200", contentHash: "changed" };
const [restored] = roundTrip([
queryTab({
sql: "SELECT 1",
originalSql: "SELECT 1",
externalSqlPath: "/tmp/query.sql",
externalSqlFileVersion: version,
externalSqlIgnoredFileVersion: ignoredVersion,
externalSqlFileMissing: true,
}),
]);
expect(restored.externalSqlFileVersion).toEqual(version);
expect(restored.externalSqlIgnoredFileVersion).toEqual(ignoredVersion);
expect(restored.externalSqlFileMissing).toBe(true);
});
it("preserves the disk baseline for a dirty external file after an ignored change", () => {
const version = { sizeBytes: 9, modifiedNs: "100", contentHash: "original" };
const ignoredVersion = { sizeBytes: 9, modifiedNs: "200", contentHash: "changed" };
const [restored] = roundTrip([
queryTab({
sql: "SELECT 2",
originalSql: "SELECT 1",
externalSqlPath: "/tmp/query.sql",
externalSqlFileVersion: version,
externalSqlIgnoredFileVersion: ignoredVersion,
}),
]);
expect(restored.sql).toBe("SELECT 2");
expect(restored.originalSql).toBe("SELECT 1");
expect(restored.externalSqlIgnoredFileVersion).toEqual(ignoredVersion);
});
it("preserves the disk baseline for a dirty external file acknowledged as missing", () => {
const [restored] = roundTrip([
queryTab({
sql: "SELECT 2",
originalSql: "SELECT 1",
externalSqlPath: "/tmp/query.sql",
externalSqlFileMissing: true,
}),
]);
expect(restored.sql).toBe("SELECT 2");
expect(restored.originalSql).toBe("SELECT 1");
expect(restored.externalSqlFileMissing).toBe(true);
});
it("preserves plugin workbench identity and connection-safe context", () => {
const [restored] = roundTrip([
queryTab({
id: "plugin-tab",
title: "Hello connection · Workbench",
connectionId: "plugin-connection",
database: "",
mode: "plugin-workbench",
pluginWorkbench: {
pluginId: "dbx.example.hello",
contributionId: "dbx.example.hello.main",
context: {
connectionId: "plugin-connection",
providerId: "hello.connection",
connectionType: "hello",
},
},
}),
]);
expect(restored.mode).toBe("plugin-workbench");
expect(restored.pluginWorkbench).toEqual({
pluginId: "dbx.example.hello",
contributionId: "dbx.example.hello.main",
context: {
connectionId: "plugin-connection",
providerId: "hello.connection",
connectionType: "hello",
},
});
});
it("preserves host-owned plugin filesystem navigation", () => {
const [restored] = roundTrip([
queryTab({
id: "plugin-files",
title: "Object storage · Files",
connectionId: "plugin-connection",
database: "",
mode: "plugin-filesystem",
pluginFilesystem: {
pluginId: "dbx.example.storage",
providerId: "dbx.example.storage.files",
rootUri: "s3://bucket/",
currentUri: "s3://bucket/reports/",
},
}),
]);
expect(restored.mode).toBe("plugin-filesystem");
expect(restored.pluginFilesystem).toEqual({
pluginId: "dbx.example.storage",
providerId: "dbx.example.storage.files",
rootUri: "s3://bucket/",
currentUri: "s3://bucket/reports/",
});
});
});