Background
The Last Mile team at a large online retailer lets customers rate the driver who delivered their order. Operations uses the scores to reward great drivers and to coach drivers who are struggling. The service is a Node.js REST API (Express + MongoDB via Mongoose). After a recent clean-up the service stopped starting, and the scores that do come out look off.
How the system works
Callers
- Every request needs an
x-user-idheader (already authenticated upstream). A missing or blank header is401with{ "error": "sign in required" }. - The dispatch system also sends
x-role: dispatcher; operations staff sendx-role: ops(roles are compared case-insensitively).
Errors
Every error response is { "error": "<message>" }: 400 invalid input, 401 no x-user-id,
403 not allowed, 404 unknown delivery, 409 a rule was broken (duplicate delivery, outside the
rating window).
Deliveries
POST /deliveries (dispatchers only, else 403): body
{ "deliveryId", "driverId", "customerId", "deliveredAt" }.
deliveryIdis trimmed and upper-cased and must look likeD-followed by 4–10 digits.driverIdandcustomerIdare trimmed, 1–40 characters.deliveredAtis an ISO 8601 timestamp with a time zone (Zor+05:30), e.g.2026-07-01T09:30:00Z. It is returned in UTC.- A
deliveryIdthat is already recorded is409. Returns201with{ "deliveryId", "driverId", "customerId", "deliveredAt" }.
Ratings
PUT /deliveries/:deliveryId/rating: body { "stars", "comment", "tags" }.
- Only the delivery's
customerIdmay rate it (else403); an unknown delivery is404. starsis a whole number from 1 to 5.commentis optional text (trimmed, at most 280 characters, default"").tagsis an optional list ofON_TIME,FRIENDLY,CAREFUL,LATE,DAMAGED(upper-cased; repeats dropped, keeping the first position; anything else is400).- Ratings are accepted from the moment of delivery up to and including 14 days (14 × 24 hours)
after
deliveredAt; before or after that it's409. - A delivery has at most one rating. The first rating returns
201; rating again replaces stars, comment and tags (missing fields go back to their defaults) and returns200. - A rating that operations has hidden can't be changed (
409). - The response is
{ "deliveryId", "driverId", "stars", "comment", "tags", "ratedAt", "hidden" }, whereratedAtis when the rating was last set.
PUT /deliveries/:deliveryId/rating/visibility (operations only, else 403): body { "hidden", "reason" }.
hiddenmust betrueorfalse. Hiding needs areason(trimmed, 5–200 characters); showing again doesn't. A delivery without a rating is404. Returns200with the rating.- Hidden ratings don't count anywhere: not in scores, lists, the last-7-days figures or the leaderboard.
Driver score
GET /drivers/:driverId/score: 200 with { "driverId", "ratings", "average", "lowRatings", "badge", "last7Days" }.
ratings: how many ratings the driver has.average: the mean of the stars rounded to one decimal place, halves rounded up (4.65 → 4.7, 4.333… → 4.3), ornullwith no ratings.lowRatings: ratings of 1 or 2 stars.badge(decided from the rounded average, and only with at least 5 ratings; otherwisenull):"TOP"when the average is 4.7 or more,"WATCH"when it is below 3.5, elsenull.last7Days:{ "ratings", "average" }computed the same way from the ratings whoseratedAtis at most 7 days (7 × 24 hours, inclusive) before now.
GET /drivers/:driverId/ratings?limit=N: 200 with { "ratings": [...] }, the driver's most recent
ratings, newest ratedAt first; ratings with the same ratedAt are ordered by deliveryId
(A to Z). limit is optional (default 10) and must be a whole number from 1 to 50 (else 400).
GET /drivers/leaderboard?minRatings=N: 200 with { "drivers": [ { "driverId", "ratings", "average" } ] }:
at most 10 drivers that have at least minRatings ratings (optional, default 5, a whole number from 1
to 1000), ordered by the rounded average (highest first), then by more ratings, then by driverId
(A to Z).
Project structure
src/
app.js createApp(): JSON parsing, routes, error handler
server.js connects to MongoDB and starts the server
routes/
deliveries.js /deliveries
drivers.js /drivers
controllers/
deliveryController.js record a delivery, rate it, hide a rating
driverController.js driver score, recent ratings, leaderboard
middleware/
auth.js x-user-id and role checks
errorHandler.js errors → status + { error }
models/
Delivery.js a completed delivery
Rating.js a customer's rating of a delivery
services/
deliveryService.js delivery lookups
ratingService.js rating rules and recent ratings
scoreService.js average, low ratings, badges, leaderboard
utils/
AppError.js an error with an HTTP status
input.js reading ids, text, timestamps, tags and query numbers
present.js documents → API JSON
tags.js the allowed rating tags
tests/
score.test.js average and badge rules
ratings.test.js delivery and rating endpoints
drivers.test.js driver endpoints
setup/ in-memory MongoDB for the tests
Your task
Operations and drivers report these problems:
- The service doesn't start.
- Some drivers see a lower average than their ratings give, and miss a badge they earned.
- A driver's ratings page shows their oldest ratings instead of the latest ones.
Find and fix the defects so that all tests pass.
- Do not modify
tests/,package.jsonorjest.config.js. - Keep your changes minimal. Fix the defects; don't rewrite modules.
- Additional hidden tests check the behaviour described in this README.