refactor: clean up code to py39+ supported syntax - f-strings instead of format - latest typing support instead of pre 3.9 TitleCase - remove UTF-8 declarations. - many more changes Powered by https://github.com/asottile/pyupgrade/ + manual cleanups
94 lines
2.7 KiB
Python
94 lines
2.7 KiB
Python
import time
|
|
import unittest
|
|
from unittest.mock import MagicMock
|
|
|
|
import frappe
|
|
from frappe.tests.test_api import FrappeAPITestCase
|
|
from frappe.utils.caching import request_cache, site_cache
|
|
|
|
CACHE_TTL = 4
|
|
external_service = MagicMock(return_value=30)
|
|
register_with_external_service = MagicMock(return_value=True)
|
|
|
|
|
|
@request_cache
|
|
def request_specific_api(a: list | tuple | dict | int, b: int) -> int:
|
|
# API that takes very long to return a result
|
|
todays_value = external_service()
|
|
if not isinstance(a, (int, float)):
|
|
a = 1
|
|
return a**b * todays_value
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True)
|
|
@site_cache
|
|
def ping() -> str:
|
|
register_with_external_service(frappe.local.site)
|
|
return frappe.local.site
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True)
|
|
@site_cache(ttl=CACHE_TTL)
|
|
def ping_with_ttl() -> str:
|
|
register_with_external_service(frappe.local.site)
|
|
return frappe.local.site
|
|
|
|
|
|
class TestCachingUtils(unittest.TestCase):
|
|
def test_request_cache(self):
|
|
retval = []
|
|
acceptable_args = [
|
|
[1, 2, 3, 4],
|
|
range(10),
|
|
{"abc": "test-key"},
|
|
frappe.get_last_doc("DocType"),
|
|
frappe._dict(),
|
|
]
|
|
same_output_received = lambda: all([x for x in set(retval) if x == retval[0]])
|
|
|
|
# ensure that external service was called only once
|
|
# thereby return value of request_specific_api is cached
|
|
for _ in range(5):
|
|
retval.append(request_specific_api(120, 23))
|
|
external_service.assert_called_once()
|
|
self.assertTrue(same_output_received())
|
|
|
|
# ensure that cache differentiates between int & float
|
|
# types. Giving different return values for both
|
|
retval.append(request_specific_api(120.0, 23))
|
|
self.assertTrue(external_service.call_count, 2)
|
|
|
|
# ensure that function is executed when call isn't
|
|
# already cached
|
|
retval.clear()
|
|
for _ in range(10):
|
|
request_specific_api(120, 13)
|
|
self.assertTrue(external_service.call_count, 3)
|
|
self.assertTrue(same_output_received())
|
|
|
|
# ensure key generation capacity for different types
|
|
retval.clear()
|
|
for arg in acceptable_args:
|
|
external_service.call_count = 0
|
|
for _ in range(2):
|
|
request_specific_api(arg, 13)
|
|
self.assertTrue(external_service.call_count, 1)
|
|
self.assertTrue(same_output_received())
|
|
|
|
|
|
class TestSiteCache(FrappeAPITestCase):
|
|
def test_site_cache(self):
|
|
module = __name__
|
|
api_with_ttl = f"{module}.ping_with_ttl"
|
|
api_without_ttl = f"{module}.ping"
|
|
|
|
start = time.monotonic()
|
|
for _ in range(5):
|
|
self.get(f"/api/method/{api_with_ttl}")
|
|
self.get(f"/api/method/{api_without_ttl}")
|
|
end = time.monotonic()
|
|
|
|
self.assertEqual(register_with_external_service.call_count, 2)
|
|
time.sleep(CACHE_TTL - (end - start))
|
|
self.get(f"/api/method/{api_with_ttl}")
|
|
self.assertEqual(register_with_external_service.call_count, 3)
|