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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]
### Added
- Attribute splitting if they are passed as `str` in configs, by @HardNorth

## [5.6.6]
### Added
- Microseconds precision for timestamps, by @HardNorth
### Changed
- Client version updated to [5.7.4](https://github.com/reportportal/client-Python/releases/tag/5.7.4), by @HardNorth
Expand Down
22 changes: 20 additions & 2 deletions pytest_reportportal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@
from reportportal_client.helpers import to_bool
from reportportal_client.logs import MAX_LOG_BATCH_PAYLOAD_SIZE

ATTRIBUTES_SEPARATOR = ";"


def normalize_attributes(attributes: Optional[Any]) -> Optional[Any]:
"""Split a string of attributes into a deduplicated list of attributes."""
if not attributes:
return attributes
if not isinstance(attributes, str):
return attributes
normalized_attributes = []
unique_attributes = set()
for attribute in attributes.split(ATTRIBUTES_SEPARATOR):
attribute = attribute.strip()
if attribute and attribute not in unique_attributes:
unique_attributes.add(attribute)
normalized_attributes.append(attribute)
return normalized_attributes


class AgentConfig:
"""Storage for the RP agent initialization attributes."""
Expand Down Expand Up @@ -115,8 +133,8 @@ def __init__(self, pytest_config: Config) -> None:
)
self.rp_launch_uuid = self.find_option(pytest_config, "rp_launch_uuid", self.rp_launch_uuid)

self.rp_launch_attributes = self.find_option(pytest_config, "rp_launch_attributes")
self.rp_tests_attributes = self.find_option(pytest_config, "rp_tests_attributes")
self.rp_launch_attributes = normalize_attributes(self.find_option(pytest_config, "rp_launch_attributes"))
self.rp_tests_attributes = normalize_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_limit = self.find_option(pytest_config, "rp_log_batch_payload_limit")
Expand Down
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.6.6"
__version__ = "5.6.7"


def read_file(fname):
Expand Down
37 changes: 37 additions & 0 deletions tests/integration/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,40 @@ def test_rp_tests_attributes_add(mock_client_init):
assert len(attributes) == 2
assert {"key": "scope", "value": "smoke"} in attributes
assert {"key": "test_key", "value": "test_value"} in attributes


@mock.patch(REPORT_PORTAL_SERVICE)
def test_rp_tests_attributes_string_split_and_deduplicated(mock_client_init, monkeypatch):
"""Verify string `rp_tests_attributes` are split and deduplicated."""
monkeypatch.setenv("RP_TESTS_ATTRIBUTES", " test_key:test_value ; smoke ; test_key:test_value ")
variables = {}
variables.update(utils.DEFAULT_VARIABLES.items())
result = utils.run_pytest_tests(tests=["examples/test_simple.py"], variables=variables)
assert int(result) == 0, "Exit code should be 0 (no errors)"

mock_client = mock_client_init.return_value
assert mock_client.start_test_item.call_count > 0, '"start_test_item" called incorrect number of times'

call_args = mock_client.start_test_item.call_args_list
step_call_args = call_args[-1][1]
actual_attributes = step_call_args["attributes"]

assert utils.attributes_to_tuples(actual_attributes) == {("test_key", "test_value"), (None, "smoke")}


@mock.patch(REPORT_PORTAL_SERVICE)
def test_rp_launch_attributes_string_split_and_deduplicated(mock_client_init, monkeypatch):
"""Verify string `rp_launch_attributes` are split and deduplicated."""
monkeypatch.setenv("RP_LAUNCH_ATTRIBUTES", " launch_key:launch_value ; smoke ; launch_key:launch_value ")
variables = {}
variables.update(utils.DEFAULT_VARIABLES.items())
result = utils.run_pytest_tests(tests=["examples/test_simple.py"], variables=variables)
assert int(result) == 0, "Exit code should be 0 (no errors)"

mock_client = mock_client_init.return_value
assert mock_client.start_launch.call_count > 0, '"start_launch" called incorrect number of times'

launch_call_args = mock_client.start_launch.call_args_list
launch_attributes = launch_call_args[0][1]["attributes"]

assert {("launch_key", "launch_value"), (None, "smoke")} <= utils.attributes_to_tuples(launch_attributes)
34 changes: 34 additions & 0 deletions tests/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,37 @@ def test_env_var_overrides_log_level(monkeypatch, mocked_config):
def test_env_var_not_set_falls_back_to_config(mocked_config):
config = AgentConfig(mocked_config)
assert config.rp_endpoint == "http://docker.local:8080/"


@pytest.mark.parametrize(
["option_name", "option_value", "expected_result"],
[
("rp_launch_attributes", " smoke ; launch:demo ; smoke ; launch:demo ", ["smoke", "launch:demo"]),
("rp_tests_attributes", " test:key ; smoke ; test:key ", ["test:key", "smoke"]),
],
)
def test_string_attributes_are_split_and_deduplicated(mocked_config, option_name, option_value, expected_result):
mocked_config.option.rp_launch_attributes = None
mocked_config.option.rp_tests_attributes = None
mocked_config.getini.side_effect = lambda x: option_value if x == option_name else None

config = AgentConfig(mocked_config)

assert getattr(config, option_name) == expected_result


@pytest.mark.parametrize(
["option_name", "option_value"],
[
("rp_launch_attributes", ["smoke", "smoke"]),
("rp_tests_attributes", ["test:key", "test:key"]),
],
)
def test_attributes_not_split_if_not_string(mocked_config, option_name, option_value):
mocked_config.option.rp_launch_attributes = None
mocked_config.option.rp_tests_attributes = None
mocked_config.getini.side_effect = lambda x: option_value if x == option_name else None

config = AgentConfig(mocked_config)

assert getattr(config, option_name) == option_value
Loading