mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-11 01:03:51 +08:00
166 lines
5.5 KiB
TypeScript
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();
|
|
});
|
|
});
|