Use pytest for queries

Switches queries testing to use of pytest which provides the following:
- test generator for each query (parametrize)
- ability to test a single query test
- generate html report with test results, making easier to investigate
  failures.
- parallel executions
- minor bugfix which prevented running queries from running with py38
  as the  config parser requires only strings (None being invalid).

Change-Id: I982c694a5160a9ecfd117d177d30b911cfe53425
This commit is contained in:
Sorin Sbarnea 2020-09-08 16:36:01 +01:00 committed by Sorin Sbarnea (zbr)
parent df62575028
commit c6f07d7f93
4 changed files with 82 additions and 59 deletions

View File

@ -106,7 +106,7 @@ class Config(object):
{'es_url': ES_URL,
'ls_url': LS_URL,
'db_uri': DB_URI,
'server_password': None,
'server_password': '',
'ci_username': CI_USERNAME,
'jobs_re': JOBS_RE,
'pidfile': PID_FN,

View File

@ -11,82 +11,48 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from functools import lru_cache
import os
import pytest
import logging
from launchpadlib import launchpad
import pyelasticsearch
import elastic_recheck.config as er_conf
from elastic_recheck import elasticRecheck
import elastic_recheck.query_builder as qb
from elastic_recheck import tests
# from elastic_recheck import tests
LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
LOGGER = logging.getLogger(__name__)
class TestQueries(tests.TestCase):
"""Make sure queries are valid.
class Context():
Make sure all queries are for open OpenStack bugs in Launchpad or have
hits in logstash.openstack.org.
This test is used to validate any changes to queries.yaml
"""
def setUp(self):
super(TestQueries, self).setUp()
@lru_cache()
def __init__(self):
config = er_conf.Config(config_file='elasticRecheck.conf')
self.classifier = elasticRecheck.Classifier(config.gerrit_query_file,
config=config)
self.classifier = elasticRecheck.Classifier(
config.gerrit_query_file,
config=config)
self.lp = launchpad.Launchpad.login_anonymously(
'grabbing bugs',
'production',
LPCACHEDIR)
self.lp = launchpad.Launchpad.login_anonymously('grabbing bugs',
'production',
LPCACHEDIR)
self.openstack_projects = (self.get_group_projects('openstack') +
self.get_group_projects('oslo') +
# Fix for story 2006737 since os-brick is
# not in the openstack group in launchpad.
['os-brick'])
self.openstack_projects = (
self.get_group_projects('openstack') +
self.get_group_projects('oslo') +
# Fix for story 2006737 since os-brick is
# not in the openstack group in launchpad.
['os-brick'])
def get_group_projects(self, group_name):
group = self.lp.project_groups[group_name]
return list(map(lambda project: project.name,
group.projects))
def test_launchpad(self):
bad_bugs = []
for x in self.classifier.queries:
print("Looking for bug: https://bugs.launchpad.net/bugs/%s"
% x['bug'])
if not self._is_valid_launchpad_bug(x['bug']):
bad_bugs.append("https://bugs.launchpad.net/bugs/%s" %
x['bug'])
if len(bad_bugs) > 0:
self.fail("the following bugs are not targeted to openstack "
"on launchpad: %s" % bad_bugs)
def test_elasticsearch_query(self):
for x in self.classifier.queries:
print("Looking for bug: https://bugs.launchpad.net/bugs/%s"
% x['bug'])
self.assertTrue(
self._is_valid_ElasticSearch_query(x, x['bug']),
("Something is wrong with bug %s" % x['bug']))
def _is_valid_ElasticSearch_query(self, x, bug):
query = qb.generic(x['query'])
try:
results = self.classifier.es.search(query, size='10')
except pyelasticsearch.ElasticHttpError:
self.fail("Failure to process query for bug %s" % bug)
valid_query = len(results) > 0
if not valid_query:
print("Didn't find any hits for bug %s" % x['bug'])
# don't fail tests if no hits for a bug
return True
def _is_valid_launchpad_bug(self, bug):
def _is_valid_launchpad_bug(self, bug) -> bool:
bug_tasks = self.lp.bugs[bug].bug_tasks
projects = map(lambda bug_task: bug_task.bug_target_name, bug_tasks)
# Check that at least one bug_tasks is targeted to an OpenStack Project
@ -96,5 +62,57 @@ class TestQueries(tests.TestCase):
continue
if project in self.openstack_projects:
return True
print("bug has no targets in the openstack launchpad group")
LOGGER.error("bug has no targets in the openstack launchpad group")
return False
def _is_valid_ElasticSearch_query(self, x, bug) -> bool:
query = qb.generic(x['query'])
results = self.classifier.es.search(query, size='10')
valid_query = len(results) > 0
if not valid_query:
LOGGER.error("Didn't find any hits for bug %s" % x['bug'])
# don't fail tests if no hits for a bug
return True
_ctx = Context()
@pytest.fixture(scope='session')
def ctx(request):
return _ctx
@pytest.mark.parametrize(
"x",
_ctx.classifier.queries,
ids=[x['bug'] for x in _ctx.classifier.queries])
def test_launchpad(ctx, x):
"""Make sure queries are valid.
Make sure all queries are for open OpenStack bugs in Launchpad or have
hits in logstash.openstack.org.
This test is used to validate any changes to queries.yaml
"""
LOGGER.debug(
"Looking for bug: https://bugs.launchpad.net/bugs/%s",
x['bug'])
assert \
ctx._is_valid_launchpad_bug(x['bug']) is True, \
f"Bug {x['bug']} is not targeted to openstack on launchpad."
@pytest.mark.parametrize(
"x",
_ctx.classifier.queries,
ids=[x['bug'] for x in _ctx.classifier.queries])
def test_elasticsearch_query(ctx, x):
LOGGER.debug(
"Looking for bug: https://bugs.launchpad.net/bugs/%s",
x['bug'])
try:
assert ctx._is_valid_ElasticSearch_query(x, x['bug']), \
("Something is wrong with bug %s" % x['bug'])
except Exception as e:
pytest.fail(f"Exception {e} receive with query: {x}")

View File

@ -5,3 +5,7 @@ testrepository>=0.0.17
testscenarios>=0.4
testtools>=0.9.36,!=1.2.0
mock>=1.0
# queries use pytest
pytest
pytest-xdist
pytest-html

View File

@ -22,7 +22,8 @@ commands = python setup.py testr --slowest --testr-args='{posargs} tests.functio
[testenv:queries]
basepython = python3
commands = python setup.py testr --slowest --testr-args='{posargs} tests.functional.test_queries'
commands =
pytest -k functional {posargs:-n auto --html={envlogdir}/reports.html --self-contained-html -v}
[testenv:venv]
basepython = python3