Pytest Caught 11 Hidden Bugs in an AI Long-Term Memory Module That Manual Testing Missed
AI features that rely on persistent user memory—health info, preferences, schedules—carry real safety risk when consistency bugs slip through. A fast, deterministic test harness turns a module nobody wants to touch into one the team confidently extends, and the same pytest patterns apply to any stateful in-memory logic with LRU and TTL semantics.
An AI assistant's long-term memory module misread a three-month-old allergy as a current symptom, triggering a dangerous medical recommendation. The root cause was a boundary bug in deduplication logic that three rounds of manual testing had missed. Manual regression took 30 minutes and covered only 12 main-path cases; developers avoided re-running it after refactoring, so bugs accumulated.
A pytest suite with 47 parameterized tests now exercises the full CRUD surface, LRU eviction, TTL expiration, and edge cases like duplicate timestamps and exact-expiry-boundary behavior. The suite runs in 5.2 seconds and uses pure in-memory instances with no mocking, isolating logical consistency from IO noise.
Key pitfalls included fixture scope leaking state between tests when set to `session`, mutable default arguments in parameterized lists corrupting subsequent test runs, and time-sleep flakiness in CI pipelines. The fix for CI instability was a looser TTL window rather than introducing a time-freezing library.
Manual testing's real failure mode is not slowness but avoidance: after refactoring, developers skip re-running tedious manual suites, so bugs quietly accumulate.
The LRU eviction bug—failing to update access position on `get`—is a classic example of a semantic error invisible to happy-path tests but catastrophic in production when recently accessed data gets evicted.
Pytest's fixture-per-function default is the correct safety boundary for stateful tests; optimizing for speed with session-scoped fixtures creates non-deterministic cross-test contamination that wastes more time than it saves.
Time-dependent tests without a virtual clock are inherently flaky in CI; the pragmatic tradeoff of a wider tolerance window is often cheaper than pulling in a library like `freezegun` for a small suite.