tressi
Version:
A lightweight, declarative stress testing CLI for modern developers.
170 lines (128 loc) • 5.18 kB
text/typescript
import { MockAgent, setGlobalDispatcher } from 'undici';
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
import { RequestConfig } from '../src/config';
import { RunOptions } from '../src/index';
import { Runner } from '../src/runner';
let mockAgent: MockAgent;
beforeAll(() => {
mockAgent = new MockAgent();
mockAgent.disableNetConnect();
setGlobalDispatcher(mockAgent);
});
afterEach(() => {
mockAgent.assertNoPendingInterceptors();
});
afterAll(() => {
mockAgent.close();
});
const baseOptions: RunOptions = {
config: { requests: [] },
workers: 1,
durationSec: 1,
};
const baseRequests: RequestConfig[] = [
{ url: 'http://localhost:8080/test', method: 'GET' },
];
describe('Object Allocation Optimizations', () => {
describe('Endpoint Key Caching', () => {
it('should handle multiple endpoints correctly', async () => {
const mockPool = mockAgent.get('http://localhost:8080');
mockPool.intercept({ path: '/test1', method: 'GET' }).reply(200);
mockPool.intercept({ path: '/test2', method: 'POST' }).reply(201);
const requests: RequestConfig[] = [
{ url: 'http://localhost:8080/test1', method: 'GET' },
{ url: 'http://localhost:8080/test2', method: 'POST' },
];
const runner = new Runner(baseOptions, requests, {});
await runner.run();
const results = runner.getSampledResults();
expect(results.length).toBeGreaterThan(0);
const urls = new Set(results.map((r) => r.url));
expect(urls.has('http://localhost:8080/test1')).toBe(true);
expect(urls.has('http://localhost:8080/test2')).toBe(true);
});
});
describe('Response Sampling', () => {
it('should sample different status codes per endpoint', async () => {
const mockPool = mockAgent.get('http://localhost:8080');
mockPool.intercept({ path: '/test', method: 'GET' }).reply(200);
mockPool.intercept({ path: '/test', method: 'GET' }).reply(404);
mockPool.intercept({ path: '/test', method: 'GET' }).reply(500);
const runner = new Runner(baseOptions, baseRequests, {});
await runner.run();
const results = runner.getSampledResults();
expect(results.length).toBeGreaterThan(0);
const statusCodes = new Set(results.map((r) => r.status));
expect(statusCodes.size).toBeGreaterThan(0);
});
});
describe('Error Handling', () => {
it('should handle error scenarios gracefully', async () => {
const mockPool = mockAgent.get('http://localhost:8080');
mockPool.intercept({ path: '/test', method: 'GET' }).reply(500);
const runner = new Runner(baseOptions, baseRequests, {});
await runner.run();
const results = runner.getSampledResults();
expect(results.length).toBeGreaterThan(0);
expect(results.some((r) => !r.success)).toBe(true);
});
it('should handle network errors gracefully', async () => {
const mockPool = mockAgent.get('http://localhost:8080');
mockPool
.intercept({ path: '/test', method: 'GET' })
.replyWithError(new Error('Network error'));
const runner = new Runner(baseOptions, baseRequests, {});
await runner.run();
const results = runner.getSampledResults();
expect(results.length).toBeGreaterThan(0);
expect(results.some((r) => r.error)).toBe(true);
});
});
describe('Concurrency and Thread Safety', () => {
it('should handle multiple workers correctly', async () => {
const mockPool = mockAgent.get('http://localhost:8080');
mockPool.intercept({ path: '/test', method: 'GET' }).reply(200).times(20);
const options = { ...baseOptions, workers: 3 };
const runner = new Runner(options, baseRequests, {});
await runner.run();
const results = runner.getSampledResults();
expect(results.length).toBeGreaterThan(0);
});
it('should handle high concurrency with optimizations', async () => {
const mockPool = mockAgent.get('http://localhost:8080');
mockPool.intercept({ path: '/test', method: 'GET' }).reply(200).times(50);
const options = {
...baseOptions,
workers: 5,
durationSec: 2,
rps: 25,
};
const runner = new Runner(options, baseRequests, {});
await runner.run();
const results = runner.getSampledResults();
expect(results.length).toBeGreaterThan(0);
});
});
describe('Memory Management', () => {
it('should complete test without memory leaks', async () => {
const mockPool = mockAgent.get('http://localhost:8080');
mockPool
.intercept({ path: '/test', method: 'GET' })
.reply(200)
.times(100);
const options = {
...baseOptions,
workers: 2,
durationSec: 3,
rps: 50,
};
const runner = new Runner(options, baseRequests, {});
await runner.run();
const results = runner.getSampledResults();
expect(results.length).toBeGreaterThan(0);
// Verify cleanup happened
const histograms = runner.getEndpointHistograms();
expect(histograms.size).toBeGreaterThan(0);
});
});
});