seitime-frappe/frappe/tests/test_rate_limiter.py
Gavin D'souza 3446026555 chore: Update header: license.txt => LICENSE
The license.txt file has been replaced with LICENSE for quite a while
now. INAL but it didn't seem accurate to say "hey, checkout license.txt
although there's no such file". Apart from this, there were
inconsistencies in the headers altogether...this change brings
consistency.
2021-09-03 12:02:59 +05:30

116 lines
3.5 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
import unittest
from werkzeug.wrappers import Response
import time
import frappe
import frappe.rate_limiter
from frappe.rate_limiter import RateLimiter
from frappe.utils import cint
class TestRateLimiter(unittest.TestCase):
def setUp(self):
pass
def test_apply_with_limit(self):
frappe.conf.rate_limit = {"window": 86400, "limit": 1}
frappe.rate_limiter.apply()
self.assertTrue(hasattr(frappe.local, "rate_limiter"))
self.assertIsInstance(frappe.local.rate_limiter, RateLimiter)
frappe.cache().delete(frappe.local.rate_limiter.key)
delattr(frappe.local, "rate_limiter")
def test_apply_without_limit(self):
frappe.conf.rate_limit = None
frappe.rate_limiter.apply()
self.assertFalse(hasattr(frappe.local, "rate_limiter"))
def test_respond_over_limit(self):
limiter = RateLimiter(0.01, 86400)
time.sleep(0.01)
limiter.update()
frappe.conf.rate_limit = {"window": 86400, "limit": 0.01}
self.assertRaises(frappe.TooManyRequestsError, frappe.rate_limiter.apply)
frappe.rate_limiter.update()
response = frappe.rate_limiter.respond()
self.assertIsInstance(response, Response)
self.assertEqual(response.status_code, 429)
headers = frappe.local.rate_limiter.headers()
self.assertIn("Retry-After", headers)
self.assertNotIn("X-RateLimit-Used", headers)
self.assertIn("X-RateLimit-Reset", headers)
self.assertIn("X-RateLimit-Limit", headers)
self.assertIn("X-RateLimit-Remaining", headers)
self.assertTrue(int(headers["X-RateLimit-Reset"]) <= 86400)
self.assertEqual(int(headers["X-RateLimit-Limit"]), 10000)
self.assertEqual(int(headers["X-RateLimit-Remaining"]), 0)
frappe.cache().delete(limiter.key)
frappe.cache().delete(frappe.local.rate_limiter.key)
delattr(frappe.local, "rate_limiter")
def test_respond_under_limit(self):
frappe.conf.rate_limit = {"window": 86400, "limit": 0.01}
frappe.rate_limiter.apply()
frappe.rate_limiter.update()
response = frappe.rate_limiter.respond()
self.assertEqual(response, None)
frappe.cache().delete(frappe.local.rate_limiter.key)
delattr(frappe.local, "rate_limiter")
def test_headers_under_limit(self):
frappe.conf.rate_limit = {"window": 86400, "limit": 0.01}
frappe.rate_limiter.apply()
frappe.rate_limiter.update()
headers = frappe.local.rate_limiter.headers()
self.assertNotIn("Retry-After", headers)
self.assertIn("X-RateLimit-Reset", headers)
self.assertTrue(int(headers["X-RateLimit-Reset"] < 86400))
self.assertEqual(int(headers["X-RateLimit-Used"]), frappe.local.rate_limiter.duration)
self.assertEqual(int(headers["X-RateLimit-Limit"]), 10000)
self.assertEqual(int(headers["X-RateLimit-Remaining"]), 10000)
frappe.cache().delete(frappe.local.rate_limiter.key)
delattr(frappe.local, "rate_limiter")
def test_reject_over_limit(self):
limiter = RateLimiter(0.01, 86400)
time.sleep(0.01)
limiter.update()
limiter = RateLimiter(0.01, 86400)
self.assertRaises(frappe.TooManyRequestsError, limiter.apply)
frappe.cache().delete(limiter.key)
def test_do_not_reject_under_limit(self):
limiter = RateLimiter(0.01, 86400)
time.sleep(0.01)
limiter.update()
limiter = RateLimiter(0.02, 86400)
self.assertEqual(limiter.apply(), None)
frappe.cache().delete(limiter.key)
def test_update_method(self):
limiter = RateLimiter(0.01, 86400)
time.sleep(0.01)
limiter.update()
self.assertEqual(limiter.duration, cint(frappe.cache().get(limiter.key)))
frappe.cache().delete(limiter.key)