Skip to content

Commit

Permalink
Merge pull request #13 from fundakol/xray-report-file
Browse files Browse the repository at this point in the history
Save XRAY execution report to file
  • Loading branch information
fundakol authored Oct 13, 2021
2 parents f3e217d + 8094496 commit a6eaf24
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 103 deletions.
1 change: 1 addition & 0 deletions src/pytest_xray/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
XRAY_TEST_PLAN_ID = '--testplan'
XRAY_EXECUTION_ID = '--execution'
JIRA_CLOUD = '--cloud'
XRAYPATH = '--xraypath'
5 changes: 5 additions & 0 deletions src/pytest_xray/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class XrayError(Exception):
"""Custom exception for Jira XRAY"""

def __init__(self, message=''):
self.message = message
33 changes: 33 additions & 0 deletions src/pytest_xray/file_publisher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import json
import logging
from pathlib import Path

from pytest_xray.exceptions import XrayError

logger = logging.getLogger(__name__)


class FilePublisher:

def __init__(self, filepath: str):
self.filepath: Path = Path(filepath)

def publish(self, data: dict) -> str:
"""
Save results to a file or raise XrayError.
:param data: data to save
:return: file path where data was saved
"""
self.filepath.parent.mkdir(parents=True, exist_ok=True)
try:
with open(self.filepath, 'w', encoding='UTF-8') as file:
json.dump(data, file, indent=2)
except FileNotFoundError as e:
logger.exception(e)
raise XrayError(f'File not found: {self.filepath}') from e
except TypeError as e:
logger.exception(e)
raise XrayError(f'Cannot save data to file: {self.filepath}') from e
else:
return f'{self.filepath}'
59 changes: 59 additions & 0 deletions src/pytest_xray/helper.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import datetime as dt
import enum
import os
from os import environ
from typing import List, Dict, Union, Any, Type, Optional

from _pytest.mark import Mark
from _pytest.nodes import Item

from pytest_xray.constant import XRAY_MARKER_NAME, DATETIME_FORMAT
from pytest_xray.exceptions import XrayError

_test_keys = {}

Expand Down Expand Up @@ -117,3 +120,59 @@ def get_test_key_for(item: Item) -> Optional[str]:
if test_id:
return test_id
return None


def get_base_options() -> dict:
options = {}
try:
base_url = environ['XRAY_API_BASE_URL']
except KeyError as e:
raise XrayError(
'pytest-jira-xray plugin requires environment variable: XRAY_API_BASE_URL'
) from e

verify = os.environ.get('XRAY_API_VERIFY_SSL', 'True')

if verify.upper() == 'TRUE':
verify = True
elif verify.upper() == 'FALSE':
verify = False
else:
if not os.path.exists(verify):
raise XrayError(f'Cannot find certificate file "{verify}"')

options['VERIFY'] = verify
options['BASE_URL'] = base_url
return options


def get_basic_auth() -> dict:
options = get_base_options()
try:
user = environ['XRAY_API_USER']
password = environ['XRAY_API_PASSWORD']
except KeyError as e:
raise XrayError(
'Basic authentication requires environment variables: '
'XRAY_API_USER, XRAY_API_PASSWORD'
) from e

options['USER'] = user
options['PASSWORD'] = password
return options


def get_bearer_auth() -> dict:
options = get_base_options()
try:
client_id = environ['XRAY_CLIENT_ID']
client_secret = environ['XRAY_CLIENT_SECRET']
except KeyError as e:
raise XrayError(
'Bearer authentication requires environment variables: '
'XRAY_CLIENT_ID, XRAY_CLIENT_SECRET'
) from e

options['CLIENT_ID'] = client_id
options['CLIENT_SECRET'] = client_secret
return options
149 changes: 57 additions & 92 deletions src/pytest_xray/plugin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
from os import environ
from pathlib import Path
from typing import List

from _pytest.config import Config, ExitCode
Expand All @@ -12,101 +11,23 @@
XRAY_PLUGIN,
XRAY_TEST_PLAN_ID,
XRAY_EXECUTION_ID,
JIRA_CLOUD
JIRA_CLOUD,
XRAYPATH
)
from pytest_xray.exceptions import XrayError
from pytest_xray.file_publisher import FilePublisher
from pytest_xray.helper import (
associate_marker_metadata_for,
get_test_key_for,
Status,
TestCase,
TestExecution,
StatusBuilder,
CloudStatus
CloudStatus,
get_bearer_auth,
get_basic_auth
)
from pytest_xray.xray_publisher import XrayPublisher, BearerAuth, XrayError


def get_base_options() -> dict:
options = {}
try:
base_url = environ['XRAY_API_BASE_URL']
except KeyError as e:
raise XrayError(
'pytest-jira-xray plugin requires environment variable: XRAY_API_BASE_URL'
) from e

verify = os.environ.get('XRAY_API_VERIFY_SSL', 'True')

if verify.upper() == 'TRUE':
verify = True
elif verify.upper() == 'FALSE':
verify = False
else:
if not os.path.exists(verify):
raise XrayError(f'Cannot find certificate file "{verify}"')

options['VERIFY'] = verify
options['BASE_URL'] = base_url
return options


def get_basic_auth() -> dict:
options = get_base_options()
try:
user = environ['XRAY_API_USER']
password = environ['XRAY_API_PASSWORD']
except KeyError as e:
raise XrayError(
'Basic authentication requires environment variables: '
'XRAY_API_USER, XRAY_API_PASSWORD'
) from e

options['USER'] = user
options['PASSWORD'] = password
return options


