code.pablomurad.com

prami-pet

Community virtual pet that lives as a Mastodon account

tests/test_engine.py raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from datetime import timedelta

from prami import engine
from prami import repository as repo
from prami.models import utcnow
from prami.state import compute_mood


def test_feed_reduces_hunger_and_is_accepted(session, pet, user, config):
    pet.hunger = 80
    result = engine.handle(session, pet, user, "feed", config)
    assert result.accepted is True
    assert pet.hunger == 45


def test_stats_never_leave_bounds(session, pet, user, config):
    pet.hunger = 10
    engine.handle(session, pet, user, "feed", config)
    assert pet.hunger == 0
    assert 0 <= pet.happiness <= 100


def test_overfeeding_raises_chaos(session, pet, user, config):
    pet.hunger = 20
    pet.chaos = 30
    engine.handle(session, pet, user, "feed", config)
    assert pet.chaos > 30


def test_play_drops_energy_and_raises_chaos(session, pet, user, config):
    pet.energy = 80
    pet.chaos = 20
    engine.handle(session, pet, user, "play", config)
    assert pet.energy == 65
    assert pet.chaos == 25


def test_clean_costs_some_happiness(session, pet, user, config):
    pet.cleanliness = 20
    pet.happiness = 60
    engine.handle(session, pet, user, "clean", config)
    assert pet.cleanliness == 60
    assert pet.happiness == 54


def test_user_cooldown_blocks_second_feed(session, pet, user, config):
    first = engine.handle(session, pet, user, "feed", config)
    second = engine.handle(session, pet, user, "feed", config)
    assert first.accepted is True
    assert second.accepted is False


def test_cooldown_clears_after_window(session, pet, user, config):
    now = utcnow()
    engine.handle(session, pet, user, "feed", config, now=now)
    again = engine.handle(session, pet, user, "feed", config, now=now + timedelta(minutes=31))
    assert again.accepted is True


def test_global_per_command_rate_limit(session, pet, user, config):
    now = utcnow()
    for i in range(5):
        other = repo.get_or_create_user(session, f"u{i}@example.social")
        result = engine.handle(session, pet, other, "clean", config, now=now)
        assert result.accepted is True
    blocked = repo.get_or_create_user(session, "late@example.social")
    sixth = engine.handle(session, pet, blocked, "clean", config, now=now)
    assert sixth.accepted is False


def test_sleep_then_wake_toggles_state(session, pet, user, config):
    engine.handle(session, pet, user, "sleep", config)
    assert pet.asleep is True
    engine.handle(session, pet, user, "wake", config)
    assert pet.asleep is False


def test_sleep_global_cooldown_applies_across_users(session, pet, user, config):
    now = utcnow()
    engine.handle(session, pet, user, "sleep", config, now=now)
    engine.handle(session, pet, user, "wake", config, now=now)
    other = repo.get_or_create_user(session, "second@example.social")
    blocked = engine.handle(session, pet, other, "sleep", config, now=now + timedelta(minutes=1))
    assert blocked.accepted is False


def test_active_care_rejected_while_asleep(session, pet, user, config):
    pet.asleep = True
    result = engine.handle(session, pet, user, "play", config)
    assert result.accepted is False
    assert pet.asleep is True


def test_blocked_user_is_silently_ignored(session, pet, user, config):
    user.blocked = True
    assert engine.handle(session, pet, user, "feed", config) is None


def test_per_user_hourly_cap(session, pet, user, config):
    config.max_per_user_hour = 1
    engine.handle(session, pet, user, "status", config)
    capped = engine.handle(session, pet, user, "status", config)
    assert capped.accepted is False


def test_unknown_command_still_responds(session, pet, user, config):
    result = engine.handle(session, pet, user, None, config)
    assert result is not None
    assert result.accepted is True


def test_mood_reflects_hunger(session, pet):
    pet.hunger = 90
    pet.asleep = False
    assert compute_mood(pet) == "ravenous"