owenrusk.dev

casebench

test cases straight onto the board.

git clone https://owenrusk.dev/casebench.git

commit b44d61677233222bf05fc0f12d066d262ca5089b
parent 1f13f6d4cdf9b8149847efa986de18cb69babb20
author Owen Rusk <owen@papermothgames.com>
date   2026-01-21 11:24:48 -0600
check every board before writing to any

with --all, a refusal halfway down left half the boards done.
casebench/cli.py+13-6
tests/test_commands.py+6-0
diff --git a/casebench/cli.py b/casebench/cli.py
index a9ea075..6b897cb 100644
--- a/casebench/cli.py
+++ b/casebench/cli.py
@@ -107,21 +107,25 @@ def on_board(store: Store, who: Player, case_id: str) -> Placed:
 
 def run_put(store: Store, args: argparse.Namespace) -> None:
     case = casefile.load(args.folder)
-    for who in targets(store, args):
+    players = targets(store, args)
+    for who in players:
         placed = store.placed(who, case.id)
         if placed is not None and placed.source != BENCH:
             raise Fail(f"{who.handle} already has {case.id} from the runtime")
+    for who in players:
         store.put(who, case)
         print(f"{who.handle}: put {case.id}")
 
 
 def run_note(store: Store, args: argparse.Namespace) -> None:
     text = args.file.read_text(encoding="utf-8") if args.file else args.text
-    where = args.case + (f" / {args.item}" if args.item else "")
-    for who in targets(store, args):
+    players = targets(store, args)
+    for who in players:
         on_board(store, who, args.case)
         if args.item and args.item not in store.items(who, args.case):
             raise Fail(f"{who.handle}: {args.case} has no item {args.item}")
+    where = args.case + (f" / {args.item}" if args.item else "")
+    for who in players:
         store.note(who, args.case, args.item, text)
         print(f"{who.handle}: noted {where}")
 
@@ -129,9 +133,10 @@ def run_note(store: Store, args: argparse.Namespace) -> None:
 def run_lock(store: Store, args: argparse.Namespace) -> None:
     rest = lock.parse_rest(args.rest)
     players = targets(store, args)
-    new = lock.make(args.answer or ask(), rest, args.text)
     for who in players:
         on_board(store, who, args.case)
+    new = lock.make(args.answer or ask(), rest, args.text)
+    for who in players:
         store.lock(who, args.case, new)
         print(f"{who.handle}: locked {args.case}")
 
@@ -159,8 +164,10 @@ def run_list(store: Store, args: argparse.Namespace) -> None:
 
 
 def run_pull(store: Store, args: argparse.Namespace) -> None:
-    for who in targets(store, args):
+    players = targets(store, args)
+    for who in players:
         if on_board(store, who, args.case).source != BENCH:
-            raise Fail(f"{args.case} came from the runtime; the bench only pulls what it put there")
+            raise Fail(f"{who.handle}: {args.case} came from the runtime; the bench only pulls what it put there")
+    for who in players:
         store.pull(who, args.case)
         print(f"{who.handle}: pulled {args.case}")
diff --git a/tests/test_commands.py b/tests/test_commands.py
index b77609b..bfcd71f 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -39,6 +39,12 @@ class PutTest(unittest.TestCase):
         with self.assertRaises(SystemExit):
             run(self.store, "put", str(UMBRELLA))
 
+    def test_one_refusal_writes_nothing(self):
+        self.store.runtime_case(self.store.player("tess"), "test-lost-umbrella", "Something real")
+        code, _, _ = run(self.store, "put", str(UMBRELLA), "--all")
+        self.assertEqual(code, 1)
+        self.assertIsNone(self.store.placed(self.owen, "test-lost-umbrella"))
+
     def test_no_such_player(self):
         code, _, err = run(self.store, "put", str(UMBRELLA), "--player", "nobody")
         self.assertEqual(code, 1)