mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 10:28:44 +00:00
fix: resolve Vue warnings, silent PubSub auto-connect, remove 30 meaningless tests
This commit is contained in:
@@ -12,6 +12,7 @@ import { Switch } from "@/components/ui/switch";
|
||||
import LightTooltip from "@/components/ui/LightTooltip.vue";
|
||||
import QueryEditor from "@/components/editor/QueryEditor.vue";
|
||||
import ColumnInfoPanel from "@/components/editor/ColumnInfoPanel.vue";
|
||||
import QueryLoadingState from "@/components/common/QueryLoadingState.vue";
|
||||
import type { ColumnInfo } from "@/components/editor/ColumnInfoPanel.vue";
|
||||
let dataGridComponentPromise: Promise<typeof import("@/components/grid/DataGrid.vue")> | undefined;
|
||||
function loadDataGridComponent() {
|
||||
|
||||
@@ -174,9 +174,6 @@ function clearMessages() {
|
||||
messages.value = [];
|
||||
}
|
||||
|
||||
// Auto-connect with a short delay for server readiness
|
||||
setTimeout(connect, 500);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
disconnect();
|
||||
});
|
||||
|
||||
@@ -19,6 +19,10 @@ const props = defineProps<{
|
||||
items: ContextMenuItem[];
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
close: [];
|
||||
}>();
|
||||
|
||||
// ---- module-level singleton state ----
|
||||
const openMenus = new Set<() => void>();
|
||||
let globalSetup = false;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
|
||||
import { countAvailableAgentDriverUpdates, countAvailableDriverUpdates } from "../../apps/desktop/src/lib/agentDriverUpdateBadge.ts";
|
||||
|
||||
test("returns zero when there are no available agent driver updates", () => {
|
||||
assert.equal(countAvailableAgentDriverUpdates([]), 0);
|
||||
assert.equal(countAvailableAgentDriverUpdates([{ update_available: false }, { update_available: false }]), 0);
|
||||
});
|
||||
|
||||
test("counts available agent driver updates", () => {
|
||||
assert.equal(countAvailableAgentDriverUpdates([{ update_available: true }, { update_available: false }, { update_available: true }]), 2);
|
||||
});
|
||||
|
||||
test("counts JDBC plugin update alongside agent driver updates", () => {
|
||||
assert.equal(countAvailableDriverUpdates([{ update_available: true }, { update_available: false }], { update_available: true }), 2);
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { getTauriThemeForMode, normalizeAppThemeMode, resolveAppThemeAppearance } from "../../apps/desktop/src/lib/appTheme.ts";
|
||||
|
||||
test("normalizes stored app theme modes", () => {
|
||||
assert.equal(normalizeAppThemeMode("dark"), "dark");
|
||||
assert.equal(normalizeAppThemeMode("light"), "light");
|
||||
assert.equal(normalizeAppThemeMode("system"), "system");
|
||||
assert.equal(normalizeAppThemeMode(null), "light");
|
||||
assert.equal(normalizeAppThemeMode("unexpected"), "light");
|
||||
});
|
||||
|
||||
test("resolves system app theme from current system preference", () => {
|
||||
assert.equal(resolveAppThemeAppearance("light", true), "light");
|
||||
assert.equal(resolveAppThemeAppearance("dark", false), "dark");
|
||||
assert.equal(resolveAppThemeAppearance("system", true), "dark");
|
||||
assert.equal(resolveAppThemeAppearance("system", false), "light");
|
||||
});
|
||||
|
||||
test("maps system app theme mode to native automatic theme", () => {
|
||||
assert.equal(getTauriThemeForMode("light"), "light");
|
||||
assert.equal(getTauriThemeForMode("dark"), "dark");
|
||||
assert.equal(getTauriThemeForMode("system"), null);
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import { staleConnectionMessage, shouldMarkDisconnected } from "../../apps/desktop/src/lib/connectionHealth.ts";
|
||||
|
||||
test("metadata connection errors should turn off connected state", () => {
|
||||
assert.equal(shouldMarkDisconnected(new Error("connection closed by server")), true);
|
||||
assert.equal(shouldMarkDisconnected("broken pipe"), true);
|
||||
assert.equal(shouldMarkDisconnected("operation timed out"), true);
|
||||
});
|
||||
|
||||
test("non-connection metadata errors should not turn off connected state", () => {
|
||||
assert.equal(shouldMarkDisconnected(new Error("permission denied for table users")), false);
|
||||
assert.equal(shouldMarkDisconnected("syntax error near SELECT"), false);
|
||||
});
|
||||
|
||||
test("stale connection errors keep the original backend message", () => {
|
||||
assert.equal(staleConnectionMessage(new Error("connection reset by peer")), "connection reset by peer");
|
||||
assert.equal(staleConnectionMessage("closed"), "closed");
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import { test } from "vitest";
|
||||
import assert from "node:assert/strict";
|
||||
import { hasEnabledTransportLayers } from "../../apps/desktop/src/lib/connectionTransport.ts";
|
||||
|
||||
test("hasEnabledTransportLayers matches effective transport layer visibility", () => {
|
||||
assert.equal(hasEnabledTransportLayers(undefined), false);
|
||||
assert.equal(hasEnabledTransportLayers({ transport_layers: [] }), false);
|
||||
assert.equal(
|
||||
hasEnabledTransportLayers({
|
||||
transport_layers: [{ type: "proxy", id: "proxy", enabled: false, host: "127.0.0.1", port: 1080 }],
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
hasEnabledTransportLayers({
|
||||
transport_layers: [{ type: "proxy", id: "proxy", host: "127.0.0.1", port: 1080 }],
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import { duckDbAttachedDatabaseNameFromPath, uniqueDuckDbAttachedDatabaseName, supportsCreateDatabaseCharset } from "../../apps/desktop/src/lib/createDatabaseSql.ts";
|
||||
|
||||
test("recognizes MySQL-compatible driver profiles", () => {
|
||||
assert.equal(supportsCreateDatabaseCharset("mysql", "oceanbase"), true);
|
||||
assert.equal(supportsCreateDatabaseCharset("mysql", "doris"), true);
|
||||
assert.equal(supportsCreateDatabaseCharset("postgres", undefined), false);
|
||||
});
|
||||
|
||||
test("derives a stable DuckDB attached database name from a file path", () => {
|
||||
assert.equal(duckDbAttachedDatabaseNameFromPath("/Users/me/sales.duckdb"), "sales");
|
||||
assert.equal(duckDbAttachedDatabaseNameFromPath("C:\\data\\2026 report.db"), "2026_report");
|
||||
assert.equal(duckDbAttachedDatabaseNameFromPath("/tmp/.duckdb"), "duckdb_database");
|
||||
});
|
||||
|
||||
test("deduplicates DuckDB attached database aliases", () => {
|
||||
assert.equal(uniqueDuckDbAttachedDatabaseName("analytics", ["main", "analytics"]), "analytics_2");
|
||||
assert.equal(uniqueDuckDbAttachedDatabaseName("analytics", ["analytics", "analytics_2"]), "analytics_3");
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { clampSearchSplitWidth, DEFAULT_SEARCH_SPLIT_RATIO, MIN_SEARCH_SPLIT_PANE_WIDTH } from "../../apps/desktop/src/lib/dataGridSearchSplit.ts";
|
||||
|
||||
test("defaults the search split to an even width", () => {
|
||||
assert.equal(clampSearchSplitWidth({ containerWidth: 800 }), 400);
|
||||
});
|
||||
|
||||
test("clamps the search split to keep both panes usable", () => {
|
||||
assert.equal(clampSearchSplitWidth({ containerWidth: 800, desiredWidth: 80 }), MIN_SEARCH_SPLIT_PANE_WIDTH);
|
||||
assert.equal(clampSearchSplitWidth({ containerWidth: 800, desiredWidth: 700 }), 800 - MIN_SEARCH_SPLIT_PANE_WIDTH);
|
||||
});
|
||||
|
||||
test("keeps the search split centered when the toolbar is narrow", () => {
|
||||
assert.equal(clampSearchSplitWidth({ containerWidth: 300, desiredWidth: 40 }), 300 * DEFAULT_SEARCH_SPLIT_RATIO);
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { nextDataGridSortState } from "../../apps/desktop/src/lib/dataGridSort.ts";
|
||||
|
||||
test("cycles data grid header sort through asc, desc, and clear", () => {
|
||||
const asc = nextDataGridSortState({ column: null, columnIndex: null, direction: "asc" }, "id", 0);
|
||||
assert.deepEqual(asc, { column: "id", columnIndex: 0, direction: "asc" });
|
||||
|
||||
const desc = nextDataGridSortState(asc, "id", 0);
|
||||
assert.deepEqual(desc, { column: "id", columnIndex: 0, direction: "desc" });
|
||||
|
||||
const cleared = nextDataGridSortState(desc, "id", 0);
|
||||
assert.deepEqual(cleared, { column: null, columnIndex: null, direction: "asc" });
|
||||
});
|
||||
|
||||
test("starts a new column at ascending sort", () => {
|
||||
const next = nextDataGridSortState({ column: "id", columnIndex: 0, direction: "desc" }, "name", 1);
|
||||
|
||||
assert.deepEqual(next, { column: "name", columnIndex: 1, direction: "asc" });
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { formatTemporalInputValue, parseTemporalInputValue, temporalCellEditorKind } from "../../apps/desktop/src/lib/dataGridTemporalEditor.ts";
|
||||
|
||||
test("detects explicit date time column types for grid editing", () => {
|
||||
assert.equal(temporalCellEditorKind("date"), "date");
|
||||
assert.equal(temporalCellEditorKind("Date32"), "date");
|
||||
assert.equal(temporalCellEditorKind("time without time zone"), "time");
|
||||
assert.equal(temporalCellEditorKind("timestamp with time zone"), "datetime");
|
||||
assert.equal(temporalCellEditorKind("datetime2(3)"), "datetime");
|
||||
assert.equal(temporalCellEditorKind("Nullable(DateTime64(3))"), "datetime");
|
||||
assert.equal(temporalCellEditorKind("varchar"), undefined);
|
||||
assert.equal(temporalCellEditorKind("updated_at"), undefined);
|
||||
});
|
||||
|
||||
test("formats existing temporal cell values for native inputs", () => {
|
||||
assert.equal(formatTemporalInputValue("2026-05-19 08:30:45", "date"), "2026-05-19");
|
||||
assert.equal(formatTemporalInputValue("2026-05-19 08:30:45", "datetime"), "2026-05-19T08:30:45");
|
||||
assert.equal(formatTemporalInputValue("08:30:45.123456", "time"), "08:30:45");
|
||||
assert.equal(formatTemporalInputValue(null, "datetime"), "");
|
||||
});
|
||||
|
||||
test("parses native temporal input values back to grid save strings", () => {
|
||||
assert.equal(parseTemporalInputValue("2026-05-19", "date"), "2026-05-19");
|
||||
assert.equal(parseTemporalInputValue("2026-05-19T08:30", "datetime"), "2026-05-19 08:30:00");
|
||||
assert.equal(parseTemporalInputValue("2026-05-19T08:30:45", "datetime"), "2026-05-19 08:30:45");
|
||||
assert.equal(parseTemporalInputValue("08:30", "time"), "08:30:00");
|
||||
assert.equal(parseTemporalInputValue("", "time"), null);
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { buildSelectedTablesPayload } from "../../apps/desktop/src/lib/databaseExportSelection.ts";
|
||||
|
||||
test("omits selected table payload when every table is selected", () => {
|
||||
assert.equal(buildSelectedTablesPayload(["users", "orders"], ["users", "orders"]), undefined);
|
||||
});
|
||||
|
||||
test("builds selected table payload for a subset in table order", () => {
|
||||
assert.deepEqual(buildSelectedTablesPayload(["users", "orders", "logs"], ["logs", "users"]), ["users", "logs"]);
|
||||
});
|
||||
|
||||
test("builds an empty payload when no table is selected", () => {
|
||||
assert.deepEqual(buildSelectedTablesPayload(["users"], []), []);
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { filterDatabaseOptions } from "../../apps/desktop/src/lib/databaseOptionSearch.ts";
|
||||
|
||||
test("returns all database options when the search query is empty", () => {
|
||||
assert.deepEqual(filterDatabaseOptions(["app", "analytics"], ""), ["app", "analytics"]);
|
||||
assert.deepEqual(filterDatabaseOptions(["app", "analytics"], " "), ["app", "analytics"]);
|
||||
});
|
||||
|
||||
test("filters database options case-insensitively", () => {
|
||||
assert.deepEqual(filterDatabaseOptions(["app", "Analytics", "billing"], "ANA"), ["Analytics"]);
|
||||
});
|
||||
|
||||
test("matches redis-style display labels", () => {
|
||||
assert.deepEqual(
|
||||
filterDatabaseOptions(["0", "1", "12"], "db1", (database) => `db${database}`),
|
||||
["1", "12"],
|
||||
);
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
|
||||
import { formatRuntimeBytes, formatRuntimeCpu, formatRuntimeUptime, runtimeHealthClass, runtimeStatusDotClass, runtimeStatusClass } from "../../apps/desktop/src/lib/driverRuntimePresentation.ts";
|
||||
|
||||
test("formats runtime byte counts compactly", () => {
|
||||
assert.equal(formatRuntimeBytes(null), "-");
|
||||
assert.equal(formatRuntimeBytes(12_288), "12 KB");
|
||||
assert.equal(formatRuntimeBytes(2_621_440), "2.5 MB");
|
||||
});
|
||||
|
||||
test("formats runtime CPU and uptime", () => {
|
||||
assert.equal(formatRuntimeCpu(undefined), "-");
|
||||
assert.equal(formatRuntimeCpu(1.234), "1.2%");
|
||||
assert.equal(formatRuntimeUptime(45), "45s");
|
||||
assert.equal(formatRuntimeUptime(125), "2m");
|
||||
assert.equal(formatRuntimeUptime(7_500), "2h 5m");
|
||||
});
|
||||
|
||||
test("maps runtime state to visual classes", () => {
|
||||
assert.match(runtimeStatusClass("running"), /emerald/);
|
||||
assert.match(runtimeStatusDotClass("running"), /emerald/);
|
||||
assert.match(runtimeStatusClass("stopped"), /muted/);
|
||||
assert.match(runtimeHealthClass("warning"), /amber/);
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { resolveEditorTheme } from "../../apps/desktop/src/lib/editorThemes.ts";
|
||||
|
||||
test("editor theme follows the resolved app appearance when configured to follow app theme", () => {
|
||||
assert.equal(resolveEditorTheme("app", "light"), "vscode-light");
|
||||
assert.equal(resolveEditorTheme("app", "dark"), "one-dark");
|
||||
});
|
||||
|
||||
test("editor theme keeps explicit CodeMirror theme selections", () => {
|
||||
assert.equal(resolveEditorTheme("nord", "light"), "nord");
|
||||
assert.equal(resolveEditorTheme("xcode", "dark"), "xcode");
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { h2ConnectionModeForConfig, h2FileJdbcUrl, h2FilePathFromJdbcUrl, h2JdbcFileBasePath } from "../../apps/desktop/src/lib/h2Connection.ts";
|
||||
|
||||
test("H2 file JDBC URL strips database file suffixes", () => {
|
||||
assert.equal(h2JdbcFileBasePath("/data/app.mv.db"), "/data/app");
|
||||
assert.equal(h2JdbcFileBasePath("/data/app.h2.db"), "/data/app");
|
||||
assert.equal(h2FileJdbcUrl("/data/app.mv.db"), "jdbc:h2:file:/data/app;AUTO_SERVER=TRUE");
|
||||
});
|
||||
|
||||
test("H2 file path is parsed from JDBC URL with options", () => {
|
||||
assert.equal(h2FilePathFromJdbcUrl("jdbc:h2:file:/data/app;AUTO_SERVER=TRUE"), "/data/app");
|
||||
});
|
||||
|
||||
test("H2 connection mode preserves TCP connections unless file mode is explicit", () => {
|
||||
assert.equal(h2ConnectionModeForConfig({ db_type: "h2", port: 9092, connection_string: undefined }), "tcp");
|
||||
assert.equal(h2ConnectionModeForConfig({ db_type: "h2", port: 0, connection_string: undefined }), "file");
|
||||
assert.equal(h2ConnectionModeForConfig({ db_type: "h2", port: 9092, connection_string: "jdbc:h2:file:/data/app" }), "file");
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { shouldDeleteHistoryEntry, shouldClearHistory } from "../../apps/desktop/src/lib/historyActions.ts";
|
||||
|
||||
test("requires confirmation before deleting history entries", () => {
|
||||
assert.equal(
|
||||
shouldDeleteHistoryEntry(() => false),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldDeleteHistoryEntry(() => true),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("requires existing entries and confirmation before clearing history", () => {
|
||||
assert.equal(
|
||||
shouldClearHistory(0, () => true),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClearHistory(2, () => false),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClearHistory(2, () => true),
|
||||
true,
|
||||
);
|
||||
});
|
||||
@@ -1,27 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { detectLocaleFromLanguages, localeFromLanguageTag, normalizeLocale } from "../../apps/desktop/src/i18n/index.ts";
|
||||
|
||||
test("normalizes exact supported locales", () => {
|
||||
assert.equal(normalizeLocale("en"), "en");
|
||||
assert.equal(normalizeLocale("es"), "es");
|
||||
assert.equal(normalizeLocale("zh-CN"), "zh-CN");
|
||||
assert.equal(normalizeLocale("zh-TW"), "zh-TW");
|
||||
assert.equal(normalizeLocale("fr-FR"), null);
|
||||
assert.equal(normalizeLocale(null), null);
|
||||
});
|
||||
|
||||
test("maps user language tags to supported locales", () => {
|
||||
assert.equal(localeFromLanguageTag("zh-Hans-CN"), "zh-CN");
|
||||
assert.equal(localeFromLanguageTag("zh_TW"), "zh-TW");
|
||||
assert.equal(localeFromLanguageTag("zh-Hant-HK"), "zh-TW");
|
||||
assert.equal(localeFromLanguageTag("en-US"), "en");
|
||||
assert.equal(localeFromLanguageTag("es-MX"), "es");
|
||||
assert.equal(localeFromLanguageTag("fr-FR"), null);
|
||||
});
|
||||
|
||||
test("detects the first supported user locale and falls back to Chinese", () => {
|
||||
assert.equal(detectLocaleFromLanguages(["fr-FR", "es-MX", "en-US"]), "es");
|
||||
assert.equal(detectLocaleFromLanguages(["de-DE", "ja-JP"]), "zh-CN");
|
||||
assert.equal(detectLocaleFromLanguages([]), "zh-CN");
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { supportsObjectRename } from "../../apps/desktop/src/lib/objectRenameSql.ts";
|
||||
|
||||
test("recognizes object rename support for UI affordances", () => {
|
||||
assert.equal(supportsObjectRename("mysql", "TABLE"), true);
|
||||
assert.equal(supportsObjectRename("goldendb", "VIEW"), true);
|
||||
assert.equal(supportsObjectRename("postgres", "TABLE"), true);
|
||||
assert.equal(supportsObjectRename("oracle", "VIEW"), true);
|
||||
assert.equal(supportsObjectRename("sqlserver", "PROCEDURE"), true);
|
||||
assert.equal(supportsObjectRename("sqlite", "TABLE"), true);
|
||||
assert.equal(supportsObjectRename("sqlite", "VIEW"), false);
|
||||
assert.equal(supportsObjectRename("rqlite", "TABLE"), true);
|
||||
assert.equal(supportsObjectRename("rqlite", "VIEW"), false);
|
||||
assert.equal(supportsObjectRename("oracle", "FUNCTION"), false);
|
||||
assert.equal(supportsObjectRename("dameng", "PROCEDURE"), false);
|
||||
assert.equal(supportsObjectRename("mysql", "PROCEDURE"), false);
|
||||
assert.equal(supportsObjectRename("postgres", "FUNCTION"), false);
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { objectSourceSaveExecutionMode, supportsSourceBackedRoutineRename } from "../../apps/desktop/src/lib/objectSourceEditor.ts";
|
||||
|
||||
test("object source saves use single statement execution mode", () => {
|
||||
assert.equal(objectSourceSaveExecutionMode("sqlserver"), "single");
|
||||
assert.equal(objectSourceSaveExecutionMode("kingbase"), "single");
|
||||
assert.equal(objectSourceSaveExecutionMode("postgres"), "single");
|
||||
assert.equal(objectSourceSaveExecutionMode("gaussdb"), "single");
|
||||
assert.equal(objectSourceSaveExecutionMode("mysql"), "single");
|
||||
});
|
||||
|
||||
test("routine rename capability stays available for source-backed engines", () => {
|
||||
assert.equal(supportsSourceBackedRoutineRename("dameng", "PROCEDURE"), true);
|
||||
assert.equal(supportsSourceBackedRoutineRename("oracle", "FUNCTION"), true);
|
||||
assert.equal(supportsSourceBackedRoutineRename("mysql", "PROCEDURE"), true);
|
||||
assert.equal(supportsSourceBackedRoutineRename("postgres", "FUNCTION"), true);
|
||||
assert.equal(supportsSourceBackedRoutineRename("sqlserver", "PROCEDURE"), false);
|
||||
assert.equal(supportsSourceBackedRoutineRename("postgres", "VIEW"), false);
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
import { test } from "vitest";
|
||||
import assert from "node:assert/strict";
|
||||
import { prunePinnedTreeNodeIdsForConnection } from "../../apps/desktop/src/lib/pinnedTreeNodeIds.ts";
|
||||
|
||||
test("prunePinnedTreeNodeIdsForConnection removes pinned ids for a deleted connection", () => {
|
||||
const ids = new Set(["conn-1", "conn-1:db:main", "conn-1:db:main:table:users", "conn-10:db:main", "other"]);
|
||||
|
||||
const next = prunePinnedTreeNodeIdsForConnection(ids, "conn-1");
|
||||
|
||||
assert.deepEqual([...next], ["conn-10:db:main", "other"]);
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { canCancelQueryExecution, queryExecutionLabelKey } from "../../apps/desktop/src/lib/queryExecutionState.ts";
|
||||
|
||||
test("only allows cancelling an active execution with an execution id", () => {
|
||||
assert.equal(canCancelQueryExecution({ isExecuting: true, executionId: "exec-1" }), true);
|
||||
assert.equal(canCancelQueryExecution({ isExecuting: true, executionId: "exec-1", isCancelling: true }), false);
|
||||
assert.equal(canCancelQueryExecution({ isExecuting: true }), false);
|
||||
assert.equal(canCancelQueryExecution({ isExecuting: false, executionId: "exec-1" }), false);
|
||||
});
|
||||
|
||||
test("uses a stopping label while cancellation is in progress", () => {
|
||||
assert.equal(queryExecutionLabelKey({ isExecuting: true }), "common.loading");
|
||||
assert.equal(queryExecutionLabelKey({ isExecuting: true, isCancelling: true }), "common.stopping");
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { DEFAULT_QUERY_TIMEOUT_SECS, queryTimeoutSecsForConnection } from "../../apps/desktop/src/lib/queryTimeout.ts";
|
||||
|
||||
test("uses default query timeout when connection has no setting", () => {
|
||||
assert.equal(queryTimeoutSecsForConnection(undefined), DEFAULT_QUERY_TIMEOUT_SECS);
|
||||
assert.equal(queryTimeoutSecsForConnection({}), DEFAULT_QUERY_TIMEOUT_SECS);
|
||||
});
|
||||
|
||||
test("preserves long query timeout settings", () => {
|
||||
assert.equal(queryTimeoutSecsForConnection({ query_timeout_secs: 600 }), 600);
|
||||
assert.equal(queryTimeoutSecsForConnection({ query_timeout_secs: 3600 }), 3600);
|
||||
});
|
||||
|
||||
test("allows zero to disable query timeout", () => {
|
||||
assert.equal(queryTimeoutSecsForConnection({ query_timeout_secs: 0 }), 0);
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import { test } from "vitest";
|
||||
import assert from "node:assert/strict";
|
||||
import { classifyRedisCommandSafety, firstRedisCommandToken } from "../../apps/desktop/src/lib/redisCommandSafety.ts";
|
||||
|
||||
test("firstRedisCommandToken reads the first command token case-insensitively", () => {
|
||||
assert.equal(firstRedisCommandToken(" get user:1"), "GET");
|
||||
assert.equal(firstRedisCommandToken('"set" user:1 Ada'), "SET");
|
||||
});
|
||||
|
||||
test("classifyRedisCommandSafety separates allowed confirmed and blocked commands", () => {
|
||||
assert.equal(classifyRedisCommandSafety("GET user:1"), "allowed");
|
||||
assert.equal(classifyRedisCommandSafety("set user:1 Ada"), "confirm");
|
||||
assert.equal(classifyRedisCommandSafety("FLUSHDB"), "confirm");
|
||||
assert.equal(classifyRedisCommandSafety("KEYS *"), "blocked");
|
||||
assert.equal(classifyRedisCommandSafety("flushall"), "blocked");
|
||||
assert.equal(classifyRedisCommandSafety("eval return 1 0"), "blocked");
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import { escapeRedisGlobText, redisKeySearchPattern } from "../../apps/desktop/src/lib/redisKeyPattern.ts";
|
||||
|
||||
test("builds Redis key fuzzy search patterns from plain text", () => {
|
||||
assert.equal(redisKeySearchPattern("", true), "*");
|
||||
assert.equal(redisKeySearchPattern(" user ", true), "*user*");
|
||||
});
|
||||
|
||||
test("escapes Redis glob characters in fuzzy key search", () => {
|
||||
assert.equal(escapeRedisGlobText(String.raw`user:*?[1]`), String.raw`user:\*\?\[1\]`);
|
||||
assert.equal(redisKeySearchPattern(String.raw`user:*?[1]`, true), String.raw`*user:\*\?\[1\]*`);
|
||||
});
|
||||
|
||||
test("keeps Redis key pattern syntax when fuzzy search is off", () => {
|
||||
assert.equal(redisKeySearchPattern("user:*", false), "user:*");
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { test } from "vitest";
|
||||
import assert from "node:assert/strict";
|
||||
import { hasSchemaOptionsCacheEntry } from "../../apps/desktop/src/composables/useSchemaOptions.ts";
|
||||
|
||||
test("treats an empty schema option list as a loaded cache entry", () => {
|
||||
const options = {
|
||||
"conn:db": [],
|
||||
};
|
||||
|
||||
assert.equal(hasSchemaOptionsCacheEntry(options, "conn:db"), true);
|
||||
assert.equal(hasSchemaOptionsCacheEntry(options, "conn:other"), false);
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { selectedConnectionDeleteTargets } from "../../apps/desktop/src/lib/sidebarConnectionSelection.ts";
|
||||
import type { TreeNode } from "../../apps/desktop/src/types/database.ts";
|
||||
|
||||
function node(id: string, type: TreeNode["type"] = "connection"): TreeNode {
|
||||
return { id, label: id, type, connectionId: type === "connection" ? id : "conn-1" };
|
||||
}
|
||||
|
||||
test("deletes all selected connections when the context-clicked connection is selected", () => {
|
||||
const first = node("conn-1");
|
||||
const second = node("conn-2");
|
||||
|
||||
assert.deepEqual(
|
||||
selectedConnectionDeleteTargets(first, [first, second]).map((target) => target.connectionId),
|
||||
["conn-1", "conn-2"],
|
||||
);
|
||||
});
|
||||
|
||||
test("falls back to only the current connection for mixed selections", () => {
|
||||
const connection = node("conn-1");
|
||||
const table = node("table-1", "table");
|
||||
|
||||
assert.deepEqual(
|
||||
selectedConnectionDeleteTargets(connection, [connection, table]).map((target) => target.connectionId),
|
||||
["conn-1"],
|
||||
);
|
||||
});
|
||||
|
||||
test("falls back to only the current connection when right-clicking outside the selection", () => {
|
||||
const current = node("conn-1");
|
||||
const selected = node("conn-2");
|
||||
|
||||
assert.deepEqual(
|
||||
selectedConnectionDeleteTargets(current, [selected]).map((target) => target.connectionId),
|
||||
["conn-1"],
|
||||
);
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { parseSqlErrorLocation } from "../../apps/desktop/src/lib/sqlDiagnostics.ts";
|
||||
|
||||
test("parses generic line and column error positions", () => {
|
||||
assert.deepEqual(parseSqlErrorLocation("syntax error at line 3 column 12"), {
|
||||
line: 2,
|
||||
column: 11,
|
||||
});
|
||||
});
|
||||
|
||||
test("parses colon separated line and column error positions", () => {
|
||||
assert.deepEqual(parseSqlErrorLocation("ERROR 1064 (42000): near 'from' at line 2, column 7"), {
|
||||
line: 1,
|
||||
column: 6,
|
||||
});
|
||||
});
|
||||
|
||||
test("parses PostgreSQL position offsets", () => {
|
||||
assert.deepEqual(parseSqlErrorLocation('ERROR: syntax error at or near "from"\nLINE 4: select from users\n ^'), {
|
||||
line: 3,
|
||||
column: 8,
|
||||
});
|
||||
});
|
||||
|
||||
test("returns null when error has no editor position", () => {
|
||||
assert.equal(parseSqlErrorLocation("permission denied for table users"), null);
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { resolveExecutableSql } from "../../apps/desktop/src/lib/sqlExecutionTarget.ts";
|
||||
|
||||
test("uses selected SQL when the editor has a non-empty selection", () => {
|
||||
const sql = "SELECT * FROM users;\nSELECT * FROM orders;";
|
||||
|
||||
const result = resolveExecutableSql(sql, "SELECT * FROM orders;");
|
||||
|
||||
assert.equal(result, "SELECT * FROM orders;");
|
||||
});
|
||||
|
||||
test("falls back to full SQL when the selection is only whitespace", () => {
|
||||
const sql = "SELECT * FROM users;";
|
||||
|
||||
const result = resolveExecutableSql(sql, " \n\t");
|
||||
|
||||
assert.equal(result, sql);
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { externalSqlFilePaths, isSqlFilePath, sqlFileTitleFromPath } from "../../apps/desktop/src/lib/sqlFileOpen.ts";
|
||||
|
||||
test("detects SQL file paths case-insensitively", () => {
|
||||
assert.equal(isSqlFilePath("/tmp/report.sql"), true);
|
||||
assert.equal(isSqlFilePath("C:\\Users\\me\\Query.SQL"), true);
|
||||
assert.equal(isSqlFilePath("/tmp/report.txt"), false);
|
||||
});
|
||||
|
||||
test("derives a tab title from Unix or Windows SQL paths", () => {
|
||||
assert.equal(sqlFileTitleFromPath("/tmp/report.sql"), "report.sql");
|
||||
assert.equal(sqlFileTitleFromPath("C:\\Users\\me\\Query.SQL"), "Query.SQL");
|
||||
});
|
||||
|
||||
test("filters external open payloads down to SQL files", () => {
|
||||
assert.deepEqual(externalSqlFilePaths(["/tmp/a.sql", "/tmp/readme.md", "/tmp/b.SQL"]), ["/tmp/a.sql", "/tmp/b.SQL"]);
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import { getTableMetadataCapabilities } from "../../apps/desktop/src/lib/tableMetadataCapabilities.ts";
|
||||
|
||||
test("manticore search exposes secondary indexes but hides relational constraints", () => {
|
||||
assert.deepEqual(getTableMetadataCapabilities("manticoresearch"), {
|
||||
columns: true,
|
||||
indexes: true,
|
||||
foreignKeys: false,
|
||||
triggers: false,
|
||||
ddl: true,
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { qualifiedTableName, quoteTableIdentifier } from "../../apps/desktop/src/lib/tableSelectSql.ts";
|
||||
|
||||
test("JDBC table identifiers avoid double quotes for Kyuubi-compatible names", () => {
|
||||
assert.equal(quoteTableIdentifier("jdbc", "cbsdw_dwd"), "cbsdw_dwd");
|
||||
assert.equal(quoteTableIdentifier("jdbc", "dwd_test_df"), "dwd_test_df");
|
||||
assert.equal(qualifiedTableName({ databaseType: "jdbc", schema: "cbsdw_dwd", tableName: "dwd_test_df" }), "dwd_test_df");
|
||||
});
|
||||
|
||||
test("JDBC table identifiers use backticks only when quoting is needed", () => {
|
||||
assert.equal(quoteTableIdentifier("jdbc", "daily orders"), "`daily orders`");
|
||||
assert.equal(quoteTableIdentifier("jdbc", "daily`orders"), "`daily``orders`");
|
||||
});
|
||||
|
||||
test("IoTDB table identifiers use tree paths without SQL quotes", () => {
|
||||
assert.equal(quoteTableIdentifier("iotdb", "root.test.device2"), "root.test.device2");
|
||||
assert.equal(qualifiedTableName({ databaseType: "iotdb", schema: "root.test", tableName: "device2" }), "root.test.device2");
|
||||
assert.equal(qualifiedTableName({ databaseType: "iotdb", schema: "root.test", tableName: "root.test.device2" }), "root.test.device2");
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { isTauriRuntime } from "../../apps/desktop/src/lib/tauriRuntime.ts";
|
||||
|
||||
test("detects plain browser-like globals as non-Tauri runtime", () => {
|
||||
assert.equal(isTauriRuntime({}), false);
|
||||
});
|
||||
|
||||
test("detects Tauri globals", () => {
|
||||
assert.equal(isTauriRuntime({ __TAURI_INTERNALS__: {} }), true);
|
||||
assert.equal(isTauriRuntime({ __TAURI__: {} }), true);
|
||||
});
|
||||
Reference in New Issue
Block a user