Spaces:
Running
Running
| 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() | |