Skip to content

Commit

Permalink
add option to return jira issue metadata (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
gs202 authored and lukas-bednar committed Sep 23, 2019
1 parent 8123759 commit 722d663
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 18 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ Requires
- requests >= 2.13.0
- six
- retry>=0.9.2
- marshmallow>=3.2.0

Installation
============
Expand Down Expand Up @@ -193,6 +194,7 @@ Usage
# resolved_statuses = comma separated list of statuses (closed, resolved)
# run_test_case = True (default value for 'run' parameter)
# connection_error_strategy [strict|skip|ignore] Choose how to handle connection errors
# return_jira_metadata = False (return Jira issue with metadata instead of boolean result)
You can set the password field by setting the PYTEST_JIRA_PASSWORD environment variable:

Expand Down
80 changes: 80 additions & 0 deletions issue_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from marshmallow import Schema, fields, EXCLUDE


class Basic(Schema):
id = fields.String()
name = fields.String()


class Components(Schema):
name = fields.String()


class Version(Schema):
name = fields.String()


class Priority(Basic):
pass


class Resolution(Basic):
description = fields.String()


class Status(Basic):
description = fields.String()


class Type(Basic):
subtask = fields.Boolean()


class User(Schema):
key = fields.String()
name = fields.String()
displayName = fields.String()
active = fields.Boolean()


class JiraIssueSchema(Schema):
class Meta:
unknown = EXCLUDE # exclude unknown fields
# Default set to None for fields that are not filled
issuetype = fields.Nested(Type(), default=None)
status = fields.Nested(Status(), default=None)
priority = fields.Nested(Priority(), default=None)
reporter = fields.Nested(User(), default=None)
creator = fields.Nested(User(), default=None)
versions = fields.List(fields.Nested(Version()), default=None)
summary = fields.String(default=None)
updated = fields.String(default=None)
created = fields.String(default=None)
resolutiondate = fields.String(default=None)
duedate = fields.String(default=None)
fixVersions = fields.List(fields.Nested(Version()), default=None)
components = fields.List(fields.Nested(Components()), default=None)
resolution = fields.Nested(Resolution(), default=None)
assignee = fields.Nested(User(), default=None)
labels = fields.List(fields.String())


class JiraIssue:
def __init__(self, issue_id, **entries):
self.__dict__.update(entries)
self.issue_id = issue_id

def __repr__(self):
return 'JiraIssue {}'.format(self.issue_id)

@property
def components_list(self):
return set(component['name'] for component in self.components)

@property
def fixed_versions(self):
return set(version['name'] for version in self.fix_versions)

@property
def versions_list(self):
return set(version['name'] for version in self.versions)
54 changes: 37 additions & 17 deletions pytest_jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import six
from retry import retry

from issue_model import JiraIssue, JiraIssueSchema

DEFAULT_RESOLVE_STATUSES = 'closed', 'resolved'
DEFAULT_RUN_TEST_CASE = True
CONNECTION_SKIP_MESSAGE = 'Jira connection issue, skipping test: %s'
Expand All @@ -40,7 +42,8 @@ def __init__(
resolved_statuses=None,
run_test_case=DEFAULT_RUN_TEST_CASE,
strict_xfail=False,
connection_error_strategy=None
connection_error_strategy=None,
return_jira_metadata=False
):
self.conn = connection
self.mark = marker
Expand All @@ -56,6 +59,7 @@ def __init__(
self.issue_cache = dict()

self.strict_xfail = strict_xfail
self.return_jira_metadata = return_jira_metadata

def is_issue_resolved(self, issue_id):
"""
Expand All @@ -65,12 +69,17 @@ def is_issue_resolved(self, issue_id):
# Access Jira issue (may be cached)
if issue_id not in self.issue_cache:
try:
self.issue_cache[issue_id] = self.conn.get_issue(issue_id)
self.issue_cache[issue_id] = self.conn.get_issue(
issue_id, self.return_jira_metadata
)
except requests.RequestException as e:
if not hasattr(e.response, 'status_code') \
or not e.response.status_code == 404:
raise
self.issue_cache[issue_id] = self.mark.get_default(issue_id)
if self.return_jira_metadata:
issue = JiraIssueSchema().dump(self.issue_cache[issue_id])
return JiraIssue(issue_id, **issue)

# Skip test if issue remains unresolved
if self.issue_cache[issue_id] is None:
Expand Down Expand Up @@ -217,26 +226,27 @@ def check_connection(self):
return True

@retry(JSONDecodeError, tries=3, delay=2)
def get_issue(self, issue_id):
def get_issue(self, issue_id, return_jira_metadata):
if not self.is_connected:
self.check_connection()
issue_url = '{url}/rest/api/2/issue/{issue_id}'.format(
url=self.url, issue_id=issue_id
)
issue = self._jira_request(issue_url).json()
field = issue['fields']
return {
'components': set(
c['name'] for c in field.get('components', set())
),
'versions': set(
v['name'] for v in field.get('versions', set())
),
'fixed_versions': set(
v['name'] for v in field.get('fixVersions', set())
),
'status': field['status']['name'].lower(),
}
return field if return_jira_metadata else \
{
'components': set(
c['name'] for c in field.get('components', set())
),
'versions': set(
v['name'] for v in field.get('versions', set())
),
'fixed_versions': set(
v['name'] for v in field.get('fixVersions', set())
),
'status': field['status']['name'].lower(),
}

def get_url(self):
return self.url
Expand Down Expand Up @@ -444,6 +454,12 @@ def pytest_addoption(parser):
skip - skip any test that has a marker
"""
)
group.addoption('--jira-return-metadata',
action='store_true',
dest='return_jira_metadata',
default=False,
help='If set, will return Jira issue with ticket metadata'
)


def pytest_configure(config):
Expand Down Expand Up @@ -495,7 +511,8 @@ def pytest_configure(config):
resolved_statuses,
config.getvalue('jira_run_test_case'),
config.getini("xfail_strict"),
config.getvalue('jira_connection_error_strategy')
config.getvalue('jira_connection_error_strategy'),
config.getvalue('return_jira_metadata')
)
ok = config.pluginmanager.register(jira_plugin, PLUGIN_NAME)
assert ok
Expand All @@ -514,7 +531,10 @@ def wrapper_jira_issue(issue_id):
jira_plugin = request.config.pluginmanager.getplugin(PLUGIN_NAME)
if jira_plugin:
try:
return not jira_plugin.is_issue_resolved(issue_id)
result = jira_plugin.is_issue_resolved(issue_id)
if request.config.option.return_jira_metadata:
return result
return not result # return boolean representing of issue state
except requests.RequestException as e:
strategy = request.config.getoption(CONNECTION_ERROR_FLAG_NAME)
if strategy == SKIP:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pytest>=2.2.4
six
requests>=2.13.0
retry>=0.9.2
marshmallow>=3.2.0
26 changes: 25 additions & 1 deletion tests/test_jira.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import os
import pytest
from distutils.version import LooseVersion

import pytest

CONFTEST = """
import pytest
Expand Down Expand Up @@ -1017,3 +1018,26 @@ def test_pass(jira_issue):
failed=failed,
error=error
)


@pytest.mark.parametrize("ticket", ['ORG-1382', 'Foo-Bar'])
@pytest.mark.parametrize("return_method, _type", [
('--jira-return-metadata', 'JiraIssue'),
('', 'bool'),
])
def test_jira_fixture_return_metadata(testdir, return_method, _type, ticket):
testdir.makepyfile("""
import pytest
from issue_model import JiraIssue
def test_pass(jira_issue):
issue = jira_issue('%s')
assert isinstance(issue, %s)
""" % (ticket, _type))
ARGS = (
'--jira',
'--jira-url', 'https://issues.jboss.org',
return_method
)
result = testdir.runpytest(*ARGS)
result.assert_outcomes(1, 0, 0)

0 comments on commit 722d663

Please sign in to comment.