Skip to content

[BE-32] Backend Unit Tests: Bids, Notifications, Documents & Carrier Modules #1018

@mftee

Description

@mftee

⚠️ Depends on: [BE-31] Test Suite Foundation — Jest config and testing utilities must be in place.

Overview

Continuation of the backend test effort (see [BE-31]). This issue covers unit tests for the remaining business-critical modules: Bids, Notifications, Documents, and Carrier Profile.

Technical Details

1. Bids Service Tests (backend/src/bids/bids.service.spec.ts)

describe('BidsService') {
  it('create() creates a bid with status=PENDING and expiresAt = now + 48h')
  it('create() throws ForbiddenException if the carrier tries to bid on their own shipment')
  it('create() throws BadRequestException if the shipment status is not pending')
  it('accept() sets the bid to ACCEPTED, assigns carrier to shipment, rejects other bids')
  it('accept() throws ForbiddenException if called by non-shipment-owner')
  it('accept() throws BadRequestException if the bid is expired')
  it('counter() sets bid to COUNTER_OFFERED and emits the correct event')
  it('counter() throws BadRequestException if bid is not in PENDING status')
  it('acceptCounter() transitions to COUNTER_ACCEPTED and triggers carrier assignment')
}

2. Notifications Service Tests (backend/src/notifications/notifications.service.spec.ts)

describe('NotificationsService') {
  it('create() saves a Notification record to the database')
  it('findAll() returns only notifications for the requesting user')
  it('findAll() filters correctly when isRead=false')
  it('markAsRead() sets isRead=true and readAt timestamp')
  it('markAsRead() throws ForbiddenException when another user's notification is targeted')
  it('markAllAsRead() marks all unread notifications for the user')
  it('getUnreadCount() returns the correct count of unread notifications')
}

3. Documents Service Tests (backend/src/documents/documents.service.spec.ts)

describe('DocumentsService') {
  it('upload() creates a document record with the correct shipmentId and userId')
  it('findAll() returns only documents belonging to the requesting user's shipments')
  it('findOne() throws ForbiddenException if the document does not belong to the user's shipment')
  it('remove() deletes the document record and calls storage cleanup')
  it('remove() throws ForbiddenException if called by a non-owner')
}

4. Carrier Profile Service Tests (backend/src/carriers/carriers.service.spec.ts)

describe('CarriersService') {
  it('createOrUpdate() creates a new profile for a carrier with no existing profile')
  it('createOrUpdate() updates an existing profile without creating a duplicate')
  it('updateAvailability() sets the correct availability status')
  it('findPublic() returns only public fields (no sensitive data)')
  it('uploadDocument() creates a CarrierDocument with status=PENDING_REVIEW')
  it('adminReviewDocument() sets APPROVED and sets isVerified=true when all docs approved')
  it('adminReviewDocument() sets REJECTED and adds the review note')
}

5. Admin Module Tests (backend/src/admin/admin.service.spec.ts)

describe('AdminService') {
  it('suspendUser() sets isSuspended=true and invalidates their refresh token')
  it('suspendUser() throws BadRequestException if the target is an ADMIN role')
  it('unsuspendUser() clears isSuspended and suspensionReason')
  it('getStats() returns correct aggregated counts from the database')
  it('broadcast() creates Notification records for all matching users')
}

Acceptance Criteria

  • npm run test passes all tests with no errors
  • Bids service: all 9 tests pass (including expired bid check)
  • Notifications service: all 7 tests pass (including ownership check)
  • Documents service: all 5 tests pass
  • Carrier profile service: all 7 tests pass (including full verification flow)
  • Admin service: all 5 tests pass
  • All tests use mocked repositories — no real database connections
  • Combined line coverage across all tested modules reaches ≥ 65%

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions