casebench
test cases straight onto the board.
git clone https://owenrusk.dev/casebench.git
casebench / tests/test_lock.py -rw-r--r-- · 5213 bytes
1 import os
2 import unittest
3 from datetime import UTC, datetime, timedelta
4 from unittest import mock
5
6 from casebench import lock
7 from casebench.memstore import MemStore
8
9 from .helpers import UMBRELLA, run
10
11 T0 = datetime(2025, 7, 17, 14, 0, tzinfo=UTC)
12
13
14 class AttemptTest(unittest.TestCase):
15 def setUp(self):
16 self.lock = lock.make(["Tuesday", "114"], lock.Rest(3, 60))
17
18 def test_opens_on_both_lines(self):
19 result, after = lock.attempt(self.lock, ["tuesday", " 114 "], T0)
20 self.assertEqual(result, "open")
21 self.assertEqual(after.opened_at, T0)
22
23 def test_one_line_right_is_still_wrong(self):
24 result, after = lock.attempt(self.lock, ["tuesday", "115"], T0)
25 self.assertEqual(result, "wrong")
26 self.assertEqual(after.wrong, 1)
27
28 def test_wrong_number_of_lines(self):
29 result, _ = lock.attempt(self.lock, ["tuesday"], T0)
30 self.assertEqual(result, "wrong")
31
32 def test_rests_after_three_wrong(self):
33 state = self.lock
34 for _ in range(3):
35 result, state = lock.attempt(state, ["no", "no"], T0)
36 self.assertEqual(result, "wrong")
37 self.assertEqual(state.resting_until, T0 + timedelta(minutes=60))
38
39 def test_takes_nothing_while_resting(self):
40 state = self.lock
41 for _ in range(3):
42 _, state = lock.attempt(state, ["no", "no"], T0)
43 result, held = lock.attempt(state, ["tuesday", "114"], T0 + timedelta(minutes=59))
44 self.assertEqual(result, "resting")
45 self.assertIs(held, state)
46 result, _ = lock.attempt(state, ["tuesday", "114"], T0 + timedelta(minutes=60))
47 self.assertEqual(result, "open")
48
49 def test_count_starts_over_after_a_rest(self):
50 state = self.lock
51 for _ in range(3):
52 _, state = lock.attempt(state, ["no", "no"], T0)
53 result, state = lock.attempt(state, ["no", "no"], T0 + timedelta(minutes=61))
54 self.assertEqual(result, "wrong")
55 self.assertEqual(state.wrong, 1)
56
57 def test_right_answer_clears_the_count(self):
58 _, state = lock.attempt(self.lock, ["no", "no"], T0)
59 _, state = lock.attempt(state, ["tuesday", "114"], T0)
60 self.assertEqual(state.wrong, 0)
61
62 def test_answers_are_not_stored(self):
63 self.assertNotIn("tuesday", repr(self.lock).casefold())
64
65 def test_alternates(self):
66 either = lock.make(["grey|gray", "glove|mitten"], lock.Rest(3, 60))
67 self.assertEqual(len(either.hashes), 4)
68 for typed in (["Gray", "MITTEN"], ["grey", "glove"]):
69 self.assertEqual(lock.attempt(either, typed, T0)[0], "open")
70 self.assertEqual(lock.attempt(either, ["grey", "hat"], T0)[0], "wrong")
71
72 def test_too_many_combinations(self):
73 with self.assertRaisesRegex(ValueError, "combinations"):
74 lock.make(["a|b|c|d"] * 4, lock.Rest(3, 60))
75
76 def test_empty_line(self):
77 with self.assertRaises(ValueError):
78 lock.make(["a", " | "], lock.Rest(3, 60))
79
80 def test_parse_rest(self):
81 self.assertEqual(lock.parse_rest("3/60"), lock.Rest(3, 60))
82 for bad in ("3", "0/60", "a/b", "3/-1"):
83 with self.assertRaises(ValueError):
84 lock.parse_rest(bad)
85
86
87 class LockCommandTest(unittest.TestCase):
88 def setUp(self):
89 self.store = MemStore(("owen",))
90 self.owen = self.store.player("owen")
91 run(self.store, "put", str(UMBRELLA), "--player", "owen")
92
93 def test_lock_and_unlock(self):
94 code, _, _ = run(
95 self.store, "lock", "test-lost-umbrella", "--answer", "tuesday", "--rest", "5/10", "--player", "owen"
96 )
97 self.assertEqual(code, 0)
98 held = self.store.get_lock(self.owen, "test-lost-umbrella")
99 self.assertEqual((held.text, held.lines, held.rest), ("locked.", 1, lock.Rest(5, 10)))
100 code, out, _ = run(self.store, "unlock", "test-lost-umbrella", "--player", "owen")
101 self.assertEqual((code, out), (0, "owen: unlocked test-lost-umbrella\n"))
102 self.assertIsNone(self.store.get_lock(self.owen, "test-lost-umbrella"))
103
104 def test_unlock_when_not_locked(self):
105 code, _, err = run(self.store, "unlock", "test-lost-umbrella", "--player", "owen")
106 self.assertEqual(code, 1)
107 self.assertIn("wasn't locked anywhere", err)
108
109 def test_locks_a_runtime_case(self):
110 self.store.runtime_case(self.owen, "case-x", "A real case")
111 code, _, _ = run(self.store, "lock", "case-x", "--answer", "a", "--player", "owen")
112 self.assertEqual(code, 0)
113 self.assertIsNotNone(self.store.get_lock(self.owen, "case-x"))
114
115 def test_rest_from_the_environment(self):
116 with mock.patch.dict(os.environ, {"CASEBENCH_REST": "2/15"}):
117 run(self.store, "lock", "test-lost-umbrella", "--answer", "a", "--player", "owen")
118 self.assertEqual(self.store.get_lock(self.owen, "test-lost-umbrella").rest, lock.Rest(2, 15))
119
120 def test_bad_rest(self):
121 code, _, err = run(self.store, "lock", "test-lost-umbrella", "--answer", "a", "--rest", "three", "--player", "owen")
122 self.assertEqual(code, 1)
123 self.assertIn("tries/minutes", err)
124
125
126 if __name__ == "__main__":
127 unittest.main()