Skip to content

Instantly share code, notes, and snippets.

@arubis
Created October 13, 2025 21:19
Show Gist options
  • Save arubis/93aadce283a76b06897e4b04565850cf to your computer and use it in GitHub Desktop.
Save arubis/93aadce283a76b06897e4b04565850cf to your computer and use it in GitHub Desktop.
Phase 2 Frontend Test Integration - Interactive Demonstrations (RFC-060-AMENDMENT-001)
/**
* TestIntegrationClient Retry Behavior Demonstration
*
* This shows how the backend API client handles errors and retries
* with exponential backoff.
*/
console.log('='.repeat(80));
console.log('TEST INTEGRATION CLIENT - RETRY BEHAVIOR DEMONSTRATION');
console.log('='.repeat(80));
console.log();
// =============================================================================
// Helper to simulate timing
// =============================================================================
class TimingLogger {
private startTime: number;
constructor() {
this.startTime = Date.now();
}
log(message: string) {
const elapsed = Date.now() - this.startTime;
console.log(`[${elapsed.toString().padStart(5, ' ')}ms] ${message}`);
}
reset() {
this.startTime = Date.now();
}
}
// =============================================================================
// DEMO 1: Exponential Backoff Timing
// =============================================================================
console.log('πŸ“‹ DEMO 1: Exponential Backoff Timing');
console.log('-'.repeat(80));
console.log();
const demoExponentialBackoff = () => {
console.log('Formula: delay = baseDelay * 2^attempt');
console.log('Base delay: 100ms');
console.log();
const baseDelay = 100;
const maxRetries = 3;
console.log('Retry schedule:');
for (let attempt = 0; attempt < maxRetries; attempt++) {
const delay = baseDelay * Math.pow(2, attempt);
console.log(` Attempt ${attempt + 1}: Wait ${delay}ms before retry`);
}
console.log();
console.log('Timeline for a 3-retry scenario:');
console.log(' 0ms β†’ Initial request (fails)');
console.log(' 100ms β†’ Retry 1 (fails)');
console.log(' 300ms β†’ Retry 2 (100ms + 200ms wait, fails)');
console.log(' 700ms β†’ Retry 3 (300ms + 400ms wait, succeeds)');
console.log(' Total: 700ms for 3 retries');
console.log();
console.log('Why exponential backoff?');
console.log(' β€’ Gives server time to recover from temporary issues');
console.log(' β€’ Prevents overwhelming server with rapid retries');
console.log(' β€’ Industry standard pattern (AWS, Google Cloud, etc.)');
};
demoExponentialBackoff();
// =============================================================================
// DEMO 2: Retry Decision Matrix
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 2: Retry Decision Matrix');
console.log('-'.repeat(80));
console.log();
const demoRetryDecisionMatrix = () => {
const scenarios = [
{
status: 200,
error: null,
decision: 'βœ… Success',
retries: 0,
reason: 'Request succeeded'
},
{
status: 400,
error: 'Bad Request',
decision: '❌ No Retry',
retries: 0,
reason: 'Client error - request is invalid'
},
{
status: 401,
error: 'Unauthorized',
decision: '❌ No Retry',
retries: 0,
reason: 'Authentication failed - API key is wrong'
},
{
status: 404,
error: 'Not Found',
decision: '❌ No Retry',
retries: 0,
reason: 'Endpoint doesn\'t exist'
},
{
status: 500,
error: 'Internal Server Error',
decision: 'πŸ”„ Retry',
retries: 3,
reason: 'Server error - might be temporary'
},
{
status: 502,
error: 'Bad Gateway',
decision: 'πŸ”„ Retry',
retries: 3,
reason: 'Gateway issue - likely temporary'
},
{
status: 503,
error: 'Service Unavailable',
decision: 'πŸ”„ Retry',
retries: 3,
reason: 'Server overloaded - will recover'
},
{
status: null,
error: 'ECONNREFUSED',
decision: 'πŸ”„ Retry',
retries: 3,
reason: 'Network error - connection refused'
},
{
status: null,
error: 'ETIMEDOUT',
decision: 'πŸ”„ Retry',
retries: 3,
reason: 'Network error - request timed out'
}
];
console.log('Status Code β†’ Retry Decision:\n');
for (const scenario of scenarios) {
const statusStr = scenario.status ? `HTTP ${scenario.status}` : scenario.error!;
console.log(` ${statusStr.padEnd(25)} ${scenario.decision.padEnd(15)} ${scenario.reason}`);
}
console.log();
console.log('Rule Summary:');
console.log(' β€’ 2xx: Success, no retry needed');
console.log(' β€’ 4xx: Client error, NO RETRY (fixing won\'t help)');
console.log(' β€’ 5xx: Server error, RETRY (might be temporary)');
console.log(' β€’ Network errors: RETRY (transient issues)');
};
demoRetryDecisionMatrix();
// =============================================================================
// DEMO 3: Simulated Network Timeout Scenario
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 3: Simulated Network Timeout Scenario');
console.log('-'.repeat(80));
console.log();
const demoNetworkTimeout = () => {
const timer = new TimingLogger();
console.log('Request: POST /api/v1/test-integration/analyze');
console.log('Headers: { x-api-key: rsolv_xxx, Content-Type: application/json }');
console.log();
// Attempt 1
timer.log('β†’ Attempt 1: Making request...');
timer.log('← Network timeout error: ETIMEDOUT');
timer.log('⚠️ Will retry with 100ms backoff');
console.log();
// Attempt 2 (after 100ms)
timer.log('β†’ Attempt 2: Making request...');
timer.log('← Network timeout error: ETIMEDOUT');
timer.log('⚠️ Will retry with 200ms backoff');
console.log();
// Attempt 3 (after 200ms)
timer.log('β†’ Attempt 3: Making request...');
timer.log('← Success! HTTP 200');
timer.log('βœ… Request succeeded on attempt 3');
console.log();
console.log('Total time: ~300ms (including retry delays)');
console.log('Result: Resilient to temporary network issues');
};
demoNetworkTimeout();
// =============================================================================
// DEMO 4: Simulated 503 Service Unavailable Scenario
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 4: Simulated 503 Service Unavailable Scenario');
console.log('-'.repeat(80));
console.log();
const demo503Retry = () => {
const timer = new TimingLogger();
console.log('Scenario: Backend server is temporarily overloaded');
console.log('Request: POST /api/v1/test-integration/generate');
console.log();
// Attempt 1
timer.log('β†’ Attempt 1: Making request...');
timer.log('← HTTP 503: Service Unavailable');
timer.log(' Response: { error: "Server overloaded, try again" }');
timer.log('⚠️ Will retry with 100ms backoff');
console.log();
// Attempt 2 (after 100ms)
timer.log('β†’ Attempt 2: Making request...');
timer.log('← HTTP 503: Service Unavailable');
timer.log(' Response: { error: "Server overloaded, try again" }');
timer.log('⚠️ Will retry with 200ms backoff');
console.log();
// Attempt 3 (after 200ms)
timer.log('β†’ Attempt 3: Making request...');
timer.log('← Success! HTTP 200');
timer.log(' Response: { integratedContent: "...", method: "ast", ... }');
timer.log('βœ… Request succeeded on attempt 3');
console.log();
console.log('Why this works:');
console.log(' β€’ Server had time to shed load');
console.log(' β€’ Exponential backoff gave increasing recovery time');
console.log(' β€’ Client didn\'t give up too early');
};
demo503Retry();
// =============================================================================
// DEMO 5: Simulated 401 Unauthorized (No Retry)
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 5: Simulated 401 Unauthorized (No Retry)');
console.log('-'.repeat(80));
console.log();
const demo401NoRetry = () => {
const timer = new TimingLogger();
console.log('Scenario: Invalid API key provided');
console.log('Request: POST /api/v1/test-integration/analyze');
console.log('Headers: { x-api-key: invalid_key, ... }');
console.log();
timer.log('β†’ Attempt 1: Making request...');
timer.log('← HTTP 401: Unauthorized');
timer.log(' Response: { error: "Invalid API key" }');
timer.log('❌ No retry - client error');
timer.log('βœ— Throwing error: "Unauthorized: Invalid API key"');
console.log();
console.log('Why no retry?');
console.log(' β€’ Authentication failed - API key is wrong');
console.log(' β€’ Retrying with same key will always fail');
console.log(' β€’ User needs to fix configuration');
console.log(' β€’ Total requests: 1 (fast failure)');
};
demo401NoRetry();
// =============================================================================
// DEMO 6: Complete Request/Response Flow
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 6: Complete Request/Response Flow');
console.log('-'.repeat(80));
console.log();
const demoCompleteFlow = () => {
console.log('Example: Successful analyze() call');
console.log();
console.log('1️⃣ Client code:');
console.log(' const client = new TestIntegrationClient(apiKey, baseUrl);');
console.log(' const result = await client.analyze({');
console.log(' vulnerableFile: "app/controllers/users_controller.rb",');
console.log(' vulnerabilityType: "sql_injection",');
console.log(' candidateTestFiles: ["spec/controllers/users_controller_spec.rb"],');
console.log(' framework: "rspec"');
console.log(' });');
console.log();
console.log('2️⃣ HTTP Request:');
console.log(' POST https://api.rsolv.dev/api/v1/test-integration/analyze');
console.log(' Headers:');
console.log(' x-api-key: rsolv_xxx');
console.log(' Content-Type: application/json');
console.log(' Body:');
console.log(' {');
console.log(' "vulnerableFile": "app/controllers/users_controller.rb",');
console.log(' "vulnerabilityType": "sql_injection",');
console.log(' "candidateTestFiles": ["spec/controllers/users_controller_spec.rb"],');
console.log(' "framework": "rspec"');
console.log(' }');
console.log();
console.log('3️⃣ HTTP Response:');
console.log(' HTTP 200 OK');
console.log(' Content-Type: application/json');
console.log(' Body:');
console.log(' {');
console.log(' "recommendations": [');
console.log(' {');
console.log(' "path": "spec/controllers/users_controller_spec.rb",');
console.log(' "score": 1.5,');
console.log(' "reason": "Direct unit test for vulnerable controller"');
console.log(' }');
console.log(' ],');
console.log(' "fallback": {');
console.log(' "path": "spec/security/users_controller_security_spec.rb",');
console.log(' "reason": "Generated security test file"');
console.log(' }');
console.log(' }');
console.log();
console.log('4️⃣ Client receives:');
console.log(' result.recommendations[0].path β†’ "spec/controllers/users_controller_spec.rb"');
console.log(' result.recommendations[0].score β†’ 1.5');
console.log(' result.fallback.path β†’ "spec/security/users_controller_security_spec.rb"');
};
demoCompleteFlow();
// =============================================================================
// DEMO 7: Environment Configuration
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 7: Environment Configuration');
console.log('-'.repeat(80));
console.log();
const demoEnvironmentConfig = () => {
console.log('TestIntegrationClient supports multiple environments:');
console.log();
const configs = [
{
scenario: 'Production (default)',
code: 'new TestIntegrationClient(apiKey)',
envVar: 'Not set',
resolvedUrl: 'https://api.rsolv.dev'
},
{
scenario: 'Staging (via env)',
code: 'new TestIntegrationClient(apiKey)',
envVar: 'RSOLV_API_URL=https://api.rsolv-staging.com',
resolvedUrl: 'https://api.rsolv-staging.com'
},
{
scenario: 'Local dev (explicit)',
code: 'new TestIntegrationClient(apiKey, "http://localhost:4000")',
envVar: 'Any value',
resolvedUrl: 'http://localhost:4000'
},
{
scenario: 'CI/Testing (env)',
code: 'new TestIntegrationClient(apiKey)',
envVar: 'RSOLV_API_URL=https://api.rsolv.test',
resolvedUrl: 'https://api.rsolv.test'
}
];
for (const config of configs) {
console.log(` ${config.scenario}:`);
console.log(` Code: ${config.code}`);
console.log(` Env Var: ${config.envVar}`);
console.log(` URL: ${config.resolvedUrl}`);
console.log();
}
console.log('Priority:');
console.log(' 1. Explicit baseUrl parameter (highest)');
console.log(' 2. RSOLV_API_URL environment variable');
console.log(' 3. Default production URL (lowest)');
};
demoEnvironmentConfig();
// =============================================================================
// SUMMARY
// =============================================================================
console.log('\n' + '='.repeat(80));
console.log('TEST INTEGRATION CLIENT DEMONSTRATION COMPLETE');
console.log('='.repeat(80));
console.log();
console.log('Key Features Demonstrated:');
console.log();
console.log('βœ… Exponential Backoff:');
console.log(' β€’ 100ms, 200ms, 400ms retry delays');
console.log(' β€’ Prevents overwhelming server');
console.log(' β€’ Industry-standard pattern');
console.log();
console.log('βœ… Smart Retry Logic:');
console.log(' β€’ Retries: 5xx errors, network timeouts');
console.log(' β€’ No retry: 4xx client errors');
console.log(' β€’ Max 3 retry attempts');
console.log();
console.log('βœ… Authentication:');
console.log(' β€’ x-api-key header (project convention)');
console.log(' β€’ NOT Bearer token (different from GitHub API)');
console.log();
console.log('βœ… Environment Support:');
console.log(' β€’ Production, staging, local dev, CI');
console.log(' β€’ RSOLV_API_URL environment variable');
console.log(' β€’ Explicit baseUrl parameter');
console.log();
console.log('βœ… Type Safety:');
console.log(' β€’ Full TypeScript interfaces');
console.log(' β€’ AnalyzeRequest/Response');
console.log(' β€’ GenerateRequest/Response');
console.log();
console.log('='.repeat(80));
/**
* Simulation of Retry Flow with Mock LLM Responses
*
* This demonstrates the actual execution path through the retry loop
* by simulating different scenarios with mock data.
*/
import type { Vulnerability, TestFileContext, AttemptHistory, TestFramework } from '../src/modes/types.js';
console.log('='.repeat(80));
console.log('RETRY FLOW SIMULATION');
console.log('Demonstrating actual execution paths through generateTestWithRetry()');
console.log('='.repeat(80));
console.log();
// =============================================================================
// Setup Mock Data
// =============================================================================
const mockVulnerability: Vulnerability = {
type: 'sql_injection',
description: 'String interpolation in SQL WHERE clause (CWE-89)',
location: 'app/controllers/users_controller.rb:42',
attackVector: "5') OR admin = 't' --'",
vulnerablePattern: 'User.where("id = \'#{params[:user][:id]}\'")',
source: 'RailsGoat'
};
const mockTargetFile: TestFileContext = {
path: 'spec/controllers/users_controller_spec.rb',
content: `describe UsersController do
before do
@user = User.create(name: 'testuser', admin: false)
end
it 'updates user' do
patch :update, params: { user: { id: @user.id, name: 'newname' } }
expect(response).to be_successful
end
end`,
framework: 'rspec'
};
const mockFramework: TestFramework = {
name: 'rspec',
syntaxCheckCommand: 'ruby -c',
testCommand: 'bundle exec rspec'
};
// =============================================================================
// SCENARIO 1: Success on First Attempt
// =============================================================================
console.log('πŸ“‹ SCENARIO 1: Success on First Attempt');
console.log('-'.repeat(80));
console.log();
const simulateScenario1 = () => {
console.log('Starting generateTestWithRetry()...');
console.log(` Vulnerability: ${mockVulnerability.type}`);
console.log(` Target: ${mockTargetFile.path}`);
console.log(` Max attempts: 3`);
console.log();
const previousAttempts: AttemptHistory[] = [];
let attempt = 1;
console.log(`[Attempt ${attempt}/3]`);
console.log(' Step 1: Generate test with LLM');
console.log(' β†’ Prompt includes:');
console.log(' β€’ Vulnerability: sql_injection');
console.log(' β€’ Attack vector: 5\') OR admin = \'t\' --\'');
console.log(' β€’ Target file content (for context)');
console.log(' β€’ Real-world RailsGoat example');
console.log(' β€’ No previous attempts (first try)');
console.log();
const mockTestCode = `it 'rejects SQL injection in user update (CWE-89)' do
# Attack: SQL injection via user ID parameter
patch :update, params: {
user: {
id: "5') OR admin = 't' --'",
name: 'attacker'
}
}
expect(response.status).to eq(400)
expect(User.find(5).admin).to be_falsey
end`;
console.log(' Step 2: Write to temp file');
console.log(` β†’ File: .rsolv/temp/test_${Date.now()}.rb`);
console.log(' β†’ Content:');
console.log(' ' + mockTestCode.split('\n').slice(0, 3).join('\n '));
console.log(' ...');
console.log();
console.log(' Step 3: Validate syntax');
console.log(' β†’ Command: ruby -c test_1234.rb');
console.log(' β†’ Result: βœ… Syntax OK');
console.log();
console.log(' Step 4: Run test');
console.log(' β†’ Command: bundle exec rspec test_1234.rb');
console.log(' β†’ Result: ❌ Test FAILED (expected - proves vulnerability!)');
console.log(' β†’ Output: 1 example, 1 failure');
console.log();
console.log(' Step 5: Check regressions');
console.log(' β†’ Only 1 failure detected (the new test)');
console.log(' β†’ Result: βœ… No regressions');
console.log();
console.log('βœ… SUCCESS on attempt 1!');
console.log();
console.log('Return TestSuite:');
console.log(JSON.stringify({
framework: 'rspec',
testFile: 'spec/controllers/users_controller_spec.rb',
redTests: [{
testName: 'rejects SQL injection in user update (CWE-89)',
testCode: mockTestCode.substring(0, 50) + '...',
attackVector: "5') OR admin = 't' --'",
expectedBehavior: 'should_fail_on_vulnerable_code'
}]
}, null, 2));
};
simulateScenario1();
// =============================================================================
// SCENARIO 2: Syntax Error on First Attempt, Success on Second
// =============================================================================
console.log('\n\nπŸ“‹ SCENARIO 2: Syntax Error β†’ Retry β†’ Success');
console.log('-'.repeat(80));
console.log();
const simulateScenario2 = () => {
const previousAttempts: AttemptHistory[] = [];
// Attempt 1
console.log('[Attempt 1/3]');
console.log(' Step 1: Generate test with LLM');
console.log(' β†’ Generated test code (with syntax error)');
console.log();
const badTestCode = `it 'rejects SQL injection' do
patch :update, params: { user: { id: "5') OR admin = 't' --'" } }
expect(response.status).to eq(400)
# Missing 'end' keyword!`;
console.log(' Step 2: Write to temp file');
console.log(` β†’ File: .rsolv/temp/test_1234.rb`);
console.log();
console.log(' Step 3: Validate syntax');
console.log(' β†’ Command: ruby -c test_1234.rb');
console.log(' β†’ Result: ❌ SyntaxError');
console.log(' β†’ Error: "unexpected end-of-input, expecting keyword_end"');
console.log();
previousAttempts.push({
attempt: 1,
error: 'SyntaxError',
errorMessage: 'unexpected end-of-input, expecting keyword_end',
timestamp: new Date().toISOString()
});
console.log(' ⚠️ Syntax error detected');
console.log(' β†’ Recording attempt in history');
console.log(' β†’ CONTINUE to attempt 2');
console.log();
console.log('-'.repeat(40));
console.log();
// Attempt 2
console.log('[Attempt 2/3]');
console.log(' Step 1: Generate test with LLM (with error feedback)');
console.log(' β†’ Prompt NOW includes:');
console.log(' β€’ All previous content');
console.log(' β€’ PREVIOUS ATTEMPTS section:');
console.log(' - Attempt 1: SyntaxError - unexpected end-of-input, expecting keyword_end');
console.log(' β€’ IMPORTANT: Fix the syntax error. Ensure valid rspec syntax.');
console.log();
const fixedTestCode = `it 'rejects SQL injection' do
patch :update, params: { user: { id: "5') OR admin = 't' --'" } }
expect(response.status).to eq(400)
end`;
console.log(' Step 2: Write to temp file');
console.log(` β†’ File: .rsolv/temp/test_1235.rb`);
console.log(' β†’ LLM fixed the missing end keyword!');
console.log();
console.log(' Step 3: Validate syntax');
console.log(' β†’ Command: ruby -c test_1235.rb');
console.log(' β†’ Result: βœ… Syntax OK');
console.log();
console.log(' Step 4: Run test');
console.log(' β†’ Command: bundle exec rspec test_1235.rb');
console.log(' β†’ Result: ❌ Test FAILED (good!)');
console.log();
console.log(' Step 5: Check regressions');
console.log(' β†’ Result: βœ… No regressions');
console.log();
console.log('βœ… SUCCESS on attempt 2!');
console.log();
console.log('Attempt history for debugging:');
console.log(JSON.stringify(previousAttempts, null, 2));
};
simulateScenario2();
// =============================================================================
// SCENARIO 3: Test Passed Unexpectedly β†’ Retry with Stronger Test
// =============================================================================
console.log('\n\nπŸ“‹ SCENARIO 3: Test Passed Unexpectedly β†’ Retry');
console.log('-'.repeat(80));
console.log();
const simulateScenario3 = () => {
const previousAttempts: AttemptHistory[] = [];
// Attempt 1
console.log('[Attempt 1/3]');
console.log(' Steps 1-3: Generate, write, validate syntax β†’ βœ… OK');
console.log();
console.log(' Step 4: Run test');
console.log(' β†’ Command: bundle exec rspec test_1234.rb');
console.log(' β†’ Result: βœ… Test PASSED');
console.log(' β†’ Output: 1 example, 0 failures');
console.log();
console.log(' ⚠️ Problem: Test PASSED when it should FAIL!');
console.log(' β†’ This means the test did NOT detect the vulnerability');
console.log(' β†’ Test is too weak or checks wrong thing');
console.log();
previousAttempts.push({
attempt: 1,
error: 'TestPassedUnexpectedly',
errorMessage: 'Test passed when it should fail to demonstrate vulnerability',
timestamp: new Date().toISOString()
});
console.log(' β†’ Recording attempt in history');
console.log(' β†’ CONTINUE to attempt 2');
console.log();
console.log('-'.repeat(40));
console.log();
// Attempt 2
console.log('[Attempt 2/3]');
console.log(' Step 1: Generate test with LLM (with error feedback)');
console.log(' β†’ Prompt NOW includes:');
console.log(' β€’ PREVIOUS ATTEMPTS section:');
console.log(' - Attempt 1: TestPassedUnexpectedly - Test passed when should fail');
console.log(' β€’ IMPORTANT: Make the test MORE AGGRESSIVE. It must FAIL on vulnerable code.');
console.log();
console.log(' LLM adjusts strategy:');
console.log(' β†’ Original test: Checked if response was successful');
console.log(' β†’ New test: Actually executes SQL injection AND checks admin flag');
console.log();
console.log(' Steps 2-3: Write to file, validate syntax β†’ βœ… OK');
console.log();
console.log(' Step 4: Run test');
console.log(' β†’ Command: bundle exec rspec test_1235.rb');
console.log(' β†’ Result: ❌ Test FAILED (good!)');
console.log(' β†’ The more aggressive test detected the vulnerability!');
console.log();
console.log('βœ… SUCCESS on attempt 2!');
console.log();
console.log('Key insight: Error feedback helps LLM understand what went wrong');
};
simulateScenario3();
// =============================================================================
// SCENARIO 4: All Retries Exhausted β†’ Tag Issue
// =============================================================================
console.log('\n\nπŸ“‹ SCENARIO 4: All Retries Exhausted β†’ Tag Issue');
console.log('-'.repeat(80));
console.log();
const simulateScenario4 = () => {
const previousAttempts: AttemptHistory[] = [];
console.log('[Attempt 1/3]');
console.log(' β†’ SyntaxError: unexpected token');
previousAttempts.push({
attempt: 1,
error: 'SyntaxError',
errorMessage: 'unexpected token',
timestamp: new Date().toISOString()
});
console.log();
console.log('[Attempt 2/3]');
console.log(' β†’ SyntaxError: unexpected end-of-input (different error!)');
previousAttempts.push({
attempt: 2,
error: 'SyntaxError',
errorMessage: 'unexpected end-of-input',
timestamp: new Date().toISOString()
});
console.log();
console.log('[Attempt 3/3]');
console.log(' β†’ TestPassedUnexpectedly: Test passed when should fail');
previousAttempts.push({
attempt: 3,
error: 'TestPassedUnexpectedly',
errorMessage: 'Test passed when it should fail to demonstrate vulnerability',
timestamp: new Date().toISOString()
});
console.log();
console.log('❌ All retries exhausted!');
console.log();
console.log('Calling tagIssueNotValidated():');
console.log(' β†’ Would add "not-validated" label to issue');
console.log(' β†’ Would add comment with attempt history:');
console.log();
console.log(' ⚠️ Unable to Generate Valid Test');
console.log(' ');
console.log(' After 3 attempts, we could not generate a valid RED test for this vulnerability.');
console.log(' ');
console.log(' **Vulnerability Details:**');
console.log(' - Type: sql_injection');
console.log(' - Location: app/controllers/users_controller.rb:42');
console.log(' ');
console.log(' **Attempt History:**');
console.log(' 1. Attempt 1: SyntaxError - unexpected token');
console.log(' 2. Attempt 2: SyntaxError - unexpected end-of-input');
console.log(' 3. Attempt 3: TestPassedUnexpectedly - Test passed when should fail');
console.log(' ');
console.log(' **Next Steps:**');
console.log(' - Manual review recommended');
console.log(' - May require custom test generation');
console.log(' - Vulnerability may be too complex for automated testing');
console.log();
console.log('Return: null (no valid TestSuite)');
console.log();
console.log('Caller (commitTestsToBranch) would:');
console.log(' β†’ Skip AST integration');
console.log(' β†’ Skip commit');
console.log(' β†’ Log failure');
console.log(' β†’ Continue with next vulnerability');
};
simulateScenario4();
// =============================================================================
// SCENARIO 5: Regression Detection
// =============================================================================
console.log('\n\nπŸ“‹ SCENARIO 5: Regression Detection β†’ Retry');
console.log('-'.repeat(80));
console.log();
const simulateScenario5 = () => {
console.log('[Attempt 1/3]');
console.log(' Steps 1-4: Generate, write, validate, run test β†’ ❌ Test failed (good!)');
console.log();
console.log(' Step 5: Check regressions');
console.log(' β†’ Detected multiple test failures:');
console.log(' 1. NEW test failed (expected)');
console.log(' 2. Existing test "updates user" also failed (REGRESSION!)');
console.log();
console.log(' ⚠️ Problem: New test broke existing tests!');
console.log(' β†’ Likely modified shared state (database, globals, etc.)');
console.log(' β†’ Or conflicting setup/teardown');
console.log();
const previousAttempts: AttemptHistory[] = [{
attempt: 1,
error: 'ExistingTestsRegression',
errorMessage: 'Existing tests failed: updates user',
timestamp: new Date().toISOString()
}];
console.log(' β†’ Recording regression in history');
console.log(' β†’ CONTINUE to attempt 2');
console.log();
console.log('-'.repeat(40));
console.log();
console.log('[Attempt 2/3]');
console.log(' Step 1: Generate test with LLM (with error feedback)');
console.log(' β†’ Prompt NOW includes:');
console.log(' β€’ PREVIOUS ATTEMPTS:');
console.log(' - Attempt 1: ExistingTestsRegression - Existing tests failed: updates user');
console.log(' β€’ IMPORTANT: Don\'t break existing tests. Avoid modifying shared state.');
console.log();
console.log(' LLM adjusts strategy:');
console.log(' β†’ Original: Modified @user directly (broke existing test setup)');
console.log(' β†’ New: Creates separate test user, doesn\'t touch @user');
console.log();
console.log(' Steps 2-5: Write, validate, run, check regressions β†’ βœ… All OK');
console.log();
console.log('βœ… SUCCESS on attempt 2!');
};
simulateScenario5();
// =============================================================================
// SUMMARY
// =============================================================================
console.log('\n\n' + '='.repeat(80));
console.log('RETRY FLOW SIMULATION COMPLETE');
console.log('='.repeat(80));
console.log();
console.log('Key Takeaways:');
console.log();
console.log('1. Error Feedback Loop Works:');
console.log(' β€’ LLM receives specific error messages from previous attempts');
console.log(' β€’ Guidance is error-type specific (syntax vs. test behavior vs. regression)');
console.log(' β€’ Each retry has more context to improve the test');
console.log();
console.log('2. Multiple Error Types Handled:');
console.log(' β€’ SyntaxError β†’ Fix syntax');
console.log(' β€’ TestPassedUnexpectedly β†’ Make test more aggressive');
console.log(' β€’ ExistingTestsRegression β†’ Don\'t break existing tests');
console.log(' β€’ TestExecutionError β†’ General execution failures');
console.log();
console.log('3. Graceful Degradation:');
console.log(' β€’ After 3 attempts, issue is tagged "not-validated"');
console.log(' β€’ Detailed comment added with attempt history');
console.log(' β€’ Returns null to signal failure');
console.log(' β€’ Caller can skip this vulnerability and continue');
console.log();
console.log('4. Success Cases:');
console.log(' β€’ Can succeed on first attempt (ideal case)');
console.log(' β€’ Can recover from errors on retry (common case)');
console.log(' β€’ Test must FAIL on vulnerable code (RED test requirement)');
console.log(' β€’ No regressions allowed (existing tests must pass)');
console.log();
console.log('='.repeat(80));
/**
* Demonstration of Phase 2 Test Integration Implementation
*
* This script demonstrates the key features of the test generation retry loop:
* 1. TestIntegrationClient with retry logic
* 2. ValidationMode.generateTestWithRetry() with LLM feedback
* 3. Syntax validation and test execution
* 4. Error feedback loop
*/
import { TestIntegrationClient } from '../src/modes/test-integration-client.js';
import { ValidationMode } from '../src/modes/validation-mode.js';
import type { ActionConfig } from '../src/types/index.js';
import type { Vulnerability, TestFileContext } from '../src/modes/types.js';
console.log('='.repeat(80));
console.log('PHASE 2 TEST INTEGRATION DEMONSTRATION');
console.log('RFC-060-AMENDMENT-001: Test Generation with Retry Loop');
console.log('='.repeat(80));
console.log();
// =============================================================================
// DEMO 1: TestIntegrationClient - API Communication with Retry Logic
// =============================================================================
console.log('πŸ“‹ DEMO 1: TestIntegrationClient - Backend API Client');
console.log('-'.repeat(80));
const demoTestIntegrationClient = () => {
console.log('\n1️⃣ Creating TestIntegrationClient instance...');
const client = new TestIntegrationClient('demo-api-key', 'https://api.rsolv.test');
console.log(' βœ“ Client created with API key authentication');
console.log(' βœ“ Base URL: https://api.rsolv.test');
console.log(' βœ“ Uses x-api-key header (project convention)');
console.log('\n2️⃣ Client Configuration Features:');
console.log(' β€’ Exponential backoff: 100ms, 200ms, 400ms');
console.log(' β€’ Max retries: 3 attempts');
console.log(' β€’ Retry on: 5xx errors, network timeouts');
console.log(' β€’ No retry on: 4xx client errors');
console.log(' β€’ Environment variable support: RSOLV_API_URL');
console.log('\n3️⃣ API Methods Available:');
console.log(' β€’ analyze(request) - Score test files for integration');
console.log(' β€’ generate(request) - Generate AST-based integration');
console.log('\n Example analyze() request:');
const analyzeRequest = {
vulnerableFile: 'app/controllers/users_controller.rb',
vulnerabilityType: 'sql_injection',
candidateTestFiles: ['spec/controllers/users_controller_spec.rb'],
framework: 'rspec'
};
console.log(' ', JSON.stringify(analyzeRequest, null, 2).split('\n').join('\n '));
console.log('\n Example generate() request:');
const generateRequest = {
targetFileContent: 'describe UsersController do\n # existing tests\nend',
testSuite: {
redTests: [{
testName: 'rejects SQL injection',
testCode: 'expect(response).to have_http_status(400)',
attackVector: "5') OR admin = 't' --'",
expectedBehavior: 'should_fail_on_vulnerable_code'
}]
},
framework: 'rspec',
language: 'ruby'
};
console.log(' ', JSON.stringify(generateRequest, null, 2).split('\n').slice(0, 8).join('\n '));
console.log(' ...');
return client;
};
const client = demoTestIntegrationClient();
// =============================================================================
// DEMO 2: Framework Detection
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 2: Framework Detection from File Paths');
console.log('-'.repeat(80));
const demoFrameworkDetection = () => {
const mockConfig: ActionConfig = {
apiKey: 'demo-key',
githubToken: 'demo-token',
mode: 'validate',
aiProvider: {
apiKey: 'demo-ai-key',
model: 'claude-sonnet-4-5-20250929',
provider: 'anthropic'
}
} as ActionConfig;
const validationMode = new ValidationMode(mockConfig, '/tmp/demo-repo');
const testPaths = [
'spec/controllers/users_controller_spec.rb',
'test/models/user_test.py',
'src/components/Button.test.tsx',
'src/services/api.spec.ts',
'tests/Feature/UserTest.php',
'src/test/java/com/example/UserTest.java',
];
console.log('\nDetecting frameworks from test file paths:\n');
for (const testPath of testPaths) {
// Call private method via type assertion for demo
const framework = (validationMode as any).detectFrameworkFromPath(testPath);
console.log(` ${testPath}`);
console.log(` β†’ Framework: ${framework.name}`);
console.log(` β†’ Syntax check: ${framework.syntaxCheckCommand}`);
console.log(` β†’ Test command: ${framework.testCommand}`);
console.log();
}
return validationMode;
};
const validationMode = demoFrameworkDetection();
// =============================================================================
// DEMO 3: Realistic Vulnerability Examples
// =============================================================================
console.log('\nπŸ“‹ DEMO 3: Realistic Vulnerability Examples (from Phase 0)');
console.log('-'.repeat(80));
const demoVulnerabilityExamples = () => {
const vulnerabilityTypes = [
'sql_injection',
'nosql_injection',
'xss',
'command_injection',
'path_traversal'
];
console.log('\nVulnerability examples used in LLM prompts:\n');
for (const vulnType of vulnerabilityTypes) {
const example = (validationMode as any).getRealisticVulnerabilityExample(vulnType);
console.log(`πŸ”΄ ${vulnType.toUpperCase().replace('_', ' ')}`);
console.log(' ' + example.split('\n').join('\n '));
console.log();
}
console.log('These examples come from REALISTIC-VULNERABILITY-EXAMPLES.md:');
console.log(' β€’ Source: NodeGoat, RailsGoat (OWASP projects)');
console.log(' β€’ Real attack vectors from production exploits');
console.log(' β€’ CWE classifications for compliance');
};
demoVulnerabilityExamples();
// =============================================================================
// DEMO 4: LLM Prompt Building with Retry Feedback
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 4: LLM Prompt Building with Retry Feedback');
console.log('-'.repeat(80));
const demoPromptBuilding = () => {
const vulnerability: Vulnerability = {
type: 'sql_injection',
description: 'String interpolation in SQL WHERE clause (CWE-89)',
location: 'app/controllers/users_controller.rb:42',
attackVector: "5') OR admin = 't' --'",
vulnerablePattern: 'User.where("id = \'#{params[:user][:id]}\'")',
source: 'RailsGoat'
};
const targetTestFile: TestFileContext = {
path: 'spec/controllers/users_controller_spec.rb',
content: `describe UsersController do
before do
@user = User.create(name: 'testuser', admin: false)
end
it 'updates user' do
patch :update, params: { user: { id: @user.id, name: 'newname' } }
expect(response).to be_successful
end
end`,
framework: 'rspec'
};
const framework = (validationMode as any).detectFrameworkFromPath(targetTestFile.path);
console.log('\n1️⃣ Initial prompt (Attempt 1 - no previous errors):\n');
const initialPrompt = (validationMode as any).buildLLMPrompt(
vulnerability,
targetTestFile,
[],
framework
);
console.log(' ' + initialPrompt.split('\n').slice(0, 25).join('\n '));
console.log(' ...');
console.log(`\n Total length: ${initialPrompt.length} characters`);
console.log('\n2️⃣ Retry prompt after syntax error (Attempt 2):\n');
const attemptWithSyntaxError = [{
attempt: 1,
error: 'SyntaxError' as const,
errorMessage: 'unexpected end-of-input, expecting keyword_end',
timestamp: new Date().toISOString()
}];
const retryPrompt = (validationMode as any).buildLLMPrompt(
vulnerability,
targetTestFile,
attemptWithSyntaxError,
framework
);
// Show the retry feedback section
const retrySection = retryPrompt.split('PREVIOUS ATTEMPTS')[1];
if (retrySection) {
console.log(' PREVIOUS ATTEMPTS' + retrySection.split('\n').slice(0, 5).join('\n '));
}
console.log('\n3️⃣ Retry prompt after test passed unexpectedly (Attempt 3):\n');
const attemptWithTestPassed = [
...attemptWithSyntaxError,
{
attempt: 2,
error: 'TestPassedUnexpectedly' as const,
errorMessage: 'Test passed when it should fail to demonstrate vulnerability',
timestamp: new Date().toISOString()
}
];
const retryPrompt2 = (validationMode as any).buildLLMPrompt(
vulnerability,
targetTestFile,
attemptWithTestPassed,
framework
);
const retrySection2 = retryPrompt2.split('PREVIOUS ATTEMPTS')[1];
if (retrySection2) {
console.log(' PREVIOUS ATTEMPTS' + retrySection2.split('\n').slice(0, 8).join('\n '));
}
console.log('\n4️⃣ Key prompt features:');
console.log(' βœ“ Vulnerability details (type, location, attack vector)');
console.log(' βœ“ Real-world example from NodeGoat/RailsGoat');
console.log(' βœ“ Target file content (so LLM sees existing setup blocks)');
console.log(' βœ“ Framework-specific conventions');
console.log(' βœ“ Previous attempt errors with specific guidance');
console.log(' βœ“ Error-specific instructions (syntax, test passed, regression)');
};
demoPromptBuilding();
// =============================================================================
// DEMO 5: Retry Loop Logic Flow
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 5: Retry Loop Logic Flow');
console.log('-'.repeat(80));
const demoRetryLogic = () => {
console.log('\nRetry loop implementation (generateTestWithRetry):');
console.log();
console.log(' for (attempt = 1; attempt <= 3; attempt++) {');
console.log(' ');
console.log(' // Step 1: Generate test with LLM');
console.log(' testSuite = await generateTestWithLLM(vulnerability, targetFile, previousAttempts)');
console.log(' ');
console.log(' // Step 2: Write to temp file');
console.log(' fs.writeFileSync(tempFile, testSuite.redTests[0].testCode)');
console.log(' ');
console.log(' // Step 3: Validate syntax');
console.log(' try {');
console.log(' await validateSyntax(tempFile, framework)');
console.log(' βœ“ Syntax OK');
console.log(' } catch (syntaxError) {');
console.log(' βœ— Syntax error β†’ Record attempt β†’ CONTINUE to next attempt');
console.log(' }');
console.log(' ');
console.log(' // Step 4: Run test (must FAIL on vulnerable code)');
console.log(' testResult = await runTest(tempFile, framework)');
console.log(' ');
console.log(' if (testResult.passed) {');
console.log(' βœ— Test passed unexpectedly β†’ Record attempt β†’ CONTINUE');
console.log(' }');
console.log(' ');
console.log(' // Step 5: Check for regressions');
console.log(' if (testResult.existingTestsFailed) {');
console.log(' βœ— Existing tests failed β†’ Record attempt β†’ CONTINUE');
console.log(' }');
console.log(' ');
console.log(' // Success!');
console.log(' βœ“ Return testSuite');
console.log(' }');
console.log(' ');
console.log(' // All retries exhausted');
console.log(' await tagIssueNotValidated(vulnerability, previousAttempts)');
console.log(' return null');
console.log('\n\nError handling by type:\n');
const errorTypes = [
{
type: 'SyntaxError',
action: 'Continue to next attempt',
feedback: 'Fix the syntax error. Ensure valid [framework] syntax.'
},
{
type: 'TestPassedUnexpectedly',
action: 'Continue to next attempt',
feedback: 'Make the test MORE AGGRESSIVE. It must FAIL on vulnerable code.'
},
{
type: 'ExistingTestsRegression',
action: 'Continue to next attempt',
feedback: 'Don\'t break existing tests. Avoid modifying shared state.'
},
{
type: 'TestExecutionError',
action: 'Continue to next attempt',
feedback: 'Test execution failed with error details'
}
];
for (const error of errorTypes) {
console.log(` πŸ”΄ ${error.type}`);
console.log(` Action: ${error.action}`);
console.log(` Feedback to LLM: "${error.feedback}"`);
console.log();
}
};
demoRetryLogic();
// =============================================================================
// DEMO 6: Language Extension Mapping
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 6: Language Extension Mapping');
console.log('-'.repeat(80));
const demoLanguageExtensions = () => {
const frameworks = [
'rspec', 'minitest', 'pytest', 'phpunit', 'pest',
'jest', 'vitest', 'mocha', 'junit5', 'testng', 'exunit'
];
console.log('\nFramework β†’ File Extension Mapping:\n');
for (const framework of frameworks) {
const ext = (validationMode as any).getLanguageExtension(framework);
console.log(` ${framework.padEnd(12)} β†’ ${ext}`);
}
};
demoLanguageExtensions();
// =============================================================================
// DEMO 7: Type Safety
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 7: TypeScript Type Safety');
console.log('-'.repeat(80));
const demoTypeSafety = () => {
console.log('\nNew types added to src/modes/types.ts:\n');
const types = [
{
name: 'Vulnerability',
fields: ['type', 'description', 'location', 'attackVector', 'vulnerablePattern?', 'source?'],
purpose: 'Vulnerability information for test generation'
},
{
name: 'TestFileContext',
fields: ['path', 'content', 'framework'],
purpose: 'Target test file with content for LLM context'
},
{
name: 'TestSuite',
fields: ['framework', 'testFile', 'redTests[]'],
purpose: 'Generated test suite structure'
},
{
name: 'AttemptHistory',
fields: ['attempt', 'error', 'errorMessage', 'timestamp'],
purpose: 'Retry loop history tracking'
},
{
name: 'TestFramework',
fields: ['name', 'version?', 'testCommand?', 'syntaxCheckCommand?'],
purpose: 'Framework detection metadata'
}
];
for (const type of types) {
console.log(` interface ${type.name} {`);
for (const field of type.fields) {
console.log(` ${field}`);
}
console.log(` }`);
console.log(` β†’ ${type.purpose}`);
console.log();
}
console.log('Benefits:');
console.log(' βœ“ Full TypeScript type checking');
console.log(' βœ“ IntelliSense support in IDEs');
console.log(' βœ“ Compile-time error detection');
console.log(' βœ“ Self-documenting code');
};
demoTypeSafety();
// =============================================================================
// DEMO 8: Integration with Existing Code
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 8: Integration with Existing ValidationMode');
console.log('-'.repeat(80));
const demoIntegration = () => {
console.log('\nNew ValidationMode capabilities:\n');
console.log('1️⃣ New method: generateTestWithRetry()');
console.log(' β€’ Takes: Vulnerability, TestFileContext, maxAttempts');
console.log(' β€’ Returns: TestSuite | null');
console.log(' β€’ Used by: commitTestsToBranch() (future enhancement)');
console.log();
console.log('2️⃣ TestIntegrationClient initialization:');
console.log(' β€’ Created in constructor alongside PhaseDataClient');
console.log(' β€’ Uses same rsolvApiKey from config');
console.log(' β€’ Available as private property for future use');
console.log();
console.log('3️⃣ Helper methods added:');
console.log(' β€’ validateSyntax() - Framework-specific syntax checking');
console.log(' β€’ runTest() - Test execution with pass/fail detection');
console.log(' β€’ scanTestFiles() - Repository test file discovery');
console.log(' β€’ detectFrameworkFromPath() - Auto-detect test framework');
console.log(' β€’ buildLLMPrompt() - Construct prompts with retry feedback');
console.log(' β€’ getRealisticVulnerabilityExample() - Phase 0 examples');
console.log();
console.log('4️⃣ Ready for commitTestsToBranch() enhancement:');
console.log(' β€’ All pieces in place for full workflow');
console.log(' β€’ Can be called with: vulnerability + target file');
console.log(' β€’ Returns validated TestSuite or null');
};
demoIntegration();
// =============================================================================
// DEMO 9: Complete Workflow Example
// =============================================================================
console.log('\n\nπŸ“‹ DEMO 9: Complete Workflow Example');
console.log('-'.repeat(80));
const demoCompleteWorkflow = () => {
console.log('\nExample workflow for SQL Injection vulnerability:\n');
console.log('β”Œβ”€ Issue #123: SQL Injection in users_controller.rb');
console.log('β”‚');
console.log('β”œβ”€ 1. ValidationMode.validateVulnerability(issue)');
console.log('β”‚ └─ Calls generateTestWithRetry()');
console.log('β”‚');
console.log('β”œβ”€ 2. generateTestWithRetry(vulnerability, targetTestFile)');
console.log('β”‚ β”‚');
console.log('β”‚ β”œβ”€ Attempt 1:');
console.log('β”‚ β”‚ β”œβ”€ LLM generates test with SQL injection attack vector');
console.log('β”‚ β”‚ β”œβ”€ Write to temp file: /tmp/.rsolv/temp/test_1234.rb');
console.log('β”‚ β”‚ β”œβ”€ Validate syntax: ruby -c test_1234.rb');
console.log('β”‚ β”‚ β”‚ └─ βœ— SyntaxError: "unexpected end"');
console.log('β”‚ β”‚ └─ Record error β†’ Continue');
console.log('β”‚ β”‚');
console.log('β”‚ β”œβ”€ Attempt 2:');
console.log('β”‚ β”‚ β”œβ”€ LLM generates test WITH previous error feedback');
console.log('β”‚ β”‚ β”œβ”€ Write to temp file: /tmp/.rsolv/temp/test_1235.rb');
console.log('β”‚ β”‚ β”œβ”€ Validate syntax: ruby -c test_1235.rb');
console.log('β”‚ β”‚ β”‚ └─ βœ“ Syntax OK');
console.log('β”‚ β”‚ β”œβ”€ Run test: bundle exec rspec test_1235.rb');
console.log('β”‚ β”‚ β”‚ └─ βœ“ Test FAILED (good! proves vulnerability)');
console.log('β”‚ β”‚ └─ βœ“ SUCCESS - Return TestSuite');
console.log('β”‚ β”‚');
console.log('β”‚ └─ TestSuite {');
console.log('β”‚ framework: "rspec",');
console.log('β”‚ testFile: "spec/controllers/users_controller_spec.rb",');
console.log('β”‚ redTests: [{');
console.log('β”‚ testName: "rejects SQL injection in user update (CWE-89)",');
console.log('β”‚ testCode: "...",');
console.log('β”‚ attackVector: "5\') OR admin = \'t\' --\'",');
console.log('β”‚ expectedBehavior: "should_fail_on_vulnerable_code"');
console.log('β”‚ }]');
console.log('β”‚ }');
console.log('β”‚');
console.log('β”œβ”€ 3. Backend AST Integration (future)');
console.log('β”‚ β”œβ”€ testIntegrationClient.analyze(testFiles)');
console.log('β”‚ β”œβ”€ testIntegrationClient.generate(testSuite)');
console.log('β”‚ └─ Write integrated file');
console.log('β”‚');
console.log('└─ 4. Commit and push to validation branch');
console.log(' └─ Branch: rsolv/validate/issue-123');
console.log();
};
demoCompleteWorkflow();
// =============================================================================
// SUMMARY
// =============================================================================
console.log('\n' + '='.repeat(80));
console.log('DEMONSTRATION COMPLETE');
console.log('='.repeat(80));
console.log();
console.log('βœ… TestIntegrationClient - Backend API client with retry logic');
console.log('βœ… generateTestWithRetry() - LLM-based test generation with 3-attempt loop');
console.log('βœ… Realistic vulnerability examples from Phase 0 (NodeGoat, RailsGoat)');
console.log('βœ… Framework detection and multi-language support');
console.log('βœ… Syntax validation and test execution');
console.log('βœ… Intelligent error feedback to LLM');
console.log('βœ… TypeScript type safety with new interfaces');
console.log('βœ… Integration with existing ValidationMode');
console.log();
console.log('All features are implemented and TypeScript compilation passes.');
console.log('Ready for integration testing with backend AST service.');
console.log('='.repeat(80));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment