diff --git a/.gitea/workflows/docker.yaml b/.gitea/workflows/docker.yaml index 92670ae..b1e224d 100644 --- a/.gitea/workflows/docker.yaml +++ b/.gitea/workflows/docker.yaml @@ -31,6 +31,7 @@ jobs: needs: detect-changes if: needs.detect-changes.outputs.backend == 'true' runs-on: ubuntu-latest + timeout-minutes: 15 steps: - uses: actions/checkout@v4 - name: Setup Node.js @@ -39,15 +40,18 @@ jobs: node-version: '22' cache: 'npm' - name: Install dependencies - run: npm ci + run: npm ci --ignore-scripts + - name: Install backend native deps + run: npm rebuild sqlite3 bcrypt --workspace=farma-clic-backend - name: Run tests - run: npm test --workspace=farma-clic-backend -- --ci + run: npm test --workspace=farma-clic-backend test-frontend: name: Frontend Tests needs: detect-changes if: needs.detect-changes.outputs.frontend == 'true' runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Setup Node.js @@ -56,7 +60,7 @@ jobs: node-version: '22' cache: 'npm' - name: Install dependencies - run: npm ci + run: npm ci --ignore-scripts - name: Run tests run: npm test --workspace=farma-clic-frontend -- --run --reporter=basic diff --git a/.gitea/workflows/test-branches.yaml b/.gitea/workflows/test-branches.yaml index 9bfbf3f..877a4d4 100644 --- a/.gitea/workflows/test-branches.yaml +++ b/.gitea/workflows/test-branches.yaml @@ -6,42 +6,82 @@ on: - 'main' jobs: - test: - name: Run Tests + detect-changes: + name: Detect Changes runs-on: ubuntu-latest + outputs: + backend: ${{ steps.filter.outputs.backend }} + frontend: ${{ steps.filter.outputs.frontend }} + frontend-mobile: ${{ steps.filter.outputs.frontend-mobile }} steps: - - name: Checkout Code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter with: - fetch-depth: 2 - - - name: Detect Changes - id: changes - run: | - BASE=${{ github.event.before }} - [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ] && BASE=$(git rev-parse HEAD~1) - echo "backend=$(git diff --name-only "$BASE"...HEAD | grep -qE '^(apps/backend/|packages/)' && echo true || echo false)" >> "$GITHUB_OUTPUT" - echo "frontend=$(git diff --name-only "$BASE"...HEAD | grep -qE '^(apps/frontend/|packages/)' && echo true || echo false)" >> "$GITHUB_OUTPUT" - echo "frontend-mobile=$(git diff --name-only "$BASE"...HEAD | grep -qE '^(apps/frontend-mobile/|packages/)' && echo true || echo false)" >> "$GITHUB_OUTPUT" + filters: | + backend: + - 'apps/backend/**' + - 'packages/**' + frontend: + - 'apps/frontend/**' + - 'packages/**' + frontend-mobile: + - 'apps/frontend-mobile/**' + - 'packages/**' + test-backend: + name: Backend Tests + needs: detect-changes + if: needs.detect-changes.outputs.backend == 'true' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' - - - name: Install Dependencies - run: npm ci - + - name: Install dependencies + run: npm ci --ignore-scripts + - name: Install backend native deps + run: npm rebuild sqlite3 bcrypt --workspace=farma-clic-backend - name: Run Backend Tests - if: steps.changes.outputs.backend == 'true' - run: npm test --workspace=farma-clic-backend -- --ci + run: npm test --workspace=farma-clic-backend + test-frontend: + name: Frontend Tests + needs: detect-changes + if: needs.detect-changes.outputs.frontend == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + - name: Install dependencies + run: npm ci --ignore-scripts - name: Run Frontend Tests - if: steps.changes.outputs.frontend == 'true' run: npm test --workspace=farma-clic-frontend -- --run --reporter=basic + test-frontend-mobile: + name: Frontend Mobile Tests + needs: detect-changes + if: needs.detect-changes.outputs.frontend-mobile == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + - name: Install dependencies + run: npm ci --ignore-scripts - name: Run Frontend Mobile Tests - if: steps.changes.outputs.frontend-mobile == 'true' run: npm test --workspace=frontend-mobile -- --ci continue-on-error: true diff --git a/apps/backend/jest.config.js b/apps/backend/jest.config.js index 99c960b..d3766c0 100644 --- a/apps/backend/jest.config.js +++ b/apps/backend/jest.config.js @@ -3,4 +3,8 @@ export default { transform: {}, moduleFileExtensions: ['js', 'json'], testMatch: ['**/__tests__/**/*.test.js'], + testTimeout: 15000, + openHandlesTimeout: 5000, + // Run test files in parallel (not individual tests within a file) + // workerIdleMemoryLimit: '512MB', } diff --git a/apps/backend/package.json b/apps/backend/package.json index 924e3c8..9f4521c 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -12,7 +12,7 @@ "migrate": "node migrate.js", "reset-db": "bash reset-db.sh", "import-farmacias": "node import-farmacias.js", - "test": "NODE_OPTIONS='--experimental-vm-modules' npx jest --forceExit" + "test": "NODE_OPTIONS='--experimental-vm-modules' npx jest --ci --forceExit --forceExitTimeout=30000" }, "keywords": [], "author": "", diff --git a/apps/backend/redis-client.js b/apps/backend/redis-client.js index 7e82d52..ac604fd 100644 --- a/apps/backend/redis-client.js +++ b/apps/backend/redis-client.js @@ -4,7 +4,11 @@ import { createClient } from 'redis'; const redisClient = createClient({ socket: { host: process.env.REDIS_HOST || 'localhost', - port: process.env.REDIS_PORT || 6379 + port: process.env.REDIS_PORT || 6379, + reconnectStrategy: (retries) => { + if (retries > 10) return new Error('Redis max retries reached'); + return Math.min(retries * 100, 3000); + } }, password: process.env.REDIS_PASSWORD || undefined }); @@ -19,7 +23,9 @@ redisClient.on('connect', () => { console.log('✅ Connected to Redis'); }); -// Connect to Redis -await redisClient.connect(); +// Connect to Redis — skip in test (services are mocked) or when REDIS_URL is unset +if (process.env.NODE_ENV !== 'test') { + await redisClient.connect(); +} export default redisClient; diff --git a/apps/backend/server.js b/apps/backend/server.js index fb3ffdc..18dc88a 100644 --- a/apps/backend/server.js +++ b/apps/backend/server.js @@ -1,5 +1,8 @@ // OpenTelemetry SDK — must be imported first to instrument http, express, pg, etc. -import './src/tracing.js'; +// Skipped in test to avoid gRPC connection attempts to a non-existent collector. +if (process.env.NODE_ENV !== 'test') { + await import('./src/tracing.js'); +} import express from 'express'; import cors from 'cors'; @@ -17,8 +20,6 @@ import webpush from 'web-push'; import pino from 'pino'; import pinoHttp from 'pino-http'; import multer from 'multer'; -import { createWorker } from 'tesseract.js'; -import { BarcodeDetector } from 'barcode-detector'; import { searchMedicines, getMedicineDetails, searchOTC } from './cima-service.js'; import { searchBabyProducts, getBabyProductDetails } from './off-service.js'; import { runFarmaciaWebhookImport, DEFAULT_FARMACIAS_WEBHOOK, importPharmaciesFromRows } from './farmacias-webhook-import.js'; @@ -866,6 +867,7 @@ app.post('/api/tsi/ocr', upload.single('photo'), async (req, res) => { console.log('[OCR] Processing image, size:', req.file.size, 'bytes'); + const { createWorker } = await import('tesseract.js'); const worker = await createWorker('spa+eng'); const { data } = await worker.recognize(req.file.buffer); await worker.terminate();