mirror of https://github.com/buster-so/buster.git
238 lines
7.6 KiB
TypeScript
238 lines
7.6 KiB
TypeScript
|
import { describe, expect, test } from 'vitest';
|
||
|
import {
|
||
|
ensureSqlLimit,
|
||
|
ensureSqlLimitsForMultiple,
|
||
|
} from '../../../src/tools/database-tools/sql-limit-helper';
|
||
|
|
||
|
describe('SQL Limit Helper', () => {
|
||
|
describe('ensureSqlLimit', () => {
|
||
|
test('should add LIMIT 25 to SELECT statements without LIMIT', () => {
|
||
|
const sql = 'SELECT * FROM users';
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe('SELECT * FROM users LIMIT 25');
|
||
|
});
|
||
|
|
||
|
test('should not modify statements that already have LIMIT', () => {
|
||
|
const sql = 'SELECT * FROM users LIMIT 10';
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe('SELECT * FROM users LIMIT 10');
|
||
|
});
|
||
|
|
||
|
test('should handle statements with semicolons', () => {
|
||
|
const sql = 'SELECT * FROM users;';
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe('SELECT * FROM users LIMIT 25;');
|
||
|
});
|
||
|
|
||
|
test('should preserve existing LIMIT with semicolon', () => {
|
||
|
const sql = 'SELECT * FROM users LIMIT 50;';
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe('SELECT * FROM users LIMIT 50;');
|
||
|
});
|
||
|
|
||
|
test('should handle SQL with JOIN statements', () => {
|
||
|
const sql = `
|
||
|
SELECT u.id, u.name, o.order_id
|
||
|
FROM users u
|
||
|
JOIN orders o ON u.id = o.user_id
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result.trim()).toBe(`SELECT u.id, u.name, o.order_id
|
||
|
FROM users u
|
||
|
JOIN orders o ON u.id = o.user_id LIMIT 25`);
|
||
|
});
|
||
|
|
||
|
test('should handle SQL with GROUP BY', () => {
|
||
|
const sql = `
|
||
|
SELECT department, COUNT(*) as count
|
||
|
FROM employees
|
||
|
GROUP BY department
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result.trim()).toBe(`SELECT department, COUNT(*) as count
|
||
|
FROM employees
|
||
|
GROUP BY department LIMIT 25`);
|
||
|
});
|
||
|
|
||
|
test('should handle SQL with GROUP BY and HAVING', () => {
|
||
|
const sql = `
|
||
|
SELECT department, COUNT(*) as count
|
||
|
FROM employees
|
||
|
GROUP BY department
|
||
|
HAVING COUNT(*) > 10
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result.trim()).toBe(`SELECT department, COUNT(*) as count
|
||
|
FROM employees
|
||
|
GROUP BY department
|
||
|
HAVING COUNT(*) > 10 LIMIT 25`);
|
||
|
});
|
||
|
|
||
|
test('should handle SQL with ORDER BY', () => {
|
||
|
const sql = 'SELECT * FROM users ORDER BY created_at DESC';
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe('SELECT * FROM users ORDER BY created_at DESC LIMIT 25');
|
||
|
});
|
||
|
|
||
|
test('should handle complex query with multiple clauses', () => {
|
||
|
const sql = `
|
||
|
SELECT u.id, u.name, COUNT(o.id) as order_count
|
||
|
FROM users u
|
||
|
LEFT JOIN orders o ON u.id = o.user_id
|
||
|
WHERE u.active = true
|
||
|
GROUP BY u.id, u.name
|
||
|
HAVING COUNT(o.id) > 5
|
||
|
ORDER BY order_count DESC
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result.trim()).toBe(`SELECT u.id, u.name, COUNT(o.id) as order_count
|
||
|
FROM users u
|
||
|
LEFT JOIN orders o ON u.id = o.user_id
|
||
|
WHERE u.active = true
|
||
|
GROUP BY u.id, u.name
|
||
|
HAVING COUNT(o.id) > 5
|
||
|
ORDER BY order_count DESC LIMIT 25`);
|
||
|
});
|
||
|
|
||
|
test('should use custom limit when provided', () => {
|
||
|
const sql = 'SELECT * FROM users';
|
||
|
const result = ensureSqlLimit(sql, 100);
|
||
|
expect(result).toBe('SELECT * FROM users LIMIT 100');
|
||
|
});
|
||
|
|
||
|
test('should handle whitespace variations', () => {
|
||
|
const sql = ' SELECT * FROM users ';
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe('SELECT * FROM users LIMIT 25');
|
||
|
});
|
||
|
|
||
|
test('should handle case variations of LIMIT', () => {
|
||
|
const testCases = [
|
||
|
'SELECT * FROM users limit 10',
|
||
|
'SELECT * FROM users Limit 10',
|
||
|
'SELECT * FROM users LIMIT 10',
|
||
|
];
|
||
|
|
||
|
for (const sql of testCases) {
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe(sql);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('should not modify non-SELECT statements', () => {
|
||
|
const nonSelectQueries = [
|
||
|
'INSERT INTO users (name) VALUES ("John")',
|
||
|
'UPDATE users SET name = "John" WHERE id = 1',
|
||
|
'DELETE FROM users WHERE id = 1',
|
||
|
'CREATE TABLE users (id INT)',
|
||
|
'DROP TABLE users',
|
||
|
];
|
||
|
|
||
|
for (const sql of nonSelectQueries) {
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe(sql);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('should handle empty or invalid input', () => {
|
||
|
expect(ensureSqlLimit('')).toBe('');
|
||
|
expect(ensureSqlLimit(null as never)).toBe(null);
|
||
|
expect(ensureSqlLimit(undefined as never)).toBe(undefined);
|
||
|
expect(ensureSqlLimit(123 as never)).toBe(123);
|
||
|
});
|
||
|
|
||
|
test('should handle SQL with schema and table qualifiers', () => {
|
||
|
const sql = 'SELECT analytics.users.id, analytics.users.name FROM analytics.users';
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result).toBe(
|
||
|
'SELECT analytics.users.id, analytics.users.name FROM analytics.users LIMIT 25'
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('should handle SQL with LIMIT in different positions', () => {
|
||
|
// LIMIT with OFFSET
|
||
|
const sql1 = 'SELECT * FROM users LIMIT 10 OFFSET 20';
|
||
|
expect(ensureSqlLimit(sql1)).toBe(sql1);
|
||
|
|
||
|
// LIMIT with comma syntax (MySQL)
|
||
|
const sql2 = 'SELECT * FROM users LIMIT 20, 10';
|
||
|
expect(ensureSqlLimit(sql2)).toBe(sql2);
|
||
|
});
|
||
|
|
||
|
test('should handle SQL with comments', () => {
|
||
|
const sql = `
|
||
|
-- Get all active users
|
||
|
SELECT * FROM users
|
||
|
WHERE active = true
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result.includes('LIMIT 25')).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should handle CTEs (WITH clause)', () => {
|
||
|
const sql = `
|
||
|
WITH active_users AS (
|
||
|
SELECT * FROM users WHERE active = true
|
||
|
)
|
||
|
SELECT * FROM active_users
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result.includes('LIMIT 25')).toBe(true);
|
||
|
expect(result.endsWith('LIMIT 25')).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should handle subqueries', () => {
|
||
|
const sql = `
|
||
|
SELECT * FROM (
|
||
|
SELECT * FROM users LIMIT 100
|
||
|
) AS subquery
|
||
|
WHERE age > 18
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
// Should add LIMIT to outer query, not modify inner LIMIT
|
||
|
expect(result.includes('LIMIT 100')).toBe(true);
|
||
|
expect(result.endsWith('LIMIT 25')).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should handle UNION queries', () => {
|
||
|
const sql = `
|
||
|
SELECT id, name FROM users
|
||
|
UNION
|
||
|
SELECT id, name FROM customers
|
||
|
`;
|
||
|
const result = ensureSqlLimit(sql);
|
||
|
expect(result.endsWith('LIMIT 25')).toBe(true);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('ensureSqlLimitsForMultiple', () => {
|
||
|
test('should process multiple statements', () => {
|
||
|
const statements = [
|
||
|
'SELECT * FROM users',
|
||
|
'SELECT * FROM orders LIMIT 10',
|
||
|
'SELECT * FROM products;',
|
||
|
];
|
||
|
|
||
|
const results = ensureSqlLimitsForMultiple(statements);
|
||
|
|
||
|
expect(results).toEqual([
|
||
|
'SELECT * FROM users LIMIT 25',
|
||
|
'SELECT * FROM orders LIMIT 10',
|
||
|
'SELECT * FROM products LIMIT 25;',
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
test('should handle empty array', () => {
|
||
|
const results = ensureSqlLimitsForMultiple([]);
|
||
|
expect(results).toEqual([]);
|
||
|
});
|
||
|
|
||
|
test('should use custom limit for all statements', () => {
|
||
|
const statements = ['SELECT * FROM users', 'SELECT * FROM orders'];
|
||
|
const results = ensureSqlLimitsForMultiple(statements, 50);
|
||
|
|
||
|
expect(results).toEqual(['SELECT * FROM users LIMIT 50', 'SELECT * FROM orders LIMIT 50']);
|
||
|
});
|
||
|
});
|
||
|
});
|