def test_get_usage_uses_refund(self): timestamp = time.time() self.get_project_quota.return_value = (200, 60) self.get_organization_quota.return_value = (300, 60) n = 10 for _ in range(n): self.quota.is_rate_limited(self.project, timestamp=timestamp) self.quota.refund(self.project, timestamp=timestamp) quotas = self.quota.get_quotas(self.project) all_quotas = quotas + [ QuotaConfig( id="unlimited", limit=None, window=60, reason_code="unlimited"), QuotaConfig(id="dummy", limit=10, window=60, reason_code="dummy"), ] usage = self.quota.get_usage(self.project.organization_id, all_quotas, timestamp=timestamp) # Only quotas with an ID are counted in Redis (via this ID). Assume the # count for these quotas and None for the others. # The ``- 1`` is because we refunded once. assert usage == [n - 1 if q.id else None for q in quotas] + [0, 0]
def test_refund(self, mock_get_quotas): timestamp = time.time() mock_get_quotas.return_value = ( QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=1, limit=None, window=1, reason_code="project_quota", ), QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=2, limit=1, window=1, reason_code="project_quota", ), ) self.quota.refund(self.project, timestamp=timestamp) client = self.quota.cluster.get_local_client_for_key( six.text_type(self.project.organization.pk) ) keys = client.keys("r:quota:p:?:*") assert len(keys) == 2 for key in keys: assert client.get(key) == "1"
def test_get_usage_uses_refund(self): timestamp = time.time() self.get_project_quota.return_value = (200, 60) self.get_organization_quota.return_value = (300, 60) n = 10 for _ in xrange(n): self.quota.is_rate_limited(self.project, timestamp=timestamp) self.quota.refund(self.project, timestamp=timestamp) quotas = self.quota.get_quotas(self.project) assert self.quota.get_usage( self.project.organization_id, quotas + [ QuotaConfig(id="unlimited", limit=None, window=60, reason_code="unlimited"), QuotaConfig( id="dummy", limit=10, window=60, reason_code="dummy"), ], timestamp=timestamp, # the - 1 is because we refunded once ) == [n - 1 for _ in quotas] + [0, 0]
def get_quotas(self, project, key=None, keys=None): if key: key.project = project results = [] if not features.has("organizations:releases-v2", project.organization): results.append( QuotaConfig( limit=0, scope=QuotaScope.ORGANIZATION, categories=[DataCategory.SESSION], reason_code="sessions_unavailable", )) pquota = self.get_project_quota(project) if pquota[0] is not None: results.append( QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=project.id, limit=pquota[0], window=pquota[1], reason_code="project_quota", )) oquota = self.get_organization_quota(project.organization) if oquota[0] is not None: results.append( QuotaConfig( id="o", scope=QuotaScope.ORGANIZATION, scope_id=project.organization.id, limit=oquota[0], window=oquota[1], reason_code="org_quota", )) if key and not keys: keys = [key] elif not keys: keys = [] for key in keys: kquota = self.get_key_quota(key) if kquota[0] is not None: results.append( QuotaConfig( id="k", scope=QuotaScope.KEY, scope_id=key.id, limit=kquota[0], window=kquota[1], reason_code="key_quota", )) return results
def get_quotas(self, project, key=None, keys=None): if key: key.project = project results = [] pquota = self.get_project_quota(project) if pquota[0] is not None: results.append( QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=project.id, categories=DataCategory.error_categories(), limit=pquota[0], window=pquota[1], reason_code="project_quota", ) ) oquota = self.get_organization_quota(project.organization) if oquota[0] is not None: results.append( QuotaConfig( id="o", scope=QuotaScope.ORGANIZATION, scope_id=project.organization.id, categories=DataCategory.error_categories(), limit=oquota[0], window=oquota[1], reason_code="org_quota", ) ) if key and not keys: keys = [key] elif not keys: keys = [] for key in keys: kquota = self.get_key_quota(key) if kquota[0] is not None: results.append( QuotaConfig( id="k", scope=QuotaScope.KEY, scope_id=key.id, categories=DataCategory.error_categories(), limit=kquota[0], window=kquota[1], reason_code="key_quota", ) ) return results
def test_refund_categories(self, mock_get_quotas): timestamp = time.time() mock_get_quotas.return_value = ( QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=1, limit=None, window=1, reason_code="project_quota", categories=[DataCategory.ERROR], ), QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=2, limit=1, window=1, reason_code="project_quota", categories=[DataCategory.ERROR], ), # Should be ignored QuotaConfig( id="a", scope=QuotaScope.PROJECT, scope_id=1, limit=1**6, window=1, reason_code="attachment_quota", categories=[DataCategory.ATTACHMENT], ), ) self.quota.refund(self.project, timestamp=timestamp, category=DataCategory.ATTACHMENT, quantity=100) client = self.quota.cluster.get_local_client_for_key( str(self.project.organization.pk)) error_keys = client.keys("r:quota:p:?:*") assert len(error_keys) == 0 attachment_keys = client.keys("r:quota:a:*") assert len(attachment_keys) == 1 for key in attachment_keys: assert client.get(key) == b"100"
def test_limited_with_unlimited_quota(self, mock_is_rate_limited, mock_get_quotas): mock_get_quotas.return_value = ( QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=1, limit=None, window=1, reason_code="project_quota", ), QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=2, limit=1, window=1, reason_code="project_quota", ), ) assert self.quota.is_rate_limited(self.project).is_limited
def test_get_organization_quota_with_no_account_limit_and_relative_system_limit_single_org( self, ): org = self.create_organization() with self.settings( SENTRY_DEFAULT_MAX_EVENTS_PER_MINUTE="50%", SENTRY_SINGLE_ORGANIZATION=True ), self.options({"system.rate-limit": 10}): assert self.backend.get_organization_quota(org) == (10, 60) @pytest.mark.parametrize( "obj,json", [ ( QuotaConfig(id="o", limit=4711, window=42, reason_code="not_so_fast"), {"prefix": "o", "limit": 4711, "window": 42, "reasonCode": "not_so_fast"}, ), ( QuotaConfig( id="p", scope=QuotaScope.PROJECT, scope_id=1, limit=None, window=1, reason_code="go_away", ), {"prefix": "p", "subscope": "1", "window": 1, "reasonCode": "go_away"}, ), (QuotaConfig(limit=0, reason_code="go_away"), {"limit": 0, "reasonCode": "go_away"}), (