aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
533 lines • 20.7 kB
JavaScript
/**
* TestDataFactory - Generate realistic test data for all SDLC artifact types
* Provides consistent but varied fixtures for testing AIWG components
*
* Features:
* - Seeded random generation for reproducible tests
* - Customizable data generation with options
* - Follows AIWG template structure
* - Supports edge cases (empty, minimum, maximum values)
*/
import { randomBytes } from 'crypto';
/**
* Random Number Generator with optional seeding
*/
class SeededRandom {
seed;
constructor(seed) {
this.seed = seed ?? this.generateSeed();
}
generateSeed() {
return randomBytes(4).readUInt32BE(0);
}
setSeed(seed) {
this.seed = seed;
}
next() {
// Linear Congruential Generator (simple but sufficient for tests)
this.seed = (this.seed * 1664525 + 1013904223) % 0x100000000;
return this.seed / 0x100000000;
}
nextInt(min, max) {
return Math.floor(this.next() * (max - min + 1)) + min;
}
pick(items) {
return items[this.nextInt(0, items.length - 1)];
}
shuffle(items) {
const result = [...items];
for (let i = result.length - 1; i > 0; i--) {
const j = this.nextInt(0, i);
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
}
/**
* TestDataFactory - Main factory class for generating test data
*/
export class TestDataFactory {
rng;
// Sample data pools
actors = [
'User', 'Admin', 'Customer', 'Manager', 'System', 'Analyst',
'Operator', 'Developer', 'Stakeholder', 'Guest'
];
verbs = [
'create', 'update', 'delete', 'view', 'manage', 'process',
'validate', 'submit', 'approve', 'configure'
];
nouns = [
'account', 'profile', 'order', 'report', 'document', 'transaction',
'record', 'request', 'configuration', 'notification'
];
authors = [
'Alice Johnson', 'Bob Smith', 'Carol White', 'David Brown',
'Emma Davis', 'Frank Miller', 'Grace Wilson', 'Henry Moore'
];
techTerms = [
'authentication', 'authorization', 'caching', 'encryption',
'microservices', 'database', 'API', 'middleware', 'repository',
'queue', 'container', 'proxy', 'gateway', 'registry'
];
constructor(seed) {
this.rng = new SeededRandom(seed);
}
/**
* Set seed for reproducible random generation
*/
seed(value) {
this.rng.setSeed(value);
}
/**
* Generate random text with specified word count
*/
generateRandomText(wordCount) {
const words = [
'the', 'system', 'shall', 'must', 'should', 'will', 'provide',
'enable', 'support', 'ensure', 'allow', 'process', 'validate',
'user', 'data', 'information', 'feature', 'functionality',
'performance', 'security', 'reliability', 'scalability'
];
const result = [];
for (let i = 0; i < wordCount; i++) {
result.push(this.rng.pick(words));
}
// Capitalize first word
if (result.length > 0) {
result[0] = result[0].charAt(0).toUpperCase() + result[0].slice(1);
}
return result.join(' ') + '.';
}
/**
* Generate date string (ISO 8601 format)
*/
generateDate(daysAgo = 0) {
const date = new Date();
date.setDate(date.getDate() - daysAgo);
return date.toISOString();
}
/**
* Generate unique ID with prefix
*/
generateId(prefix) {
const num = this.rng.nextInt(1, 999);
return `${prefix}-${num.toString().padStart(3, '0')}`;
}
/**
* Generate Use Case artifact
*/
generateUseCase(options = {}) {
const verb = options.title ? '' : this.rng.pick(this.verbs);
const noun = options.title ? '' : this.rng.pick(this.nouns);
const scenarioStepCount = options.scenarioStepCount ?? this.rng.nextInt(3, 8);
const alternateFlowCount = options.alternateFlowCount ?? this.rng.nextInt(0, 2);
const id = options.id ?? this.generateId('UC');
const title = options.title ?? `${verb.charAt(0).toUpperCase() + verb.slice(1)} ${noun}`;
const actors = options.actors ?? [
this.rng.pick(this.actors),
...(this.rng.next() > 0.5 ? [this.rng.pick(this.actors)] : [])
];
const preconditions = [];
for (let i = 0; i < this.rng.nextInt(1, 3); i++) {
preconditions.push(`Precondition ${i + 1}: ${this.generateRandomText(5)}`);
}
const mainScenario = [];
for (let i = 0; i < scenarioStepCount; i++) {
mainScenario.push(`${i + 1}. ${this.generateRandomText(6)}`);
}
const alternateFlows = [];
for (let i = 0; i < alternateFlowCount; i++) {
const flow = [];
const flowSteps = this.rng.nextInt(2, 4);
for (let j = 0; j < flowSteps; j++) {
flow.push(`${j + 1}. ${this.generateRandomText(5)}`);
}
alternateFlows.push(flow);
}
const acceptanceCriteria = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
acceptanceCriteria.push(`AC${i + 1}: ${this.generateRandomText(7)}`);
}
return {
id,
title,
actors,
preconditions,
mainScenario,
alternateFlows,
acceptanceCriteria
};
}
/**
* Generate Non-Functional Requirement (NFR)
*/
generateNFR(category, options = {}) {
const priority = options.priority ?? this.rng.pick(['P0', 'P1', 'P2']);
const id = options.id ?? this.generateId(`NFR-${category.substring(0, 4).toUpperCase()}`);
const descriptions = {
Performance: [
'Response time shall not exceed 2 seconds for 95% of requests',
'System shall support 1000 concurrent users',
'API latency shall be under 100ms at p99'
],
Security: [
'All data transmissions shall be encrypted using TLS 1.3',
'Authentication shall use multi-factor authentication',
'Access logs shall be retained for 90 days'
],
Reliability: [
'System uptime shall be 99.9% measured monthly',
'Data backups shall occur every 24 hours',
'Failover time shall not exceed 30 seconds'
],
Usability: [
'UI shall be accessible to WCAG 2.1 Level AA standards',
'Critical workflows shall require no more than 3 clicks',
'Help documentation shall be available inline'
],
Scalability: [
'System shall handle 10x traffic increase without degradation',
'Database shall support horizontal scaling',
'Storage shall auto-scale based on usage'
]
};
const targets = {
Performance: ['< 2s response time', '1000+ concurrent users', '< 100ms p99 latency'],
Security: ['TLS 1.3 encryption', 'MFA required', '90-day log retention'],
Reliability: ['99.9% uptime', 'Daily backups', '< 30s failover'],
Usability: ['WCAG 2.1 AA', '≤ 3 clicks', 'Inline help'],
Scalability: ['10x traffic capacity', 'Horizontal scaling', 'Auto-scaling storage']
};
const measurements = {
Performance: ['APM monitoring', 'Load testing', 'Latency percentiles'],
Security: ['Security audit', 'Penetration testing', 'Log analysis'],
Reliability: ['Uptime monitoring', 'Backup verification', 'Failover testing'],
Usability: ['Accessibility audit', 'User testing', 'Analytics tracking'],
Scalability: ['Load testing', 'Capacity planning', 'Resource monitoring']
};
const description = options.description ?? this.rng.pick(descriptions[category]);
const target = this.rng.pick(targets[category]);
const measurement = this.rng.pick(measurements[category]);
return {
id,
category,
description,
target,
measurement,
priority
};
}
/**
* Generate Supplemental Specification (collection of NFRs)
*/
generateSupplementalSpec(nfrCount = 5) {
const categories = ['Performance', 'Security', 'Reliability', 'Usability', 'Scalability'];
const nfrs = [];
for (let i = 0; i < nfrCount; i++) {
const category = this.rng.pick(categories);
nfrs.push(this.generateNFR(category));
}
return {
id: this.generateId('SUPP'),
title: 'Supplemental Specification',
nfrs,
createdAt: this.generateDate(this.rng.nextInt(1, 30))
};
}
/**
* Generate Architecture Decision Record (ADR)
*/
generateADR(options = {}) {
const number = options.number ?? this.rng.nextInt(1, 50);
const status = options.status ?? this.rng.pick(['Proposed', 'Accepted', 'Deprecated', 'Superseded']);
const techA = this.rng.pick(this.techTerms);
const techB = this.rng.pick(this.techTerms);
const title = options.title ?? `Use ${techA} for ${techB}`;
const context = `The system requires ${techA} to handle ${techB}. ${this.generateRandomText(15)}`;
const decision = `We will adopt ${techA} as the solution for ${techB}. ${this.generateRandomText(12)}`;
const consequences = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
const sign = this.rng.next() > 0.5 ? 'Positive' : 'Negative';
consequences.push(`${sign}: ${this.generateRandomText(8)}`);
}
const alternatives = [];
for (let i = 0; i < this.rng.nextInt(1, 3); i++) {
const altTech = this.rng.pick(this.techTerms);
alternatives.push(`Alternative ${i + 1}: Use ${altTech} instead`);
}
return {
number,
title,
status,
context,
decision,
consequences,
alternatives,
date: this.generateDate(this.rng.nextInt(1, 90))
};
}
/**
* Generate Software Architecture Document section
*/
generateSADSection(section, _options = {}) {
const content = {
overview: () => `# Architecture Overview\n\n${this.generateRandomText(30)}\n\nThe system architecture follows ${this.rng.pick(['microservices', 'monolithic', 'serverless', 'event-driven'])} pattern.`,
goals: () => {
const goals = [];
for (let i = 0; i < this.rng.nextInt(3, 5); i++) {
goals.push(`${i + 1}. ${this.generateRandomText(10)}`);
}
return `# Architecture Goals\n\n${goals.join('\n')}`;
},
constraints: () => {
const constraints = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
constraints.push(`- ${this.generateRandomText(12)}`);
}
return `# Constraints\n\n${constraints.join('\n')}`;
},
principles: () => {
const principles = ['Separation of Concerns', 'DRY', 'SOLID', 'KISS', 'YAGNI'];
return `# Architecture Principles\n\n${this.rng.shuffle(principles).slice(0, 3).map(p => `- ${p}`).join('\n')}`;
},
components: () => {
const components = [];
for (let i = 0; i < this.rng.nextInt(3, 6); i++) {
const name = this.rng.pick(this.techTerms);
components.push(`## ${name}\n\n${this.generateRandomText(15)}`);
}
return `# Components\n\n${components.join('\n\n')}`;
},
deployment: () => `# Deployment\n\nThe system will be deployed using ${this.rng.pick(['Kubernetes', 'Docker Swarm', 'AWS ECS', 'Azure AKS'])}.\n\n${this.generateRandomText(20)}`,
security: () => `# Security Architecture\n\nSecurity measures include:\n- ${this.rng.pick(['TLS 1.3', 'OAuth 2.0', 'JWT tokens', 'API keys'])}\n- ${this.rng.pick(['RBAC', 'ABAC', 'ACL', 'Policy-based'])}\n\n${this.generateRandomText(15)}`,
performance: () => `# Performance Architecture\n\nPerformance targets:\n- Response time: < ${this.rng.nextInt(100, 3000)}ms\n- Throughput: ${this.rng.nextInt(100, 10000)} req/sec\n\n${this.generateRandomText(15)}`
};
return content[section]();
}
/**
* Generate Component Design
*/
generateComponentDesign(name) {
const responsibilities = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
responsibilities.push(this.generateRandomText(8));
}
const interfaces = [];
for (let i = 0; i < this.rng.nextInt(1, 3); i++) {
const verb = this.rng.pick(this.verbs);
interfaces.push(`I${verb.charAt(0).toUpperCase() + verb.slice(1)}`);
}
const dependencies = [];
for (let i = 0; i < this.rng.nextInt(1, 4); i++) {
dependencies.push(this.rng.pick(this.techTerms));
}
return {
name,
purpose: this.generateRandomText(12),
responsibilities,
interfaces,
dependencies
};
}
/**
* Generate Test Case
*/
generateTestCase(options = {}) {
const id = options.id ?? this.generateId('TC');
const priority = options.priority ?? this.rng.pick(['P0', 'P1', 'P2']);
const stepCount = options.stepCount ?? this.rng.nextInt(3, 6);
const verb = this.rng.pick(this.verbs);
const noun = this.rng.pick(this.nouns);
const title = options.title ?? `Test ${verb} ${noun}`;
const preconditions = [];
for (let i = 0; i < this.rng.nextInt(1, 3); i++) {
preconditions.push(`Precondition: ${this.generateRandomText(6)}`);
}
const steps = [];
const expectedResults = [];
for (let i = 0; i < stepCount; i++) {
steps.push(`Step ${i + 1}: ${this.generateRandomText(7)}`);
expectedResults.push(`Expected: ${this.generateRandomText(6)}`);
}
return {
id,
title,
preconditions,
steps,
expectedResults,
priority
};
}
/**
* Generate Test Plan
*/
generateTestPlan(testCaseCount = 5) {
const testCases = [];
for (let i = 0; i < testCaseCount; i++) {
testCases.push(this.generateTestCase());
}
const objectives = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
objectives.push(this.generateRandomText(10));
}
return {
id: this.generateId('TP'),
title: 'Test Plan',
objectives,
scope: this.generateRandomText(15),
testCases,
schedule: `Week ${this.rng.nextInt(1, 12)}`
};
}
/**
* Generate Test Result
*/
generateTestResult(testCase, passed) {
const status = passed ? 'passed' : (this.rng.next() > 0.8 ? 'skipped' : 'failed');
const executionTime = this.rng.nextInt(10, 5000);
const message = status === 'failed' ? `Assertion failed: ${this.generateRandomText(5)}` : undefined;
return {
testCaseId: testCase.id,
status,
executionTime,
message,
timestamp: this.generateDate(0)
};
}
/**
* Generate Git Commit
*/
generateGitCommit(options = {}) {
const author = options.author ?? this.rng.pick(this.authors);
const email = `${author.toLowerCase().replace(' ', '.')}.com`;
const fileCount = options.fileCount ?? this.rng.nextInt(1, 5);
const files = [];
const extensions = ['ts', 'js', 'md', 'json', 'yaml'];
for (let i = 0; i < fileCount; i++) {
const dir = this.rng.pick(['src', 'test', 'docs', 'config']);
const file = this.rng.pick(this.nouns);
const ext = this.rng.pick(extensions);
files.push(`${dir}/${file}.${ext}`);
}
const verb = this.rng.pick(['feat', 'fix', 'docs', 'refactor', 'test', 'chore']);
const message = options.message ?? `${verb}: ${this.generateRandomText(8)}`;
return {
hash: randomBytes(20).toString('hex').substring(0, 40),
author,
email,
date: this.generateDate(this.rng.nextInt(0, 30)),
message,
files
};
}
/**
* Generate Pull Request
*/
generatePullRequest(options = {}) {
const number = options.number ?? this.rng.nextInt(1, 500);
const author = this.rng.pick(this.authors);
const commitCount = options.commitCount ?? this.rng.nextInt(1, 5);
const commits = [];
for (let i = 0; i < commitCount; i++) {
commits.push(this.generateGitCommit({ author }));
}
const title = options.title ?? `${this.rng.pick(['feat', 'fix', 'refactor'])}: ${this.generateRandomText(8)}`;
const description = `${this.generateRandomText(20)}\n\n## Changes\n- ${this.generateRandomText(10)}\n- ${this.generateRandomText(10)}`;
return {
number,
title,
description,
author,
status: this.rng.pick(['open', 'merged', 'closed']),
commits,
createdAt: this.generateDate(this.rng.nextInt(1, 14))
};
}
/**
* Generate Git History (multiple commits)
*/
generateGitHistory(commitCount) {
const commits = [];
for (let i = 0; i < commitCount; i++) {
commits.push(this.generateGitCommit());
}
return commits;
}
/**
* Generate Project Intake
*/
generateProjectIntake(options = {}) {
const projectName = options.projectName ?? `${this.rng.pick(['E-commerce', 'Analytics', 'CRM', 'Portal'])} Platform`;
const stakeholderCount = options.stakeholderCount ?? this.rng.nextInt(2, 5);
const stakeholders = [];
for (let i = 0; i < stakeholderCount; i++) {
stakeholders.push(this.rng.pick(this.authors));
}
const objectives = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
objectives.push(this.generateRandomText(12));
}
const constraints = [];
for (let i = 0; i < this.rng.nextInt(1, 3); i++) {
constraints.push(this.generateRandomText(10));
}
const risks = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
risks.push(this.generateRandomText(11));
}
return {
projectName,
description: this.generateRandomText(25),
stakeholders,
objectives,
constraints,
risks
};
}
/**
* Generate Risk Register
*/
generateRiskRegister(riskCount = 5) {
const risks = [];
for (let i = 0; i < riskCount; i++) {
risks.push({
id: this.generateId('RISK'),
description: this.generateRandomText(15),
impact: this.rng.pick(['High', 'Medium', 'Low']),
probability: this.rng.pick(['High', 'Medium', 'Low']),
mitigation: this.generateRandomText(12)
});
}
return {
id: this.generateId('RR'),
risks,
createdAt: this.generateDate(this.rng.nextInt(1, 30))
};
}
/**
* Generate Iteration Plan
*/
generateIterationPlan(weekCount) {
const iteration = this.rng.nextInt(1, 20);
const startDate = this.generateDate(0);
const endDate = this.generateDate(-weekCount * 7);
const objectives = [];
for (let i = 0; i < this.rng.nextInt(2, 4); i++) {
objectives.push(this.generateRandomText(10));
}
const stories = [];
for (let i = 0; i < this.rng.nextInt(3, 8); i++) {
stories.push(this.generateId('US'));
}
return {
id: this.generateId('ITER'),
iteration,
startDate,
endDate,
objectives,
stories
};
}
}
//# sourceMappingURL=test-data-factory.js.map