First commit

This commit is contained in:
CyberRex
2026-05-23 17:03:05 +09:00
commit 40e7953ee5
52 changed files with 13004 additions and 0 deletions

225
tests/monitoring.test.js Normal file
View File

@@ -0,0 +1,225 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { runCertificateMonitoring } from '../src/server/modules/monitoring/monitor.js';
import { pool } from '../src/server/db/pool.js';
import { getCertificateExpiry } from '../src/server/modules/monitoring/certificate.js';
import { deliverNotifications } from '../src/server/modules/monitoring/notifications.js';
const mocks = vi.hoisted(() => ({
client: {
query: vi.fn(),
release: vi.fn(),
},
getCertificateExpiry: vi.fn(),
deliverNotifications: vi.fn(),
}));
vi.mock('../src/server/db/pool.js', () => ({
pool: {
connect: vi.fn(async () => mocks.client),
},
}));
vi.mock('../src/server/modules/monitoring/certificate.js', () => ({
getCertificateExpiry: mocks.getCertificateExpiry,
}));
vi.mock('../src/server/modules/monitoring/notifications.js', () => ({
deliverNotifications: mocks.deliverNotifications,
}));
const SITE_ID = '22222222-2222-4222-8222-222222222222';
const USER_ID = '11111111-1111-4111-8111-111111111111';
const WEBHOOK_ID = '44444444-4444-4444-8444-444444444444';
describe('certificate monitoring', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('stores the latest certificate expiry and creates an alert when a threshold matches', async () => {
const issuer = 'C = US, O = Example CA, CN = Example Root';
const issuedAt = new Date('2026-01-01T00:00:00.000Z');
const expiresAt = new Date(Date.now() + 12 * 60 * 60 * 1000);
getCertificateExpiry.mockResolvedValue({
issuer,
issuedAt,
expiresAt,
hoursUntilExpiry: 12,
});
deliverNotifications.mockResolvedValue({
webhook: [{ ok: true }],
push: { ok: true },
});
mocks.client.query.mockImplementation(async (sql, params) => {
if (sql.includes('FROM sites s') && sql.includes('LEFT JOIN site_alert_conditions')) {
return {
rows: [
{
site_id: SITE_ID,
user_id: USER_ID,
url: 'https://example.com/',
alias: 'Example',
conditions: [
{
site_alert_condition_id: '55555555-5555-4555-8555-555555555555',
threshold_hours: 24,
webhook_method_ids: [WEBHOOK_ID],
push_enabled: true,
},
],
},
],
};
}
if (sql.includes('SET certificate_issuer')) {
expect(sql).toContain('certificate_issued_at');
expect(sql).toContain('certificate_expires_at');
expect(params).toEqual([SITE_ID, issuer, issuedAt, expiresAt]);
return { rows: [] };
}
if (sql.includes('FROM notification_methods') && sql.includes("notification_type = 'webhook'")) {
expect(params).toEqual([USER_ID, [WEBHOOK_ID]]);
return {
rows: [
{
notification_method_id: WEBHOOK_ID,
alias: 'Deploy hook',
url: 'https://hooks.example.com/',
},
],
};
}
if (sql.includes('FROM notification_methods') && sql.includes("notification_type = 'push'")) {
expect(params).toEqual([USER_ID]);
return {
rows: [
{
notification_method_id: '66666666-6666-4666-8666-666666666666',
push_endpoint: 'https://push.example.com/subscription',
push_p256dh: 'p256dh',
push_auth: 'auth',
},
],
};
}
if (sql.includes('INSERT INTO alert_history')) {
expect(params[0]).toBe(USER_ID);
expect(params[1]).toBe(SITE_ID);
expect(params[2]).toBe('certificate_expiring');
expect(params[4]).toEqual(['app', 'webhook', 'push']);
return { rows: [{ alert_id: '77777777-7777-4777-8777-777777777777' }] };
}
throw new Error(`Unexpected query: ${sql}`);
});
const result = await runCertificateMonitoring({ concurrency: 1 });
expect(pool.connect).toHaveBeenCalledOnce();
expect(getCertificateExpiry).toHaveBeenCalledWith('https://example.com/');
expect(deliverNotifications).toHaveBeenCalledOnce();
expect(mocks.client.release).toHaveBeenCalledOnce();
expect(result).toMatchObject({
checkedSites: 1,
alertsCreated: 1,
results: [{ siteId: SITE_ID, ok: true, alertsCreated: 1 }],
});
});
it('stores certificate check errors without clearing the previous expiry', async () => {
const error = new Error('openssl failed');
getCertificateExpiry.mockRejectedValue(error);
mocks.client.query.mockImplementation(async (sql, params) => {
if (sql.includes('FROM sites s') && sql.includes('LEFT JOIN site_alert_conditions')) {
return {
rows: [
{
site_id: SITE_ID,
user_id: USER_ID,
url: 'https://example.com/',
alias: 'Example',
conditions: [],
},
],
};
}
if (sql.includes('SET certificate_checked_at') && sql.includes('certificate_check_error = $2')) {
expect(sql).not.toContain('certificate_expires_at');
expect(params).toEqual([SITE_ID, error.message]);
return { rows: [] };
}
if (sql.includes('INSERT INTO alert_history')) {
expect(params[2]).toBe('certificate_check_failed');
expect(JSON.parse(params[5])).toMatchObject({ error: error.message });
return { rows: [{ alert_id: '77777777-7777-4777-8777-777777777777' }] };
}
throw new Error(`Unexpected query: ${sql}`);
});
const result = await runCertificateMonitoring({ concurrency: 1 });
expect(result).toMatchObject({
checkedSites: 1,
alertsCreated: 1,
results: [{ siteId: SITE_ID, ok: false, error: error.message }],
});
});
it('checks and stores certificate expiry for sites without alert conditions', async () => {
const issuer = 'C = US, O = Example CA, CN = Example Root';
const issuedAt = new Date('2026-01-01T00:00:00.000Z');
const expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000);
getCertificateExpiry.mockResolvedValue({
issuer,
issuedAt,
expiresAt,
hoursUntilExpiry: 90 * 24,
});
mocks.client.query.mockImplementation(async (sql, params) => {
if (sql.includes('FROM sites s') && sql.includes('LEFT JOIN site_alert_conditions')) {
return {
rows: [
{
site_id: SITE_ID,
user_id: USER_ID,
url: 'https://example.com/',
alias: 'Example',
conditions: [],
},
],
};
}
if (sql.includes('SET certificate_issuer')) {
expect(sql).toContain('certificate_issued_at');
expect(sql).toContain('certificate_expires_at');
expect(params).toEqual([SITE_ID, issuer, issuedAt, expiresAt]);
return { rows: [] };
}
throw new Error(`Unexpected query: ${sql}`);
});
const result = await runCertificateMonitoring({ concurrency: 1 });
expect(getCertificateExpiry).toHaveBeenCalledWith('https://example.com/');
expect(deliverNotifications).not.toHaveBeenCalled();
expect(result).toMatchObject({
checkedSites: 1,
alertsCreated: 0,
results: [{ siteId: SITE_ID, ok: true, alertsCreated: 0 }],
});
});
});