[TASK-2026-01-30-ANALISIS-INTEGRACION] docs: Add DATABASE-SCHEMA.md and TESTING-STRATEGY.md
Sprint 4 deliverables: - DATABASE-SCHEMA.md: ER diagrams for 12 schemas (~90 tables) - TESTING-STRATEGY.md: Testing pyramid (70/20/10), frameworks, CI/CD Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3a623576fb
commit
6dce71d5d2
515
docs/01-arquitectura/DATABASE-SCHEMA.md
Normal file
515
docs/01-arquitectura/DATABASE-SCHEMA.md
Normal file
@ -0,0 +1,515 @@
|
||||
# DATABASE-SCHEMA.md - Trading Platform
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Fecha:** 2026-01-30
|
||||
**Motor:** PostgreSQL 16+
|
||||
**Proyecto:** trading-platform
|
||||
|
||||
---
|
||||
|
||||
## Resumen de Schemas
|
||||
|
||||
| Schema | Tablas | Descripción |
|
||||
|--------|--------|-------------|
|
||||
| `auth` | 10 | Autenticación, usuarios, sesiones, OAuth |
|
||||
| `education` | 14 | Cursos, lecciones, quizzes, gamificación |
|
||||
| `trading` | 10+ | Símbolos, órdenes, posiciones, watchlists |
|
||||
| `investment` | 7 | Productos PAMM, cuentas, distribuciones |
|
||||
| `financial` | 10 | Wallets, transacciones, suscripciones |
|
||||
| `portfolio` | 5+ | Perfiles de riesgo, allocations, goals |
|
||||
| `market_data` | 4 | OHLCV 5m/15m, staging |
|
||||
| `ml` | 9 | Modelos, predicciones, feature store |
|
||||
| `llm` | 5 | Conversaciones, mensajes, embeddings |
|
||||
| `audit` | 7 | Logs de auditoría, seguridad, compliance |
|
||||
| `notifications` | 3+ | Notificaciones, preferencias |
|
||||
| `feature_flags` | 3 | Flags, user overrides, evaluations |
|
||||
|
||||
**Total estimado:** ~90 tablas
|
||||
|
||||
---
|
||||
|
||||
## Diagrama ER por Schema
|
||||
|
||||
### 1. Schema: auth
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ auth │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ users │──┬──<│ user_profiles │ │ oauth_accounts │ │
|
||||
│ ├─────────────────┤ │ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ id (PK) │ │ │ user_id (FK) │ │ user_id (FK) │ │
|
||||
│ │ email │ │ │ first_name │ │ provider │ │
|
||||
│ │ password_hash │ │ │ last_name │ │ provider_id │ │
|
||||
│ │ status │ │ │ avatar_url │ │ access_token │ │
|
||||
│ │ role │ │ │ subscription_id │ │ refresh_token │ │
|
||||
│ │ mfa_enabled │ │ │ trading_exp │ └─────────────────┘ │
|
||||
│ │ mfa_secret │ │ │ risk_tolerance │ │
|
||||
│ │ backup_codes │ │ │ ... │ │
|
||||
│ │ ... │ │ └─────────────────┘ │
|
||||
│ └─────────────────┘ │ │
|
||||
│ │ │ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ └──<│ sessions │ │ auth_logs │ │
|
||||
│ │ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ │ user_id (FK) │ │ user_id (FK) │ │
|
||||
│ │ │ token_hash │ │ event │ │
|
||||
│ │ │ ip_address │ │ ip_address │ │
|
||||
│ │ │ user_agent │ │ success │ │
|
||||
│ │ │ expires_at │ │ created_at │ │
|
||||
│ │ └─────────────────┘ └─────────────────┘ │
|
||||
│ │ │
|
||||
│ │ ┌──────────────────┐ ┌──────────────────┐ │
|
||||
│ └──<│ email_verify │ │ password_resets │ │
|
||||
│ ├──────────────────┤ ├──────────────────┤ │
|
||||
│ │ user_id (FK) │ │ user_id (FK) │ │
|
||||
│ │ token │ │ token │ │
|
||||
│ │ expires_at │ │ expires_at │ │
|
||||
│ └──────────────────┘ └──────────────────┘ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2. Schema: education
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ education │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐│
|
||||
│ │ categories │───<│ courses │───<│ modules │──<│ lessons ││
|
||||
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├───────────┤│
|
||||
│ │ id (PK) │ │ id (PK) │ │ id (PK) │ │ id (PK) ││
|
||||
│ │ name │ │ category_id │ │ course_id │ │ module_id ││
|
||||
│ │ slug │ │ title │ │ title │ │ title ││
|
||||
│ │ description │ │ difficulty │ │ order │ │ type ││
|
||||
│ └─────────────┘ │ duration │ └─────────────┘ │ content ││
|
||||
│ │ price │ │ video_url ││
|
||||
│ │ status │ │ duration ││
|
||||
│ └─────────────┘ └───────────┘│
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ┌─────────────────┐ │ ┌─────────────────┐ │ │
|
||||
│ │ enrollments │<──┘ │ quizzes │<───────────┘ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ user_id (FK) │ │ lesson_id (FK) │ │
|
||||
│ │ course_id (FK) │ │ title │ │
|
||||
│ │ progress_pct │ │ passing_score │ │
|
||||
│ │ status │ │ time_limit │ │
|
||||
│ │ completed_at │ └────────┬────────┘ │
|
||||
│ └────────┬────────┘ │ │
|
||||
│ │ │ │
|
||||
│ ┌────────┴────────┐ ┌────────────┴──────────┐ │
|
||||
│ │ progress │ │ quiz_questions │ │
|
||||
│ ├─────────────────┤ ├───────────────────────┤ │
|
||||
│ │ enrollment_id │ │ quiz_id (FK) │ │
|
||||
│ │ lesson_id (FK) │ │ question_text │ │
|
||||
│ │ completed │ │ question_type │ │
|
||||
│ │ completed_at │ │ options (JSONB) │ │
|
||||
│ └─────────────────┘ │ correct_answer │ │
|
||||
│ └───────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────┐ ┌───────────────────┐ ┌─────────────────┐ │
|
||||
│ │ certificates │ │ user_achievements │ │ gamification │ │
|
||||
│ ├───────────────────┤ ├───────────────────┤ │ _profile │ │
|
||||
│ │ user_id (FK) │ │ user_id (FK) │ ├─────────────────┤ │
|
||||
│ │ course_id (FK) │ │ achievement_type │ │ user_id (FK) │ │
|
||||
│ │ issued_at │ │ earned_at │ │ xp_total │ │
|
||||
│ │ certificate_url │ │ metadata │ │ level │ │
|
||||
│ └───────────────────┘ └───────────────────┘ │ streak_days │ │
|
||||
│ └─────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3. Schema: trading
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ trading │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ symbols │──────────────────────────────┐ │
|
||||
│ ├─────────────────┤ │ │
|
||||
│ │ id (PK) │ │ │
|
||||
│ │ name (XAUUSD) │ │ │
|
||||
│ │ base_currency │ │ │
|
||||
│ │ quote_currency │ │ │
|
||||
│ │ pip_value │ │ │
|
||||
│ │ is_active │ │ │
|
||||
│ └─────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────────┐ ┌────────────┴──────────┐ │
|
||||
│ └────<│ watchlists │ │ paper_accounts │ │
|
||||
│ ├─────────────────┤ ├───────────────────────┤ │
|
||||
│ │ user_id (FK) │ │ user_id (FK) │ │
|
||||
│ │ name │ │ balance │ │
|
||||
│ │ symbols[] │ │ equity │ │
|
||||
│ │ is_default │ │ margin_used │ │
|
||||
│ └─────────────────┘ │ realized_pnl │ │
|
||||
│ └───────────┬───────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │ │
|
||||
│ │ trade_bots │───<│ bot_positions │ │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ │ │
|
||||
│ │ id (PK) │ │ bot_id (FK) │ │ │
|
||||
│ │ user_id (FK) │ │ symbol │ │ │
|
||||
│ │ name │ │ side (BUY/SELL) │ │ │
|
||||
│ │ strategy │ │ size │ │ │
|
||||
│ │ status │ │ entry_price │ │ │
|
||||
│ │ risk_per_trade │ │ current_price │ │ │
|
||||
│ └─────────────────┘ │ pnl │ │ │
|
||||
│ └─────────────────┘ │ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────┐ ┌─────────────────┴───────┐ │
|
||||
│ │ paper_orders │ │ paper_positions │ │
|
||||
│ ├─────────────────────┤ ├─────────────────────────┤ │
|
||||
│ │ account_id (FK) │ │ account_id (FK) │ │
|
||||
│ │ symbol │ │ symbol │ │
|
||||
│ │ type (MARKET/LIMIT) │ │ side │ │
|
||||
│ │ side │ │ quantity │ │
|
||||
│ │ quantity │ │ entry_price │ │
|
||||
│ │ price │ │ current_price │ │
|
||||
│ │ status │ │ unrealized_pnl │ │
|
||||
│ │ filled_at │ │ stop_loss │ │
|
||||
│ └─────────────────────┘ │ take_profit │ │
|
||||
│ └─────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ signals │ │ user_signals │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ id (PK) │ │ user_id (FK) │ │
|
||||
│ │ symbol │ │ signal_id (FK) │ │
|
||||
│ │ direction │ │ followed_at │ │
|
||||
│ │ entry_price │ │ status │ │
|
||||
│ │ confidence │ └─────────────────┘ │
|
||||
│ │ created_at │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4. Schema: investment
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ investment │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ products │ (PAMM accounts: Atlas, Orion, Nova) │
|
||||
│ ├─────────────────────┤ │
|
||||
│ │ id (PK) │ │
|
||||
│ │ name │───────────────────────────────┐ │
|
||||
│ │ code (ATLAS/ORION) │ │ │
|
||||
│ │ risk_profile │ │ │
|
||||
│ │ target_return │ │ │
|
||||
│ │ max_drawdown │ │ │
|
||||
│ │ min_investment │ │ │
|
||||
│ │ management_fee │ │ │
|
||||
│ │ performance_fee │ │ │
|
||||
│ │ status │ │ │
|
||||
│ └─────────────────────┘ │ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐│ │
|
||||
│ │ risk_questionnaire │ │ accounts ││ │
|
||||
│ ├─────────────────────┤ ├─────────────────────┤│ │
|
||||
│ │ user_id (FK) │ │ id (PK) ││ │
|
||||
│ │ answers (JSONB) │ │ user_id (FK) │┘ │
|
||||
│ │ risk_score │ │ product_id (FK) │ │
|
||||
│ │ profile_type │ │ balance │ │
|
||||
│ │ completed_at │ │ status │ │
|
||||
│ └─────────────────────┘ │ performance_pct │ │
|
||||
│ │ opened_at │ │
|
||||
│ └──────────┬──────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────┐ │ │
|
||||
│ │ distributions │<─────────────┤ │
|
||||
│ ├─────────────────────┤ │ │
|
||||
│ │ account_id (FK) │ │ │
|
||||
│ │ amount │ │ │
|
||||
│ │ distribution_date │ │ │
|
||||
│ │ status │ │ │
|
||||
│ └─────────────────────┘ │ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────┐ ┌────────┴────────────┐ │
|
||||
│ │ withdrawal_requests │ │ transactions │ │
|
||||
│ ├─────────────────────┤ ├─────────────────────┤ │
|
||||
│ │ account_id (FK) │ │ account_id (FK) │ │
|
||||
│ │ amount │ │ type (deposit/ │ │
|
||||
│ │ status │ │ withdrawal) │ │
|
||||
│ │ requested_at │ │ amount │ │
|
||||
│ │ processed_at │ │ status │ │
|
||||
│ └─────────────────────┘ │ created_at │ │
|
||||
│ └─────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ daily_performance │ │
|
||||
│ ├─────────────────────┤ │
|
||||
│ │ product_id (FK) │ │
|
||||
│ │ date │ │
|
||||
│ │ nav │ │
|
||||
│ │ return_pct │ │
|
||||
│ │ drawdown │ │
|
||||
│ └─────────────────────┘ │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5. Schema: financial
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ financial │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ customers │ │ subscriptions │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ user_id (FK) │────────────────────│ user_id (FK) │ │
|
||||
│ │ stripe_id │ │ plan_id │ │
|
||||
│ │ created_at │ │ status │ │
|
||||
│ └─────────────────┘ │ stripe_sub_id │ │
|
||||
│ │ │ current_period │ │
|
||||
│ │ └─────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────┴─────────┐ ┌─────────────────┐ │
|
||||
│ │ wallets │───<│ wallet_trans │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ user_id (FK) │ │ wallet_id (FK) │ │
|
||||
│ │ balance │ │ type │ │
|
||||
│ │ currency │ │ amount │ │
|
||||
│ │ updated_at │ │ status │ │
|
||||
│ └─────────────────┘ │ reference_id │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ invoices │───<│ payments │ │ payment_methods │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ user_id (FK) │ │ invoice_id (FK) │ │ user_id (FK) │ │
|
||||
│ │ amount │ │ amount │ │ type │ │
|
||||
│ │ status │ │ method │ │ stripe_pm_id │ │
|
||||
│ │ due_date │ │ status │ │ is_default │ │
|
||||
│ │ paid_at │ │ processed_at │ │ last_four │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ currency_exchange_rates │ │ wallet_limits │ │
|
||||
│ ├─────────────────────────┤ ├─────────────────────────┤ │
|
||||
│ │ from_currency │ │ user_id (FK) │ │
|
||||
│ │ to_currency │ │ daily_limit │ │
|
||||
│ │ rate │ │ monthly_limit │ │
|
||||
│ │ updated_at │ │ single_tx_limit │ │
|
||||
│ └─────────────────────────┘ └─────────────────────────┘ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6. Schema: ml
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ ml │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ models │───<│ model_versions │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ id (PK) │ │ model_id (FK) │ │
|
||||
│ │ name │ │ version │ │
|
||||
│ │ type │ │ metrics (JSONB) │ │
|
||||
│ │ symbol │ │ file_path │ │
|
||||
│ │ status │ │ is_active │ │
|
||||
│ └─────────────────┘ │ created_at │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ predictions │───<│ prediction_outcomes │ │
|
||||
│ ├─────────────────┤ ├─────────────────────┤ │
|
||||
│ │ model_id (FK) │ │ prediction_id (FK) │ │
|
||||
│ │ symbol │ │ actual_high │ │
|
||||
│ │ horizon │ │ actual_low │ │
|
||||
│ │ predicted_high │ │ accuracy │ │
|
||||
│ │ predicted_low │ │ evaluated_at │ │
|
||||
│ │ confidence │ └─────────────────────┘ │
|
||||
│ │ created_at │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ feature_store │ │ llm_predictions │ │ llm_decisions │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ symbol │ │ id (PK) │ │ prediction_id │ │
|
||||
│ │ timestamp │ │ user_id (FK) │ │ decision │ │
|
||||
│ │ features (JSONB)│ │ symbol │ │ reasoning │ │
|
||||
│ │ created_at │ │ analysis │ │ confidence │ │
|
||||
│ └─────────────────┘ │ recommendation │ │ created_at │ │
|
||||
│ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ risk_events │ (Risk management alerts) │
|
||||
│ ├─────────────────┤ │
|
||||
│ │ user_id (FK) │ │
|
||||
│ │ event_type │ │
|
||||
│ │ severity │ │
|
||||
│ │ details (JSONB) │ │
|
||||
│ │ created_at │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 7. Schema: llm
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ llm │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ conversations │───<│ messages │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ id (PK) │ │ conversation_id │ │
|
||||
│ │ user_id (FK) │ │ role │ │
|
||||
│ │ title │ │ content │ │
|
||||
│ │ created_at │ │ tools_used │ │
|
||||
│ │ updated_at │ │ tokens_used │ │
|
||||
│ └─────────────────┘ │ created_at │ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │user_preferences │ │ user_memory │ │ embeddings │ │
|
||||
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
|
||||
│ │ user_id (FK) │ │ user_id (FK) │ │ id (PK) │ │
|
||||
│ │ model_provider │ │ key │ │ content_type │ │
|
||||
│ │ temperature │ │ value │ │ content_id │ │
|
||||
│ │ context_length │ │ category │ │ embedding │ │ (pgvector)
|
||||
│ │ system_prompt │ │ expires_at │ │ metadata │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8. Schema: audit
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ audit │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ audit_logs │ │ security_events │ │
|
||||
│ ├─────────────────────┤ ├─────────────────────┤ │
|
||||
│ │ id (PK) │ │ id (PK) │ │
|
||||
│ │ event_type │ │ category │ │
|
||||
│ │ event_status │ │ severity │ │
|
||||
│ │ severity │ │ user_id (FK) │ │
|
||||
│ │ user_id (FK) │ │ ip_address │ │
|
||||
│ │ resource_type │ │ event_code │ │
|
||||
│ │ resource_id │ │ is_blocked │ │
|
||||
│ │ action │ │ requires_review │ │
|
||||
│ │ old_values (JSONB) │ │ raw_data (JSONB) │ │
|
||||
│ │ new_values (JSONB) │ │ created_at │ │
|
||||
│ │ ip_address │ └─────────────────────┘ │
|
||||
│ │ created_at │ │
|
||||
│ └─────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ compliance_logs │ │ trading_audit │ │
|
||||
│ ├─────────────────────┤ ├─────────────────────┤ │
|
||||
│ │ id (PK) │ │ id (PK) │ │
|
||||
│ │ regulation │ │ user_id (FK) │ │
|
||||
│ │ requirement │ │ action │ │
|
||||
│ │ compliance_status │ │ symbol │ │
|
||||
│ │ risk_level │ │ order_details │ │
|
||||
│ │ evidence (JSONB) │ │ execution_time │ │
|
||||
│ │ remediation_req │ │ created_at │ │
|
||||
│ └─────────────────────┘ └─────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ api_request_logs │ │ data_access_logs │ │
|
||||
│ ├─────────────────────┤ ├─────────────────────┤ │
|
||||
│ │ endpoint │ │ user_id (FK) │ │
|
||||
│ │ method │ │ table_name │ │
|
||||
│ │ status_code │ │ operation │ │
|
||||
│ │ response_time_ms │ │ record_id │ │
|
||||
│ │ user_id (FK) │ │ fields_accessed │ │
|
||||
│ │ created_at │ │ created_at │ │
|
||||
│ └─────────────────────┘ └─────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 9. Schema: feature_flags
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ feature_flags │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ flags │───<│ user_flags │ │
|
||||
│ ├─────────────────────┤ ├─────────────────────┤ │
|
||||
│ │ id (PK) │ │ user_id (FK) │ │
|
||||
│ │ code (UNIQUE) │ │ flag_id (FK) │ │
|
||||
│ │ name │ │ is_enabled │ │
|
||||
│ │ description │ │ reason │ │
|
||||
│ │ category │ │ expires_at │ │
|
||||
│ │ status │ └─────────────────────┘ │
|
||||
│ │ rollout_percentage │ │
|
||||
│ │ targeting_rules │ ┌─────────────────────┐ │
|
||||
│ │ expires_at │───<│ evaluations │ │
|
||||
│ └─────────────────────┘ ├─────────────────────┤ │
|
||||
│ │ flag_id (FK) │ │
|
||||
│ │ user_id (FK) │ │
|
||||
│ │ flag_code │ │
|
||||
│ │ result │ │
|
||||
│ │ evaluation_reason │ │
|
||||
│ │ created_at │ │
|
||||
│ └─────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Extensiones PostgreSQL Utilizadas
|
||||
|
||||
| Extensión | Uso |
|
||||
|-----------|-----|
|
||||
| `uuid-ossp` | Generación de UUIDs |
|
||||
| `pgcrypto` | Funciones criptográficas |
|
||||
| `citext` | Texto case-insensitive (emails) |
|
||||
| `pgvector` | Embeddings para LLM (schema llm) |
|
||||
|
||||
---
|
||||
|
||||
## Convenciones de Nomenclatura
|
||||
|
||||
| Elemento | Convención | Ejemplo |
|
||||
|----------|------------|---------|
|
||||
| Tablas | snake_case, plural | `audit_logs`, `user_profiles` |
|
||||
| Columnas | snake_case | `created_at`, `user_id` |
|
||||
| PKs | `id` (UUID) | `id UUID PRIMARY KEY` |
|
||||
| FKs | `{tabla}_id` | `user_id`, `course_id` |
|
||||
| Índices | `idx_{tabla}_{columnas}` | `idx_users_email` |
|
||||
| Enums | `{schema}.{nombre}` | `auth.user_status` |
|
||||
| Timestamps | `_at` suffix | `created_at`, `updated_at` |
|
||||
|
||||
---
|
||||
|
||||
## Credenciales de Conexión
|
||||
|
||||
```
|
||||
Database: trading_platform
|
||||
User: trading_user
|
||||
Password: trading_dev_2026
|
||||
Host: localhost
|
||||
Port: 5432
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 2026-01-30
|
||||
**Generado por:** Claude Code (Opus 4.5) - Sprint 4
|
||||
638
docs/95-guias-desarrollo/TESTING-STRATEGY.md
Normal file
638
docs/95-guias-desarrollo/TESTING-STRATEGY.md
Normal file
@ -0,0 +1,638 @@
|
||||
# TESTING-STRATEGY.md - Trading Platform
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Fecha:** 2026-01-30
|
||||
**Proyecto:** trading-platform
|
||||
|
||||
---
|
||||
|
||||
## Resumen Ejecutivo
|
||||
|
||||
Este documento define la estrategia de testing para Trading Platform, cubriendo todos los niveles de la pirámide de testing y consideraciones específicas para sistemas de trading con ML.
|
||||
|
||||
---
|
||||
|
||||
## Niveles de Testing
|
||||
|
||||
### Pirámide de Testing
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ E2E │ 10%
|
||||
│ Tests │
|
||||
┌──┴─────────────┴──┐
|
||||
│ Integration │ 20%
|
||||
│ Tests │
|
||||
┌──┴───────────────────┴──┐
|
||||
│ Unit Tests │ 70%
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
| Nivel | Porcentaje | Responsabilidad |
|
||||
|-------|------------|-----------------|
|
||||
| Unit | 70% | Lógica de negocio aislada |
|
||||
| Integration | 20% | Interacción entre módulos |
|
||||
| E2E | 10% | Flujos críticos completos |
|
||||
|
||||
---
|
||||
|
||||
## 1. Unit Tests
|
||||
|
||||
### 1.1 Backend (TypeScript/Express)
|
||||
|
||||
**Framework:** Jest + ts-jest
|
||||
|
||||
**Estructura:**
|
||||
```
|
||||
apps/backend/src/
|
||||
├── modules/
|
||||
│ ├── auth/
|
||||
│ │ ├── services/
|
||||
│ │ │ ├── token.service.ts
|
||||
│ │ │ └── __tests__/
|
||||
│ │ │ └── token.service.spec.ts
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
```
|
||||
|
||||
**Convenciones:**
|
||||
- Archivos: `*.spec.ts` junto al archivo fuente
|
||||
- Naming: `describe('ServiceName')` > `describe('methodName')` > `it('should...')`
|
||||
- Mocks: Usar `jest.mock()` para dependencias externas
|
||||
|
||||
**Ejemplo:**
|
||||
```typescript
|
||||
// token.service.spec.ts
|
||||
describe('TokenService', () => {
|
||||
describe('generateAccessToken', () => {
|
||||
it('should return a valid JWT token', () => {
|
||||
const token = tokenService.generateAccessToken(mockUser);
|
||||
expect(token).toMatch(/^eyJ/);
|
||||
});
|
||||
|
||||
it('should include user id in payload', () => {
|
||||
const token = tokenService.generateAccessToken(mockUser);
|
||||
const decoded = jwt.decode(token);
|
||||
expect(decoded.sub).toBe(mockUser.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Coverage mínimo:** 80%
|
||||
|
||||
### 1.2 Frontend (React)
|
||||
|
||||
**Framework:** Vitest + React Testing Library
|
||||
|
||||
**Estructura:**
|
||||
```
|
||||
apps/frontend/src/
|
||||
├── components/
|
||||
│ ├── Button/
|
||||
│ │ ├── Button.tsx
|
||||
│ │ └── Button.test.tsx
|
||||
│ └── ...
|
||||
├── hooks/
|
||||
│ ├── useAuth.ts
|
||||
│ └── __tests__/
|
||||
│ └── useAuth.test.ts
|
||||
```
|
||||
|
||||
**Convenciones:**
|
||||
- Archivos: `*.test.tsx` o `*.test.ts`
|
||||
- Testing Library: Preferir queries por rol y accesibilidad
|
||||
- Avoid testing implementation details
|
||||
|
||||
**Ejemplo:**
|
||||
```typescript
|
||||
// Button.test.tsx
|
||||
describe('Button', () => {
|
||||
it('should call onClick when clicked', async () => {
|
||||
const handleClick = vi.fn();
|
||||
render(<Button onClick={handleClick}>Click me</Button>);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /click me/i }));
|
||||
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Coverage mínimo:** 70%
|
||||
|
||||
### 1.3 ML Engine (Python)
|
||||
|
||||
**Framework:** pytest + pytest-cov
|
||||
|
||||
**Estructura:**
|
||||
```
|
||||
apps/ml-engine/
|
||||
├── src/
|
||||
│ ├── models/
|
||||
│ │ └── range_predictor.py
|
||||
│ └── ...
|
||||
├── tests/
|
||||
│ ├── unit/
|
||||
│ │ ├── test_range_predictor.py
|
||||
│ │ └── test_feature_engineering.py
|
||||
│ └── ...
|
||||
```
|
||||
|
||||
**Convenciones:**
|
||||
- Archivos: `test_*.py`
|
||||
- Fixtures: Usar `@pytest.fixture` para datos de prueba
|
||||
- Parametrize: Usar `@pytest.mark.parametrize` para múltiples casos
|
||||
|
||||
**Ejemplo:**
|
||||
```python
|
||||
# test_range_predictor.py
|
||||
class TestRangePredictor:
|
||||
@pytest.fixture
|
||||
def predictor(self):
|
||||
return RangePredictor(symbol='XAUUSD')
|
||||
|
||||
def test_predict_returns_valid_range(self, predictor, sample_features):
|
||||
result = predictor.predict(sample_features)
|
||||
|
||||
assert 'predicted_high' in result
|
||||
assert 'predicted_low' in result
|
||||
assert result['predicted_high'] > result['predicted_low']
|
||||
|
||||
@pytest.mark.parametrize('confidence_threshold', [0.5, 0.7, 0.9])
|
||||
def test_filter_by_confidence(self, predictor, confidence_threshold):
|
||||
# Test filtering logic
|
||||
pass
|
||||
```
|
||||
|
||||
**Coverage mínimo:** 75%
|
||||
|
||||
---
|
||||
|
||||
## 2. Integration Tests
|
||||
|
||||
### 2.1 API Integration Tests
|
||||
|
||||
**Framework:** Jest + Supertest
|
||||
|
||||
**Objetivo:** Verificar endpoints con base de datos real (test DB)
|
||||
|
||||
**Setup:**
|
||||
```typescript
|
||||
// setup/test-db.ts
|
||||
beforeAll(async () => {
|
||||
await setupTestDatabase();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await teardownTestDatabase();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await clearTables();
|
||||
});
|
||||
```
|
||||
|
||||
**Ejemplo:**
|
||||
```typescript
|
||||
// auth.integration.spec.ts
|
||||
describe('Auth API Integration', () => {
|
||||
describe('POST /api/v1/auth/login', () => {
|
||||
it('should return tokens for valid credentials', async () => {
|
||||
// Arrange
|
||||
await createTestUser({ email: 'test@example.com', password: 'Test123!' });
|
||||
|
||||
// Act
|
||||
const response = await request(app)
|
||||
.post('/api/v1/auth/login')
|
||||
.send({ email: 'test@example.com', password: 'Test123!' });
|
||||
|
||||
// Assert
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.data).toHaveProperty('accessToken');
|
||||
expect(response.body.data).toHaveProperty('refreshToken');
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 2.2 Database Integration Tests
|
||||
|
||||
**Objetivo:** Verificar queries, triggers y funciones PL/pgSQL
|
||||
|
||||
```typescript
|
||||
// wallet.integration.spec.ts
|
||||
describe('Wallet Transactions', () => {
|
||||
it('should update balance after deposit', async () => {
|
||||
const wallet = await createTestWallet({ balance: 100 });
|
||||
|
||||
await db.query(
|
||||
'SELECT financial.process_transaction($1, $2, $3)',
|
||||
[wallet.id, 'deposit', 50]
|
||||
);
|
||||
|
||||
const updated = await getWallet(wallet.id);
|
||||
expect(updated.balance).toBe(150);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 2.3 ML Pipeline Integration Tests
|
||||
|
||||
**Objetivo:** Verificar pipeline completo de predicción
|
||||
|
||||
```python
|
||||
# test_prediction_pipeline.py
|
||||
class TestPredictionPipeline:
|
||||
def test_full_prediction_flow(self, db_session, redis_client):
|
||||
# 1. Load features from feature store
|
||||
features = load_features('XAUUSD', '2024-01-01')
|
||||
|
||||
# 2. Run prediction
|
||||
prediction = run_prediction(features)
|
||||
|
||||
# 3. Verify stored in database
|
||||
stored = db_session.query(Prediction).filter_by(
|
||||
symbol='XAUUSD'
|
||||
).first()
|
||||
|
||||
assert stored is not None
|
||||
assert stored.predicted_high == prediction['high']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. E2E Tests
|
||||
|
||||
### 3.1 Framework
|
||||
|
||||
**Herramienta:** Playwright
|
||||
|
||||
**Estructura:**
|
||||
```
|
||||
e2e/
|
||||
├── tests/
|
||||
│ ├── auth/
|
||||
│ │ ├── login.spec.ts
|
||||
│ │ └── register.spec.ts
|
||||
│ ├── trading/
|
||||
│ │ └── place-order.spec.ts
|
||||
│ └── ...
|
||||
├── fixtures/
|
||||
│ └── test-data.ts
|
||||
└── playwright.config.ts
|
||||
```
|
||||
|
||||
### 3.2 Flujos Críticos a Testear
|
||||
|
||||
| Prioridad | Flujo | Descripción |
|
||||
|-----------|-------|-------------|
|
||||
| P0 | Login/Logout | Autenticación completa |
|
||||
| P0 | Registro + Verificación | Nuevo usuario |
|
||||
| P0 | Depositar fondos | Wallet + Stripe |
|
||||
| P1 | Abrir cuenta inversión | PAMM flow |
|
||||
| P1 | Paper trading | Orden → Posición |
|
||||
| P2 | Completar curso | Education flow |
|
||||
| P2 | 2FA setup | Security flow |
|
||||
|
||||
### 3.3 Ejemplo E2E
|
||||
|
||||
```typescript
|
||||
// e2e/tests/auth/login.spec.ts
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Login Flow', () => {
|
||||
test('user can login with valid credentials', async ({ page }) => {
|
||||
// Navigate
|
||||
await page.goto('/login');
|
||||
|
||||
// Fill form
|
||||
await page.getByLabel('Email').fill('test@example.com');
|
||||
await page.getByLabel('Password').fill('Test123!');
|
||||
|
||||
// Submit
|
||||
await page.getByRole('button', { name: /sign in/i }).click();
|
||||
|
||||
// Verify redirect to dashboard
|
||||
await expect(page).toHaveURL('/dashboard');
|
||||
await expect(page.getByText('Welcome back')).toBeVisible();
|
||||
});
|
||||
|
||||
test('shows error for invalid credentials', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.getByLabel('Email').fill('wrong@example.com');
|
||||
await page.getByLabel('Password').fill('wrongpassword');
|
||||
await page.getByRole('button', { name: /sign in/i }).click();
|
||||
|
||||
await expect(page.getByText(/invalid credentials/i)).toBeVisible();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Testing Específico para Trading
|
||||
|
||||
### 4.1 Backtesting de Modelos ML
|
||||
|
||||
**Objetivo:** Validar performance de modelos con datos históricos
|
||||
|
||||
```python
|
||||
# tests/backtesting/test_model_performance.py
|
||||
class TestModelBacktest:
|
||||
def test_model_accuracy_oos(self):
|
||||
"""Test Out-of-Sample accuracy"""
|
||||
# Load 12 months of excluded data
|
||||
oos_data = load_oos_data('2025-01', '2025-12')
|
||||
|
||||
# Run predictions
|
||||
predictions = model.predict(oos_data.features)
|
||||
|
||||
# Calculate metrics
|
||||
accuracy = calculate_directional_accuracy(
|
||||
predictions,
|
||||
oos_data.actual
|
||||
)
|
||||
|
||||
# Minimum threshold
|
||||
assert accuracy >= 0.55, f"OOS accuracy {accuracy} below threshold"
|
||||
```
|
||||
|
||||
### 4.2 Paper Trading Tests
|
||||
|
||||
**Objetivo:** Simular operaciones sin riesgo
|
||||
|
||||
```typescript
|
||||
// tests/paper-trading/order-execution.spec.ts
|
||||
describe('Paper Trading Order Execution', () => {
|
||||
it('should execute market order at current price', async () => {
|
||||
const account = await createPaperAccount({ balance: 10000 });
|
||||
|
||||
const order = await paperTradingService.placeOrder({
|
||||
accountId: account.id,
|
||||
symbol: 'XAUUSD',
|
||||
side: 'BUY',
|
||||
type: 'MARKET',
|
||||
quantity: 0.1
|
||||
});
|
||||
|
||||
expect(order.status).toBe('FILLED');
|
||||
expect(order.filledPrice).toBeCloseTo(currentPrice, 2);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 4.3 Risk Management Tests
|
||||
|
||||
```typescript
|
||||
// tests/risk/position-limits.spec.ts
|
||||
describe('Position Limits', () => {
|
||||
it('should reject order exceeding max position size', async () => {
|
||||
const result = await riskService.validateOrder({
|
||||
userId: testUser.id,
|
||||
symbol: 'XAUUSD',
|
||||
quantity: 100 // Exceeds limit
|
||||
});
|
||||
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.reason).toContain('position limit');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Mocking Strategies
|
||||
|
||||
### 5.1 External Services
|
||||
|
||||
| Servicio | Estrategia |
|
||||
|----------|------------|
|
||||
| Stripe | Mock SDK responses |
|
||||
| Binance API | Recorded fixtures |
|
||||
| Redis | Redis-mock o real |
|
||||
| LLM (Claude) | Canned responses |
|
||||
|
||||
### 5.2 Mock de Stripe
|
||||
|
||||
```typescript
|
||||
// __mocks__/stripe.ts
|
||||
export const mockStripe = {
|
||||
customers: {
|
||||
create: jest.fn().mockResolvedValue({ id: 'cus_test123' }),
|
||||
retrieve: jest.fn().mockResolvedValue({ id: 'cus_test123', email: 'test@example.com' })
|
||||
},
|
||||
subscriptions: {
|
||||
create: jest.fn().mockResolvedValue({ id: 'sub_test123', status: 'active' })
|
||||
}
|
||||
};
|
||||
|
||||
jest.mock('stripe', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => mockStripe)
|
||||
}));
|
||||
```
|
||||
|
||||
### 5.3 Mock de Market Data
|
||||
|
||||
```python
|
||||
# tests/fixtures/market_data.py
|
||||
@pytest.fixture
|
||||
def mock_binance_client(mocker):
|
||||
mock = mocker.patch('binance.Client')
|
||||
mock.return_value.get_klines.return_value = [
|
||||
[1704067200000, "2050.00", "2055.00", "2048.00", "2052.00", "1000"],
|
||||
[1704067260000, "2052.00", "2058.00", "2051.00", "2056.00", "1200"],
|
||||
]
|
||||
return mock
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. CI/CD Integration
|
||||
|
||||
### 6.1 GitHub Actions Workflow
|
||||
|
||||
```yaml
|
||||
# .github/workflows/test.yml
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Run unit tests
|
||||
run: npm run test:unit
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
integration-tests:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_DB: trading_test
|
||||
POSTGRES_USER: test
|
||||
POSTGRES_PASSWORD: test
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis:7
|
||||
ports:
|
||||
- 6379:6379
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run integration tests
|
||||
run: npm run test:integration
|
||||
|
||||
e2e-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Playwright
|
||||
run: npx playwright install --with-deps
|
||||
- name: Run E2E tests
|
||||
run: npm run test:e2e
|
||||
```
|
||||
|
||||
### 6.2 Pre-commit Hooks
|
||||
|
||||
```yaml
|
||||
# .pre-commit-config.yaml
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: test-affected
|
||||
name: Run affected tests
|
||||
entry: npm run test:affected
|
||||
language: system
|
||||
pass_filenames: false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Test Data Management
|
||||
|
||||
### 7.1 Factories
|
||||
|
||||
```typescript
|
||||
// tests/factories/user.factory.ts
|
||||
export const userFactory = {
|
||||
build: (overrides = {}) => ({
|
||||
id: faker.string.uuid(),
|
||||
email: faker.internet.email(),
|
||||
passwordHash: bcrypt.hashSync('Test123!', 10),
|
||||
status: 'active',
|
||||
role: 'user',
|
||||
...overrides
|
||||
}),
|
||||
|
||||
create: async (overrides = {}) => {
|
||||
const user = userFactory.build(overrides);
|
||||
await db.query('INSERT INTO auth.users ...', [user]);
|
||||
return user;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 7.2 Fixtures para ML
|
||||
|
||||
```python
|
||||
# tests/conftest.py
|
||||
@pytest.fixture(scope='session')
|
||||
def sample_ohlcv_data():
|
||||
return pd.read_parquet('tests/fixtures/xauusd_sample.parquet')
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def trained_model():
|
||||
return joblib.load('tests/fixtures/model_v1.joblib')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Comandos de Ejecución
|
||||
|
||||
### Backend
|
||||
```bash
|
||||
# Unit tests
|
||||
npm run test:unit
|
||||
|
||||
# Integration tests
|
||||
npm run test:integration
|
||||
|
||||
# Coverage
|
||||
npm run test:coverage
|
||||
```
|
||||
|
||||
### Frontend
|
||||
```bash
|
||||
# Unit tests
|
||||
npm run test
|
||||
|
||||
# Watch mode
|
||||
npm run test:watch
|
||||
|
||||
# Coverage
|
||||
npm run test:coverage
|
||||
```
|
||||
|
||||
### ML Engine
|
||||
```bash
|
||||
# All tests
|
||||
pytest
|
||||
|
||||
# Unit only
|
||||
pytest tests/unit/
|
||||
|
||||
# With coverage
|
||||
pytest --cov=src --cov-report=html
|
||||
```
|
||||
|
||||
### E2E
|
||||
```bash
|
||||
# Headless
|
||||
npm run test:e2e
|
||||
|
||||
# UI mode
|
||||
npm run test:e2e:ui
|
||||
|
||||
# Specific browser
|
||||
npx playwright test --project=chromium
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Métricas y Umbrales
|
||||
|
||||
| Métrica | Umbral | Medición |
|
||||
|---------|--------|----------|
|
||||
| Unit Test Coverage | ≥ 70% | Codecov |
|
||||
| Integration Test Coverage | ≥ 50% | Codecov |
|
||||
| E2E Critical Paths | 100% pass | Playwright |
|
||||
| ML Model Accuracy (OOS) | ≥ 55% | Custom |
|
||||
| Test Execution Time | < 10 min | CI |
|
||||
|
||||
---
|
||||
|
||||
## 10. Próximos Pasos
|
||||
|
||||
1. **Implementar tests faltantes por módulo**
|
||||
2. **Configurar CI/CD con GitHub Actions**
|
||||
3. **Integrar Codecov para tracking de coverage**
|
||||
4. **Crear fixtures de datos de mercado**
|
||||
5. **Implementar backtesting automatizado**
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 2026-01-30
|
||||
**Generado por:** Claude Code (Opus 4.5) - Sprint 4
|
||||
Loading…
Reference in New Issue
Block a user