Files
sam-scenarios/_global-testdata-config.json
light 4d732842d2 feat: 테스트 데이터 관리 전역 설정 추가
- 네이밍 컨벤션: E2E_TEST_{entity}_{timestamp}
- 엔티티별 템플릿: freeBoard, vendor, employee, deposit, card
- 라이프사이클 관리: beforeTest → CREATE → TEST → DELETE → afterTest
- 정리 전략: 즉시 삭제 (immediate)
- 데이터 의존성 관리 설정
- 보호 데이터 패턴 정의

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 11:21:21 +09:00

272 lines
8.4 KiB
JSON

{
"$schema": "E2E Test Data Management Configuration",
"version": "1.0.0",
"description": "테스트 데이터 생성, 관리, 정리를 위한 전역 설정",
"lastUpdated": "2026-01-31",
"namingConvention": {
"prefix": "E2E_TEST_",
"format": "{prefix}{entity}_{timestamp}",
"timestampFormat": "YYYYMMDD_HHmmss",
"examples": [
"E2E_TEST_게시글_20260131_110000",
"E2E_TEST_거래처_20260131_110000",
"E2E_TEST_사원_20260131_110000"
]
},
"dataTemplates": {
"freeBoard": {
"entity": "게시글",
"fields": {
"title": "E2E_TEST_게시글_{timestamp}",
"content": "E2E 자동화 테스트용 게시글입니다.\n생성시간: {datetime}\n테스트 목적: CRUD 흐름 검증"
},
"updateFields": {
"title": "E2E_TEST_게시글_수정됨_{timestamp}"
},
"identifierField": "title",
"searchableBy": ["title"]
},
"vendor": {
"entity": "거래처",
"fields": {
"name": "E2E_TEST_거래처_{timestamp}",
"businessNumber": "123-45-{random6}",
"representative": "테스트담당자",
"phone": "02-1234-5678",
"address": "서울시 테스트구 자동화로 123"
},
"updateFields": {
"representative": "수정된담당자",
"phone": "02-9999-8888"
},
"identifierField": "name",
"searchableBy": ["name", "businessNumber"]
},
"employee": {
"entity": "사원",
"fields": {
"name": "E2E_TEST_사원_{timestamp}",
"employeeNumber": "E2E{random4}",
"department": "테스트부서",
"position": "테스터",
"email": "e2e_test_{timestamp}@test.com"
},
"updateFields": {
"department": "수정부서",
"position": "시니어테스터"
},
"identifierField": "name",
"searchableBy": ["name", "employeeNumber"]
},
"deposit": {
"entity": "입금",
"fields": {
"description": "E2E_TEST_입금_{timestamp}",
"amount": 100000,
"depositor": "테스트입금자",
"accountNumber": "110-123-456789"
},
"updateFields": {
"amount": 200000,
"description": "E2E_TEST_입금_수정됨_{timestamp}"
},
"identifierField": "description",
"searchableBy": ["description", "depositor"]
},
"card": {
"entity": "카드",
"fields": {
"cardName": "E2E_TEST_카드_{timestamp}",
"cardNumber": "1234-5678-{random4}-{random4}",
"cardCompany": "테스트카드사",
"holder": "테스트소지자"
},
"updateFields": {
"holder": "수정된소지자"
},
"identifierField": "cardName",
"searchableBy": ["cardName", "cardNumber"]
}
},
"scripts": {
"generateTimestamp": "(() => { const now = new Date(); const pad = n => n.toString().padStart(2, '0'); return `${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`; })()",
"generateRandom": "(length) => Math.random().toString().substring(2, 2 + length)",
"generateTestData": "(template, timestamp) => { const data = {}; Object.entries(template.fields).forEach(([key, value]) => { if (typeof value === 'string') { data[key] = value.replace('{timestamp}', timestamp).replace('{datetime}', new Date().toLocaleString('ko-KR')).replace('{random4}', Math.random().toString().substring(2, 6)).replace('{random6}', Math.random().toString().substring(2, 8)); } else { data[key] = value; } }); return data; }",
"findTestData": "(prefix = 'E2E_TEST_') => { const rows = document.querySelectorAll('table tbody tr, [class*=\"table\"] [class*=\"row\"]'); return Array.from(rows).filter(row => row.innerText?.includes(prefix)).map(row => ({ text: row.innerText?.substring(0, 100), element: row })); }",
"cleanupTestData": "(async (prefix = 'E2E_TEST_') => { const testRows = []; const rows = document.querySelectorAll('table tbody tr'); rows.forEach(row => { if (row.innerText?.includes(prefix)) { testRows.push(row); } }); return { found: testRows.length, message: `Found ${testRows.length} test data rows to cleanup` }; })",
"verifyDataCreated": "(identifierValue) => { const pageText = document.body.innerText; return pageText.includes(identifierValue); }",
"verifyDataDeleted": "(identifierValue) => { const pageText = document.body.innerText; return !pageText.includes(identifierValue); }"
},
"lifecycle": {
"beforeTest": {
"description": "테스트 시작 전 실행",
"actions": [
"generateTimestamp",
"prepareTestData"
]
},
"afterCreate": {
"description": "데이터 생성 후 실행",
"actions": [
"verifyDataCreated",
"storeCreatedId"
]
},
"afterUpdate": {
"description": "데이터 수정 후 실행",
"actions": [
"verifyDataUpdated"
]
},
"afterDelete": {
"description": "데이터 삭제 후 실행",
"actions": [
"verifyDataDeleted"
]
},
"afterTest": {
"description": "테스트 종료 후 실행",
"actions": [
"cleanupOrphanedData",
"generateReport"
]
},
"onError": {
"description": "에러 발생 시 실행",
"actions": [
"attemptCleanup",
"logError"
]
}
},
"cleanup": {
"strategy": "immediate",
"strategies": {
"immediate": "테스트 완료 즉시 삭제 (권장)",
"batch": "테스트 세션 종료 시 일괄 삭제",
"scheduled": "정기적으로 삭제 (위험)"
},
"orphanedDataHandling": {
"description": "이전 테스트에서 정리되지 않은 데이터 처리",
"maxAge": "24h",
"autoCleanup": true,
"pattern": "E2E_TEST_*"
},
"protectedData": {
"description": "삭제하면 안되는 데이터 패턴",
"patterns": [
"가우스*",
"실제데이터*",
"운영*"
]
}
},
"validation": {
"rules": {
"uniqueIdentifier": {
"description": "테스트 데이터는 고유 식별자를 가져야 함",
"check": "timestamp 포함 여부"
},
"noProductionData": {
"description": "운영 데이터와 구분 가능해야 함",
"check": "E2E_TEST_ 접두사 필수"
},
"cleanupable": {
"description": "쉽게 식별하고 삭제 가능해야 함",
"check": "검색 가능한 필드 존재"
}
}
},
"isolation": {
"description": "테스트 데이터 격리 전략",
"levels": {
"session": {
"description": "세션별 고유 타임스탬프 사용",
"implementation": "timestamp를 세션 시작 시 한번만 생성"
},
"scenario": {
"description": "시나리오별 고유 데이터",
"implementation": "시나리오 ID + timestamp 조합"
},
"step": {
"description": "스텝별 고유 데이터",
"implementation": "스텝 번호 + timestamp 조합"
}
},
"currentLevel": "session"
},
"dependencies": {
"description": "테스트 데이터 간 의존성 관리",
"examples": {
"approval": {
"requires": ["employee", "document"],
"description": "결재 테스트는 사원과 문서 데이터 필요"
},
"payment": {
"requires": ["vendor", "invoice"],
"description": "지급 테스트는 거래처와 청구서 필요"
}
},
"resolutionOrder": "topological",
"autoCreate": true
},
"reporting": {
"trackCreated": true,
"trackDeleted": true,
"trackOrphaned": true,
"includeInTestReport": true,
"format": {
"summary": {
"created": "생성된 테스트 데이터 수",
"deleted": "삭제된 테스트 데이터 수",
"orphaned": "정리되지 않은 데이터 수"
},
"detail": {
"entity": "엔티티 유형",
"identifier": "식별자",
"createdAt": "생성 시간",
"deletedAt": "삭제 시간",
"status": "상태 (created/deleted/orphaned)"
}
}
},
"errorRecovery": {
"onCreateFailure": {
"action": "skip",
"log": true,
"continueTest": false
},
"onDeleteFailure": {
"action": "retry",
"maxRetries": 2,
"log": true,
"continueTest": true
},
"onOrphanedData": {
"action": "warn",
"autoCleanup": false,
"log": true
}
}
}