| """Standalone, no-LLM, no-server smoke tests for the JewelryShop env. |
| |
| Run from inside the ShopManagerEng folder so `models` / `server` import. |
| |
| Usage: |
| cd ShopManagerEng |
| SHOPMANAGER_MARKET_MODE=synthetic python test_env_smoke.py # default: A+B |
| SHOPMANAGER_MARKET_MODE=real python test_env_smoke.py live # C only |
| python test_env_smoke.py all # all three |
| |
| Why a script: putting `f'gold_price=${o.gold_price}/oz'` inside `bash -c "..."` |
| makes bash try to expand `${o.gold_price}` as a shell variable and crash with |
| `bad substitution`. Running it from a .py file removes that whole class of bugs. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import os |
| import sys |
|
|
| from server.ShopManagerEng_environment import JewelryShopEnvironment |
| from models import JewelryAction |
|
|
|
|
| def test_reward_path() -> None: |
| """A. Synthetic episode end-to-end, prints per-step partial + cumulative.""" |
| print("\n=== A. reward path (synthetic) ===") |
| os.environ["SHOPMANAGER_MARKET_MODE"] = "synthetic" |
| e = JewelryShopEnvironment() |
| o = e.reset(seed=42, task_id="market_timing", starting_cash=10000.0) |
| print( |
| f" reset: phase={o.phase} cash=${o.cash} gold={o.gold_oz}oz " |
| f"price=${o.gold_price} weights={o.weights}" |
| ) |
|
|
| actions = [ |
| JewelryAction(market_action="buy", gold_qty=1.0), |
| JewelryAction(product_choice="ring"), |
| JewelryAction(message="I accept"), |
| ] |
| for i, a in enumerate(actions, 1): |
| o = e.step(a) |
| print( |
| f" step {i}: phase={o.phase} reward={o.reward:.4f} " |
| f"cum={o.cumulative_reward:.4f} done={o.done}" |
| ) |
| print(f" FINAL cum={o.cumulative_reward:.4f} (must be in [0, 1])") |
|
|
|
|
| def test_bounce_loop() -> None: |
| """B. Warehouse cannot craft -> agent is bounced back to MARKET.""" |
| print("\n=== B. bounce loop (warehouse -> market) ===") |
| os.environ["SHOPMANAGER_MARKET_MODE"] = "synthetic" |
| e = JewelryShopEnvironment() |
| o = e.reset(seed=42, task_id="profit_negotiator", starting_cash=10000.0) |
|
|
| o = e.step(JewelryAction(market_action="buy", gold_qty=0.2)) |
| print(f" bought 0.2oz: phase={o.phase}, gold={o.gold_oz}oz") |
|
|
| o = e.step(JewelryAction(product_choice="ring")) |
| print( |
| f" tried ring: phase={o.phase} reentries={o.market_reentries}" |
| f"/{o.max_market_reentries} urgent={o.inventory_urgent} " |
| f"cannot_wait={o.cannot_wait}" |
| ) |
| assert o.phase == "market", "expected bounce back to market" |
| assert o.inventory_urgent is True, "expected urgent flag" |
|
|
| o = e.step(JewelryAction(market_action="wait")) |
| print(f" tried wait while urgent: phase={o.phase} (should still be market)") |
| assert o.phase == "market", "wait should be blocked when urgent" |
|
|
| o = e.step(JewelryAction(market_action="buy", gold_qty=1.0)) |
| print(f" bought 1.0oz more: phase={o.phase} gold={o.gold_oz}oz") |
|
|
| o = e.step(JewelryAction(product_choice="ring")) |
| print(f" craft ring: phase={o.phase} cum={o.cumulative_reward:.4f}") |
|
|
| o = e.step(JewelryAction(message="I accept")) |
| print(f" FINAL cum={o.cumulative_reward:.4f}") |
|
|
|
|
| def test_live_quote() -> None: |
| """C. Real mode: live yfinance gold price (needs network).""" |
| print("\n=== C. live yfinance quote (real mode) ===") |
| os.environ["SHOPMANAGER_MARKET_MODE"] = "real" |
| e = JewelryShopEnvironment() |
| o = e.reset(seed=0, task_id="market_timing", starting_cash=10000.0) |
| print(f" gold_price=${o.gold_price}/oz source={o.gold_price_source}") |
| print(f" market_mode={o.market_mode}") |
| print(f" history(last 5)={o.gold_price_history[-5:]}") |
|
|
|
|
| def main(argv: list[str]) -> None: |
| arg = (argv[1] if len(argv) > 1 else "default").lower() |
|
|
| if arg in ("default", "ab"): |
| test_reward_path() |
| test_bounce_loop() |
| elif arg == "live": |
| test_live_quote() |
| elif arg == "all": |
| test_reward_path() |
| test_bounce_loop() |
| test_live_quote() |
| elif arg == "a": |
| test_reward_path() |
| elif arg == "b": |
| test_bounce_loop() |
| elif arg == "c": |
| test_live_quote() |
| else: |
| print(f"unknown arg: {arg}. use one of: a / b / c / ab / live / all") |
| sys.exit(2) |
|
|
|
|
| if __name__ == "__main__": |
| main(sys.argv) |
|
|