# -*- coding: utf-8 -*-

# Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors
#
# 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
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# 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.

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


@pytest.mark.usefixtures("base_app")
class BasicTest(object):
    pass


@pytest.mark.usefixtures("base_app")
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

        event_dict, event_type = parse_sentry_event(
            payload_examples.SENTRY_LOG_PAYLOAD_7
        )
        assert ParsedSentryEventType.LOG == event_type
        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)

    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

        utcnow = datetime.utcnow().date().strftime("%Y-%m-%dT%H:%M:%SZ")
        event_dict, event_type = parse_sentry_event(
            payload_examples.SENTRY_PYTHON_PAYLOAD_7
        )
        assert ParsedSentryEventType.ERROR_REPORT == event_type
        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>"),
            ]
        )


class TestAPIReports_0_5_Validation(object):
    @pytest.mark.parametrize("dummy_json", ["", {}, [], None])
    def test_no_payload(self, dummy_json):
        import colander
        from appenlight.validators import ReportListSchema_0_5

        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

        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 = [{"report_details": [{}]}]
        from appenlight.validators import ReportListSchema_0_5

        utcnow = datetime.utcnow()
        schema = ReportListSchema_0_5().bind(utcnow=utcnow)

        deserialized = schema.deserialize(dummy_json)

        expected_deserialization = [
            {
                "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": "",
            }
        ]
        assert deserialized == expected_deserialization

    def test_full_payload(self):
        import appenlight.tests.payload_examples as payload_examples
        from appenlight.validators import ReportListSchema_0_5

        PYTHON_PAYLOAD = copy.deepcopy(payload_examples.PYTHON_PAYLOAD_0_5)
        utcnow = datetime.utcnow()
        schema = ReportListSchema_0_5().bind(utcnow=utcnow)
        PYTHON_PAYLOAD["tags"] = [
            ("foo", 1),
            ("action", "test"),
            ("baz", 1.1),
            ("date", utcnow.strftime("%Y-%m-%dT%H:%M:%S.0")),
        ]
        dummy_json = [PYTHON_PAYLOAD]
        deserialized = schema.deserialize(dummy_json)[0]
        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")
class TestAPILogsValidation(object):
    @pytest.mark.parametrize("dummy_json", ["", {}, [], None])
    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]
        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"]

    def test_normal_payload(self, log_schema):
        import appenlight.tests.payload_examples as payload_examples

        deserialized = log_schema.deserialize(payload_examples.LOG_EXAMPLES)[0]
        expected = payload_examples.LOG_EXAMPLES[0]
        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

    def test_normal_payload_date_without_microseconds(self, log_schema):
        import appenlight.tests.payload_examples as payload_examples

        LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES)
        LOG_EXAMPLE[0]["date"] = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
        deserialized = log_schema.deserialize(LOG_EXAMPLE)
        assert (
            deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M:%S")
            == LOG_EXAMPLE[0]["date"]
        )

    def test_normal_payload_date_without_seconds(self, log_schema):
        import appenlight.tests.payload_examples as payload_examples

        LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES)
        LOG_EXAMPLE[0]["date"] = datetime.utcnow().date().strftime("%Y-%m-%dT%H:%M")
        deserialized = log_schema.deserialize(LOG_EXAMPLE)
        assert (
            deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M") == LOG_EXAMPLE[0]["date"]
        )

    def test_payload_empty_date(self, log_schema):
        import appenlight.tests.payload_examples as payload_examples

        LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES)
        LOG_EXAMPLE[0]["date"] = None
        deserialized = log_schema.deserialize(LOG_EXAMPLE)
        assert deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M") is not None

    def test_payload_no_date(self, log_schema):
        import appenlight.tests.payload_examples as payload_examples

        LOG_EXAMPLE = copy.deepcopy(payload_examples.LOG_EXAMPLES)
        LOG_EXAMPLE[0].pop("date", None)
        deserialized = log_schema.deserialize(LOG_EXAMPLE)
        assert deserialized[0]["date"].strftime("%Y-%m-%dT%H:%M") is not None


