Files
MyGoNavi/frontend/src/utils/objectQueryTemplates.test.ts
2026-08-07 16:45:31 +08:00

166 lines
5.5 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
const { dbGetColumnsMock } = vi.hoisted(() => ({
dbGetColumnsMock: vi.fn(),
}));
vi.mock('../../wailsjs/go/app/App', () => ({
DBGetColumns: dbGetColumnsMock,
}));
import {
buildContextualNewQueryTemplate,
buildTableSelectQuery,
extractTableSelectColumnNames,
isElasticsearchDbType,
resolveTableSelectQuery,
} from './objectQueryTemplates';
describe('buildContextualNewQueryTemplate', () => {
it('builds a dialect-aware select for the active table with the default template', () => {
expect(buildContextualNewQueryTemplate({
dbType: 'mysql',
tableName: 'Order Items',
customTemplate: null,
})).toBe('SELECT * FROM `Order Items`;');
});
it('appends the active table when a custom template ends with FROM', () => {
expect(buildContextualNewQueryTemplate({
dbType: 'postgres',
tableName: 'public.OrderItems',
customTemplate: 'SELECT id, total FROM ',
})).toBe('SELECT id, total FROM public."OrderItems"');
});
it('preserves custom templates that do not expose a table insertion point', () => {
expect(buildContextualNewQueryTemplate({
dbType: 'mysql',
tableName: 'users',
customTemplate: 'SELECT CURRENT_TIMESTAMP;',
})).toBeNull();
});
it('builds the native query format for an active Elasticsearch index', () => {
expect(buildContextualNewQueryTemplate({
dbType: 'elasticsearch',
tableName: 'orders-v1',
customTemplate: null,
})).toBe('GET /orders-v1/_search\n{\n "query": {\n "match_all": {}\n }\n}\n');
});
});
describe('buildTableSelectQuery', () => {
it('quotes uppercase postgres table names in new query templates', () => {
expect(buildTableSelectQuery('postgres', 'public.MyTable')).toBe('SELECT * FROM public."MyTable";');
});
it('expands provided columns into a multi-line select list', () => {
expect(buildTableSelectQuery('mysql', 'users', ['id', 'name', 'created_at'])).toBe(
'SELECT\n `id`,\n `name`,\n `created_at`\nFROM `users`;',
);
});
it('quotes reserved and uppercase column names for postgres', () => {
expect(buildTableSelectQuery('postgres', 'public.orders', ['user', 'OrderID'])).toBe(
'SELECT\n "user",\n "OrderID"\nFROM public.orders;',
);
});
it('adds a preview limit for RocketMQ topic browsing', () => {
expect(buildTableSelectQuery('rocketmq', 'orders.events')).toBe('SELECT * FROM "orders.events" LIMIT 100;');
});
it('adds a preview limit for Kafka topic browsing', () => {
expect(buildTableSelectQuery('kafka', 'logs.app-1')).toBe('SELECT * FROM "logs.app-1" LIMIT 100;');
});
it('adds a preview limit for MQTT topic browsing', () => {
expect(buildTableSelectQuery('mqtt', 'devices/+/telemetry')).toBe('SELECT * FROM "devices/+/telemetry" LIMIT 100;');
});
it('adds a preview limit for RabbitMQ queue browsing', () => {
expect(buildTableSelectQuery('rabbitmq', 'orders.events.v1')).toBe('SELECT * FROM "orders.events.v1" LIMIT 100;');
});
it('builds an Elasticsearch match_all request for an index', () => {
expect(buildTableSelectQuery('elasticsearch', 'orders-v1')).toBe(
'GET /orders-v1/_search\n{\n "query": {\n "match_all": {}\n }\n}\n',
);
expect(isElasticsearchDbType('elasticsearch')).toBe(true);
expect(isElasticsearchDbType('elastic')).toBe(true);
});
});
describe('extractTableSelectColumnNames', () => {
it('reads mixed-case column name fields and keeps order without duplicates', () => {
expect(extractTableSelectColumnNames([
{ Name: 'id' },
{ name: 'name' },
{ COLUMN_NAME: 'name' },
{ field: 'created_at' },
{},
])).toEqual(['id', 'name', 'created_at']);
});
});
describe('resolveTableSelectQuery', () => {
beforeEach(() => {
dbGetColumnsMock.mockReset();
});
it('loads columns and expands them into the select list', async () => {
dbGetColumnsMock.mockResolvedValueOnce({
success: true,
data: [
{ name: 'id' },
{ name: 'email' },
],
});
await expect(resolveTableSelectQuery({
dbType: 'mysql',
tableName: 'users',
dbName: 'app',
connectionConfig: { type: 'mysql', host: 'localhost' },
})).resolves.toBe('SELECT\n `id`,\n `email`\nFROM `users`;');
expect(dbGetColumnsMock).toHaveBeenCalledTimes(1);
});
it('falls back to select star when column metadata is unavailable', async () => {
dbGetColumnsMock.mockResolvedValueOnce({
success: false,
message: 'unavailable',
data: null,
});
await expect(resolveTableSelectQuery({
dbType: 'postgres',
tableName: 'public.users',
dbName: 'app',
connectionConfig: { type: 'postgres', host: 'localhost' },
})).resolves.toBe('SELECT * FROM public.users;');
});
it('keeps message-queue templates on select star without loading columns', async () => {
await expect(resolveTableSelectQuery({
dbType: 'kafka',
tableName: 'logs.app-1',
connectionConfig: { type: 'kafka' },
})).resolves.toBe('SELECT * FROM "logs.app-1" LIMIT 100;');
expect(dbGetColumnsMock).not.toHaveBeenCalled();
});
it('opens an Elasticsearch index with a match_all request without loading SQL columns', async () => {
await expect(resolveTableSelectQuery({
dbType: 'elasticsearch',
tableName: 'orders-v1',
connectionConfig: { type: 'elasticsearch' },
})).resolves.toBe('GET /orders-v1/_search\n{\n "query": {\n "match_all": {}\n }\n}\n');
expect(dbGetColumnsMock).not.toHaveBeenCalled();
});
});