Skip to content

Commit ae412e6

Browse files
committed
Add tests
1 parent 7ebbc1a commit ae412e6

1 file changed

Lines changed: 342 additions & 0 deletions

File tree

instrumentation/opentelemetry-instrumentation-urllib3/tests/test_urllib3_integration.py

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
1415
import json
1516
import typing
1617
from unittest import mock
@@ -609,3 +610,344 @@ def test_no_op_tracer_provider(self):
609610
response = self.perform_request(self.HTTP_URL)
610611
self.assertEqual(b"Hello!", response.data)
611612
self.assert_span(num_spans=0)
613+
614+
def test_custom_response_headers_captured(self):
615+
URLLib3Instrumentor().uninstrument()
616+
URLLib3Instrumentor().instrument(
617+
captured_response_headers=["X-Custom-Header", "X-Another-Header"]
618+
)
619+
620+
response_headers = {
621+
"X-Custom-Header": "custom-value",
622+
"X-Another-Header": "another-value",
623+
}
624+
url = "http://mock//capture_headers"
625+
httpretty.register_uri(
626+
httpretty.GET, url, body="Hello!", adding_headers=response_headers
627+
)
628+
self.perform_request(url)
629+
630+
span = self.assert_span(num_spans=1)
631+
self.assertEqual(
632+
span.attributes["http.response.header.x_custom_header"],
633+
("custom-value",),
634+
)
635+
self.assertEqual(
636+
span.attributes["http.response.header.x_another_header"],
637+
("another-value",),
638+
)
639+
self.assertNotIn(
640+
"http.response.header.x_excluded_header", span.attributes
641+
)
642+
643+
def test_custom_headers_not_captured_when_not_configured(self):
644+
"""Test that headers are not captured when env vars are not set."""
645+
URLLib3Instrumentor().uninstrument()
646+
URLLib3Instrumentor().instrument()
647+
648+
self.perform_request(
649+
self.HTTP_URL,
650+
headers={"X-Request-Header": "request-value"},
651+
)
652+
653+
span = self.assert_span(num_spans=1)
654+
self.assertNotIn(
655+
"http.request.header.x_request_header", span.attributes
656+
)
657+
self.assertNotIn(
658+
"http.response.header.x_response_header", span.attributes
659+
)
660+
661+
def test_sensitive_headers_sanitized(self):
662+
"""Test that sensitive header values are redacted."""
663+
URLLib3Instrumentor().uninstrument()
664+
URLLib3Instrumentor().instrument(
665+
captured_request_headers=["Authorization", "X-Api-Key"],
666+
captured_response_headers=["Set-Cookie", "X-Secret"],
667+
sensitive_headers=[
668+
"Authorization",
669+
"X-Api-Key",
670+
"Set-Cookie",
671+
"X-Secret",
672+
],
673+
)
674+
675+
response_headers = {
676+
"Set-Cookie": "session=abc123",
677+
"X-Secret": "secret",
678+
}
679+
url = "http://mock//capture_headers"
680+
httpretty.register_uri(
681+
httpretty.GET, url, body="Hello!", adding_headers=response_headers
682+
)
683+
self.perform_request(
684+
url,
685+
headers={
686+
"Authorization": "Bearer secret-token",
687+
"X-Api-Key": "secret-key",
688+
},
689+
)
690+
691+
span = self.assert_span(num_spans=1)
692+
self.assertEqual(
693+
span.attributes["http.request.header.authorization"],
694+
("[REDACTED]",),
695+
)
696+
self.assertEqual(
697+
span.attributes["http.request.header.x_api_key"],
698+
("[REDACTED]",),
699+
)
700+
self.assertEqual(
701+
span.attributes["http.response.header.set_cookie"],
702+
("[REDACTED]",),
703+
)
704+
self.assertEqual(
705+
span.attributes["http.response.header.x_secret"],
706+
("[REDACTED]",),
707+
)
708+
709+
def test_custom_headers_with_regex(self):
710+
"""Test that header capture works with regex patterns."""
711+
URLLib3Instrumentor().uninstrument()
712+
URLLib3Instrumentor().instrument(
713+
captured_request_headers=["X-Custom-Request-.*"],
714+
captured_response_headers=["X-Custom-Response-.*"],
715+
)
716+
717+
response_headers = {
718+
"X-Custom-Response-A": "value-A",
719+
"X-Custom-Response-B": "value-B",
720+
"X-Other-Response-Header": "other-value",
721+
}
722+
url = "http://mock//capture_headers"
723+
httpretty.register_uri(
724+
httpretty.GET, url, body="Hello!", adding_headers=response_headers
725+
)
726+
self.perform_request(
727+
url,
728+
headers={
729+
"X-Custom-Request-One": "value-one",
730+
"X-Custom-Request-Two": "value-two",
731+
"X-Other-Request-Header": "other-value",
732+
},
733+
)
734+
735+
span = self.assert_span(num_spans=1)
736+
self.assertEqual(
737+
span.attributes["http.request.header.x_custom_request_one"],
738+
("value-one",),
739+
)
740+
self.assertEqual(
741+
span.attributes["http.request.header.x_custom_request_two"],
742+
("value-two",),
743+
)
744+
self.assertNotIn(
745+
"http.request.header.x_other_request_header", span.attributes
746+
)
747+
self.assertEqual(
748+
span.attributes["http.response.header.x_custom_response_a"],
749+
("value-A",),
750+
)
751+
self.assertEqual(
752+
span.attributes["http.response.header.x_custom_response_b"],
753+
("value-B",),
754+
)
755+
self.assertNotIn(
756+
"http.response.header.x_other_response_header", span.attributes
757+
)
758+
759+
def test_custom_headers_case_insensitive(self):
760+
"""Test that header capture is case-insensitive."""
761+
URLLib3Instrumentor().uninstrument()
762+
URLLib3Instrumentor().instrument(
763+
captured_request_headers=["x-request-header"],
764+
captured_response_headers=["x-response-header"],
765+
)
766+
767+
response_headers = {"X-ReSPoNse-HeaDER": "custom-value"}
768+
url = "http://mock//capture_headers"
769+
httpretty.register_uri(
770+
httpretty.GET, url, body="Hello!", adding_headers=response_headers
771+
)
772+
self.perform_request(
773+
url,
774+
headers={"X-ReQuESt-HeaDER": "custom-value"},
775+
)
776+
777+
span = self.assert_span(num_spans=1)
778+
self.assertEqual(
779+
span.attributes["http.request.header.x_request_header"],
780+
("custom-value",),
781+
)
782+
self.assertEqual(
783+
span.attributes["http.response.header.x_response_header"],
784+
("custom-value",),
785+
)
786+
787+
def test_standard_http_headers_captured(self):
788+
"""Test that standard HTTP headers can be captured."""
789+
URLLib3Instrumentor().uninstrument()
790+
URLLib3Instrumentor().instrument(
791+
captured_request_headers=["Content-Type", "Accept"],
792+
captured_response_headers=["Content-Type", "Server"],
793+
)
794+
795+
response_headers = {
796+
"Content-Type": "text/plain",
797+
"Server": "TestServer/1.0",
798+
}
799+
url = "http://mock//capture_headers"
800+
httpretty.register_uri(
801+
httpretty.GET, url, body="Hello!", adding_headers=response_headers
802+
)
803+
self.perform_request(
804+
url,
805+
headers={
806+
"Content-Type": "application/json",
807+
"Accept": "application/json",
808+
},
809+
)
810+
811+
span = self.assert_span(num_spans=1)
812+
self.assertEqual(
813+
span.attributes["http.request.header.content_type"],
814+
("application/json",),
815+
)
816+
self.assertEqual(
817+
span.attributes["http.request.header.accept"],
818+
("application/json",),
819+
)
820+
self.assertEqual(
821+
span.attributes["http.response.header.content_type"],
822+
("text/plain",),
823+
)
824+
self.assertEqual(
825+
span.attributes["http.response.header.server"],
826+
("TestServer/1.0",),
827+
)
828+
829+
def test_capture_all_request_headers(self):
830+
"""Test that all request headers can be captured with .* pattern."""
831+
URLLib3Instrumentor().uninstrument()
832+
URLLib3Instrumentor().instrument(captured_request_headers=[".*"])
833+
834+
self.perform_request(
835+
self.HTTP_URL,
836+
headers={
837+
"X-Header-One": "value1",
838+
"X-Header-Two": "value2",
839+
"X-Header-Three": "value3",
840+
},
841+
)
842+
843+
span = self.assert_span(num_spans=1)
844+
self.assertEqual(
845+
span.attributes["http.request.header.x_header_one"],
846+
("value1",),
847+
)
848+
self.assertEqual(
849+
span.attributes["http.request.header.x_header_two"],
850+
("value2",),
851+
)
852+
self.assertEqual(
853+
span.attributes["http.request.header.x_header_three"],
854+
("value3",),
855+
)
856+
857+
def test_capture_all_response_headers(self):
858+
"""Test that all response headers can be captured with .* pattern."""
859+
URLLib3Instrumentor().uninstrument()
860+
URLLib3Instrumentor().instrument(captured_response_headers=[".*"])
861+
862+
response_headers = {
863+
"X-Response-One": "value1",
864+
"X-Response-Two": "value2",
865+
"X-Response-Three": "value3",
866+
}
867+
url = "http://mock//capture_headers"
868+
httpretty.register_uri(
869+
httpretty.GET, url, body="Hello!", adding_headers=response_headers
870+
)
871+
self.perform_request(url)
872+
873+
span = self.assert_span(num_spans=1)
874+
self.assertEqual(
875+
span.attributes["http.response.header.x_response_one"],
876+
("value1",),
877+
)
878+
self.assertEqual(
879+
span.attributes["http.response.header.x_response_two"],
880+
("value2",),
881+
)
882+
self.assertEqual(
883+
span.attributes["http.response.header.x_response_three"],
884+
("value3",),
885+
)
886+
887+
def test_sanitize_with_regex_pattern(self):
888+
"""Test that sanitization works with regex patterns."""
889+
URLLib3Instrumentor().uninstrument()
890+
URLLib3Instrumentor().instrument(
891+
captured_request_headers=["X-Test.*"],
892+
sensitive_headers=[".*secret.*"],
893+
)
894+
895+
self.perform_request(
896+
self.HTTP_URL,
897+
headers={
898+
"X-Test": "normal-value",
899+
"X-Test-Secret": "secret-value",
900+
},
901+
)
902+
903+
span = self.assert_span(num_spans=1)
904+
self.assertEqual(
905+
span.attributes["http.request.header.x_test"],
906+
("normal-value",),
907+
)
908+
self.assertEqual(
909+
span.attributes["http.request.header.x_test_secret"],
910+
("[REDACTED]",),
911+
)
912+
913+
@mock.patch.dict(
914+
"os.environ",
915+
{
916+
"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_REQUEST": "x-request-one,x-request-two",
917+
"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_CLIENT_RESPONSE": "x-response-one",
918+
"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS": "x-request-two",
919+
},
920+
)
921+
def test_capture_and_sanitize_environment_variables(self):
922+
URLLib3Instrumentor().uninstrument()
923+
URLLib3Instrumentor().instrument()
924+
925+
response_headers = {
926+
"X-Response-One": "value1",
927+
"X-Response-Two": "value2",
928+
}
929+
url = "http://mock//capture_headers"
930+
httpretty.register_uri(
931+
httpretty.GET, url, body="Hello!", adding_headers=response_headers
932+
)
933+
self.perform_request(
934+
url, headers={"x-request-one": "one", "x-request-two": "two"}
935+
)
936+
937+
span = self.assert_span(num_spans=1)
938+
self.assertEqual(
939+
span.attributes["http.request.header.x_request_one"],
940+
("one",),
941+
)
942+
self.assertEqual(
943+
span.attributes["http.request.header.x_request_two"],
944+
("[REDACTED]",),
945+
)
946+
self.assertEqual(
947+
span.attributes["http.response.header.x_response_one"],
948+
("value1",),
949+
)
950+
self.assertNotIn(
951+
"http.response.header.x_response_two",
952+
span.attributes,
953+
)

0 commit comments

Comments
 (0)