Files
Autonomous-Bug-Explorer/tests/plugins/networkChaos.test.ts

290 lines
9.4 KiB
TypeScript

/**
* Tests for network chaos: applyNetworkCondition (via PlaywrightAgent private method)
* and route interception for blocked/slow endpoints.
*
* Since applyNetworkChaos is private, we test it indirectly via PlaywrightAgent
* by mocking the CDP session and route handler.
*/
import { NETWORK_PROFILES } from '../../src/core/ExplorationConfig';
// ─── NETWORK_PROFILES constants ───────────────────────────────────────────────
describe('NETWORK_PROFILES', () => {
it('defines fast-3g profile', () => {
const p = NETWORK_PROFILES['fast-3g'];
expect(p).toBeDefined();
expect(p!.offline).toBe(false);
expect(p!.downloadKbps).toBe(1500);
expect(p!.latencyMs).toBe(40);
});
it('defines slow-3g profile', () => {
const p = NETWORK_PROFILES['slow-3g'];
expect(p).toBeDefined();
expect(p!.downloadKbps).toBe(400);
expect(p!.latencyMs).toBe(400);
});
it('defines 2g profile', () => {
const p = NETWORK_PROFILES['2g'];
expect(p).toBeDefined();
expect(p!.downloadKbps).toBe(50);
expect(p!.latencyMs).toBe(800);
});
it('defines offline profile', () => {
const p = NETWORK_PROFILES['offline'];
expect(p).toBeDefined();
expect(p!.offline).toBe(true);
expect(p!.downloadKbps).toBe(0);
});
it('returns null for "none" profile', () => {
expect(NETWORK_PROFILES['none']).toBeNull();
});
it('covers all expected profile keys', () => {
const expected = ['fast-3g', 'slow-3g', '2g', 'offline', 'none'] as const;
for (const key of expected) {
expect(NETWORK_PROFILES).toHaveProperty(key);
}
});
});
// ─── applyNetworkCondition via mocked CDP ─────────────────────────────────────
describe('PlaywrightAgent network chaos via CDP mock', () => {
const cdpSendMock = jest.fn().mockResolvedValue(undefined);
const routeMock = jest.fn().mockResolvedValue(undefined);
const newCDPSessionMock = jest.fn().mockResolvedValue({ send: cdpSendMock });
function buildMockContext() {
return { newCDPSession: newCDPSessionMock };
}
function buildMockPage(url = 'http://localhost') {
return {
url: () => url,
route: routeMock,
goto: jest.fn().mockResolvedValue(undefined),
waitForTimeout: jest.fn().mockResolvedValue(undefined),
evaluate: jest.fn().mockResolvedValue('<body></body>'),
title: jest.fn().mockResolvedValue('Test'),
on: jest.fn(),
};
}
beforeEach(() => {
cdpSendMock.mockClear();
routeMock.mockClear();
newCDPSessionMock.mockClear();
});
it('calls CDP Network.emulateNetworkConditions for fast-3g', async () => {
const { PlaywrightAgent } = await import('../../src/plugins/agents/PlaywrightAgent');
const agent = new PlaywrightAgent({
explorationConfig: {
networkChaos: {
enabled: true,
profile: 'fast-3g',
blockedEndpoints: [],
slowEndpoints: [],
},
},
});
// Inject mocked internals
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(agent as any).context = buildMockContext();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const page = buildMockPage();
// Call applyNetworkChaos via a private method trick
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (agent as any).applyNetworkChaos(page);
expect(newCDPSessionMock).toHaveBeenCalledWith(page);
expect(cdpSendMock).toHaveBeenCalledWith('Network.emulateNetworkConditions', expect.objectContaining({
offline: false,
latency: 40,
}));
});
it('calls CDP with offline:true for offline profile', async () => {
const { PlaywrightAgent } = await import('../../src/plugins/agents/PlaywrightAgent');
const agent = new PlaywrightAgent({
explorationConfig: {
networkChaos: {
enabled: true,
profile: 'offline',
blockedEndpoints: [],
slowEndpoints: [],
},
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(agent as any).context = buildMockContext();
const page = buildMockPage();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (agent as any).applyNetworkChaos(page);
expect(cdpSendMock).toHaveBeenCalledWith('Network.emulateNetworkConditions', expect.objectContaining({
offline: true,
downloadThroughput: -1,
uploadThroughput: -1,
}));
});
it('sets up route interception for blocked endpoints', async () => {
const { PlaywrightAgent } = await import('../../src/plugins/agents/PlaywrightAgent');
const agent = new PlaywrightAgent({
explorationConfig: {
networkChaos: {
enabled: true,
profile: 'none',
blockedEndpoints: ['*/api/analytics'],
slowEndpoints: [],
},
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(agent as any).context = buildMockContext();
const page = buildMockPage();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (agent as any).applyNetworkChaos(page);
expect(routeMock).toHaveBeenCalledWith('**/*', expect.any(Function));
});
it('sets up route interception for slow endpoints', async () => {
const { PlaywrightAgent } = await import('../../src/plugins/agents/PlaywrightAgent');
const agent = new PlaywrightAgent({
explorationConfig: {
networkChaos: {
enabled: true,
profile: 'none',
blockedEndpoints: [],
slowEndpoints: [{ pattern: '*/api/slow', delayMs: 2000 }],
},
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(agent as any).context = buildMockContext();
const page = buildMockPage();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (agent as any).applyNetworkChaos(page);
expect(routeMock).toHaveBeenCalledWith('*/api/slow', expect.any(Function));
});
it('does nothing when networkChaos is disabled', async () => {
const { PlaywrightAgent } = await import('../../src/plugins/agents/PlaywrightAgent');
const agent = new PlaywrightAgent({
explorationConfig: {
networkChaos: { enabled: false, profile: 'fast-3g', blockedEndpoints: [], slowEndpoints: [] },
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(agent as any).context = buildMockContext();
const page = buildMockPage();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (agent as any).applyNetworkChaos(page);
expect(cdpSendMock).not.toHaveBeenCalled();
expect(routeMock).not.toHaveBeenCalled();
});
it('fulfills with 503 for requests matching blocked endpoint patterns', async () => {
const { PlaywrightAgent } = await import('../../src/plugins/agents/PlaywrightAgent');
const agent = new PlaywrightAgent({
explorationConfig: {
networkChaos: {
enabled: true,
profile: 'none',
blockedEndpoints: ['*/api/blocked'],
slowEndpoints: [],
},
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(agent as any).context = buildMockContext();
// Capture the route handler
let capturedHandler: ((route: unknown) => void) | null = null;
const page = {
...buildMockPage(),
route: jest.fn().mockImplementation((_pattern: unknown, handler: (r: unknown) => void) => {
capturedHandler = handler;
}),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (agent as any).applyNetworkChaos(page);
expect(capturedHandler).not.toBeNull();
// Simulate a request to a blocked URL
const fulfillMock = jest.fn();
const continueMock = jest.fn();
const blockedRoute = {
request: () => ({ url: () => 'http://example.com/api/blocked' }),
fulfill: fulfillMock,
continue: continueMock,
};
capturedHandler!(blockedRoute);
expect(fulfillMock).toHaveBeenCalledWith(expect.objectContaining({ status: 503 }));
expect(continueMock).not.toHaveBeenCalled();
});
it('continues requests that do not match blocked patterns', async () => {
const { PlaywrightAgent } = await import('../../src/plugins/agents/PlaywrightAgent');
const agent = new PlaywrightAgent({
explorationConfig: {
networkChaos: {
enabled: true,
profile: 'none',
blockedEndpoints: ['*/api/blocked'],
slowEndpoints: [],
},
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(agent as any).context = buildMockContext();
let capturedHandler: ((route: unknown) => void) | null = null;
const page = {
...buildMockPage(),
route: jest.fn().mockImplementation((_pattern: unknown, handler: (r: unknown) => void) => {
capturedHandler = handler;
}),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (agent as any).applyNetworkChaos(page);
const fulfillMock = jest.fn();
const continueMock = jest.fn();
const allowedRoute = {
request: () => ({ url: () => 'http://example.com/api/users' }),
fulfill: fulfillMock,
continue: continueMock,
};
capturedHandler!(allowedRoute);
expect(continueMock).toHaveBeenCalled();
expect(fulfillMock).not.toHaveBeenCalled();
});
});