Files
Charon/tests/fixtures/test-data.ts
akanealw eec8c28fb3
Some checks are pending
Go Benchmark / Performance Regression Check (push) Waiting to run
Cerberus Integration / Cerberus Security Stack Integration (push) Waiting to run
Upload Coverage to Codecov / Backend Codecov Upload (push) Waiting to run
Upload Coverage to Codecov / Frontend Codecov Upload (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (go) (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Waiting to run
CrowdSec Integration / CrowdSec Bouncer Integration (push) Waiting to run
Docker Build, Publish & Test / build-and-push (push) Waiting to run
Docker Build, Publish & Test / Security Scan PR Image (push) Blocked by required conditions
Quality Checks / Auth Route Protection Contract (push) Waiting to run
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Waiting to run
Quality Checks / Backend (Go) (push) Waiting to run
Quality Checks / Frontend (React) (push) Waiting to run
Rate Limit integration / Rate Limiting Integration (push) Waiting to run
Security Scan (PR) / Trivy Binary Scan (push) Waiting to run
Supply Chain Verification (PR) / Verify Supply Chain (push) Waiting to run
WAF integration / Coraza WAF Integration (push) Waiting to run
changed perms
2026-04-22 18:19:14 +00:00

596 lines
15 KiB
TypeScript
Executable File

/**
* Test Data Generators - Common test data factories
*
* This module provides functions to generate realistic test data
* with unique identifiers to prevent collisions in parallel tests.
*
* @example
* ```typescript
* import { generateProxyHostData, generateUserData } from './fixtures/test-data';
*
* test('create proxy host', async ({ testData }) => {
* const hostData = generateProxyHostData();
* const { id } = await testData.createProxyHost(hostData);
* });
* ```
*/
import * as crypto from 'crypto';
/**
* Generate a unique identifier with optional prefix
* Uses timestamp + high-resolution time + random bytes for maximum uniqueness
* @param prefix - Optional prefix for the ID
* @returns Unique identifier string
*
* @example
* ```typescript
* const id = generateUniqueId('test');
* // Returns: "test-m1abc123-0042-deadbeef"
* ```
*/
export function generateUniqueId(prefix = ''): string {
const timestamp = Date.now().toString(36);
// Add high-resolution time component for nanosecond-level uniqueness
const hrTime = process.hrtime.bigint().toString(36).slice(-4);
const random = crypto.randomBytes(4).toString('hex');
return prefix ? `${prefix}-${timestamp}-${hrTime}-${random}` : `${timestamp}-${hrTime}-${random}`;
}
/**
* Generate a unique UUID v4
* @returns UUID string
*
* @example
* ```typescript
* const uuid = generateUUID();
* // Returns: "550e8400-e29b-41d4-a716-446655440000"
* ```
*/
export function generateUUID(): string {
return crypto.randomUUID();
}
/**
* Generate a valid private IP address (RFC 1918)
* Uses 10.x.x.x range to avoid conflicts with local networks
* @param options - Configuration options
* @returns Valid IP address string
*
* @example
* ```typescript
* const ip = generateIPAddress();
* // Returns: "10.42.128.15"
*
* const specificRange = generateIPAddress({ octet2: 100 });
* // Returns: "10.100.x.x"
* ```
*/
export function generateIPAddress(options: {
/** Second octet (0-255), random if not specified */
octet2?: number;
/** Third octet (0-255), random if not specified */
octet3?: number;
/** Fourth octet (1-254), random if not specified */
octet4?: number;
} = {}): string {
const o2 = options.octet2 ?? secureRandomInt(256);
const o3 = options.octet3 ?? secureRandomInt(256);
const o4 = options.octet4 ?? secureRandomInt(253) + 1; // 1-254
return `10.${o2}.${o3}.${o4}`;
}
/**
* Generate a valid CIDR range
* @param prefix - CIDR prefix (8-32)
* @returns Valid CIDR string
*
* @example
* ```typescript
* const cidr = generateCIDR(24);
* // Returns: "10.42.128.0/24"
* ```
*/
export function generateCIDR(prefix: number = 24): string {
const ip = generateIPAddress({ octet4: 0 });
return `${ip}/${prefix}`;
}
/**
* Generate a valid port number
* @param options - Configuration options
* @returns Valid port number
*
* @example
* ```typescript
* const port = generatePort();
* // Returns: 8080-65000 range
*
* const specificRange = generatePort({ min: 3000, max: 4000 });
* // Returns: 3000-4000 range
* ```
*/
export function generatePort(options: {
/** Minimum port (default: 8080) */
min?: number;
/** Maximum port (default: 65000) */
max?: number;
} = {}): number {
const { min = 8080, max = 65000 } = options;
return secureRandomInt(max - min + 1) + min;
}
/**
* Common ports for testing purposes
*/
export const commonPorts = {
http: 80,
https: 443,
ssh: 22,
mysql: 3306,
postgres: 5432,
redis: 6379,
mongodb: 27017,
node: 3000,
react: 3000,
vite: 5173,
backend: 8080,
proxy: 8081,
} as const;
/**
* Generate a password that meets common requirements
* - At least 12 characters
* - Contains uppercase, lowercase, numbers, and special characters
* @param length - Password length (default: 16)
* @returns Strong password string
*
* @example
* ```typescript
* const password = generatePassword();
* // Returns: "Xy7!kM2@pL9#nQ4$"
* ```
*/
function secureRandomInt(maxExclusive: number): number {
if (maxExclusive <= 0) {
throw new Error('maxExclusive must be positive');
}
const maxUint32 = 0xffffffff;
const limit = maxUint32 - ((maxUint32 + 1) % maxExclusive);
while (true) {
const random = crypto.randomBytes(4).readUInt32BE(0);
if (random <= limit) {
return random % maxExclusive;
}
}
}
function shuffleArraySecure<T>(arr: T[]): T[] {
for (let i = arr.length - 1; i > 0; i--) {
const j = secureRandomInt(i + 1);
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
}
export function generatePassword(length: number = 16): string {
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const lower = 'abcdefghijklmnopqrstuvwxyz';
const numbers = '0123456789';
const special = '!@#$%^&*';
const all = upper + lower + numbers + special;
// Ensure at least one of each type
let password = '';
password += upper[secureRandomInt(upper.length)];
password += lower[secureRandomInt(lower.length)];
password += numbers[secureRandomInt(numbers.length)];
password += special[secureRandomInt(special.length)];
// Fill the rest randomly
for (let i = 4; i < length; i++) {
password += all[secureRandomInt(all.length)];
}
// Shuffle the password using a cryptographically secure shuffle
return shuffleArraySecure(password.split('')).join('');
}
/**
* Common test passwords for different scenarios
*/
export const testPasswords = {
/** Valid strong password */
valid: 'TestPass123!',
/** Another valid password */
validAlt: 'SecureP@ss456',
/** Too short */
tooShort: 'Ab1!',
/** No uppercase */
noUppercase: 'password123!',
/** No lowercase */
noLowercase: 'PASSWORD123!',
/** No numbers */
noNumbers: 'Password!!',
/** No special characters */
noSpecial: 'Password123',
/** Common weak password */
weak: 'password',
} as const;
/**
* Counter for additional uniqueness within same millisecond
*/
let domainCounter = 0;
/**
* Generate a unique domain name for testing
* Uses timestamp + counter for uniqueness while keeping length reasonable
* @param subdomain - Optional subdomain prefix
* @returns Unique domain string guaranteed to be unique even in rapid calls
*/
export function generateDomain(subdomain = 'app'): string {
// Increment counter and wrap at 9999 to keep domain lengths reasonable
domainCounter = (domainCounter + 1) % 10000;
// Use shorter ID format: base36 timestamp (7 chars) + random (4 chars)
const timestamp = Date.now().toString(36).slice(-7);
const random = crypto.randomBytes(2).toString('hex');
// Format: subdomain-timestamp-random-counter.test.local
// Example: proxy-1abc123-a1b2-1.test.local (max ~35 chars)
return `${subdomain}-${timestamp}-${random}-${domainCounter}.test.local`;
}
/**
* Generate a unique email address for testing
* @param prefix - Optional email prefix
* @returns Unique email string
*/
export function generateEmail(prefix = 'test'): string {
const id = generateUniqueId();
return `${prefix}-${id}@test.local`;
}
/**
* Proxy host test data
*/
export interface ProxyHostTestData {
domain: string;
forwardHost: string;
forwardPort: number;
scheme: 'http' | 'https';
websocketSupport: boolean;
}
/**
* Generate proxy host test data with unique domain
* @param overrides - Optional overrides for default values
* @returns ProxyHostTestData object
*/
export function generateProxyHostData(
overrides: Partial<ProxyHostTestData> = {}
): ProxyHostTestData {
return {
domain: generateDomain('proxy'),
forwardHost: '192.168.1.100',
forwardPort: 3000,
scheme: 'http',
websocketSupport: false,
...overrides,
};
}
/**
* Generate proxy host data for Docker container
* @param containerName - Docker container name
* @param port - Container port
* @returns ProxyHostTestData object
*/
export function generateDockerProxyHostData(
containerName: string,
port = 80
): ProxyHostTestData {
return {
domain: generateDomain('docker'),
forwardHost: containerName,
forwardPort: port,
scheme: 'http',
websocketSupport: false,
};
}
/**
* Access list test data
*/
export interface AccessListTestData {
name: string;
rules: Array<{ type: 'allow' | 'deny'; value: string }>;
}
/**
* Generate access list test data with unique name
* @param overrides - Optional overrides for default values
* @returns AccessListTestData object
*/
export function generateAccessListData(
overrides: Partial<AccessListTestData> = {}
): AccessListTestData {
const id = generateUniqueId();
return {
name: `ACL-${id}`,
rules: [
{ type: 'allow', value: '192.168.1.0/24' },
{ type: 'deny', value: '0.0.0.0/0' },
],
...overrides,
};
}
/**
* Generate an allowlist that permits all traffic
* @param name - Optional name override
* @returns AccessListTestData object
*/
export function generateAllowAllAccessList(name?: string): AccessListTestData {
return {
name: name || `AllowAll-${generateUniqueId()}`,
rules: [{ type: 'allow', value: '0.0.0.0/0' }],
};
}
/**
* Generate a denylist that blocks specific IPs
* @param blockedIPs - Array of IPs to block
* @returns AccessListTestData object
*/
export function generateDenyListAccessList(blockedIPs: string[]): AccessListTestData {
return {
name: `DenyList-${generateUniqueId()}`,
rules: blockedIPs.map((ip) => ({ type: 'deny' as const, value: ip })),
};
}
/**
* Certificate test data
*/
export interface CertificateTestData {
domains: string[];
type: 'letsencrypt' | 'custom';
privateKey?: string;
certificate?: string;
}
/**
* Generate certificate test data with unique domains
* @param overrides - Optional overrides for default values
* @returns CertificateTestData object
*/
export function generateCertificateData(
overrides: Partial<CertificateTestData> = {}
): CertificateTestData {
return {
domains: [generateDomain('cert')],
type: 'letsencrypt',
...overrides,
};
}
/**
* Generate custom certificate test data
* Note: Uses placeholder values - in real tests, use actual cert/key
* @param domains - Domains for the certificate
* @returns CertificateTestData object
*/
export function generateCustomCertificateData(domains?: string[]): CertificateTestData {
return {
domains: domains || [generateDomain('custom-cert')],
type: 'custom',
// Placeholder - real tests should provide actual certificate data
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQ...\n-----END PRIVATE KEY-----',
certificate: '-----BEGIN CERTIFICATE-----\nMIIDazCCAlOgAwIBAgIUa...\n-----END CERTIFICATE-----',
};
}
/**
* Generate wildcard certificate test data
* @param baseDomain - Base domain for the wildcard
* @returns CertificateTestData object
*/
export function generateWildcardCertificateData(baseDomain?: string): CertificateTestData {
const domain = baseDomain || `${generateUniqueId()}.test.local`;
return {
domains: [`*.${domain}`, domain],
type: 'letsencrypt',
};
}
/**
* User test data
*/
export interface UserTestData {
email: string;
password: string;
role: 'admin' | 'user' | 'guest';
name?: string;
}
/**
* Generate user test data with unique email
* @param overrides - Optional overrides for default values
* @returns UserTestData object
*/
export function generateUserData(overrides: Partial<UserTestData> = {}): UserTestData {
const id = generateUniqueId();
return {
email: generateEmail('user'),
password: 'TestPass123!',
role: 'user',
name: `Test User ${id}`,
...overrides,
};
}
/**
* Generate admin user test data
* @param overrides - Optional overrides
* @returns UserTestData object
*/
export function generateAdminUserData(overrides: Partial<UserTestData> = {}): UserTestData {
return generateUserData({ ...overrides, role: 'admin' });
}
/**
* Generate guest user test data
* @param overrides - Optional overrides
* @returns UserTestData object
*/
export function generateGuestUserData(overrides: Partial<UserTestData> = {}): UserTestData {
return generateUserData({ ...overrides, role: 'guest' });
}
/**
* DNS provider test data
*/
export interface DNSProviderTestData {
name: string;
type: 'manual' | 'cloudflare' | 'route53' | 'webhook' | 'rfc2136';
credentials?: Record<string, string>;
}
/**
* Generate DNS provider test data with unique name
* @param providerType - Type of DNS provider
* @param overrides - Optional overrides for default values
* @returns DNSProviderTestData object
*/
export function generateDNSProviderData(
providerType: DNSProviderTestData['type'] = 'manual',
overrides: Partial<DNSProviderTestData> = {}
): DNSProviderTestData {
const id = generateUniqueId();
const baseData: DNSProviderTestData = {
name: `DNS-${providerType}-${id}`,
type: providerType,
};
// Add type-specific credentials
switch (providerType) {
case 'cloudflare':
baseData.credentials = {
api_token: `test-token-${id}`,
};
break;
case 'route53':
baseData.credentials = {
access_key_id: `AKIATEST${id.toUpperCase()}`,
secret_access_key: `secretkey${id}`,
region: 'us-east-1',
};
break;
case 'webhook':
baseData.credentials = {
create_url: `https://example.com/dns/${id}/create`,
delete_url: `https://example.com/dns/${id}/delete`,
};
break;
case 'rfc2136':
baseData.credentials = {
nameserver: 'ns.example.com:53',
tsig_key_name: `ddns-${id}.example.com`,
tsig_key: 'base64-encoded-key==',
tsig_algorithm: 'hmac-sha256',
};
break;
case 'manual':
default:
baseData.credentials = {};
break;
}
return { ...baseData, ...overrides };
}
/**
* CrowdSec decision test data
*/
export interface CrowdSecDecisionTestData {
ip: string;
duration: string;
reason: string;
scope: 'ip' | 'range' | 'country';
}
/**
* Generate CrowdSec decision test data
* @param overrides - Optional overrides for default values
* @returns CrowdSecDecisionTestData object
*/
export function generateCrowdSecDecisionData(
overrides: Partial<CrowdSecDecisionTestData> = {}
): CrowdSecDecisionTestData {
return {
ip: `10.0.${secureRandomInt(255)}.${secureRandomInt(255)}`,
duration: '4h',
reason: 'Test ban - automated testing',
scope: 'ip',
...overrides,
};
}
/**
* Rate limit rule test data
*/
export interface RateLimitRuleTestData {
name: string;
requests: number;
window: string;
action: 'block' | 'throttle';
}
/**
* Generate rate limit rule test data
* @param overrides - Optional overrides for default values
* @returns RateLimitRuleTestData object
*/
export function generateRateLimitRuleData(
overrides: Partial<RateLimitRuleTestData> = {}
): RateLimitRuleTestData {
const id = generateUniqueId();
return {
name: `RateLimit-${id}`,
requests: 100,
window: '1m',
action: 'block',
...overrides,
};
}
/**
* Backup test data
*/
export interface BackupTestData {
name: string;
includeConfig: boolean;
includeCertificates: boolean;
includeDatabase: boolean;
}
/**
* Generate backup test data
* @param overrides - Optional overrides for default values
* @returns BackupTestData object
*/
export function generateBackupData(
overrides: Partial<BackupTestData> = {}
): BackupTestData {
const id = generateUniqueId();
return {
name: `Backup-${id}`,
includeConfig: true,
includeCertificates: true,
includeDatabase: true,
...overrides,
};
}