@pytest.mark.usefixtures("general_metrics_schema")
class TestAPIGeneralMetricsValidation(object):
    @pytest.mark.parametrize("dummy_json", ["", {}, [], None])
    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):
        dummy_json = [{"tags": [["counter_a", 15.5], ["counter_b", 63]]}]
        deserialized = general_metrics_schema.deserialize(dummy_json)[0]
        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"]

    def test_normal_payload(self, general_metrics_schema):
        import appenlight.tests.payload_examples as payload_examples

        dummy_json = [payload_examples.METRICS_PAYLOAD]
        deserialized = general_metrics_schema.deserialize(dummy_json)[0]
        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"]


@pytest.mark.usefixtures("request_metrics_schema")
class TestAPIRequestMetricsValidation(object):
    @pytest.mark.parametrize("dummy_json", ["", {}, [], None])
    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

        dummy_json = payload_examples.REQUEST_METRICS_EXAMPLES
        deserialized = request_metrics_schema.deserialize(dummy_json)[0]
        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]
        assert metric[0] == expected_metric[0]
        assert sorted(metric[1].items()) == sorted(expected_metric[1].items())


@pytest.mark.usefixtures("default_application")
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables")
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)
        request = testing.DummyRequest(headers={"Content-Type": "application/json"})
        request.unsafe_json_body = ""
        request.context = context
        route = mock.Mock()
        route.name = "api_reports"
        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

        route = mock.Mock()
        route.name = "api_reports"
        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

        route = mock.Mock()
        route.name = "api_reports"
        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),
            copy.deepcopy(PYTHON_PAYLOAD),
        ]
        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

        route = mock.Mock()
        route.name = "api_reports"
        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
        request.unsafe_json_body = [
            copy.deepcopy(PYTHON_PAYLOAD),
            copy.deepcopy(PARSED_REPORT_404),
        ]
        reports_create(request)
        query = DBSession.query(ReportGroup)
        report = query.first()
        assert query.count() == 2
        assert report.total_reports == 1


@pytest.mark.usefixtures("default_application")
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables")
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()
        request = testing.DummyRequest(headers={"Content-Type": "application/xml"})
        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]
        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"

    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)
        request = testing.DummyRequest(headers={"Content-Type": "application/xml"})
        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()
        route.name = "api_airbrake"
        request.matched_route = route
        result = airbrake_xml_compat(request)
        assert "<notice><id>" in result


@pytest.mark.usefixtures("default_application")
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables")
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)
        request = testing.DummyRequest(headers={"Content-Type": "application/json"})
        request.context = context
        request.registry = base_app.registry
        request.unsafe_json_body = ""
        route = mock.Mock()
        route.name = "api_logs"
        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

        route = mock.Mock()
        route.name = "api_logs"
        request = pyramid.threadlocal.get_current_request()
        context = DummyContext()
        context.resource = ApplicationService.by_id(1)
        request.context = context
        request.matched_route = route
        request.unsafe_json_body = [copy.deepcopy(payload_examples.LOG_EXAMPLES[0])]
        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

        route = mock.Mock()
        route.name = "api_logs"
        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

        route = mock.Mock()
        route.name = "api_logs"
        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])
        LOG_PAYLOAD["primary_key"] = "X2"
        LOG_PAYLOAD2["primary_key"] = "X2"
        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"


@pytest.mark.usefixtures("default_application")
@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables")
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

        route = mock.Mock()
        route.name = "api_general_metrics"
        context = DummyContext()
        context.resource = ApplicationService.by_id(1)
        request = testing.DummyRequest(headers={"Content-Type": "application/json"})
        request.context = context
        request.registry = base_app.registry
        request.unsafe_json_body = ""
        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
        from appenlight.models.metric import Metric
        from appenlight.views.api import general_metrics_create
        from appenlight.models.services.application import ApplicationService

        route = mock.Mock()
        route.name = "api_general_metric"
        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
        assert metric.namespace == "some.monitor"

    def test_multiple_json_payload(self):
        import appenlight.tests.payload_examples as payload_examples
        from appenlight.models.metric import Metric
        from appenlight.views.api import general_metrics_create
        from appenlight.models.services.application import ApplicationService

        route = mock.Mock()
        route.name = "api_general_metrics"
        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
        assert metric.namespace == "some.monitor"


