Files
dbx/apps/desktop/src/lib/__tests__/table/tableEditing.spec.ts
T

112 lines
5.6 KiB
TypeScript
Vendored

import { describe, expect, it } from "vitest";
import {
DBX_ROWID_COLUMN,
DBX_TDENGINE_TBNAME_COLUMN,
canDeleteExistingTdengineRows,
canEditExistingTableRows,
canUseKeylessRowPredicate,
editablePrimaryKeys,
editableRowIdentifierColumns,
hasCompleteTdengineRowIdentity,
isClickHouseExistingRowReadonlyColumn,
isTdengineExistingRowReadonlyColumn,
isTableDataEditable,
supportsDataGridTransaction,
usesSyntheticRowIdKey,
} from "@/lib/table/tableEditing";
import type { ColumnInfo, IndexInfo } from "@/types/database";
function column(name: string, isPrimaryKey = false): ColumnInfo {
return {
name,
data_type: "varchar",
is_nullable: true,
column_default: null,
is_primary_key: isPrimaryKey,
extra: null,
};
}
function index(columns: string[], isUnique = true, filter: string | null = null): IndexInfo {
return {
name: columns.join("_"),
columns,
is_unique: isUnique,
is_primary: false,
filter,
};
}
describe("tableEditing", () => {
it("synthesizes ROWID only for Oracle-compatible base tables", () => {
expect(editablePrimaryKeys("oracle", [column("ID"), column("NAME")], "VIEW")).toEqual([]);
expect(editablePrimaryKeys("oracle", [column("ID"), column("NAME")], "TABLE")).toEqual([DBX_ROWID_COLUMN]);
expect(editablePrimaryKeys("oceanbase-oracle", [column("ID"), column("NAME")], "TABLE")).toEqual([DBX_ROWID_COLUMN]);
expect(editablePrimaryKeys("oceanbase-oracle", [column("ID", true), column("NAME")], "TABLE")).toEqual(["ID"]);
});
it("treats view data tabs as readonly", () => {
expect(isTableDataEditable("oracle", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
});
it("does not include Oracle ROWID for view data tabs", () => {
expect(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
expect(usesSyntheticRowIdKey("oracle", [DBX_ROWID_COLUMN], "MATERIALIZED_VIEW")).toBe(false);
expect(usesSyntheticRowIdKey("oceanbase-oracle", [DBX_ROWID_COLUMN], "TABLE")).toBe(true);
expect(usesSyntheticRowIdKey("oceanbase-oracle", [DBX_ROWID_COLUMN], "VIEW")).toBe(false);
});
it("allows keyless row predicates only for databases that support them", () => {
expect(canUseKeylessRowPredicate("postgres", [])).toBe(true);
expect(canUseKeylessRowPredicate("mysql", [])).toBe(true);
expect(canUseKeylessRowPredicate("jdbc", [])).toBe(false);
expect(canUseKeylessRowPredicate("postgres", ["id"])).toBe(false);
});
it("uses unique indexes as row identifiers when primary keys are absent", () => {
expect(editableRowIdentifierColumns("postgres", [column("email"), column("name")], [index(["email", "name"]), index(["email"])])).toEqual(["email"]);
expect(editableRowIdentifierColumns("postgres", [column("email"), column("name")], [index(["email"], true, "email IS NOT NULL")])).toEqual([]);
expect(editableRowIdentifierColumns("postgres", [column("id", true), column("email")], [index(["email"])])).toEqual(["id"]);
});
it("allows ClickHouse table editing when row identifiers are available", () => {
expect(isTableDataEditable("clickhouse", ["id"], "BASE TABLE")).toBe(true);
expect(canEditExistingTableRows("clickhouse", undefined, ["id"])).toBe(true);
expect(supportsDataGridTransaction("clickhouse")).toBe(false);
expect(isTableDataEditable("clickhouse", [], "BASE TABLE")).toBe(true);
expect(canEditExistingTableRows("clickhouse", undefined, [])).toBe(false);
});
it("uses tbname only when editing TDengine stable rows", () => {
const columns = [column("ts", true), column("seq", true), column("voltage")];
expect(editablePrimaryKeys("tdengine", columns, "STABLE")).toEqual([DBX_TDENGINE_TBNAME_COLUMN, "ts", "seq"]);
expect(editablePrimaryKeys("tdengine", columns, "TABLE")).toEqual(["ts", "seq"]);
expect(canEditExistingTableRows("tdengine", undefined, ["ts", "seq"])).toBe(true);
expect(canEditExistingTableRows("tdengine", undefined, [])).toBe(false);
expect(isTdengineExistingRowReadonlyColumn("tdengine", "seq", columns)).toBe(true);
});
it("requires every TDengine row identifier in editable results", () => {
const stableKeys = [DBX_TDENGINE_TBNAME_COLUMN, "ts", "seq"];
expect(hasCompleteTdengineRowIdentity("tdengine", stableKeys, ["tbname", "ts", "seq", "voltage"])).toBe(true);
expect(hasCompleteTdengineRowIdentity("tdengine", stableKeys, ["tbname", "ts", "voltage"])).toBe(false);
expect(hasCompleteTdengineRowIdentity("tdengine", ["ts", "seq"], ["ts", "seq", "voltage"])).toBe(true);
expect(hasCompleteTdengineRowIdentity("postgres", ["id"], [])).toBe(true);
});
it("disables existing-row deletion for TDengine composite keys", () => {
expect(canDeleteExistingTdengineRows("tdengine", ["ts"])).toBe(true);
expect(canDeleteExistingTdengineRows("tdengine", [DBX_TDENGINE_TBNAME_COLUMN, "ts"])).toBe(true);
expect(canDeleteExistingTdengineRows("tdengine", ["ts", "seq"])).toBe(false);
expect(canDeleteExistingTdengineRows("tdengine", [DBX_TDENGINE_TBNAME_COLUMN, "ts", "seq"])).toBe(false);
expect(canDeleteExistingTdengineRows("postgres", ["id", "tenant_id"])).toBe(true);
});
it("treats ClickHouse row identifier cells as readonly on existing rows", () => {
expect(isClickHouseExistingRowReadonlyColumn("clickhouse", "ID", ["id"])).toBe(true);
expect(isClickHouseExistingRowReadonlyColumn("clickhouse", "name", ["id"])).toBe(false);
expect(isClickHouseExistingRowReadonlyColumn("clickhouse", "event_date", ["id"], [{ ...column("event_date"), extra: "partition_key" }])).toBe(true);
expect(isClickHouseExistingRowReadonlyColumn("postgres", "id", ["id"])).toBe(false);
});
});