Files
atalaya/src/db.test.ts
dcarrillo 3882a1941a Kick off (#1)
* Kick off
* Update LICENSE copyright
2026-04-11 13:22:36 +02:00

156 lines
4.3 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { getMonitorStates, writeCheckResults, updateMonitorStates, recordAlert } from './db.js';
function createMockDatabase() {
const mockRun = vi.fn().mockResolvedValue({});
const mockAll = vi.fn().mockResolvedValue({ results: [] });
const mockBind = vi.fn().mockReturnThis();
const mockStmt = {
bind: mockBind,
run: mockRun,
all: mockAll,
};
const mockPrepare = vi.fn().mockReturnValue(mockStmt);
const mockBatch = vi.fn().mockResolvedValue([]);
type MockDb = D1Database & {
_mockStmt: typeof mockStmt;
_mockBind: typeof mockBind;
_mockAll: typeof mockAll;
_mockRun: typeof mockRun;
};
return {
prepare: mockPrepare,
batch: mockBatch,
_mockStmt: mockStmt,
_mockBind: mockBind,
_mockAll: mockAll,
_mockRun: mockRun,
} as unknown as MockDb;
}
describe('getMonitorStates', () => {
it('returns empty array when no states exist', async () => {
const db = createMockDatabase();
const result = await getMonitorStates(db);
expect(result).toEqual([]);
});
it('returns monitor states from database', async () => {
const db = createMockDatabase();
const mockStates = [
{
monitor_name: 'test-monitor',
current_status: 'up',
consecutive_failures: 0,
last_status_change: 1_700_000_000,
last_checked: 1_700_001_000,
},
];
db._mockAll.mockResolvedValue({ results: mockStates });
const result = await getMonitorStates(db);
expect(result).toEqual(mockStates);
expect(db.prepare).toHaveBeenCalledWith(expect.stringContaining('SELECT'));
});
});
describe('writeCheckResults', () => {
it('does nothing when writes array is empty', async () => {
const db = createMockDatabase();
await writeCheckResults(db, []);
expect(db.batch).not.toHaveBeenCalled();
});
it('batches writes to database', async () => {
const db = createMockDatabase();
const writes = [
{
monitorName: 'test-monitor',
checkedAt: 1_700_000_000,
status: 'up',
responseTimeMs: 150,
errorMessage: '',
attempts: 1,
},
{
monitorName: 'test-monitor-2',
checkedAt: 1_700_000_000,
status: 'down',
responseTimeMs: 5000,
errorMessage: 'Timeout',
attempts: 3,
},
];
await writeCheckResults(db, writes);
expect(db.prepare).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO check_results'));
expect(db.batch).toHaveBeenCalledTimes(1);
expect(db._mockBind).toHaveBeenCalledTimes(2);
});
});
describe('updateMonitorStates', () => {
it('does nothing when updates array is empty', async () => {
const db = createMockDatabase();
await updateMonitorStates(db, []);
expect(db.batch).not.toHaveBeenCalled();
});
it('batches state updates to database', async () => {
const db = createMockDatabase();
const updates = [
{
monitorName: 'test-monitor',
currentStatus: 'down',
consecutiveFailures: 3,
lastStatusChange: 1_700_000_000,
lastChecked: 1_700_001_000,
},
];
await updateMonitorStates(db, updates);
expect(db.prepare).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO monitor_state'));
expect(db.prepare).toHaveBeenCalledWith(expect.stringContaining('ON CONFLICT'));
expect(db.batch).toHaveBeenCalledTimes(1);
});
});
describe('recordAlert', () => {
it('inserts alert record', async () => {
const db = createMockDatabase();
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-01-15T12:00:00Z'));
await recordAlert(db, 'test-monitor', 'down', 'slack', true);
expect(db.prepare).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO alerts'));
expect(db._mockBind).toHaveBeenCalledWith(
'test-monitor',
'down',
expect.any(Number),
'slack',
1
);
expect(db._mockRun).toHaveBeenCalled();
vi.useRealTimers();
});
it('records failure correctly', async () => {
const db = createMockDatabase();
await recordAlert(db, 'test-monitor', 'recovery', 'discord', false);
expect(db._mockBind).toHaveBeenCalledWith(
'test-monitor',
'recovery',
expect.any(Number),
'discord',
0
);
});
});