class TestGroupingMessageReplacements(object):
    def replace_default_repr_python(self):
        test_str = """
        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)'))
        """
        regex = r"<(.*?) object at (.*?)>"


class TestRulesKeyGetter(object):
    def test_default_dict_getter_top_key(self):
        from appenlight.lib.rule import Rule

        struct = {"a": {"b": "b", "c": {"d": "d", "g": {"h": "h"}}, "e": "e"}, "f": "f"}
        result = Rule.default_dict_struct_getter(struct, "a")
        assert result == struct["a"]

    def test_default_dict_getter_sub_key(self):
        from appenlight.lib.rule import Rule

        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"]

    def test_default_obj_getter_top_key(self):
        from appenlight.lib.rule import Rule

        class TestStruct(object):
            def __init__(self, a, b):
                self.a = a
                self.b = b

        struct = TestStruct(a="a", b=TestStruct(a="x", b="y"))
        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

        class TestStruct(object):
            def __init__(self, name, a, b):
                self.name = name
                self.a = a
                self.b = b

            def __repr__(self):
                return "<obj {}>".format(self.name)

        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")
        assert result == struct.b.b
        result = Rule.default_obj_struct_getter(struct, "b:a:b")
        assert result == struct.b.a.b


@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
    ):
        from appenlight.lib.rule import Rule

        rule_config = {"op": op, "field": "http_status", "value": test_value}
        rule = Rule(rule_config, report_type_matrix)

        data = {"http_status": struct_value}
        assert rule.match(data) is match_result

    @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
    ):
        from appenlight.lib.rule import Rule

        rule_config = {"op": op, "field": "duration", "value": test_value}
        rule = Rule(rule_config, report_type_matrix)

        data = {"duration": struct_value}
        assert rule.match(data) is match_result

    @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
    ):
        from appenlight.lib.rule import Rule

        rule_config = {"op": op, "field": "error", "value": test_value}
        rule = Rule(rule_config, report_type_matrix)

        data = {"error": struct_value}
        assert rule.match(data) is match_result

    @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),
        ],
    )
    def test_type_normalization(self, field, value, s_type):
        from appenlight.lib.rule import Rule

        type_matrix = {
            "field_unicode": {"type": "unicode"},
            "field_float": {"type": "float"},
            "field_int": {"type": "int"},
        }

        rule = Rule({}, type_matrix)
        n_value = rule.normalized_type(field, value)
        assert isinstance(n_value, s_type) is True


