From d1198f22535e618d1647843587c328c569d9f5b1 Mon Sep 17 00:00:00 2001 From: lewistemple22 <318986045+lewistemple22@users.noreply.github.com> Date: Wed, 30 Sep 2026 09:27:17 +0100 Subject: [PATCH] fix(backend): document conventions and repair route wiring --- .github/pull_request_template.md | 1 + docs/ci-pipeline.md | 60 +++++++++++++++++++++++ docs/code-conventions.md | 52 ++++++++++++++++++++ src/controllers/deliveryController.ts | 5 ++ src/controllers/deliveryCrudController.ts | 5 ++ src/di/container.ts | 2 +- src/models/deliveryModel.ts | 5 ++ src/services/deliveryService.ts | 5 ++ src/sockets/index.ts | 6 ++- tests/adminRoutes.test.ts | 22 +++++++++ tests/indexerService.test.ts | 10 ++++ 11 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 docs/ci-pipeline.md create mode 100644 docs/code-conventions.md create mode 100644 tests/adminRoutes.test.ts create mode 100644 tests/indexerService.test.ts diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d413811..96c03c6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -80,6 +80,7 @@ curl http://localhost:3000/api/v1/disputes/ ## Checklist - [ ] Code follows repo conventions and lints +- [ ] New files follow [the code conventions](../docs/code-conventions.md) and do not introduce duplicate legacy stacks - [ ] Unit/integration tests added for critical logic (could be added in follow-up) - [x] All existing tests pass - [x] PR references related issue: Closes #129 diff --git a/docs/ci-pipeline.md b/docs/ci-pipeline.md new file mode 100644 index 0000000..3065077 --- /dev/null +++ b/docs/ci-pipeline.md @@ -0,0 +1,60 @@ +# CI/CD Pipeline + +Use this checklist before opening a pull request: + +- [ ] Node.js 22.x and MongoDB 6.0 are available locally. +- [ ] Dependencies are installed with `pnpm install` and any `pnpm-lock.yaml` change is committed. +- [ ] `pnpm run lint` passes. +- [ ] `pnpm exec tsc --noEmit` passes (CI also runs `pnpm run build`). +- [ ] `pnpm test:coverage` passes with the required coverage thresholds. + +## What CI runs + +The workflow in `.github/workflows/ci.yml` runs on pushes and pull requests targeting `main` or +`develop`. Its environment matrix uses Node.js 22.x and MongoDB 6.0. The job installs pnpm 9, +starts MongoDB, installs dependencies, then runs these stages: + +```bash +pnpm install +pnpm run lint +pnpm run build +pnpm test:coverage +``` + +`pnpm run build` invokes `tsc` and is the CI compilation gate. The equivalent no-emit check is +useful during development because it validates TypeScript without writing `dist/`: + +```bash +pnpm exec tsc --noEmit +``` + +Tests use MongoDB at `mongodb://localhost:27017/swiftchain_test` in CI. For a local run, start a +MongoDB 6.0-compatible server and provide a JWT secret accepted by the environment schema: + +```bash +CI=true MONGO_URI=mongodb://localhost:27017/swiftchain_test \ + JWT_SECRET=test-secret-key-16chars pnpm test:coverage +``` + +## Coverage and pull-request comments + +Jest writes text, JSON summary, and `coverage/lcov.info` output to `coverage/`. The configured +minimums are: + +| Scope | Branches | Functions | Lines | Statements | +| --------------- | -------: | --------: | ----: | ---------: | +| Global | 60% | 60% | 60% | 60% | +| `src/services/` | 80% | 80% | 80% | 80% | +| `src/models/` | 70% | 70% | 70% | 70% | +| `src/routes/` | 60% | 60% | 60% | 60% | + +CI uploads the complete `coverage/` directory as an artifact. On pull requests, the +`romeovs/lcov-reporter-action` reads `coverage/lcov.info` and posts a coverage comment. The +comment step is allowed to continue on error, but Jest fails the job when a configured threshold +is not met. + +## Lockfile and package-manager requirement + +CI uses pnpm 9 and the repository's `pnpm-lock.yaml`. Run project commands with pnpm rather than +silently generating an npm or Yarn lockfile. If dependency resolution changes, review and commit +the resulting `pnpm-lock.yaml` in the same pull request. diff --git a/docs/code-conventions.md b/docs/code-conventions.md new file mode 100644 index 0000000..da0fdf7 --- /dev/null +++ b/docs/code-conventions.md @@ -0,0 +1,52 @@ +# Backend Code Conventions + +This document prevents parallel implementations from drifting apart. New code must follow these +rules; do not create a second file solely because an older naming variant already exists. + +## Layering + +Keep request flow in the following direction: + +```text +route -> controller -> service -> repository -> Mongoose model +``` + +Controllers translate HTTP input and output. Services own business rules. Repositories own +database access. Models define persistence contracts. Background jobs may call services, but must +not bypass the service/repository boundaries for new behavior. + +## File names + +- Services use `*.service.ts` (`delivery.service.ts`, `escrow.service.ts`). +- Controllers use `*.controller.ts` (`delivery.controller.ts`, `escrow.controller.ts`). +- Repositories use `*Repository.ts` for the existing repository convention. +- Models use PascalCase names (`Delivery.ts`, `Escrow.ts`). +- Middleware files use a descriptive camelCase name under `src/middleware/` or + `src/middlewares/`, matching the directory convention already used by the feature. +- Socket transport entrypoints are handlers under `src/sockets/`; the live server wiring belongs + in `connectionHandler.ts`. + +## Exports and dependency injection + +Prefer named exports for services, controllers, repositories, and model types. A default export is +acceptable only where the surrounding module already establishes a singleton default convention. +Use one camelCase Awilix token per dependency (`deliveryService`, `escrowService`, +`deliveryController`). Never register snake_case aliases or two tokens for the same implementation. +The container must point canonical tokens at canonical modules. + +## Legacy files + +The following files are retained only for compatibility and must not receive new features: + +| Legacy file | Canonical replacement | +| ------------------------------------------- | ---------------------------------------- | +| `src/services/deliveryService.ts` | `src/services/delivery.service.ts` | +| `src/controllers/deliveryController.ts` | `src/controllers/delivery.controller.ts` | +| `src/controllers/deliveryCrudController.ts` | `src/controllers/delivery.controller.ts` | +| `src/models/deliveryModel.ts` | `src/models/Delivery.ts` | +| `src/sockets/index.ts` | `src/sockets/connectionHandler.ts` | + +Each retained legacy file has a deprecation banner. The former `src/services/escrowService.ts` was +removed; use `src/services/escrow.service.ts` and do not recreate the removed parallel stack. + +Before adding a file, search `src/` for the canonical implementation and update it when one exists. diff --git a/src/controllers/deliveryController.ts b/src/controllers/deliveryController.ts index d2f3b75..44fe0d8 100644 --- a/src/controllers/deliveryController.ts +++ b/src/controllers/deliveryController.ts @@ -1,3 +1,8 @@ +/** + * @deprecated Use `src/controllers/delivery.controller.ts` for canonical + * delivery endpoints. This controller remains only for the legacy ETA route. + */ + import { Request, Response } from 'express'; import { StatusCodes } from 'http-status-codes'; import { deliveryService } from '../services/deliveryService'; diff --git a/src/controllers/deliveryCrudController.ts b/src/controllers/deliveryCrudController.ts index 99302c5..fac1882 100644 --- a/src/controllers/deliveryCrudController.ts +++ b/src/controllers/deliveryCrudController.ts @@ -1,3 +1,8 @@ +/** + * @deprecated Use `src/controllers/delivery.controller.ts` and the canonical + * delivery routes. This standalone handler module is retained for compatibility. + */ + import { Request, Response, NextFunction } from 'express'; import { StatusCodes } from 'http-status-codes'; import crypto from 'crypto'; diff --git a/src/di/container.ts b/src/di/container.ts index 616c29d..e45aef9 100644 --- a/src/di/container.ts +++ b/src/di/container.ts @@ -43,7 +43,7 @@ import IdempotencyRecord from '../models/IdempotencyRecord'; // ─── Services ────────────────────────────────────────────────────────────────── import authService from '../services/authService'; -import { deliveryService } from '../services/deliveryService'; +import { deliveryService } from '../services/delivery.service'; import { driverService } from '../services/driverService'; import { driverRatingService } from '../services/driverRatingService'; import * as fleetService from '../services/fleetService'; diff --git a/src/models/deliveryModel.ts b/src/models/deliveryModel.ts index de948f4..3a17dc0 100644 --- a/src/models/deliveryModel.ts +++ b/src/models/deliveryModel.ts @@ -1,3 +1,8 @@ +/** + * @deprecated Use `src/models/Delivery.ts`. The canonical model is registered + * in the DI container as `deliveryModel`. + */ + import mongoose, { Document, Model, Schema } from 'mongoose'; export type DeliveryStatus = 'pending' | 'assigned' | 'picked_up' | 'in_transit' | 'delivered'; diff --git a/src/services/deliveryService.ts b/src/services/deliveryService.ts index 94920e5..5b024ef 100644 --- a/src/services/deliveryService.ts +++ b/src/services/deliveryService.ts @@ -1,3 +1,8 @@ +/** + * @deprecated Use `src/services/delivery.service.ts` for the canonical delivery + * service. This module remains only for the legacy ETA/QR compatibility route. + */ + import { Delivery, DeliveryStatus } from '../models/Delivery'; import { routingService, ETARequest } from './routingService'; import QRCode from 'qrcode'; diff --git a/src/sockets/index.ts b/src/sockets/index.ts index ffe2422..c8fa048 100644 --- a/src/sockets/index.ts +++ b/src/sockets/index.ts @@ -4,7 +4,11 @@ import env from '../config/env'; import registerSocketHandlers from './socketController'; import logger from '../config/logger'; import socketAuth from '../middlewares/socketAuth'; -import env from '../config/env'; + +/** + * @deprecated The live Socket.IO entrypoint is `connectionHandler.ts`. Keep + * this compatibility initializer out of new imports and registrations. + */ let realtimeNsp: Namespace | null = null; diff --git a/tests/adminRoutes.test.ts b/tests/adminRoutes.test.ts new file mode 100644 index 0000000..d777c3a --- /dev/null +++ b/tests/adminRoutes.test.ts @@ -0,0 +1,22 @@ +import express from 'express'; +import request from 'supertest'; +import adminRoutes from '../src/routes/adminRoutes'; +import errorHandler from '../src/middleware/errorHandler'; + +describe('admin DLQ routes', () => { + const app = express(); + app.use('/v1/admin', adminRoutes); + app.use(errorHandler); + + it('mounts the DLQ list route and rejects unauthenticated requests', async () => { + const response = await request(app).get('/v1/admin/dlq'); + + expect(response.status).toBe(401); + }); + + it('mounts the DLQ retry route and rejects unauthenticated requests', async () => { + const response = await request(app).post('/v1/admin/dlq/example-id/retry'); + + expect(response.status).toBe(401); + }); +}); diff --git a/tests/indexerService.test.ts b/tests/indexerService.test.ts new file mode 100644 index 0000000..4237597 --- /dev/null +++ b/tests/indexerService.test.ts @@ -0,0 +1,10 @@ +import { IndexerService } from '../src/services/indexerService'; + +describe('IndexerService module wiring', () => { + it('loads without relying on the removed webSocketService module', () => { + const service = new IndexerService(); + + expect(service).toBeInstanceOf(IndexerService); + expect(typeof service.processDeliveryStatusUpdated).toBe('function'); + }); +});