1238 lines
56 KiB
Python
1238 lines
56 KiB
Python
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# 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 datetime
|
|
import json
|
|
import tempfile
|
|
import uuid
|
|
|
|
from dateutil import parser as date_parser
|
|
import feedparser
|
|
import mock
|
|
import numpy
|
|
from pbr import version
|
|
import six
|
|
from subunit2sql.db import models
|
|
|
|
from openstack_health import api
|
|
from openstack_health.tests import base
|
|
|
|
timestamp_a = datetime.datetime(1914, 8, 26, 20, 0, 0)
|
|
timestamp_b = datetime.datetime(1914, 8, 26, 20, 0, 1)
|
|
|
|
timestamp_s1 = datetime.datetime(1914, 8, 26, 20, 0, 0)
|
|
timestamp_s2 = datetime.datetime(1914, 8, 26, 20, 0, 1)
|
|
timestamp_s3 = datetime.datetime(1914, 8, 26, 20, 0, 2)
|
|
|
|
timestamp_m1 = datetime.datetime(1914, 8, 26, 20, 0, 0)
|
|
timestamp_m2 = datetime.datetime(1914, 8, 26, 20, 1, 0)
|
|
timestamp_m3 = datetime.datetime(1914, 8, 26, 20, 2, 0)
|
|
|
|
timestamp_h1 = datetime.datetime(1914, 8, 26, 20, 0, 0)
|
|
timestamp_h2 = datetime.datetime(1914, 8, 26, 21, 0, 0)
|
|
timestamp_h3 = datetime.datetime(1914, 8, 26, 22, 0, 0)
|
|
|
|
timestamp_d1 = datetime.datetime(1914, 8, 26, 0, 0, 0)
|
|
timestamp_d2 = datetime.datetime(1914, 8, 27, 0, 0, 0)
|
|
|
|
|
|
class TestRestAPI(base.TestCase):
|
|
|
|
def setUp(self):
|
|
super(TestRestAPI, self).setUp()
|
|
api.app.config['TESTING'] = True
|
|
self.app = api.app.test_client()
|
|
# NOTE(mtreinish): This is mocking the flask function which calls
|
|
# whatever uses the .before_first_request decorator, simply mocking
|
|
# out the setup function was insufficient
|
|
setup_mock = mock.patch(
|
|
'flask.app.Flask.try_trigger_before_first_request_functions')
|
|
setup_mock.start()
|
|
self.addCleanup(setup_mock.stop)
|
|
api.Session = mock.MagicMock()
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_run_metadata_keys',
|
|
return_value=['build_name', 'project', 'build_uuid'])
|
|
def test_get_run_metadata_keys(self, api_mock):
|
|
res = self.app.get('/runs/metadata/keys')
|
|
self.assertEqual(200, res.status_code)
|
|
api_mock.assert_called_once_with(api.Session())
|
|
expected_response = [
|
|
u'build_name',
|
|
u'project',
|
|
u'build_uuid'
|
|
]
|
|
self.assertItemsEqual(expected_response,
|
|
json.loads(res.data.decode('utf-8')))
|
|
|
|
@mock.patch('subunit2sql.db.api.get_test_run_dict_by_run_meta_key_value',
|
|
return_value=[
|
|
{'test_id': 'fake_test_a',
|
|
'status': 'success',
|
|
'start_time': timestamp_a,
|
|
'stop_time': timestamp_b},
|
|
{'test_id': 'fake_test_b',
|
|
'status': 'fail',
|
|
'start_time': timestamp_a,
|
|
'stop_time': timestamp_b}
|
|
])
|
|
def _test_get_test_runs_by_build_name(self, build_name, api_mock):
|
|
api.region = mock.MagicMock()
|
|
api.region.cache_on_arguments = mock.MagicMock()
|
|
api.region.cache_on_arguments.return_value = lambda x: x
|
|
res = self.app.get('/build_name/%s/test_runs' % build_name)
|
|
self.assertEqual(200, res.status_code)
|
|
api_mock.assert_called_once_with('build_name', build_name, None,
|
|
None, api.Session())
|
|
expected_response = {
|
|
six.text_type(timestamp_a.isoformat()): {
|
|
u'fake_test_a': {
|
|
u'pass': 1,
|
|
u'fail': 0,
|
|
u'skip': 0,
|
|
u'run_time': 1},
|
|
u'fake_test_b': {
|
|
u'pass': 0,
|
|
u'fail': 1,
|
|
u'skip': 0,
|
|
u'run_time': 0},
|
|
}
|
|
}
|
|
self.assertEqual({u'tests': expected_response},
|
|
json.loads(res.data.decode('utf-8')))
|
|
|
|
def test_get_test_runs_by_build_name(self):
|
|
self._test_get_test_runs_by_build_name('fake_tests')
|
|
|
|
def test_get_test_runs_by_build_name_with_forward_slash(self):
|
|
self._test_get_test_runs_by_build_name('fake/tests')
|
|
|
|
def test_list_routes(self):
|
|
res = self.app.get('/')
|
|
res_dict = json.loads(res.data.decode('utf-8'))
|
|
self.assertEqual(200, res.status_code)
|
|
self.assertEqual(['routes'], list(res_dict.keys()))
|
|
route_dict = {
|
|
'name': 'list_routes',
|
|
'methods': ['GET', 'HEAD', 'OPTIONS'],
|
|
'url': '/',
|
|
}
|
|
self.assertIn(route_dict, res_dict['routes'])
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_tests',
|
|
return_value=[models.Test(
|
|
id='fake_id', test_id='test.id', run_count=4, success=2,
|
|
failure=2, run_time=21.2)])
|
|
def test_get_tests(self, api_mock):
|
|
res = self.app.get('/tests')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = {'tests': [
|
|
{"failure": 2,
|
|
"id": "fake_id",
|
|
"run_count": 4,
|
|
"run_time": 21.2,
|
|
"success": 2,
|
|
"test_id": "test.id"}
|
|
]}
|
|
self.assertEqual(json.loads(res.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_test_prefixes',
|
|
return_value=['tempest', 'common', 'api'])
|
|
def test_get_test_prefixes(self, api_mock):
|
|
res = self.app.get('/tests/prefix')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = ['tempest', 'common', 'api']
|
|
self.assertEqual(json.loads(res.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_tests_by_prefix',
|
|
return_value=[models.Test(id='fake_id', test_id='prefix.test',
|
|
run_count=1, success=2, failure=2,
|
|
run_time=1.0)])
|
|
def test_get_tests_by_prefix(self, api_mock):
|
|
res = self.app.get('/tests/prefix/prefix')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = {'tests': [{
|
|
'id': 'fake_id',
|
|
'test_id': 'prefix.test',
|
|
'run_count': 1,
|
|
'success': 2,
|
|
'failure': 2,
|
|
'run_time': 1.0
|
|
}]}
|
|
|
|
self.assertEqual(json.loads(res.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_runs_by_date',
|
|
return_value=[models.Run(
|
|
id='fake_id', skips=2, fails=4, passes=2, run_time=21.2,
|
|
artifacts='fake_url.com',
|
|
run_at=timestamp_a)])
|
|
def test_get_runs(self, api_mock):
|
|
res = self.app.get('/runs')
|
|
self.assertEqual(200, res.status_code)
|
|
format_time = timestamp_a.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
expected_response = {'runs': [
|
|
{"fails": 4,
|
|
"id": "fake_id",
|
|
"skips": 2,
|
|
"run_time": 21.2,
|
|
"passes": 2,
|
|
"artifacts": "fake_url.com",
|
|
"run_at": format_time}
|
|
]}
|
|
self.assertEqual(json.loads(res.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_runs_by_key_value',
|
|
return_value=[models.Run(
|
|
id='fake_id', skips=2, fails=4, passes=2, run_time=21.2,
|
|
artifacts='fake_url.com',
|
|
run_at=timestamp_a)])
|
|
def test_get_runs_from_build_name(self, api_mock):
|
|
res = self.app.get('/build_name/test_build_name/runs')
|
|
self.assertEqual(200, res.status_code)
|
|
format_time = timestamp_a.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
expected_response = {'runs': [
|
|
{"fails": 4,
|
|
"id": "fake_id",
|
|
"skips": 2,
|
|
"run_time": 21.2,
|
|
"passes": 2,
|
|
"artifacts": "fake_url.com",
|
|
"run_at": format_time}
|
|
]}
|
|
self.assertEqual(json.loads(res.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_tests_from_run_id',
|
|
return_value=[models.Test(
|
|
id='fake_id', test_id='test.id', run_count=4, success=2,
|
|
failure=2, run_time=21.2)])
|
|
def test_get_tests_from_run(self, api_mock):
|
|
res = self.app.get('/run/fake_id/tests')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = {'tests': [
|
|
{"failure": 2,
|
|
"id": "fake_id",
|
|
"run_count": 4,
|
|
"run_time": 21.2,
|
|
"success": 2,
|
|
"test_id": "test.id"}
|
|
]}
|
|
self.assertEqual(json.loads(res.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_tests_run_dicts_from_run_id',
|
|
return_value={'tempest.api.compute.test_servers': {
|
|
'status': 'success',
|
|
'start_time': timestamp_a,
|
|
'stop_time': timestamp_b,
|
|
'metadata': {
|
|
'attrs': 'ab,cd',
|
|
'tags': 'worker-1',
|
|
}
|
|
}
|
|
})
|
|
def test_get_run_test_runs(self, api_mock):
|
|
res = self.app.get('/run/test_run_id/test_runs')
|
|
self.assertEqual(200, res.status_code)
|
|
format_time_a = timestamp_a.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
format_time_b = timestamp_b.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
expected_response = {u'tempest.api.compute.test_servers': {
|
|
u"status": u"success",
|
|
u"start_time": six.text_type(format_time_a),
|
|
u"stop_time": six.text_type(format_time_b),
|
|
u"metadata": {
|
|
u'attrs': u'ab,cd',
|
|
u'tags': u'worker-1',
|
|
}
|
|
}}
|
|
self.assertEqual(json.loads(res.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_runs_time_series_by_key',
|
|
return_value={
|
|
timestamp_a: [{'pass': 2, 'fail': 3, 'skip': 1}]
|
|
})
|
|
def test_get_runs_by_date_sec_res(self, api_mock):
|
|
res = self.app.get('/runs/group_by/project')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = {u'runs': {
|
|
six.text_type(timestamp_a.isoformat()): [{
|
|
u'pass': 2, u'fail': 3, u'skip': 1
|
|
}]
|
|
}}
|
|
self.assertEqual(expected_response,
|
|
json.loads(res.data.decode('utf-8')))
|
|
# Ensure db api called correctly
|
|
api_mock.assert_called_once_with('project', None, None, api.Session())
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_runs_time_series_by_key',
|
|
return_value={
|
|
timestamp_a: [{'pass': 2, 'fail': 3, 'skip': 1}]
|
|
})
|
|
def test_get_runs_by_date_min_res(self, api_mock):
|
|
res = self.app.get('/runs/group_by/project?datetime_resolution=min')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = {u'runs': {
|
|
six.text_type(timestamp_a.replace(second=0,
|
|
microsecond=0).isoformat()): [{
|
|
u'pass': 2,
|
|
u'fail': 3,
|
|
u'skip': 1}]
|
|
}}
|
|
self.assertEqual(expected_response,
|
|
json.loads(res.data.decode('utf-8')))
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_runs_time_series_by_key',
|
|
return_value={
|
|
timestamp_a: [{'pass': 2, 'fail': 3, 'skip': 1}]
|
|
})
|
|
def test_get_runs_by_date_hour_res(self, api_mock):
|
|
res = self.app.get('/runs/group_by/projects?datetime_resolution=hour')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = {u'runs': {
|
|
six.text_type(timestamp_a.replace(minute=0, second=0,
|
|
microsecond=0).isoformat()): [{
|
|
u'pass': 2,
|
|
u'fail': 3,
|
|
u'skip': 1}]
|
|
}}
|
|
self.assertEqual(expected_response,
|
|
json.loads(res.data.decode('utf-8')))
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_runs_time_series_by_key',
|
|
return_value={
|
|
timestamp_a: {'tempest': [{'pass': 2,
|
|
'fail': 3,
|
|
'skip': 1}],
|
|
'neutron': [{'pass': 2,
|
|
'fail': 0,
|
|
'skip': 1}]},
|
|
timestamp_b: {'neutron': [{'pass': 0,
|
|
'fail': 1,
|
|
'skip': 0}]}
|
|
})
|
|
def test_get_runs_by_date_day_res(self, api_mock):
|
|
res = self.app.get('runs/group_by/projects?datetime_resolution=day')
|
|
self.assertEqual(200, res.status_code)
|
|
date = six.text_type(timestamp_a.date().isoformat())
|
|
expected_response = {u'runs': {
|
|
date: {
|
|
u'neutron': sorted([{u'pass': 2,
|
|
u'fail': 0,
|
|
u'skip': 1},
|
|
{u'fail': 1,
|
|
u'pass': 0,
|
|
u'skip': 0}],
|
|
key=lambda run: run['fail']),
|
|
u'tempest': [{u'fail': 3,
|
|
u'pass': 2,
|
|
u'skip': 1}]}
|
|
}}
|
|
response = json.loads(res.data.decode('utf-8'))
|
|
self.assertEqual(sorted(expected_response['runs'].keys()),
|
|
sorted(response['runs'].keys()))
|
|
for project in expected_response['runs'][date]:
|
|
self.assertIn(project, response['runs'][date])
|
|
self.assertEqual(sorted(expected_response['runs'][date][project],
|
|
key=lambda run: run['fail']),
|
|
sorted(response['runs'][date][project],
|
|
key=lambda run: run['fail']))
|
|
|
|
def test_parse_datetimes_isoformat(self):
|
|
parsed_out = api._parse_datetimes(timestamp_a.isoformat())
|
|
self.assertEqual(timestamp_a, parsed_out)
|
|
|
|
def test_parse_datetimes_almost_isoformat(self):
|
|
format_str = timestamp_a.strftime('%Y-%m-%d %H:%M:%S')
|
|
parsed_out = api._parse_datetimes(format_str)
|
|
self.assertEqual(timestamp_a, parsed_out)
|
|
|
|
def test_parse_datetimes_no_input(self):
|
|
parsed_out = api._parse_datetimes('')
|
|
self.assertEqual('', parsed_out)
|
|
parsed_out = api._parse_datetimes(None)
|
|
self.assertIsNone(parsed_out)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_all_runs_time_series_by_key',
|
|
return_value={
|
|
timestamp_a: [{'pass': 2, 'fail': 3, 'skip': 1}]
|
|
})
|
|
def test_get_runs_by_date_explicit_sec_res(self, api_mock):
|
|
res = self.app.get('/runs/group_by/project?datetime_resolution=sec')
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response = {u'runs': {
|
|
six.text_type(timestamp_a.isoformat()): [{
|
|
u'pass': 2, u'fail': 3, u'skip': 1
|
|
}]
|
|
}}
|
|
self.assertEqual(expected_response,
|
|
json.loads(res.data.decode('utf-8')))
|
|
# Ensure db api called correctly
|
|
api_mock.assert_called_once_with('project', None, None, api.Session())
|
|
|
|
def test_get_runs_by_date_invalid_resolution(self):
|
|
res = self.app.get(
|
|
'/runs/group_by/projects?datetime_resolution=century')
|
|
self.assertEqual(res.status_code, 400)
|
|
self.assertEqual('Datetime resolution: century, is not a valid choice',
|
|
res.data.decode('utf-8'))
|
|
|
|
@mock.patch('subunit2sql.db.api.get_time_series_runs_by_key_value',
|
|
return_value={
|
|
timestamp_s1: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 1.0,
|
|
'metadata': {'build_name': 'value-1'}}],
|
|
timestamp_s2: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc2',
|
|
'run_time': 2.0,
|
|
'metadata': {'build_name': 'value-2'}}],
|
|
timestamp_s3: [{'pass': 1,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc3',
|
|
'run_time': 3.0,
|
|
'metadata': {'build_name': 'value-3'}}],
|
|
})
|
|
def test_get_runs_by_project_resolution_sec(self, api_mock):
|
|
query = 'datetime_resolution=sec'
|
|
res = self.app.get('/runs/key/project/openstack/trove?{0}'
|
|
.format(query))
|
|
|
|
self.assertEqual(200, res.status_code)
|
|
|
|
expected_response_data = {u'data': {u'timedelta': [
|
|
{u'datetime': u'%s' % timestamp_s1.isoformat(),
|
|
u'job_data': [{u'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': u'value-1',
|
|
u'mean_run_time': 1.0}]},
|
|
{u'datetime': u'%s' % timestamp_s2.isoformat(),
|
|
u'job_data': [{u'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': u'value-2',
|
|
u'mean_run_time': 2.0}]},
|
|
{u'datetime': u'%s' % timestamp_s3.isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 0,
|
|
u'job_name': u'value-3',
|
|
u'mean_run_time': 3.0}]},
|
|
]}, u'numeric': {u'value-3': {u'%s' % timestamp_s3.isoformat(): 3.0}}}
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
|
|
self.assertEqual(expected_response_data, response_data)
|
|
api_mock.assert_called_once_with('project',
|
|
'openstack/trove',
|
|
None,
|
|
None,
|
|
api.Session())
|
|
|
|
@mock.patch('subunit2sql.db.api.get_time_series_runs_by_key_value',
|
|
return_value={
|
|
timestamp_m1: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 1.0,
|
|
'metadata': {'build_name': 'value-1'}}],
|
|
timestamp_m2: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc2',
|
|
'run_time': 2.0,
|
|
'metadata': {'build_name': 'value-2'}}],
|
|
timestamp_m3: [{'pass': 1,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc3',
|
|
'run_time': 3.0,
|
|
'metadata': {'build_name': 'value-3'}}]
|
|
})
|
|
def test_get_runs_by_project_resolution_min(self, api_mock):
|
|
query = 'datetime_resolution=min'
|
|
res = self.app.get('/runs/key/project/openstack/trove?{0}'
|
|
.format(query))
|
|
|
|
self.assertEqual(200, res.status_code)
|
|
|
|
expected_response_data = {u'data': {u'timedelta': [
|
|
{u'datetime': u'%s' % timestamp_m1.isoformat(),
|
|
u'job_data': [{u'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': u'value-1',
|
|
u'mean_run_time': 1.0}]},
|
|
{u'datetime': u'%s' % timestamp_m2.isoformat(),
|
|
u'job_data': [{'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': u'value-2',
|
|
u'mean_run_time': 2.0}]},
|
|
{u'datetime': u'%s' % timestamp_m3.isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 0,
|
|
u'job_name': u'value-3',
|
|
u'mean_run_time': 3.0}]},
|
|
]}, u'numeric': {u'value-3': {u'%s' % timestamp_m3.isoformat(): 3.0}}}
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
|
|
self.assertEqual(expected_response_data, response_data)
|
|
api_mock.assert_called_once_with('project',
|
|
'openstack/trove',
|
|
None,
|
|
None,
|
|
api.Session())
|
|
|
|
@mock.patch('subunit2sql.db.api.get_time_series_runs_by_key_value',
|
|
return_value={
|
|
timestamp_h1: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 1.0,
|
|
'metadata': {'build_name': 'value-1'}}],
|
|
timestamp_h2: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc2',
|
|
'run_time': 2.0,
|
|
'metadata': {'build_name': 'value-2'}}],
|
|
timestamp_h3: [{'pass': 1,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc3',
|
|
'run_time': 3.0,
|
|
'metadata': {'build_name': 'value-3'}}]
|
|
})
|
|
def test_get_runs_by_project_resolution_hour(self, api_mock):
|
|
query = 'datetime_resolution=hour'
|
|
res = self.app.get('/runs/key/project/openstack/trove?{0}'
|
|
.format(query))
|
|
|
|
self.assertEqual(200, res.status_code)
|
|
|
|
expected_response_data = {u'data': {u'timedelta': [
|
|
{u'datetime': u'%s' % timestamp_h1.isoformat(),
|
|
u'job_data': [{u'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': u'value-1',
|
|
u'mean_run_time': 1.0}]},
|
|
{u'datetime': u'%s' % timestamp_h2.isoformat(),
|
|
u'job_data': [{u'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': u'value-2',
|
|
u'mean_run_time': 2.0}]},
|
|
{u'datetime': u'%s' % timestamp_h3.isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 0,
|
|
u'job_name': u'value-3',
|
|
u'mean_run_time': 3.0}]},
|
|
]}, u'numeric': {u'value-3': {u'%s' % timestamp_h3.isoformat(): 3.0}}}
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
|
|
self.assertEqual(expected_response_data, response_data)
|
|
api_mock.assert_called_once_with('project',
|
|
'openstack/trove',
|
|
None,
|
|
None,
|
|
api.Session())
|
|
|
|
@mock.patch('subunit2sql.db.api.get_time_series_runs_by_key_value',
|
|
return_value={
|
|
timestamp_d1: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 1.0,
|
|
'metadata': {'build_name': 'value-1'}},
|
|
{'pass': 10,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 9.0,
|
|
'metadata': {'build_name': 'value-1'}},
|
|
{'pass': 2,
|
|
'fail': 2,
|
|
'skip': 0,
|
|
'id': 'abc2',
|
|
'run_time': 2.0,
|
|
'metadata': {'build_name': 'value-2'}}],
|
|
timestamp_d2: [{'pass': 100,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc3',
|
|
'run_time': 20.0,
|
|
'metadata': {'build_name': 'value-3'}}]
|
|
})
|
|
def test_get_runs_by_project_resolution_day(self, api_mock):
|
|
query = 'datetime_resolution=day'
|
|
res = self.app.get('/runs/key/project/openstack/trove?{0}'
|
|
.format(query))
|
|
|
|
self.assertEqual(200, res.status_code)
|
|
|
|
expected_response_data = {u'data': {u'timedelta': [
|
|
{u'datetime': u'%s' % timestamp_d1.date().isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 1,
|
|
u'job_name': u'value-1',
|
|
u'mean_run_time': 5.0},
|
|
{u'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': 'value-2',
|
|
u'mean_run_time': 2.0}]},
|
|
{u'datetime': u'%s' % timestamp_d2.date().isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 0,
|
|
u'job_name': u'value-3',
|
|
u'mean_run_time': 20.0}]}
|
|
]}, u'numeric': {u'value-1': {u'%s' % timestamp_d1.isoformat(): 9.0,
|
|
u'%s' % timestamp_d2.isoformat():
|
|
numpy.NaN},
|
|
u'value-3': {u'%s' % timestamp_d1.isoformat():
|
|
numpy.NaN,
|
|
u'%s' % timestamp_d2.isoformat():
|
|
20.0}}}
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
# numpy.NaN == numpy.NaN result is False, a key error here means the
|
|
# dicts are not equal
|
|
for project, item in list(expected_response_data['numeric'].items()):
|
|
for date, run_time in list(item.items()):
|
|
if (numpy.isnan(run_time) and
|
|
numpy.isnan(response_data['numeric'][project][date])):
|
|
del expected_response_data['numeric'][project][date]
|
|
del response_data['numeric'][project][date]
|
|
self.assertEqual(expected_response_data, response_data)
|
|
api_mock.assert_called_once_with('project',
|
|
'openstack/trove',
|
|
None,
|
|
None,
|
|
api.Session())
|
|
|
|
@mock.patch('subunit2sql.db.api.get_time_series_runs_by_key_value',
|
|
return_value={
|
|
timestamp_d1: [{'pass': 1,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 1.0,
|
|
'metadata': {'build_name': 'value-1'}},
|
|
{'pass': 10,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 9.0,
|
|
'metadata': {'build_name': 'value-1'}},
|
|
{'pass': 2,
|
|
'fail': 2,
|
|
'skip': 0,
|
|
'id': 'abc2',
|
|
'run_time': 2.0,
|
|
'metadata': {'build_name': 'value-2'}}],
|
|
timestamp_d2: [{'pass': 100,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc3',
|
|
'run_time': 20.0,
|
|
'metadata': {'build_name': 'value-3'}}]
|
|
})
|
|
def test_get_runs_by_project_by_start_and_end_date(self, api_mock):
|
|
start_date = timestamp_d1.date().isoformat()
|
|
stop_date = timestamp_d2.date().isoformat()
|
|
query = ('datetime_resolution=day&start_date={0}&stop_date={1}'
|
|
.format(start_date, stop_date))
|
|
res = self.app.get('/runs/key/project/openstack/trove?{0}'
|
|
.format(query))
|
|
|
|
self.assertEqual(200, res.status_code)
|
|
|
|
expected_response_data = {u'data': {u'timedelta': [
|
|
{u'datetime': u'%s' % timestamp_d1.date().isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 1,
|
|
u'job_name': u'value-1',
|
|
u'mean_run_time': 5.0},
|
|
{u'pass': 0,
|
|
u'fail': 1,
|
|
u'job_name': u'value-2',
|
|
u'mean_run_time': 2.0}]},
|
|
{u'datetime': u'%s' % timestamp_d2.date().isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 0,
|
|
u'job_name': u'value-3',
|
|
u'mean_run_time': 20.0}]}
|
|
]}, u'numeric': {u'value-1': {u'%s' % timestamp_d1.isoformat(): 9.0,
|
|
u'%s' % timestamp_d2.isoformat():
|
|
numpy.NaN},
|
|
u'value-3': {u'%s' % timestamp_d1.isoformat():
|
|
numpy.NaN,
|
|
u'%s' % timestamp_d2.isoformat(): 20.0}}}
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
# numpy.NaN == numpy.NaN result is False, a key error here means the
|
|
# dicts are not equal
|
|
for project, item in expected_response_data['numeric'].items():
|
|
for date, run_time in list(item.items()):
|
|
if (numpy.isnan(run_time) and
|
|
numpy.isnan(response_data['numeric'][project][date])):
|
|
del expected_response_data['numeric'][project][date]
|
|
del response_data['numeric'][project][date]
|
|
self.assertEqual(expected_response_data, response_data)
|
|
api_mock.assert_called_once_with('project',
|
|
'openstack/trove',
|
|
timestamp_d1,
|
|
timestamp_d2,
|
|
api.Session())
|
|
|
|
def test_get_runs_by_project_invalid_resolution(self):
|
|
res = self.app.get(
|
|
'/runs/key/project/openstack/trove?datetime_resolution=century')
|
|
self.assertEqual(res.status_code, 400)
|
|
self.assertEqual('Datetime resolution: century, is not a valid choice',
|
|
res.data.decode('utf-8'))
|
|
|
|
@mock.patch('subunit2sql.db.api.get_time_series_runs_by_key_value',
|
|
return_value={
|
|
timestamp_d1: [{'pass': 1,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 4.0,
|
|
'metadata': {
|
|
'build_name':
|
|
'tempest-dsvm-neutron-full'}},
|
|
{'pass': 10,
|
|
'fail': 1,
|
|
'skip': 0,
|
|
'id': 'abc1',
|
|
'run_time': 9.0,
|
|
'metadata': {
|
|
'build_name':
|
|
'tempest-dsvm-neutron-full'}},
|
|
{'pass': 2,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc2',
|
|
'run_time': 2.0,
|
|
'metadata': {
|
|
'build_name':
|
|
'tempest-dsvm-neutron-full'}}],
|
|
timestamp_d2: [{'pass': 100,
|
|
'fail': 0,
|
|
'skip': 0,
|
|
'id': 'abc3',
|
|
'run_time': 20.0,
|
|
'metadata': {
|
|
'build_name':
|
|
'tempest-dsvm-neutron-full'}}]
|
|
})
|
|
def test_get_runs_by_build_name_and_same_run_at_times(self, api_mock):
|
|
start_date = timestamp_d1.date().isoformat()
|
|
stop_date = timestamp_d2.date().isoformat()
|
|
query = ('datetime_resolution=day&start_date={0}&stop_date={1}'
|
|
.format(start_date, stop_date))
|
|
res = self.app.get('/runs/key/build_name/tempest-dsvm-neutron-full?{0}'
|
|
.format(query))
|
|
self.assertEqual(200, res.status_code)
|
|
expected_response_data = {u'data': {u'timedelta': [
|
|
{u'datetime': u'%s' % timestamp_d1.date().isoformat(),
|
|
u'job_data': [{u'pass': 2,
|
|
u'fail': 1,
|
|
u'job_name': u'tempest-dsvm-neutron-full',
|
|
u'mean_run_time': 5.0}]},
|
|
{u'datetime': u'%s' % timestamp_d2.date().isoformat(),
|
|
u'job_data': [{u'pass': 1,
|
|
u'fail': 0,
|
|
u'job_name': u'tempest-dsvm-neutron-full',
|
|
u'mean_run_time': 20.0}]}]},
|
|
u'numeric': {
|
|
u'tempest-dsvm-neutron-full': {
|
|
u'%s' % timestamp_d1.isoformat(): 3.0,
|
|
u'%s' % timestamp_d2.isoformat(): 20.0}}}
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
self.maxDiff = None
|
|
self.assertDictEqual(expected_response_data, response_data)
|
|
|
|
@mock.patch('openstack_health.api._check_db_availability',
|
|
return_value=False)
|
|
@mock.patch('openstack_health.api._check_er_availability',
|
|
return_value='NotInstalled')
|
|
def test_get_status_failure_er_not_installed(self, status_check_mock,
|
|
er_mock):
|
|
expected_response = {'status': {
|
|
'availability': {
|
|
'database': False,
|
|
'elastic-recheck': 'NotInstalled'
|
|
},
|
|
'version': version.VersionInfo(
|
|
'openstack_health').version_string_with_vcs()
|
|
}}
|
|
response = self.app.get('/status')
|
|
self.assertEqual(response.status_code, 500)
|
|
self.assertEqual(json.loads(response.data.decode('utf-8')),
|
|
expected_response)
|
|
|
|
@mock.patch('openstack_health.api._check_db_availability',
|
|
return_value=True)
|
|
@mock.patch('openstack_health.api._check_er_availability',
|
|
return_value='NotConfigured')
|
|
def test_get_db_status_success_er_not_configured(self, status_check_mock,
|
|
er_mock):
|
|
expected_response = {'status': {
|
|
'availability': {
|
|
'database': True,
|
|
'elastic-recheck': 'NotConfigured'
|
|
},
|
|
'version': version.VersionInfo(
|
|
'openstack_health').version_string_with_vcs()
|
|
}}
|
|
response = self.app.get('/status')
|
|
self.assertEqual(response.status_code, 200)
|
|
output = json.loads(response.data.decode('utf-8'))
|
|
self.assertEqual(output, expected_response)
|
|
|
|
@mock.patch('openstack_health.api._check_db_availability',
|
|
return_value=True)
|
|
@mock.patch('openstack_health.api._check_er_availability',
|
|
return_value={'Configured': {'elastic-search': 'green'}})
|
|
def test_get_db_status_success_er_green(self, status_check_mock, er_mock):
|
|
expected_response = {'status': {
|
|
'availability': {
|
|
'database': True,
|
|
'elastic-recheck': {'Configured': {'elastic-search': 'green'}}
|
|
},
|
|
'version': version.VersionInfo(
|
|
'openstack_health').version_string_with_vcs()
|
|
}}
|
|
response = self.app.get('/status')
|
|
self.assertEqual(response.status_code, 200)
|
|
output = json.loads(response.data.decode('utf-8'))
|
|
self.assertEqual(output, expected_response)
|
|
|
|
def test_failed_runs_count_should_not_consider_unknown_ones(self):
|
|
runs = [
|
|
{'fail': 1, 'pass': 0},
|
|
{'fail': 0, 'pass': 0}
|
|
]
|
|
failed_runs = api._calc_amount_of_failed_runs(runs)
|
|
self.assertEqual(failed_runs, 1)
|
|
|
|
def test_successful_runs_count_should_not_consider_unknown_ones(self):
|
|
runs = [
|
|
{'fail': 0, 'pass': 1},
|
|
{'fail': 0, 'pass': 0}
|
|
]
|
|
successful_runs = api._calc_amount_of_successful_runs(runs)
|
|
self.assertEqual(successful_runs, 1)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_test_runs_by_test_test_id',
|
|
return_value=[models.TestRun(
|
|
id=2, test_id=1234, run_id=int(1234),
|
|
status='success', start_time=timestamp_a,
|
|
start_time_microsecond=0,
|
|
stop_time=timestamp_b,
|
|
stop_time_microsecond=0)])
|
|
def test_get_test_runs_for_test(self, api_mock):
|
|
setup_mock = mock.patch('openstack_health.api.setup')
|
|
setup_mock.start()
|
|
self.addCleanup(setup_mock.stop)
|
|
api.classifier = None
|
|
api.region = mock.MagicMock()
|
|
api.region.cache_on_arguments = mock.MagicMock()
|
|
api.region.cache_on_arguments.return_value = lambda x: x
|
|
res = self.app.get('/test_runs/fake.test.id')
|
|
self.assertEqual(200, res.status_code)
|
|
exp_result = {'data': {
|
|
timestamp_a.isoformat(): {
|
|
'run_id': 1234,
|
|
'status': 'success',
|
|
}}, 'numeric': {
|
|
timestamp_a.isoformat(): {
|
|
'avg_run_time': numpy.NaN,
|
|
'run_time': 1.0,
|
|
'std_dev_run_time': numpy.NaN
|
|
}}, 'failed_runs': []
|
|
}
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
numpy.testing.assert_equal(exp_result, response_data)
|
|
api_mock.assert_called_once_with('fake.test.id', start_date=None,
|
|
stop_date=None,
|
|
session=api.Session())
|
|
|
|
@mock.patch('subunit2sql.db.api.get_run_metadata',
|
|
return_value=[models.RunMetadata(key='build_name',
|
|
value='job')])
|
|
@mock.patch('subunit2sql.db.api.get_recent_runs_by_key_value_metadata',
|
|
return_value=[
|
|
models.Run(uuid='a_uuid', run_at=timestamp_a,
|
|
artifacts='http://fake_url', passes=2, fails=0),
|
|
models.Run(uuid='b_uuid', run_at=timestamp_b,
|
|
artifacts='http://less_fake_url', fails=1,
|
|
passes=42)
|
|
])
|
|
def test_get_recent_runs(self, api_mock, api_meta_mock):
|
|
res = self.app.get('/runs/key/a_key/a_value/recent')
|
|
self.assertEqual(200, res.status_code)
|
|
api_mock.assert_called_once_with('a_key', 'a_value',
|
|
10, api.Session())
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
expected_res = [{
|
|
u'id': u'a_uuid',
|
|
u'build_name': u'job',
|
|
u'start_date': timestamp_a.isoformat(),
|
|
u'link': u'http://fake_url',
|
|
u'status': 'success'
|
|
}, {
|
|
u'id': u'b_uuid',
|
|
u'build_name': u'job',
|
|
u'start_date': timestamp_b.isoformat(),
|
|
u'link': u'http://less_fake_url',
|
|
u'status': 'fail'
|
|
}]
|
|
self.assertEqual(expected_res, response_data)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_run_metadata',
|
|
return_value=[models.RunMetadata(key='build_name',
|
|
value='job')])
|
|
@mock.patch('subunit2sql.db.api.get_recent_runs_by_key_value_metadata',
|
|
return_value=[
|
|
models.Run(uuid='uuid', run_at=timestamp_a,
|
|
artifacts='http://fake_url', passes=2, fails=0,
|
|
id='a_id', run_time=174, skips=10),
|
|
])
|
|
def test_get_recent_runs_detail(self, api_mock, api_meta_mock):
|
|
api.Session = mock.MagicMock()
|
|
res = self.app.get('/runs/key/a_key/a_value/recent/detail')
|
|
self.assertEqual(200, res.status_code)
|
|
api_mock.assert_called_once_with('a_key', 'a_value',
|
|
10, api.Session())
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
format_time = timestamp_a.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
expected_res = [{
|
|
u'artifacts': u'http://fake_url',
|
|
u'id': u'a_id',
|
|
u'build_name': u'job',
|
|
u'fails': 0,
|
|
u'passes': 2,
|
|
u'skips': 10,
|
|
u'run_at': format_time,
|
|
u'run_time': 174,
|
|
u'uuid': u'uuid'
|
|
}]
|
|
self.assertEqual(expected_res, response_data)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_recent_failed_runs',
|
|
return_value=['a_convincing_id'])
|
|
@mock.patch('subunit2sql.db.api.get_test_runs_by_status_for_run_ids',
|
|
return_value=[
|
|
{
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a,
|
|
'stop_time': timestamp_b,
|
|
'uuid': 'a_convincing_id',
|
|
}
|
|
])
|
|
@mock.patch('subunit2sql.db.api.get_run_metadata',
|
|
return_value=[
|
|
models.RunMetadata(key='build_short_uuid', value='abcd'),
|
|
models.RunMetadata(key='build_change', value='1234'),
|
|
models.RunMetadata(key='build_patchset', value='3'),
|
|
models.RunMetadata(key='build_name', value='job_name_foo'),
|
|
])
|
|
def test_get_recent_test_failures_no_es(self, meta_mock,
|
|
db_mock, recent_mock):
|
|
setup_mock = mock.patch('openstack_health.api.setup')
|
|
setup_mock.start()
|
|
self.addCleanup(setup_mock.stop)
|
|
api.classifier = None
|
|
api.region = mock.MagicMock()
|
|
api.region.cache_on_arguments = mock.MagicMock()
|
|
api.region.cache_on_arguments.return_value = lambda x: x
|
|
res = self.app.get('/tests/recent/fail')
|
|
self.assertEqual(200, res.status_code)
|
|
db_mock.assert_called_once_with('fail', ['a_convincing_id'],
|
|
session=api.Session(),
|
|
include_run_id=True)
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
expected_resp = {
|
|
'bugs': {},
|
|
'test_runs': [{
|
|
'job_name': 'job_name_foo',
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a.isoformat(),
|
|
'stop_time': timestamp_b.isoformat(),
|
|
'uuid': 'a_convincing_id',
|
|
}]}
|
|
self.assertEqual(expected_resp, response_data)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_recent_failed_runs',
|
|
return_value=['a_convincing_id'])
|
|
@mock.patch('subunit2sql.db.api.get_test_runs_by_status_for_run_ids',
|
|
return_value=[
|
|
{
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a,
|
|
'stop_time': timestamp_b,
|
|
'uuid': 'a_convincing_id',
|
|
}
|
|
])
|
|
@mock.patch('subunit2sql.db.api.get_run_metadata',
|
|
return_value=[
|
|
models.RunMetadata(key='build_short_uuid', value='abcd'),
|
|
models.RunMetadata(key='build_change', value='1234'),
|
|
models.RunMetadata(key='build_patchset', value='3'),
|
|
models.RunMetadata(key='build_name', value='fake_job'),
|
|
])
|
|
def test_get_recent_test_failures_with_es(self, meta_mock, db_mock,
|
|
recent_mock):
|
|
setup_mock = mock.patch('openstack_health.api.setup')
|
|
setup_mock.start()
|
|
self.addCleanup(setup_mock.stop)
|
|
api.region = mock.MagicMock()
|
|
api.region.cache_on_arguments = mock.MagicMock()
|
|
api.region.cache_on_arguments.return_value = lambda x: x
|
|
api.classifier = mock.MagicMock()
|
|
api.classifier.classify = mock.MagicMock()
|
|
api.classifier.classify.return_value = ['12345']
|
|
res = self.app.get('/tests/recent/fail')
|
|
self.assertEqual(200, res.status_code)
|
|
db_mock.assert_called_once_with('fail', ['a_convincing_id'],
|
|
session=api.Session(),
|
|
include_run_id=True)
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
expected_resp = {
|
|
'bugs': {'a_convincing_id': ['12345']},
|
|
'test_runs': [{
|
|
'job_name': 'fake_job',
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a.isoformat(),
|
|
'stop_time': timestamp_b.isoformat(),
|
|
'uuid': 'a_convincing_id',
|
|
}]}
|
|
self.assertEqual(expected_resp, response_data)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_recent_failed_runs',
|
|
return_value=['a_convincing_id'])
|
|
@mock.patch('subunit2sql.db.api.get_test_runs_by_status_for_run_ids',
|
|
return_value=[
|
|
{
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a,
|
|
'stop_time': timestamp_b,
|
|
'uuid': 'a_convincing_id',
|
|
}
|
|
])
|
|
@mock.patch('subunit2sql.db.api.get_run_metadata',
|
|
return_value=[
|
|
models.RunMetadata(key='build_short_uuid', value='abcd'),
|
|
models.RunMetadata(key='build_patchset', value='3'),
|
|
models.RunMetadata(key='build_name', value='job_name_foo'),
|
|
])
|
|
def test_get_recent_test_failures_with_missing_meta(self, meta_mock,
|
|
db_mock, recent_mock):
|
|
setup_mock = mock.patch('openstack_health.api.setup')
|
|
setup_mock.start()
|
|
self.addCleanup(setup_mock.stop)
|
|
api.region = mock.MagicMock()
|
|
api.region.cache_on_arguments = mock.MagicMock()
|
|
api.region.cache_on_arguments.return_value = lambda x: x
|
|
api.classifier = mock.MagicMock()
|
|
api.classifier.classify = mock.MagicMock()
|
|
api.classifier.classify.return_value = ['12345']
|
|
res = self.app.get('/tests/recent/fail')
|
|
self.assertEqual(200, res.status_code)
|
|
self.assertEqual(0, api.classifier.classify.call_count)
|
|
db_mock.assert_called_once_with('fail', ['a_convincing_id'],
|
|
session=api.Session(),
|
|
include_run_id=True)
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
expected_resp = {
|
|
'bugs': {},
|
|
'test_runs': [{
|
|
'job_name': 'job_name_foo',
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a.isoformat(),
|
|
'stop_time': timestamp_b.isoformat(),
|
|
'uuid': 'a_convincing_id',
|
|
}]}
|
|
self.assertEqual(expected_resp, response_data)
|
|
|
|
@mock.patch('subunit2sql.db.api.get_recent_failed_runs',
|
|
return_value=['a_convincing_id'])
|
|
@mock.patch('subunit2sql.db.api.get_test_runs_by_status_for_run_ids',
|
|
return_value=[
|
|
{
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a,
|
|
'stop_time': timestamp_b,
|
|
'uuid': 'a_convincing_id',
|
|
}
|
|
])
|
|
@mock.patch('subunit2sql.db.api.get_run_metadata',
|
|
return_value=[
|
|
models.RunMetadata(key='build_short_uuid', value='abcd'),
|
|
models.RunMetadata(key='build_patchset', value='3'),
|
|
])
|
|
def test_get_recent_test_failures_with_missing_build_name(self, meta_mock,
|
|
db_mock,
|
|
recent_mock):
|
|
setup_mock = mock.patch('openstack_health.api.setup')
|
|
setup_mock.start()
|
|
self.addCleanup(setup_mock.stop)
|
|
api.region = mock.MagicMock()
|
|
api.region.cache_on_arguments = mock.MagicMock()
|
|
api.region.cache_on_arguments.return_value = lambda x: x
|
|
api.classifier = mock.MagicMock()
|
|
api.classifier.classify = mock.MagicMock()
|
|
api.classifier.classify.return_value = ['12345']
|
|
res = self.app.get('/tests/recent/fail')
|
|
self.assertEqual(200, res.status_code)
|
|
self.assertEqual(0, api.classifier.classify.call_count)
|
|
db_mock.assert_called_once_with('fail', ['a_convincing_id'],
|
|
session=api.Session(),
|
|
include_run_id=True)
|
|
response_data = json.loads(res.data.decode('utf-8'))
|
|
expected_resp = {
|
|
'bugs': {},
|
|
'test_runs': [{
|
|
'job_name': None,
|
|
'test_id': u'fake_test',
|
|
'link': u'fake_url',
|
|
'start_time': timestamp_a.isoformat(),
|
|
'stop_time': timestamp_b.isoformat(),
|
|
'uuid': 'a_convincing_id',
|
|
}]}
|
|
self.assertEqual(expected_resp, response_data)
|
|
|
|
def test__gen_feed(self):
|
|
url = 'fake_url'
|
|
key = 'zeon'
|
|
value = 'zaku'
|
|
fg = api._gen_feed(url, key, value)
|
|
res = feedparser.parse(fg.rss_str())
|
|
title = 'Failures for %s: %s' % (key, value)
|
|
self.assertEqual(title, res['feed']['title'])
|
|
self.assertEqual(url, res['feed']['link'])
|
|
self.assertEqual('en', res['feed']['language'])
|
|
|
|
@mock.patch('subunit2sql.db.api.get_recent_failed_runs_by_run_metadata',
|
|
return_value=[
|
|
models.Run(uuid='a_uuid', run_at=timestamp_b,
|
|
artifacts='http://less_fake_url', fails=1,
|
|
passes=42)
|
|
])
|
|
def test_get_recent_failed_runs_rss_no_previous(self, db_mock):
|
|
api.rss_opts['data_dir'] = tempfile.gettempdir()
|
|
api.rss_opts['frontend_url'] = 'http://status.openstack.org'
|
|
build_uuid = str(uuid.uuid4())
|
|
meta_mock = mock.patch(
|
|
'subunit2sql.db.api.get_run_metadata',
|
|
return_value=[
|
|
models.RunMetadata(key='build_name', value='job'),
|
|
models.RunMetadata(key='build_uuid', value=build_uuid)])
|
|
meta_mock.start()
|
|
self.addCleanup(meta_mock.stop)
|
|
res = self.app.get('/runs/key/a_key/a_value/recent/rss')
|
|
self.assertEqual(200, res.status_code)
|
|
db_mock.assert_called_once_with('a_key', 'a_value',
|
|
start_date=None, session=api.Session())
|
|
out = feedparser.parse(res.data.decode('utf-8'))
|
|
title = 'Failures for %s: %s' % ('a_key', 'a_value')
|
|
self.assertEqual(title, out['feed']['title'])
|
|
self.assertEqual('en', out['feed']['language'])
|
|
self.assertEqual(1, len(out.entries))
|
|
self.assertEqual(build_uuid, out.entries[0].id)
|
|
self.assertEqual(timestamp_b,
|
|
date_parser.parse(out.entries[0].published).replace(
|
|
tzinfo=None))
|
|
|
|
@mock.patch('subunit2sql.db.api.get_recent_failed_runs_by_run_metadata',
|
|
return_value=[
|
|
models.Run(uuid='b_uuid', run_at=timestamp_b,
|
|
artifacts='http://less_fake_url', fails=1,
|
|
passes=42)
|
|
])
|
|
def test_get_recent_failed_runs_rss_with_previous(self, db_mock):
|
|
api.rss_opts['data_dir'] = tempfile.gettempdir()
|
|
api.rss_opts['frontend_url'] = 'http://status.openstack.org'
|
|
build_uuid = str(uuid.uuid4())
|
|
meta_mock = mock.patch(
|
|
'subunit2sql.db.api.get_run_metadata',
|
|
return_value=[
|
|
models.RunMetadata(key='build_name', value='job'),
|
|
models.RunMetadata(key='build_uuid', value=build_uuid)])
|
|
meta_mock.start()
|
|
self.addCleanup(meta_mock.stop)
|
|
res = self.app.get('/runs/key/b_key/b_value/recent/rss')
|
|
self.assertEqual(200, res.status_code)
|
|
db_mock.assert_called_once_with('b_key', 'b_value',
|
|
start_date=None, session=api.Session())
|
|
db_mock.reset_mock()
|
|
res = self.app.get('/runs/key/b_key/b_value/recent/rss')
|
|
db_mock.assert_called_once_with('b_key', 'b_value',
|
|
start_date=timestamp_b,
|
|
session=api.Session())
|
|
out = feedparser.parse(res.data.decode('utf-8'))
|
|
title = 'Failures for %s: %s' % ('b_key', 'b_value')
|
|
self.assertEqual(title, out['feed']['title'])
|
|
self.assertEqual('en', out['feed']['language'])
|
|
self.assertEqual(1, len(out.entries))
|
|
self.assertEqual(build_uuid, out.entries[0].id)
|
|
self.assertEqual(timestamp_b,
|
|
date_parser.parse(out.entries[0].published).replace(
|
|
tzinfo=None))
|
|
|
|
@mock.patch('subunit2sql.db.api.get_recent_failed_runs_by_run_metadata',
|
|
return_value=[])
|
|
@mock.patch('subunit2sql.db.api.get_runs_counts_by_run_metadata',
|
|
return_value=1)
|
|
def test_get_recent_failed_runs_rss_no_failure_valid_meta(self, count_mock,
|
|
db_mock):
|
|
api.rss_opts['data_dir'] = tempfile.gettempdir()
|
|
api.rss_opts['frontend_url'] = 'http://status.openstack.org'
|
|
build_uuid = str(uuid.uuid4())
|
|
meta_mock = mock.patch(
|
|
'subunit2sql.db.api.get_run_metadata',
|
|
return_value=[
|
|
models.RunMetadata(key='build_name', value='job'),
|
|
models.RunMetadata(key='build_uuid', value=build_uuid)])
|
|
meta_mock.start()
|
|
self.addCleanup(meta_mock.stop)
|
|
res = self.app.get('/runs/key/b_key/b_value/recent/rss')
|
|
self.assertEqual(200, res.status_code)
|
|
db_mock.assert_called_once_with('b_key', 'b_value',
|
|
start_date=None, session=api.Session())
|
|
count_mock.assert_called_once_with('b_key', 'b_value',
|
|
session=api.Session())
|
|
db_mock.reset_mock()
|
|
count_mock.reset_mock()
|
|
out = feedparser.parse(res.data.decode('utf-8'))
|
|
title = 'Failures for %s: %s' % ('b_key', 'b_value')
|
|
self.assertEqual(title, out['feed']['title'])
|
|
self.assertEqual('en', out['feed']['language'])
|
|
self.assertEqual(0, len(out.entries))
|