@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),
        ],
    )
    def test_NOT_rule(self, data, result, report_type_matrix):
        from appenlight.lib.rule import Rule

        rule_config = {
            "field": "__NOT__",
            "rules": [
                {"op": "ge", "field": "group:occurences", "value": "10"},
                {"op": "ge", "field": "group:priority", "value": "4"},
            ],
        }

        rule = Rule(rule_config, report_type_matrix)
        assert rule.match(data) is result

    @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),
        ],
    )
    def test_nested_OR_AND_rule(self, data, result, report_type_matrix):
        from appenlight.lib.rule import Rule

        rule_config = {
            "field": "__OR__",
            "rules": [
                {
                    "field": "__AND__",
                    "rules": [
                        {"op": "ge", "field": "group:occurences", "value": "10"},
                        {"op": "ge", "field": "group:priority", "value": "4"},
                    ],
                },
                {"op": "eq", "field": "http_status", "value": "500"},
            ],
        }

        rule = Rule(rule_config, report_type_matrix)
        assert rule.match(data) is result

    @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),
        ],
    )
    def test_nested_OR_OR_rule(self, data, result, report_type_matrix):
        from appenlight.lib.rule import Rule

        rule_config = {
            "field": "__OR__",
            "rules": [
                {
                    "field": "__OR__",
                    "rules": [
                        {"op": "ge", "field": "group:occurences", "value": "10"},
                        {"op": "ge", "field": "group:priority", "value": "4"},
                    ],
                },
                {"op": "eq", "field": "http_status", "value": "500"},
            ],
        }

        rule = Rule(rule_config, report_type_matrix)
        assert rule.match(data) is result

    @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),
        ],
    )
    def test_nested_AND_AND_rule(self, data, result, report_type_matrix):
        from appenlight.lib.rule import Rule

        rule_config = {
            "field": "__AND__",
            "rules": [
                {
                    "field": "__AND__",
                    "rules": [
                        {"op": "ge", "field": "group:occurences", "value": "10"},
                        {"op": "ge", "field": "group:priority", "value": "4"},
                    ],
                },
                {"op": "eq", "field": "http_status", "value": "500"},
            ],
        }

        rule = Rule(rule_config, report_type_matrix)
        assert rule.match(data) is result

    @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,
            ),
        ],
    )
    def test_nested_AND_AND_AND_rule(self, data, result, report_type_matrix):
        from appenlight.lib.rule import Rule

        rule_config = {
            "field": "__AND__",
            "rules": [
                {
                    "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"},
            ],
        }

        rule = Rule(rule_config, report_type_matrix)
        assert rule.match(data) is result

    @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,
            ),
        ],
    )
    def test_nested_AND_AND_OR_rule(self, data, result, report_type_matrix):
        from appenlight.lib.rule import Rule

        rule_config = {
            "field": "__AND__",
            "rules": [
                {
                    "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"},
            ],
        }

        rule = Rule(rule_config, report_type_matrix)
        assert rule.match(data) is result

    @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):
        import colander
        from appenlight.validators import build_rule_schema

        rule_config = {"op": op, "field": field, "value": value}

        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

        rule_config = {
            "field": "__AND__",
            "rules": [
                {
                    "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"},
            ],
        }

        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

        rule_config = {
            "field": "__AND__",
            "rules": [
                {
                    "field": "__AND__",
                    "rules": [
                        {"op": "ge", "field": "group:occurences", "value": "10"},
                        {
                            "field": "__OR__",
                            "rules": [
                                {"op": "gt", "field": "url_path", "value": "register"},
                                {"op": "contains", "field": "error", "value": "test"},
                            ],
                        },
                    ],
                },
                {"op": "eq", "field": "http_status", "value": "500"},
            ],
        }

        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

        type_matrix = {
            "a": {"type": "int", "ops": ("eq", "ne", "ge", "le")},
            "b": {"type": "int", "ops": ("eq", "ne", "ge", "le")},
        }
        rule_config = {
            "field": "__OR__",
            "rules": [
                {
                    "field": "__OR__",
                    "rules": [{"op": "ge", "field": "a", "value": "10"}],
                },
                {"op": "eq", "field": "b", "value": "500"},
            ],
        }

        def rule_manipulator(rule):
            if "value" in rule.config:
                rule.config["value"] = "1"

        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"

    def test_dynamic_config_manipulator(self):
        from appenlight.lib.rule import Rule

        rule_config = {
            "field": "__OR__",
            "rules": [
                {
                    "field": "__OR__",
                    "rules": [{"op": "ge", "field": "a", "value": "10"}],
                },
                {"op": "eq", "field": "b", "value": "500"},
            ],
        }

        def rule_manipulator(rule):
            rule.type_matrix = {
                "a": {"type": "int", "ops": ("eq", "ne", "ge", "le")},
                "b": {"type": "unicode", "ops": ("eq", "ne", "ge", "le")},
            }

            if "value" in rule.config:
                if rule.config["field"] == "a":
                    rule.config["value"] = "1"
                elif rule.config["field"] == "b":
                    rule.config["value"] = "2"

        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"


