mud/tests/test_weather.py
Jared Miller 25339edbf5
Add weather-driven ambient messages
Rain, storm, snow, and fog now have atmospheric ambient messages.
Clear and cloudy conditions return empty list. Messages are evocative
and lowercase, ready to be mixed with zone-specific ambience.
2026-02-14 16:20:00 -05:00

260 lines
8.7 KiB
Python

"""Tests for weather system."""
import random
from mudlib.weather import (
WeatherCondition,
WeatherState,
advance_weather,
get_weather_description,
)
def test_weather_condition_enum():
assert WeatherCondition.clear.value == "clear"
assert WeatherCondition.cloudy.value == "cloudy"
assert WeatherCondition.rain.value == "rain"
assert WeatherCondition.storm.value == "storm"
assert WeatherCondition.snow.value == "snow"
assert WeatherCondition.fog.value == "fog"
def test_weather_state_dataclass():
state = WeatherState(condition=WeatherCondition.rain, intensity=0.5)
assert state.condition == WeatherCondition.rain
assert state.intensity == 0.5
def test_get_weather_description_returns_non_empty():
state = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
description = get_weather_description(state)
assert isinstance(description, str)
assert len(description) > 0
def test_get_weather_description_varies_by_condition():
clear = get_weather_description(
WeatherState(condition=WeatherCondition.clear, intensity=0.5)
)
rain = get_weather_description(
WeatherState(condition=WeatherCondition.rain, intensity=0.5)
)
snow = get_weather_description(
WeatherState(condition=WeatherCondition.snow, intensity=0.5)
)
# Different conditions should produce different descriptions
assert clear != rain
assert rain != snow
assert clear != snow
def test_get_weather_description_varies_by_intensity():
light_rain = get_weather_description(
WeatherState(condition=WeatherCondition.rain, intensity=0.2)
)
heavy_rain = get_weather_description(
WeatherState(condition=WeatherCondition.rain, intensity=0.9)
)
# Different intensities should produce different descriptions
assert light_rain != heavy_rain
def test_advance_weather_returns_new_state():
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
rng = random.Random(42)
new_state = advance_weather(current, season="summer", rng=rng)
assert isinstance(new_state, WeatherState)
assert isinstance(new_state.condition, WeatherCondition)
assert 0.0 <= new_state.intensity <= 1.0
def test_advance_weather_is_deterministic_with_seed():
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
rng1 = random.Random(42)
state1 = advance_weather(current, season="summer", rng=rng1)
rng2 = random.Random(42)
state2 = advance_weather(current, season="summer", rng=rng2)
assert state1.condition == state2.condition
assert state1.intensity == state2.intensity
def test_advance_weather_transitions_naturally():
# Clear can become cloudy
rng = random.Random(42)
for _ in range(100):
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
new_state = advance_weather(current, season="summer", rng=rng)
if new_state.condition == WeatherCondition.cloudy:
break
else:
raise AssertionError("clear never transitioned to cloudy in 100 iterations")
# Cloudy can become rain or clear
rng = random.Random(43)
found_rain = False
found_clear = False
for _ in range(100):
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
new_state = advance_weather(current, season="summer", rng=rng)
if new_state.condition == WeatherCondition.rain:
found_rain = True
if new_state.condition == WeatherCondition.clear:
found_clear = True
if found_rain and found_clear:
break
assert found_rain or found_clear
def test_storm_transitions_to_rain_or_cloudy():
# Storm should always transition away (doesn't last)
rng = random.Random(44)
found_non_storm = False
for _ in range(50):
current = WeatherState(condition=WeatherCondition.storm, intensity=0.8)
new_state = advance_weather(current, season="summer", rng=rng)
if new_state.condition in (WeatherCondition.rain, WeatherCondition.cloudy):
found_non_storm = True
break
assert found_non_storm, "storm should transition to rain or cloudy"
def test_snow_only_in_winter_autumn():
# Snow in winter
rng = random.Random(45)
found_snow = False
for _ in range(200):
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
new_state = advance_weather(current, season="winter", rng=rng)
if new_state.condition == WeatherCondition.snow:
found_snow = True
break
assert found_snow, "snow should be possible in winter"
# Snow should be rare or impossible in summer
rng = random.Random(46)
for _ in range(100):
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
new_state = advance_weather(current, season="summer", rng=rng)
# Should not produce snow in summer
assert new_state.condition != WeatherCondition.snow
def test_climate_temperate_default():
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
rng = random.Random(47)
# Should work without climate parameter (defaults to temperate)
new_state = advance_weather(current, season="summer", rng=rng)
assert isinstance(new_state, WeatherState)
def test_climate_arid_favors_clear():
# Arid should heavily favor clear weather
rng = random.Random(48)
clear_count = 0
for _ in range(100):
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
new_state = advance_weather(current, season="summer", rng=rng, climate="arid")
if new_state.condition == WeatherCondition.clear:
clear_count += 1
# Arid should stay clear most of the time
assert clear_count > 70, f"arid should favor clear, got {clear_count}/100"
def test_climate_arid_no_snow():
# Arid should never produce snow
rng = random.Random(49)
for _ in range(100):
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
new_state = advance_weather(current, season="winter", rng=rng, climate="arid")
assert new_state.condition != WeatherCondition.snow
def test_climate_arctic_favors_snow_fog_cloudy():
# Arctic should produce snow, fog, or cloudy frequently
rng = random.Random(50)
arctic_conditions = 0
for _ in range(100):
current = WeatherState(condition=WeatherCondition.cloudy, intensity=0.5)
new_state = advance_weather(current, season="winter", rng=rng, climate="arctic")
if new_state.condition in (
WeatherCondition.snow,
WeatherCondition.fog,
WeatherCondition.cloudy,
):
arctic_conditions += 1
# Arctic should heavily favor snow/fog/cloudy
assert arctic_conditions > 70, (
f"arctic should favor snow/fog/cloudy, got {arctic_conditions}/100"
)
def test_advance_weather_accepts_all_seasons():
current = WeatherState(condition=WeatherCondition.clear, intensity=0.5)
rng = random.Random(51)
for season in ["spring", "summer", "autumn", "winter"]:
new_state = advance_weather(current, season=season, rng=rng)
assert isinstance(new_state, WeatherState)
def test_get_weather_ambience_rain():
from mudlib.weather import get_weather_ambience
messages = get_weather_ambience(WeatherCondition.rain)
assert isinstance(messages, list)
assert len(messages) > 0
assert all(isinstance(msg, str) for msg in messages)
def test_get_weather_ambience_storm():
from mudlib.weather import get_weather_ambience
messages = get_weather_ambience(WeatherCondition.storm)
assert isinstance(messages, list)
assert len(messages) > 0
assert all(isinstance(msg, str) for msg in messages)
def test_get_weather_ambience_snow():
from mudlib.weather import get_weather_ambience
messages = get_weather_ambience(WeatherCondition.snow)
assert isinstance(messages, list)
assert len(messages) > 0
assert all(isinstance(msg, str) for msg in messages)
def test_get_weather_ambience_fog():
from mudlib.weather import get_weather_ambience
messages = get_weather_ambience(WeatherCondition.fog)
assert isinstance(messages, list)
assert len(messages) > 0
assert all(isinstance(msg, str) for msg in messages)
def test_get_weather_ambience_clear():
from mudlib.weather import get_weather_ambience
messages = get_weather_ambience(WeatherCondition.clear)
assert isinstance(messages, list)
assert len(messages) == 0
def test_get_weather_ambience_cloudy():
from mudlib.weather import get_weather_ambience
messages = get_weather_ambience(WeatherCondition.cloudy)
assert isinstance(messages, list)
assert len(messages) == 0