# 🎭 Sistema de Mocks - MSW e Banco em Memória

Documentação completa do sistema de mocks implementado com MSW (Mock Service Worker) para desenvolvimento e testes.

## 🎯 Visão Geral

O sistema de mocks permite desenvolvimento e testes sem dependências externas, simulando um banco de dados real com persistência em memória durante a sessão.

### ✨ Características

- 🗄️ **Banco de Dados em Memória** - Persistência durante a sessão
- 🔄 **CRUD Completo** - Create, Read, Update, Delete funcionais
- 📝 **Logs Detalhados** - Console.log para cada operação
- 🎯 **Reset Fácil** - Volta ao estado inicial quando necessário
- 🚀 **Performance** - Respostas instantâneas sem latência de rede
- 🧪 **Testes Determinísticos** - Dados consistentes para testes

## 📁 Estrutura dos Arquivos

```
src/mocks/
├── data/
│   ├── portfolios.json          # Dados iniciais dos portfolios
│   └── portfolio-assets.json    # Dados iniciais dos portfolio assets
├── database.ts                  # Sistema de banco de dados em memória
├── handlers.ts                  # Handlers MSW para interceptar requisições
├── setup.ts                     # Configuração e controle do MSW
├── server.ts                    # Servidor MSW para Node.js (testes)
└── browser.ts                   # Configuração MSW para browser
```

## 🗃️ Banco de Dados em Memória

### Classe MockDatabase

A classe `MockDatabase` simula um banco de dados completo:

```typescript
class MockDatabase {
  private portfolios: Portfolio[] = [];
  private assets: PortfolioAsset[] = [];
  private excludedTransactions: ExcludedTransaction[] = [];

  // Inicialização com dados dos arquivos JSON
  constructor() {
    this.loadInitialData();
  }

  // Operações CRUD para Portfolios
  getAllPortfolios(): Portfolio[]
  getPortfolioById(id: string): Portfolio | null
  getPortfoliosByUserId(userId: string): Portfolio[]
  createPortfolio(data: CreatePortfolioDto): Portfolio
  updatePortfolio(id: string, data: UpdatePortfolioDto): Portfolio
  deletePortfolio(id: string): boolean

  // Operações CRUD para Portfolio Assets
  getAllPortfolioAssets(): PortfolioAsset[]
  getPortfolioAssetById(id: string): PortfolioAsset | null
  getPortfolioAssetsByPortfolioId(portfolioId: string): PortfolioAsset[]
  getPortfolioAssetsByType(type: AssetType): PortfolioAsset[]
  createPortfolioAsset(data: CreatePortfolioAssetDto): PortfolioAsset
  updatePortfolioAsset(id: string, data: UpdatePortfolioAssetDto): PortfolioAsset
  deletePortfolioAsset(id: string): boolean

  // Métodos auxiliares
  getPortfolioValue(portfolioId: string): number
  getAssetPrice(symbol: string): number
  getStats(): DatabaseStats
  reset(): void
}
```

### Persistência em Memória

```typescript
// Os dados ficam disponíveis durante toda a sessão
const mockDatabase = new MockDatabase();

// Exemplo: criar um portfolio
const newPortfolio = mockDatabase.createPortfolio({
  name: 'Novo Portfolio',
  userId: 'user_123'
});

// O portfolio fica disponível para outras requisições
const portfolios = mockDatabase.getAllPortfolios(); // Inclui o novo portfolio
```

## 🔧 Handlers MSW

### Estrutura dos Handlers

