lifeops / tests /test_env.py
hilarycc's picture
initial environment setup
d40203a
import unittest
from env.actions import ActionType
from env.lifeops_env import LifeOpsEnv
class TestLifeOpsEnv(unittest.TestCase):
def test_reset_returns_structured_state(self) -> None:
env = LifeOpsEnv(seed=1)
obs = env.reset("s1_basic_conflict")
self.assertIn("calendar", obs)
self.assertIn("tasks", obs)
self.assertIn("current_request", obs)
self.assertIsInstance(obs["calendar"], list)
self.assertIsInstance(obs["tasks"], list)
def test_valid_actions_are_constrained_to_current_request(self) -> None:
env = LifeOpsEnv(seed=1)
obs = env.reset("s1_basic_conflict")
req_id = obs["current_request"]["event_id"]
valid = env.valid_actions()
# All request-handling actions should reference the current request id.
for a in valid:
if a.action_type in {
ActionType.accept_event,
ActionType.reject_event,
ActionType.reschedule_event,
ActionType.propose_new_time,
}:
self.assertEqual(a.request_id, req_id)
def test_accept_moves_request_into_calendar(self) -> None:
env = LifeOpsEnv(seed=1)
obs = env.reset("s2_travel_tight")
req_id = obs["current_request"]["event_id"]
accept = next(a for a in env.valid_actions() if a.action_type == ActionType.accept_event)
obs2, reward, done, info = env.step(accept)
self.assertEqual(obs2["pending_request_count"], 0)
self.assertTrue(any(e["event_id"] == req_id for e in obs2["calendar"]))
def test_propose_does_not_add_to_calendar(self) -> None:
env = LifeOpsEnv(seed=1)
obs = env.reset("s2_travel_tight")
req_id = obs["current_request"]["event_id"]
propose = next(a for a in env.valid_actions() if a.action_type == ActionType.propose_new_time)
obs2, reward, done, info = env.step(propose)
self.assertEqual(obs2["pending_request_count"], 0)
self.assertFalse(any(e["event_id"] == req_id for e in obs2["calendar"]))
def test_accept_overlap_is_penalized(self) -> None:
env = LifeOpsEnv(seed=1)
env.reset("s1_basic_conflict") # request overlaps standup
accept = next(a for a in env.valid_actions() if a.action_type == ActionType.accept_event)
obs2, reward, done, info = env.step(accept)
self.assertLess(reward, 0.0)
self.assertTrue(info["overlaps"])
def test_travel_infeasible_is_penalized(self) -> None:
env = LifeOpsEnv(seed=1)
env.reset("s2_travel_tight") # Downtown -> Office with 5 minute gap
accept = next(a for a in env.valid_actions() if a.action_type == ActionType.accept_event)
obs2, reward, done, info = env.step(accept)
self.assertLess(reward, 0.0)
self.assertTrue(info["travel_issues"])
def test_block_focus_time_reduces_task_minutes(self) -> None:
env = LifeOpsEnv(seed=1)
obs = env.reset("s3_focus_vs_meeting")
before = obs["tasks"][0]["remaining_minutes"]
focus = next(a for a in env.valid_actions() if a.action_type == ActionType.block_focus_time)
obs2, reward, done, info = env.step(focus)
after = obs2["tasks"][0]["remaining_minutes"]
self.assertLess(after, before)
if __name__ == "__main__":
unittest.main()