From e3db56715bcf67d3ed28569460576ad7991ec10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoni=20Nu=C3=B1ez=20Romeu?= Date: Tue, 14 Jul 2026 12:42:02 +0200 Subject: [PATCH 1/2] =?UTF-8?q?ci:=20optimize=20test=20performance=20?= =?UTF-8?q?=E2=80=94=20from=20~3h=20to=203.5s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Lazy-load tesseract.js only in OCR route (skips 30MB WASM download) - Conditional tracing.js import (skip OTel gRPC in test mode) - Remove unused barcode-detector import - Add testTimeout/openHandlesTimeout to jest.config.js - Add --forceExitTimeout=30000 to test script - Split CI test jobs into parallel workflows (backend/frontend/mobile) - Add timeout-minutes: 15 to all test jobs - Use npm ci --ignore-scripts + selective native rebuild --- .gitea/workflows/docker.yaml | 10 ++-- .gitea/workflows/test-branches.yaml | 84 +++++++++++++++++++++-------- apps/backend/jest.config.js | 4 ++ apps/backend/package.json | 2 +- apps/backend/server.js | 8 +-- 5 files changed, 79 insertions(+), 29 deletions(-) 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/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(); From bd7fe563308a7b605c91523dc527355e8a7eaedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoni=20Nu=C3=B1ez=20Romeu?= Date: Tue, 14 Jul 2026 13:03:46 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20skip=20Redis=20connection=20in=20tes?= =?UTF-8?q?t=20mode=20=E2=80=94=20prevents=20CI=20hang?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The top-level await redisClient.connect() in redis-client.js was blocking module import when no Redis server is available (CI env). Add NODE_ENV guard + reconnect strategy with max retries. --- apps/backend/redis-client.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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;