@pytest.mark.usefixtures("base_app", "with_migrations")
class TestViewsWithForms(object):
    def test_bad_csrf(self):
        from appenlight.forms import CSRFException
        from appenlight.views.index import register

        post_data = {"dupa": "dupa"}
        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

        request = pyramid.threadlocal.get_current_request()
        post_data = {"dupa": "dupa", "csrf_token": request.session.get_csrf_token()}
        request = testing.DummyRequest(post=post_data)
        request.POST = webob.multidict.MultiDict(request.POST)
        result = register(request)
        assert result["form"].errors["email"][0] == "This field is required."


@pytest.mark.usefixtures("base_app", "with_migrations", "default_data")
class TestRegistration(object):
    def test_invalid_form(self):
        from appenlight.views.index import register

        request = pyramid.threadlocal.get_current_request()
        post_data = {
            "user_name": "",
            "user_password": "",
            "email": "",
            "csrf_token": request.session.get_csrf_token(),
        }
        request = testing.DummyRequest(post=post_data)
        request.POST = webob.multidict.MultiDict(request.POST)
        result = register(request)
        assert result["form"].errors["user_name"][0] == "This field is required."

    def test_valid_form(self):
        from appenlight.views.index import register
        from ziggurat_foundations.models.services.user import UserService

        request = pyramid.threadlocal.get_current_request()
        post_data = {
            "user_name": "foo",
            "user_password": "barr",
            "email": "test@test.foo",
            "csrf_token": request.session.get_csrf_token(),
        }
        request = testing.DummyRequest(post=post_data)
        request.add_flash_to_headers = mock.Mock()
        request.POST = webob.multidict.MultiDict(request.POST)
        assert UserService.by_user_name("foo") is None
        register(request)
        user = UserService.by_user_name("foo")
        assert user.user_name == "foo"
        assert len(user.user_password) >= 60


@pytest.mark.usefixtures("base_app", "with_migrations", "clean_tables", "default_user")
class TestApplicationCreation(object):
    def test_wrong_data(self):
        import appenlight.views.applications as applications
        from ziggurat_foundations.models.services.user import UserService

        request = pyramid.threadlocal.get_current_request()
        request.user = UserService.by_user_name("testuser")
        request.unsafe_json_body = {}
        request.headers["X-XSRF-TOKEN"] = request.session.get_csrf_token()
        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()
        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()
        app_dict = applications.application_create(request)
        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")
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)
        request = testing.DummyRequest(headers={"Content-Type": "application/json"})
        request.unsafe_json_body = ""
        request.context = context
        route = mock.Mock()
        route.name = "api_sentry"
        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

        route = mock.Mock()
        route.name = "api_sentry"
        request = pyramid.threadlocal.get_current_request()
        context = DummyContext()
        context.resource = ApplicationService.by_id(1)
        context.resource.allow_permanent_storage = True
        request.context = context
        request.matched_route = route
        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=="
        )
        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

        route = mock.Mock()
        route.name = "api_sentry"
        request = testing.DummyRequest(
            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",
            }
        )
        context = DummyContext()
        context.resource = ApplicationService.by_id(1)
        context.resource.allow_permanent_storage = True
        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

        route = mock.Mock()
        route.name = "api_sentry"
        request = pyramid.threadlocal.get_current_request()
        context = DummyContext()
        context.resource = ApplicationService.by_id(1)
        context.resource.allow_permanent_storage = True
        request.context = context
        request.matched_route = route
        request.body = json.dumps(SENTRY_PYTHON_PAYLOAD_7).encode("utf8")
        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

        route = mock.Mock()
        route.name = "api_sentry"
        request = testing.DummyRequest(
            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",
            }
        )
        context = DummyContext()
        context.resource = ApplicationService.by_id(1)
        context.resource.allow_permanent_storage = True
        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
