Skip to content

Commit

Permalink
Merge pull request #223 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Oct 17, 2023
2 parents 4b968ae + f2a8e56 commit bdd4bfc
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 15 deletions.
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]
### Fixed
- Attribute truncation for every method with attributes, by @HardNorth

## [5.5.1]
### Fixed
- Multipart file upload for Async clients, by @HardNorth

## [5.5.0]
Expand Down
2 changes: 2 additions & 0 deletions reportportal_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ def create_client(
:type launch_uuid_print: bool
:param print_output: Set output stream for Launch UUID printing.
:type print_output: OutputType
:param truncate_attributes: Truncate test item attributes to default maximum length.
:type truncate_attributes: bool
:param log_batch_size: Option to set the maximum number of logs that can be processed in one
batch.
:type log_batch_size: int
Expand Down
24 changes: 16 additions & 8 deletions reportportal_client/aio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class Client:
mode: str
launch_uuid_print: bool
print_output: OutputType
truncate_attributes: bool
_skip_analytics: str
_session: Optional[RetryingClientSession]
__stat_task: Optional[asyncio.Task]
Expand All @@ -107,6 +108,7 @@ def __init__(
mode: str = 'DEFAULT',
launch_uuid_print: bool = False,
print_output: OutputType = OutputType.STDOUT,
truncate_attributes: bool = True,
**kwargs: Any
) -> None:
"""Initialize the class instance with arguments.
Expand All @@ -125,6 +127,7 @@ def __init__(
:param mode: Launch mode, all Launches started by the client will be in that mode.
:param launch_uuid_print: Print Launch UUID into passed TextIO or by default to stdout.
:param print_output: Set output stream for Launch UUID printing.
:param truncate_attributes: Truncate test item attributes to default maximum length.
"""
self.api_v1, self.api_v2 = 'v1', 'v2'
self.endpoint = endpoint
Expand All @@ -144,6 +147,7 @@ def __init__(
self._session = None
self.__stat_task = None
self.api_key = api_key
self.truncate_attributes = truncate_attributes

async def session(self) -> RetryingClientSession:
"""Return aiohttp.ClientSession class instance, initialize it if necessary.
Expand All @@ -156,7 +160,7 @@ async def session(self) -> RetryingClientSession:
if self.verify_ssl is None or (type(self.verify_ssl) == bool and not self.verify_ssl):
ssl_config = False
else:
if type(self.verify_ssl) == str:
if type(self.verify_ssl) is str:
ssl_config = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=self.verify_ssl)
else:
ssl_config = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=certifi.where())
Expand Down Expand Up @@ -242,7 +246,7 @@ async def start_launch(self,
request_payload = LaunchStartRequest(
name=name,
start_time=start_time,
attributes=attributes,
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
description=description,
mode=self.mode,
rerun=rerun,
Expand Down Expand Up @@ -306,7 +310,7 @@ async def start_test_item(self,
start_time,
item_type,
launch_uuid,
attributes=verify_value_length(attributes),
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
code_ref=code_ref,
description=description,
has_stats=has_stats,
Expand Down Expand Up @@ -355,7 +359,7 @@ async def finish_test_item(self,
end_time,
launch_uuid,
status,
attributes=verify_value_length(attributes),
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
description=description,
is_skipped_an_issue=self.is_skipped_an_issue,
issue=issue,
Expand Down Expand Up @@ -389,7 +393,7 @@ async def finish_launch(self,
request_payload = LaunchFinishRequest(
end_time,
status=status,
attributes=verify_value_length(attributes),
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
description=kwargs.get('description')
).payload
response = await AsyncHttpRequest((await self.session()).put, url=url, json=request_payload,
Expand All @@ -415,7 +419,7 @@ async def update_test_item(self,
"""
data = {
'description': description,
'attributes': verify_value_length(attributes),
'attributes': verify_value_length(attributes) if self.truncate_attributes else attributes,
}
item_id = await self.get_item_id_by_uuid(item_uuid)
url = root_uri_join(self.base_url_v1, 'item', item_id, 'update')
Expand Down Expand Up @@ -650,6 +654,7 @@ def __init__(
:param mode: Launch mode, all Launches started by the client will be in that mode.
:param launch_uuid_print: Print Launch UUID into passed TextIO or by default to stdout.
:param print_output: Set output stream for Launch UUID printing.
:param truncate_attributes: Truncate test item attributes to default maximum length.
:param client: ReportPortal async Client instance to use. If set, all above arguments
will be ignored.
:param launch_uuid: A launch UUID to use instead of starting own one.
Expand Down Expand Up @@ -1009,6 +1014,7 @@ def __init__(
:param mode: Launch mode, all Launches started by the client will be in that mode.
:param launch_uuid_print: Print Launch UUID into passed TextIO or by default to stdout.
:param print_output: Set output stream for Launch UUID printing.
:param truncate_attributes: Truncate test item attributes to default maximum length.
:param client: ReportPortal async Client instance to use. If set, all above arguments
will be ignored.
:param launch_uuid: A launch UUID to use instead of starting own one.
Expand Down Expand Up @@ -1384,6 +1390,7 @@ def __init__(
:param mode: Launch mode, all Launches started by the client will be in that mode.
:param launch_uuid_print: Print Launch UUID into passed TextIO or by default to stdout.
:param print_output: Set output stream for Launch UUID printing.
:param truncate_attributes: Truncate test item attributes to default maximum length.
:param client: ReportPortal async Client instance to use. If set, all above arguments
will be ignored.
:param launch_uuid: A launch UUID to use instead of starting own one.
Expand All @@ -1406,7 +1413,7 @@ def __init__(
self.shutdown_timeout = shutdown_timeout
self.__init_task_list(task_list, task_mutex)
self.__init_loop(loop)
if type(launch_uuid) == str:
if type(launch_uuid) is str:
super().__init__(endpoint, project,
launch_uuid=self.create_task(self.__return_value(launch_uuid)), **kwargs)
else:
Expand Down Expand Up @@ -1561,6 +1568,7 @@ def __init__(
:param mode: Launch mode, all Launches started by the client will be in that mode.
:param launch_uuid_print: Print Launch UUID into passed TextIO or by default to stdout.
:param print_output: Set output stream for Launch UUID printing.
:param truncate_attributes: Truncate test item attributes to default maximum length.
:param client: ReportPortal async Client instance to use. If set, all above arguments
will be ignored.
:param launch_uuid: A launch UUID to use instead of starting own one.
Expand Down Expand Up @@ -1588,7 +1596,7 @@ def __init__(
self.__init_task_list(task_list, task_mutex)
self.__last_run_time = datetime.time()
self.__init_loop(loop)
if type(launch_uuid) == str:
if type(launch_uuid) is str:
super().__init__(endpoint, project,
launch_uuid=self.create_task(self.__return_value(launch_uuid)), **kwargs)
else:
Expand Down
16 changes: 10 additions & 6 deletions reportportal_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ class RPClient(RP):
mode: str
launch_uuid_print: Optional[bool]
print_output: OutputType
truncate_attributes: bool
_skip_analytics: str
_item_stack: LifoQueue
_log_batcher: LogBatcher[RPRequestLog]
Expand Down Expand Up @@ -433,6 +434,7 @@ def __init__(
launch_uuid_print: bool = False,
print_output: OutputType = OutputType.STDOUT,
log_batcher: Optional[LogBatcher[RPRequestLog]] = None,
truncate_attributes: bool = True,
**kwargs: Any
) -> None:
"""Initialize the class instance with arguments.
Expand All @@ -455,6 +457,7 @@ def __init__(
:param launch_uuid_print: Print Launch UUID into passed TextIO or by default to stdout.
:param print_output: Set output stream for Launch UUID printing.
:param log_batcher: Use existing LogBatcher instance instead of creation of own one.
:param truncate_attributes: Truncate test item attributes to default maximum length.
"""
set_current(self)
self.api_v1, self.api_v2 = 'v1', 'v2'
Expand Down Expand Up @@ -490,6 +493,7 @@ def __init__(
self._skip_analytics = getenv('AGENT_NO_ANALYTICS')
self.launch_uuid_print = launch_uuid_print
self.print_output = print_output
self.truncate_attributes = truncate_attributes

self.api_key = api_key
if not self.api_key:
Expand All @@ -505,7 +509,7 @@ def __init__(
if not self.api_key:
warnings.warn(
message='Argument `api_key` is `None` or empty string, that is not supposed to happen '
'because Report Portal is usually requires an authorization key. Please check '
'because ReportPortal is usually requires an authorization key. Please check '
'your code.',
category=RuntimeWarning,
stacklevel=2
Expand Down Expand Up @@ -538,7 +542,7 @@ def start_launch(self,
request_payload = LaunchStartRequest(
name=name,
start_time=start_time,
attributes=attributes,
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
description=description,
mode=self.mode,
rerun=rerun,
Expand Down Expand Up @@ -601,7 +605,7 @@ def start_test_item(self,
start_time,
item_type,
self.launch_uuid,
attributes=verify_value_length(attributes),
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
code_ref=code_ref,
description=description,
has_stats=has_stats,
Expand Down Expand Up @@ -655,7 +659,7 @@ def finish_test_item(self,
end_time,
self.launch_uuid,
status,
attributes=attributes,
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
description=description,
is_skipped_an_issue=self.is_skipped_an_issue,
issue=issue,
Expand Down Expand Up @@ -691,7 +695,7 @@ def finish_launch(self,
request_payload = LaunchFinishRequest(
end_time,
status=status,
attributes=attributes,
attributes=verify_value_length(attributes) if self.truncate_attributes else attributes,
description=kwargs.get('description')
).payload
response = HttpRequest(self.session.put, url=url, json=request_payload,
Expand All @@ -718,7 +722,7 @@ def update_test_item(self, item_uuid: str, attributes: Optional[Union[list, dict
"""
data = {
'description': description,
'attributes': verify_value_length(attributes),
'attributes': verify_value_length(attributes) if self.truncate_attributes else attributes,
}
item_id = self.get_item_id_by_uuid(item_uuid)
url = uri_join(self.base_url_v1, 'item', item_id, 'update')
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from setuptools import setup, find_packages

__version__ = '5.5.1'
__version__ = '5.5.2'

TYPE_STUBS = ['*.pyi']

Expand Down
28 changes: 28 additions & 0 deletions tests/aio/test_aio_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,3 +772,31 @@ async def test_get_launch_ui_url(aio_client: Client):
session.get.assert_called_once()
call_args = session.get.call_args_list[0]
assert expected_uri == call_args[0][0]


@pytest.mark.skipif(sys.version_info < (3, 8),
reason='the test requires AsyncMock which was introduced in Python 3.8')
@pytest.mark.parametrize(
'method, mock_method, call_method, arguments',
[
('start_launch', mock_basic_post_response, 'post', ['Test Launch', timestamp()]),
('start_test_item', mock_basic_post_response, 'post', ['test_launch_uuid', 'Test Item', timestamp(),
'SUITE']),
('finish_test_item', mock_basic_post_response, 'put', ['test_launch_uuid', 'test_item_uuid',
timestamp()]),
('finish_launch', mock_basic_post_response, 'put', ['test_launch_uuid', timestamp()]),
('update_test_item', mock_basic_post_response, 'put', ['test_item_uuid']),
]
)
@pytest.mark.asyncio
async def test_attribute_truncation(aio_client: Client, method, mock_method, call_method, arguments):
# noinspection PyTypeChecker
session: mock.AsyncMock = await aio_client.session()
mock_method(session)

await getattr(aio_client, method)(*arguments, **{'attributes': {'key': 'value' * 26}})
getattr(session, call_method).assert_called_once()
kwargs = getattr(session, call_method).call_args_list[0][1]
assert 'attributes' in kwargs['json']
assert kwargs['json']['attributes']
assert len(kwargs['json']['attributes'][0]['value']) == 128
24 changes: 24 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,27 @@ def test_client_pickling():
pickled_client = pickle.dumps(client)
unpickled_client = pickle.loads(pickled_client)
assert unpickled_client is not None


@pytest.mark.parametrize(
'method, call_method, arguments',
[
('start_launch', 'post', ['Test Launch', timestamp()]),
('start_test_item', 'post', ['Test Item', timestamp(), 'SUITE']),
('finish_test_item', 'put', ['test_item_uuid', timestamp()]),
('finish_launch', 'put', [timestamp()]),
('update_test_item', 'put', ['test_item_uuid']),
]
)
def test_attribute_truncation(rp_client: RPClient, method, call_method, arguments):
# noinspection PyTypeChecker
session: mock.Mock = rp_client.session
if method != 'start_launch':
rp_client._RPClient__launch_uuid = 'test_launch_id'

getattr(rp_client, method)(*arguments, **{'attributes': {'key': 'value' * 26}})
getattr(session, call_method).assert_called_once()
kwargs = getattr(session, call_method).call_args_list[0][1]
assert 'attributes' in kwargs['json']
assert kwargs['json']['attributes']
assert len(kwargs['json']['attributes'][0]['value']) == 128

0 comments on commit bdd4bfc

Please sign in to comment.