feat: add optimistic locking and improve type safety

- Implement optimistic locking for deposit balance updates
  - Prevent race conditions in concurrent deposit requests
  - Add automatic retry with exponential backoff (max 3 attempts)
  - Add version column to user_deposits table

- Improve type safety across codebase
  - Add explicit types for Namecheap API responses
  - Add typed function arguments (ManageDepositArgs, etc.)
  - Remove `any` types from deposit-agent and tool files

- Add reconciliation job for balance integrity verification
  - Compare user_deposits.balance vs SUM(confirmed transactions)
  - Alert admin on discrepancy detection

- Set up test environment with Vitest + Miniflare
  - Add 50+ test cases for deposit system
  - Add helper functions for test data creation

- Update documentation
  - Add migration guide for version columns
  - Document optimistic locking patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-19 23:23:09 +09:00
parent 8d0fe30722
commit f5df0c0ffe
21 changed files with 13448 additions and 169 deletions

228
tests/README.md Normal file
View File

@@ -0,0 +1,228 @@
# Unit Tests for telegram-bot-workers
이 디렉토리는 Cloudflare Workers 환경에서 실행되는 Telegram Bot의 단위 테스트를 포함합니다.
## 테스트 프레임워크
- **Vitest**: 빠르고 현대적인 테스트 프레임워크
- **Miniflare**: Cloudflare Workers 로컬 시뮬레이터 (D1, KV 지원)
- **Coverage**: V8 Coverage Provider
## 설치
```bash
npm install
```
필요한 의존성:
- `vitest` - 테스트 러너
- `miniflare` - Workers 환경 시뮬레이션
- `@cloudflare/vitest-pool-workers` - Vitest Workers 통합
- `@vitest/coverage-v8` - 커버리지 리포트
## 실행
```bash
# 모든 테스트 실행
npm test
# Watch 모드 (파일 변경 감지)
npm run test:watch
# 커버리지 리포트 생성
npm run test:coverage
```
## 테스트 파일
### `deposit-agent.test.ts`
**예치금 시스템 (deposit-agent.ts) 테스트**
**커버리지**:
-**get_balance**: 신규 사용자 0원, 기존 잔액 조회
-**get_account_info**: 은행 계좌 정보 반환
-**request_deposit**: 음수/0원 거부, 7글자 매칭, 자동/수동 처리
-**get_transactions**: 거래 내역 조회, 정렬, LIMIT 처리
-**cancel_transaction**: 본인/관리자 권한, 상태 검증
-**confirm_deposit**: 잔액 증가, Batch 실패 처리
-**reject_deposit**: 거래 거절
-**get_pending_list**: 관리자 전용
-**Concurrency**: 동시 요청 처리, Race condition
-**Edge Cases**: 큰 금액, 특수문자, 짧은 이름
**테스트 수**: 50+개
### `setup.ts`
**테스트 환경 초기화 및 헬퍼 함수**
**기능**:
- D1 Database 스키마 초기화 (in-memory SQLite)
- 각 테스트 후 데이터 정리 (스키마 유지)
- 테스트 데이터 생성 헬퍼 함수
**헬퍼 함수**:
```typescript
createTestUser(telegramId, username)
createBankNotification(depositorName, amount)
createDepositTransaction(userId, amount, status, depositorName?)
getTestDB()
```
## 테스트 작성 가이드
### 기본 구조
```typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { executeDepositFunction } from '../src/deposit-agent';
import { createTestUser, getTestDB } from './setup';
describe('Feature Name', () => {
let testUserId: number;
beforeEach(async () => {
testUserId = await createTestUser('123456789', 'testuser');
});
it('should do something', async () => {
const result = await executeDepositFunction('function_name', {
param: 'value'
}, {
userId: testUserId,
telegramUserId: '123456789',
isAdmin: false,
db: getTestDB()
});
expect(result).toHaveProperty('success', true);
});
});
```
### Mock 전략
**D1 Database**: Miniflare가 자동으로 in-memory SQLite 제공
**환경 변수**: `vitest.config.ts`에서 설정
```typescript
environmentOptions: {
bindings: {
BOT_TOKEN: 'test-bot-token',
WEBHOOK_SECRET: 'test-webhook-secret',
DEPOSIT_ADMIN_ID: '999999999',
}
}
```
**외부 API Mock**: `vi.fn()` 사용
```typescript
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ data: 'mocked' })
});
```
### Batch 실패 시뮬레이션
```typescript
const originalBatch = testContext.db.batch;
testContext.db.batch = vi.fn().mockResolvedValue([
{ success: true, meta: { changes: 1 } },
{ success: false, meta: { changes: 0 } }, // 실패
]);
await expect(someFunction()).rejects.toThrow('처리 실패');
// 복원
testContext.db.batch = originalBatch;
```
## 디버깅
### Verbose 모드
```bash
npm test -- --reporter=verbose
```
### 특정 테스트만 실행
```bash
# 파일명으로 필터링
npm test deposit-agent
# 테스트 설명으로 필터링
npm test -- -t "should reject negative amounts"
```
### 로그 출력
테스트 내에서 `console.log` 사용 가능:
```typescript
it('debugging test', async () => {
console.log('Debug info:', result);
expect(result).toBeDefined();
});
```
## CI/CD 통합
**GitHub Actions 예시**:
```yaml
- name: Run tests
run: npm test
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
```
## 향후 계획
- [ ] `openai-service.ts` - Function Calling 도구 테스트
- [ ] `summary-service.ts` - 프로필 시스템 테스트
- [ ] Integration Tests - 전체 워크플로우 테스트
- [ ] E2E Tests - Telegram Bot API 통합 테스트
## 문제 해결
### "Cannot find module" 에러
```bash
npm install
```
### D1 스키마 초기화 실패
`setup.ts`에서 `schema.sql` 경로 확인:
```typescript
const schemaPath = join(__dirname, '../schema.sql');
```
### Miniflare 버전 충돌
`package.json`에서 버전 확인:
```json
{
"miniflare": "^3.20231030.0",
"@cloudflare/vitest-pool-workers": "^0.1.0"
}
```
### 테스트 타임아웃
`vitest.config.ts`에서 타임아웃 증가:
```typescript
test: {
testTimeout: 10000, // 기본 5초 → 10초
}
```
## 참고 자료
- [Vitest 공식 문서](https://vitest.dev/)
- [Miniflare 공식 문서](https://miniflare.dev/)
- [Cloudflare Workers Testing](https://developers.cloudflare.com/workers/testing/)