```typescript
export const handlers = [
  // Portfolios
  rest.get('/portfolios', (req, res, ctx) => {
    const userId = req.url.searchParams.get('userId');
    const portfolios = userId 
      ? mockDatabase.getPortfoliosByUserId(userId)
      : mockDatabase.getAllPortfolios();
    
    console.log(`📝 MSW: GET /portfolios - Found ${portfolios.length} portfolios`);
    return res(ctx.status(200), ctx.json(portfolios));
  }),

  rest.post('/portfolios', async (req, res, ctx) => {
    const data = await req.json();
    const portfolio = mockDatabase.createPortfolio(data);
    
    console.log(`📝 MSW: POST /portfolios - Created:`, portfolio);
    return res(ctx.status(201), ctx.json(portfolio));
  }),

  // Portfolio Assets
  rest.get('/portfolio-assets', (req, res, ctx) => {
    const portfolioId = req.url.searchParams.get('portfolioId');
    const assets = portfolioId
      ? mockDatabase.getPortfolioAssetsByPortfolioId(portfolioId)
      : mockDatabase.getAllPortfolioAssets();
    
    console.log(`📝 MSW: GET /portfolio-assets - Found ${assets.length} assets`);
    return res(ctx.status(200), ctx.json(assets));
  }),

  // Dashboard do Asset
  rest.get('/portfolio-assets/:id/dashboard', (req, res, ctx) => {
    const { id } = req.params;
    const asset = mockDatabase.getPortfolioAssetById(id as string);
    
    if (!asset) {
      return res(ctx.status(404), ctx.json({ message: 'Asset not found' }));
    }

    const dashboard = {
      basicInfo: {
        ticker: asset.symbol,
        name: asset.name,
        quantity: asset.quantity,
        averagePrice: asset.price,
        lastQuote: asset.price * 1.02, // Simula variação
        investedValue: asset.quantity * asset.price,
        grossValue: asset.quantity * asset.price * 1.02,
        result: asset.quantity * asset.price * 0.02,
        resultPercentage: 2.0
      },
      monthlyHistory: generateMonthlyHistory(asset),
      historicalChart: generateHistoricalChart(asset),
      transactions: generateTransactions(asset)
    };

    console.log(`📝 MSW: GET /portfolio-assets/${id}/dashboard - Generated dashboard`);
    return res(ctx.status(200), ctx.json(dashboard));
  }),

  // Estatísticas do sistema
  rest.get('/stats', (req, res, ctx) => {
    const stats = mockDatabase.getStats();
    console.log(`📝 MSW: GET /stats -`, stats);
    return res(ctx.status(200), ctx.json(stats));
  }),

  // Reset do sistema
  rest.post('/reset', (req, res, ctx) => {
    mockDatabase.reset();
    console.log(`🔄 MSW: POST /reset - Database reset to initial state`);
    return res(ctx.status(200), ctx.json({ message: 'Database reset successfully' }));
  })
];
```

### Validação de API Key

```typescript
const validateApiKey = (req: RestRequest): boolean => {
  const apiKey = req.headers.get('x-api-key');
  const validApiKey = 'JOqyUcP45H875xmuI2gT3H9dDK42I2Wt';
  
  if (!apiKey || apiKey !== validApiKey) {
    console.log(`❌ MSW: Invalid or missing API key: ${apiKey}`);
    return false;
  }
  
  return true;
};

// Uso nos handlers
rest.get('/portfolios', (req, res, ctx) => {
  if (!validateApiKey(req)) {
    return res(ctx.status(401), ctx.json({ message: 'Invalid API key' }));
  }
  
  // Continua com a lógica...
}),
```

## 🚀 Configuração e Uso

### Setup Automático

```typescript
// src/mocks/setup.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

const server = setupServer(...handlers);

export const startMSW = () => {
  server.listen({
    onUnhandledRequest: 'warn',
  });
  console.log('🚀 MSW Server started - Mocking API responses');
};

export const stopMSW = () => {
  server.close();
  console.log('🛑 MSW Server stopped');
};

export const resetMSW = () => {
  server.resetHandlers();
  console.log('🔄 MSW Handlers reset');
};
```

### Inicialização Automática

```typescript
// main.ts (desenvolvimento)
async function bootstrap() {
  if (process.env.NODE_ENV === 'development') {
    const { startMSW } = await import('./mocks/setup');
    startMSW();
  }

  const app = await NestJS.create(AppModule);
  await app.listen(3333);
}
```

### Configuração para Testes

```typescript
// test/setup-e2e.ts
import { startMSW, stopMSW } from '../src/mocks/setup';

beforeAll(async () => {
  await startMSW();
});

afterAll(async () => {
  await stopMSW();
});

beforeEach(() => {
  // Reset database para cada teste
  mockDatabase.reset();
});
```

## 📊 Dados Iniciais

### Portfolios (portfolios.json)

```json
[
  {
    "id": "portfolio_1",
    "name": "Portfolio Principal",
    "description": "Investimentos principais do usuário",
    "userId": "user_123",
    "totalValue": 50000.0,
    "createdAt": "2024-01-01T10:00:00Z",
    "updatedAt": "2024-01-01T10:00:00Z"
  },
  {
    "id": "portfolio_2",
    "name": "Portfolio Conservador",
    "description": "Investimentos de baixo risco",
    "userId": "user_456",
    "totalValue": 25000.0,
    "createdAt": "2024-01-01T11:00:00Z",
    "updatedAt": "2024-01-01T11:00:00Z"
  }
]
```

