[
  { "name": "markup 50 gives x1.5 (old per-hour calculator gave x51)", "fn": "priceFromCost", "args": [100, "markup", 0.5], "expect": 150 },
  { "name": "pct(50) is the fraction 0.5", "fn": "pct", "args": [50], "expect": 0.5 },
  { "name": "margin 20% prices cost / (1 - m)", "fn": "priceFromCost", "args": [80, "margin", 0.2], "expect": 100 },
  { "name": "margin profit on $80 at 20%", "fn": "marginProfit", "args": [80, 0.2], "expect": 20 },
  { "name": "markup 25% equals margin 20%", "fn": "markupToMargin", "args": [0.25], "expect": 0.2 },
  { "name": "margin 20% equals markup 25%", "fn": "marginToMarkup", "args": [0.2], "expect": 0.25 },

  { "name": "regression: commercial add-ons are qty x price, unchecked ignored", "fn": "addonsTotal",
    "args": [[{ "on": true, "qty": 3, "price": 25 }, { "on": false, "qty": 2, "price": 100 }, { "on": true, "qty": 2, "price": 12.5 }]], "expect": 100 },
  { "name": "regression: total-hours mode uses the typed hours", "fn": "commercialHours", "args": [{ "mode": "total", "totalHours": 12, "sqft": 50000 }], "expect": 12 },
  { "name": "commercial sq ft mode divides by production rate", "fn": "commercialHours", "args": [{ "mode": "sqft", "sqft": 9000, "productionRate": 3000 }], "expect": 3 },
  { "name": "regression: a +15% flag applies exactly 15%", "fn": "applySurcharges", "args": [200, [15]], "expect": 30 },
  { "name": "regression: margin applied once (cost 80 at 20% is 100, not 125)", "fn": "quote",
    "args": [{ "wage": 20, "burdenPct": 0, "laborHours": 4, "rate": 0.2 }], "expect": { "cost": 80, "price": 100, "total": 100 } },
  { "name": "regression: house quote with crew 1", "fn": "quote",
    "args": [{ "wage": 20, "burdenPct": 0.2, "laborHours": 4, "crew": 1, "rate": 0.2 }], "expect": { "total": 120, "clockHours": 4 } },
  { "name": "regression: house quote with crew 3 has the same price", "fn": "quote",
    "args": [{ "wage": 20, "burdenPct": 0.2, "laborHours": 4, "crew": 3, "rate": 0.2 }], "expect": { "total": 120, "clockHours": 1.3333333 } },
  { "name": "regression: carpet per-room uses a per-room rate", "fn": "carpetBase", "args": [{ "model": "room", "rooms": 3, "roomRate": 40 }], "expect": 120 },
  { "name": "carpet per sq ft", "fn": "carpetBase", "args": [{ "model": "sqft", "sqft": 750, "sqftRate": 0.35 }], "expect": 262.5 },
  { "name": "regression: window panes drive labour hours", "fn": "windowJob",
    "args": [{ "windows": 20, "panesPerWindow": 4, "minutesPerWindow": 10, "stories": 1 }], "expect": { "panes": 80, "laborHours": 6.6666667, "heightPremium": 0 } },
  { "name": "regression: window height premium on upper-floor windows only", "fn": "windowJob",
    "args": [{ "windows": 20, "panesPerWindow": 2, "minutesPerWindow": 10, "stories": 2 }], "expect": { "upperWindows": 10, "heightPremium": 50 } },
  { "name": "window 3 stories premium on two thirds", "fn": "windowJob",
    "args": [{ "windows": 30, "panesPerWindow": 2, "minutesPerWindow": 10, "stories": 3 }], "expect": { "upperWindows": 20, "heightPremium": 300 } },
  { "name": "regression: furniture stain fee skipped when priced per item", "fn": "furnitureJobFees",
    "args": [{ "stain": true }, [{ "on": true, "kind": "stain", "fee": 45 }, { "on": true, "kind": "odor", "fee": 65 }]], "expect": 65 },
  { "name": "regression: default result with no location uses the national median", "fn": "wageFrom", "args": [null], "expect": 17.71 },
  { "name": "wage falls back from median to mean", "fn": "wageFrom", "args": [{ "h_median": "", "h_mean": "20.5" }], "expect": 20.5 },
  { "name": "wage uses median when present", "fn": "wageFrom", "args": [{ "h_median": "18.02", "h_mean": "19.40" }], "expect": 18.02 },

  { "name": "minimum charge applies and untaxed service adds no tax", "fn": "finalize",
    "args": [{ "price": 80, "minimum": 100, "taxable": false, "taxRate": 0.08 }], "expect": { "preTax": 100, "tax": 0, "total": 100, "minimumApplied": true } },
  { "name": "frequency discount then tax when taxable", "fn": "finalize",
    "args": [{ "price": 200, "addons": 50, "freqDisc": 0.1, "taxable": true, "taxRate": 0.0625 }], "expect": { "preTax": 225, "tax": 14.06, "total": 239.06 } },
  { "name": "payroll burden with SUTA 2.7%", "fn": "burden", "args": [20, 2080, { "sutaRate": 0.027 }], "expect": { "total": 0.0820529 } },
  { "name": "solo rate covers SE tax and expenses", "fn": "soloRate", "args": [{ "takeHome": 50000, "expenses": 8000, "billableHours": 1500 }], "expect": 43.38 },
  { "name": "customer range p25/low band to p75/high band", "fn": "customerRange",
    "args": [{ "laborHours": 4, "burdenPct": 0.2, "rate": 0.2 }, { "p25": 16, "median": 18, "p75": 21 }, { "low": { "rate": 0.15 }, "high": { "rate": 0.3 } }],
    "expect": { "low": 90.35, "mid": 108, "high": 144, "wageSource": "oews" } },
  { "name": "customer range with no location uses the national median", "fn": "customerRange",
    "args": [{ "laborHours": 2, "burdenPct": 0, "rate": 0 }, {}, {}], "expect": { "low": 35.42, "mid": 35.42, "high": 35.42, "wageSource": "national-median" } },
  { "name": "customer range from a quote re-prices labour at p25/p75", "fn": "rangeFromTotal",
    "args": [200, 0.5, 18, { "p25": 16, "p75": 21 }, 0.2], "expect": { "low": 177.78, "high": 231.11, "wageSource": "oews" } },
  { "name": "customer range from a quote with no location keeps only the margin band", "fn": "rangeFromTotal",
    "args": [200, 0.5, 17.71, {}, 0.2], "expect": { "low": 188.24, "high": 213.33, "wageSource": "national-median" } },
  { "name": "house hours 2BR/1BA standard", "fn": "houseHours", "args": [{ "beds": 2, "baths": 1, "service": "standard" }], "expect": 2.75 },
  { "name": "house hours 2BR/1BA deep", "fn": "houseHours", "args": [{ "beds": 2, "baths": 1, "service": "deep" }], "expect": 4.25 },
  { "name": "house hours 3BR/2BA 1,800 sq ft standard", "fn": "houseHours", "args": [{ "beds": 3, "baths": 2, "living": 1, "sqft": 1800, "service": "standard" }], "expect": 5 },
  { "name": "full cost stack with travel, mileage, supplies, equipment and overhead", "fn": "quote",
    "args": [{ "wage": 18, "burdenPct": 0.1, "laborHours": 3, "travelHours": 0.5, "miles": 10, "mileageRate": 0.725, "supplies": 12, "equipRate": 2, "overheadRate": 5, "rate": 0.25, "minimum": 0 }],
    "expect": { "loaded": 19.8, "cost": 109.55, "price": 146.07, "total": 146.07 } }
]
