blob: e268546e034856411b32149126247ae19c4e43fa [file]
# Copyright 2022 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.
from __future__ import absolute_import
import datetime
import json
import unittest
import os
import gae_ts_mon
import mock
import flask
from .test_support import test_case
from infra_libs.ts_mon import config
from infra_libs.ts_mon import flask_handlers
from infra_libs.ts_mon.common import interface
from infra_libs.ts_mon.common import metrics
from infra_libs.ts_mon.common import targets
class FlaskTSMonJSHandlerTest(test_case.TestCase):
def setUp(self):
super(FlaskTSMonJSHandlerTest, self).setUp()
config.reset_for_unittest()
target = targets.TaskTarget('test_service', 'test_job', 'test_region',
'test_host')
self.mock_state = interface.State(target=target)
mock.patch(
'infra_libs.ts_mon.common.interface.state',
new=self.mock_state).start()
self.ts_mon_handler = flask_handlers.TSMonJSFlaskHandler(flask=flask)
self.ts_mon_handler.register_metrics([
metrics.BooleanMetric(
'frontend/boolean_test',
'Boolean metric test',
field_spec=[metrics.StringField('client_id')]),
])
self.ts_mon_handler.xsrf_is_valid = mock.Mock(return_value=True)
self.mock_timestamp = 1537821859
def time_fn():
return self.mock_timestamp
self.ts_mon_handler.time_fn = time_fn
self.app = flask.Flask('test_app')
self.app.config['TESTING'] = True
self.app.add_url_rule(
'/_/ts_mon_js.do', view_func=self.ts_mon_handler.post, methods=['POST'])
def tearDown(self):
mock.patch.stopall()
config.reset_for_unittest()
super(FlaskTSMonJSHandlerTest, self).tearDown()
def test_time_fn(self):
time_fn_res = flask_handlers.TSMonJSFlaskHandler(flask=flask).time_fn()
self.assertTrue(isinstance(time_fn_res, float))
def test_post_metrics_no_metrics(self):
self.ts_mon_handler._metrics = None
res = self.app.test_client().post('/_/ts_mon_js.do')
self.assertEqual(res.status_code, 400)
def test_post_metrics_invalid_json(self):
"""Test case when JSON request body is invalid."""
res = self.app.test_client().post('/_/ts_mon_js.do', data='rutabaga')
self.assertEqual(res.status_code, 400)
def test_post_metrics_invalid_xsrf(self):
"""Test case when XSRF token is invalid."""
self.ts_mon_handler.xsrf_is_valid = mock.Mock(return_value=False)
res = self.app.test_client().post(
'/_/ts_mon_js.do', data=json.dumps({'metrics': []}))
self.assertEqual(res.status_code, 403)
self.ts_mon_handler.xsrf_is_valid.assert_called_once()
def test_post_metrics_must_be_dict(self):
"""Test case when body is not a dict."""
res = self.app.test_client().post('/_/ts_mon_js.do', data=json.dumps([]))
self.assertEqual(res.status_code, 400)
def test_post_no_metrics_key(self):
"""Test case when body is not a dict."""
res = self.app.test_client().post(
'/_/ts_mon_js.do', data=json.dumps({'test': []}))
self.assertEqual(res.status_code, 400)
def test_post_metrics_unregistered_metric_name(self):
"""Test case when a metric name isn't registered."""
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/not_defined',
'ValueType': 2,
},
'Cells': [{
'value': 'rutabaga',
'fields': {
'client_id': '789',
'rutabaga_id': '789',
},
}],
},],
})
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_metrics_malcious_metric_name(self):
"""Metric name is echoed back in a safely escaped form."""
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/not_defined<script>alert(1)</script>',
'ValueType': 2,
},
'Cells': [{
'value': 'rutabaga',
'fields': {
'client_id': '789',
'rutabaga_id': '789',
},
}],
},],
})
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_metrics_invalid_fields(self):
"""Test case when metric name is fine but fields are not."""
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/boolean_test',
'ValueType': 2,
},
'Cells': [{
'value': True,
'fields': {
'client_id': '789',
'rutabaga_id': '789',
},
}],
},],
})
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_metrics_maliciou_fields(self):
"""Invalid fields are echoed in a safely escaped format."""
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/boolean_test',
'ValueType': 2,
},
'Cells': [{
'value': True,
'fields': {
'client_id<script>alert(1)</script>': '789',
'rutabaga_id': '789',
},
}],
},],
})
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_rejects_cumulative_without_start_time(self):
"""Test case where start_time is not supplied for CumulativeDistribution."""
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/cumulative_test',
'ValueType': 2,
},
'Cells': [{
'value': 'rutabaga',
'fields': {
'client_id': '789',
},
}],
},],
})
self.ts_mon_handler.register_metrics([
metrics.CumulativeDistributionMetric(
'frontend/cumulative_test',
'Cumulative metric test',
field_spec=[metrics.StringField('client_id')]),
])
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_rejects_start_time_in_future(self):
"""Test rejects when start_time is in the future."""
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/cumulative_test',
'ValueType': 2,
},
'Cells': [{
'value': 'rutabaga',
'fields': {
'client_id': '789',
},
'start_time': self.mock_timestamp + 1,
}],
},],
})
self.ts_mon_handler.register_metrics([
metrics.CumulativeDistributionMetric(
'frontend/cumulative_test',
'Cumulative metric test',
field_spec=[metrics.StringField('client_id')]),
])
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_rejects_start_time_in_past(self):
"""Test rejects when start_time is >1 month in the past."""
one_month_seconds = 60 * 60 * 24 * 30
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/cumulative_test',
'ValueType': 2,
},
'Cells': [{
'value': 'rutabaga',
'fields': {
'client_id': '789',
},
'start_time': self.mock_timestamp - one_month_seconds * 2,
}],
},],
})
self.ts_mon_handler.register_metrics([
metrics.CumulativeDistributionMetric(
'frontend/cumulative_test',
'Cumulative metric test',
field_spec=[metrics.StringField('client_id')]),
])
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_distribution_metrics_not_a_dict(self):
"""Test case when a distribution metric value is not a dict."""
body = json.dumps({
'metrics': [{
'MetricInfo': {
'Name': 'frontend/cumulative_test',
'ValueType': 2,
},
'Cells': [{
'value': 'rutabaga',
'fields': {
'client_id': '789',
},
'start_time': self.mock_timestamp - 60,
}],
},],
})
self.ts_mon_handler.register_metrics([
metrics.CumulativeDistributionMetric(
'frontend/cumulative_test',
'Cumulative metric test',
field_spec=[metrics.StringField('client_id')]),
])
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 400)
def test_post_metrics_normal(self):
"""Test successful POST case."""
body = json.dumps({
'metrics': [
{
'MetricInfo': {
'Name': 'frontend/boolean_test',
'ValueType': 2,
},
'Cells': [{
'value': True,
'fields': {
'client_id': '789',
},
}],
},
{
'MetricInfo': {
'Name': 'frontend/cumulative_test',
'ValueType': 2,
},
'Cells': [{
'value': {
'sum': 1234,
'count': 4321,
'buckets': {
0: 123,
1: 321,
2: 213,
},
},
'fields': {
'client_id': '789',
},
'start_time': self.mock_timestamp - 60,
}],
},
],
})
self.ts_mon_handler.register_metrics([
metrics.CumulativeDistributionMetric(
'frontend/cumulative_test',
'Cumulative metric test',
field_spec=[metrics.StringField('client_id')]),
])
res = self.app.test_client().post('/_/ts_mon_js.do', data=body)
self.assertEqual(res.status_code, 201)