🚗 Rental App

Car rental management system for testing bookings, invoicing, fleet management, and state-machine workflows

Overview

DriveQA Car Rental is a fully-featured rental management system with two roles: Manager (fleet owner) and Renter (customer). It simulates real-world car rental workflows with booking state machines, invoice calculations, fuel fees, early/late return adjustments, and payment method management.

What You Can Test

  • Cars: Fleet management, availability search with date-overlap logic, soft delete, status state machine
  • Add-ons: Optional rental extras (GPS, child seat, etc.) with per-day pricing
  • Bookings: Full lifecycle — PENDING → APPROVED → ACTIVE → COMPLETED / CANCELLED
  • Invoice Calculation: Base rate + add-ons + insurance + fuel fee + early/late adjustments
  • Payment Methods: Save cards, set default, validate cardholder name matches driver
  • Mileage Tracking: Pick-up and return odometer readings in km/miles
  • Data Isolation: Each user only sees their own cars, bookings, and invoices
💡 All monetary values (daily rates, fees, totals) are stored and returned in cents (integers). $65.00 = 6500.

Requirements & Fields

Car Fields

  • make — string, required (e.g. "Toyota")
  • model — string, required (e.g. "Camry")
  • year — integer, required, range 1990–2030
  • color — string, required
  • license_plate — string, required, unique per user
  • daily_rate_cents — integer, required, minimum 1000 ($10.00)
  • tank_capacity_gallons — number, optional (default 12.0)
  • mileage_km — integer, optional, current odometer in km
  • status — enum: AVAILABLE | RENTED | RETURNED | DAMAGED | IN_SHOP
  • image_url — string, optional, public image URL
  • deleted_at — timestamp, null means active (soft delete)

Add-on Fields

  • name — string, required (e.g. "GPS Navigator")
  • description — string, optional
  • daily_price_cents — integer, required, price per rental day in cents
  • is_active — boolean, default true

Booking Fields

  • car_id — UUID, required
  • start_date — date string, required (YYYY-MM-DD)
  • end_date — date string, required (YYYY-MM-DD), must be after start_date
  • insurance_type — enum: BASIC ($15/day) | FULL ($35/day) | OWN ($0)
  • addon_ids — array of UUIDs, optional
  • payment_method_id — UUID, optional
  • status — enum: PENDING | APPROVED | ACTIVE | COMPLETED | CANCELLED

Invoice Fields

  • rental_days — integer, booked number of days
  • actual_rental_days — integer, actual days used (may differ on early/late return)
  • base_cost_cents — daily rate × booked days
  • addons_cost_cents — sum of add-on daily prices × days
  • insurance_cost_cents — insurance rate × days
  • fuel_fee_cents — charged when car returned with less fuel than picked up
  • early_return_credit_cents — refund when returned before booked end date
  • late_return_fee_cents — extra charge when returned after booked end date
  • discount_cents — manager discretionary discount
  • extra_fees_cents — cleaning or damage surcharges
  • total_cents — final amount due
  • payment_status — enum: UNPAID | PAID
  • returned_condition — enum: GOOD | DIRTY | MINOR_DAMAGE | MAJOR_DAMAGE

Payment Method Fields

  • nickname — string, required (e.g. "Personal Visa")
  • cardholder_name — string, required, uppercase, must match driver name at booking
  • card_type — enum: Visa | Mastercard | Amex | Discover
  • card_number — string, 13–19 digits (test cards only)
  • last4 — string, auto-derived from card_number
  • cvv — string, 3–4 digits
  • expiry_month / expiry_year — two-digit month, four-digit year
  • billing_street, billing_city, billing_state, billing_zip — billing address
  • is_default — boolean, only one card can be default at a time

Business Rules

Car Limits

  • Each user can list a maximum of 30 personal cars via POST /api/rental/cars/mine
  • Fleet cars (POST /api/rental/cars) have no per-user limit but are unowned (owner_id = NULL)
  • Cars with PENDING, APPROVED, or ACTIVE bookings cannot be deleted

Car Status State Machine

  • AVAILABLE → any status (manual)
  • RENTED → cannot be changed manually; must process a return first
  • RETURNED / DAMAGED / IN_SHOP → any status (manual)
  • Booking approval automatically sets status to RENTED
  • Return processing sets status to RETURNED (or DAMAGED if condition = MAJOR_DAMAGE)

Booking Availability

  • A car is available if it has no PENDING, APPROVED, or ACTIVE bookings overlapping the requested dates
  • Date overlap check: start < existing_end AND end > existing_start
  • Maximum rental period: 31 days

