From 11a5d33211cd6095dbf0dbad5ea9be6ddf63de72 Mon Sep 17 00:00:00 2001 From: leiwilson <305842734+leiwilson@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:54:41 +0100 Subject: [PATCH 1/3] feat(core): add Deadline.elapsed Co-authored-by: Cursor Agent --- clients/deadline.py | 4 ++++ tests/test_deadline.py | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/clients/deadline.py b/clients/deadline.py index 7212104..dac5a08 100644 --- a/clients/deadline.py +++ b/clients/deadline.py @@ -25,6 +25,10 @@ def __init__(self, clock: MonotonicClock, timeout: float): self._timeout = float(timeout) self._started_at = clock.monotonic() + def elapsed(self) -> float: + """Return seconds since start; uncapped, may exceed timeout.""" + return self._clock.monotonic() - self._started_at + def remaining(self) -> float: """Return seconds left before the deadline (never negative).""" elapsed = self._clock.monotonic() - self._started_at diff --git a/tests/test_deadline.py b/tests/test_deadline.py index ec9dde2..572aebf 100644 --- a/tests/test_deadline.py +++ b/tests/test_deadline.py @@ -41,6 +41,16 @@ def test_negative_timeout_rejected(self): with self.assertRaises(ValueError): Deadline(clock, timeout=-1.0) + def test_elapsed_tracks_fake_clock_uncapped(self): + clock = FakeClock() + deadline = Deadline(clock, timeout=0.5) + self.assertAlmostEqual(deadline.elapsed(), 0.0) + clock.sleep(0.4) + self.assertAlmostEqual(deadline.elapsed(), 0.4) + clock.sleep(0.4) + self.assertAlmostEqual(deadline.elapsed(), 0.8) + self.assertGreater(deadline.elapsed(), 0.5) + if __name__ == "__main__": unittest.main() From 2587bdc9fe465f16dd22f5d8163235da19fba0e2 Mon Sep 17 00:00:00 2001 From: leiwilson <305842734+leiwilson@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:57:10 +0100 Subject: [PATCH 2/3] feat(cli): add count command Co-authored-by: Cursor Agent --- scenarios/cli.py | 6 ++++++ tests/test_cli.py | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/scenarios/cli.py b/scenarios/cli.py index 1925c56..05f1e69 100644 --- a/scenarios/cli.py +++ b/scenarios/cli.py @@ -61,6 +61,8 @@ def build_parser() -> argparse.ArgumentParser: subparsers.add_parser("version", help="Print package version") subparsers.add_parser("ids", help="Print known scenario ids") + + subparsers.add_parser("count", help="Print number of known scenarios") return parser @@ -157,6 +159,10 @@ def main(argv: list[str] | None = None) -> int: print(scenario_id) return 0 + if args.command == "count": + print(len(known_scenario_ids())) + return 0 + return 1 diff --git a/tests/test_cli.py b/tests/test_cli.py index d3381ec..9639284 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -199,7 +199,12 @@ def test_ids_prints_one_id_per_line(self): lines = [line for line in buffer.getvalue().splitlines() if line.strip()] self.assertEqual(lines, list(known_scenario_ids())) - + def test_count_prints_known_scenario_count(self): + buffer = io.StringIO() + with redirect_stdout(buffer): + code = main(["count"]) + self.assertEqual(code, 0) + self.assertEqual(buffer.getvalue().strip(), str(len(known_scenario_ids()))) if __name__ == "__main__": From c58e3a56c080216ad2545d9b8417d89b5665118d Mon Sep 17 00:00:00 2001 From: leiwilson <305842734+leiwilson@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:59:06 +0100 Subject: [PATCH 3/3] feat(cli): support json format for ids Co-authored-by: Cursor Agent --- scenarios/cli.py | 16 +++++++++++++--- tests/test_cli.py | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/scenarios/cli.py b/scenarios/cli.py index 05f1e69..97b2093 100644 --- a/scenarios/cli.py +++ b/scenarios/cli.py @@ -60,7 +60,13 @@ def build_parser() -> argparse.ArgumentParser: subparsers.add_parser("version", help="Print package version") - subparsers.add_parser("ids", help="Print known scenario ids") + ids_parser = subparsers.add_parser("ids", help="Print known scenario ids") + ids_parser.add_argument( + "--format", + choices=("text", "json"), + default="text", + help="Output format (default: text)", + ) subparsers.add_parser("count", help="Print number of known scenarios") return parser @@ -155,8 +161,12 @@ def main(argv: list[str] | None = None) -> int: return 0 if args.command == "ids": - for scenario_id in known_scenario_ids(): - print(scenario_id) + ids = list(known_scenario_ids()) + if args.format == "json": + print(json.dumps(ids, indent=2)) + else: + for scenario_id in ids: + print(scenario_id) return 0 if args.command == "count": diff --git a/tests/test_cli.py b/tests/test_cli.py index 9639284..b16c784 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -206,6 +206,22 @@ def test_count_prints_known_scenario_count(self): self.assertEqual(code, 0) self.assertEqual(buffer.getvalue().strip(), str(len(known_scenario_ids()))) + def test_ids_json_format(self): + buffer = io.StringIO() + with redirect_stdout(buffer): + code = main(["ids", "--format", "json"]) + self.assertEqual(code, 0) + parsed = json.loads(buffer.getvalue()) + self.assertEqual(parsed, list(known_scenario_ids())) + + def test_ids_text_format_default_unchanged(self): + buffer = io.StringIO() + with redirect_stdout(buffer): + code = main(["ids", "--format", "text"]) + self.assertEqual(code, 0) + lines = [line for line in buffer.getvalue().splitlines() if line.strip()] + self.assertEqual(lines, list(known_scenario_ids())) + if __name__ == "__main__": unittest.main()