# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE

import time

from werkzeug.wrappers import Response

import xhiveframework
import xhiveframework.rate_limiter
from xhiveframework.rate_limiter import RateLimiter
from xhiveframework.tests.utils import XhiveFrameworkTestCase
from xhiveframework.utils import cint


class TestRateLimiter(XhiveFrameworkTestCase):
	def test_apply_with_limit(self):
		xhiveframework.conf.rate_limit = {"window": 86400, "limit": 1}
		xhiveframework.rate_limiter.apply()

		self.assertTrue(hasattr(xhiveframework.local, "rate_limiter"))
		self.assertIsInstance(xhiveframework.local.rate_limiter, RateLimiter)

		xhiveframework.cache.delete(xhiveframework.local.rate_limiter.key)
		delattr(xhiveframework.local, "rate_limiter")

	def test_apply_without_limit(self):
		xhiveframework.conf.rate_limit = None
		xhiveframework.rate_limiter.apply()

		self.assertFalse(hasattr(xhiveframework.local, "rate_limiter"))

	def test_respond_over_limit(self):
		limiter = RateLimiter(0.01, 86400)
		time.sleep(0.01)
		limiter.update()

		xhiveframework.conf.rate_limit = {"window": 86400, "limit": 0.01}
		self.assertRaises(xhiveframework.TooManyRequestsError, xhiveframework.rate_limiter.apply)
		xhiveframework.rate_limiter.update()

		response = xhiveframework.rate_limiter.respond()

		self.assertIsInstance(response, Response)
		self.assertEqual(response.status_code, 429)

		headers = xhiveframework.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)

		xhiveframework.cache.delete(limiter.key)
		xhiveframework.cache.delete(xhiveframework.local.rate_limiter.key)
		delattr(xhiveframework.local, "rate_limiter")

	def test_respond_under_limit(self):
		xhiveframework.conf.rate_limit = {"window": 86400, "limit": 0.01}
		xhiveframework.rate_limiter.apply()
		xhiveframework.rate_limiter.update()
		response = xhiveframework.rate_limiter.respond()
		self.assertEqual(response, None)

		xhiveframework.cache.delete(xhiveframework.local.rate_limiter.key)
		delattr(xhiveframework.local, "rate_limiter")

	def test_headers_under_limit(self):
		xhiveframework.conf.rate_limit = {"window": 86400, "limit": 0.01}
		xhiveframework.rate_limiter.apply()
		xhiveframework.rate_limiter.update()
		headers = xhiveframework.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"]), xhiveframework.local.rate_limiter.duration)
		self.assertEqual(int(headers["X-RateLimit-Limit"]), 10000)
		self.assertEqual(int(headers["X-RateLimit-Remaining"]), 10000)

		xhiveframework.cache.delete(xhiveframework.local.rate_limiter.key)
		delattr(xhiveframework.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(xhiveframework.TooManyRequestsError, limiter.apply)

		xhiveframework.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)

		xhiveframework.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(xhiveframework.cache.get(limiter.key)))

		xhiveframework.cache.delete(limiter.key)
