diff --git a/server/lib/python/cartodb_services/test/mock_plpy.py b/server/lib/python/cartodb_services/test/mock_plpy.py index 5298a98..0fe067f 100644 --- a/server/lib/python/cartodb_services/test/mock_plpy.py +++ b/server/lib/python/cartodb_services/test/mock_plpy.py @@ -16,7 +16,7 @@ class MockPlPy: def __init__(self): self._reset() - def _reset(self): + def _reset(self, log_executed_queries=False): self.infos = [] self.notices = [] self.debugs = [] @@ -28,11 +28,30 @@ class MockPlPy: self.results = [] self.prepares = [] self.results = {} + self._log_executed_queries = log_executed_queries + self._logged_queries = [] def _define_result(self, query, result): pattern = re.compile(query, re.IGNORECASE | re.MULTILINE) self.results[pattern] = result + def _executed_queries(self): + if self._log_executed_queries: + return self._logged_queries + else: + raise Exception('Executed queries logging is not active') + + def _has_executed_query(self, query): + pattern = re.compile(re.escape(query)) + for executed_query in self._executed_queries(): + if pattern.search(executed_query): + return True + return False + + def _start_logging_executed_queries(self): + self._logged_queries = [] + self._log_executed_queries = True + def notice(self, msg): self.notices.append(msg) @@ -47,7 +66,15 @@ class MockPlPy: return MockCursor(data) def execute(self, query, rows=1): + if self._log_executed_queries: + self._logged_queries.append(query) for pattern, result in self.results.iteritems(): if pattern.search(query): return result return [] + + def quote_nullable(self, value): + if value is None: + return 'NULL' + else: + return "'{0}'".format(value) diff --git a/server/lib/python/cartodb_services/test/test_ratelimitsconfig.py b/server/lib/python/cartodb_services/test/test_ratelimitsconfig.py new file mode 100644 index 0000000..976b0c9 --- /dev/null +++ b/server/lib/python/cartodb_services/test/test_ratelimitsconfig.py @@ -0,0 +1,124 @@ +from test_helper import * +from unittest import TestCase +from mock import Mock, MagicMock, patch +from nose.tools import assert_raises, assert_not_equal, assert_equal + +from datetime import datetime, date +from mockredis import MockRedis +import cartodb_services + +from cartodb_services.tools import ServiceManager, LegacyServiceManager + +from cartodb_services.metrics import GeocoderConfig +from cartodb_services.refactor.service.mapzen_geocoder_config import MapzenGeocoderConfigBuilder +from cartodb_services.refactor.backend.redis_metrics_connection import RedisConnectionBuilder +from cartodb_services.tools import RateLimitExceeded + +from cartodb_services.refactor.storage.redis_config import * +from cartodb_services.refactor.storage.mem_config import InMemoryConfigStorage +from cartodb_services.refactor.backend.server_config import ServerConfigBackendFactory +from cartodb_services.config import RateLimitsConfig, RateLimitsConfigBuilder, RateLimitsConfigSetter + +class TestRateLimitsConfig(TestCase): + + def setUp(self): + plpy_mock_config() + cartodb_services.init(plpy_mock, _GD={}) + self.username = 'test_user' + self.orgname = 'test_org' + self.redis_conn = MockRedis() + build_redis_user_config(self.redis_conn, self.username, 'geocoding') + build_redis_org_config(self.redis_conn, self.orgname, 'geocoding', provider='mapzen') + self.environment = 'production' + plpy_mock._define_result("CDB_Conf_GetConf\('server_conf'\)", [{'conf': '{"environment": "production"}'}]) + plpy_mock._define_result("CDB_Conf_GetConf\('redis_metadata_config'\)", [{'conf': '{"redis_host":"localhost","redis_port":"6379"}'}]) + plpy_mock._define_result("CDB_Conf_GetConf\('redis_metrics_config'\)", [{'conf': '{"redis_host":"localhost","redis_port":"6379"}'}]) + basic_server_conf = {"server_conf": {"environment": "testing"}, + "mapzen_conf": + {"geocoder": + {"api_key": "search-xxxxxxx", "monthly_quota": 1500000, "service":{"base_url":"http://base"}} + }, "logger_conf": {}} + self.empty_server_config = InMemoryConfigStorage(basic_server_conf) + self.empty_redis_config = InMemoryConfigStorage({}) + self.user_config = RedisUserConfigStorageBuilder(self.redis_conn, self.username).get() + self.org_config = RedisOrgConfigStorageBuilder(self.redis_conn, self.orgname).get() + self.server_config = ServerConfigBackendFactory().get() + + def test_server_config(self): + with patch.object(RedisConnectionBuilder,'get') as get_fn: + get_fn.return_value = self.redis_conn + + # Write server level configuration + config = RateLimitsConfig(service='geocoder', username=self.username, limit=1234, period=86400) + config_setter = RateLimitsConfigSetter(service='geocoder', username=self.username, orgname=self.orgname) + plpy_mock._start_logging_executed_queries() + config_setter.set_server_rate_limits(config) + assert plpy_mock._has_executed_query('cdb_conf_setconf(\'rate_limits\', \'{"geocoder": {"limit": 1234, "period": 86400}}\')') + + # Re-read configuration + plpy_mock._define_result("CDB_Conf_GetConf\('rate_limits'\)", [{'conf': '{"geocoder": {"limit": 1234, "period": 86400}}'}]) + read_config = RateLimitsConfigBuilder( + server_conf=self.server_config, + user_conf=self.empty_redis_config, + org_conf=self.empty_redis_config, + service='geocoder', + user=self.username, + org=self.orgname + ).get() + plpy_mock._define_result("CDB_Conf_GetConf\('rate_limits'\)", []) + assert_equal(read_config, config) + + def test_server_org_config(self): + with patch.object(RedisConnectionBuilder,'get') as get_fn: + get_fn.return_value = self.redis_conn + + server_config = RateLimitsConfig(service='geocoder', username=self.username, limit=1234, period=86400) + org_config = RateLimitsConfig(service='geocoder', username=self.username, limit=1235, period=86400) + config_setter = RateLimitsConfigSetter(service='geocoder', username=self.username, orgname=self.orgname) + + # Write server level configuration + config_setter.set_server_rate_limits(server_config) + # Override with org level configuration + config_setter.set_org_rate_limits(org_config) + + # Re-read configuration + plpy_mock._define_result("CDB_Conf_GetConf\('rate_limits'\)", [{'conf': '{"geocoder": {"limit": 1234, "period": 86400}}'}]) + read_config = RateLimitsConfigBuilder( + server_conf=self.server_config, + user_conf=self.empty_redis_config, + org_conf=self.org_config, + service='geocoder', + user=self.username, + org=self.orgname + ).get() + plpy_mock._define_result("CDB_Conf_GetConf\('rate_limits'\)", []) + assert_equal(read_config, org_config) + + def test_server_org_user_config(self): + with patch.object(RedisConnectionBuilder,'get') as get_fn: + get_fn.return_value = self.redis_conn + + server_config = RateLimitsConfig(service='geocoder', username=self.username, limit=1234, period=86400) + org_config = RateLimitsConfig(service='geocoder', username=self.username, limit=1235, period=86400) + user_config = RateLimitsConfig(service='geocoder', username=self.username, limit=1236, period=86400) + config_setter = RateLimitsConfigSetter(service='geocoder', username=self.username, orgname=self.orgname) + + # Write server level configuration + config_setter.set_server_rate_limits(server_config) + # Override with org level configuration + config_setter.set_org_rate_limits(org_config) + # Override with user level configuration + config_setter.set_user_rate_limits(user_config) + + # Re-read configuration + plpy_mock._define_result("CDB_Conf_GetConf\('rate_limits'\)", [{'conf': '{"geocoder": {"limit": 1234, "period": 86400}}'}]) + read_config = RateLimitsConfigBuilder( + server_conf=self.server_config, + user_conf=self.user_config, + org_conf=self.org_config, + service='geocoder', + user=self.username, + org=self.orgname + ).get() + plpy_mock._define_result("CDB_Conf_GetConf\('rate_limits'\)", []) + assert_equal(read_config, user_config)