""" کشِ لیدربورد بی «ازدحامِ لحظه‌ی انقضا» (progress/leaderboard.py، cached_scores): وقتی کش منقضی می‌شود فقط یک درخواست جدول را از نو حساب می‌کند و بقیه آخرین نسخه را می‌گیرند. هم‌زمانیِ واقعی بینِ workerها آزموده نشده؛ این تست‌ها قفل را با گذاشتنِ دستیِ کلیدش شبیه‌سازی می‌کنند. """ from unittest import mock from django.core.cache import cache from django.test import SimpleTestCase from progress import leaderboard class CachedScoresTests(SimpleTestCase): def setUp(self): cache.clear() def test_first_call_computes_and_fills_both_copies(self): with mock.patch.object(leaderboard, 'compute_scores', return_value=[(1, 50)]) as compute: self.assertEqual(leaderboard.cached_scores('weekly'), [(1, 50)]) self.assertEqual(leaderboard.cached_scores('weekly'), [(1, 50)]) compute.assert_called_once_with('weekly') self.assertEqual(cache.get('leaderboard:weekly:stale'), [(1, 50)]) self.assertIsNone(cache.get('leaderboard:weekly:lock')) def test_while_someone_else_recomputes_the_stale_copy_is_served(self): cache.set('leaderboard:weekly:stale', [(2, 30)], 600) cache.set('leaderboard:weekly:lock', 1, 30) with mock.patch.object(leaderboard, 'compute_scores') as compute: self.assertEqual(leaderboard.cached_scores('weekly'), [(2, 30)]) compute.assert_not_called() def test_without_any_copy_it_computes_even_if_locked(self): cache.set('leaderboard:weekly:lock', 1, 30) with mock.patch.object(leaderboard, 'compute_scores', return_value=[(3, 10)]) as compute: self.assertEqual(leaderboard.cached_scores('weekly'), [(3, 10)]) compute.assert_called_once() def test_a_failed_recompute_releases_the_lock(self): with mock.patch.object(leaderboard, 'compute_scores', side_effect=RuntimeError('db')): with self.assertRaises(RuntimeError): leaderboard.cached_scores('weekly') self.assertIsNone(cache.get('leaderboard:weekly:lock'))