Dockerfile with Chromium, capture_steps module, Chrome environment support, stale-element retries, owned-amount purchase check, the five screenshots from a 300 second run (16.6 cookies per second) and the README from the framework template. Reviewed in RC-013. Task: MIL-004#1 Task: MIL-004#2 Task: MIL-004#3 Task: MIL-004#4 Task: MIL-004#5 Closes #19 Closes #20 Closes #21 Closes #22 Closes #23
111 lines
3.4 KiB
Python
111 lines
3.4 KiB
Python
"""@file
|
|
@brief Tests of the run loop against a fake game and a fake clock.
|
|
"""
|
|
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
|
|
from cookie_clicker_bot.application.use_cases.run_bot import run_bot
|
|
from cookie_clicker_bot.constants import (
|
|
BIG_COOKIE_ID,
|
|
CLASS_ATTRIBUTE,
|
|
CLICKS_PER_BURST,
|
|
COOKIE_COUNTER_ID,
|
|
CPS_ID,
|
|
PRODUCT_ID_PREFIX,
|
|
PRODUCT_OWNED_ID_PREFIX,
|
|
PRODUCT_PRICE_ID_PREFIX,
|
|
RUN_DURATION_SECONDS,
|
|
STORE_CHECK_INTERVAL_SECONDS,
|
|
)
|
|
from cookie_clicker_bot.domain.errors import UnreadableNumberError
|
|
from tests.unit.fakes import FakeClock, FakeGame
|
|
|
|
PRODUCT = f"{PRODUCT_ID_PREFIX}0"
|
|
|
|
|
|
def make_game(cookies: int = 100, price: int = 15) -> FakeGame:
|
|
"""A store with one affordable product that is spent once, then unaffordable."""
|
|
game = FakeGame(
|
|
texts={
|
|
COOKIE_COUNTER_ID: f"{cookies} cookies",
|
|
f"{PRODUCT_PRICE_ID_PREFIX}0": str(price),
|
|
CPS_ID: "per second: 4.5",
|
|
f"{PRODUCT_OWNED_ID_PREFIX}0": "",
|
|
},
|
|
attributes={(PRODUCT, CLASS_ATTRIBUTE): "product unlocked enabled"},
|
|
)
|
|
|
|
def spend(element_id: str) -> None:
|
|
if element_id == PRODUCT:
|
|
game.texts[COOKIE_COUNTER_ID] = f"{cookies - price} cookies"
|
|
game.texts[f"{PRODUCT_OWNED_ID_PREFIX}0"] = "1"
|
|
game.attributes[(PRODUCT, CLASS_ATTRIBUTE)] = "product unlocked disabled"
|
|
|
|
game.on_click = spend
|
|
return game
|
|
|
|
|
|
def test_run_bot_returns_the_cps() -> None:
|
|
assert run_bot(make_game(), FakeClock(), 3) == Decimal("4.5")
|
|
|
|
|
|
def test_run_bot_clicks_in_bursts_until_the_duration_is_over() -> None:
|
|
game = FakeGame(texts={CPS_ID: "per second: 1"})
|
|
run_bot(game, FakeClock(step=1.0), 3)
|
|
assert game.clicked == [BIG_COOKIE_ID] * CLICKS_PER_BURST * 3
|
|
|
|
|
|
def test_run_bot_stops_after_the_duration() -> None:
|
|
clock = FakeClock(step=10.0)
|
|
game = FakeGame(texts={CPS_ID: "per second: 1"})
|
|
run_bot(game, clock, 25)
|
|
assert clock.now == 40.0
|
|
|
|
|
|
def test_run_bot_does_not_visit_the_store_before_the_interval() -> None:
|
|
game = make_game()
|
|
run_bot(game, FakeClock(step=1.0), STORE_CHECK_INTERVAL_SECONDS - 1)
|
|
assert PRODUCT not in game.clicked
|
|
|
|
|
|
def test_run_bot_buys_at_the_interval() -> None:
|
|
game = make_game()
|
|
run_bot(game, FakeClock(step=1.0), STORE_CHECK_INTERVAL_SECONDS + 2)
|
|
assert game.clicked.count(PRODUCT) == 1
|
|
|
|
|
|
def test_run_bot_stops_buying_when_nothing_is_affordable() -> None:
|
|
game = make_game()
|
|
run_bot(game, FakeClock(step=1.0), STORE_CHECK_INTERVAL_SECONDS * 3)
|
|
assert game.clicked.count(PRODUCT) == 1
|
|
|
|
|
|
def test_run_bot_stops_buying_when_a_purchase_fails() -> None:
|
|
game = make_game()
|
|
game.on_click = None
|
|
run_bot(game, FakeClock(step=1.0), STORE_CHECK_INTERVAL_SECONDS + 1)
|
|
assert game.clicked.count(PRODUCT) == 1
|
|
|
|
|
|
@pytest.mark.parametrize("duration", [0, -1])
|
|
def test_run_bot_rejects_a_non_positive_duration(duration: float) -> None:
|
|
game = FakeGame()
|
|
with pytest.raises(ValueError, match="positive"):
|
|
run_bot(game, FakeClock(), duration)
|
|
assert game.clicked == []
|
|
|
|
|
|
def test_run_bot_propagates_an_unreadable_cps() -> None:
|
|
game = FakeGame(texts={CPS_ID: "nothing"})
|
|
with pytest.raises(UnreadableNumberError):
|
|
run_bot(game, FakeClock(), 1)
|
|
|
|
|
|
def test_run_duration_default_is_the_constant() -> None:
|
|
clock = FakeClock(step=100.0)
|
|
game = FakeGame(texts={CPS_ID: "per second: 1"})
|
|
run_bot(game, clock)
|
|
assert clock.now == 100.0 * (RUN_DURATION_SECONDS // 100 + 1)
|