🚗 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
6500.
Requirements & Fields
Car Fields
make— string, required (e.g. "Toyota")model— string, required (e.g. "Camry")year— integer, required, range 1990–2030color— string, requiredlicense_plate— string, required, unique per userdaily_rate_cents— integer, required, minimum 1000 ($10.00)tank_capacity_gallons— number, optional (default 12.0)mileage_km— integer, optional, current odometer in kmstatus— enum:AVAILABLE|RENTED|RETURNED|DAMAGED|IN_SHOPimage_url— string, optional, public image URLdeleted_at— timestamp, null means active (soft delete)
Add-on Fields
name— string, required (e.g. "GPS Navigator")description— string, optionaldaily_price_cents— integer, required, price per rental day in centsis_active— boolean, default true
Booking Fields
car_id— UUID, requiredstart_date— date string, required (YYYY-MM-DD)end_date— date string, required (YYYY-MM-DD), must be after start_dateinsurance_type— enum:BASIC($15/day) |FULL($35/day) |OWN($0)addon_ids— array of UUIDs, optionalpayment_method_id— UUID, optionalstatus— enum:PENDING|APPROVED|ACTIVE|COMPLETED|CANCELLED
Invoice Fields
rental_days— integer, booked number of daysactual_rental_days— integer, actual days used (may differ on early/late return)base_cost_cents— daily rate × booked daysaddons_cost_cents— sum of add-on daily prices × daysinsurance_cost_cents— insurance rate × daysfuel_fee_cents— charged when car returned with less fuel than picked upearly_return_credit_cents— refund when returned before booked end datelate_return_fee_cents— extra charge when returned after booked end datediscount_cents— manager discretionary discountextra_fees_cents— cleaning or damage surchargestotal_cents— final amount duepayment_status— enum:UNPAID|PAIDreturned_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 bookingcard_type— enum:Visa|Mastercard|Amex|Discovercard_number— string, 13–19 digits (test cards only)last4— string, auto-derived from card_numbercvv— string, 3–4 digitsexpiry_month/expiry_year— two-digit month, four-digit yearbilling_street,billing_city,billing_state,billing_zip— billing addressis_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, orACTIVEbookings cannot be deleted
Car Status State Machine
AVAILABLE→ any status (manual)RENTED→ cannot be changed manually; must process a return firstRETURNED/DAMAGED/IN_SHOP→ any status (manual)- Booking approval automatically sets status to
RENTED - Return processing sets status to
RETURNED(orDAMAGEDif condition = MAJOR_DAMAGE)
Booking Availability
- A car is available if it has no
PENDING,APPROVED, orACTIVEbookings 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 =
APPROVEDand car status =RENTED - Process return via
POST /bookings/:id/return - Verify booking status =
COMPLETEDand 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/statuswithstatus = "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