OpenEnv / tests /test_openenv.py
mahammadaftab's picture
Initial OpenEnv Email Triage Submission
4b77608
"""
Test Suite for OpenEnv Email Triage
Comprehensive tests covering:
- Environment initialization
- API compliance (Pydantic models)
- Email generation dynamics
- Reward computation
- Termination conditions
- Configuration system
"""
import pytest
import os
import sys
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from openenv.core.env import OpenEnv
from openenv.core.config import EnvConfig
from openenv.core.models import Observation, Action, Reward, EnvState
class TestEnvInitialization:
def test_default_initialization(self):
env = OpenEnv()
assert env is not None
assert isinstance(env.config, EnvConfig)
env.close()
def test_custom_config_initialization(self):
config = EnvConfig(num_emails=10, verbose=False, random_seed=42)
env = OpenEnv(config=config)
assert env.config.num_emails == 10
assert env.config.random_seed == 42
env.close()
def test_invalid_config(self):
with pytest.raises(ValueError):
config = EnvConfig(num_emails=-5)
config.validate()
class TestAPICompliance:
def test_reset_returns_observation(self):
env = OpenEnv()
obs, info = env.reset()
assert isinstance(obs, Observation)
assert isinstance(info, dict)
assert obs.emails_remaining == env.config.num_emails
env.close()
def test_step_returns_correct_format(self):
env = OpenEnv()
env.reset()
act = Action(action_type=0)
obs, reward, terminated, truncated, info = env.step(act)
assert isinstance(obs, Observation)
assert isinstance(reward, float)
assert isinstance(terminated, bool)
assert isinstance(truncated, bool)
assert isinstance(info, dict)
env.close()
def test_state_returns_envstate(self):
env = OpenEnv()
env.reset()
state = env.state()
assert isinstance(state, EnvState)
assert isinstance(state.observation, Observation)
assert isinstance(state.reward, Reward)
env.close()
class TestRewardFunction:
def test_correct_action_reward(self):
env = OpenEnv(config=EnvConfig(num_emails=1))
obs, _ = env.reset()
email = obs.current_email
# Determine ground truth locally to force correct action
if email.is_spam: act = 4
elif email.is_urgent:
act = 2 if "forward" in email.body.lower() else 1
elif email.is_internal:
act = 1 if "?" in email.body else 3
else: act = 3
_, reward, _, _, _ = env.step(Action(action_type=act))
assert reward == 1.0
env.close()
def test_critical_failure_penalty(self):
env = OpenEnv(config=EnvConfig(num_emails=10, urgent_ratio=1.0, spam_ratio=0.0))
obs, _ = env.reset()
# It's an urgent email
assert obs.current_email.is_urgent == True
# Ignored or deleted
_, reward, _, _, _ = env.step(Action(action_type=0))
assert reward == -5.0
env.close()
class TestTerminationConditions:
def test_episode_termination(self):
config = EnvConfig(num_emails=2, verbose=False)
env = OpenEnv(config=config)
env.reset()
_, _, term1, _, _ = env.step(Action(action_type=0))
assert not term1
_, _, term2, _, _ = env.step(Action(action_type=0))
assert term2
# further steps should safely do nothing and return 0 rew
obs, rem_rew, term3, _, _ = env.step(Action(action_type=0))
assert term3
assert rem_rew == 0.0
assert obs.emails_remaining == 0
env.close()
class TestConfiguration:
def test_config_save_load(self, tmp_path):
config = EnvConfig(num_emails=55, task_level="hard")
filepath = tmp_path / "config.json"
config.save(str(filepath))
loaded = EnvConfig.load(str(filepath))
assert loaded.num_emails == 55
assert loaded.task_level == "hard"
if __name__ == "__main__":
pytest.main([__file__, "-v"])