owenrusk.dev

casebench

test cases straight onto the board.

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

commit 0bbf945299c6283c0b5d2187f3b63d4eab8a62dd
parent 578e4ea04fdffc20cf4401bad7aa09da682ac011
author Owen Rusk <owen@papermothgames.com>
date   2025-07-17 13:18:52 -0500
tests: lock, rest and unlock
tests/test_lock.py+105-0
diff --git a/tests/test_lock.py b/tests/test_lock.py
new file mode 100644
index 0000000..f316d13
--- /dev/null
+++ b/tests/test_lock.py
@@ -0,0 +1,105 @@
+import unittest
+from datetime import UTC, datetime, timedelta
+
+from casebench import lock
+from casebench.memstore import MemStore
+
+from .helpers import UMBRELLA, run
+
+T0 = datetime(2025, 7, 17, 14, 0, tzinfo=UTC)
+
+
+class AttemptTest(unittest.TestCase):
+    def setUp(self):
+        self.lock = lock.make(["Tuesday", "114"], lock.Rest(3, 60))
+
+    def test_opens_on_both_lines(self):
+        result, after = lock.attempt(self.lock, ["tuesday", " 114 "], T0)
+        self.assertEqual(result, "open")
+        self.assertEqual(after.opened_at, T0)
+
+    def test_one_line_right_is_still_wrong(self):
+        result, after = lock.attempt(self.lock, ["tuesday", "115"], T0)
+        self.assertEqual(result, "wrong")
+        self.assertEqual(after.wrong, 1)
+
+    def test_wrong_number_of_lines(self):
+        result, _ = lock.attempt(self.lock, ["tuesday"], T0)
+        self.assertEqual(result, "wrong")
+
+    def test_rests_after_three_wrong(self):
+        state = self.lock
+        for _ in range(3):
+            result, state = lock.attempt(state, ["no", "no"], T0)
+        self.assertEqual(result, "wrong")
+        self.assertEqual(state.resting_until, T0 + timedelta(minutes=60))
+
+    def test_takes_nothing_while_resting(self):
+        state = self.lock
+        for _ in range(3):
+            _, state = lock.attempt(state, ["no", "no"], T0)
+        result, held = lock.attempt(state, ["tuesday", "114"], T0 + timedelta(minutes=59))
+        self.assertEqual(result, "resting")
+        self.assertIs(held, state)
+        result, _ = lock.attempt(state, ["tuesday", "114"], T0 + timedelta(minutes=60))
+        self.assertEqual(result, "open")
+
+    def test_count_starts_over_after_a_rest(self):
+        state = self.lock
+        for _ in range(3):
+            _, state = lock.attempt(state, ["no", "no"], T0)
+        result, state = lock.attempt(state, ["no", "no"], T0 + timedelta(minutes=61))
+        self.assertEqual(result, "wrong")
+        self.assertEqual(state.wrong, 1)
+
+    def test_right_answer_clears_the_count(self):
+        _, state = lock.attempt(self.lock, ["no", "no"], T0)
+        _, state = lock.attempt(state, ["tuesday", "114"], T0)
+        self.assertEqual(state.wrong, 0)
+
+    def test_answers_are_not_stored(self):
+        self.assertNotIn("tuesday", repr(self.lock).casefold())
+
+    def test_parse_rest(self):
+        self.assertEqual(lock.parse_rest("3/60"), lock.Rest(3, 60))
+        for bad in ("3", "0/60", "a/b", "3/-1"):
+            with self.assertRaises(ValueError):
+                lock.parse_rest(bad)
+
+
+class LockCommandTest(unittest.TestCase):
+    def setUp(self):
+        self.store = MemStore(("owen",))
+        self.owen = self.store.player("owen")
+        run(self.store, "put", str(UMBRELLA), "--player", "owen")
+
+    def test_lock_and_unlock(self):
+        code, _, _ = run(
+            self.store, "lock", "test-lost-umbrella", "--answer", "tuesday", "--rest", "5/10", "--player", "owen"
+        )
+        self.assertEqual(code, 0)
+        held = self.store.get_lock(self.owen, "test-lost-umbrella")
+        self.assertEqual((held.text, held.lines, held.rest), ("locked.", 1, lock.Rest(5, 10)))
+        code, out, _ = run(self.store, "unlock", "test-lost-umbrella", "--player", "owen")
+        self.assertEqual((code, out), (0, "owen: unlocked test-lost-umbrella\n"))
+        self.assertIsNone(self.store.get_lock(self.owen, "test-lost-umbrella"))
+
+    def test_unlock_when_not_locked(self):
+        code, _, err = run(self.store, "unlock", "test-lost-umbrella", "--player", "owen")
+        self.assertEqual(code, 1)
+        self.assertIn("isn't locked", err)
+
+    def test_locks_a_runtime_case(self):
+        self.store.runtime_case(self.owen, "case-x", "A real case")
+        code, _, _ = run(self.store, "lock", "case-x", "--answer", "a", "--player", "owen")
+        self.assertEqual(code, 0)
+        self.assertIsNotNone(self.store.get_lock(self.owen, "case-x"))
+
+    def test_bad_rest(self):
+        code, _, err = run(self.store, "lock", "test-lost-umbrella", "--answer", "a", "--rest", "three", "--player", "owen")
+        self.assertEqual(code, 1)
+        self.assertIn("tries/minutes", err)
+
+
+if __name__ == "__main__":
+    unittest.main()