Code Farm economy rebalance: market saturation, infrastructure/defense/cosmetics, mastery track, secondary leaderboards, admin eras, underdog bonus and weekly contracts
Every purchase/upgrade path (new and pre-existing) is now race-safe against concurrent requests via atomic conditional SQL updates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ def _reset_farm(username, coins=100000):
|
||||
if farm:
|
||||
get_table("game_plots").delete(farm_uid=farm["uid"])
|
||||
get_table("game_farms").delete(uid=farm["uid"])
|
||||
get_table("game_market_ticks").delete()
|
||||
farm = store.ensure_farm(user["uid"])
|
||||
get_table("game_farms").update({"uid": farm["uid"], "coins": coins}, ["uid"])
|
||||
refresh_snapshot()
|
||||
@@ -189,6 +190,130 @@ def _ripen_owner_plot(owner, slot=0):
|
||||
return plot
|
||||
|
||||
|
||||
def _set_farm(username, **fields):
|
||||
refresh_snapshot()
|
||||
user = get_table("users").find_one(username=username)
|
||||
farm = store.get_farm(user["uid"])
|
||||
get_table("game_farms").update({"uid": farm["uid"], **fields}, ["uid"])
|
||||
refresh_snapshot()
|
||||
|
||||
|
||||
def test_defense_upgrade_requires_auth(app_server, seeded_db):
|
||||
r = requests.post(f"{BASE_URL}/game/defense/upgrade", headers=JSON)
|
||||
assert r.status_code in (401, 303)
|
||||
|
||||
|
||||
def test_defense_upgrade_success(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name)
|
||||
r = session.post(f"{BASE_URL}/game/defense/upgrade", headers=JSON)
|
||||
assert r.status_code == 200, r.text[:200]
|
||||
assert r.json()["farm"]["defense_level"] == 1
|
||||
|
||||
|
||||
def test_defense_upgrade_insufficient_coins_returns_400(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name, coins=0)
|
||||
r = session.post(f"{BASE_URL}/game/defense/upgrade", headers=JSON)
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_infrastructure_buy_requires_prestige(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name, coins=100_000_000)
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/infrastructure/buy", data={"key": "registry"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_infrastructure_buy_success(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name, coins=100_000_000)
|
||||
_set_farm(name, prestige=5)
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/infrastructure/buy", data={"key": "registry"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 200, r.text[:200]
|
||||
farm = r.json()["farm"]
|
||||
owned = next(i for i in farm["infrastructure"] if i["key"] == "registry")
|
||||
assert owned["owned"] is True
|
||||
|
||||
|
||||
def test_infrastructure_buy_unknown_key_returns_400(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name)
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/infrastructure/buy", data={"key": "does_not_exist"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_cosmetics_buy_requires_auth(app_server, seeded_db):
|
||||
r = requests.post(
|
||||
f"{BASE_URL}/game/cosmetics/buy", data={"key": "title_architect"}, headers=JSON
|
||||
)
|
||||
assert r.status_code in (401, 303)
|
||||
|
||||
|
||||
def test_cosmetics_buy_and_equip(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name, coins=1_000_000)
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/cosmetics/buy", data={"key": "title_architect"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 200, r.text[:200]
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/cosmetics/equip", data={"key": "title_architect"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 200, r.text[:200]
|
||||
assert r.json()["farm"]["active_title"] == "title_architect"
|
||||
|
||||
|
||||
def test_cosmetics_buy_insufficient_coins_returns_400(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name, coins=0)
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/cosmetics/buy", data={"key": "title_architect"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_mastery_upgrade_requires_auth(app_server, seeded_db):
|
||||
r = requests.post(
|
||||
f"{BASE_URL}/game/mastery", data={"key": "autoreplant"}, headers=JSON
|
||||
)
|
||||
assert r.status_code in (401, 303)
|
||||
|
||||
|
||||
def test_mastery_upgrade_insufficient_points_returns_400(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name)
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/mastery", data={"key": "autoreplant"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_mastery_upgrade_success(app_server, seeded_db):
|
||||
session, name = _signup()
|
||||
_reset_farm(name)
|
||||
_set_farm(name, mastery_points=1000)
|
||||
r = session.post(
|
||||
f"{BASE_URL}/game/mastery", data={"key": "autoreplant"}, headers=JSON
|
||||
)
|
||||
assert r.status_code == 200, r.text[:200]
|
||||
upgrade = next(m for m in r.json()["farm"]["mastery"] if m["key"] == "autoreplant")
|
||||
assert upgrade["level"] == 1
|
||||
|
||||
|
||||
def test_leaderboard_boards_all_return_200(app_server, seeded_db):
|
||||
for board in ("score", "prestige", "harvests", "raids", "time_to_kernel", "fair_play", "era"):
|
||||
r = requests.get(f"{BASE_URL}/game/leaderboard", params={"board": board}, headers=JSON)
|
||||
assert r.status_code == 200, (board, r.text[:200])
|
||||
assert "entries" in r.json()
|
||||
|
||||
|
||||
def test_steal_cooldown_blocks_second_raid(app_server, seeded_db):
|
||||
owner_session, owner = _signup()
|
||||
_reset_farm(owner, coins=100000)
|
||||
|
||||
Reference in New Issue
Block a user