fix(informix): resolve metadata owners correctly

This commit is contained in:
hb authored and GitHub committed 2026-08-02 09:04:30 +08:00
1 parent 4ee1f2b946
commit b5206415ae
10 files changed
+530 -79

No files matched your search

@@ -75,6 +75,19 @@ function genericJdbcConnection(): ConnectionConfig {
} as ConnectionConfig;
}
function informixConnection(): ConnectionConfig {
return {
id: "informix-1",
name: "Informix",
db_type: "informix",
host: "127.0.0.1",
port: 9088,
username: "informix",
password: "",
database: "prulife",
} as ConnectionConfig;
}
function procedure(name: string): ObjectInfo {
return {
name,
@@ -2245,6 +2258,151 @@ describe("connectionStore metadata loading", () => {
expect(dbNode.isExpanded).toBe(false);
});
it("does not re-expand an Informix schema collapsed while its metadata cache is being saved", async () => {
let resolveCacheSave!: () => void;
const saveSchemaCache = vi.fn(
() =>
new Promise<void>((resolve) => {
resolveCacheSave = resolve;
}),
);
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
vi.doMock("@/lib/backend/api", () => ({
checkConnectionHealth: vi.fn().mockResolvedValue(undefined),
deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined),
loadSchemaCache: vi.fn().mockResolvedValue(null),
saveSchemaCache,
saveConnections: vi.fn().mockResolvedValue(undefined),
saveSidebarLayout: vi.fn().mockResolvedValue(undefined),
}));
const { useConnectionStore } = await import("@/stores/connectionStore");
const { useSettingsStore } = await import("@/stores/settingsStore");
const store = useConnectionStore();
useSettingsStore().editorSettings.sidebarObjectDisplay = "grouped";
const connection = informixConnection();
const databaseNode: TreeNode = {
id: `${connection.id}:prulife`,
label: "prulife",
type: "database",
connectionId: connection.id,
database: "prulife",
isExpanded: true,
children: [],
};
const schemaNode: TreeNode = {
id: `${connection.id}:prulife:xtdpcky`,
label: "xtdpcky",
type: "schema",
connectionId: connection.id,
database: "prulife",
schema: "xtdpcky",
isExpanded: true,
children: [],
};
databaseNode.children = [schemaNode];
store.connections = [connection];
store.connectedIds.add(connection.id);
store.treeNodes = [
{
id: connection.id,
label: connection.name,
type: "connection",
connectionId: connection.id,
isExpanded: true,
children: [databaseNode],
},
];
const loadPromise = store.loadTables(connection.id, "prulife", "xtdpcky", { force: true });
await vi.waitFor(() => expect(saveSchemaCache).toHaveBeenCalledTimes(1));
expect(saveSchemaCache.mock.calls[0]?.[0]).toBe(`${connection.id}:prulife:xtdpcky:objects-grouped-v7-informix-owner-v2`);
expect(schemaNode.isLoading).toBe(true);
schemaNode.isExpanded = false;
store.cancelTreeNodeLoad(schemaNode.id);
resolveCacheSave();
await loadPromise;
expect(schemaNode.isLoading).toBe(false);
expect(schemaNode.isExpanded).toBe(false);
});
it("does not let an Informix schema load reclaim ownership after collapse during a health check", async () => {
let resolveHealthCheck!: () => void;
const checkConnectionHealth = vi.fn(
() =>
new Promise<void>((resolve) => {
resolveHealthCheck = resolve;
}),
);
const saveSchemaCache = vi.fn().mockResolvedValue(undefined);
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
vi.doMock("@/lib/backend/api", () => ({
checkConnectionHealth,
deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined),
loadSchemaCache: vi.fn().mockResolvedValue(null),
saveSchemaCache,
saveConnections: vi.fn().mockResolvedValue(undefined),
saveSidebarLayout: vi.fn().mockResolvedValue(undefined),
}));
const { useConnectionStore } = await import("@/stores/connectionStore");
const { useSettingsStore } = await import("@/stores/settingsStore");
const store = useConnectionStore();
useSettingsStore().editorSettings.sidebarObjectDisplay = "grouped";
const connection = informixConnection();
const schemaNode: TreeNode = {
id: `${connection.id}:prulife:xtdpcky`,
label: "xtdpcky",
type: "schema",
connectionId: connection.id,
database: "prulife",
schema: "xtdpcky",
isExpanded: true,
children: [],
};
store.connections = [connection];
store.connectedIds.add(connection.id);
store.treeNodes = [
{
id: connection.id,
label: connection.name,
type: "connection",
connectionId: connection.id,
isExpanded: true,
children: [
{
id: `${connection.id}:prulife`,
label: "prulife",
type: "database",
connectionId: connection.id,
database: "prulife",
isExpanded: true,
children: [schemaNode],
},
],
},
];
const loadPromise = store.loadTables(connection.id, "prulife", "xtdpcky", { force: true });
await vi.waitFor(() => expect(checkConnectionHealth).toHaveBeenCalledTimes(1));
expect(schemaNode.isLoading).toBe(true);
schemaNode.isExpanded = false;
store.cancelTreeNodeLoad(schemaNode.id);
resolveHealthCheck();
await loadPromise;
expect(saveSchemaCache).not.toHaveBeenCalled();
expect(schemaNode.isLoading).toBe(false);
expect(schemaNode.isExpanded).toBe(false);
});
it("does not apply load-more results after the parent generation is invalidated", async () => {
const firstPage = Array.from({ length: 201 }, (_, index) => ({
name: `t_${String(index + 1).padStart(4, "0")}`,