### Portfolio Assets (portfolio-assets.json)

```json
[
  {
    "id": "asset_1",
    "name": "Petrobras PN",
    "symbol": "PETR4",
    "type": "STOCK",
    "quantity": 100,
    "price": 30.50,
    "totalValue": 3050.0,
    "portfolioId": "portfolio_1",
    "createdAt": "2024-01-01T10:00:00Z",
    "updatedAt": "2024-01-01T10:00:00Z"
  },
  {
    "id": "asset_2",
    "name": "Fundo DI",
    "symbol": "FDI",
    "type": "FUND",
    "quantity": 1000,
    "price": 25.0,
    "totalValue": 25000.0,
    "portfolioId": "portfolio_2",
    "createdAt": "2024-01-01T11:00:00Z",
    "updatedAt": "2024-01-01T11:00:00Z"
  }
]
```

## 🔄 Operações CRUD

### Exemplo Completo: Gestão de Portfolio

```typescript
// 1. Criar portfolio
const newPortfolio = mockDatabase.createPortfolio({
  name: 'Meu Novo Portfolio',
  description: 'Portfolio criado via API',
  userId: 'user_789'
});
console.log('Portfolio criado:', newPortfolio.id);

// 2. Buscar portfolios do usuário
const userPortfolios = mockDatabase.getPortfoliosByUserId('user_789');
console.log('Portfolios do usuário:', userPortfolios.length);

// 3. Adicionar asset ao portfolio
const newAsset = mockDatabase.createPortfolioAsset({
  name: 'Vale S.A.',
  symbol: 'VALE3',
  type: 'STOCK',
  quantity: 50,
  price: 65.80,
  portfolioId: newPortfolio.id
});

// 4. Calcular valor total do portfolio
const totalValue = mockDatabase.getPortfolioValue(newPortfolio.id);
console.log('Valor total:', totalValue);

// 5. Atualizar portfolio
const updatedPortfolio = mockDatabase.updatePortfolio(newPortfolio.id, {
  name: 'Portfolio Atualizado',
  description: 'Nova descrição'
});

// 6. Remover asset
const removed = mockDatabase.deletePortfolioAsset(newAsset.id);
console.log('Asset removido:', removed);
```

## 📝 Logs do Sistema

### Tipos de Logs

```typescript
// Operações de banco
console.log('📝 Database: Portfolio criado:', portfolio);
console.log('📝 Database: Asset atualizado:', asset);
console.log('📝 Database: Portfolio removido:', { id });

// Operações MSW
console.log('📝 MSW: GET /portfolios - Found 2 portfolios');
console.log('📝 MSW: POST /portfolio-assets - Created asset');

// Sistema
console.log('🚀 MSW Server started - Mocking API responses');
console.log('🔄 Database: Reset to initial state');
console.log('⚠️ MSW: Invalid API key provided');
```

### Exemplo de Saída

```
🚀 MSW Server started - Mocking API responses
📝 MSW: GET /portfolios - Found 2 portfolios
📝 MSW: POST /portfolios - Created: { id: 'portfolio_1234567890', name: 'Novo Portfolio' }
📝 Database: Portfolio criado: { id: 'portfolio_1234567890', name: 'Novo Portfolio', userId: 'user_123' }
📝 MSW: GET /portfolios?userId=user_123 - Found 1 portfolios
📝 MSW: GET /portfolio-assets/asset_1/dashboard - Generated dashboard
```

## 🧪 Testando o Sistema

### Teste de Persistência

```bash
# 1. Criar um portfolio
curl -X POST "http://localhost:3333/portfolios" \
  -H "x-api-key: JOqyUcP45H875xmuI2gT3H9dDK42I2Wt" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Portfolio de Teste",
    "userId": "user_test"
  }'

# 2. Listar portfolios (deve incluir o novo)
curl "http://localhost:3333/portfolios" \
  -H "x-api-key: JOqyUcP45H875xmuI2gT3H9dDK42I2Wt"

# 3. Ver estatísticas
curl "http://localhost:3333/stats" \
  -H "x-api-key: JOqyUcP45H875xmuI2gT3H9dDK42I2Wt"
```

### Reset do Sistema

```bash
# Resetar para estado inicial
curl -X POST "http://localhost:3333/reset" \
  -H "x-api-key: JOqyUcP45H875xmuI2gT3H9dDK42I2Wt"

# Verificar que voltou ao estado inicial
curl "http://localhost:3333/portfolios" \
  -H "x-api-key: JOqyUcP45H875xmuI2gT3H9dDK42I2Wt"
```

