From 0dcd0f20f6cc68f76b4bfc831f9628b0cc82411e Mon Sep 17 00:00:00 2001 From: EmeditWeb Date: Thu, 24 Sep 2026 17:11:32 +0100 Subject: [PATCH] ci(api): fix coverage collection and enforce a ratchet coverage gate Coverage was reporting 0% because the jest config rooted discovery and collection under `test/` while sources live in `src/`, so no source files were ever instrumented and the in-`src` spec was never discovered. - Move jest `rootDir` to the project root; discover specs under both `test/` and `src/` via `roots`; collect coverage from `src/**` and write to `coverage/`. This surfaces the previously-orphaned vendors.service.spec.ts (40 suites / 473 tests now run, up from 39/460). - Fix vendors.service.spec.ts: provide the missing `VendorsRepository` and `CACHE_MANAGER` so the Nest testing module can construct VendorsService (it previously failed DI and every case errored). - Add a global `coverageThreshold` (statements 45 / branches 30 / functions 35 / lines 45), a ratchet set a few points below the measured baseline (51.63 / 37.82 / 43.34 / 52.23) so it enforces non-regression without turning main red. - CI: run `npm run test:cov` instead of `npm test` so the gate is enforced on every PR. Verified locally: `npm run lint:ci`, `npm run build`, and `npm run test:cov` all exit 0. --- .github/workflows/ci.yml | 2 +- package.json | 20 ++++++++++++++++---- src/modules/vendors/vendors.service.spec.ts | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7a7ad5..34248ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,4 +39,4 @@ jobs: run: npm run build - name: Test - run: npm test + run: npm run test:cov diff --git a/package.json b/package.json index ac8dbaa..c26f604 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,11 @@ "json", "ts" ], - "rootDir": "test", + "rootDir": ".", + "roots": [ + "/test", + "/src" + ], "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.ts$": [ @@ -105,12 +109,20 @@ ] }, "collectCoverageFrom": [ - "../src/**/*.{t,j}s" + "src/**/*.{t,j}s" ], - "coverageDirectory": "../coverage", + "coverageDirectory": "coverage", + "coverageThreshold": { + "global": { + "statements": 45, + "branches": 30, + "functions": 35, + "lines": 45 + } + }, "testEnvironment": "node", "moduleNameMapper": { - "^src/(.*)$": "/../src/$1" + "^src/(.*)$": "/src/$1" } }, "overrides": { diff --git a/src/modules/vendors/vendors.service.spec.ts b/src/modules/vendors/vendors.service.spec.ts index 5d865b0..ea5931c 100644 --- a/src/modules/vendors/vendors.service.spec.ts +++ b/src/modules/vendors/vendors.service.spec.ts @@ -1,9 +1,11 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ConflictException, NotFoundException, ForbiddenException, UnauthorizedException, ExecutionContext } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { VendorsService } from './vendors.service'; import { VendorsController } from './vendors.controller'; import { SupabaseService } from '../../database/supabase.client'; +import { VendorsRepository } from '../../database/repositories/vendors.repository'; import { VendorRegistryContractClient } from '../../stellar/contracts/clients/vendor-registry.client'; import { VendorType, VendorStatus } from './dto/vendor.dto'; import { AdminGuard } from '../../common/guards/admin.guard'; @@ -14,6 +16,8 @@ describe('VendorsModule', () => { let mockSupabaseService: any; let mockVendorRegistryClient: any; let mockConfigService: any; + let mockVendorsRepository: any; + let mockCacheManager: any; let adminGuard: AdminGuard; const mockAdminWallet = 'GA3D5342W...ADMIN_WALLET_ADDRESS'; @@ -79,14 +83,30 @@ describe('VendorsModule', () => { }), }; + // The methods under test read through supabaseService and the registry + // client; the repository and cache are wired only to satisfy DI. + mockVendorsRepository = { + findById: jest.fn(), + findAll: jest.fn(), + updateStatus: jest.fn(), + }; + + mockCacheManager = { + get: jest.fn(), + set: jest.fn(), + del: jest.fn(), + }; + const module: TestingModule = await Test.createTestingModule({ controllers: [VendorsController], providers: [ VendorsService, AdminGuard, { provide: SupabaseService, useValue: mockSupabaseService }, + { provide: VendorsRepository, useValue: mockVendorsRepository }, { provide: VendorRegistryContractClient, useValue: mockVendorRegistryClient }, { provide: ConfigService, useValue: mockConfigService }, + { provide: CACHE_MANAGER, useValue: mockCacheManager }, ], }).compile();