← Questions

Debug a codebase then propose long-term fixes

0
CursorCoding

You inherit a small order service: an inventory that holds stock for orders, a pricing module, and the order service that ties them together. Support tickets say stock "disappears", some order totals are a cent off, page 1 of the order list skips orders, and a cart hold sometimes never expires. Find and fix the defects, add tests that would have caught them, then write up the structural changes that stop this class of bug coming back. You have about 60 minutes and may run code.

Prices are integer cents. A discount is rounded half up to the cent. Pages are numbered from 1.

class OutOfStock(Exception):
    pass


class Inventory:
    def __init__(self, stock=None):
        self.stock = dict(stock or {})  # sku -> units on the shelf
        self.reserved = {}  # order_id -> {sku: qty}

    def available(self, sku):
        return self.stock.get(sku, 0)

    def reserve(self, order_id, items):
        """Hold every (sku, qty) in `items` for one order, or none of them."""
        held = {}
        for sku, qty in items.items():
            if self.stock.get(sku, 0) < qty:
                raise OutOfStock(sku)
            self.stock[sku] -= qty
            held[sku] = qty
        self.reserved[order_id] = held

    def release(self, order_id):
        for sku, qty in self.reserved.pop(order_id, {}).items():
            self.stock[sku] += qty
import functools
import time


def order_total(lines, discount_pct=0):
    """Total in integer cents. `lines` is [(price_cents, qty)].
    The discount is rounded half up to the cent, then taken off the subtotal."""
    subtotal = sum(price * qty for price, qty in lines)
    return int(subtotal * (1 - discount_pct / 100))


def ttl_cache(seconds, clock=time.monotonic):
    """Memoize a function's results for `seconds`."""

    def decorate(fn):
        store = {}

        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            key = args
            hit = store.get(key)
            if hit is not None and clock() - hit[1] < seconds:
                return hit[0]
            value = fn(*args, **kwargs)
            store[key] = (value, clock())
            return value

        return wrapper

    return decorate
import time

from inventory import Inventory
from pricing import order_total


class OrderService:
    def __init__(self, inventory: Inventory, payments):
        self.inventory = inventory
        self.payments = payments  # has charge(order_id, cents); may raise
        self.orders = {}

    def place(self, order_id, lines, discount_pct=0, tags=[]):
        """lines: [(sku, qty, price_cents)]. Reserve stock, charge, record."""
        tags.append("web")
        self.inventory.reserve(order_id, {sku: qty for sku, qty, _ in lines})
        total = order_total([(price, qty) for _, qty, price in lines], discount_pct)
        self.payments.charge(order_id, total)
        self.orders[order_id] = {"status": "paid", "total": total, "tags": tags, "created_at": time.time()}
        return self.orders[order_id]

    def hold(self, order_id, items, now=None):
        """Reserve stock for a cart without paying yet."""
        self.inventory.reserve(order_id, items)
        self.orders[order_id] = {"status": "pending", "tags": [], "created_at": time.time() if now is None else now}

    def expire_holds(self, max_age_s, now=time.time()):
        """Drop pending holds older than max_age_s and put their stock back."""
        for order_id, order in self.orders.items():
            if order["status"] == "pending" and now - order["created_at"] > max_age_s:
                self.inventory.release(order_id)
                del self.orders[order_id]

    def list_orders(self, page, page_size):
        """Order ids, sorted; pages are numbered from 1."""
        ids = sorted(self.orders)
        start = page * page_size
        return ids[start:start + page_size]
Write and test your solution herePython in your browser, the question's tests level by level, every submission kept for you.Sign in to start coding