|
| 1 | +"""Test for caching enable/disable regression issue. |
| 2 | +
|
| 3 | +This test ensures that decorators defined when caching is disabled can still be |
| 4 | +enabled later via enable_caching(). |
| 5 | +
|
| 6 | +""" |
| 7 | + |
| 8 | +import datetime |
| 9 | + |
| 10 | +import cachier |
| 11 | + |
| 12 | + |
| 13 | +def test_enable_caching_after_decorator_definition(): |
| 14 | + """Test that enable_caching() affects decorators when caching disabled.""" |
| 15 | + # Start with caching disabled |
| 16 | + cachier.set_global_params(caching_enabled=False) |
| 17 | + |
| 18 | + call_count = 0 |
| 19 | + |
| 20 | + # Use memory backend to avoid file cache persistence issues |
| 21 | + @cachier.cachier(backend="memory") |
| 22 | + def test_func(param): |
| 23 | + nonlocal call_count |
| 24 | + call_count += 1 |
| 25 | + return f"result_{param}" |
| 26 | + |
| 27 | + # Initially caching is disabled, so function should be called each time |
| 28 | + result1 = test_func("a") |
| 29 | + assert result1 == "result_a" |
| 30 | + assert call_count == 1 |
| 31 | + |
| 32 | + result2 = test_func("a") # Same args, but caching disabled |
| 33 | + assert result2 == "result_a" |
| 34 | + assert call_count == 2 # Function called again |
| 35 | + |
| 36 | + # Now enable caching |
| 37 | + cachier.enable_caching() |
| 38 | + |
| 39 | + # Function should now be cached |
| 40 | + result3 = test_func("a") # Same args, caching now enabled |
| 41 | + assert result3 == "result_a" |
| 42 | + assert call_count == 3 # Called once more to populate cache |
| 43 | + |
| 44 | + result4 = test_func("a") # Same args, should come from cache |
| 45 | + assert result4 == "result_a" |
| 46 | + assert call_count == 3 # Not called again - came from cache! |
| 47 | + |
| 48 | + # Different args should still call the function |
| 49 | + result5 = test_func("b") |
| 50 | + assert result5 == "result_b" |
| 51 | + assert call_count == 4 |
| 52 | + |
| 53 | + # Same new args should be cached |
| 54 | + result6 = test_func("b") |
| 55 | + assert result6 == "result_b" |
| 56 | + assert call_count == 4 # Not called again - came from cache! |
| 57 | + |
| 58 | + |
| 59 | +def test_disable_caching_after_decorator_definition(): |
| 60 | + """Test that disable_caching() affects decorators when caching enabled.""" |
| 61 | + # Start with caching enabled |
| 62 | + cachier.enable_caching() |
| 63 | + |
| 64 | + call_count = 0 |
| 65 | + |
| 66 | + # Use memory backend to avoid file cache persistence issues |
| 67 | + @cachier.cachier(backend="memory") |
| 68 | + def test_func(param): |
| 69 | + nonlocal call_count |
| 70 | + call_count += 1 |
| 71 | + return f"result_{param}" |
| 72 | + |
| 73 | + # Caching is enabled, so function should be cached |
| 74 | + result1 = test_func("a") |
| 75 | + assert result1 == "result_a" |
| 76 | + assert call_count == 1 |
| 77 | + |
| 78 | + result2 = test_func("a") # Same args, should come from cache |
| 79 | + assert result2 == "result_a" |
| 80 | + assert call_count == 1 # Not called again |
| 81 | + |
| 82 | + # Now disable caching |
| 83 | + cachier.disable_caching() |
| 84 | + |
| 85 | + # Function should no longer use cache |
| 86 | + result3 = test_func("a") # Same args, but caching now disabled |
| 87 | + assert result3 == "result_a" |
| 88 | + assert call_count == 2 # Function called again |
| 89 | + |
| 90 | + result4 = test_func("a") # Same args, caching still disabled |
| 91 | + assert result4 == "result_a" |
| 92 | + assert call_count == 3 # Called again |
| 93 | + |
| 94 | + |
| 95 | +def test_original_issue_scenario(): |
| 96 | + """Test the exact scenario from the GitHub issue.""" |
| 97 | + # Set up the same initial state as the issue |
| 98 | + cachier.set_global_params(caching_enabled=False, separate_files=True) |
| 99 | + |
| 100 | + class Test: |
| 101 | + def __init__(self, cache_ttl=None): |
| 102 | + self.counter = 0 |
| 103 | + if cache_ttl is not None: |
| 104 | + stale_after = datetime.timedelta(seconds=cache_ttl) |
| 105 | + cachier.set_global_params(stale_after=stale_after) |
| 106 | + cachier.enable_caching() |
| 107 | + |
| 108 | + # Use memory backend to avoid file cache persistence issues |
| 109 | + @cachier.cachier(backend="memory") |
| 110 | + def test(self, param): |
| 111 | + self.counter += 1 |
| 112 | + return param |
| 113 | + |
| 114 | + # This should work without assertion error |
| 115 | + t = Test(cache_ttl=1) |
| 116 | + result1 = t.test("a") |
| 117 | + assert result1 == "a" |
| 118 | + assert t.counter == 1 |
| 119 | + |
| 120 | + result2 = t.test("a") # Should come from cache |
| 121 | + assert result2 == "a" |
| 122 | + assert t.counter == 1 # Counter should not increment |
| 123 | + |
| 124 | + |
| 125 | +if __name__ == "__main__": |
| 126 | + test_enable_caching_after_decorator_definition() |
| 127 | + test_disable_caching_after_decorator_definition() |
| 128 | + test_original_issue_scenario() |
| 129 | + print("All tests passed!") |
0 commit comments