UNPKG

tressi

Version:

A lightweight, declarative stress testing CLI for modern developers.

174 lines (158 loc) 4.9 kB
import { build, Histogram } from 'hdr-histogram-js'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { RunOptions } from '../src'; import { TressiConfig } from '../src/config'; import { Distribution } from '../src/distribution'; import { Runner } from '../src/runner'; import { generateMarkdownReport, generateSummary } from '../src/summarizer'; const createHistogram = (latencies: number[]): Histogram => { const histogram = build(); for (const l of latencies) { histogram.recordValue(l); } return histogram; }; const mockRunner = { getSampledResults: () => [ { method: 'GET', url: 'http://a.com', latencyMs: 100, status: 200, success: true, timestamp: 1, }, { method: 'GET', url: 'http://a.com', latencyMs: 150, status: 200, success: true, timestamp: 2, }, { method: 'GET', url: 'http://b.com', latencyMs: 200, status: 200, success: true, timestamp: 3, }, { method: 'GET', url: 'http://b.com', latencyMs: 500, status: 500, success: false, error: 'Error', timestamp: 4, }, ], getHistogram: () => createHistogram([100, 150, 200, 500]), getEndpointHistograms: () => { const map = new Map<string, Histogram>(); map.set('GET http://a.com', createHistogram([100, 150])); map.set('GET http://b.com', createHistogram([200, 500])); return map; }, getStatusCodeMap: () => ({ 200: 3, 500: 1 }), getSuccessfulRequestsCount: () => 3, getFailedRequestsCount: () => 1, getAverageLatency: () => 237.5, getDistribution: () => { const distribution = new Distribution(); [100, 150, 200, 500].forEach((l) => distribution.add(l)); return distribution; }, getSuccessfulRequestsByEndpoint: () => { const map = new Map<string, number>(); map.set('GET http://a.com', 2); map.set('GET http://b.com', 1); return map; }, getFailedRequestsByEndpoint: () => { const map = new Map<string, number>(); map.set('GET http://a.com', 0); map.set('GET http://b.com', 1); return map; }, } as unknown as Runner; const mockOptions: RunOptions = { config: { requests: [] }, durationSec: 10, rps: 1, // 10 requests total theoretical }; const mockConfig: TressiConfig = { requests: [{ url: 'http://a.com', method: 'GET' }], }; /** * Test suite for the summary generation logic. */ describe('summarizer', () => { beforeAll(() => { vi.useFakeTimers(); vi.setSystemTime(new Date('2023-01-01T03:00:00.000Z')); }); afterAll(() => { vi.useRealTimers(); }); /** * It should correctly calculate all global statistics for a given set of results. */ it('should generate an accurate global summary', () => { const summary = generateSummary(mockRunner, mockOptions, 10); const { global: g } = summary; expect(g.totalRequests).toBe(4); expect(g.successfulRequests).toBe(3); expect(g.failedRequests).toBe(1); expect(g.avgLatencyMs).toBeCloseTo(237.5); expect(g.minLatencyMs).toBe(100); expect(g.maxLatencyMs).toBe(500); expect(g.p95LatencyMs).toBe(500); expect(g.actualRps).toBe(0.4); expect(g.theoreticalMaxRps).toBeCloseTo(1); expect(g.achievedPercentage).toBeCloseTo(40); }); /** * It should correctly aggregate results by URL and calculate statistics for each endpoint. */ it('should generate an accurate summary for each endpoint', () => { const summary = generateSummary(mockRunner, mockOptions); const { endpoints: e } = summary; expect(e).toHaveLength(2); const summaryA = e.find((s) => s.url === 'http://a.com'); expect(summaryA).toBeDefined(); expect(summaryA?.method).toBe('GET'); expect(summaryA?.totalRequests).toBe(2); expect(summaryA?.successfulRequests).toBe(2); expect(summaryA?.failedRequests).toBe(0); expect(summaryA?.avgLatencyMs).toBe(125); expect(summaryA?.p95LatencyMs).toBe(150); const summaryB = e.find((s) => s.url === 'http://b.com'); expect(summaryB).toBeDefined(); expect(summaryB?.method).toBe('GET'); expect(summaryB?.totalRequests).toBe(2); expect(summaryB?.successfulRequests).toBe(1); expect(summaryB?.failedRequests).toBe(1); expect(summaryB?.avgLatencyMs).toBe(350); expect(summaryB?.p95LatencyMs).toBe(500); }); /** * It should generate a Markdown report containing all summary sections. */ it('should generate a comprehensive Markdown report', () => { const summary = generateSummary(mockRunner, mockOptions, 10); const metadata = { exportName: 'my-test-report', runDate: new Date(), }; const markdown = generateMarkdownReport( summary, mockOptions, mockRunner, mockConfig, metadata, ); expect(markdown).toMatchSnapshot(); }); });