test_unit.py
1610 lines
| 62.8 KiB
| text/x-python
|
PythonLexer
r0 | # -*- coding: utf-8 -*- | |||
r112 | # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors | |||
r0 | # | |||
r112 | # Licensed under the Apache License, Version 2.0 (the "License"); | |||
# you may not use this file except in compliance with the License. | ||||
# You may obtain a copy of the License at | ||||
r0 | # | |||
r112 | # http://www.apache.org/licenses/LICENSE-2.0 | |||
r0 | # | |||
r112 | # Unless required by applicable law or agreed to in writing, software | |||
# distributed under the License is distributed on an "AS IS" BASIS, | ||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
# See the License for the specific language governing permissions and | ||||
# limitations under the License. | ||||
r0 | ||||
import copy | ||||
import logging | ||||
import mock | ||||
import pyramid | ||||
import pytest | ||||
import sqlalchemy as sa | ||||
import webob | ||||
from datetime import datetime | ||||
from pyramid import testing | ||||
from appenlight.models import DBSession | ||||
from appenlight.lib.ext_json import json | ||||
log = logging.getLogger(__name__) | ||||
class DummyContext(object): | ||||
pass | ||||
r153 | @pytest.mark.usefixtures("base_app") | |||
r0 | class BasicTest(object): | |||
pass | ||||
r153 | @pytest.mark.usefixtures("base_app") | |||
r0 | class TestMigration(object): | |||
def test_migration(self): | ||||
assert 1 == 1 | ||||
class TestSentryProto_7(object): | ||||
def test_log_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.lib.enums import ParsedSentryEventType | ||||
from appenlight.lib.utils.sentry import parse_sentry_event | ||||
r153 | ||||
r0 | event_dict, event_type = parse_sentry_event( | |||
r153 | payload_examples.SENTRY_LOG_PAYLOAD_7 | |||
) | ||||
r0 | assert ParsedSentryEventType.LOG == event_type | |||
r153 | assert event_dict["log_level"] == "CRITICAL" | |||
assert event_dict["message"] == "TEST from django logging" | ||||
assert event_dict["namespace"] == "testlogger" | ||||
assert event_dict["request_id"] == "9a6172f2e6d2444582f83a6c333d9cfb" | ||||
assert event_dict["server"] == "ergo-virtual-machine" | ||||
assert event_dict["date"] == datetime.utcnow().date().strftime( | ||||
"%Y-%m-%dT%H:%M:%SZ" | ||||
) | ||||
tags = [ | ||||
("site", "example.com"), | ||||
("sys.argv", ["'manage.py'", "'runserver'"]), | ||||
("price", 6), | ||||
("tag", "'extra'"), | ||||
("dupa", True), | ||||
("project", "sentry"), | ||||
("sentry_culprit", "testlogger in index"), | ||||
("sentry_language", "python"), | ||||
("sentry_release", "test"), | ||||
] | ||||
assert sorted(event_dict["tags"]) == sorted(tags) | ||||
r0 | ||||
def test_report_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.lib.enums import ParsedSentryEventType | ||||
from appenlight.lib.utils.sentry import parse_sentry_event | ||||
r153 | ||||
utcnow = datetime.utcnow().date().strftime("%Y-%m-%dT%H:%M:%SZ") | ||||
r0 | event_dict, event_type = parse_sentry_event( | |||
r153 | payload_examples.SENTRY_PYTHON_PAYLOAD_7 | |||
) | ||||
r0 | assert ParsedSentryEventType.ERROR_REPORT == event_type | |||
r153 | assert event_dict["client"] == "sentry" | |||
assert ( | ||||
event_dict["error"] == "Exception: test 500 " | ||||
"\u0142\xf3\u201c\u0107\u201c\u0107\u017c\u0105" | ||||
) | ||||
assert event_dict["language"] == "python" | ||||
assert event_dict["ip"] == "127.0.0.1" | ||||
assert event_dict["request_id"] == "9fae652c8c1c4d6a8eee09260f613a98" | ||||
assert event_dict["server"] == "ergo-virtual-machine" | ||||
assert event_dict["start_time"] == utcnow | ||||
assert event_dict["url"] == "http://127.0.0.1:8000/error" | ||||
assert ( | ||||
event_dict["user_agent"] == "Mozilla/5.0 (X11; Linux x86_64) " | ||||
"AppleWebKit/537.36 (KHTML, " | ||||
"like Gecko) Chrome/47.0.2526.106 " | ||||
"Safari/537.36" | ||||
) | ||||
assert event_dict["view_name"] == "djangoapp.views in error" | ||||
tags = [("site", "example.com"), ("sentry_release", "test")] | ||||
assert sorted(event_dict["tags"]) == sorted(tags) | ||||
extra = [("sys.argv", ["'manage.py'", "'runserver'"]), ("project", "sentry")] | ||||
assert sorted(event_dict["extra"]) == sorted(extra) | ||||
request = event_dict["request"] | ||||
assert request["url"] == "http://127.0.0.1:8000/error" | ||||
assert request["cookies"] == {"appenlight": "X"} | ||||
assert request["data"] is None | ||||
assert request["method"] == "GET" | ||||
assert request["query_string"] == "" | ||||
assert request["env"] == { | ||||
"REMOTE_ADDR": "127.0.0.1", | ||||
"SERVER_NAME": "localhost", | ||||
"SERVER_PORT": "8000", | ||||
} | ||||
assert request["headers"] == { | ||||
"Accept": "text/html,application/xhtml+xml," | ||||
"application/xml;q=0.9,image/webp,*/*;q=0.8", | ||||
"Accept-Encoding": "gzip, deflate, sdch", | ||||
"Accept-Language": "en-US,en;q=0.8,pl;q=0.6", | ||||
"Connection": "keep-alive", | ||||
"Content-Length": "", | ||||
"Content-Type": "text/plain", | ||||
"Cookie": "appenlight=X", | ||||
"Dnt": "1", | ||||
"Host": "127.0.0.1:8000", | ||||
"Upgrade-Insecure-Requests": "1", | ||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) " | ||||
"AppleWebKit/537.36 (KHTML, like Gecko) " | ||||
"Chrome/47.0.2526.106 Safari/537.36", | ||||
} | ||||
traceback = event_dict["traceback"] | ||||
assert ( | ||||
traceback[0]["cline"] == "response = wrapped_callback(request, " | ||||
"*callback_args, **callback_kwargs)" | ||||
) | ||||
assert traceback[0]["file"] == "django/core/handlers/base.py" | ||||
assert traceback[0]["fn"] == "get_response" | ||||
assert traceback[0]["line"] == 111 | ||||
assert traceback[0]["module"] == "django.core.handlers.base" | ||||
assert ( | ||||
traceback[1]["cline"] == "raise Exception(u'test 500 " | ||||
"\u0142\xf3\u201c\u0107\u201c\u0107" | ||||
"\u017c\u0105')" | ||||
) | ||||
assert traceback[1]["file"] == "djangoapp/views.py" | ||||
assert traceback[1]["fn"] == "error" | ||||
assert traceback[1]["line"] == 84 | ||||
assert traceback[1]["module"] == "djangoapp.views" | ||||
assert sorted(traceback[1]["vars"]) == sorted( | ||||
[ | ||||
("c", "<sqlite3.Cursor object at 0x7fe7c82af8f0>"), | ||||
("request", "<WSGIRequest at 0x140633490316304>"), | ||||
("conn", "<sqlite3.Connection object at 0x7fe7c8b23bf8>"), | ||||
] | ||||
) | ||||
r0 | ||||
class TestAPIReports_0_5_Validation(object): | ||||
r153 | @pytest.mark.parametrize("dummy_json", ["", {}, [], None]) | |||
r0 | def test_no_payload(self, dummy_json): | |||
import colander | ||||
from appenlight.validators import ReportListSchema_0_5 | ||||
r153 | ||||
r0 | utcnow = datetime.utcnow() | |||
schema = ReportListSchema_0_5().bind(utcnow=utcnow) | ||||
with pytest.raises(colander.Invalid): | ||||
schema.deserialize(dummy_json) | ||||
def test_minimal_payload(self): | ||||
dummy_json = [{}] | ||||
import colander | ||||
from appenlight.validators import ReportListSchema_0_5 | ||||
r153 | ||||
r0 | utcnow = datetime.utcnow() | |||
schema = ReportListSchema_0_5().bind(utcnow=utcnow) | ||||
with pytest.raises(colander.Invalid): | ||||
schema.deserialize(dummy_json) | ||||
def test_minimal_payload(self): | ||||
r153 | dummy_json = [{"report_details": [{}]}] | |||
r0 | from appenlight.validators import ReportListSchema_0_5 | |||
r153 | ||||
r0 | utcnow = datetime.utcnow() | |||
schema = ReportListSchema_0_5().bind(utcnow=utcnow) | ||||
deserialized = schema.deserialize(dummy_json) | ||||
expected_deserialization = [ | ||||
r153 | { | |||
"language": "unknown", | ||||
"server": "unknown", | ||||
"occurences": 1, | ||||
"priority": 5, | ||||
"view_name": "", | ||||
"client": "unknown", | ||||
"http_status": 200, | ||||
"error": "", | ||||
"tags": None, | ||||
"username": "", | ||||
"traceback": None, | ||||
"extra": None, | ||||
"url": "", | ||||
"ip": None, | ||||
"start_time": utcnow, | ||||
"group_string": None, | ||||
"request": {}, | ||||
"request_stats": None, | ||||
"end_time": None, | ||||
"request_id": "", | ||||
"message": "", | ||||
"slow_calls": [], | ||||
"user_agent": "", | ||||
} | ||||
r0 | ] | |||
assert deserialized == expected_deserialization | ||||
def test_full_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.validators import ReportListSchema_0_5 | ||||
r153 | ||||
r0 | PYTHON_PAYLOAD = copy.deepcopy(payload_examples.PYTHON_PAYLOAD_0_5) | |||
utcnow = datetime.utcnow() | ||||
schema = ReportListSchema_0_5().bind(utcnow=utcnow) | ||||
r153 | PYTHON_PAYLOAD["tags"] = [ | |||
("foo", 1), | ||||
("action", "test"), | ||||
("baz", 1.1), | ||||
("date", utcnow.strftime("%Y-%m-%dT%H:%M:%S.0")), | ||||
] | ||||
r0 | dummy_json = [PYTHON_PAYLOAD] | |||
deserialized = schema.deserialize(dummy_json)[0] | ||||
r153 | assert deserialized["error"] == PYTHON_PAYLOAD["error"] | |||
assert deserialized["language"] == PYTHON_PAYLOAD["language"] | ||||
assert deserialized["server"] == PYTHON_PAYLOAD["server"] | ||||
assert deserialized["priority"] == PYTHON_PAYLOAD["priority"] | ||||
assert deserialized["view_name"] == PYTHON_PAYLOAD["view_name"] | ||||
assert deserialized["client"] == PYTHON_PAYLOAD["client"] | ||||
assert deserialized["http_status"] == PYTHON_PAYLOAD["http_status"] | ||||
assert deserialized["error"] == PYTHON_PAYLOAD["error"] | ||||
assert deserialized["occurences"] == PYTHON_PAYLOAD["occurences"] | ||||
assert deserialized["username"] == PYTHON_PAYLOAD["username"] | ||||
assert deserialized["traceback"] == PYTHON_PAYLOAD["traceback"] | ||||
assert deserialized["url"] == PYTHON_PAYLOAD["url"] | ||||
assert deserialized["ip"] == PYTHON_PAYLOAD["ip"] | ||||
assert ( | ||||
deserialized["start_time"].strftime("%Y-%m-%dT%H:%M:%S.0") | ||||
== PYTHON_PAYLOAD["start_time"] | ||||
) | ||||
assert deserialized["ip"] == PYTHON_PAYLOAD["ip"] | ||||
assert deserialized["group_string"] is None | ||||
assert deserialized["request_stats"] == PYTHON_PAYLOAD["request_stats"] | ||||
assert ( | ||||
deserialized["end_time"].strftime("%Y-%m-%dT%H:%M:%S.0") | ||||
== PYTHON_PAYLOAD["end_time"] | ||||
) | ||||
assert deserialized["request_id"] == PYTHON_PAYLOAD["request_id"] | ||||
assert deserialized["message"] == PYTHON_PAYLOAD["message"] | ||||
assert deserialized["user_agent"] == PYTHON_PAYLOAD["user_agent"] | ||||
assert ( | ||||
deserialized["slow_calls"][0]["start"].strftime("%Y-%m-%dT%H:%M:%S.0") | ||||
== PYTHON_PAYLOAD["slow_calls"][0]["start"] | ||||
) | ||||
assert ( | ||||
deserialized["slow_calls"][0]["end"].strftime("%Y-%m-%dT%H:%M:%S.0") | ||||
== PYTHON_PAYLOAD["slow_calls"][0]["end"] | ||||
) | ||||
assert ( | ||||
deserialized["slow_calls"][0]["statement"] | ||||
== PYTHON_PAYLOAD["slow_calls"][0]["statement"] | ||||
) | ||||
assert ( | ||||
deserialized["slow_calls"][0]["parameters"] | ||||
== PYTHON_PAYLOAD["slow_calls"][0]["parameters"] | ||||
) | ||||
assert ( | ||||
deserialized["slow_calls"][0]["type"] | ||||
== PYTHON_PAYLOAD["slow_calls"][0]["type"] | ||||
) | ||||
assert ( | ||||
deserialized["slow_calls"][0]["subtype"] | ||||
== PYTHON_PAYLOAD["slow_calls"][0]["subtype"] | ||||
) | ||||
assert deserialized["slow_calls"][0]["location"] == "" | ||||
assert deserialized["tags"] == [ | ||||
("foo", 1), | ||||
("action", "test"), | ||||
("baz", 1.1), | ||||
("date", utcnow.strftime("%Y-%m-%dT%H:%M:%S.0")), | ||||
] | ||||
@pytest.mark.usefixtures("log_schema") | ||||
r0 | class TestAPILogsValidation(object): | |||
r153 | @pytest.mark.parametrize("dummy_json", ["", {}, [], None]) | |||
r0 | def test_no_payload(self, dummy_json, log_schema): | |||
import colander | ||||
with pytest.raises(colander.Invalid): | ||||
log_schema.deserialize(dummy_json) | ||||
def test_minimal_payload(self, log_schema): | ||||
dummy_json = [{}] | ||||
deserialized = log_schema.deserialize(dummy_json)[0] | ||||
r153 | expected = { | |||
"log_level": "UNKNOWN", | ||||
"namespace": "", | ||||
"server": "unknown", | ||||
"request_id": "", | ||||
"primary_key": None, | ||||
"date": datetime.utcnow(), | ||||
"message": "", | ||||
"tags": None, | ||||
} | ||||
assert deserialized["log_level"] == expected["log_level"] | ||||
assert deserialized["message"] == expected["message"] | ||||
assert deserialized["namespace"] == expected["namespace"] | ||||
assert deserialized["request_id"] == expected["request_id"] | ||||
assert deserialized["server"] == expected["server"] | ||||
assert deserialized["tags"] == expected["tags"] | ||||
assert deserialized["primary_key"] == expected["primary_key"] | ||||
r0 | ||||
def test_normal_payload(self, log_schema): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r153 | ||||
r0 | deserialized = log_schema.deserialize(payload_examples.LOG_EXAMPLES)[0] | |||
expected = payload_examples.LOG_EXAMPLES[0] | ||||
r153 | assert deserialized["log_level"] == expected["log_level"] | |||
assert deserialized["message"] == expected["message"] | ||||
assert deserialized["namespace"] == expected["namespace"] | ||||
assert deserialized["request_id"] == expected["request_id"] | ||||
assert deserialized["server"] == expected["server"] | ||||
assert deserialized["date"].strftime("%Y-%m-%dT%H:%M:%S.%f") == expected["date"] | ||||
assert deserialized["tags"][0][0] == "tag_name" | ||||
assert deserialized["tags"][0][1] == "tag_value" | ||||
assert deserialized["tags"][1][0] == "tag_name2" | ||||
assert deserialized["tags"][1][1] == 2 | ||||
r0 | ||||
def test_normal_payload_date_without_microseconds(self, log_schema): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r153 | ||||
r0 | LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES) | |||
r153 | LOG_EXAMPLE[0]["date"] = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S") | |||
r0 | deserialized = log_schema.deserialize(LOG_EXAMPLE) | |||
r153 | assert ( | |||
deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M:%S") | ||||
== LOG_EXAMPLE[0]["date"] | ||||
) | ||||
r0 | ||||
def test_normal_payload_date_without_seconds(self, log_schema): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r153 | ||||
r0 | LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES) | |||
r153 | LOG_EXAMPLE[0]["date"] = datetime.utcnow().date().strftime("%Y-%m-%dT%H:%M") | |||
r0 | deserialized = log_schema.deserialize(LOG_EXAMPLE) | |||
r153 | assert ( | |||
deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M") == LOG_EXAMPLE[0]["date"] | ||||
) | ||||
r0 | ||||
def test_payload_empty_date(self, log_schema): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r153 | ||||
r0 | LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES) | |||
r153 | LOG_EXAMPLE[0]["date"] = None | |||
r0 | deserialized = log_schema.deserialize(LOG_EXAMPLE) | |||
r153 | assert deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M") is not None | |||
r0 | ||||
def test_payload_no_date(self, log_schema): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r153 | ||||
r0 | LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES) | |||
r153 | LOG_EXAMPLE[0].pop("date", None) | |||
r0 | deserialized = log_schema.deserialize(LOG_EXAMPLE) | |||
r153 | assert deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M") is not None | |||
r0 | ||||
r153 | @pytest.mark.usefixtures("general_metrics_schema") | |||
r0 | class TestAPIGeneralMetricsValidation(object): | |||
r153 | @pytest.mark.parametrize("dummy_json", ["", {}, [], None]) | |||
r0 | def test_no_payload(self, dummy_json, general_metrics_schema): | |||
import colander | ||||
with pytest.raises(colander.Invalid): | ||||
general_metrics_schema.deserialize(dummy_json) | ||||
def test_minimal_payload(self, general_metrics_schema): | ||||
r153 | dummy_json = [{"tags": [["counter_a", 15.5], ["counter_b", 63]]}] | |||
r0 | deserialized = general_metrics_schema.deserialize(dummy_json)[0] | |||
r153 | expected = { | |||
"namespace": "", | ||||
"server_name": "unknown", | ||||
"tags": [("counter_a", 15.5), ("counter_b", 63)], | ||||
"timestamp": datetime.utcnow(), | ||||
} | ||||
assert deserialized["namespace"] == expected["namespace"] | ||||
assert deserialized["server_name"] == expected["server_name"] | ||||
assert deserialized["tags"] == expected["tags"] | ||||
r0 | ||||
def test_normal_payload(self, general_metrics_schema): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r153 | ||||
r0 | dummy_json = [payload_examples.METRICS_PAYLOAD] | |||
deserialized = general_metrics_schema.deserialize(dummy_json)[0] | ||||
r153 | expected = { | |||
"namespace": "some.monitor", | ||||
"server_name": "server.name", | ||||
"tags": [("usage_foo", 15.5), ("usage_bar", 63)], | ||||
"timestamp": datetime.utcnow(), | ||||
} | ||||
assert deserialized["namespace"] == expected["namespace"] | ||||
assert deserialized["server_name"] == expected["server_name"] | ||||
assert deserialized["tags"] == expected["tags"] | ||||
r0 | ||||
r153 | @pytest.mark.usefixtures("request_metrics_schema") | |||
r0 | class TestAPIRequestMetricsValidation(object): | |||
r153 | @pytest.mark.parametrize("dummy_json", ["", {}, [], None]) | |||
r0 | def test_no_payload(self, dummy_json, request_metrics_schema): | |||
import colander | ||||
with pytest.raises(colander.Invalid): | ||||
print(request_metrics_schema.deserialize(dummy_json)) | ||||
def test_normal_payload(self, request_metrics_schema): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r153 | ||||
r0 | dummy_json = payload_examples.REQUEST_METRICS_EXAMPLES | |||
deserialized = request_metrics_schema.deserialize(dummy_json)[0] | ||||
r153 | expected = { | |||
"metrics": [ | ||||
( | ||||
"dir/module:func", | ||||
{ | ||||
"custom": 0.0, | ||||
"custom_calls": 0.0, | ||||
"main": 0.01664, | ||||
"nosql": 0.00061, | ||||
"nosql_calls": 23.0, | ||||
"remote": 0.0, | ||||
"remote_calls": 0.0, | ||||
"requests": 1, | ||||
"sql": 0.00105, | ||||
"sql_calls": 2.0, | ||||
"tmpl": 0.0, | ||||
"tmpl_calls": 0.0, | ||||
}, | ||||
), | ||||
( | ||||
"SomeView.function", | ||||
{ | ||||
"custom": 0.0, | ||||
"custom_calls": 0.0, | ||||
"main": 0.647261, | ||||
"nosql": 0.306554, | ||||
"nosql_calls": 140.0, | ||||
"remote": 0.0, | ||||
"remote_calls": 0.0, | ||||
"requests": 28, | ||||
"sql": 0.0, | ||||
"sql_calls": 0.0, | ||||
"tmpl": 0.0, | ||||
"tmpl_calls": 0.0, | ||||
}, | ||||
), | ||||
], | ||||
"server": "some.server.hostname", | ||||
"timestamp": datetime.utcnow(), | ||||
} | ||||
assert deserialized["server"] == expected["server"] | ||||
metric = deserialized["metrics"][0] | ||||
expected_metric = expected["metrics"][0] | ||||
r0 | assert metric[0] == expected_metric[0] | |||
assert sorted(metric[1].items()) == sorted(expected_metric[1].items()) | ||||
r153 | @pytest.mark.usefixtures("default_application") | |||
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables") | ||||
r0 | class TestAPIReportsView(object): | |||
def test_no_json_payload(self, default_application): | ||||
import colander | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.views.api import reports_create | ||||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
r153 | request = testing.DummyRequest(headers={"Content-Type": "application/json"}) | |||
request.unsafe_json_body = "" | ||||
r0 | request.context = context | |||
route = mock.Mock() | ||||
r153 | route.name = "api_reports" | |||
r0 | request.matched_route = route | |||
with pytest.raises(colander.Invalid): | ||||
response = reports_create(request) | ||||
def test_single_proper_json_0_5_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.views.api import reports_create | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.models.report_group import ReportGroup | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_reports" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.matched_route = route | ||||
PYTHON_PAYLOAD = payload_examples.PYTHON_PAYLOAD_0_5 | ||||
request.unsafe_json_body = [copy.deepcopy(PYTHON_PAYLOAD)] | ||||
reports_create(request) | ||||
query = DBSession.query(ReportGroup) | ||||
report = query.first() | ||||
assert query.count() == 1 | ||||
assert report.total_reports == 1 | ||||
def test_grouping_0_5(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.views.api import reports_create | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.models.report_group import ReportGroup | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_reports" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.matched_route = route | ||||
PYTHON_PAYLOAD = payload_examples.PYTHON_PAYLOAD_0_5 | ||||
r153 | request.unsafe_json_body = [ | |||
copy.deepcopy(PYTHON_PAYLOAD), | ||||
copy.deepcopy(PYTHON_PAYLOAD), | ||||
] | ||||
r0 | reports_create(request) | |||
query = DBSession.query(ReportGroup) | ||||
report = query.first() | ||||
assert query.count() == 1 | ||||
assert report.total_reports == 2 | ||||
def test_grouping_different_reports_0_5(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.views.api import reports_create | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.models.report_group import ReportGroup | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_reports" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.matched_route = route | ||||
PYTHON_PAYLOAD = payload_examples.PYTHON_PAYLOAD_0_5 | ||||
PARSED_REPORT_404 = payload_examples.PARSED_REPORT_404 | ||||
r153 | request.unsafe_json_body = [ | |||
copy.deepcopy(PYTHON_PAYLOAD), | ||||
copy.deepcopy(PARSED_REPORT_404), | ||||
] | ||||
r0 | reports_create(request) | |||
query = DBSession.query(ReportGroup) | ||||
report = query.first() | ||||
assert query.count() == 2 | ||||
assert report.total_reports == 1 | ||||
r153 | @pytest.mark.usefixtures("default_application") | |||
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables") | ||||
r0 | class TestAirbrakeXMLView(object): | |||
def test_normal_payload_parsing(self): | ||||
import datetime | ||||
import defusedxml.ElementTree as ElementTree | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.lib.utils.airbrake import parse_airbrake_xml | ||||
from appenlight.validators import ReportListSchema_0_5 | ||||
context = DummyContext() | ||||
r153 | request = testing.DummyRequest(headers={"Content-Type": "application/xml"}) | |||
r0 | request.context = context | |||
request.context.possibly_public = False | ||||
root = ElementTree.fromstring(payload_examples.AIRBRAKE_RUBY_EXAMPLE) | ||||
request.context.airbrake_xml_etree = root | ||||
error_dict = parse_airbrake_xml(request) | ||||
schema = ReportListSchema_0_5().bind(utcnow=datetime.datetime.utcnow()) | ||||
deserialized_report = schema.deserialize([error_dict])[0] | ||||
r153 | assert deserialized_report["client"] == "Airbrake Notifier" | |||
assert ( | ||||
deserialized_report["error"] | ||||
== "NameError: undefined local variable or method `sdfdfdf' for #<#<Class:0x000000039a8b90>:0x00000002c53df0>" | ||||
) | ||||
assert deserialized_report["http_status"] == 500 | ||||
assert deserialized_report["language"] == "unknown" | ||||
assert deserialized_report["message"] == "" | ||||
assert deserialized_report["occurences"] == 1 | ||||
assert deserialized_report["priority"] == 5 | ||||
d_request = deserialized_report["request"] | ||||
assert d_request["GET"] == {"test": "1234"} | ||||
assert d_request["action_dispatch.request.parameters"] == { | ||||
"action": "index", | ||||
"controller": "welcome", | ||||
"test": "1234", | ||||
} | ||||
assert deserialized_report["request_id"] == "c11b2267f3ad8b00a1768cae35559fa1" | ||||
assert deserialized_report["server"] == "ergo-desktop" | ||||
assert deserialized_report["traceback"][0] == { | ||||
"cline": "block in start_thread", | ||||
"file": "/home/ergo/.rbenv/versions/1.9.3-p327/lib/ruby/1.9.1/webrick/server.rb", | ||||
"fn": "block in start_thread", | ||||
"line": "191", | ||||
"module": "", | ||||
"vars": {}, | ||||
} | ||||
assert deserialized_report["traceback"][-1] == { | ||||
"cline": "_app_views_welcome_index_html_erb___2570061166873166679_31748940", | ||||
"file": "[PROJECT_ROOT]/app/views/welcome/index.html.erb", | ||||
"fn": "_app_views_welcome_index_html_erb___2570061166873166679_31748940", | ||||
"line": "3", | ||||
"module": "", | ||||
"vars": {}, | ||||
} | ||||
assert ( | ||||
deserialized_report["url"] == "http://0.0.0.0:3000/welcome/index?test=1234" | ||||
) | ||||
assert deserialized_report["view_name"] == "welcome:index" | ||||
r0 | ||||
def test_normal_payload_view(self): | ||||
import defusedxml.ElementTree as ElementTree | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.views.api import airbrake_xml_compat | ||||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
r153 | request = testing.DummyRequest(headers={"Content-Type": "application/xml"}) | |||
r0 | request.context = context | |||
request.context.possibly_public = False | ||||
root = ElementTree.fromstring(payload_examples.AIRBRAKE_RUBY_EXAMPLE) | ||||
request.context.airbrake_xml_etree = root | ||||
route = mock.Mock() | ||||
r153 | route.name = "api_airbrake" | |||
r0 | request.matched_route = route | |||
result = airbrake_xml_compat(request) | ||||
r153 | assert "<notice><id>" in result | |||
r0 | ||||
r153 | @pytest.mark.usefixtures("default_application") | |||
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables") | ||||
r0 | class TestAPILogView(object): | |||
def test_no_json_payload(self, base_app): | ||||
import colander | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.views.api import logs_create | ||||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
r153 | request = testing.DummyRequest(headers={"Content-Type": "application/json"}) | |||
r0 | request.context = context | |||
request.registry = base_app.registry | ||||
r153 | request.unsafe_json_body = "" | |||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_logs" | |||
r0 | request.matched_route = route | |||
with pytest.raises(colander.Invalid): | ||||
response = logs_create(request) | ||||
def test_single_json_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.models.log import Log | ||||
from appenlight.views.api import logs_create | ||||
from appenlight.models.services.application import ApplicationService | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_logs" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.matched_route = route | ||||
r153 | request.unsafe_json_body = [copy.deepcopy(payload_examples.LOG_EXAMPLES[0])] | |||
r0 | logs_create(request) | |||
query = DBSession.query(Log) | ||||
log = query.first() | ||||
assert query.count() == 1 | ||||
assert log.message == "OMG ValueError happened" | ||||
def test_multiple_json_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.models.log import Log | ||||
from appenlight.views.api import logs_create | ||||
from appenlight.models.services.application import ApplicationService | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_logs" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.matched_route = route | ||||
LOG_PAYLOAD = payload_examples.LOG_EXAMPLES[0] | ||||
LOG_PAYLOAD2 = payload_examples.LOG_EXAMPLES[1] | ||||
request.unsafe_json_body = copy.deepcopy([LOG_PAYLOAD, LOG_PAYLOAD2]) | ||||
logs_create(request) | ||||
query = DBSession.query(Log).order_by(sa.asc(Log.log_id)) | ||||
assert query.count() == 2 | ||||
assert query[0].message == "OMG ValueError happened" | ||||
assert query[1].message == "OMG ValueError happened2" | ||||
def test_public_key_rewriting(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
from appenlight.models.log import Log | ||||
from appenlight.views.api import logs_create | ||||
from appenlight.models.services.application import ApplicationService | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_logs" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.matched_route = route | ||||
LOG_PAYLOAD = copy.deepcopy(payload_examples.LOG_EXAMPLES[0]) | ||||
LOG_PAYLOAD2 = copy.deepcopy(payload_examples.LOG_EXAMPLES[1]) | ||||
r153 | LOG_PAYLOAD["primary_key"] = "X2" | |||
LOG_PAYLOAD2["primary_key"] = "X2" | ||||
r0 | request.unsafe_json_body = [LOG_PAYLOAD, LOG_PAYLOAD2] | |||
logs_create(request) | ||||
query = DBSession.query(Log).order_by(sa.asc(Log.log_id)) | ||||
assert query.count() == 1 | ||||
assert query[0].message == "OMG ValueError happened2" | ||||
r153 | ||||
@pytest.mark.usefixtures("default_application") | ||||
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables") | ||||
r0 | class TestAPIGeneralMetricsView(object): | |||
def test_no_json_payload(self, base_app): | ||||
import colander | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.views.api import general_metrics_create | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_general_metrics" | |||
r0 | context = DummyContext() | |||
context.resource = ApplicationService.by_id(1) | ||||
r153 | request = testing.DummyRequest(headers={"Content-Type": "application/json"}) | |||
r0 | request.context = context | |||
request.registry = base_app.registry | ||||
r153 | request.unsafe_json_body = "" | |||
r0 | request.matched_route = route | |||
with pytest.raises(colander.Invalid): | ||||
general_metrics_create(request) | ||||
def test_single_json_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r49 | from appenlight.models.metric import Metric | |||
r0 | from appenlight.views.api import general_metrics_create | |||
from appenlight.models.services.application import ApplicationService | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_general_metric" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
request.matched_route = route | ||||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.unsafe_json_body = payload_examples.METRICS_PAYLOAD | ||||
general_metrics_create(request) | ||||
query = DBSession.query(Metric) | ||||
metric = query.first() | ||||
assert query.count() == 1 | ||||
r153 | assert metric.namespace == "some.monitor" | |||
r0 | ||||
def test_multiple_json_payload(self): | ||||
import appenlight.tests.payload_examples as payload_examples | ||||
r49 | from appenlight.models.metric import Metric | |||
r0 | from appenlight.views.api import general_metrics_create | |||
from appenlight.models.services.application import ApplicationService | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_general_metrics" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
request.matched_route = route | ||||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
request.context = context | ||||
request.unsafe_json_body = [ | ||||
copy.deepcopy(payload_examples.METRICS_PAYLOAD), | ||||
copy.deepcopy(payload_examples.METRICS_PAYLOAD), | ||||
] | ||||
general_metrics_create(request) | ||||
query = DBSession.query(Metric) | ||||
metric = query.first() | ||||
assert query.count() == 2 | ||||
r153 | assert metric.namespace == "some.monitor" | |||
r0 | ||||
class TestGroupingMessageReplacements(object): | ||||
def replace_default_repr_python(self): | ||||
r153 | test_str = """ | |||
r0 | ConnectionError: ConnectionError((<urllib3.connection.HTTPConnection object at 0x7f87a0ba9fd0>, 'Connection to domain.gr timed out. (connect timeout=10)')) caused by: ConnectTimeoutError((<urllib3.connection.HTTPConnection object at 0x7f87a0ba9fd0>, 'Connection to domain.gr timed out. (connect timeout=10)')) | |||
r153 | """ | |||
regex = r"<(.*?) object at (.*?)>" | ||||
r0 | ||||
class TestRulesKeyGetter(object): | ||||
def test_default_dict_getter_top_key(self): | ||||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
struct = {"a": {"b": "b", "c": {"d": "d", "g": {"h": "h"}}, "e": "e"}, "f": "f"} | ||||
r0 | result = Rule.default_dict_struct_getter(struct, "a") | |||
r153 | assert result == struct["a"] | |||
r0 | ||||
def test_default_dict_getter_sub_key(self): | ||||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
struct = {"a": {"b": "b", "c": {"d": "d", "g": {"h": "h"}}, "e": "e"}, "f": "f"} | ||||
result = Rule.default_dict_struct_getter(struct, "a:b") | ||||
assert result == struct["a"]["b"] | ||||
result = Rule.default_dict_struct_getter(struct, "a:c:d") | ||||
assert result == struct["a"]["c"]["d"] | ||||
r0 | ||||
def test_default_obj_getter_top_key(self): | ||||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | class TestStruct(object): | |||
def __init__(self, a, b): | ||||
self.a = a | ||||
self.b = b | ||||
r153 | struct = TestStruct(a="a", b=TestStruct(a="x", b="y")) | |||
r0 | result = Rule.default_obj_struct_getter(struct, "a") | |||
assert result == struct.a | ||||
def test_default_obj_getter_sub_key(self): | ||||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | class TestStruct(object): | |||
def __init__(self, name, a, b): | ||||
self.name = name | ||||
self.a = a | ||||
self.b = b | ||||
def __repr__(self): | ||||
r153 | return "<obj {}>".format(self.name) | |||
r0 | ||||
r153 | c = TestStruct("c", a=5, b="z") | |||
b = TestStruct("b", a=c, b="y") | ||||
struct = TestStruct("a", a="a", b=b) | ||||
result = Rule.default_obj_struct_getter(struct, "b:b") | ||||
r0 | assert result == struct.b.b | |||
r153 | result = Rule.default_obj_struct_getter(struct, "b:a:b") | |||
r0 | assert result == struct.b.a.b | |||
r153 | @pytest.mark.usefixtures("report_type_matrix") | |||
class TestRulesParsing: | ||||
@pytest.mark.parametrize( | ||||
"op, struct_value, test_value, match_result", | ||||
[ | ||||
("eq", 500, 500, True), | ||||
("eq", 600, 500, False), | ||||
("eq", 300, 500, False), | ||||
("eq", "300", 500, False), | ||||
("eq", "600", 500, False), | ||||
("eq", "500", 500, True), | ||||
("ne", 500, 500, False), | ||||
("ne", 600, 500, True), | ||||
("ne", 300, 500, True), | ||||
("ne", "300", 500, True), | ||||
("ne", "600", 500, True), | ||||
("ne", "500", 500, False), | ||||
("ge", 500, 500, True), | ||||
("ge", 600, 500, True), | ||||
("ge", 499, 500, False), | ||||
("gt", 499, 500, False), | ||||
("gt", 500, 500, False), | ||||
("gt", 501, 500, True), | ||||
("le", 499, 500, True), | ||||
("le", 500, 500, True), | ||||
("le", 501, 500, False), | ||||
("lt", 499, 500, True), | ||||
("lt", 500, 500, False), | ||||
("lt", 501, 500, False), | ||||
], | ||||
) | ||||
def test_single_op_int( | ||||
self, op, struct_value, test_value, match_result, report_type_matrix | ||||
): | ||||
r0 | from appenlight.lib.rule import Rule | |||
r153 | ||||
rule_config = {"op": op, "field": "http_status", "value": test_value} | ||||
r0 | rule = Rule(rule_config, report_type_matrix) | |||
r153 | data = {"http_status": struct_value} | |||
r0 | assert rule.match(data) is match_result | |||
r153 | @pytest.mark.parametrize( | |||
"op, struct_value, test_value, match_result", | ||||
[ | ||||
("ge", "500.01", 500, True), | ||||
("ge", "500.01", 500.02, False), | ||||
("le", "500.01", 500.02, True), | ||||
], | ||||
) | ||||
def test_single_op_float( | ||||
self, op, struct_value, test_value, match_result, report_type_matrix | ||||
): | ||||
r0 | from appenlight.lib.rule import Rule | |||
r153 | ||||
rule_config = {"op": op, "field": "duration", "value": test_value} | ||||
r0 | rule = Rule(rule_config, report_type_matrix) | |||
r153 | data = {"duration": struct_value} | |||
r0 | assert rule.match(data) is match_result | |||
r153 | @pytest.mark.parametrize( | |||
"op, struct_value, test_value, match_result", | ||||
[ | ||||
("contains", "foo bar baz", "foo", True), | ||||
("contains", "foo bar baz", "bar", True), | ||||
("contains", "foo bar baz", "dupa", False), | ||||
("startswith", "foo bar baz", "foo", True), | ||||
("startswith", "foo bar baz", "bar", False), | ||||
("endswith", "foo bar baz", "baz", True), | ||||
("endswith", "foo bar baz", "bar", False), | ||||
], | ||||
) | ||||
def test_single_op_string( | ||||
self, op, struct_value, test_value, match_result, report_type_matrix | ||||
): | ||||
r0 | from appenlight.lib.rule import Rule | |||
r153 | ||||
rule_config = {"op": op, "field": "error", "value": test_value} | ||||
r0 | rule = Rule(rule_config, report_type_matrix) | |||
r153 | data = {"error": struct_value} | |||
r0 | assert rule.match(data) is match_result | |||
r153 | @pytest.mark.parametrize( | |||
"field, value, s_type", | ||||
[ | ||||
("field_unicode", 500, str), | ||||
("field_unicode", 500.0, str), | ||||
("field_unicode", "500", str), | ||||
("field_int", "500", int), | ||||
("field_int", 500, int), | ||||
("field_int", 500.0, int), | ||||
("field_float", "500", float), | ||||
("field_float", 500, float), | ||||
("field_float", 500.0, float), | ||||
], | ||||
) | ||||
r0 | def test_type_normalization(self, field, value, s_type): | |||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | type_matrix = { | |||
r153 | "field_unicode": {"type": "unicode"}, | |||
"field_float": {"type": "float"}, | ||||
"field_int": {"type": "int"}, | ||||
r0 | } | |||
rule = Rule({}, type_matrix) | ||||
n_value = rule.normalized_type(field, value) | ||||
assert isinstance(n_value, s_type) is True | ||||
r153 | @pytest.mark.usefixtures("report_type_matrix") | |||
class TestNestedRuleParsing: | ||||
@pytest.mark.parametrize( | ||||
"data, result", | ||||
[ | ||||
({"http_status": 501, "group": {"priority": 7, "occurences": 11}}, False), | ||||
({"http_status": 101, "group": {"priority": 7, "occurences": 11}}, False), | ||||
({"http_status": 500, "group": {"priority": 1, "occurences": 11}}, False), | ||||
({"http_status": 101, "group": {"priority": 3, "occurences": 5}}, True), | ||||
], | ||||
) | ||||
r41 | def test_NOT_rule(self, data, result, report_type_matrix): | |||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r41 | rule_config = { | |||
"field": "__NOT__", | ||||
"rules": [ | ||||
r153 | {"op": "ge", "field": "group:occurences", "value": "10"}, | |||
{"op": "ge", "field": "group:priority", "value": "4"}, | ||||
], | ||||
r41 | } | |||
rule = Rule(rule_config, report_type_matrix) | ||||
assert rule.match(data) is result | ||||
r153 | @pytest.mark.parametrize( | |||
"data, result", | ||||
[ | ||||
({"http_status": 501, "group": {"priority": 7, "occurences": 11}}, True), | ||||
({"http_status": 101, "group": {"priority": 7, "occurences": 11}}, True), | ||||
({"http_status": 500, "group": {"priority": 1, "occurences": 1}}, True), | ||||
({"http_status": 101, "group": {"priority": 3, "occurences": 11}}, False), | ||||
], | ||||
) | ||||
r0 | def test_nested_OR_AND_rule(self, data, result, report_type_matrix): | |||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__OR__", | ||||
"rules": [ | ||||
{ | ||||
"field": "__AND__", | ||||
"rules": [ | ||||
r153 | {"op": "ge", "field": "group:occurences", "value": "10"}, | |||
{"op": "ge", "field": "group:priority", "value": "4"}, | ||||
], | ||||
r0 | }, | |||
r153 | {"op": "eq", "field": "http_status", "value": "500"}, | |||
], | ||||
r0 | } | |||
rule = Rule(rule_config, report_type_matrix) | ||||
assert rule.match(data) is result | ||||
r153 | @pytest.mark.parametrize( | |||
"data, result", | ||||
[ | ||||
({"http_status": 501, "group": {"priority": 7, "occurences": 11}}, True), | ||||
({"http_status": 101, "group": {"priority": 7, "occurences": 11}}, True), | ||||
({"http_status": 500, "group": {"priority": 1, "occurences": 1}}, True), | ||||
({"http_status": 101, "group": {"priority": 3, "occurences": 1}}, False), | ||||
], | ||||
) | ||||
r0 | def test_nested_OR_OR_rule(self, data, result, report_type_matrix): | |||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__OR__", | ||||
"rules": [ | ||||
r153 | { | |||
"field": "__OR__", | ||||
"rules": [ | ||||
{"op": "ge", "field": "group:occurences", "value": "10"}, | ||||
{"op": "ge", "field": "group:priority", "value": "4"}, | ||||
], | ||||
}, | ||||
{"op": "eq", "field": "http_status", "value": "500"}, | ||||
], | ||||
r0 | } | |||
rule = Rule(rule_config, report_type_matrix) | ||||
assert rule.match(data) is result | ||||
r153 | @pytest.mark.parametrize( | |||
"data, result", | ||||
[ | ||||
({"http_status": 500, "group": {"priority": 7, "occurences": 11}}, True), | ||||
({"http_status": 101, "group": {"priority": 7, "occurences": 11}}, False), | ||||
({"http_status": 500, "group": {"priority": 1, "occurences": 1}}, False), | ||||
({"http_status": 101, "group": {"priority": 3, "occurences": 1}}, False), | ||||
], | ||||
) | ||||
r0 | def test_nested_AND_AND_rule(self, data, result, report_type_matrix): | |||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
r153 | { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
{"op": "ge", "field": "group:occurences", "value": "10"}, | ||||
{"op": "ge", "field": "group:priority", "value": "4"}, | ||||
], | ||||
}, | ||||
{"op": "eq", "field": "http_status", "value": "500"}, | ||||
], | ||||
r0 | } | |||
rule = Rule(rule_config, report_type_matrix) | ||||
assert rule.match(data) is result | ||||
r153 | @pytest.mark.parametrize( | |||
"data, result", | ||||
[ | ||||
( | ||||
{ | ||||
"http_status": 500, | ||||
"group": {"priority": 7, "occurences": 11}, | ||||
"url_path": "/test/register", | ||||
"error": "foo test bar", | ||||
}, | ||||
True, | ||||
), | ||||
( | ||||
{ | ||||
"http_status": 500, | ||||
"group": {"priority": 7, "occurences": 11}, | ||||
"url_path": "/test/register", | ||||
"error": "foo INVALID bar", | ||||
}, | ||||
False, | ||||
), | ||||
], | ||||
) | ||||
r0 | def test_nested_AND_AND_AND_rule(self, data, result, report_type_matrix): | |||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
r153 | { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
{"op": "ge", "field": "group:occurences", "value": "10"}, | ||||
{ | ||||
"field": "__AND__", | ||||
"rules": [ | ||||
{ | ||||
"op": "endswith", | ||||
"field": "url_path", | ||||
"value": "register", | ||||
}, | ||||
{"op": "contains", "field": "error", "value": "test"}, | ||||
], | ||||
}, | ||||
], | ||||
}, | ||||
{"op": "eq", "field": "http_status", "value": "500"}, | ||||
], | ||||
r0 | } | |||
rule = Rule(rule_config, report_type_matrix) | ||||
assert rule.match(data) is result | ||||
r153 | @pytest.mark.parametrize( | |||
"data, result", | ||||
[ | ||||
( | ||||
{ | ||||
"http_status": 500, | ||||
"group": {"priority": 7, "occurences": 11}, | ||||
"url_path": 6, | ||||
"error": 3, | ||||
}, | ||||
False, | ||||
), | ||||
( | ||||
{ | ||||
"http_status": 500, | ||||
"group": {"priority": 7, "occurences": 11}, | ||||
"url_path": "/test/register", | ||||
"error": "foo INVALID bar", | ||||
}, | ||||
True, | ||||
), | ||||
], | ||||
) | ||||
r0 | def test_nested_AND_AND_OR_rule(self, data, result, report_type_matrix): | |||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
r153 | { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
{"op": "ge", "field": "group:occurences", "value": "10"}, | ||||
{ | ||||
"field": "__OR__", | ||||
"rules": [ | ||||
{ | ||||
"op": "endswith", | ||||
"field": "url_path", | ||||
"value": "register", | ||||
}, | ||||
{"op": "contains", "field": "error", "value": "test"}, | ||||
], | ||||
}, | ||||
], | ||||
}, | ||||
{"op": "eq", "field": "http_status", "value": "500"}, | ||||
], | ||||
r0 | } | |||
rule = Rule(rule_config, report_type_matrix) | ||||
assert rule.match(data) is result | ||||
r153 | @pytest.mark.parametrize( | |||
"op, field, value, should_fail", | ||||
[ | ||||
("eq", "http_status", "1", False), | ||||
("ne", "http_status", "1", False), | ||||
("ne", "http_status", "foo", True), | ||||
("startswith", "http_status", "1", True), | ||||
("eq", "group:priority", "1", False), | ||||
("ne", "group:priority", "1", False), | ||||
("ge", "group:priority", "1", False), | ||||
("le", "group:priority", "1", False), | ||||
("startswith", "group:priority", "1", True), | ||||
("eq", "url_domain", "1", False), | ||||
("ne", "url_domain", "1", False), | ||||
("startswith", "url_domain", "1", False), | ||||
("endswith", "url_domain", "1", False), | ||||
("contains", "url_domain", "1", False), | ||||
("ge", "url_domain", "1", True), | ||||
("eq", "url_path", "1", False), | ||||
("ne", "url_path", "1", False), | ||||
("startswith", "url_path", "1", False), | ||||
("endswith", "url_path", "1", False), | ||||
("contains", "url_path", "1", False), | ||||
("ge", "url_path", "1", True), | ||||
("eq", "error", "1", False), | ||||
("ne", "error", "1", False), | ||||
("startswith", "error", "1", False), | ||||
("endswith", "error", "1", False), | ||||
("contains", "error", "1", False), | ||||
("ge", "error", "1", True), | ||||
("ge", "url_path", "1", True), | ||||
("eq", "tags:server_name", "1", False), | ||||
("ne", "tags:server_name", "1", False), | ||||
("startswith", "tags:server_name", "1", False), | ||||
("endswith", "tags:server_name", "1", False), | ||||
("contains", "tags:server_name", "1", False), | ||||
("ge", "tags:server_name", "1", True), | ||||
("contains", "traceback", "1", False), | ||||
("ge", "traceback", "1", True), | ||||
("eq", "group:occurences", "1", False), | ||||
("ne", "group:occurences", "1", False), | ||||
("ge", "group:occurences", "1", False), | ||||
("le", "group:occurences", "1", False), | ||||
("contains", "group:occurences", "1", True), | ||||
], | ||||
) | ||||
def test_rule_validation(self, op, field, value, should_fail, report_type_matrix): | ||||
r0 | import colander | |||
from appenlight.validators import build_rule_schema | ||||
r153 | ||||
rule_config = {"op": op, "field": field, "value": value} | ||||
r0 | ||||
schema = build_rule_schema(rule_config, report_type_matrix) | ||||
if should_fail: | ||||
with pytest.raises(colander.Invalid): | ||||
schema.deserialize(rule_config) | ||||
else: | ||||
schema.deserialize(rule_config) | ||||
def test_nested_proper_rule_validation(self, report_type_matrix): | ||||
from appenlight.validators import build_rule_schema | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
{ | ||||
"field": "__AND__", | ||||
"rules": [ | ||||
r153 | {"op": "ge", "field": "group:occurences", "value": "10"}, | |||
r0 | { | |||
"field": "__OR__", | ||||
"rules": [ | ||||
{ | ||||
"op": "endswith", | ||||
"field": "url_path", | ||||
r153 | "value": "register", | |||
r0 | }, | |||
r153 | {"op": "contains", "field": "error", "value": "test"}, | |||
], | ||||
}, | ||||
], | ||||
r0 | }, | |||
r153 | {"op": "eq", "field": "http_status", "value": "500"}, | |||
], | ||||
r0 | } | |||
schema = build_rule_schema(rule_config, report_type_matrix) | ||||
deserialized = schema.deserialize(rule_config) | ||||
def test_nested_bad_rule_validation(self, report_type_matrix): | ||||
import colander | ||||
from appenlight.validators import build_rule_schema | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__AND__", | ||||
"rules": [ | ||||
{ | ||||
"field": "__AND__", | ||||
"rules": [ | ||||
r153 | {"op": "ge", "field": "group:occurences", "value": "10"}, | |||
r0 | { | |||
"field": "__OR__", | ||||
"rules": [ | ||||
r153 | {"op": "gt", "field": "url_path", "value": "register"}, | |||
{"op": "contains", "field": "error", "value": "test"}, | ||||
], | ||||
}, | ||||
], | ||||
r0 | }, | |||
r153 | {"op": "eq", "field": "http_status", "value": "500"}, | |||
], | ||||
r0 | } | |||
schema = build_rule_schema(rule_config, report_type_matrix) | ||||
with pytest.raises(colander.Invalid): | ||||
deserialized = schema.deserialize(rule_config) | ||||
def test_config_manipulator(self): | ||||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | type_matrix = { | |||
r153 | "a": {"type": "int", "ops": ("eq", "ne", "ge", "le")}, | |||
"b": {"type": "int", "ops": ("eq", "ne", "ge", "le")}, | ||||
r0 | } | |||
rule_config = { | ||||
"field": "__OR__", | ||||
"rules": [ | ||||
{ | ||||
"field": "__OR__", | ||||
r153 | "rules": [{"op": "ge", "field": "a", "value": "10"}], | |||
r0 | }, | |||
r153 | {"op": "eq", "field": "b", "value": "500"}, | |||
], | ||||
r0 | } | |||
def rule_manipulator(rule): | ||||
r153 | if "value" in rule.config: | |||
rule.config["value"] = "1" | ||||
r0 | ||||
r153 | rule = Rule(rule_config, type_matrix, config_manipulator=rule_manipulator) | |||
rule.match({"a": 1, "b": "2"}) | ||||
assert rule.config["rules"][0]["rules"][0]["value"] == "1" | ||||
assert rule.config["rules"][1]["value"] == "1" | ||||
assert rule.type_matrix["b"]["type"] == "int" | ||||
r0 | ||||
def test_dynamic_config_manipulator(self): | ||||
from appenlight.lib.rule import Rule | ||||
r153 | ||||
r0 | rule_config = { | |||
"field": "__OR__", | ||||
"rules": [ | ||||
{ | ||||
"field": "__OR__", | ||||
r153 | "rules": [{"op": "ge", "field": "a", "value": "10"}], | |||
r0 | }, | |||
r153 | {"op": "eq", "field": "b", "value": "500"}, | |||
], | ||||
r0 | } | |||
def rule_manipulator(rule): | ||||
rule.type_matrix = { | ||||
r153 | "a": {"type": "int", "ops": ("eq", "ne", "ge", "le")}, | |||
"b": {"type": "unicode", "ops": ("eq", "ne", "ge", "le")}, | ||||
r0 | } | |||
r153 | if "value" in rule.config: | |||
if rule.config["field"] == "a": | ||||
rule.config["value"] = "1" | ||||
elif rule.config["field"] == "b": | ||||
rule.config["value"] = "2" | ||||
r0 | ||||
r153 | rule = Rule(rule_config, {}, config_manipulator=rule_manipulator) | |||
rule.match({"a": 11, "b": "55"}) | ||||
assert rule.config["rules"][0]["rules"][0]["value"] == "1" | ||||
assert rule.config["rules"][1]["value"] == "2" | ||||
assert rule.type_matrix["b"]["type"] == "unicode" | ||||
r0 | ||||
r153 | @pytest.mark.usefixtures("base_app", "with_migrations") | |||
r0 | class TestViewsWithForms(object): | |||
def test_bad_csrf(self): | ||||
from appenlight.forms import CSRFException | ||||
from appenlight.views.index import register | ||||
r153 | ||||
post_data = {"dupa": "dupa"} | ||||
r0 | request = testing.DummyRequest(post=post_data) | |||
request.POST = webob.multidict.MultiDict(request.POST) | ||||
with pytest.raises(CSRFException): | ||||
register(request) | ||||
def test_proper_csrf(self): | ||||
from appenlight.views.index import register | ||||
r153 | ||||
r0 | request = pyramid.threadlocal.get_current_request() | |||
r153 | post_data = {"dupa": "dupa", "csrf_token": request.session.get_csrf_token()} | |||
r0 | request = testing.DummyRequest(post=post_data) | |||
request.POST = webob.multidict.MultiDict(request.POST) | ||||
result = register(request) | ||||
r153 | assert result["form"].errors["email"][0] == "This field is required." | |||
r0 | ||||
r153 | @pytest.mark.usefixtures("base_app", "with_migrations", "default_data") | |||
r0 | class TestRegistration(object): | |||
def test_invalid_form(self): | ||||
from appenlight.views.index import register | ||||
r153 | ||||
r0 | request = pyramid.threadlocal.get_current_request() | |||
r153 | post_data = { | |||
"user_name": "", | ||||
"user_password": "", | ||||
"email": "", | ||||
"csrf_token": request.session.get_csrf_token(), | ||||
} | ||||
r0 | request = testing.DummyRequest(post=post_data) | |||
request.POST = webob.multidict.MultiDict(request.POST) | ||||
result = register(request) | ||||
r153 | assert result["form"].errors["user_name"][0] == "This field is required." | |||
r0 | ||||
def test_valid_form(self): | ||||
from appenlight.views.index import register | ||||
from ziggurat_foundations.models.services.user import UserService | ||||
r153 | ||||
r0 | request = pyramid.threadlocal.get_current_request() | |||
r153 | post_data = { | |||
"user_name": "foo", | ||||
"user_password": "barr", | ||||
"email": "test@test.foo", | ||||
"csrf_token": request.session.get_csrf_token(), | ||||
} | ||||
r0 | request = testing.DummyRequest(post=post_data) | |||
request.add_flash_to_headers = mock.Mock() | ||||
request.POST = webob.multidict.MultiDict(request.POST) | ||||
r153 | assert UserService.by_user_name("foo") is None | |||
r0 | register(request) | |||
r153 | user = UserService.by_user_name("foo") | |||
assert user.user_name == "foo" | ||||
r135 | assert len(user.user_password) >= 60 | |||
r0 | ||||
r153 | @pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables", "default_user") | |||
r0 | class TestApplicationCreation(object): | |||
def test_wrong_data(self): | ||||
import appenlight.views.applications as applications | ||||
from ziggurat_foundations.models.services.user import UserService | ||||
r153 | ||||
r0 | request = pyramid.threadlocal.get_current_request() | |||
r153 | request.user = UserService.by_user_name("testuser") | |||
r0 | request.unsafe_json_body = {} | |||
r153 | request.headers["X-XSRF-TOKEN"] = request.session.get_csrf_token() | |||
r0 | response = applications.application_create(request) | |||
assert response.code == 422 | ||||
def test_proper_data(self): | ||||
import appenlight.views.applications as applications | ||||
from ziggurat_foundations.models.services.user import UserService | ||||
request = pyramid.threadlocal.get_current_request() | ||||
r153 | request.user = UserService.by_user_name("testuser") | |||
request.unsafe_json_body = {"resource_name": "app name", "domains": "foo"} | ||||
request.headers["X-XSRF-TOKEN"] = request.session.get_csrf_token() | ||||
r0 | app_dict = applications.application_create(request) | |||
r153 | assert app_dict["public_key"] is not None | |||
assert app_dict["api_key"] is not None | ||||
assert app_dict["resource_name"] == "app name" | ||||
assert app_dict["owner_group_id"] is None | ||||
assert app_dict["resource_id"] is not None | ||||
assert app_dict["default_grouping"] == "url_traceback" | ||||
assert app_dict["possible_permissions"] == ("view", "update_reports") | ||||
assert app_dict["slow_report_threshold"] == 10 | ||||
assert app_dict["owner_user_name"] == "testuser" | ||||
assert app_dict["owner_user_id"] == request.user.id | ||||
assert app_dict["domains"] is "foo" | ||||
assert app_dict["postprocessing_rules"] == [] | ||||
assert app_dict["error_report_threshold"] == 10 | ||||
assert app_dict["allow_permanent_storage"] is False | ||||
assert app_dict["resource_type"] == "application" | ||||
assert app_dict["current_permissions"] == [] | ||||
@pytest.mark.usefixtures("default_application") | ||||
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables") | ||||
r0 | class TestAPISentryView(object): | |||
def test_no_payload(self, default_application): | ||||
import colander | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.views.api import sentry_compat | ||||
from appenlight.lib.request import JSONException | ||||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
r153 | request = testing.DummyRequest(headers={"Content-Type": "application/json"}) | |||
request.unsafe_json_body = "" | ||||
r0 | request.context = context | |||
route = mock.Mock() | ||||
r153 | route.name = "api_sentry" | |||
r0 | request.matched_route = route | |||
with pytest.raises(JSONException): | ||||
sentry_compat(request) | ||||
def test_java_client_payload(self): | ||||
from appenlight.views.api import sentry_compat | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.models.report_group import ReportGroup | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_sentry" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
r95 | context.resource.allow_permanent_storage = True | |||
r0 | request.context = context | |||
request.matched_route = route | ||||
r153 | request.body = ( | |||
b"eJy1UmFr2zAQ/S0T+7BCLOzYThp/C6xjG6SDLd/GCBf57Ki" | ||||
b"RJSHJJiXkv+/UlC7p2kAZA33Ru6f33t1pz3BAHVayZhWr87" | ||||
b"JMs+I6q3MsrifFep2vc1iXM1HMpgBTNmIdeg8tEvlmJ9AGa" | ||||
b"fQ7goOkQoDOUmGcZpMkLZO0WGZFRadMiaHIR1EVnTMu3k3b" | ||||
b"oiMgqJrXpgOpOVjLLTiPkWAVhMa4jih3MAAholfWyUDAksz" | ||||
b"m1iopICbg8fWH52B8VWXZVYwHrWfV/jBipD2gW2no8CFMa5" | ||||
b"JButCDSjoQG6mR6LgLDojPPn/7sbydL25ep34HGl+y3DiE+" | ||||
b"lH0xXBXjMzFBsXW99SS7pWKYXRw91zqgK4BgZ4/DZVVP/cs" | ||||
b"3NuzSZPfAKqP2Cdj4tw7U/cKH0fEFeiWQFqE2FIHAmMPjaN" | ||||
b"Y/kHvbzY/JqdHUq9o/KxqQHkcsabX4piDuT4aK+pXG1ZNi/" | ||||
b"IwOpEyruXC1LiB3vPO3BmOOxTUCIqv5LIg5H12oh9cf0l+P" | ||||
b"MvP5P8kddgoFIEvMGzM5cRSD2aLJ6qTdHKm6nv9pPcRFba0" | ||||
b"Kd0eleeCFuGN+9JZ9TaXIn/V5JYMBvxXg3L6PwzSE4dkfOb" | ||||
b"w7CtfWmP85SdCs8OvA53fUV19cg==" | ||||
) | ||||
r0 | sentry_compat(request) | |||
query = DBSession.query(ReportGroup) | ||||
report = query.first() | ||||
assert query.count() == 1 | ||||
assert report.total_reports == 1 | ||||
def test_ruby_client_payload(self): | ||||
from appenlight.views.api import sentry_compat | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.models.report_group import ReportGroup | ||||
from appenlight.tests.payload_examples import SENTRY_RUBY_ENCODED | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_sentry" | |||
r0 | request = testing.DummyRequest( | |||
r153 | headers={ | |||
"Content-Type": "application/octet-stream", | ||||
"User-Agent": "sentry-ruby/1.0.0", | ||||
"X-Sentry-Auth": "Sentry sentry_version=5, " | ||||
"sentry_client=raven-ruby/1.0.0, " | ||||
"sentry_timestamp=1462378483, " | ||||
"sentry_key=xxx, sentry_secret=xxx", | ||||
} | ||||
) | ||||
r0 | context = DummyContext() | |||
context.resource = ApplicationService.by_id(1) | ||||
r95 | context.resource.allow_permanent_storage = True | |||
r0 | request.context = context | |||
request.matched_route = route | ||||
request.body = SENTRY_RUBY_ENCODED | ||||
sentry_compat(request) | ||||
query = DBSession.query(ReportGroup) | ||||
report = query.first() | ||||
assert query.count() == 1 | ||||
assert report.total_reports == 1 | ||||
def test_python_client_decoded_payload(self): | ||||
from appenlight.views.api import sentry_compat | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.models.report_group import ReportGroup | ||||
from appenlight.tests.payload_examples import SENTRY_PYTHON_PAYLOAD_7 | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_sentry" | |||
r0 | request = pyramid.threadlocal.get_current_request() | |||
context = DummyContext() | ||||
context.resource = ApplicationService.by_id(1) | ||||
r95 | context.resource.allow_permanent_storage = True | |||
r0 | request.context = context | |||
request.matched_route = route | ||||
r153 | request.body = json.dumps(SENTRY_PYTHON_PAYLOAD_7).encode("utf8") | |||
r0 | sentry_compat(request) | |||
query = DBSession.query(ReportGroup) | ||||
report = query.first() | ||||
assert query.count() == 1 | ||||
assert report.total_reports == 1 | ||||
def test_python_client_encoded_payload(self): | ||||
from appenlight.views.api import sentry_compat | ||||
from appenlight.models.services.application import ApplicationService | ||||
from appenlight.models.report_group import ReportGroup | ||||
from appenlight.tests.payload_examples import SENTRY_PYTHON_ENCODED | ||||
r153 | ||||
r0 | route = mock.Mock() | |||
r153 | route.name = "api_sentry" | |||
r0 | request = testing.DummyRequest( | |||
r153 | headers={ | |||
"Content-Type": "application/octet-stream", | ||||
"Content-Encoding": "deflate", | ||||
"User-Agent": "sentry-ruby/1.0.0", | ||||
"X-Sentry-Auth": "Sentry sentry_version=5, " | ||||
"sentry_client=raven-ruby/1.0.0, " | ||||
"sentry_timestamp=1462378483, " | ||||
"sentry_key=xxx, sentry_secret=xxx", | ||||
} | ||||
) | ||||
r0 | context = DummyContext() | |||
context.resource = ApplicationService.by_id(1) | ||||
r95 | context.resource.allow_permanent_storage = True | |||
r0 | request.context = context | |||
request.matched_route = route | ||||
request.body = SENTRY_PYTHON_ENCODED | ||||
sentry_compat(request) | ||||
query = DBSession.query(ReportGroup) | ||||
report = query.first() | ||||
assert query.count() == 1 | ||||
assert report.total_reports == 1 | ||||