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

109 lines
3.7 KiB
TypeScript

/**
* Unit tests for scope enforcement and auth in PlaywrightAgent.
* These tests use the private helper methods indirectly via the agent's behavior
* by testing the isExcludedPath, isExternalLink, and isAllowedUrl logic
* through subclassing or direct method exposure.
*
* Since these methods are private, we test observable behavior via
* discoverActions and executeAction with mock pages or just verify config logic.
*/
import { ExplorationConfig, DEFAULT_EXPLORATION_CONFIG } from '../../src/core/ExplorationConfig';
describe('ExplorationConfig', () => {
it('has sensible defaults', () => {
const cfg = { ...DEFAULT_EXPLORATION_CONFIG };
expect(cfg.maxStates).toBe(50);
expect(cfg.maxDepth).toBe(5);
expect(cfg.actionDelayMs).toBe(500);
expect(cfg.sessionTimeoutMs).toBe(300000);
expect(cfg.fuzzingEnabled).toBe(true);
expect(cfg.fuzzingIntensity).toBe('medium');
expect(cfg.auth).toBeNull();
expect(cfg.excludedPaths).toEqual([]);
expect(cfg.excludedSelectors).toEqual([]);
});
it('accepts cookies auth config', () => {
const config: ExplorationConfig = {
...DEFAULT_EXPLORATION_CONFIG,
auth: {
type: 'cookies',
cookies: [{ name: 'session', value: 'abc', domain: 'localhost' }],
},
};
expect(config.auth?.type).toBe('cookies');
});
it('accepts headers auth config', () => {
const config: ExplorationConfig = {
...DEFAULT_EXPLORATION_CONFIG,
auth: {
type: 'headers',
headers: { Authorization: 'Bearer token123' },
},
};
expect(config.auth?.type).toBe('headers');
});
it('accepts login_flow auth config', () => {
const config: ExplorationConfig = {
...DEFAULT_EXPLORATION_CONFIG,
auth: {
type: 'login_flow',
loginUrl: 'http://app.com/login',
usernameSelector: 'input[name="email"]',
passwordSelector: 'input[name="password"]',
submitSelector: 'button[type="submit"]',
username: 'user@test.com',
password: 'secret',
},
};
expect(config.auth?.type).toBe('login_flow');
});
});
// Helper to test URL-based scope rules (extracted for testability)
describe('Scope URL rules', () => {
function isExcludedPath(urlOrPath: string, excludedPaths: string[]): boolean {
if (excludedPaths.length === 0) return false;
try {
const parsed = new URL(urlOrPath, 'http://placeholder');
return excludedPaths.some((p) => parsed.pathname.startsWith(p));
} catch {
return false;
}
}
function isExternalLink(href: string, currentUrl: string, allowedDomains: string[]): boolean {
if (allowedDomains.length === 0) return false;
try {
const base = new URL(currentUrl);
const target = new URL(href, base.origin);
return !allowedDomains.includes(target.hostname);
} catch {
return false;
}
}
it('excludes paths correctly', () => {
expect(isExcludedPath('http://app.com/logout', ['/logout'])).toBe(true);
expect(isExcludedPath('http://app.com/home', ['/logout'])).toBe(false);
expect(isExcludedPath('http://app.com/admin/users', ['/admin'])).toBe(true);
});
it('allows paths when no exclusions', () => {
expect(isExcludedPath('http://app.com/logout', [])).toBe(false);
});
it('detects external links', () => {
expect(isExternalLink('http://external.com/page', 'http://myapp.com', ['myapp.com'])).toBe(true);
expect(isExternalLink('/page', 'http://myapp.com', ['myapp.com'])).toBe(false);
expect(isExternalLink('http://myapp.com/page', 'http://myapp.com', ['myapp.com'])).toBe(false);
});
it('allows all links when no allowedDomains', () => {
expect(isExternalLink('http://external.com/page', 'http://myapp.com', [])).toBe(false);
});
});