Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[flake8]
ignore = E203, W503
max-line-length = 119
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ celerybeat.pid
# Environments
.env
.venv
.venv38
.venv39
.venv310
.venv311
env/
venv/
ENV/
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Changelog

## [Unreleased]
### Added
- OAuth 2.0 Password Grant authentication, by @HardNorth
### Changed
- Client version updated to [5.6.7](https://github.com/reportportal/client-Python/releases/tag/5.6.7), by @HardNorth
### Fixed
- Some configuration parameter names, which are different in the client, by @HardNorth
### Removed
- `rp_uuid` param support, as it was deprecated pretty while ago, by @HardNorth

## [5.5.2]
### Fixed
- Issue [#397](https://github.com/reportportal/agent-python-pytest/issues/397) pytest launch stuck with xdist, by @HardNorth

Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[build-system]
requires = [
# sync with setup.py until we discard non-pep-517/518
"setuptools>=68.0.0",
"setuptools-scm",
"wheel==0.40.0",
Expand Down
58 changes: 29 additions & 29 deletions pytest_reportportal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
class AgentConfig:
"""Storage for the RP agent initialization attributes."""

rp_enabled: bool
rp_client_type: Optional[ClientType]
rp_rerun: Optional[bool]
pconfig: Config
Expand All @@ -53,16 +54,26 @@ class AgentConfig:
rp_tests_attributes: Optional[List[str]]
rp_launch_description: str
rp_log_batch_size: int
rp_log_batch_payload_size: int
rp_log_batch_payload_limit: int
rp_log_level: Optional[int]
rp_log_format: Optional[str]
rp_mode: str
rp_parent_item_id: Optional[str]
rp_project: str
rp_rerun_of: Optional[str]
rp_api_retries: int
rp_skip_connection_test: bool
rp_api_key: str

# API key auth parameter
rp_api_key: Optional[str]

# OAuth 2.0 parameters
rp_oauth_uri: Optional[str]
rp_oauth_username: Optional[str]
rp_oauth_password: Optional[str]
rp_oauth_client_id: Optional[str]
rp_oauth_client_secret: Optional[str]
rp_oauth_scope: Optional[str]

rp_verify_ssl: Union[bool, str]
rp_launch_timeout: int
rp_launch_uuid_print: bool
Expand All @@ -72,8 +83,9 @@ class AgentConfig:

def __init__(self, pytest_config: Config) -> None:
"""Initialize required attributes."""
self.rp_enabled = to_bool(getattr(pytest_config.option, "rp_enabled", True))
self.rp_rerun = pytest_config.option.rp_rerun or pytest_config.getini("rp_rerun")
self.rp_endpoint = self.find_option(pytest_config, "rp_endpoint")
self.rp_endpoint = getenv("RP_ENDPOINT") or self.find_option(pytest_config, "rp_endpoint")
self.rp_hierarchy_code = to_bool(self.find_option(pytest_config, "rp_hierarchy_code"))
self.rp_dir_level = int(self.find_option(pytest_config, "rp_hierarchy_dirs_level"))
self.rp_hierarchy_dirs = to_bool(self.find_option(pytest_config, "rp_hierarchy_dirs"))
Expand All @@ -100,19 +112,18 @@ def __init__(self, pytest_config: Config) -> None:
self.rp_tests_attributes = self.find_option(pytest_config, "rp_tests_attributes")
self.rp_launch_description = self.find_option(pytest_config, "rp_launch_description")
self.rp_log_batch_size = int(self.find_option(pytest_config, "rp_log_batch_size"))
batch_payload_size = self.find_option(pytest_config, "rp_log_batch_payload_size")
if batch_payload_size:
self.rp_log_batch_payload_size = int(batch_payload_size)
batch_payload_size_limit = self.find_option(pytest_config, "rp_log_batch_payload_limit")
if batch_payload_size_limit:
self.rp_log_batch_payload_limit = int(batch_payload_size_limit)
else:
self.rp_log_batch_payload_size = MAX_LOG_BATCH_PAYLOAD_SIZE
self.rp_log_batch_payload_limit = MAX_LOG_BATCH_PAYLOAD_SIZE
self.rp_log_level = get_actual_log_level(pytest_config, "rp_log_level")
self.rp_log_format = self.find_option(pytest_config, "rp_log_format")
self.rp_thread_logging = to_bool(self.find_option(pytest_config, "rp_thread_logging") or False)
self.rp_mode = self.find_option(pytest_config, "rp_mode")
self.rp_parent_item_id = self.find_option(pytest_config, "rp_parent_item_id")
self.rp_project = self.find_option(pytest_config, "rp_project")
self.rp_rerun_of = self.find_option(pytest_config, "rp_rerun_of")
self.rp_skip_connection_test = to_bool(self.find_option(pytest_config, "rp_skip_connection_test"))

rp_api_retries_str = self.find_option(pytest_config, "rp_api_retries")
rp_api_retries = rp_api_retries_str and int(rp_api_retries_str)
Expand All @@ -134,27 +145,16 @@ def __init__(self, pytest_config: Config) -> None:
else:
self.rp_api_retries = 0

# API key auth parameter
self.rp_api_key = getenv("RP_API_KEY") or self.find_option(pytest_config, "rp_api_key")
if not self.rp_api_key:
self.rp_api_key = getenv("RP_UUID") or self.find_option(pytest_config, "rp_uuid")
if self.rp_api_key:
warnings.warn(
"Parameter `rp_uuid` is deprecated since 5.1.9 "
"and will be subject for removing in the next "
"major version. Use `rp_api_key` argument "
"instead.",
DeprecationWarning,
2,
)
else:
warnings.warn(
"Argument `rp_api_key` is `None` or empty string, "
"that is not supposed to happen because Report "
"Portal is usually requires an authorization key. "
"Please check your configuration.",
RuntimeWarning,
2,
)

# OAuth 2.0 parameters
self.rp_oauth_uri = self.find_option(pytest_config, "rp_oauth_uri")
self.rp_oauth_username = self.find_option(pytest_config, "rp_oauth_username")
self.rp_oauth_password = self.find_option(pytest_config, "rp_oauth_password")
self.rp_oauth_client_id = self.find_option(pytest_config, "rp_oauth_client_id")
self.rp_oauth_client_secret = self.find_option(pytest_config, "rp_oauth_client_secret")
self.rp_oauth_scope = self.find_option(pytest_config, "rp_oauth_scope")

rp_verify_ssl = self.find_option(pytest_config, "rp_verify_ssl", True)
try:
Expand Down
48 changes: 12 additions & 36 deletions pytest_reportportal/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import pytest

# noinspection PyPackageRequirements
import requests
from pytest import Item, Session
from reportportal_client import RP, RPLogHandler
from reportportal_client.errors import ResponseError
Expand All @@ -49,10 +48,7 @@
LOGGER: Logger = logging.getLogger(__name__)

MANDATORY_PARAMETER_MISSED_PATTERN: str = (
"One of the following mandatory parameters is unset: "
+ "rp_project: {}, "
+ "rp_endpoint: {}, "
+ "rp_api_key: {}"
"One of the following mandatory parameters is unset: " + "rp_project: {}, rp_endpoint: {}"
)

FAILED_LAUNCH_WAIT: str = (
Expand Down Expand Up @@ -182,25 +178,6 @@ def register_markers(config) -> None:
config.addinivalue_line("markers", "name(name): report the test case with a custom Name.")


def check_connection(agent_config: AgentConfig):
"""Check connection to RP using provided options.

If connection is successful returns True either False.
:param agent_config: Instance of the AgentConfig class
:return True on successful connection check, either False
"""
url = "{0}/api/v1/project/{1}".format(agent_config.rp_endpoint, agent_config.rp_project)
headers = {"Authorization": "bearer {0}".format(agent_config.rp_api_key)}
try:
resp = requests.get(url, headers=headers, verify=agent_config.rp_verify_ssl)
resp.raise_for_status()
return True
except requests.exceptions.RequestException as exc:
LOGGER.exception(exc)
LOGGER.error("Unable to connect to Report Portal, the launch won't be reported")
return False


# no 'config' type for backward compatibility for older pytest versions
# noinspection PyProtectedMember
def pytest_configure(config) -> None:
Expand All @@ -216,24 +193,17 @@ def pytest_configure(config) -> None:
or not config.option.rp_enabled
)
if not config._rp_enabled:
LOGGER.debug("Disabling reporting to RP.")
return

agent_config = AgentConfig(config)

cond = (agent_config.rp_project, agent_config.rp_endpoint, agent_config.rp_api_key)
cond = (agent_config.rp_project, agent_config.rp_endpoint)
config._rp_enabled = all(cond)
if not config._rp_enabled:
LOGGER.debug(MANDATORY_PARAMETER_MISSED_PATTERN.format(*cond))
LOGGER.debug("Disabling reporting to RP.")
return

if not agent_config.rp_skip_connection_test:
config._rp_enabled = check_connection(agent_config)

if not config._rp_enabled:
LOGGER.debug("Failed to establish connection with RP. " "Disabling reporting.")
return

config._reporter_config = agent_config

if is_control(config):
Expand Down Expand Up @@ -592,7 +562,6 @@ def add_shared_option(name, help_str, default=None, action="store"):
name="rp_parent_item_id",
help_str="Create all test item as child items of the given (already " "existing) item.",
)
add_shared_option(name="rp_uuid", help_str="Deprecated: use `rp_api_key` " "instead.")
add_shared_option(name="rp_api_key", help_str="API key of Report Portal. Usually located on UI profile " "page.")
add_shared_option(name="rp_endpoint", help_str="Server endpoint")
add_shared_option(name="rp_mode", help_str="Visibility of current launch [DEFAULT, DEBUG]", default="DEFAULT")
Expand All @@ -613,11 +582,19 @@ def add_shared_option(name, help_str, default=None, action="store"):
help_str="Launch UUID print output. Default `stdout`. Possible values: [stderr, stdout]",
)

# OAuth 2.0 parameters
parser.addini("rp_oauth_uri", type="args", help="OAuth 2.0 token endpoint URL for password grant authentication")
parser.addini("rp_oauth_username", type="args", help="OAuth 2.0 username for password grant authentication")
parser.addini("rp_oauth_password", type="args", help="OAuth 2.0 password for password grant authentication")
parser.addini("rp_oauth_client_id", type="args", help="OAuth 2.0 client identifier")
parser.addini("rp_oauth_client_secret", type="args", help="OAuth 2.0 client secret")
parser.addini("rp_oauth_scope", type="args", help="OAuth 2.0 access token scope")

parser.addini("rp_launch_attributes", type="args", help="Launch attributes, i.e Performance Regression")
parser.addini("rp_tests_attributes", type="args", help="Attributes for all tests items, e.g. Smoke")
parser.addini("rp_log_batch_size", default="20", help="Size of batch log requests in async mode")
parser.addini(
"rp_log_batch_payload_size",
"rp_log_batch_payload_limit",
default=str(MAX_LOG_BATCH_PAYLOAD_SIZE),
help="Maximum payload size in bytes of async batch log requests",
)
Expand Down Expand Up @@ -669,7 +646,6 @@ def add_shared_option(name, help_str, default=None, action="store"):
parser.addini("rp_issue_id_marks", type="bool", default=True, help="Add tag with issue id to the test")
parser.addini("retries", default="0", help="Deprecated: use `rp_api_retries` instead")
parser.addini("rp_api_retries", default="0", help="Amount of retries for performing REST calls to RP server")
parser.addini("rp_skip_connection_test", default=False, type="bool", help="Skip Report Portal connection test")
parser.addini(
"rp_launch_timeout",
default=86400,
Expand Down
19 changes: 15 additions & 4 deletions pytest_reportportal/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def _create_leaf(

:param leaf_type: the leaf type
:param parent_item: parent pytest.Item of the current leaf
:param item: leaf's pytest.Item
:param item: the leaf's pytest.Item
:return: a leaf
"""
return {
Expand Down Expand Up @@ -434,7 +434,10 @@ def _merge_code(self, test_tree: Dict[str, Any]) -> None:

def _build_item_paths(self, leaf: Dict[str, Any], path: List[Dict[str, Any]]) -> None:
children = leaf.get("children", {})
all_background_steps = all([isinstance(child, Background) for child in children.keys()])
if PYTEST_BDD:
all_background_steps = all([isinstance(child, Background) for child in children.keys()])
else:
all_background_steps = False
if len(children) > 0 and not all_background_steps:
path.append(leaf)
for name, child_leaf in leaf["children"].items():
Expand Down Expand Up @@ -1123,7 +1126,8 @@ def start_bdd_scenario(self, feature: Feature, scenario: Scenario) -> None:
root_leaf = self._bdd_tree
if not root_leaf:
self._bdd_tree = root_leaf = self._create_leaf(LeafType.ROOT, None, None, item_id=self.parent_item_id)
children_leafs = root_leaf["children"]
# noinspection PyTypeChecker
children_leafs: Dict[Any, Any] = root_leaf["children"]
if feature in children_leafs:
feature_leaf = children_leafs[feature]
else:
Expand Down Expand Up @@ -1389,11 +1393,18 @@ def start(self) -> None:
retries=self._config.rp_api_retries,
verify_ssl=self._config.rp_verify_ssl,
launch_uuid=launch_id,
log_batch_payload_size=self._config.rp_log_batch_payload_size,
log_batch_payload_limit=self._config.rp_log_batch_payload_limit,
launch_uuid_print=self._config.rp_launch_uuid_print,
print_output=self._config.rp_launch_uuid_print_output,
http_timeout=self._config.rp_http_timeout,
mode=self._config.rp_mode,
# OAuth 2.0 parameters
oauth_uri=self._config.rp_oauth_uri,
oauth_username=self._config.rp_oauth_username,
oauth_password=self._config.rp_oauth_password,
oauth_client_id=self._config.rp_oauth_client_id,
oauth_client_secret=self._config.rp_oauth_client_secret,
oauth_scope=self._config.rp_oauth_scope,
)
if hasattr(self.rp, "get_project_settings"):
self.project_settings = self.rp.get_project_settings()
Expand Down
2 changes: 2 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
delayed-assert
pytest-cov
pytest-parallel
black
isort
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dill>=0.3.6
pytest>=4.6.10
reportportal-client~=5.6.4
reportportal-client~=5.6.7
aenum>=3.1.0
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from setuptools import setup

__version__ = "5.5.2"
__version__ = "5.5.3"


def read_file(fname):
Expand Down
1 change: 0 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@
"""This package contains tests for the project."""

REPORT_PORTAL_SERVICE = "reportportal_client.RPClient"
REQUESTS_SERVICE = "reportportal_client.client.requests.Session"
Loading