Invoice Calculation

  • base_cost = daily_rate × booked_days
  • addons_cost = Σ(addon_daily_price × booked_days)
  • insurance_cost = BASIC $15/day | FULL $35/day | OWN $0
  • fuel_fee = ((pickup_pct - return_pct) / 100) × tank_gallons × gas_price_cents (only when return% < pickup%)
  • early_return_credit = (booked_days - actual_days) × total_daily_rate
  • late_return_fee = (actual_days - booked_days) × total_daily_rate
  • total = base + addons + insurance + fuel_fee + extra_fees - discount ± early/late adjustment

Payment Method Rules

  • Cardholder name on the card must exactly match the driver's first + last name (case-insensitive)
  • The first card added automatically becomes the default
  • Deleting the default card promotes the next most-recent card to default
  • Card number, CVV, and expiry cannot be changed after creation

Test Case Examples

TC-001: Car Availability with Date Overlap

Objective: Verify a car cannot be double-booked for overlapping dates

Steps:

  • Create a booking for car A from Apr 10 → Apr 14
  • Approve the booking
  • Attempt a second booking for car A from Apr 12 → Apr 16
  • Verify response status 409
  • Verify error indicates date conflict

Expected: Second booking rejected due to date overlap

TC-002: Booking State Machine — Full Lifecycle

Objective: Verify booking transitions PENDING → APPROVED → COMPLETED

Steps:

  • Create booking, verify status = PENDING
  • Approve booking via PATCH /bookings/:id/approve
  • Verify booking status = APPROVED and car status = RENTED
  • Process return via POST /bookings/:id/return
  • Verify booking status = COMPLETED and car status = RETURNED
  • Verify invoice is created with payment_status = UNPAID

Expected: All state transitions apply correctly; invoice generated on return

TC-003: Invoice Calculation Accuracy

Objective: Verify invoice totals are calculated correctly

Steps:

  • Car daily rate = $65.00 (6500 cents)
  • Book for 4 days with BASIC insurance ($15/day) and one add-on at $3.99/day
  • Return with 75% fuel (picked up at 100%), tank = 13.2 gal, gas = $3.99/gal
  • Process return with no extra fees or discount
  • Verify: base = $260.00, insurance = $60.00, addons = $15.96, fuel_fee ≈ $13.17
  • Verify: total ≈ $349.13

Expected: All invoice line items match the expected calculation

TC-004: Early Return Credit

Objective: Verify a credit is applied when the car is returned before the booked end date

Steps:

  • Create booking for 5 days
  • Process return after only 3 days (actual_return_date = start_date + 3)
  • Verify early_return_credit_cents = 2 days × daily_rate in invoice
  • Verify actual_rental_days = 3
  • Verify total is lower than a 5-day invoice would be

Expected: Invoice reflects 2-day credit; total reduced accordingly

TC-005: Late Return Fee

Objective: Verify an extra charge is applied when the car is returned after the booked end date

Steps:

  • Create booking for 3 days
  • Process return 2 days late (actual_return_date = end_date + 2)
  • Verify late_return_fee_cents = 2 days × daily_rate in invoice
  • Verify actual_rental_days = 5
  • Verify total is higher than a 3-day invoice would be

Expected: Invoice reflects 2-day late fee; total increased accordingly

TC-006: Cannot Set Car to RENTED Manually

Objective: Verify car status cannot be manually set to RENTED via the status API

Steps:

  • Send PUT /api/rental/cars/:id/status with status = "RENTED"
  • Verify response status 400 or 409
  • Verify error message explains RENTED is only set via booking approval

Expected: Manual RENTED transition rejected

TC-007: Payment Method — Cardholder Name Validation

Objective: Verify booking is rejected when cardholder name does not match driver name

Steps:

  • Add a payment method with cardholder name "JANE SMITH"
  • Attempt to book using this card with driver name "JOHN DOE"
  • Verify booking is rejected
  • Verify error message references the name mismatch

Expected: Booking rejected due to name mismatch on card

TC-008: Maximum 30 Cars Per User

Objective: Verify a user cannot list more than 30 personal cars

Steps:

  • Add 30 cars via POST /api/rental/cars/mine
  • Attempt to add a 31st car
  • Verify response status 409
  • Verify error message indicates maximum reached

Expected: 4th car rejected with appropriate error

TC-009: Default Payment Method Promotion

Objective: Verify deleting the default card promotes the next card

Steps:

  • Add two payment methods; verify first is default
  • Delete the default card
  • Fetch payment methods
  • Verify the second card now has is_default = true

Expected: Remaining card becomes the new default automatically

TC-010: Cancel Booking Restores Car Status

Objective: Verify cancelling a PENDING booking returns the car to AVAILABLE

Steps:

  • Create a PENDING booking for an AVAILABLE car
  • Cancel the booking via PATCH /api/rental/bookings/:id/cancel
  • Verify booking status = CANCELLED
  • Verify car status returns to AVAILABLE

Expected: Car is freed and available again after cancellation