def get_bearer_auth() -> dict:
options = get_base_options()
try:
client_id = environ['XRAY_CLIENT_ID']
client_secret = environ['XRAY_CLIENT_SECRET']
except KeyError as e:
raise XrayError(
'Bearer authentication requires environment variables: '
'XRAY_CLIENT_ID, XRAY_CLIENT_SECRET'
) from e

options['CLIENT_ID'] = client_id
options['CLIENT_SECRET'] = client_secret
return options


def pytest_configure(config: Config) -> None:
if not config.getoption(JIRA_XRAY_FLAG):
return

if config.getoption(JIRA_CLOUD):
options = get_bearer_auth()
auth = BearerAuth(
options['BASE_URL'],
options['CLIENT_ID'],
options['CLIENT_SECRET']
)
else:
options = get_basic_auth()
auth = (options['USER'], options['PASSWORD'])

plugin = XrayPublisher(
base_url=options['BASE_URL'],
auth=auth,
verify=options['VERIFY']
)

config.pluginmanager.register(plugin, XRAY_PLUGIN)
config.addinivalue_line(
'markers', 'xray(JIRA_ID): mark test with JIRA XRAY test case ID'
)
from pytest_xray.xray_publisher import BearerAuth, XrayPublisher


def pytest_addoption(parser: Parser):
Expand Down Expand Up @@ -135,6 +56,44 @@ def pytest_addoption(parser: Parser):
default=None,
help='XRAY Test Plan ID'
)
xray.addoption(
XRAYPATH,
action='store',
default=None,
help='Do not upload to a server but create JSON report file at given path'
)


def pytest_configure(config: Config) -> None:
if not config.getoption(JIRA_XRAY_FLAG):
return

xray_path = config.getoption(XRAYPATH)

if xray_path:
plugin = FilePublisher(xray_path)
else:
if config.getoption(JIRA_CLOUD):
options = get_bearer_auth()
auth = BearerAuth(
options['BASE_URL'],
options['CLIENT_ID'],
options['CLIENT_SECRET']
)
else:
options = get_basic_auth()
auth = (options['USER'], options['PASSWORD'])

plugin = XrayPublisher(
base_url=options['BASE_URL'],
auth=auth,
verify=options['VERIFY']
)

config.pluginmanager.register(plugin, XRAY_PLUGIN)
config.addinivalue_line(
'markers', 'xray(JIRA_ID): mark test with JIRA XRAY test case ID'
)


def pytest_collection_modifyitems(config: Config, items: List[Item]) -> None:
Expand Down Expand Up @@ -183,15 +142,21 @@ def pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus: Exit
tc = TestCase(test_key, status_builder('ABORTED'), item.longreprtext)
test_execution.append(tc)

xray_publisher = terminalreporter.config.pluginmanager.get_plugin(XRAY_PLUGIN)
publisher = terminalreporter.config.pluginmanager.get_plugin(XRAY_PLUGIN)
try:
issue_id = xray_publisher.publish(test_execution)
issue_id = publisher.publish(test_execution.as_dict())
except XrayError as exc:
terminalreporter.ensure_newline()
terminalreporter.section('Jira XRAY', sep='-', red=True, bold=True)
terminalreporter.write_line('Could not publish results to Jira XRAY!')
if exc.message:
terminalreporter.write_line(exc.message)
else:
if issue_id:
terminalreporter.write_sep('-', f'Uploaded results to JIRA XRAY. Test Execution Id: {issue_id}')
if issue_id and terminalreporter.config.getoption(XRAYPATH):
terminalreporter.write_sep(
'-', f'Generated XRAY execution report file: {Path(issue_id).absolute()}'
)
elif issue_id:
terminalreporter.write_sep(
'-', f'Uploaded results to JIRA XRAY. Test Execution Id: {issue_id}'
)
15 changes: 4 additions & 11 deletions src/pytest_xray/xray_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,11 @@
from requests.auth import AuthBase

from pytest_xray.constant import TEST_EXECUTION_ENDPOINT, AUTHENTICATE_ENDPOINT
from pytest_xray.helper import TestExecution
from pytest_xray.exceptions import XrayError

_logger = logging.getLogger(__name__)


class XrayError(Exception):
"""Custom exception for Jira XRAY"""

def __init__(self, message=''):
self.message = message


class BearerAuth(AuthBase):

def __init__(self, base_url: str, client_id: str, client_secret: str) -> None:
Expand Down Expand Up @@ -99,13 +92,13 @@ def _send_data(self, url: str, auth: Union[AuthBase, tuple], data: dict) -> dict
raise XrayError(err_message) from exc
return response.json()

def publish(self, test_execution: TestExecution) -> str:
def publish(self, data: dict) -> str:
"""
Publish results to Jira and return testExecutionId or raise XrayError.
:param test_execution: instance of TestExecution class
:param data: data to send
:return: test execution issue id
"""
response_data = self._send_data(self.endpoint_url, self.auth, test_execution.as_dict())
response_data = self._send_data(self.endpoint_url, self.auth, data)
key = response_data['testExecIssue']['key']
return key
8 changes: 8 additions & 0 deletions tests/test_xray.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ def test_jira_xray_plugin(testdir, cli_options):
result.assert_outcomes(passed=1, failed=1, skipped=1)
assert len(result.errlines) == 0
assert re.search('Uploaded results to JIRA XRAY', '\n'.join(result.outlines))


def test_jira_xray_plugin_exports_to_file(testdir, tmpdir):
xray_file = tmpdir.join('xray.json')
result = testdir.runpytest('--jira-xray', '--xraypath', str(xray_file))
result.assert_outcomes(passed=1, failed=1, skipped=1)
assert len(result.errlines) == 0
assert re.search('Generated XRAY execution report file:.*xray.json', '\n'.join(result.outlines))

0 comments on commit a6eaf24

Please sign in to comment.