import { describe, it, expect, vi, beforeEach } from 'vitest';
import { createMockMySQLAdapter, createMockQueryResult, createMockRequestContext } from '../../../../__tests__/mocks/index.js';
import type { MySQLAdapter } from '../../MySQLAdapter.js';
import { createLocksResource } from '../locks.js';
describe('Locks Resource', () => {
let mockAdapter: ReturnType<typeof createMockMySQLAdapter>;
let mockContext: ReturnType<typeof createMockRequestContext>;
beforeEach(() => {
vi.clearAllMocks();
mockAdapter = createMockMySQLAdapter();
mockContext = createMockRequestContext();
});
it('should return lock information including waits and statistics', async () => {
// Mock data_lock_waits query
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([
{
waiting_trx_id: '100',
waiting_thread: 1,
waiting_query: 'SELECT * FROM users FOR UPDATE',
blocking_trx_id: '101',
blocking_thread: 2,
blocking_query: 'UPDATE users SET name = "test"',
wait_seconds: 5
}
]));
// Mock Innodb_row_lock% status query
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([
{ Variable_name: 'Innodb_row_lock_current_waits', Value: '1' },
{ Variable_name: 'Innodb_row_lock_time', Value: '5000' }
]));
const resource = createLocksResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://locks', mockContext) as any;
expect(result.currentLockWaits).toBe(1);
expect(result.lockWaits).toHaveLength(1);
expect(result.lockWaits[0]).toHaveProperty('waiting_trx_id', '100');
expect(result.lockStatistics).toHaveProperty('Innodb_row_lock_current_waits', '1');
});
it('should handle errors gracefully', async () => {
mockAdapter.executeQuery.mockRejectedValue(new Error('Database error'));
const resource = createLocksResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://locks', mockContext) as any;
expect(result).toHaveProperty('error');
expect(result.currentLockWaits).toBe(0);
expect(result.lockWaits).toHaveLength(0);
});
it('should handle empty lock stats', async () => {
// Mock data_lock_waits query
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
// Mock Innodb_row_lock% status query returning empty or malformed
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
const resource = createLocksResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://locks', mockContext) as any;
expect(result.currentLockWaits).toBe(0);
expect(result.lockStatistics).toEqual({});
});
it('should handle non-string variable names gracefully', async () => {
// Mock data_lock_waits query
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
// Mock Innodb_row_lock% status query with non-string name
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([
{ Variable_name: null, Value: '123' },
{ Variable_name: 123, Value: '456' },
{ Variable_name: 'valid_var', Value: '789' }
]));
const resource = createLocksResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://locks', mockContext) as any;
expect(result.lockStatistics).toEqual({
'valid_var': '789'
});
});
it('should handle undefined rows from query results gracefully', async () => {
// Mock data_lock_waits query returning valid result object but with undefined rows
mockAdapter.executeQuery.mockResolvedValueOnce({
rows: undefined,
rowsAffected: 0,
executionTimeMs: 0
});
// Mock Innodb_row_lock% status query returning valid result but undefined rows
mockAdapter.executeQuery.mockResolvedValueOnce({
rows: undefined,
rowsAffected: 0,
executionTimeMs: 0
});
const resource = createLocksResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://locks', mockContext) as any;
expect(result.currentLockWaits).toBe(0);
expect(result.lockWaits).toEqual([]);
expect(result.lockStatistics).toEqual({});
});
});