Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

trcli

Package Overview
Dependencies
Maintainers
1
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

trcli - npm Package Compare versions

Comparing version
1.12.3
to
1.12.4
+1
-1
PKG-INFO
Metadata-Version: 2.4
Name: trcli
Version: 1.12.3
Version: 1.12.4
License-File: LICENSE.md

@@ -5,0 +5,0 @@ Requires-Dist: click<8.2.2,>=8.1.0

@@ -36,3 +36,3 @@ ![Tests](https://github.com/gurock/trcli/actions/workflows/python-app.yml/badge.svg)

```
TestRail CLI v1.12.3
TestRail CLI v1.12.4
Copyright 2025 Gurock Software GmbH - www.gurock.com

@@ -51,3 +51,3 @@ Supported and loaded modules:

$ trcli --help
TestRail CLI v1.12.3
TestRail CLI v1.12.4
Copyright 2025 Gurock Software GmbH - www.gurock.com

@@ -1099,3 +1099,3 @@ Usage: trcli [OPTIONS] COMMAND [ARGS]...

$ trcli add_run --help
TestRail CLI v1.12.3
TestRail CLI v1.12.4
Copyright 2025 Gurock Software GmbH - www.gurock.com

@@ -1224,3 +1224,3 @@ Usage: trcli add_run [OPTIONS]

$ trcli parse_openapi --help
TestRail CLI v1.12.3
TestRail CLI v1.12.4
Copyright 2025 Gurock Software GmbH - www.gurock.com

@@ -1227,0 +1227,0 @@ Usage: trcli parse_openapi [OPTIONS]

@@ -422,1 +422,37 @@ import pytest

assert headers.get("Content-Type") != "application/json"
@pytest.mark.api_client
def test_empty_response_is_valid(self, api_resources_maker, requests_mock):
"""Test that empty response body with HTTP 200 is treated as valid (e.g., for DELETE operations)."""
api_client = api_resources_maker()
api_client.uploader_metadata = "test_metadata_value"
# DELETE operations may return empty body with HTTP 200
requests_mock.post(create_url("delete_section/123"), status_code=200, content=b"")
response = api_client.send_post("delete_section/123")
# Verify that the request was made only once (no retry)
assert requests_mock.call_count == 1
# Verify successful response with empty dict
check_response(200, {}, "", response)
@pytest.mark.api_client
def test_metadata_header_sent_when_enabled(self, api_resources_maker, requests_mock):
"""Test that X-Uploader-Metadata header is sent when enabled."""
api_client = api_resources_maker()
test_metadata = "test_metadata_value"
api_client.uploader_metadata = test_metadata
requests_mock.get(create_url("get_projects"), json=FAKE_PROJECT_DATA)
response = api_client.send_get("get_projects")
# Check that metadata header was sent
request_headers = requests_mock.last_request.headers
assert "X-Uploader-Metadata" in request_headers
assert request_headers["X-Uploader-Metadata"] == test_metadata
# Verify successful response
check_response(200, FAKE_PROJECT_DATA, "", response)
Metadata-Version: 2.4
Name: trcli
Version: 1.12.3
Version: 1.12.4
License-File: LICENSE.md

@@ -5,0 +5,0 @@ Requires-Dist: click<8.2.2,>=8.1.0

@@ -1,1 +0,1 @@

__version__ = "1.12.3"
__version__ = "1.12.4"

@@ -53,3 +53,3 @@ import json

verify: bool = True,
proxy: str = None, #added proxy params
proxy: str = None, # added proxy params
proxy_user: str = None,

@@ -70,5 +70,5 @@ noproxy: str = None,

self.proxy_user = proxy_user
self.noproxy = noproxy.split(',') if noproxy else []
self.uploader_metadata = uploader_metadata
self.noproxy = noproxy.split(",") if noproxy else []
self.uploader_metadata = uploader_metadata
if not host_name.endswith("/"):

@@ -90,3 +90,5 @@ host_name = host_name + "/"

def send_post(self, uri: str, payload: dict = None, files: Dict[str, Path] = None, as_form_data: bool = False) -> APIClientResult:
def send_post(
self, uri: str, payload: dict = None, files: Dict[str, Path] = None, as_form_data: bool = False
) -> APIClientResult:
"""

@@ -101,3 +103,5 @@ Sends POST request to host specified by host_name.

def __send_request(self, method: str, uri: str, payload: dict, files: Dict[str, Path] = None, as_form_data: bool = False) -> APIClientResult:
def __send_request(
self, method: str, uri: str, payload: dict, files: Dict[str, Path] = None, as_form_data: bool = False
) -> APIClientResult:
status_code = -1

@@ -124,8 +128,8 @@ response_text = ""

request_kwargs = {
'url': url,
'auth': auth,
'headers': headers,
'timeout': self.timeout,
'verify': self.verify,
'proxies': proxies
"url": url,
"auth": auth,
"headers": headers,
"timeout": self.timeout,
"verify": self.verify,
"proxies": proxies,
}

@@ -139,13 +143,13 @@ if files:

request_kwargs["json"] = payload
response = requests.post(**request_kwargs)
else:
response = requests.get(
url=url,
auth=auth,
json=payload,
timeout=self.timeout,
verify=self.verify,
url=url,
auth=auth,
json=payload,
timeout=self.timeout,
verify=self.verify,
headers=headers,
proxies=proxies
proxies=proxies,
)

@@ -173,5 +177,3 @@ except InvalidProxyURL:

except RequestException as e:
error_message = FAULT_MAPPING[
"unexpected_error_during_request_send"
].format(request=e.request)
error_message = FAULT_MAPPING["unexpected_error_during_request_send"].format(request=e.request)
self.verbose_logging_function(verbose_log_message)

@@ -193,15 +195,16 @@ break

except (JSONDecodeError, ValueError):
response_preview = response.content[:200].decode('utf-8', errors='ignore')
response_text = str(response.content)
error_message = FAULT_MAPPING["invalid_json_response"].format(
status_code=status_code,
response_preview=response_preview
)
if len(response.content) == 0:
# Empty response with HTTP 200 is valid for certain operations like delete
response_text = {}
error_message = ""
else:
response_preview = response.content[:200].decode("utf-8", errors="ignore")
response_text = str(response.content)
error_message = FAULT_MAPPING["invalid_json_response"].format(
status_code=status_code, response_preview=response_preview
)
except AttributeError:
error_message = ""
verbose_log_message = (
verbose_log_message
+ APIClient.format_response_for_vlog(
response.status_code, response_text
)
verbose_log_message = verbose_log_message + APIClient.format_response_for_vlog(
response.status_code, response_text
)

@@ -222,3 +225,3 @@ if verbose_log_message:

if self.proxy_user:
user_pass_encoded = b64encode(self.proxy_user.encode('utf-8')).decode('utf-8')
user_pass_encoded = b64encode(self.proxy_user.encode("utf-8")).decode("utf-8")

@@ -255,5 +258,5 @@ # Add Proxy-Authorization header

if self.noproxy:
# Ensure noproxy is a list or tuple
# Ensure noproxy is a list or tuple
if isinstance(self.noproxy, str):
self.noproxy = self.noproxy.split(',')
self.noproxy = self.noproxy.split(",")
if host in self.noproxy:

@@ -267,3 +270,3 @@ print(f"Bypassing proxy for host: {host}")

#print(f"Parsed URL: {url}, Proxy: {self.proxy} , NoProxy: {self.noproxy}")
# print(f"Parsed URL: {url}, Proxy: {self.proxy} , NoProxy: {self.noproxy}")

@@ -277,11 +280,9 @@ # Define the proxy dictionary

"http": self.proxy, # Use HTTP proxy for HTTP traffic
"https": self.proxy # Also use HTTP proxy for HTTPS traffic
"https": self.proxy, # Also use HTTP proxy for HTTPS traffic
}
else:
# If the proxy is HTTPS, route accordingly
proxy_dict = {
scheme: self.proxy # Match the proxy scheme with the target URL scheme
}
proxy_dict = {scheme: self.proxy} # Match the proxy scheme with the target URL scheme
#print(f"Using proxy: {proxy_dict}")
# print(f"Using proxy: {proxy_dict}")
return proxy_dict

@@ -331,7 +332,3 @@

def format_request_for_vlog(method: str, url: str, payload: dict, headers: dict = None):
log_message = (
f"\n**** API Call\n"
f"method: {method}\n"
f"url: {url}\n"
)
log_message = f"\n**** API Call\n" f"method: {method}\n" f"url: {url}\n"
if headers:

@@ -338,0 +335,0 @@ log_message += "headers:\n"