Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release #223

Merged
merged 5 commits into from
Oct 17, 2023
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]
### 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