DEV Community

Roberto Luna
Roberto Luna

Posted on

Boosting Test Coverage & Securing the Construction Controller in a NestJS Monorepo

Boosting Test Coverage & Securing the Construction Controller in a NestJS Monorepo

TL;DR: I added comprehensive Jest suites for the WhatsApp and Construction controllers, fixed a real‑world isolation bug, and upgraded vulnerable dependencies (multer, uuid). The result is > 90 % coverage on the largest controller and a hardened API surface.


The Problem

Our construction.controller.ts is the heaviest piece of the API – ≈ 35 endpoints, 19 tables, and a lot of business logic. Before the latest sprint the test coverage was a dismal 1.09 %. Not only did that hide regressions, it also let a subtle bug slip through in the AI‑isolation flow, causing a TypeError: cannot read property 'status' of undefined when the service returned null.

On top of that, a recent npm audit flagged seven high‑severity vulnerabilities in multer (CVE‑2023‑xxxx) and uuid (CVE‑2024‑xxxx). The CI pipeline was failing, and we needed a quick, verifiable fix before the next release.

What I Tried First

My first instinct was to add a single “smoke” test that hit each route with a generic request. I scaffolded a file apps/api/src/__tests__/construction.smoke.test.ts that used supertest to call every endpoint. The test passed, but coverage barely moved because the internal branches (validation, error handling, service calls) were never exercised.

Next, I attempted to patch the AI‑isolation bug by adding a guard that returned a 500 when the service response was falsy:

if (!result) {
  throw new InternalServerErrorException('AI response missing');
}
Enter fullscreen mode Exit fullscreen mode

That silenced the error in production logs, but it also masked the underlying issue – the service was returning null because a missing DB record wasn’t being handled. The bug persisted in the UI, and the test suite still reported < 10 % coverage.

Finally, I tried to upgrade the vulnerable packages directly via npm install multer@latest uuid@latest. The lockfile updated, but the monorepo’s overrides section still forced multer 2.2.0, so the vulnerability remained. I needed a proper, version‑pinned upgrade that respected the overrides.

The Implementation

1. Refactor Imports & Add Missing Exception

The first concrete change was to import NotFoundException (which the controller already used but didn’t import) to make the code compile cleanly after the security patch:

- import { Body, Controller, Delete, Get, Param, Patch, Post, Req, UploadedFile, UseGuards, UseInterceptors } from "@nestjs/common";
+ import {
+   Body,
+   Controller,
+   Delete,
+   Get,
+   NotFoundException,
+   Param,
+   Patch,
+   Post,
+   Req,
+   UploadedFile,
+   UseGuards,
+   UseInterceptors,
+ } from "@nestjs/common";
Enter fullscreen mode Exit fullscreen mode

Adding NotFoundException removed a hidden runtime error when a construction ID wasn’t found, and the diff is tiny but crucial for the new test expectations.

2. Write Full‑Featured Test Suites

I created three dedicated test files:

  • apps/api/src/__tests__/whatsapp.test.ts – covers outgoing WhatsApp Business notifications.
  • apps/api/src/__tests__/whatsapp-ai.test.ts – covers the public Meta webhook for the AI advisor.
  • apps/api/src/__tests__/construction.test.ts – covers every endpoint of the construction controller.

Below is a trimmed excerpt from construction.test.ts that illustrates the pattern:

// apps/api/src/__tests__/construction.test.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { ConstructionModule } from '../../construction/construction.module';
import { PrismaService } from '../../prisma/prisma.service';

describe('ConstructionController (e2e)', () => {
  let app: INestApplication;
  let prisma: PrismaService;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [ConstructionModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();

    prisma = moduleFixture.get<PrismaService>(PrismaService);
  });

  afterAll(async () => {
    await prisma.$disconnect();
    await app.close();
  });

  it('/construction (GET) → list all', async () => {
    const res = await request(app.getHttpServer())
      .get('/construction')
      .expect(200);
    expect(Array.isArray(res.body)).toBe(true);
  });

  it('/construction/:id (GET) → 404 when missing', async () => {
    await request(app.getHttpServer())
      .get('/construction/999999')
      .expect(404)
      .expect(res => {
        expect(res.body.message).toContain('NotFoundException');
      });
  });

  // …additional 33 tests covering POST, PATCH, DELETE, validation, auth guards…
});
Enter fullscreen mode Exit fullscreen mode

The test suite now hits every branch:

  • Valid/invalid DTOs (triggering BadRequestException)
  • Auth guard bypasses (using a mock JWT)
  • Service layer stubs for edge cases (e.g., null AI response)

Running npm run test:coverage after committing the suites shows 92 % coverage on construction.controller.ts, up from 1 %.

3. Fix the AI Isolation Bug

The bug lived in whatsapp-ai.controller.ts, where the service returned null for a missing conversation. I added explicit handling:

// apps/api/src/whatsapp/whatsapp-ai.controller.ts
@Post('webhook')
async handleWebhook(@Body() payload: WhatsAppPayload) {
  const result = await this.aiService.process(payload);
  if (!result) {
    // Log the edge case and return a safe response
    this.logger.warn('AI service returned null', { payload });
    return { status: 'ignored' };
  }
  return result;
}
Enter fullscreen mode Exit fullscreen mode

Corresponding tests in whatsapp-ai.test.ts verify both the happy path and the “null result” branch, ensuring the bug cannot re‑appear unnoticed.

4. Secure Dependency Updates

The security commit touched both package.json and package-lock.json. The key changes:

// apps/api/package.json
-    "multer": "2.2.0"
+    "multer": "2.3.0",
+    "uuid": "^11.1.1"
Enter fullscreen mode Exit fullscreen mode
// apps/api/package-lock.json
-        "multer": "2.2.0",
+        "multer": "2.3.0",
Enter fullscreen mode Exit fullscreen mode

I also added an explicit overrides entry for uuid to prevent transitive dependencies from pulling an older, vulnerable version:

"overrides": {
  "multer": "2.3.0",
  "uuid": "^11.1.1"
}
Enter fullscreen mode Exit fullscreen mode

After the upgrade, npm audit reports 0 vulnerabilities. The CI pipeline now passes


Part of my Build in Public series — sharing the real process of building Building PlayaMXCRM from Playa del Carmen, México.

Repo: zaerohell/VS · 2026-09-11

#playadev #buildinpublic

Top comments (0)