Add unit tests for P2 SaaS, P3 notifications, P4 search, P5 ingestion, P6 API

- P2: nonce validation, severity levels, RLS withTenant
- P3: notification dispatcher severity gating, Slack Block Kit emoji mapping
- P4: Meilisearch fallback, service CRUD validation, staged update actions
- P5: cost ingestion validation, snooze range, optimistic locking
- P6: runbook API validation, approval decisions, execution status machine, Slack signature
This commit is contained in:
2026-03-01 03:15:31 +00:00
parent 3326d9a714
commit bbbea3519e
5 changed files with 276 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
import { describe, it, expect } from 'vitest';
describe('Runbook API Validation', () => {
it('rejects empty runbook name', () => {
const name = '';
expect(name.length >= 1).toBe(false);
});
it('accepts valid runbook name', () => {
const name = 'restart-ecs-service';
expect(name.length >= 1 && name.length <= 200).toBe(true);
});
it('requires yaml_content', () => {
const body = { name: 'test', description: 'desc' };
expect(body).not.toHaveProperty('yaml_content');
});
});
describe('Approval Decisions', () => {
it('only allows approve or reject', () => {
const valid = ['approve', 'reject'];
expect(valid.includes('approve')).toBe(true);
expect(valid.includes('reject')).toBe(true);
expect(valid.includes('maybe')).toBe(false);
});
it('reason is optional and max 500 chars', () => {
const shortReason = 'Looks safe';
const longReason = 'x'.repeat(501);
expect(shortReason.length <= 500).toBe(true);
expect(longReason.length <= 500).toBe(false);
});
});
describe('Execution Status Machine', () => {
const validStatuses = ['pending', 'running', 'awaiting_approval', 'completed', 'failed', 'aborted'];
it('starts in pending', () => {
expect(validStatuses[0]).toBe('pending');
});
it('validates all status values', () => {
for (const s of validStatuses) {
expect(validStatuses.includes(s)).toBe(true);
}
expect(validStatuses.includes('cancelled')).toBe(false);
});
});
describe('Slack Signature Verification', () => {
it('rejects stale timestamps (>5 min)', () => {
const now = Math.floor(Date.now() / 1000);
const staleTs = now - 301;
expect(Math.abs(now - staleTs) > 300).toBe(true);
});
it('accepts fresh timestamps', () => {
const now = Math.floor(Date.now() / 1000);
const freshTs = now - 10;
expect(Math.abs(now - freshTs) > 300).toBe(false);
});
});