refactor: 추천 시스템 제거
삭제된 파일: - src/routes/recommend.ts - src/services/recommendation.ts - src/services/recommendation.test.ts - src/services/stackConfig.ts - src/services/regionFilter.ts 수정된 파일: - src/index.ts: /recommend 라우트 제거 - src/routes/index.ts: handleRecommend export 제거 - src/constants.ts: RECOMMENDATIONS TTL, rate limit 제거 - src/middleware/rateLimit.ts: /recommend 설정 제거 - src/types.ts: 추천 관련 타입 제거 - scripts/e2e-tester.ts: recommend 시나리오 제거 - scripts/api-tester.ts: recommend 테스트 제거 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -121,119 +121,15 @@ function sleep(ms: number): Promise<void> {
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Scenario 1: WordPress Server Recommendation → Detail Lookup
|
||||
*
|
||||
* Flow:
|
||||
* 1. POST /recommend with WordPress stack (nginx, php-fpm, mysql)
|
||||
* 2. Extract first recommended instance ID
|
||||
* 3. GET /instances with instance_id filter
|
||||
* 4. Validate specs meet requirements
|
||||
*/
|
||||
async function scenario1WordPress(context: TestContext, dryRun: boolean): Promise<boolean> {
|
||||
console.log('\n▶️ Scenario 1: WordPress Server Recommendation → Detail Lookup');
|
||||
|
||||
if (dryRun) {
|
||||
console.log(' [DRY RUN] Would execute:');
|
||||
console.log(' 1. POST /recommend with stack: nginx, php-fpm, mysql');
|
||||
console.log(' 2. Extract instance_id from first recommendation');
|
||||
console.log(' 3. GET /instances?instance_id={id}');
|
||||
console.log(' 4. Validate memory >= 3072MB, vCPU >= 2');
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// Step 1: Request recommendation
|
||||
logStep(1, 'Request WordPress server recommendation...');
|
||||
const recommendResp = await apiRequest('/recommend', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
stack: ['nginx', 'php-fpm', 'mysql'],
|
||||
scale: 'medium',
|
||||
}),
|
||||
});
|
||||
|
||||
logResponse('POST', '/recommend', recommendResp.status, recommendResp.duration);
|
||||
|
||||
if (recommendResp.status !== 200) {
|
||||
console.log(` ❌ Expected 200, got ${recommendResp.status}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const recommendData = recommendResp.data as {
|
||||
success: boolean;
|
||||
data?: {
|
||||
recommendations?: Array<{ instance: string; provider: string; price: { monthly: number }; region: string }>;
|
||||
};
|
||||
};
|
||||
|
||||
if (!recommendData.success || !recommendData.data?.recommendations?.[0]) {
|
||||
console.log(' ❌ No recommendations returned');
|
||||
return false;
|
||||
}
|
||||
|
||||
const firstRec = recommendData.data.recommendations[0];
|
||||
console.log(` Recommended: ${firstRec.instance} ($${firstRec.price.monthly}/mo) in ${firstRec.region}`);
|
||||
|
||||
// Step 2: Extract instance identifier (we'll use provider + instance name for search)
|
||||
const instanceName = firstRec.instance;
|
||||
const provider = firstRec.provider;
|
||||
context.recommendedInstanceId = instanceName;
|
||||
|
||||
// Step 3: Fetch instance details
|
||||
logStep(2, 'Fetch instance details...');
|
||||
const detailsResp = await apiRequest(
|
||||
`/instances?provider=${encodeURIComponent(provider)}&limit=100`,
|
||||
{ method: 'GET' }
|
||||
);
|
||||
|
||||
logResponse('GET', '/instances', detailsResp.status, detailsResp.duration);
|
||||
|
||||
if (detailsResp.status !== 200) {
|
||||
console.log(` ❌ Expected 200, got ${detailsResp.status}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const detailsData = detailsResp.data as {
|
||||
success: boolean;
|
||||
data?: {
|
||||
instances?: Array<{ instance_name: string; vcpu: number; memory_mb: number }>;
|
||||
};
|
||||
};
|
||||
|
||||
const instance = detailsData.data?.instances?.find((i) => i.instance_name === instanceName);
|
||||
|
||||
if (!instance) {
|
||||
console.log(` ❌ Instance ${instanceName} not found in details`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 4: Validate specs
|
||||
logStep(3, 'Validate specs meet requirements...');
|
||||
const memoryOk = instance.memory_mb >= 3072; // nginx 256 + php-fpm 1024 + mysql 2048 + OS 768 = 4096
|
||||
const vcpuOk = instance.vcpu >= 2;
|
||||
|
||||
logValidation(memoryOk, `Memory: ${instance.memory_mb}MB >= 3072MB required`);
|
||||
logValidation(vcpuOk, `vCPU: ${instance.vcpu} >= 2 required`);
|
||||
|
||||
const passed = memoryOk && vcpuOk;
|
||||
console.log(` ${passed ? '✅' : '❌'} Scenario ${passed ? 'PASSED' : 'FAILED'} (${recommendResp.duration + detailsResp.duration}ms)`);
|
||||
return passed;
|
||||
} catch (error) {
|
||||
console.log(` ❌ Scenario FAILED with error: ${error instanceof Error ? error.message : String(error)}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 2: Budget-Constrained Instance Search
|
||||
* Scenario 1: Budget-Constrained Instance Search
|
||||
*
|
||||
* Flow:
|
||||
* 1. GET /instances?max_price=50&sort_by=price&order=asc
|
||||
* 2. Validate all results <= $50/month
|
||||
* 3. Validate price sorting is correct
|
||||
*/
|
||||
async function scenario2Budget(context: TestContext, dryRun: boolean): Promise<boolean> {
|
||||
console.log('\n▶️ Scenario 2: Budget-Constrained Instance Search');
|
||||
async function scenario1Budget(context: TestContext, dryRun: boolean): Promise<boolean> {
|
||||
console.log('\n▶️ Scenario 1: Budget-Constrained Instance Search');
|
||||
|
||||
if (dryRun) {
|
||||
console.log(' [DRY RUN] Would execute:');
|
||||
@@ -297,15 +193,15 @@ async function scenario2Budget(context: TestContext, dryRun: boolean): Promise<b
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 3: Cross-Region Price Comparison
|
||||
* Scenario 2: Cross-Region Price Comparison
|
||||
*
|
||||
* Flow:
|
||||
* 1. GET /instances?region=ap-northeast-1 (Tokyo)
|
||||
* 2. GET /instances?region=ap-northeast-2 (Seoul)
|
||||
* 3. Compare average prices and instance counts
|
||||
*/
|
||||
async function scenario3RegionCompare(context: TestContext, dryRun: boolean): Promise<boolean> {
|
||||
console.log('\n▶️ Scenario 3: Cross-Region Price Comparison (Tokyo vs Seoul)');
|
||||
async function scenario2RegionCompare(context: TestContext, dryRun: boolean): Promise<boolean> {
|
||||
console.log('\n▶️ Scenario 2: Cross-Region Price Comparison (Tokyo vs Seoul)');
|
||||
|
||||
if (dryRun) {
|
||||
console.log(' [DRY RUN] Would execute:');
|
||||
@@ -395,15 +291,15 @@ async function scenario3RegionCompare(context: TestContext, dryRun: boolean): Pr
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 4: Provider Sync and Data Verification
|
||||
* Scenario 3: Provider Sync and Data Verification
|
||||
*
|
||||
* Flow:
|
||||
* 1. POST /sync with provider: linode
|
||||
* 2. GET /health to check sync_status
|
||||
* 3. GET /instances?provider=linode to verify data exists
|
||||
*/
|
||||
async function scenario4ProviderSync(context: TestContext, dryRun: boolean): Promise<boolean> {
|
||||
console.log('\n▶️ Scenario 4: Provider Sync and Data Verification');
|
||||
async function scenario3ProviderSync(context: TestContext, dryRun: boolean): Promise<boolean> {
|
||||
console.log('\n▶️ Scenario 3: Provider Sync and Data Verification');
|
||||
|
||||
if (dryRun) {
|
||||
console.log(' [DRY RUN] Would execute:');
|
||||
@@ -562,10 +458,9 @@ interface ScenarioFunction {
|
||||
}
|
||||
|
||||
const scenarios: Record<string, ScenarioFunction> = {
|
||||
wordpress: scenario1WordPress,
|
||||
budget: scenario2Budget,
|
||||
region: scenario3RegionCompare,
|
||||
sync: scenario4ProviderSync,
|
||||
budget: scenario1Budget,
|
||||
region: scenario2RegionCompare,
|
||||
sync: scenario3ProviderSync,
|
||||
ratelimit: scenario5RateLimit,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user