import { describe, it, expect, vi, beforeEach } from 'vitest';
import { createMockMySQLAdapter, createMockQueryResult, createMockRequestContext } from '../../../../__tests__/mocks/index.js';
import { createHealthResource } from '../health.js';
import type { MySQLAdapter } from '../../MySQLAdapter.js';
describe('Health Resource', () => {
let mockAdapter: ReturnType<typeof createMockMySQLAdapter>;
let mockContext: ReturnType<typeof createMockRequestContext>;
beforeEach(() => {
vi.clearAllMocks();
mockAdapter = createMockMySQLAdapter();
mockContext = createMockRequestContext();
});
it('should return default values when status variables are missing', async () => {
// Empty status result
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
// Missing max_connections (default 151)
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
const resource = createHealthResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://health', mockContext) as any;
expect(result.status).toBe('healthy');
expect(result.uptime_seconds).toBe(0);
expect(result.connections.current).toBe(0);
expect(result.connections.max_allowed).toBe(151); // Default
expect(result.connections.usage_percent).toBe(0);
});
it('should handle zero buffer pool requests (avoid div by zero)', async () => {
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([
{ Variable_name: 'Innodb_buffer_pool_read_requests', Value: '0' },
{ Variable_name: 'Innodb_buffer_pool_reads', Value: '0' }
]));
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([{ Value: '100' }]));
const resource = createHealthResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://health', mockContext) as any;
expect(result.performance.buffer_pool_hit_ratio).toBe(100);
});
it('should handle zero table lock waits (avoid div by zero)', async () => {
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([
{ Variable_name: 'Table_locks_waited', Value: '0' },
{ Variable_name: 'Table_locks_immediate', Value: '0' }
]));
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([{ Value: '100' }]));
const resource = createHealthResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://health', mockContext) as any;
expect(result.performance.table_lock_contention_percent).toBe(0);
});
it('should calculate metrics correctly with values', async () => {
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([
{ Variable_name: 'Uptime', Value: '3600' },
{ Variable_name: 'Threads_connected', Value: '50' },
{ Variable_name: 'Innodb_buffer_pool_read_requests', Value: '100' },
{ Variable_name: 'Innodb_buffer_pool_reads', Value: '10' }, // 90% hit ratio
{ Variable_name: 'Table_locks_waited', Value: '20' },
{ Variable_name: 'Table_locks_immediate', Value: '80' } // 20% contention
]));
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([{ Value: '100' }])); // Max connections
const resource = createHealthResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://health', mockContext) as any;
expect(result.uptime_seconds).toBe(3600);
expect(result.connections.usage_percent).toBe(50); // 50/100
expect(result.performance.buffer_pool_hit_ratio).toBe(90);
expect(result.performance.table_lock_contention_percent).toBe(20);
});
it('should include pool stats if available', async () => {
mockAdapter.executeQuery.mockResolvedValue(createMockQueryResult([]));
const mockPool = {
getStats: vi.fn().mockReturnValue({ total: 10, active: 5, idle: 5 })
};
(mockAdapter.getPool as any).mockReturnValue(mockPool);
const resource = createHealthResource(mockAdapter as unknown as MySQLAdapter);
const result = await resource.handler('mysql://health', mockContext) as any;
expect(result.pool).toEqual({ total: 10, active: 5, idle: 5 });
});
});