Simulación de APIs externas · HealthLab · PagosRapidos · WhatsApp Business
#!/bin/bash # NutriSync — Inicia WireMock para simular HealthLab, PagosRapidos y WhatsApp APIs docker run -d --name nutrisync-wiremock \ -p 8080:8080 \ -v $(pwd)/wiremock:/home/wiremock \ --restart unless-stopped \ wiremock/wiremock:3.5.4 \ --global-response-templating \ --verbose # Verificar que arrancó correctamente echo "Esperando WireMock..." until curl -s http://localhost:8080/__admin/health > /dev/null; do sleep 1 done echo "✅ WireMock activo en http://localhost:8080" echo "📊 Admin UI: http://localhost:8080/__admin/mappings"
{ "request": { "method": "GET", "urlPathPattern": "/v2/patient/[0-9]+/results", "headers": { "X-Api-Key": { "present": true } } }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "jsonBody": { "patientId": "{{request.pathSegments.[2]}}", "analysisDate": "2026-06-10", "status": "completed", "markers": [ { "code": "GLU", "value": 94, "unit": "mg/dL", "range": "70-100" }, { "code": "HBA1C", "value": 5.4, "unit": "%", "range": "<5.7" } ] }, "transformers": ["response-template"] } }
{ "request": { "method": "POST", "urlPath": "/v1/subscription/create", "bodyPatterns": [ { "matchesJsonPath": "$.clinicId" }, { "matchesJsonPath": "$.plan" }, { "matchesJsonPath": "$.paymentMethod" } ] }, "response": { "status": 201, "jsonBody": { "subscriptionId": "{{randomValue type='UUID'}}", "clinicId": "{{jsonPath request.body '$.clinicId'}}", "plan": "{{jsonPath request.body '$.plan'}}", "status": "trial", "trialEndsAt": "2026-07-16T00:00:00Z" }, "transformers": ["response-template"] } }
{ "request": { "method": "POST", "urlPath": "/messages", "headers": { "Authorization": { "matches": "Bearer .+" } }, "bodyPatterns": [ { "matchesJsonPath": "$.to" }, { "matchesJsonPath": "$.type" } ] }, "response": { "status": 200, "jsonBody": { "messages": [{ "id": "wamid.{{randomValue type='UUID'}}", "to": "{{jsonPath request.body '$.to'}}", "status": "sent" }] }, "transformers": ["response-template"] } }
{ "mappings": [ { "scenarioName": "Subscription Lifecycle", "requiredScenarioState": "Started", "newScenarioState": "Trial", "request": { "method": "POST", "urlPath": "/v1/subscription/create" }, "response": { "status": 201, "jsonBody": { "id": "sub-ns-001", "status": "trial", "daysLeft": 14 } } }, { "scenarioName": "Subscription Lifecycle", "requiredScenarioState": "Trial", "newScenarioState": "Active", "request": { "method": "GET", "urlPath": "/v1/subscription/sub-ns-001" }, "response": { "status": 200, "jsonBody": { "id": "sub-ns-001", "status": "active", "plan": "pro" } } }, { "scenarioName": "Subscription Lifecycle", "requiredScenarioState": "Active", "newScenarioState": "Cancelled", "request": { "method": "PUT", "urlPath": "/v1/subscription/sub-ns-001/cancel" }, "response": { "status": 200, "jsonBody": { "id": "sub-ns-001", "status": "cancelled" } } } ] }
| Endpoint | Tipo de Fallo | Configuración | Qué prueba |
|---|---|---|---|
| GET /v2/patient/{id}/results | SLOW 8s | fixedDelayMilliseconds: 8000 | Circuit breaker HealthLab |
| POST /v1/subscription/create | 503 Service | status: 503, body: {error: "gateway_timeout"} | Retry logic en pagos |
| POST /messages | CONN RESET | fault: "CONNECTION_RESET_BY_PEER" | Cola de notificaciones WA |
| GET /v2/patient/*/results | RANDOM DELAY | lognormal median: 500ms, sigma: 0.6 | Timeouts variables lab |
{ "mappings": [ { "name": "healthlab-slow-response", "request": { "method": "GET", "urlPathPattern": "/v2/patient/slow/results" }, "response": { "status": 200, "fixedDelayMilliseconds": 8000, "jsonBody": { "data": "delayed response" } } }, { "name": "whatsapp-connection-reset", "request": { "method": "POST", "urlPath": "/messages/fail" }, "response": { "fault": "CONNECTION_RESET_BY_PEER" } }, { "name": "pagos-503", "request": { "method": "POST", "urlPath": "/v1/subscription/create", "headers": { "X-Force-Error": { "equalTo": "503" } } }, "response": { "status": 503, "jsonBody": { "error": "gateway_timeout", "retryAfter": 30 } } } ] }
X-Force-Error: 503 para no interferir con los tests de happy-path.
name: NutriSync Integration Tests on: push: branches: [develop, main] pull_request: branches: [main] jobs: integration-tests: runs-on: ubuntu-latest services: wiremock: image: wiremock/wiremock:3.5.4 ports: - 8080:8080 options: >- --health-cmd "curl -f http://localhost:8080/__admin/health" --health-interval 5s --health-timeout 3s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - name: Install dependencies run: npm ci - name: Load WireMock stubs run: | cp -r wiremock/mappings /tmp/wiremock-stubs curl -X POST http://localhost:8080/__admin/mappings/import \ -H 'Content-Type: application/json' \ --data-binary @wiremock/mappings/all-stubs.json - name: Run integration tests run: npm run test:integration env: HEALTHLAB_API_URL: http://localhost:8080 PAGOS_API_URL: http://localhost:8080 WHATSAPP_API_URL: http://localhost:8080 NODE_ENV: test
import { getPatientResults } from '../../services/healthlab' const MOCK_URL = process.env.HEALTHLAB_API_URL ?? 'http://localhost:8080' describe('HealthLab — Patient Results', () => { it('retorna marcadores de análisis correctamente', async () => { const result = await getPatientResults( '42', { baseUrl: MOCK_URL } ) expect(result.patientId).toBe('42') expect(result.markers).toHaveLength(2) expect(result.markers[0].code).toBe('GLU') }) it('activa circuit breaker tras 8s timeout', async () => { await expect( getPatientResults('slow', { baseUrl: MOCK_URL, timeout: 3000 }) ).rejects.toThrow('ETIMEDOUT') }) })