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
2 changes: 1 addition & 1 deletion pytest/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "testsolar-pytestx"
version = "0.1.76"
version = "0.1.77"
description = "Pytest tool for TestSolar"
authors = [
{name = "asiazhang2002", email = "asiazhang2002@gmail.com"},
Expand Down
17 changes: 14 additions & 3 deletions pytest/src/testsolar_pytestx/header_injection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

import threading
import urllib.parse
from typing import Optional, Callable, Any, Dict

from loguru import logger
Expand Down Expand Up @@ -33,6 +34,16 @@ def set_current_test_nodeid(nodeid: Optional[str]) -> None:


# ============ Header 注入逻辑 ============
def _encode_nodeid(nodeid: str) -> str:
"""对nodeid进行URL编码,确保HTTP头部值只含ASCII字符。

HTTP头部值要求latin-1编码,含中文等非ASCII字符的用例名会导致
UnicodeEncodeError,故注入前统一percent-encode。接收方可用
urllib.parse.unquote还原原始用例名。
"""
return urllib.parse.quote(nodeid, safe="")


def _inject_header_to_dict(headers: Optional[Dict[str, Any]], nodeid: str) -> Dict[str, Any]:
"""
向请求头字典注入X-Testsolar-Testcase
Expand All @@ -53,7 +64,7 @@ def _inject_header_to_dict(headers: Optional[Dict[str, Any]], nodeid: str) -> Di
# 创建副本避免修改原始dict
headers = headers.copy()

headers["X-Testsolar-Testcase"] = nodeid
headers["X-Testsolar-Testcase"] = _encode_nodeid(nodeid)
return headers


Expand Down Expand Up @@ -119,11 +130,11 @@ def _patched_httplib_request(
if nodeid:
# httplib的headers参数默认是{}而不是None
if final_headers is None:
final_headers = {"X-Testsolar-Testcase": nodeid}
final_headers = {"X-Testsolar-Testcase": _encode_nodeid(nodeid)}
else:
# 创建副本避免修改原始headers
new_headers = dict(final_headers)
new_headers["X-Testsolar-Testcase"] = nodeid
new_headers["X-Testsolar-Testcase"] = _encode_nodeid(nodeid)
final_headers = new_headers

return _original_httplib_request(self, method, url, body, final_headers, **kwargs)
Expand Down
20 changes: 16 additions & 4 deletions pytest/tests/test_header_injection.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for header_injection module."""

import threading
import urllib.parse
from typing import Any, Dict, Optional
from unittest import mock

Expand Down Expand Up @@ -78,12 +79,12 @@ class TestInjectHeaderToDict:
def test_inject_header_to_none_headers(self) -> None:
"""Test injecting header when headers is None."""
result = _inject_header_to_dict(None, "test::nodeid")
assert result == {"X-Testsolar-Testcase": "test::nodeid"}
assert result == {"X-Testsolar-Testcase": "test%3A%3Anodeid"}

def test_inject_header_to_empty_dict(self) -> None:
"""Test injecting header to empty dict."""
result = _inject_header_to_dict({}, "test::nodeid")
assert result == {"X-Testsolar-Testcase": "test::nodeid"}
assert result == {"X-Testsolar-Testcase": "test%3A%3Anodeid"}

def test_inject_header_to_existing_dict(self) -> None:
"""Test injecting header to dict with existing headers."""
Expand All @@ -92,11 +93,22 @@ def test_inject_header_to_existing_dict(self) -> None:

assert result == {
"Content-Type": "application/json",
"X-Testsolar-Testcase": "test::nodeid",
"X-Testsolar-Testcase": "test%3A%3Anodeid",
}
# Original should not be modified
assert "X-Testsolar-Testcase" not in original

def test_inject_header_encodes_non_ascii_nodeid(self) -> None:
"""含中文等非ASCII字符的nodeid需被percent-encode,避免latin-1编码错误。"""
nodeid = "test_features.py::test_重启运行中实例"
result = _inject_header_to_dict(None, nodeid)

encoded = result["X-Testsolar-Testcase"]
# 注入值必须可被latin-1编码(HTTP头部要求),否则抛UnicodeEncodeError
assert isinstance(encoded.encode("latin-1"), bytes)
# 接收方unquote可还原原始用例名
assert urllib.parse.unquote(encoded) == nodeid

def test_inject_header_does_not_modify_original(self) -> None:
"""Test that original headers dict is not modified."""
original: Dict[str, str] = {"Authorization": "Bearer token"}
Expand Down Expand Up @@ -166,7 +178,7 @@ def test_requests_header_injection(self) -> None:
if mock_send.called:
request = mock_send.call_args[0][0]
assert "X-Testsolar-Testcase" in request.headers
assert request.headers["X-Testsolar-Testcase"] == "test_file.py::test_case"
assert request.headers["X-Testsolar-Testcase"] == "test_file.py%3A%3Atest_case"

def test_requests_no_injection_when_nodeid_not_set(self) -> None:
"""Test that requests does not inject header when nodeid is not set."""
Expand Down
2 changes: 1 addition & 1 deletion pytest/testtool.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ schemaVersion: 1.0
name: pytest
nameZh: pytest自动化测试
lang: python
version: '0.1.76'
version: '0.1.77'
langType: INTERPRETED
description: |-
pytest是一个成熟的全功能Python测试工具,可以帮助您编写更好的程序。此测试工具允许您在TestSolar上运行pytest。
Expand Down
Loading