## 🔧 Personalização

### Adicionando Novos Endpoints

```typescript
// 1. Adicionar método ao MockDatabase
class MockDatabase {
  getPortfoliosByCategory(category: string): Portfolio[] {
    return this.portfolios.filter(p => p.category === category);
  }
}

// 2. Adicionar handler MSW
rest.get('/portfolios/category/:category', (req, res, ctx) => {
  const { category } = req.params;
  const portfolios = mockDatabase.getPortfoliosByCategory(category as string);
  
  console.log(`📝 MSW: GET /portfolios/category/${category} - Found ${portfolios.length}`);
  return res(ctx.status(200), ctx.json(portfolios));
}),
```

### Simulando Latência de Rede

```typescript
rest.get('/portfolios', async (req, res, ctx) => {
  // Simular latência de 100-500ms
  const delay = Math.random() * 400 + 100;
  await new Promise(resolve => setTimeout(resolve, delay));
  
  const portfolios = mockDatabase.getAllPortfolios();
  return res(ctx.status(200), ctx.json(portfolios));
}),
```

### Simulando Erros

```typescript
rest.get('/portfolios/:id', (req, res, ctx) => {
  const { id } = req.params;
  
  // Simular erro 500 em 5% das requisições
  if (Math.random() < 0.05) {
    console.log(`❌ MSW: Simulated error for portfolio ${id}`);
    return res(ctx.status(500), ctx.json({ message: 'Internal server error' }));
  }
  
  const portfolio = mockDatabase.getPortfolioById(id as string);
  if (!portfolio) {
    return res(ctx.status(404), ctx.json({ message: 'Portfolio not found' }));
  }
  
  return res(ctx.status(200), ctx.json(portfolio));
}),
```

## 🎯 Vantagens do Sistema

### Para Desenvolvimento

- ✅ **Desenvolvimento Offline** - Não precisa de APIs externas
- ✅ **Dados Consistentes** - Sempre os mesmos dados iniciais
- ✅ **Resposta Rápida** - Sem latência de rede
- ✅ **Debug Fácil** - Logs detalhados de todas as operações
- ✅ **Modificação Simples** - Fácil de ajustar dados e comportamentos

### Para Testes

- ✅ **Testes Determinísticos** - Resultados previsíveis
- ✅ **Isolamento** - Cada teste pode ter seu próprio estado
- ✅ **Performance** - Testes executam rapidamente
- ✅ **Cobertura Completa** - Pode testar todos os cenários
- ✅ **Sem Dependências** - Não precisa de banco de dados real

### Para Demonstrações

- ✅ **Dados Realistas** - Simula comportamento real
- ✅ **Funcionalidades Completas** - CRUD funcional
- ✅ **Apresentações** - Funciona sem conectividade
- ✅ **Prototipação** - Ideal para validar conceitos

## 🔄 Migração para Produção

### Desabilitando Mocks

```typescript
// main.ts
async function bootstrap() {
  // Só usar mocks em desenvolvimento
  if (process.env.NODE_ENV === 'development') {
    const { startMSW } = await import('./mocks/setup');
    startMSW();
  }

  const app = await NestJS.create(AppModule);
  await app.listen(3333);
}
```

### Configuração Condicional

```typescript
// app.module.ts
@Module({
  imports: [
    // Banco real apenas em produção
    process.env.NODE_ENV === 'production' 
      ? MongooseModule.forRoot(process.env.MONGODB_URI)
      : [], // Sem banco em desenvolvimento (usa mocks)
      
    PortfoliosModule,
    PortfolioAssetsModule,
  ],
})
export class AppModule {}
```

## 📚 Referências

- [MSW Documentation](https://mswjs.io/docs/)
- [MSW Node.js Integration](https://mswjs.io/docs/getting-started/integrate/node)
- [Jest MSW Setup](https://mswjs.io/docs/getting-started/integrate/node#jest)
- [Testing with MSW](https://kentcdodds.com/blog/stop-mocking-fetch)

---

## 🎉 Conclusão

O sistema de mocks com MSW oferece:

- 🚀 **Desenvolvimento Ágil** - Sem dependências externas
- 🧪 **Testes Robustos** - Dados consistentes e previsíveis
- 🎯 **Fácil Manutenção** - Estrutura clara e extensível
- 📊 **Dados Realistas** - Simula comportamento real da API
- 🔧 **Flexibilidade** - Fácil customização e extensão

Use este sistema para acelerar o desenvolvimento e garantir testes confiáveis! 🎭
