# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
import mock
import webapp2

from .test_support import test_case

from google.appengine.api.runtime import runtime

from infra_libs.ts_mon import config
from infra_libs.ts_mon import instrument_webapp2
from infra_libs.ts_mon.common import http_metrics
from infra_libs.ts_mon.common import interface
from infra_libs.ts_mon.common import targets


class InstrumentWebapp2Test(test_case.TestCase):

  def setUp(self):
    super(InstrumentWebapp2Test, self).setUp()
    config.reset_for_unittest()

    self.next_time = 42.0
    self.time_increment = 3.0

  def fake_time(self):
    ret = self.next_time
    self.next_time += self.time_increment
    return ret

  def test_instrument_webapp2_invoked_multiple_times(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        self.response.write('success!')

    app = webapp2.WSGIApplication([('/', Handler)])
    self.assertFalse(instrument_webapp2._is_instrumented(app))

    instrument_webapp2.instrument(app, time_fn=self.fake_time)
    self.assertTrue(instrument_webapp2._is_instrumented(app))
    instrument_webapp2.instrument(app, time_fn=self.fake_time)
    self.assertTrue(instrument_webapp2._is_instrumented(app))

    # trigger a test page handler and check if the value of the HTTP metric
    # didn't increase twice.
    app.get_response('/')
    fields = {'name': '^/$', 'status': 200, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_success(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        self.response.write('success!')

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app, time_fn=self.fake_time)

    app.get_response('/')

    fields = {'name': '^/$', 'status': 200, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))
    self.assertLessEqual(3000, http_metrics.server_durations.get(fields).sum)
    self.assertEqual(
        len('success!'),
        http_metrics.server_response_bytes.get(fields).sum)

  def test_abort(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        self.abort(417)

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/')

    fields = {'name': '^/$', 'status': 417, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_set_status(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        self.response.set_status(418)

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/')

    fields = {'name': '^/$', 'status': 418, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_exception(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        raise ValueError

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/')

    fields = {'name': '^/$', 'status': 500, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_http_exception(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        raise webapp2.exc.HTTPExpectationFailed()

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/')

    fields = {'name': '^/$', 'status': 417, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_return_response(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        ret = webapp2.Response()
        ret.set_status(418)
        return ret

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/')

    fields = {'name': '^/$', 'status': 418, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_robot(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        ret = webapp2.Response()
        ret.set_status(200)
        return ret

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/', user_agent='GoogleBot')

    fields = {'name': '^/$', 'status': 200, 'is_robot': True}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_missing_response_content_length(self):

    class Handler(webapp2.RequestHandler):

      def get(self):
        del self.response.headers['content-length']

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/')

    fields = {'name': '^/$', 'status': 200, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))
    self.assertIsNone(http_metrics.server_response_bytes.get(fields))

  def test_not_found(self):
    app = webapp2.WSGIApplication([])
    instrument_webapp2.instrument(app)

    app.get_response('/notfound')

    fields = {'name': '', 'status': 404, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))

  def test_post(self):

    class Handler(webapp2.RequestHandler):

      def post(self):
        pass

    app = webapp2.WSGIApplication([('/', Handler)])
    instrument_webapp2.instrument(app)

    app.get_response('/', POST='foo')

    fields = {'name': '^/$', 'status': 200, 'is_robot': False}
    self.assertEqual(1, http_metrics.server_response_status.get(fields))
    self.assertEqual(
        len('foo'),
        http_metrics.server_request_bytes.get(fields).sum)


class TaskNumAssignerHandlerTest(test_case.TestCase):

  def setUp(self):
    super(TaskNumAssignerHandlerTest, self).setUp()
    config.reset_for_unittest()
    target = targets.TaskTarget('test_service', 'test_job', 'test_region',
                                'test_host')
    self.mock_state = interface.State(target=target)
    self.app = instrument_webapp2.create_app()
    instrument_webapp2.instrument(self.app)

  def tearDown(self):
    mock.patch.stopall()
    super(TaskNumAssignerHandlerTest, self).tearDown()

  def test_success(self):
    with mock.patch.object(runtime, 'memory_usage') as m:
      response = self.app.get_response(
          '/internal/cron/ts_mon/send', headers=[('X-Appengine-Cron', 'true')])
      self.assertEqual(response.status_int, 200)
      m.assert_called()

  def test_unauthorized(self):
    with mock.patch.object(runtime, 'memory_usage') as m:
      response = self.app.get_response('/internal/cron/ts_mon/send')
      self.assertEqual(response.status_int, 403)
      m.assert_called()
