/mountinfo` more generally).
+
+The `cases.json` file lists each filename in this directory containing
+example `/proc/self/mountinfo` content, and the expected Docker container ID that
+should be parsed from that file.
diff --git a/tests/cross_agent/fixtures/docker_container_id_v2/cases.json b/tests/cross_agent/fixtures/docker_container_id_v2/cases.json
new file mode 100644
index 0000000000..83d6360a31
--- /dev/null
+++ b/tests/cross_agent/fixtures/docker_container_id_v2/cases.json
@@ -0,0 +1,36 @@
+[
+ {
+ "filename": "docker-20.10.16.txt",
+ "containerId": "84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce",
+ "expectedMetrics": null
+ },
+ {
+ "filename": "docker-24.0.2.txt",
+ "containerId": "b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65",
+ "expectedMetrics": null
+ },
+ {
+ "filename": "empty.txt",
+ "containerId": null,
+ "expectedMetrics": null
+ },
+ {
+ "filename": "invalid-characters.txt",
+ "containerId": null,
+ "expectedMetrics": null
+ },
+ {
+ "filename": "docker-too-long.txt",
+ "containerId": null,
+ "expectedMetrics": null
+ },
+ {
+ "filename": "invalid-length.txt",
+ "containerId": null,
+ "expectedMetrics": {
+ "Supportability/utilization/docker/error": {
+ "callCount": 1
+ }
+ }
+ }
+]
diff --git a/tests/cross_agent/fixtures/docker_container_id_v2/docker-20.10.16.txt b/tests/cross_agent/fixtures/docker_container_id_v2/docker-20.10.16.txt
new file mode 100644
index 0000000000..ce2b1bedf6
--- /dev/null
+++ b/tests/cross_agent/fixtures/docker_container_id_v2/docker-20.10.16.txt
@@ -0,0 +1,24 @@
+519 413 0:152 / / rw,relatime master:180 - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/YCID3333O5VYPYDNTQRZX4GI67:/var/lib/docker/overlay2/l/G7H4TULAFM2UBFRL7QFQPUNXY5:/var/lib/docker/overlay2/l/RLC4GCL75VGXXXYJJO57STHIYN:/var/lib/docker/overlay2/l/YOZKNWFAP6YX74XEKPHX4KG4UN:/var/lib/docker/overlay2/l/46EQ6YX5PQQZ4Z3WCSMQ6Z4YWI:/var/lib/docker/overlay2/l/KGKX3Z5ZMOCDWOFKBS2FSHMQMQ:/var/lib/docker/overlay2/l/CKFYAF4TXZD4RCE6RG6UNL5WVI,upperdir=/var/lib/docker/overlay2/358c429f7b04ee5a228b94efaebe3413a98fcc676b726f078fe875727e3bddd2/diff,workdir=/var/lib/docker/overlay2/358c429f7b04ee5a228b94efaebe3413a98fcc676b726f078fe875727e3bddd2/work
+520 519 0:155 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
+521 519 0:156 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+522 521 0:157 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+523 519 0:158 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
+524 523 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw
+525 521 0:154 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
+526 521 0:159 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
+527 519 254:1 /docker/volumes/3237dea4f8022f1addd7b6f072a9c847eb3e5b8df0d599f462ba7040884d4618/_data /data rw,relatime master:28 - ext4 /dev/vda1 rw
+528 519 254:1 /docker/containers/84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw
+529 519 254:1 /docker/containers/84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw
+530 519 254:1 /docker/containers/84cf3472a20d1bfb4b50e48b6ff50d96dfcd812652d76dd907951e6f98997bce/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw
+414 521 0:157 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+415 520 0:155 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw
+416 520 0:155 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw
+417 520 0:155 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw
+418 520 0:155 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw
+419 520 0:155 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw
+420 520 0:160 / /proc/acpi ro,relatime - tmpfs tmpfs ro
+421 520 0:156 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+422 520 0:156 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+423 520 0:156 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+424 520 0:156 /null /proc/sched_debug rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+425 523 0:161 / /sys/firmware ro,relatime - tmpfs tmpfs ro
diff --git a/tests/cross_agent/fixtures/docker_container_id_v2/docker-24.0.2.txt b/tests/cross_agent/fixtures/docker_container_id_v2/docker-24.0.2.txt
new file mode 100644
index 0000000000..1725e7726a
--- /dev/null
+++ b/tests/cross_agent/fixtures/docker_container_id_v2/docker-24.0.2.txt
@@ -0,0 +1,21 @@
+1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
+1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
+1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw
+1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
+1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
+1025 1013 254:1 /docker/containers/b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard
+1026 1013 254:1 /docker/containers/b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard
+1027 1013 254:1 /docker/containers/b0a24eed1b031271d8ba0784b8f354b3da892dfd08bbcf14dd7e8a1cf9292f65/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard
+717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw
+719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw
+720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw
+721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw
+723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw
+726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro
+727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro
diff --git a/tests/cross_agent/fixtures/docker_container_id_v2/docker-too-long.txt b/tests/cross_agent/fixtures/docker_container_id_v2/docker-too-long.txt
new file mode 100644
index 0000000000..608eaf7a49
--- /dev/null
+++ b/tests/cross_agent/fixtures/docker_container_id_v2/docker-too-long.txt
@@ -0,0 +1,21 @@
+1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
+1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
+1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw
+1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
+1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
+1025 1013 254:1 /docker/containers/3ccfa00432798ff38f85839de1e396f771b4acbe9f4ddea0a761c39b9790a7821/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard
+1026 1013 254:1 /docker/containers/3ccfa00432798ff38f85839de1e396f771b4acbe9f4ddea0a761c39b9790a7821/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard
+1027 1013 254:1 /docker/containers/3ccfa00432798ff38f85839de1e396f771b4acbe9f4ddea0a761c39b9790a7821/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard
+717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw
+719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw
+720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw
+721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw
+723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw
+726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro
+727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro
diff --git a/tests/cross_agent/fixtures/docker_container_id_v2/empty.txt b/tests/cross_agent/fixtures/docker_container_id_v2/empty.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/cross_agent/fixtures/docker_container_id_v2/invalid-characters.txt b/tests/cross_agent/fixtures/docker_container_id_v2/invalid-characters.txt
new file mode 100644
index 0000000000..b561475ac6
--- /dev/null
+++ b/tests/cross_agent/fixtures/docker_container_id_v2/invalid-characters.txt
@@ -0,0 +1,21 @@
+1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
+1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
+1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw
+1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
+1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
+1025 1013 254:1 /docker/containers/WRONGINCORRECTINVALIDCHARSERRONEOUSBADPHONYBROKEN2TERRIBLENOPE55/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard
+1026 1013 254:1 /docker/containers/WRONGINCORRECTINVALIDCHARSERRONEOUSBADPHONYBROKEN2TERRIBLENOPE55/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard
+1027 1013 254:1 /docker/containers/WRONGINCORRECTINVALIDCHARSERRONEOUSBADPHONYBROKEN2TERRIBLENOPE55/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard
+717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw
+719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw
+720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw
+721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw
+723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw
+726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro
+727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro
diff --git a/tests/cross_agent/fixtures/docker_container_id_v2/invalid-length.txt b/tests/cross_agent/fixtures/docker_container_id_v2/invalid-length.txt
new file mode 100644
index 0000000000..a8987df707
--- /dev/null
+++ b/tests/cross_agent/fixtures/docker_container_id_v2/invalid-length.txt
@@ -0,0 +1,21 @@
+1014 1013 0:269 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
+1019 1013 0:270 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+1020 1019 0:271 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+1021 1013 0:272 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
+1022 1021 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw
+1023 1019 0:268 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
+1024 1019 0:273 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
+1025 1013 254:1 /docker/containers/47cbd16b77c5/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw,discard
+1026 1013 254:1 /docker/containers/47cbd16b77c5/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw,discard
+1027 1013 254:1 /docker/containers/47cbd16b77c5/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard
+717 1019 0:271 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
+718 1014 0:269 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw
+719 1014 0:269 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw
+720 1014 0:269 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw
+721 1014 0:269 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw
+723 1014 0:269 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw
+726 1014 0:274 / /proc/acpi ro,relatime - tmpfs tmpfs ro
+727 1014 0:270 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+728 1014 0:270 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+729 1014 0:270 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
+730 1021 0:275 / /sys/firmware ro,relatime - tmpfs tmpfs ro
diff --git a/tests/cross_agent/fixtures/ecs_container_id/ecs_mock_server.py b/tests/cross_agent/fixtures/ecs_container_id/ecs_mock_server.py
new file mode 100644
index 0000000000..5c7853f4b9
--- /dev/null
+++ b/tests/cross_agent/fixtures/ecs_container_id/ecs_mock_server.py
@@ -0,0 +1,114 @@
+import pytest
+import json
+from testing_support.mock_external_http_server import MockExternalHTTPServer
+
+STANDARD_RESPONSE = {
+ "DockerId": "1e1698469422439ea356071e581e8545-2769485393",
+ "Name": "fargateapp",
+ "DockerName": "fargateapp",
+ "Image": "123456789012.dkr.ecr.us-west-2.amazonaws.com/fargatetest:latest",
+ "ImageID": "sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd",
+ "Labels": {
+ "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:123456789012:cluster/testcluster",
+ "com.amazonaws.ecs.container-name": "fargateapp",
+ "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:123456789012:task/testcluster/1e1698469422439ea356071e581e8545",
+ "com.amazonaws.ecs.task-definition-family": "fargatetestapp",
+ "com.amazonaws.ecs.task-definition-version": "7",
+ },
+ "DesiredStatus": "RUNNING",
+ "KnownStatus": "RUNNING",
+ "Limits": {"CPU": 2},
+ "CreatedAt": "2024-04-25T17:38:31.073208914Z",
+ "StartedAt": "2024-04-25T17:38:31.073208914Z",
+ "Type": "NORMAL",
+ "LogDriver": "awslogs",
+ "LogOptions": {
+ "awslogs-create-group": "true",
+ "awslogs-group": "/ecs/fargatetestapp",
+ "awslogs-region": "us-west-2",
+ "awslogs-stream": "ecs/fargateapp/1e1698469422439ea356071e581e8545",
+ },
+ "ContainerARN": "arn:aws:ecs:us-west-2:123456789012:container/testcluster/1e1698469422439ea356071e581e8545/050256a5-a7f3-461c-a16f-aca4eae37b01",
+ "Networks": [
+ {
+ "NetworkMode": "awsvpc",
+ "IPv4Addresses": ["10.10.10.10"],
+ "AttachmentIndex": 0,
+ "MACAddress": "06:d7:3f:49:1d:a7",
+ "IPv4SubnetCIDRBlock": "10.10.10.0/20",
+ "DomainNameServers": ["10.10.10.2"],
+ "DomainNameSearchList": ["us-west-2.compute.internal"],
+ "PrivateDNSName": "ip-10-10-10-10.us-west-2.compute.internal",
+ "SubnetGatewayIpv4Address": "10.10.10.1/20",
+ }
+ ],
+ "Snapshotter": "overlayfs",
+}
+
+NO_ID_RESPONSE = {
+ "Name": "fargateapp",
+ "DockerName": "fargateapp",
+ "Image": "123456789012.dkr.ecr.us-west-2.amazonaws.com/fargatetest:latest",
+ "ImageID": "sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd",
+ "Labels": {
+ "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:123456789012:cluster/testcluster",
+ "com.amazonaws.ecs.container-name": "fargateapp",
+ "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:123456789012:task/testcluster/1e1698469422439ea356071e581e8545",
+ "com.amazonaws.ecs.task-definition-family": "fargatetestapp",
+ "com.amazonaws.ecs.task-definition-version": "7",
+ },
+ "DesiredStatus": "RUNNING",
+ "KnownStatus": "RUNNING",
+ "Limits": {"CPU": 2},
+ "CreatedAt": "2024-04-25T17:38:31.073208914Z",
+ "StartedAt": "2024-04-25T17:38:31.073208914Z",
+ "Type": "NORMAL",
+ "LogDriver": "awslogs",
+ "LogOptions": {
+ "awslogs-create-group": "true",
+ "awslogs-group": "/ecs/fargatetestapp",
+ "awslogs-region": "us-west-2",
+ "awslogs-stream": "ecs/fargateapp/1e1698469422439ea356071e581e8545",
+ },
+ "ContainerARN": "arn:aws:ecs:us-west-2:123456789012:container/testcluster/1e1698469422439ea356071e581e8545/050256a5-a7f3-461c-a16f-aca4eae37b01",
+ "Networks": [
+ {
+ "NetworkMode": "awsvpc",
+ "IPv4Addresses": ["10.10.10.10"],
+ "AttachmentIndex": 0,
+ "MACAddress": "06:d7:3f:49:1d:a7",
+ "IPv4SubnetCIDRBlock": "10.10.10.0/20",
+ "DomainNameServers": ["10.10.10.2"],
+ "DomainNameSearchList": ["us-west-2.compute.internal"],
+ "PrivateDNSName": "ip-10-10-10-10.us-west-2.compute.internal",
+ "SubnetGatewayIpv4Address": "10.10.10.1/20",
+ }
+ ],
+ "Snapshotter": "overlayfs",
+}
+
+
+def simple_get(self):
+ response = json.dumps(STANDARD_RESPONSE).encode("utf-8")
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(response)
+
+
+def bad_response_get(self):
+ response = json.dumps(NO_ID_RESPONSE).encode("utf-8")
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(response)
+
+
+@pytest.fixture(scope="function")
+def mock_server():
+ with MockExternalHTTPServer(handler=simple_get) as mock_server:
+ yield mock_server
+
+
+@pytest.fixture(scope="function")
+def bad_response_mock_server():
+ with MockExternalHTTPServer(handler=bad_response_get) as bad_response_mock_server:
+ yield bad_response_mock_server
diff --git a/tests/cross_agent/fixtures/rum_client_config.json b/tests/cross_agent/fixtures/rum_client_config.json
deleted file mode 100644
index 8f6e7cbbbe..0000000000
--- a/tests/cross_agent/fixtures/rum_client_config.json
+++ /dev/null
@@ -1,91 +0,0 @@
-[
- {
- "testname":"all fields present",
-
- "apptime_milliseconds":5,
- "queuetime_milliseconds":3,
- "browser_monitoring.attributes.enabled":true,
- "transaction_name":"WebTransaction/brink/of/glory",
- "license_key":"0000111122223333444455556666777788889999",
- "connect_reply":
- {
- "beacon":"my_beacon",
- "browser_key":"my_browser_key",
- "application_id":"my_application_id",
- "error_beacon":"my_error_beacon",
- "js_agent_file":"my_js_agent_file"
- },
- "user_attributes":{"alpha":"beta"},
- "expected":
- {
- "beacon":"my_beacon",
- "licenseKey":"my_browser_key",
- "applicationID":"my_application_id",
- "transactionName":"Z1VSZENQX0JTUUZbXF4fUkJYX1oeXVQdVV9fQkk=",
- "queueTime":3,
- "applicationTime":5,
- "atts":"SxJFEgtKE1BeQlpTEQoSUlVFUBNMTw==",
- "errorBeacon":"my_error_beacon",
- "agent":"my_js_agent_file"
- }
- },
- {
- "testname":"browser_monitoring.attributes.enabled disabled",
-
- "apptime_milliseconds":5,
- "queuetime_milliseconds":3,
- "browser_monitoring.attributes.enabled":false,
- "transaction_name":"WebTransaction/brink/of/glory",
- "license_key":"0000111122223333444455556666777788889999",
- "connect_reply":
- {
- "beacon":"my_beacon",
- "browser_key":"my_browser_key",
- "application_id":"my_application_id",
- "error_beacon":"my_error_beacon",
- "js_agent_file":"my_js_agent_file"
- },
- "user_attributes":{"alpha":"beta"},
- "expected":
- {
- "beacon":"my_beacon",
- "licenseKey":"my_browser_key",
- "applicationID":"my_application_id",
- "transactionName":"Z1VSZENQX0JTUUZbXF4fUkJYX1oeXVQdVV9fQkk=",
- "queueTime":3,
- "applicationTime":5,
- "atts":"",
- "errorBeacon":"my_error_beacon",
- "agent":"my_js_agent_file"
- }
- },
- {
- "testname":"empty js_agent_file",
- "apptime_milliseconds":5,
- "queuetime_milliseconds":3,
- "browser_monitoring.attributes.enabled":true,
- "transaction_name":"WebTransaction/brink/of/glory",
- "license_key":"0000111122223333444455556666777788889999",
- "connect_reply":
- {
- "beacon":"my_beacon",
- "browser_key":"my_browser_key",
- "application_id":"my_application_id",
- "error_beacon":"my_error_beacon",
- "js_agent_file":""
- },
- "user_attributes":{"alpha":"beta"},
- "expected":
- {
- "beacon":"my_beacon",
- "licenseKey":"my_browser_key",
- "applicationID":"my_application_id",
- "transactionName":"Z1VSZENQX0JTUUZbXF4fUkJYX1oeXVQdVV9fQkk=",
- "queueTime":3,
- "applicationTime":5,
- "atts":"SxJFEgtKE1BeQlpTEQoSUlVFUBNMTw==",
- "errorBeacon":"my_error_beacon",
- "agent":""
- }
- }
-]
diff --git a/tests/cross_agent/fixtures/rum_footer_insertion_location/close-body-in-comment.html b/tests/cross_agent/fixtures/rum_footer_insertion_location/close-body-in-comment.html
deleted file mode 100644
index e32df24204..0000000000
--- a/tests/cross_agent/fixtures/rum_footer_insertion_location/close-body-in-comment.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
- Comment contains a close body tag
-
-
- The quick brown fox jumps over the lazy dog.
-
- EXPECTED_RUM_FOOTER_LOCATION
-
diff --git a/tests/cross_agent/fixtures/rum_footer_insertion_location/dynamic-iframe.html b/tests/cross_agent/fixtures/rum_footer_insertion_location/dynamic-iframe.html
deleted file mode 100644
index 5e1acc86b5..0000000000
--- a/tests/cross_agent/fixtures/rum_footer_insertion_location/dynamic-iframe.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
- Dynamic iframe Generation
-
-
- The quick brown fox jumps over the lazy dog.
-
-
- EXPECTED_RUM_FOOTER_LOCATION
-
diff --git a/tests/cross_agent/test_agent_attributes.py b/tests/cross_agent/test_agent_attributes.py
index c254be7728..527b31a753 100644
--- a/tests/cross_agent/test_agent_attributes.py
+++ b/tests/cross_agent/test_agent_attributes.py
@@ -40,7 +40,8 @@ def _default_settings():
'browser_monitoring.attributes.exclude': [],
}
-FIXTURE = os.path.join(os.curdir, 'fixtures', 'attribute_configuration.json')
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+FIXTURE = os.path.join(CURRENT_DIR, 'fixtures', 'attribute_configuration.json')
def _load_tests():
with open(FIXTURE, 'r') as fh:
diff --git a/tests/cross_agent/test_boot_id_utilization_data.py b/tests/cross_agent/test_boot_id_utilization_data.py
index ea5b26a9e7..42f58c12cf 100644
--- a/tests/cross_agent/test_boot_id_utilization_data.py
+++ b/tests/cross_agent/test_boot_id_utilization_data.py
@@ -48,7 +48,7 @@ def _parametrize_test(test):
_boot_id_tests = [_parametrize_test(t) for t in _load_tests()]
-class MockedBootIdEndpoint(object):
+class MockedBootIdEndpoint():
def __init__(self, boot_id):
self.boot_id = boot_id
diff --git a/tests/cross_agent/test_cat_map.py b/tests/cross_agent/test_cat_map.py
index 67c5ab8151..b20485c83b 100644
--- a/tests/cross_agent/test_cat_map.py
+++ b/tests/cross_agent/test_cat_map.py
@@ -18,42 +18,53 @@
can be found in test/framework_tornado_r3/test_cat_map.py
"""
-import webtest
-import pytest
import json
import os
-try:
- from urllib2 import urlopen # Py2.X
-except ImportError:
- from urllib.request import urlopen # Py3.X
+import pytest
+import webtest
+
+from urllib.request import urlopen
-from newrelic.packages import six
+from testing_support.fixtures import (
+ make_cross_agent_headers,
+ override_application_name,
+ override_application_settings,
+ validate_analytics_catmap_data,
+)
+from testing_support.mock_external_http_server import (
+ MockExternalHTTPHResponseHeadersServer,
+)
+from testing_support.validators.validate_tt_parameters import validate_tt_parameters
from newrelic.api.external_trace import ExternalTrace
-from newrelic.api.transaction import (get_browser_timing_header,
- set_transaction_name, get_browser_timing_footer, set_background_task,
- current_transaction)
+from newrelic.api.transaction import (
+ current_transaction,
+ get_browser_timing_header,
+ set_background_task,
+ set_transaction_name,
+)
from newrelic.api.wsgi_application import wsgi_application
-from newrelic.common.encoding_utils import obfuscate, json_encode
-
-from testing_support.fixtures import (override_application_settings,
- override_application_name, validate_tt_parameters,
- make_cross_agent_headers, validate_analytics_catmap_data)
-from testing_support.mock_external_http_server import (
- MockExternalHTTPHResponseHeadersServer)
+from newrelic.common.encoding_utils import json_encode, obfuscate
-ENCODING_KEY = '1234567890123456789012345678901234567890'
+ENCODING_KEY = "1234567890123456789012345678901234567890"
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
-JSON_DIR = os.path.normpath(os.path.join(CURRENT_DIR, 'fixtures'))
+JSON_DIR = os.path.normpath(os.path.join(CURRENT_DIR, "fixtures"))
OUTBOUD_REQUESTS = {}
-_parameters_list = ["name", "appName", "transactionName", "transactionGuid",
- "inboundPayload", "outboundRequests", "expectedIntrinsicFields",
- "nonExpectedIntrinsicFields"]
+_parameters_list = [
+ "name",
+ "appName",
+ "transactionName",
+ "transactionGuid",
+ "inboundPayload",
+ "outboundRequests",
+ "expectedIntrinsicFields",
+ "nonExpectedIntrinsicFields",
+]
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
def server():
with MockExternalHTTPHResponseHeadersServer() as _server:
yield _server
@@ -61,8 +72,8 @@ def server():
def load_tests():
result = []
- path = os.path.join(JSON_DIR, 'cat_map.json')
- with open(path, 'r') as fh:
+ path = os.path.join(JSON_DIR, "cat_map.json")
+ with open(path, "r") as fh:
tests = json.load(fh)
for test in tests:
@@ -77,57 +88,49 @@ def load_tests():
@wsgi_application()
def target_wsgi_application(environ, start_response):
- status = '200 OK'
+ status = "200 OK"
- txn_name = environ.get('txn')
- if six.PY2:
- txn_name = txn_name.decode('UTF-8')
- txn_name = txn_name.split('/', 3)
+ txn_name = environ.get("txn").split("/", 3)
- guid = environ.get('guid')
- old_cat = environ.get('old_cat') == 'True'
+ guid = environ.get("guid")
+ old_cat = environ.get("old_cat") == "True"
txn = current_transaction()
txn.guid = guid
for req in OUTBOUD_REQUESTS:
# Change the transaction name before making an outbound call.
- outgoing_name = req['outboundTxnName'].split('/', 3)
- if outgoing_name[0] != 'WebTransaction':
+ outgoing_name = req["outboundTxnName"].split("/", 3)
+ if outgoing_name[0] != "WebTransaction":
set_background_task(True)
set_transaction_name(outgoing_name[2], group=outgoing_name[1])
- expected_outbound_header = obfuscate(
- json_encode(req['expectedOutboundPayload']), ENCODING_KEY)
- generated_outbound_header = dict(
- ExternalTrace.generate_request_headers(txn))
+ expected_outbound_header = obfuscate(json_encode(req["expectedOutboundPayload"]), ENCODING_KEY)
+ generated_outbound_header = dict(ExternalTrace.generate_request_headers(txn))
# A 500 error is returned because 'assert' statements in the wsgi app
# are ignored.
if old_cat:
- if (expected_outbound_header !=
- generated_outbound_header['X-NewRelic-Transaction']):
- status = '500 Outbound Headers Check Failed.'
+ if expected_outbound_header != generated_outbound_header["X-NewRelic-Transaction"]:
+ status = "500 Outbound Headers Check Failed."
else:
- if 'X-NewRelic-Transaction' in generated_outbound_header:
- status = '500 Outbound Headers Check Failed.'
- r = urlopen(environ['server_url'])
+ if "X-NewRelic-Transaction" in generated_outbound_header:
+ status = "500 Outbound Headers Check Failed."
+ r = urlopen(environ["server_url"]) # nosec B310
r.read(10)
# Set the final transaction name.
- if txn_name[0] != 'WebTransaction':
+ if txn_name[0] != "WebTransaction":
set_background_task(True)
set_transaction_name(txn_name[2], group=txn_name[1])
- text = '%sRESPONSE
%s'
+ text = "%sRESPONSE
"
- output = (text % (get_browser_timing_header(),
- get_browser_timing_footer())).encode('UTF-8')
+ output = (text % get_browser_timing_header()).encode("UTF-8")
- response_headers = [('Content-type', 'text/html; charset=utf-8'),
- ('Content-Length', str(len(output)))]
+ response_headers = [("Content-type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))]
start_response(status, response_headers)
return [output]
@@ -137,26 +140,35 @@ def target_wsgi_application(environ, start_response):
@pytest.mark.parametrize(_parameters, load_tests())
-@pytest.mark.parametrize('old_cat', (True, False))
-def test_cat_map(name, appName, transactionName, transactionGuid,
- inboundPayload, outboundRequests, expectedIntrinsicFields,
- nonExpectedIntrinsicFields, old_cat, server):
+@pytest.mark.parametrize("old_cat", (True, False))
+def test_cat_map(
+ name,
+ appName,
+ transactionName,
+ transactionGuid,
+ inboundPayload,
+ outboundRequests,
+ expectedIntrinsicFields,
+ nonExpectedIntrinsicFields,
+ old_cat,
+ server,
+):
global OUTBOUD_REQUESTS
OUTBOUD_REQUESTS = outboundRequests or {}
_custom_settings = {
- 'cross_process_id': '1#1',
- 'encoding_key': ENCODING_KEY,
- 'trusted_account_ids': [1],
- 'cross_application_tracer.enabled': True,
- 'distributed_tracing.enabled': not old_cat,
- 'transaction_tracer.transaction_threshold': 0.0,
+ "cross_process_id": "1#1",
+ "encoding_key": ENCODING_KEY,
+ "trusted_account_ids": [1],
+ "cross_application_tracer.enabled": True,
+ "distributed_tracing.enabled": not old_cat,
+ "transaction_tracer.transaction_threshold": 0.0,
}
if expectedIntrinsicFields and old_cat:
_external_node_params = {
- 'path_hash': expectedIntrinsicFields['nr.pathHash'],
- 'trip_id': expectedIntrinsicFields['nr.tripId'],
+ "path_hash": expectedIntrinsicFields["nr.pathHash"],
+ "trip_id": expectedIntrinsicFields["nr.tripId"],
}
else:
_external_node_params = []
@@ -167,38 +179,39 @@ def test_cat_map(name, appName, transactionName, transactionGuid,
expectedIntrinsicFields = {}
@validate_tt_parameters(required_params=_external_node_params)
- @validate_analytics_catmap_data(transactionName,
- expected_attributes=expectedIntrinsicFields,
- non_expected_attributes=nonExpectedIntrinsicFields)
+ @validate_analytics_catmap_data(
+ transactionName, expected_attributes=expectedIntrinsicFields, non_expected_attributes=nonExpectedIntrinsicFields
+ )
@override_application_settings(_custom_settings)
@override_application_name(appName)
def run_cat_test():
-
- if six.PY2:
- txn_name = transactionName.encode('UTF-8')
- guid = transactionGuid.encode('UTF-8')
- else:
- txn_name = transactionName
- guid = transactionGuid
+ txn_name = transactionName
+ guid = transactionGuid
# Only generate old cat style headers. This will test to make sure we
# are properly ignoring these headers when the agent is using better
# cat.
- headers = make_cross_agent_headers(inboundPayload, ENCODING_KEY, '1#1')
- response = target_application.get('/', headers=headers,
- extra_environ={'txn': txn_name, 'guid': guid,
- 'old_cat': str(old_cat),
- 'server_url': 'http://localhost:%d' % server.port})
+ headers = make_cross_agent_headers(inboundPayload, ENCODING_KEY, "1#1")
+ response = target_application.get(
+ "/",
+ headers=headers,
+ extra_environ={
+ "txn": txn_name,
+ "guid": guid,
+ "old_cat": str(old_cat),
+ "server_url": f"http://localhost:{server.port}",
+ },
+ )
# Validation of analytic data happens in the decorator.
- assert response.status == '200 OK'
+ assert response.status == "200 OK"
content = response.html.html.body.p.string
# Validate actual body content as sanity check.
- assert content == 'RESPONSE'
+ assert content == "RESPONSE"
run_cat_test()
diff --git a/tests/cross_agent/test_collector_hostname.py b/tests/cross_agent/test_collector_hostname.py
index 2ce39a1ec8..a43e91262c 100644
--- a/tests/cross_agent/test_collector_hostname.py
+++ b/tests/cross_agent/test_collector_hostname.py
@@ -18,14 +18,9 @@
import sys
import tempfile
-import pytest
+from importlib import reload
-try:
- # python 2.x
- reload
-except NameError:
- # python 3.x
- from importlib import reload
+import pytest
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
@@ -68,9 +63,9 @@ def _test_collector_hostname(
os.environ["NEW_RELIC_LICENSE_KEY"] = env_key
if config_file_key:
- ini_contents += "\nlicense_key = %s" % config_file_key
+ ini_contents += f"\nlicense_key = {config_file_key}"
if config_override_host:
- ini_contents += "\nhost = %s" % config_override_host
+ ini_contents += f"\nhost = {config_override_host}"
import newrelic.config as config
import newrelic.core.config as core_config
diff --git a/tests/cross_agent/test_datstore_instance.py b/tests/cross_agent/test_datastore_instance.py
similarity index 52%
rename from tests/cross_agent/test_datstore_instance.py
rename to tests/cross_agent/test_datastore_instance.py
index aa095400fa..79a95e0be1 100644
--- a/tests/cross_agent/test_datstore_instance.py
+++ b/tests/cross_agent/test_datastore_instance.py
@@ -14,34 +14,40 @@
import json
import os
+
import pytest
from newrelic.api.background_task import background_task
-from newrelic.api.database_trace import (register_database_client,
- enable_datastore_instance_feature)
+from newrelic.api.database_trace import register_database_client
from newrelic.api.transaction import current_transaction
from newrelic.core.database_node import DatabaseNode
from newrelic.core.stats_engine import StatsEngine
-FIXTURE = os.path.join(os.curdir,
- 'fixtures', 'datastores', 'datastore_instances.json')
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+FIXTURE = os.path.join(CURRENT_DIR, "fixtures", "datastores", "datastore_instances.json")
-_parameters_list = ['name', 'system_hostname', 'db_hostname',
- 'product', 'port', 'unix_socket', 'database_path',
- 'expected_instance_metric']
+_parameters_list = [
+ "name",
+ "system_hostname",
+ "db_hostname",
+ "product",
+ "port",
+ "unix_socket",
+ "database_path",
+ "expected_instance_metric",
+]
-_parameters = ','.join(_parameters_list)
+_parameters = ",".join(_parameters_list)
def _load_tests():
- with open(FIXTURE, 'r') as fh:
+ with open(FIXTURE, "r") as fh:
js = fh.read()
return json.loads(js)
def _parametrize_test(test):
- return tuple([test.get(f, None if f != 'db_hostname' else 'localhost')
- for f in _parameters_list])
+ return tuple([test.get(f, None if f != "db_hostname" else "localhost") for f in _parameters_list])
_datastore_tests = [_parametrize_test(t) for t in _load_tests()]
@@ -49,45 +55,44 @@ def _parametrize_test(test):
@pytest.mark.parametrize(_parameters, _datastore_tests)
@background_task()
-def test_datastore_instance(name, system_hostname, db_hostname,
- product, port, unix_socket, database_path,
- expected_instance_metric, monkeypatch):
+def test_datastore_instance(
+ name, system_hostname, db_hostname, product, port, unix_socket, database_path, expected_instance_metric, monkeypatch
+):
- monkeypatch.setattr('newrelic.common.system_info.gethostname',
- lambda: system_hostname)
+ monkeypatch.setattr("newrelic.common.system_info.gethostname", lambda: system_hostname)
- class FakeModule():
+ class FakeModule:
pass
register_database_client(FakeModule, product)
- enable_datastore_instance_feature(FakeModule)
port_path_or_id = port or database_path or unix_socket
- node = DatabaseNode(dbapi2_module=FakeModule,
- sql='',
- children=[],
- start_time=0,
- end_time=1,
- duration=1,
- exclusive=1,
- stack_trace=None,
- sql_format='obfuscated',
- connect_params=None,
- cursor_params=None,
- sql_parameters=None,
- execute_params=None,
- host=db_hostname,
- port_path_or_id=port_path_or_id,
- database_name=database_path,
- guid=None,
- agent_attributes={},
- user_attributes={},
+ node = DatabaseNode(
+ dbapi2_module=FakeModule,
+ sql="",
+ children=[],
+ start_time=0,
+ end_time=1,
+ duration=1,
+ exclusive=1,
+ stack_trace=None,
+ sql_format="obfuscated",
+ connect_params=None,
+ cursor_params=None,
+ sql_parameters=None,
+ execute_params=None,
+ host=db_hostname,
+ port_path_or_id=port_path_or_id,
+ database_name=database_path,
+ guid=None,
+ agent_attributes={},
+ user_attributes={},
)
empty_stats = StatsEngine()
transaction = current_transaction()
- unscoped_scope = ''
+ unscoped_scope = ""
# Check 'Datastore/instance' metric to confirm that:
# 1. metric name is reported correctly
diff --git a/tests/cross_agent/test_distributed_tracing.py b/tests/cross_agent/test_distributed_tracing.py
index 0ff46eea24..715d22fbef 100644
--- a/tests/cross_agent/test_distributed_tracing.py
+++ b/tests/cross_agent/test_distributed_tracing.py
@@ -14,54 +14,70 @@
import json
import os
+
import pytest
import webtest
+from testing_support.fixtures import override_application_settings, validate_attributes
+from testing_support.validators.validate_error_event_attributes import (
+ validate_error_event_attributes,
+)
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_event_attributes import (
+ validate_transaction_event_attributes,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.transaction import current_transaction
from newrelic.api.wsgi_application import wsgi_application
from newrelic.common.encoding_utils import DistributedTracePayload
from newrelic.common.object_wrapper import transient_function_wrapper
-from testing_support.fixtures import (override_application_settings,
- validate_error_event_attributes, validate_attributes)
-from testing_support.validators.validate_span_events import (
- validate_span_events)
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_transaction_event_attributes import validate_transaction_event_attributes
-
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
-JSON_DIR = os.path.normpath(os.path.join(CURRENT_DIR, 'fixtures',
- 'distributed_tracing'))
-
-_parameters_list = ['account_id', 'comment', 'expected_metrics',
- 'force_sampled_true', 'inbound_payloads', 'intrinsics',
- 'major_version', 'minor_version', 'outbound_payloads',
- 'raises_exception', 'span_events_enabled', 'test_name',
- 'transport_type', 'trusted_account_key', 'web_transaction']
-_parameters = ','.join(_parameters_list)
+JSON_DIR = os.path.normpath(os.path.join(CURRENT_DIR, "fixtures", "distributed_tracing"))
+
+_parameters_list = [
+ "account_id",
+ "comment",
+ "expected_metrics",
+ "force_sampled_true",
+ "inbound_payloads",
+ "intrinsics",
+ "major_version",
+ "minor_version",
+ "outbound_payloads",
+ "raises_exception",
+ "span_events_enabled",
+ "test_name",
+ "transport_type",
+ "trusted_account_key",
+ "web_transaction",
+]
+_parameters = ",".join(_parameters_list)
def load_tests():
result = []
- path = os.path.join(JSON_DIR, 'distributed_tracing.json')
- with open(path, 'r') as fh:
+ path = os.path.join(JSON_DIR, "distributed_tracing.json")
+ with open(path, "r") as fh:
tests = json.load(fh)
for test in tests:
values = (test.get(param, None) for param in _parameters_list)
- param = pytest.param(*values, id=test.get('test_name'))
+ param = pytest.param(*values, id=test.get("test_name"))
result.append(param)
return result
def override_compute_sampled(override):
- @transient_function_wrapper('newrelic.core.adaptive_sampler',
- 'AdaptiveSampler.compute_sampled')
+ @transient_function_wrapper("newrelic.core.adaptive_sampler", "AdaptiveSampler.compute_sampled")
def _override_compute_sampled(wrapped, instance, args, kwargs):
if override:
return True
return wrapped(*args, **kwargs)
+
return _override_compute_sampled
@@ -70,58 +86,54 @@ def assert_payload(payload, payload_assertions, major_version, minor_version):
# flatten payload so it matches the test:
# payload['d']['ac'] -> payload['d.ac']
- d = payload.pop('d')
+ d = payload.pop("d")
for key, value in d.items():
- payload['d.%s' % key] = value
+ payload[f"d.{key}"] = value
- for expected in payload_assertions.get('expected', []):
+ for expected in payload_assertions.get("expected", []):
assert expected in payload
- for unexpected in payload_assertions.get('unexpected', []):
+ for unexpected in payload_assertions.get("unexpected", []):
assert unexpected not in payload
- for key, value in payload_assertions.get('exact', {}).items():
+ for key, value in payload_assertions.get("exact", {}).items():
assert key in payload
if isinstance(value, list):
value = tuple(value)
assert payload[key] == value
- assert payload['v'][0] == major_version
- assert payload['v'][1] == minor_version
+ assert payload["v"][0] == major_version
+ assert payload["v"][1] == minor_version
@wsgi_application()
def target_wsgi_application(environ, start_response):
- status = '200 OK'
- output = b'hello world'
- response_headers = [('Content-type', 'text/html; charset=utf-8'),
- ('Content-Length', str(len(output)))]
+ status = "200 OK"
+ output = b"hello world"
+ response_headers = [("Content-type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))]
txn = current_transaction()
- txn.set_transaction_name(test_settings['test_name'])
+ txn.set_transaction_name(test_settings["test_name"])
- if not test_settings['web_transaction']:
+ if not test_settings["web_transaction"]:
txn.background_task = True
- if test_settings['raises_exception']:
+ if test_settings["raises_exception"]:
try:
1 / 0
except ZeroDivisionError:
txn.notice_error()
- extra_inbound_payloads = test_settings['extra_inbound_payloads']
+ extra_inbound_payloads = test_settings["extra_inbound_payloads"]
for payload, expected_result in extra_inbound_payloads:
- result = txn.accept_distributed_trace_payload(payload,
- test_settings['transport_type'])
+ result = txn.accept_distributed_trace_payload(payload, test_settings["transport_type"])
assert result is expected_result
- outbound_payloads = test_settings['outbound_payloads']
+ outbound_payloads = test_settings["outbound_payloads"]
if outbound_payloads:
for payload_assertions in outbound_payloads:
payload = txn._create_distributed_trace_payload()
- assert_payload(payload, payload_assertions,
- test_settings['major_version'],
- test_settings['minor_version'])
+ assert_payload(payload, payload_assertions, test_settings["major_version"], test_settings["minor_version"])
start_response(status, response_headers)
return [output]
@@ -131,14 +143,26 @@ def target_wsgi_application(environ, start_response):
@pytest.mark.parametrize(_parameters, load_tests())
-def test_distributed_tracing(account_id, comment, expected_metrics,
- force_sampled_true, inbound_payloads, intrinsics, major_version,
- minor_version, outbound_payloads, raises_exception,
- span_events_enabled, test_name, transport_type, trusted_account_key,
- web_transaction):
+def test_distributed_tracing(
+ account_id,
+ comment,
+ expected_metrics,
+ force_sampled_true,
+ inbound_payloads,
+ intrinsics,
+ major_version,
+ minor_version,
+ outbound_payloads,
+ raises_exception,
+ span_events_enabled,
+ test_name,
+ transport_type,
+ trusted_account_key,
+ web_transaction,
+):
extra_inbound_payloads = []
- if transport_type != 'HTTP':
+ if transport_type != "HTTP":
# Since wsgi_application calls accept_distributed_trace_payload
# automatically with transport_type='HTTP', we must defer this call
# until we can specify the transport type.
@@ -153,78 +177,68 @@ def test_distributed_tracing(account_id, comment, expected_metrics,
global test_settings
test_settings = {
- 'test_name': test_name,
- 'web_transaction': web_transaction,
- 'raises_exception': raises_exception,
- 'extra_inbound_payloads': extra_inbound_payloads,
- 'outbound_payloads': outbound_payloads,
- 'transport_type': transport_type,
- 'major_version': major_version,
- 'minor_version': minor_version,
+ "test_name": test_name,
+ "web_transaction": web_transaction,
+ "raises_exception": raises_exception,
+ "extra_inbound_payloads": extra_inbound_payloads,
+ "outbound_payloads": outbound_payloads,
+ "transport_type": transport_type,
+ "major_version": major_version,
+ "minor_version": minor_version,
}
override_settings = {
- 'distributed_tracing.enabled': True,
- 'span_events.enabled': span_events_enabled,
- 'account_id': account_id,
- 'trusted_account_key': trusted_account_key,
+ "distributed_tracing.enabled": True,
+ "span_events.enabled": span_events_enabled,
+ "account_id": account_id,
+ "trusted_account_key": trusted_account_key,
}
- common_required = intrinsics['common']['expected']
- common_forgone = intrinsics['common']['unexpected']
- common_exact = intrinsics['common'].get('exact', {})
-
- txn_intrinsics = intrinsics.get('Transaction', {})
- txn_event_required = {'agent': [], 'user': [],
- 'intrinsic': txn_intrinsics.get('expected', [])}
- txn_event_required['intrinsic'].extend(common_required)
- txn_event_forgone = {'agent': [], 'user': [],
- 'intrinsic': txn_intrinsics.get('unexpected', [])}
- txn_event_forgone['intrinsic'].extend(common_forgone)
- txn_event_exact = {'agent': {}, 'user': {},
- 'intrinsic': txn_intrinsics.get('exact', {})}
- txn_event_exact['intrinsic'].update(common_exact)
+ common_required = intrinsics["common"]["expected"]
+ common_forgone = intrinsics["common"]["unexpected"]
+ common_exact = intrinsics["common"].get("exact", {})
+
+ txn_intrinsics = intrinsics.get("Transaction", {})
+ txn_event_required = {"agent": [], "user": [], "intrinsic": txn_intrinsics.get("expected", [])}
+ txn_event_required["intrinsic"].extend(common_required)
+ txn_event_forgone = {"agent": [], "user": [], "intrinsic": txn_intrinsics.get("unexpected", [])}
+ txn_event_forgone["intrinsic"].extend(common_forgone)
+ txn_event_exact = {"agent": {}, "user": {}, "intrinsic": txn_intrinsics.get("exact", {})}
+ txn_event_exact["intrinsic"].update(common_exact)
headers = {}
if inbound_payloads:
payload = DistributedTracePayload(inbound_payloads[0])
- headers['newrelic'] = payload.http_safe()
-
- @validate_transaction_metrics(test_name,
- rollup_metrics=expected_metrics,
- background_task=not web_transaction)
- @validate_transaction_event_attributes(
- txn_event_required, txn_event_forgone, txn_event_exact)
- @validate_attributes('intrinsic', common_required, common_forgone)
+ headers["newrelic"] = payload.http_safe()
+
+ @validate_transaction_metrics(test_name, rollup_metrics=expected_metrics, background_task=not web_transaction)
+ @validate_transaction_event_attributes(txn_event_required, txn_event_forgone, txn_event_exact)
+ @validate_attributes("intrinsic", common_required, common_forgone)
@override_compute_sampled(force_sampled_true)
@override_application_settings(override_settings)
def _test():
- response = test_application.get('/', headers=headers)
- assert 'X-NewRelic-App-Data' not in response.headers
+ response = test_application.get("/", headers=headers)
+ assert "X-NewRelic-App-Data" not in response.headers
- if 'Span' in intrinsics:
- span_intrinsics = intrinsics.get('Span')
- span_expected = span_intrinsics.get('expected', [])
+ if "Span" in intrinsics:
+ span_intrinsics = intrinsics.get("Span")
+ span_expected = span_intrinsics.get("expected", [])
span_expected.extend(common_required)
- span_unexpected = span_intrinsics.get('unexpected', [])
+ span_unexpected = span_intrinsics.get("unexpected", [])
span_unexpected.extend(common_forgone)
- span_exact = span_intrinsics.get('exact', {})
+ span_exact = span_intrinsics.get("exact", {})
span_exact.update(common_exact)
- _test = validate_span_events(exact_intrinsics=span_exact,
- expected_intrinsics=span_expected,
- unexpected_intrinsics=span_unexpected)(_test)
+ _test = validate_span_events(
+ exact_intrinsics=span_exact, expected_intrinsics=span_expected, unexpected_intrinsics=span_unexpected
+ )(_test)
elif not span_events_enabled:
_test = validate_span_events(count=0)(_test)
if raises_exception:
- error_event_required = {'agent': [], 'user': [],
- 'intrinsic': common_required}
- error_event_forgone = {'agent': [], 'user': [],
- 'intrinsic': common_forgone}
- error_event_exact = {'agent': {}, 'user': {},
- 'intrinsic': common_exact}
- _test = validate_error_event_attributes(error_event_required,
- error_event_forgone, error_event_exact)(_test)
+ error_event_required = {"agent": [], "user": [], "intrinsic": common_required}
+ error_event_forgone = {"agent": [], "user": [], "intrinsic": common_forgone}
+ error_event_exact = {"agent": {}, "user": {}, "intrinsic": common_exact}
+ _test = validate_error_event_attributes(error_event_required, error_event_forgone, error_event_exact)(_test)
_test()
diff --git a/tests/cross_agent/test_docker.py b/tests/cross_agent/test_docker_container_id.py
similarity index 65%
rename from tests/cross_agent/test_docker.py
rename to tests/cross_agent/test_docker_container_id.py
index 9bc1a73630..f14e80fcd8 100644
--- a/tests/cross_agent/test_docker.py
+++ b/tests/cross_agent/test_docker_container_id.py
@@ -19,7 +19,8 @@
import newrelic.common.utilization as u
-DOCKER_FIXTURE = os.path.join(os.curdir, 'fixtures', 'docker_container_id')
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+DOCKER_FIXTURE = os.path.join(CURRENT_DIR, 'fixtures', 'docker_container_id')
def _load_docker_test_attributes():
@@ -38,13 +39,23 @@ def _load_docker_test_attributes():
return docker_test_attributes
+def mock_open(mock_file):
+ def _mock_open(filename, mode):
+ if filename == "/proc/self/mountinfo":
+ raise FileNotFoundError()
+ elif filename == "/proc/self/cgroup":
+ return mock_file
+ raise RuntimeError()
+ return _mock_open
+
+
@pytest.mark.parametrize('filename, containerId',
_load_docker_test_attributes())
-def test_docker_container_id(filename, containerId):
+def test_docker_container_id_v1(monkeypatch, filename, containerId):
path = os.path.join(DOCKER_FIXTURE, filename)
with open(path, 'rb') as f:
- with mock.patch.object(u, 'open', create=True, return_value=f):
- if containerId is not None:
- assert u.DockerUtilization.detect() == {'id': containerId}
- else:
- assert u.DockerUtilization.detect() is None
+ monkeypatch.setattr(u, "open", mock_open(f), raising=False)
+ if containerId is not None:
+ assert u.DockerUtilization.detect() == {'id': containerId}
+ else:
+ assert u.DockerUtilization.detect() is None
diff --git a/tests/cross_agent/test_docker_container_id_v2.py b/tests/cross_agent/test_docker_container_id_v2.py
new file mode 100644
index 0000000000..eee4e1305a
--- /dev/null
+++ b/tests/cross_agent/test_docker_container_id_v2.py
@@ -0,0 +1,61 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import mock
+import os
+import pytest
+
+import newrelic.common.utilization as u
+
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+DOCKER_FIXTURE = os.path.join(CURRENT_DIR, 'fixtures', 'docker_container_id_v2')
+
+
+def _load_docker_test_attributes():
+ """Returns a list of docker test attributes in the form:
+ [(, ), ...]
+
+ """
+ docker_test_attributes = []
+ test_cases = os.path.join(DOCKER_FIXTURE, 'cases.json')
+ with open(test_cases, 'r') as fh:
+ js = fh.read()
+ json_list = json.loads(js)
+ for json_record in json_list:
+ docker_test_attributes.append(
+ (json_record['filename'], json_record['containerId']))
+ return docker_test_attributes
+
+
+def mock_open(mock_file):
+ def _mock_open(filename, mode):
+ if filename == "/proc/self/cgroup":
+ raise FileNotFoundError()
+ elif filename == "/proc/self/mountinfo":
+ return mock_file
+ raise RuntimeError()
+ return _mock_open
+
+
+@pytest.mark.parametrize('filename, containerId',
+ _load_docker_test_attributes())
+def test_docker_container_id_v2(monkeypatch, filename, containerId):
+ path = os.path.join(DOCKER_FIXTURE, filename)
+ with open(path, 'rb') as f:
+ monkeypatch.setattr(u, "open", mock_open(f), raising=False)
+ if containerId is not None:
+ assert u.DockerUtilization.detect() == {'id': containerId}
+ else:
+ assert u.DockerUtilization.detect() is None
diff --git a/tests/cross_agent/test_ecs_data.py b/tests/cross_agent/test_ecs_data.py
new file mode 100644
index 0000000000..9ec32d3b5f
--- /dev/null
+++ b/tests/cross_agent/test_ecs_data.py
@@ -0,0 +1,54 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import pytest
+import newrelic.common.utilization as u
+from fixtures.ecs_container_id.ecs_mock_server import mock_server, bad_response_mock_server
+from test_pcf_utilization_data import Environ
+
+
+@pytest.mark.parametrize("env_key", ["ECS_CONTAINER_METADATA_URI_V4", "ECS_CONTAINER_METADATA_URI"])
+def test_ecs_docker_container_id(env_key, mock_server):
+ mock_endpoint = "http://localhost:%d" % mock_server.port
+ env_dict = {env_key: mock_endpoint}
+
+ with Environ(env_dict):
+ data = u.ECSUtilization.detect()
+
+ assert data == {"ecsDockerId": "1e1698469422439ea356071e581e8545-2769485393"}
+
+
+@pytest.mark.parametrize(
+ "env_dict", [{"ECS_CONTAINER_METADATA_URI_V4": "http:/invalid-uri"}, {"ECS_CONTAINER_METADATA_URI_V4": None}]
+)
+def test_ecs_docker_container_id_bad_uri(env_dict, mock_server):
+ with Environ(env_dict):
+ data = u.ECSUtilization.detect()
+
+ assert data is None
+
+
+def test_ecs_docker_container_id_bad_response(bad_response_mock_server):
+ mock_endpoint = "http://localhost:%d" % bad_response_mock_server.port
+ env_dict = {"ECS_CONTAINER_METADATA_URI": mock_endpoint}
+
+ with Environ(env_dict):
+ data = u.ECSUtilization.detect()
+
+ assert data is None
+
+
+def test_ecs_container_id_no_metadata_env_vars():
+ assert u.ECSUtilization.detect() is None
diff --git a/tests/cross_agent/test_labels_and_rollups.py b/tests/cross_agent/test_labels_and_rollups.py
index d333ec35ba..15ebb1e369 100644
--- a/tests/cross_agent/test_labels_and_rollups.py
+++ b/tests/cross_agent/test_labels_and_rollups.py
@@ -21,7 +21,8 @@
from testing_support.fixtures import override_application_settings
-FIXTURE = os.path.join(os.curdir, 'fixtures', 'labels.json')
+CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
+FIXTURE = os.path.join(CURRENT_DIR, 'fixtures', 'labels.json')
def _load_tests():
with open(FIXTURE, 'r') as fh:
diff --git a/tests/cross_agent/test_lambda_event_source.py b/tests/cross_agent/test_lambda_event_source.py
index 511294cf6f..bea041f1a3 100644
--- a/tests/cross_agent/test_lambda_event_source.py
+++ b/tests/cross_agent/test_lambda_event_source.py
@@ -14,65 +14,70 @@
import json
import os
+
import pytest
+from testing_support.fixtures import override_application_settings
+from testing_support.validators.validate_transaction_event_attributes import (
+ validate_transaction_event_attributes,
+)
from newrelic.api.lambda_handler import lambda_handler
-from testing_support.fixtures import override_application_settings
-from testing_support.validators.validate_transaction_event_attributes import validate_transaction_event_attributes
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
-FIXTURE_DIR = os.path.normpath(os.path.join(CURRENT_DIR, 'fixtures'))
-FIXTURE = os.path.join(FIXTURE_DIR, 'lambda_event_source.json')
+FIXTURE_DIR = os.path.normpath(os.path.join(CURRENT_DIR, "fixtures"))
+FIXTURE = os.path.join(FIXTURE_DIR, "lambda_event_source.json")
tests = {}
events = {}
def _load_tests():
- with open(FIXTURE, 'r') as fh:
+ with open(FIXTURE, "r") as fh:
for test in json.loads(fh.read()):
- test_name = test.pop('name')
+ test_name = test.pop("name")
- test_file = test_name + '.json'
- path = os.path.join(FIXTURE_DIR, 'lambda_event_source', test_file)
- with open(path, 'r') as fh:
+ test_file = f"{test_name}.json"
+ path = os.path.join(FIXTURE_DIR, "lambda_event_source", test_file)
+ with open(path, "r") as fh:
events[test_name] = json.loads(fh.read())
tests[test_name] = test
return tests.keys()
-class Context(object):
- aws_request_id = 'cookies'
- invoked_function_arn = 'arn'
- function_name = 'cats'
- function_version = '$LATEST'
+class Context():
+ aws_request_id = "cookies"
+ invoked_function_arn = "arn"
+ function_name = "cats"
+ function_version = "$LATEST"
memory_limit_in_mb = 128
@lambda_handler()
def handler(event, context):
return {
- 'statusCode': '200',
- 'body': '{}',
- 'headers': {
- 'Content-Type': 'application/json',
- 'Content-Length': 2,
+ "statusCode": "200",
+ "body": "{}",
+ "headers": {
+ "Content-Type": "application/json",
+ "Content-Length": 2,
},
}
-@pytest.mark.parametrize('test_name', _load_tests())
+# The lambda_hander has been deprecated for 3+ years
+@pytest.mark.skip(reason="The lambda_handler has been deprecated")
+@pytest.mark.parametrize("test_name", _load_tests())
def test_lambda_event_source(test_name):
- _exact = {'user': {}, 'intrinsic': {}, 'agent': {}}
+ _exact = {"user": {}, "intrinsic": {}, "agent": {}}
- expected_arn = tests[test_name].get('aws.lambda.eventSource.arn', None)
+ expected_arn = tests[test_name].get("aws.lambda.eventSource.arn", None)
if expected_arn:
- _exact['agent']['aws.lambda.eventSource.arn'] = expected_arn
+ _exact["agent"]["aws.lambda.eventSource.arn"] = expected_arn
else:
pytest.skip("Nothing to test!")
return
- @override_application_settings({'attributes.include': ['aws.*']})
+ @override_application_settings({"attributes.include": ["aws.*"]})
@validate_transaction_event_attributes({}, exact_attrs=_exact)
def _test():
handler(events[test_name], Context)
diff --git a/tests/cross_agent/test_pcf_utilization_data.py b/tests/cross_agent/test_pcf_utilization_data.py
index 28b56f7592..108e77d890 100644
--- a/tests/cross_agent/test_pcf_utilization_data.py
+++ b/tests/cross_agent/test_pcf_utilization_data.py
@@ -45,7 +45,7 @@ def _parametrize_test(test):
_pcf_tests = [_parametrize_test(t) for t in _load_tests()]
-class Environ(object):
+class Environ():
def __init__(self, env_dict):
env_dict = env_dict or {}
cleaned_env_dict = {}
@@ -66,7 +66,7 @@ def __exit__(self, *args, **kwargs):
os.environ = INITIAL_ENV
-class MockResponse(object):
+class MockResponse():
def __init__(self, code, body):
self.code = code
diff --git a/tests/cross_agent/test_rules.py b/tests/cross_agent/test_rules.py
index e37db787cd..ce2983c90e 100644
--- a/tests/cross_agent/test_rules.py
+++ b/tests/cross_agent/test_rules.py
@@ -16,23 +16,23 @@
import os
import pytest
-from newrelic.core.rules_engine import RulesEngine, NormalizationRule
+from newrelic.api.application import application_instance
+from newrelic.api.background_task import background_task
+from newrelic.api.transaction import record_custom_metric
+from newrelic.core.rules_engine import RulesEngine
+
+from testing_support.validators.validate_metric_payload import validate_metric_payload
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
FIXTURE = os.path.normpath(os.path.join(
CURRENT_DIR, 'fixtures', 'rules.json'))
+
def _load_tests():
with open(FIXTURE, 'r') as fh:
js = fh.read()
return json.loads(js)
-def _prepare_rules(test_rules):
- # ensure all keys are present, if not present set to an empty string
- for rule in test_rules:
- for key in NormalizationRule._fields:
- rule[key] = rule.get(key, '')
- return test_rules
def _make_case_insensitive(rules):
# lowercase each rule
@@ -42,14 +42,14 @@ def _make_case_insensitive(rules):
rule['replacement'] = rule['replacement'].lower()
return rules
+
@pytest.mark.parametrize('test_group', _load_tests())
def test_rules_engine(test_group):
# FIXME: The test fixture assumes that matching is case insensitive when it
# is not. To avoid errors, just lowercase all rules, inputs, and expected
# values.
- insense_rules = _make_case_insensitive(test_group['rules'])
- test_rules = _prepare_rules(insense_rules)
+ test_rules = _make_case_insensitive(test_group['rules'])
rules_engine = RulesEngine(test_rules)
for test in test_group['tests']:
@@ -66,3 +66,46 @@ def test_rules_engine(test_group):
assert expected == ''
else:
assert result == expected
+
+
+@pytest.mark.parametrize('test_group', _load_tests())
+def test_rules_engine_metric_harvest(test_group):
+ # FIXME: The test fixture assumes that matching is case insensitive when it
+ # is not. To avoid errors, just lowercase all rules, inputs, and expected
+ # values.
+ test_rules = _make_case_insensitive(test_group['rules'])
+ rules_engine = RulesEngine(test_rules)
+
+ # Set rules engine on core application
+ api_application = application_instance(activate=False)
+ api_name = api_application.name
+ core_application = api_application._agent.application(api_name)
+ old_rules = core_application._rules_engine["metric"] # save previoius rules
+ core_application._rules_engine["metric"] = rules_engine
+
+ def send_metrics():
+ # Send all metrics in this test batch in one transaction, then harvest so the normalizer is run.
+ @background_task(name="send_metrics")
+ def _test():
+ for test in test_group['tests']:
+ # lowercase each value
+ input_str = test['input'].lower()
+ record_custom_metric(input_str, {"count": 1})
+ _test()
+ core_application.harvest()
+
+ try:
+ # Create a map of all result metrics to validate after harvest
+ test_metrics = []
+ for test in test_group['tests']:
+ expected = (test['expected'] or '').lower()
+ if expected == '': # Ignored
+ test_metrics.append((expected, None))
+ else:
+ test_metrics.append((expected, 1))
+
+ # Harvest and validate resulting payload
+ validate_metric_payload(metrics=test_metrics)(send_metrics)()
+ finally:
+ # Replace original rules engine
+ core_application._rules_engine["metric"] = old_rules
diff --git a/tests/cross_agent/test_rum_client_config.py b/tests/cross_agent/test_rum_client_config.py
deleted file mode 100644
index c2a4a465f9..0000000000
--- a/tests/cross_agent/test_rum_client_config.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# Copyright 2010 New Relic, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-import os
-
-import pytest
-import webtest
-from testing_support.fixtures import override_application_settings
-
-from newrelic.api.transaction import (
- add_custom_attribute,
- get_browser_timing_footer,
- set_transaction_name,
-)
-from newrelic.api.wsgi_application import wsgi_application
-
-
-def _load_tests():
- fixture = os.path.join(os.curdir, "fixtures", "rum_client_config.json")
- with open(fixture, "r") as fh:
- js = fh.read()
- return json.loads(js)
-
-
-fields = [
- "testname",
- "apptime_milliseconds",
- "queuetime_milliseconds",
- "browser_monitoring.attributes.enabled",
- "transaction_name",
- "license_key",
- "connect_reply",
- "user_attributes",
- "expected",
-]
-
-# Replace . as not a valid character in python argument names
-
-field_names = ",".join([f.replace(".", "_") for f in fields])
-
-
-def _parametrize_test(test):
- return tuple([test.get(f, None) for f in fields])
-
-
-_rum_tests = [_parametrize_test(t) for t in _load_tests()]
-
-
-@wsgi_application()
-def target_wsgi_application(environ, start_response):
- status = "200 OK"
-
- txn_name = environ.get("txn_name")
- set_transaction_name(txn_name, group="")
-
- user_attrs = json.loads(environ.get("user_attrs"))
- for key, value in user_attrs.items():
- add_custom_attribute(key, value)
-
- text = "%sRESPONSE
"
-
- output = (text % get_browser_timing_footer()).encode("UTF-8")
-
- response_headers = [("Content-Type", "text/html; charset=utf-8"), ("Content-Length", str(len(output)))]
- start_response(status, response_headers)
-
- return [output]
-
-
-target_application = webtest.TestApp(target_wsgi_application)
-
-
-@pytest.mark.parametrize(field_names, _rum_tests)
-def test_browser_montioring(
- testname,
- apptime_milliseconds,
- queuetime_milliseconds,
- browser_monitoring_attributes_enabled,
- transaction_name,
- license_key,
- connect_reply,
- user_attributes,
- expected,
-):
-
- settings = {
- "browser_monitoring.attributes.enabled": browser_monitoring_attributes_enabled,
- "license_key": license_key,
- "js_agent_loader": "",
- }
- settings.update(connect_reply)
-
- @override_application_settings(settings)
- def run_browser_data_test():
-
- response = target_application.get(
- "/", extra_environ={"txn_name": str(transaction_name), "user_attrs": json.dumps(user_attributes)}
- )
-
- # We actually put the "footer" in the header, the first script is the
- # agent "header", the second one is where the data lives, hence the [1].
-
- footer = response.html.html.head.find_all("script")[1]
- footer_data = json.loads(footer.string.split("NREUM.info=")[1])
-
- # Not feasible to test the time metric values in testing
-
- expected.pop("queueTime")
- expected.pop("applicationTime")
- assert footer_data["applicationTime"] >= 0
- assert footer_data["queueTime"] >= 0
-
- # Python always prepends stuff to the transaction name, so this
- # doesn't match the obscured value.
-
- expected.pop("transactionName")
-
- # Check that all other values are correct
-
- for key, value in expected.items():
-
- # If there are no attributes, the spec allows us to omit the
- # 'atts' field altogether, so we do. But, the cross agent tests
- # don't omit it, so we need to special case 'atts' when we compare
- # to 'expected'.
-
- if key == "atts" and value == "":
- assert key not in footer_data
- else:
- assert footer_data[key] == value
-
- run_browser_data_test()
diff --git a/tests/cross_agent/test_sql_obfuscation.py b/tests/cross_agent/test_sql_obfuscation.py
index 480b0a4176..362fc797df 100644
--- a/tests/cross_agent/test_sql_obfuscation.py
+++ b/tests/cross_agent/test_sql_obfuscation.py
@@ -53,7 +53,7 @@ def get_quoting_styles(dialects):
return set([_quoting_styles.get(dialect) for dialect in dialects])
-class DummyDB(object):
+class DummyDB():
def __init__(self, quoting_style):
self.quoting_style = quoting_style
diff --git a/tests/cross_agent/test_utilization_configs.py b/tests/cross_agent/test_utilization_configs.py
index 4a4adb4859..0b372f5769 100644
--- a/tests/cross_agent/test_utilization_configs.py
+++ b/tests/cross_agent/test_utilization_configs.py
@@ -17,6 +17,8 @@
import sys
import tempfile
+from importlib import reload
+
import pytest
# NOTE: the test_utilization_settings_from_env_vars test mocks several of the
@@ -29,12 +31,6 @@
from newrelic.common.utilization import CommonUtilization
from newrelic.core.agent_protocol import AgentProtocol
-try:
- # python 2.x
- reload
-except NameError:
- # python 3.x
- from importlib import reload
INITIAL_ENV = os.environ
@@ -76,7 +72,7 @@ def getips(*args, **kwargs):
return getips
-class UpdatedSettings(object):
+class UpdatedSettings():
def __init__(self):
self.initial_settings = newrelic.core.config._settings
@@ -179,7 +175,6 @@ def _patch_system_info(wrapped, instance, args, kwargs):
@pytest.mark.parametrize("test", _load_tests())
def test_utilization_settings(test, monkeypatch):
-
env = test.get("input_environment_variables", {})
if test.get("input_pcf_guid"):
diff --git a/tests/cross_agent/test_w3c_trace_context.py b/tests/cross_agent/test_w3c_trace_context.py
index 05f157f7b7..b10ec60818 100644
--- a/tests/cross_agent/test_w3c_trace_context.py
+++ b/tests/cross_agent/test_w3c_trace_context.py
@@ -14,88 +14,104 @@
import json
import os
+
import pytest
import webtest
-from newrelic.packages import six
-
-from newrelic.api.transaction import current_transaction
+from testing_support.fixtures import override_application_settings, validate_attributes
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_event_attributes import (
+ validate_transaction_event_attributes,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.transaction import (
+ accept_distributed_trace_headers,
+ current_transaction,
+ insert_distributed_trace_headers,
+)
from newrelic.api.wsgi_application import wsgi_application
-from newrelic.common.object_wrapper import transient_function_wrapper
-from testing_support.validators.validate_span_events import (
- validate_span_events)
-from testing_support.fixtures import (override_application_settings,
- validate_attributes)
from newrelic.common.encoding_utils import W3CTraceState
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_transaction_event_attributes import validate_transaction_event_attributes
+from newrelic.common.object_wrapper import transient_function_wrapper
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
-JSON_DIR = os.path.normpath(os.path.join(CURRENT_DIR, 'fixtures',
- 'distributed_tracing'))
-
-_parameters_list = ('test_name', 'trusted_account_key', 'account_id',
- 'web_transaction', 'raises_exception', 'force_sampled_true',
- 'span_events_enabled', 'transport_type', 'inbound_headers',
- 'outbound_payloads', 'intrinsics', 'expected_metrics')
-
-_parameters = ','.join(_parameters_list)
+JSON_DIR = os.path.normpath(os.path.join(CURRENT_DIR, "fixtures", "distributed_tracing"))
+
+_parameters_list = (
+ "test_name",
+ "trusted_account_key",
+ "account_id",
+ "web_transaction",
+ "raises_exception",
+ "force_sampled_true",
+ "span_events_enabled",
+ "transport_type",
+ "inbound_headers",
+ "outbound_payloads",
+ "intrinsics",
+ "expected_metrics",
+)
+
+_parameters = ",".join(_parameters_list)
XFAIL_TESTS = [
- 'spans_disabled_root',
- 'missing_traceparent',
- 'missing_traceparent_and_tracestate',
- 'w3c_and_newrelc_headers_present_error_parsing_traceparent'
+ "spans_disabled_root",
+ "missing_traceparent",
+ "missing_traceparent_and_tracestate",
+ "w3c_and_newrelc_headers_present_error_parsing_traceparent",
]
+
def load_tests():
result = []
- path = os.path.join(JSON_DIR, 'trace_context.json')
- with open(path, 'r') as fh:
+ path = os.path.join(JSON_DIR, "trace_context.json")
+ with open(path, "r") as fh:
tests = json.load(fh)
for test in tests:
values = (test.get(param, None) for param in _parameters_list)
- param = pytest.param(*values, id=test.get('test_name'))
+ param = pytest.param(*values, id=test.get("test_name"))
result.append(param)
return result
ATTR_MAP = {
- 'traceparent.version': 0,
- 'traceparent.trace_id': 1,
- 'traceparent.parent_id': 2,
- 'traceparent.trace_flags': 3,
- 'tracestate.version': 0,
- 'tracestate.parent_type': 1,
- 'tracestate.parent_account_id': 2,
- 'tracestate.parent_application_id': 3,
- 'tracestate.span_id': 4,
- 'tracestate.transaction_id': 5,
- 'tracestate.sampled': 6,
- 'tracestate.priority': 7,
- 'tracestate.timestamp': 8,
- 'tracestate.tenant_id': None,
+ "traceparent.version": 0,
+ "traceparent.trace_id": 1,
+ "traceparent.parent_id": 2,
+ "traceparent.trace_flags": 3,
+ "tracestate.version": 0,
+ "tracestate.parent_type": 1,
+ "tracestate.parent_account_id": 2,
+ "tracestate.parent_application_id": 3,
+ "tracestate.span_id": 4,
+ "tracestate.transaction_id": 5,
+ "tracestate.sampled": 6,
+ "tracestate.priority": 7,
+ "tracestate.timestamp": 8,
+ "tracestate.tenant_id": None,
}
def validate_outbound_payload(actual, expected, trusted_account_key):
- traceparent = ''
- tracestate = ''
+ traceparent = ""
+ tracestate = ""
for key, value in actual:
- if key == 'traceparent':
- traceparent = value.split('-')
- elif key == 'tracestate':
+ if key == "traceparent":
+ traceparent = value.split("-")
+ elif key == "tracestate":
vendors = W3CTraceState.decode(value)
- nr_entry = vendors.pop(trusted_account_key + '@nr', '')
- tracestate = nr_entry.split('-')
- exact_values = expected.get('exact', {})
- expected_attrs = expected.get('expected', [])
- unexpected_attrs = expected.get('unexpected', [])
- expected_vendors = expected.get('vendors', [])
+ nr_entry = vendors.pop(f"{trusted_account_key}@nr", "")
+ tracestate = nr_entry.split("-")
+ exact_values = expected.get("exact", {})
+ expected_attrs = expected.get("expected", [])
+ unexpected_attrs = expected.get("unexpected", [])
+ expected_vendors = expected.get("vendors", [])
for key, value in exact_values.items():
- header = traceparent if key.startswith('traceparent.') else tracestate
+ header = traceparent if key.startswith("traceparent.") else tracestate
attr = ATTR_MAP[key]
if attr is not None:
if isinstance(value, bool):
@@ -106,13 +122,13 @@ def validate_outbound_payload(actual, expected, trusted_account_key):
assert header[attr] == str(value)
for key in expected_attrs:
- header = traceparent if key.startswith('traceparent.') else tracestate
+ header = traceparent if key.startswith("traceparent.") else tracestate
attr = ATTR_MAP[key]
if attr is not None:
assert header[attr], key
for key in unexpected_attrs:
- header = traceparent if key.startswith('traceparent.') else tracestate
+ header = traceparent if key.startswith("traceparent.") else tracestate
attr = ATTR_MAP[key]
if attr is not None:
assert not header[attr], key
@@ -125,127 +141,127 @@ def validate_outbound_payload(actual, expected, trusted_account_key):
def target_wsgi_application(environ, start_response):
transaction = current_transaction()
- if not environ['.web_transaction']:
+ if not environ[".web_transaction"]:
transaction.background_task = True
- if environ['.raises_exception']:
+ if environ[".raises_exception"]:
try:
raise ValueError("oops")
except:
transaction.notice_error()
- if '.inbound_headers' in environ:
- transaction.accept_distributed_trace_headers(
- environ['.inbound_headers'],
- transport_type=environ['.transport_type'],
+ if ".inbound_headers" in environ:
+ accept_distributed_trace_headers(
+ environ[".inbound_headers"],
+ transport_type=environ[".transport_type"],
)
payloads = []
- for _ in range(environ['.outbound_calls']):
+ for _ in range(environ[".outbound_calls"]):
payloads.append([])
- transaction.insert_distributed_trace_headers(payloads[-1])
+ insert_distributed_trace_headers(payloads[-1])
- start_response('200 OK', [('Content-Type', 'application/json')])
- return [json.dumps(payloads).encode('utf-8')]
+ start_response("200 OK", [("Content-Type", "application/json")])
+ return [json.dumps(payloads).encode("utf-8")]
test_application = webtest.TestApp(target_wsgi_application)
def override_compute_sampled(override):
- @transient_function_wrapper('newrelic.core.adaptive_sampler',
- 'AdaptiveSampler.compute_sampled')
+ @transient_function_wrapper("newrelic.core.adaptive_sampler", "AdaptiveSampler.compute_sampled")
def _override_compute_sampled(wrapped, instance, args, kwargs):
if override:
return True
return wrapped(*args, **kwargs)
+
return _override_compute_sampled
@pytest.mark.parametrize(_parameters, load_tests())
-def test_trace_context(test_name, trusted_account_key, account_id,
- web_transaction, raises_exception, force_sampled_true,
- span_events_enabled, transport_type, inbound_headers,
- outbound_payloads, intrinsics, expected_metrics):
-
+def test_trace_context(
+ test_name,
+ trusted_account_key,
+ account_id,
+ web_transaction,
+ raises_exception,
+ force_sampled_true,
+ span_events_enabled,
+ transport_type,
+ inbound_headers,
+ outbound_payloads,
+ intrinsics,
+ expected_metrics,
+):
if test_name in XFAIL_TESTS:
pytest.xfail("Waiting on cross agent tests update.")
# Prepare assertions
if not intrinsics:
intrinsics = {}
- common = intrinsics.get('common', {})
- common_required = common.get('expected', [])
- common_forgone = common.get('unexpected', [])
- common_exact = common.get('exact', {})
-
- txn_intrinsics = intrinsics.get('Transaction', {})
- txn_event_required = {'agent': [], 'user': [],
- 'intrinsic': txn_intrinsics.get('expected', [])}
- txn_event_required['intrinsic'].extend(common_required)
- txn_event_forgone = {'agent': [], 'user': [],
- 'intrinsic': txn_intrinsics.get('unexpected', [])}
- txn_event_forgone['intrinsic'].extend(common_forgone)
- txn_event_exact = {'agent': {}, 'user': {},
- 'intrinsic': txn_intrinsics.get('exact', {})}
- txn_event_exact['intrinsic'].update(common_exact)
+ common = intrinsics.get("common", {})
+ common_required = common.get("expected", [])
+ common_forgone = common.get("unexpected", [])
+ common_exact = common.get("exact", {})
+
+ txn_intrinsics = intrinsics.get("Transaction", {})
+ txn_event_required = {"agent": [], "user": [], "intrinsic": txn_intrinsics.get("expected", [])}
+ txn_event_required["intrinsic"].extend(common_required)
+ txn_event_forgone = {"agent": [], "user": [], "intrinsic": txn_intrinsics.get("unexpected", [])}
+ txn_event_forgone["intrinsic"].extend(common_forgone)
+ txn_event_exact = {"agent": {}, "user": {}, "intrinsic": txn_intrinsics.get("exact", {})}
+ txn_event_exact["intrinsic"].update(common_exact)
override_settings = {
- 'distributed_tracing.enabled': True,
- 'span_events.enabled': span_events_enabled,
- 'account_id': account_id,
- 'trusted_account_key': trusted_account_key,
+ "distributed_tracing.enabled": True,
+ "span_events.enabled": span_events_enabled,
+ "account_id": account_id,
+ "trusted_account_key": trusted_account_key,
}
extra_environ = {
- '.web_transaction': web_transaction,
- '.raises_exception': raises_exception,
- '.transport_type': transport_type,
- '.outbound_calls': outbound_payloads and len(outbound_payloads) or 0,
+ ".web_transaction": web_transaction,
+ ".raises_exception": raises_exception,
+ ".transport_type": transport_type,
+ ".outbound_calls": outbound_payloads and len(outbound_payloads) or 0,
}
inbound_headers = inbound_headers and inbound_headers[0] or None
- if transport_type != 'HTTP':
- extra_environ['.inbound_headers'] = inbound_headers
+ if transport_type != "HTTP":
+ extra_environ[".inbound_headers"] = inbound_headers
inbound_headers = None
- elif six.PY2 and inbound_headers:
- inbound_headers = {
- k.encode('utf-8'): v.encode('utf-8')
- for k, v in inbound_headers.items()}
-
- @validate_transaction_metrics(test_name,
- group="Uri",
- rollup_metrics=expected_metrics,
- background_task=not web_transaction)
- @validate_transaction_event_attributes(
- txn_event_required, txn_event_forgone, txn_event_exact)
- @validate_attributes('intrinsic', common_required, common_forgone)
+
+ @validate_transaction_metrics(
+ test_name, group="Uri", rollup_metrics=expected_metrics, background_task=not web_transaction
+ )
+ @validate_transaction_event_attributes(txn_event_required, txn_event_forgone, txn_event_exact)
+ @validate_attributes("intrinsic", common_required, common_forgone)
@override_application_settings(override_settings)
@override_compute_sampled(force_sampled_true)
def _test():
return test_application.get(
- '/' + test_name,
+ f"/{test_name}",
headers=inbound_headers,
extra_environ=extra_environ,
)
- if 'Span' in intrinsics:
- span_intrinsics = intrinsics.get('Span')
- span_expected = span_intrinsics.get('expected', [])
+ if "Span" in intrinsics:
+ span_intrinsics = intrinsics.get("Span")
+ span_expected = span_intrinsics.get("expected", [])
span_expected.extend(common_required)
- span_unexpected = span_intrinsics.get('unexpected', [])
+ span_unexpected = span_intrinsics.get("unexpected", [])
span_unexpected.extend(common_forgone)
- span_exact = span_intrinsics.get('exact', {})
+ span_exact = span_intrinsics.get("exact", {})
span_exact.update(common_exact)
- _test = validate_span_events(exact_intrinsics=span_exact,
- expected_intrinsics=span_expected,
- unexpected_intrinsics=span_unexpected)(_test)
+ _test = validate_span_events(
+ exact_intrinsics=span_exact, expected_intrinsics=span_expected, unexpected_intrinsics=span_unexpected
+ )(_test)
elif not span_events_enabled:
_test = validate_span_events(count=0)(_test)
response = _test()
- assert response.status == '200 OK'
+ assert response.status == "200 OK"
payloads = response.json
if outbound_payloads:
assert len(payloads) == len(outbound_payloads)
diff --git a/tests/datastore_aiomcache/conftest.py b/tests/datastore_aiomcache/conftest.py
new file mode 100644
index 0000000000..e8b1724f20
--- /dev/null
+++ b/tests/datastore_aiomcache/conftest.py
@@ -0,0 +1,34 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
+
+_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+}
+
+collector_agent_registration = collector_agent_registration_fixture(
+ app_name="Python Agent Test (datastore_aiomcache)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_aiomcache/test_aiomcache.py b/tests/datastore_aiomcache/test_aiomcache.py
new file mode 100644
index 0000000000..32d9d44d39
--- /dev/null
+++ b/tests/datastore_aiomcache/test_aiomcache.py
@@ -0,0 +1,111 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+import aiomcache
+from testing_support.db_settings import memcached_settings
+from testing_support.fixture.event_loop import event_loop as loop
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.api.transaction import set_background_task
+from newrelic.common import system_info
+
+DB_SETTINGS = memcached_settings()[0]
+
+MEMCACHED_HOST = DB_SETTINGS["host"]
+MEMCACHED_PORT = DB_SETTINGS["port"]
+MEMCACHED_NAMESPACE = str(os.getpid())
+INSTANCE_METRIC_HOST = system_info.gethostname() if MEMCACHED_HOST == "127.0.0.1" else MEMCACHED_HOST
+INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{INSTANCE_METRIC_HOST}/{MEMCACHED_PORT}"
+
+_test_bt_set_get_delete_scoped_metrics = [
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
+_test_bt_set_get_delete_rollup_metrics = [
+ ("Datastore/all", 3),
+ ("Datastore/allOther", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allOther", 3),
+ (INSTANCE_METRIC_NAME, 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
+
+@validate_transaction_metrics(
+ "test_aiomcache:test_bt_set_get_delete",
+ scoped_metrics=_test_bt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_bt_set_get_delete_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_bt_set_get_delete(loop):
+ set_background_task(True)
+ client = aiomcache.Client(host=MEMCACHED_HOST, port=MEMCACHED_PORT)
+
+ key = f"{MEMCACHED_NAMESPACE}key".encode()
+ data = "value".encode()
+
+ loop.run_until_complete(client.set(key, data))
+ value = loop.run_until_complete(client.get(key))
+ loop.run_until_complete(client.delete(key))
+
+ assert value == data
+
+
+_test_wt_set_get_delete_scoped_metrics = [
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
+_test_wt_set_get_delete_rollup_metrics = [
+ ("Datastore/all", 3),
+ ("Datastore/allWeb", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allWeb", 3),
+ (INSTANCE_METRIC_NAME, 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
+
+@validate_transaction_metrics(
+ "test_aiomcache:test_wt_set_get_delete",
+ scoped_metrics=_test_wt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_wt_set_get_delete_rollup_metrics,
+ background_task=False,
+)
+@background_task()
+def test_wt_set_get_delete(loop):
+ set_background_task(False)
+ client = aiomcache.Client(host=MEMCACHED_HOST, port=MEMCACHED_PORT)
+
+ key = f"{MEMCACHED_NAMESPACE}key".encode()
+ data = "value".encode()
+
+ loop.run_until_complete(client.set(key, data))
+ value = loop.run_until_complete(client.get(key))
+ loop.run_until_complete(client.delete(key))
+
+ assert value == data
diff --git a/tests/datastore_aioredis/conftest.py b/tests/datastore_aioredis/conftest.py
index d501292555..895b700deb 100644
--- a/tests/datastore_aioredis/conftest.py
+++ b/tests/datastore_aioredis/conftest.py
@@ -12,13 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
+import os
-from newrelic.common.package_version_utils import get_package_version_tuple
+import pytest
from testing_support.db_settings import redis_settings
+from testing_support.fixture.event_loop import ( # noqa: F401; pylint: disable=W0611
+ event_loop as loop,
+)
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
-from testing_support.fixture.event_loop import event_loop as loop
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from newrelic.common.package_version_utils import get_package_version_tuple
try:
import aioredis
@@ -37,6 +43,7 @@
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
@@ -62,8 +69,15 @@ def client(request, loop):
raise NotImplementedError()
else:
if request.param == "Redis":
- return loop.run_until_complete(aioredis.create_redis("redis://%s:%d" % (DB_SETTINGS["host"], DB_SETTINGS["port"]), db=0))
+ return loop.run_until_complete(
+ aioredis.create_redis(f"redis://{DB_SETTINGS['host']}:{DB_SETTINGS['port']}", db=0)
+ )
elif request.param == "StrictRedis":
pytest.skip("StrictRedis not implemented.")
else:
raise NotImplementedError()
+
+
+@pytest.fixture(scope="session")
+def key():
+ return f"AIOREDIS-TEST-{str(os.getpid())}"
diff --git a/tests/datastore_aioredis/test_custom_conn_pool.py b/tests/datastore_aioredis/test_custom_conn_pool.py
index b09cf0bdd3..e976f5c728 100644
--- a/tests/datastore_aioredis/test_custom_conn_pool.py
+++ b/tests/datastore_aioredis/test_custom_conn_pool.py
@@ -30,7 +30,7 @@
DB_SETTINGS = redis_settings()[0]
-class FakeConnectionPool(object):
+class FakeConnectionPool():
"""Connection Pool without connection_kwargs attribute."""
def __init__(self, connection):
@@ -85,7 +85,7 @@ async def execute(self, *args, **kwargs):
_host = instance_hostname(DB_SETTINGS["host"])
_port = DB_SETTINGS["port"]
-_instance_metric_name = "Datastore/instance/Redis/%s/%s" % (_host, _port)
+_instance_metric_name = f"Datastore/instance/Redis/{_host}/{_port}"
_enable_rollup_metrics.append((_instance_metric_name, 3))
diff --git a/tests/datastore_aioredis/test_execute_command.py b/tests/datastore_aioredis/test_execute_command.py
index 54851a6592..b470f64b5c 100644
--- a/tests/datastore_aioredis/test_execute_command.py
+++ b/tests/datastore_aioredis/test_execute_command.py
@@ -13,8 +13,6 @@
# limitations under the License.
import pytest
-
-# import aioredis
from conftest import AIOREDIS_VERSION, loop # noqa # pylint: disable=E0611,W0611
from testing_support.db_settings import redis_settings
from testing_support.fixtures import override_application_settings
@@ -56,7 +54,7 @@
_host = instance_hostname(DB_SETTINGS["host"])
_port = DB_SETTINGS["port"]
-_instance_metric_name = "Datastore/instance/Redis/%s/%s" % (_host, _port)
+_instance_metric_name = f"Datastore/instance/Redis/{_host}/{_port}"
_enable_rollup_metrics.append((_instance_metric_name, 1))
diff --git a/tests/datastore_aioredis/test_get_and_set.py b/tests/datastore_aioredis/test_get_and_set.py
index 180f325788..3001b41800 100644
--- a/tests/datastore_aioredis/test_get_and_set.py
+++ b/tests/datastore_aioredis/test_get_and_set.py
@@ -57,16 +57,16 @@
_host = instance_hostname(DB_SETTINGS["host"])
_port = DB_SETTINGS["port"]
-_instance_metric_name = "Datastore/instance/Redis/%s/%s" % (_host, _port)
+_instance_metric_name = f"Datastore/instance/Redis/{_host}/{_port}"
_enable_rollup_metrics.append((_instance_metric_name, 2))
_disable_rollup_metrics.append((_instance_metric_name, None))
-async def exercise_redis(client):
- await client.set("key", "value")
- await client.get("key")
+async def exercise_redis(client, key):
+ await client.set(key, "value")
+ await client.get(key)
@override_application_settings(_enable_instance_settings)
@@ -77,8 +77,8 @@ async def exercise_redis(client):
background_task=True,
)
@background_task()
-def test_redis_client_operation_enable_instance(client, loop):
- loop.run_until_complete(exercise_redis(client))
+def test_redis_client_operation_enable_instance(client, loop, key):
+ loop.run_until_complete(exercise_redis(client, key))
@override_application_settings(_disable_instance_settings)
@@ -89,5 +89,5 @@ def test_redis_client_operation_enable_instance(client, loop):
background_task=True,
)
@background_task()
-def test_redis_client_operation_disable_instance(client, loop):
- loop.run_until_complete(exercise_redis(client))
+def test_redis_client_operation_disable_instance(client, loop, key):
+ loop.run_until_complete(exercise_redis(client, key))
diff --git a/tests/datastore_aioredis/test_multiple_dbs.py b/tests/datastore_aioredis/test_multiple_dbs.py
index d490c1f580..45cb067ce3 100644
--- a/tests/datastore_aioredis/test_multiple_dbs.py
+++ b/tests/datastore_aioredis/test_multiple_dbs.py
@@ -81,8 +81,8 @@
_host_2 = instance_hostname(redis_instance_2["host"])
_port_2 = redis_instance_2["port"]
- instance_metric_name_1 = "Datastore/instance/Redis/%s/%s" % (_host_1, _port_1)
- instance_metric_name_2 = "Datastore/instance/Redis/%s/%s" % (_host_2, _port_2)
+ instance_metric_name_1 = f"Datastore/instance/Redis/{_host_1}/{_port_1}"
+ instance_metric_name_2 = f"Datastore/instance/Redis/{_host_2}/{_port_2}"
_enable_rollup_metrics.extend(
[
@@ -125,10 +125,10 @@ def client_set(request, loop): # noqa
if request.param == "Redis":
return (
loop.run_until_complete(
- aioredis.create_redis("redis://%s:%d" % (DB_SETTINGS[0]["host"], DB_SETTINGS[0]["port"]), db=0)
+ aioredis.create_redis(f"redis://{DB_SETTINGS[0]['host']}:{DB_SETTINGS[0]['port']}", db=0)
),
loop.run_until_complete(
- aioredis.create_redis("redis://%s:%d" % (DB_SETTINGS[1]["host"], DB_SETTINGS[1]["port"]), db=0)
+ aioredis.create_redis(f"redis://{DB_SETTINGS[1]['host']}:{DB_SETTINGS[1]['port']}", db=0)
),
)
elif request.param == "StrictRedis":
@@ -190,7 +190,7 @@ def test_concurrent_calls(client_set, loop): # noqa
import asyncio
async def exercise_concurrent():
- await asyncio.gather(*(client.set("key-%d" % i, i) for i, client in enumerate(client_set)))
- await asyncio.gather(*(client.get("key-%d" % i) for i, client in enumerate(client_set)))
+ await asyncio.gather(*(client.set(f"key-{i}", i) for i, client in enumerate(client_set)))
+ await asyncio.gather(*(client.get(f"key-{i}") for i, client in enumerate(client_set)))
loop.run_until_complete(exercise_concurrent())
diff --git a/tests/datastore_aioredis/test_span_event.py b/tests/datastore_aioredis/test_span_event.py
index 1c9227e54a..7423fb9750 100644
--- a/tests/datastore_aioredis/test_span_event.py
+++ b/tests/datastore_aioredis/test_span_event.py
@@ -70,7 +70,7 @@ def test_span_events(client, instance_enabled, db_instance_enabled, loop):
hostname = instance_hostname(DB_SETTINGS["host"])
exact_agents.update(
{
- "peer.address": "%s:%s" % (hostname, DB_SETTINGS["port"]),
+ "peer.address": f"{hostname}:{DB_SETTINGS['port']}",
"peer.hostname": hostname,
}
)
diff --git a/tests/datastore_aioredis/test_transactions.py b/tests/datastore_aioredis/test_transactions.py
index 0f84ca684e..ced9220225 100644
--- a/tests/datastore_aioredis/test_transactions.py
+++ b/tests/datastore_aioredis/test_transactions.py
@@ -23,42 +23,46 @@
@background_task()
@pytest.mark.parametrize("in_transaction", (True, False))
-def test_pipelines_no_harm(client, in_transaction, loop):
+def test_pipelines_no_harm(client, in_transaction, loop, key):
async def exercise():
if AIOREDIS_VERSION >= (2,):
pipe = client.pipeline(transaction=in_transaction)
else:
pipe = client.pipeline() # Transaction kwarg unsupported
- pipe.set("TXN", 1)
+ pipe.set(key, 1)
return await pipe.execute()
status = loop.run_until_complete(exercise())
assert status == [True]
-def exercise_transaction_sync(pipe):
- pipe.set("TXN", 1)
+def exercise_transaction_sync(key):
+ def _run(pipe):
+ pipe.set(key, 1)
+ return _run
-async def exercise_transaction_async(pipe):
- await pipe.set("TXN", 1)
+def exercise_transaction_async(key):
+ async def _run(pipe):
+ await pipe.set(key, 1)
+ return _run
@SKIPIF_AIOREDIS_V1
@pytest.mark.parametrize("exercise", (exercise_transaction_sync, exercise_transaction_async))
@background_task()
-def test_transactions_no_harm(client, loop, exercise):
- status = loop.run_until_complete(client.transaction(exercise))
+def test_transactions_no_harm(client, loop, key, exercise):
+ status = loop.run_until_complete(client.transaction(exercise(key)))
assert status == [True]
@SKIPIF_AIOREDIS_V2
@background_task()
-def test_multi_exec_no_harm(client, loop):
+def test_multi_exec_no_harm(client, loop, key):
async def exercise():
pipe = client.multi_exec()
- pipe.set("key", "value")
+ pipe.set(key, "value")
status = await pipe.execute()
assert status == [True]
@@ -67,9 +71,7 @@ async def exercise():
@SKIPIF_AIOREDIS_V1
@background_task()
-def test_pipeline_immediate_execution_no_harm(client, loop):
- key = "TXN_WATCH"
-
+def test_pipeline_immediate_execution_no_harm(client, loop, key):
async def exercise():
await client.set(key, 1)
@@ -94,9 +96,7 @@ async def exercise():
@SKIPIF_AIOREDIS_V1
@background_task()
-def test_transaction_immediate_execution_no_harm(client, loop):
- key = "TXN_WATCH"
-
+def test_transaction_immediate_execution_no_harm(client, loop, key):
async def exercise():
async def exercise_transaction(pipe):
value = int(await pipe.get(key))
@@ -119,9 +119,7 @@ async def exercise_transaction(pipe):
@SKIPIF_AIOREDIS_V1
@validate_transaction_errors([])
@background_task()
-def test_transaction_watch_error_no_harm(client, loop):
- key = "TXN_WATCH"
-
+def test_transaction_watch_error_no_harm(client, loop, key):
async def exercise():
async def exercise_transaction(pipe):
value = int(await pipe.get(key))
diff --git a/tests/datastore_aioredis/test_uninstrumented_methods.py b/tests/datastore_aioredis/test_uninstrumented_methods.py
index 7858709c14..eeb04a996f 100644
--- a/tests/datastore_aioredis/test_uninstrumented_methods.py
+++ b/tests/datastore_aioredis/test_uninstrumented_methods.py
@@ -91,4 +91,4 @@ def test_uninstrumented_methods(client):
is_wrapped = lambda m: hasattr(getattr(client, m), "__wrapped__")
uninstrumented = {m for m in methods - IGNORED_METHODS if not is_wrapped(m)}
- assert not uninstrumented, "Uninstrumented methods: %s" % sorted(uninstrumented)
+ assert not uninstrumented, f"Uninstrumented methods: {sorted(uninstrumented)}"
diff --git a/tests/datastore_aredis/conftest.py b/tests/datastore_aredis/conftest.py
index 78067e0fed..a96484ac1d 100644
--- a/tests/datastore_aredis/conftest.py
+++ b/tests/datastore_aredis/conftest.py
@@ -12,21 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixture.event_loop import event_loop as loop # noqa: F401
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixture.event_loop import ( # noqa: F401; pylint: disable=W0611
+ event_loop as loop,
+)
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_aredis)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_aredis)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_aredis/test_custom_conn_pool.py b/tests/datastore_aredis/test_custom_conn_pool.py
index 70c75de9ea..22d6b34f05 100644
--- a/tests/datastore_aredis/test_custom_conn_pool.py
+++ b/tests/datastore_aredis/test_custom_conn_pool.py
@@ -32,7 +32,7 @@
REDIS_PY_VERSION = aredis.VERSION
-class FakeConnectionPool(object):
+class FakeConnectionPool():
"""Connection Pool without connection_kwargs attribute."""
def __init__(self, connection):
@@ -83,7 +83,7 @@ def release(self, connection):
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Redis/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 3)
diff --git a/tests/datastore_aredis/test_execute_command.py b/tests/datastore_aredis/test_execute_command.py
index c5b0fc3323..e040bc57f6 100644
--- a/tests/datastore_aredis/test_execute_command.py
+++ b/tests/datastore_aredis/test_execute_command.py
@@ -58,7 +58,7 @@
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Redis/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 1)
diff --git a/tests/datastore_aredis/test_get_and_set.py b/tests/datastore_aredis/test_get_and_set.py
index 2eeee947bc..d94777cf9c 100644
--- a/tests/datastore_aredis/test_get_and_set.py
+++ b/tests/datastore_aredis/test_get_and_set.py
@@ -58,7 +58,7 @@
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Redis/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 2)
diff --git a/tests/datastore_aredis/test_multiple_dbs.py b/tests/datastore_aredis/test_multiple_dbs.py
index cb4cbac5b2..73d6bd8d3a 100644
--- a/tests/datastore_aredis/test_multiple_dbs.py
+++ b/tests/datastore_aredis/test_multiple_dbs.py
@@ -80,8 +80,8 @@
host_2 = instance_hostname(redis_2["host"])
port_2 = redis_2["port"]
- instance_metric_name_1 = "Datastore/instance/Redis/%s/%s" % (host_1, port_1)
- instance_metric_name_2 = "Datastore/instance/Redis/%s/%s" % (host_2, port_2)
+ instance_metric_name_1 = f"Datastore/instance/Redis/{host_1}/{port_1}"
+ instance_metric_name_2 = f"Datastore/instance/Redis/{host_2}/{port_2}"
_enable_rollup_metrics.extend(
[
@@ -172,7 +172,7 @@ def test_concurrent_calls(loop):
clients = (client_1, client_2)
async def exercise_concurrent():
- await asyncio.gather(*(client.set("key-%d" % i, i) for i, client in enumerate(clients)))
- await asyncio.gather(*(client.get("key-%d" % i) for i, client in enumerate(clients)))
+ await asyncio.gather(*(client.set(f"key-{i}", i) for i, client in enumerate(clients)))
+ await asyncio.gather(*(client.get(f"key-{i}") for i, client in enumerate(clients)))
loop.run_until_complete(exercise_concurrent())
diff --git a/tests/datastore_aredis/test_span_event.py b/tests/datastore_aredis/test_span_event.py
index 2bd238bdae..db4a8a897b 100644
--- a/tests/datastore_aredis/test_span_event.py
+++ b/tests/datastore_aredis/test_span_event.py
@@ -79,7 +79,7 @@ def test_span_events(instance_enabled, db_instance_enabled, loop):
settings = _enable_instance_settings.copy()
hostname = instance_hostname(DB_SETTINGS['host'])
exact_agents.update({
- 'peer.address': '%s:%s' % (hostname, DB_SETTINGS['port']),
+ 'peer.address': f"{hostname}:{DB_SETTINGS['port']}",
'peer.hostname': hostname,
})
else:
diff --git a/tests/datastore_aredis/test_uninstrumented_methods.py b/tests/datastore_aredis/test_uninstrumented_methods.py
index 38901e5c5d..e4b9c90042 100644
--- a/tests/datastore_aredis/test_uninstrumented_methods.py
+++ b/tests/datastore_aredis/test_uninstrumented_methods.py
@@ -45,4 +45,4 @@ def test_uninstrumented_methods():
is_wrapped = lambda m: hasattr(getattr(strict_redis_client, m), "__wrapped__")
uninstrumented = {m for m in methods - IGNORED_METHODS if not is_wrapped(m)}
- assert not uninstrumented, "Uninstrumented methods: %s" % sorted(uninstrumented)
+ assert not uninstrumented, f"Uninstrumented methods: {sorted(uninstrumented)}"
diff --git a/tests/datastore_asyncpg/conftest.py b/tests/datastore_asyncpg/conftest.py
index 69bc0501a2..783e9b8462 100644
--- a/tests/datastore_asyncpg/conftest.py
+++ b/tests/datastore_asyncpg/conftest.py
@@ -12,11 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from testing_support.fixture.event_loop import event_loop
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixture.event_loop import ( # noqa: F401; pylint: disable=W0611
+ event_loop,
+)
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
diff --git a/tests/datastore_asyncpg/test_multiple_dbs.py b/tests/datastore_asyncpg/test_multiple_dbs.py
index a917a9e83d..afc6324fe2 100644
--- a/tests/datastore_asyncpg/test_multiple_dbs.py
+++ b/tests/datastore_asyncpg/test_multiple_dbs.py
@@ -12,20 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import asyncio
-
import asyncpg
import pytest
from testing_support.db_settings import postgresql_settings
from testing_support.fixtures import override_application_settings
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
from testing_support.util import instance_hostname
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
DB_MULTIPLE_SETTINGS = postgresql_settings()
-ASYNCPG_VERSION = tuple(int(x) for x in getattr(asyncpg, "__version__", "0.0").split(".")[:2])
+ASYNCPG_VERSION = get_package_version_tuple("asyncpg")
if ASYNCPG_VERSION < (0, 11):
CONNECT_METRICS = []
@@ -84,8 +85,8 @@
_host_2 = instance_hostname(_postgresql_2["host"])
_port_2 = _postgresql_2["port"]
- _instance_metric_name_1 = "Datastore/instance/Postgres/%s/%s" % (_host_1, _port_1)
- _instance_metric_name_2 = "Datastore/instance/Postgres/%s/%s" % (_host_2, _port_2)
+ _instance_metric_name_1 = f"Datastore/instance/Postgres/{_host_1}/{_port_1}"
+ _instance_metric_name_2 = f"Datastore/instance/Postgres/{_host_2}/{_port_2}"
_enable_rollup_metrics.extend(
[
@@ -100,7 +101,6 @@
async def _exercise_db():
-
postgresql1 = DB_MULTIPLE_SETTINGS[0]
postgresql2 = DB_MULTIPLE_SETTINGS[1]
@@ -145,6 +145,7 @@ async def _exercise_db():
)
@background_task()
def test_multiple_databases_enable_instance(event_loop):
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(_exercise_db())
@@ -161,4 +162,5 @@ def test_multiple_databases_enable_instance(event_loop):
)
@background_task()
def test_multiple_databases_disable_instance(event_loop):
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(_exercise_db())
diff --git a/tests/datastore_asyncpg/test_query.py b/tests/datastore_asyncpg/test_query.py
index 838ced61da..bccafbdfd5 100644
--- a/tests/datastore_asyncpg/test_query.py
+++ b/tests/datastore_asyncpg/test_query.py
@@ -27,17 +27,18 @@
)
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
DB_SETTINGS = postgresql_settings()[0]
PG_PREFIX = "Datastore/operation/Postgres/"
-ASYNCPG_VERSION = tuple(int(x) for x in getattr(asyncpg, "__version__", "0.0").split(".")[:2])
+ASYNCPG_VERSION = get_package_version_tuple("asyncpg")
if ASYNCPG_VERSION < (0, 11):
CONNECT_METRICS = ()
else:
- CONNECT_METRICS = ((PG_PREFIX + "connect", 1),)
+ CONNECT_METRICS = ((f"{PG_PREFIX}connect", 1),)
@pytest.fixture
@@ -58,13 +59,14 @@ def conn(event_loop):
@validate_transaction_metrics(
"test_single",
background_task=True,
- scoped_metrics=((PG_PREFIX + "select", 1),),
+ scoped_metrics=((f"{PG_PREFIX}select", 1),),
rollup_metrics=(("Datastore/all", 1),),
)
@validate_tt_collector_json(datastore_params={"port_path_or_id": str(DB_SETTINGS["port"])})
@background_task(name="test_single")
@pytest.mark.parametrize("method", ("execute",))
def test_single(event_loop, method, conn):
+ assert ASYNCPG_VERSION is not None
_method = getattr(conn, method)
event_loop.run_until_complete(_method("""SELECT 0"""))
@@ -73,14 +75,15 @@ def test_single(event_loop, method, conn):
"test_prepared_single",
background_task=True,
scoped_metrics=(
- (PG_PREFIX + "prepare", 1),
- (PG_PREFIX + "select", 1),
+ (f"{PG_PREFIX}prepare", 1),
+ (f"{PG_PREFIX}select", 1),
),
rollup_metrics=(("Datastore/all", 2),),
)
@background_task(name="test_prepared_single")
@pytest.mark.parametrize("method", ("fetch", "fetchrow", "fetchval"))
def test_prepared_single(event_loop, method, conn):
+ assert ASYNCPG_VERSION is not None
_method = getattr(conn, method)
event_loop.run_until_complete(_method("""SELECT 0"""))
@@ -88,19 +91,20 @@ def test_prepared_single(event_loop, method, conn):
@validate_transaction_metrics(
"test_prepare",
background_task=True,
- scoped_metrics=((PG_PREFIX + "prepare", 1),),
+ scoped_metrics=((f"{PG_PREFIX}prepare", 1),),
rollup_metrics=(("Datastore/all", 1),),
)
@background_task(name="test_prepare")
def test_prepare(event_loop, conn):
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(conn.prepare("""SELECT 0"""))
@pytest.fixture
def table(event_loop, conn):
- table_name = "table_%d" % os.getpid()
+ table_name = f"table_{os.getpid()}"
- event_loop.run_until_complete(conn.execute("""create table %s (a integer, b real, c text)""" % table_name))
+ event_loop.run_until_complete(conn.execute(f"""create table {table_name} (a integer, b real, c text)"""))
return table_name
@@ -110,8 +114,8 @@ def table(event_loop, conn):
"test_copy",
background_task=True,
scoped_metrics=(
- (PG_PREFIX + "prepare", 1),
- (PG_PREFIX + "copy", 3),
+ (f"{PG_PREFIX}prepare", 1),
+ (f"{PG_PREFIX}copy", 3),
),
rollup_metrics=(("Datastore/all", 4),),
)
@@ -125,6 +129,7 @@ async def amain():
# 2 statements
await conn.copy_from_query("""SELECT 0""", output=BytesIO())
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(amain())
@@ -132,13 +137,14 @@ async def amain():
"test_select_many",
background_task=True,
scoped_metrics=(
- (PG_PREFIX + "prepare", 1),
- (PG_PREFIX + "select", 1),
+ (f"{PG_PREFIX}prepare", 1),
+ (f"{PG_PREFIX}select", 1),
),
rollup_metrics=(("Datastore/all", 2),),
)
@background_task(name="test_select_many")
def test_select_many(event_loop, conn):
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(conn.executemany("""SELECT $1::int""", ((1,), (2,))))
@@ -146,9 +152,9 @@ def test_select_many(event_loop, conn):
"test_transaction",
background_task=True,
scoped_metrics=(
- (PG_PREFIX + "begin", 1),
- (PG_PREFIX + "select", 1),
- (PG_PREFIX + "commit", 1),
+ (f"{PG_PREFIX}begin", 1),
+ (f"{PG_PREFIX}select", 1),
+ (f"{PG_PREFIX}commit", 1),
),
rollup_metrics=(("Datastore/all", 3),),
)
@@ -158,6 +164,7 @@ async def amain():
async with conn.transaction():
await conn.execute("""SELECT 0""")
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(amain())
@@ -165,10 +172,10 @@ async def amain():
"test_cursor",
background_task=True,
scoped_metrics=(
- (PG_PREFIX + "begin", 1),
- (PG_PREFIX + "prepare", 2),
- (PG_PREFIX + "select", 3),
- (PG_PREFIX + "commit", 1),
+ (f"{PG_PREFIX}begin", 1),
+ (f"{PG_PREFIX}prepare", 2),
+ (f"{PG_PREFIX}select", 3),
+ (f"{PG_PREFIX}commit", 1),
),
rollup_metrics=(("Datastore/all", 7),),
)
@@ -181,6 +188,7 @@ async def amain():
await conn.cursor("SELECT 0")
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(amain())
@@ -193,13 +201,14 @@ async def amain():
background_task=True,
rollup_metrics=[
(
- "Datastore/instance/Postgres/" + instance_hostname("localhost") + "//.s.PGSQL.THIS_FILE_BETTER_NOT_EXIST",
+ f"Datastore/instance/Postgres/{instance_hostname('localhost')}//.s.PGSQL.THIS_FILE_BETTER_NOT_EXIST",
1,
)
],
)
@background_task(name="test_unix_socket_connect")
def test_unix_socket_connect(event_loop):
+ assert ASYNCPG_VERSION is not None
with pytest.raises(OSError):
event_loop.run_until_complete(asyncpg.connect("postgres://?host=/.s.PGSQL.THIS_FILE_BETTER_NOT_EXIST"))
@@ -211,7 +220,7 @@ def test_unix_socket_connect(event_loop):
@validate_transaction_metrics(
"test_pool_acquire",
background_task=True,
- scoped_metrics=((PG_PREFIX + "connect", 2),),
+ scoped_metrics=((f"{PG_PREFIX}connect", 2),),
)
@background_task(name="test_pool_acquire")
def test_pool_acquire(event_loop):
@@ -233,4 +242,5 @@ async def amain():
finally:
await pool.close()
+ assert ASYNCPG_VERSION is not None
event_loop.run_until_complete(amain())
diff --git a/tests/datastore_bmemcached/conftest.py b/tests/datastore_bmemcached/conftest.py
index c970c1c347..91149e18a5 100644
--- a/tests/datastore_bmemcached/conftest.py
+++ b/tests/datastore_bmemcached/conftest.py
@@ -12,20 +12,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_bmemcached)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_bmemcached)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_bmemcached/test_memcache.py b/tests/datastore_bmemcached/test_memcache.py
index 68eee06333..94b8a04810 100644
--- a/tests/datastore_bmemcached/test_memcache.py
+++ b/tests/datastore_bmemcached/test_memcache.py
@@ -13,83 +13,97 @@
# limitations under the License.
import os
-from testing_support.db_settings import memcached_settings
+
import bmemcached
+from testing_support.db_settings import memcached_settings
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
from newrelic.api.transaction import set_background_task
-
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.db_settings import memcached_settings
-
+from newrelic.common import system_info
DB_SETTINGS = memcached_settings()[0]
-MEMCACHED_HOST = DB_SETTINGS['host']
-MEMCACHED_PORT = DB_SETTINGS['port']
+MEMCACHED_HOST = DB_SETTINGS["host"]
+MEMCACHED_PORT = DB_SETTINGS["port"]
MEMCACHED_NAMESPACE = str(os.getpid())
-MEMCACHED_ADDR = '%s:%s' % (MEMCACHED_HOST, MEMCACHED_PORT)
+MEMCACHED_ADDR = f"{MEMCACHED_HOST}:{MEMCACHED_PORT}"
+INSTANCE_METRIC_HOST = system_info.gethostname() if MEMCACHED_HOST == "127.0.0.1" else MEMCACHED_HOST
+INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{INSTANCE_METRIC_HOST}/{MEMCACHED_PORT}"
_test_bt_set_get_delete_scoped_metrics = [
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
_test_bt_set_get_delete_rollup_metrics = [
- ('Datastore/all', 3),
- ('Datastore/allOther', 3),
- ('Datastore/Memcached/all', 3),
- ('Datastore/Memcached/allOther', 3),
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/all", 3),
+ ("Datastore/allOther", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allOther", 3),
+ (INSTANCE_METRIC_NAME, 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
@validate_transaction_metrics(
- 'test_memcache:test_bt_set_get_delete',
- scoped_metrics=_test_bt_set_get_delete_scoped_metrics,
- rollup_metrics=_test_bt_set_get_delete_rollup_metrics,
- background_task=True)
+ "test_memcache:test_bt_set_get_delete",
+ scoped_metrics=_test_bt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_bt_set_get_delete_rollup_metrics,
+ background_task=True,
+)
@background_task()
def test_bt_set_get_delete():
set_background_task(True)
client = bmemcached.Client([MEMCACHED_ADDR])
- key = MEMCACHED_NAMESPACE + 'key'
+ key = f"{MEMCACHED_NAMESPACE}key"
- client.set(key, 'value')
+ client.set(key, "value")
value = client.get(key)
client.delete(key)
- assert value == 'value'
+ assert value == "value"
+
_test_wt_set_get_delete_scoped_metrics = [
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
_test_wt_set_get_delete_rollup_metrics = [
- ('Datastore/all', 3),
- ('Datastore/allWeb', 3),
- ('Datastore/Memcached/all', 3),
- ('Datastore/Memcached/allWeb', 3),
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/all", 3),
+ ("Datastore/allWeb", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allWeb", 3),
+ (INSTANCE_METRIC_NAME, 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
@validate_transaction_metrics(
- 'test_memcache:test_wt_set_get_delete',
- scoped_metrics=_test_wt_set_get_delete_scoped_metrics,
- rollup_metrics=_test_wt_set_get_delete_rollup_metrics,
- background_task=False)
+ "test_memcache:test_wt_set_get_delete",
+ scoped_metrics=_test_wt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_wt_set_get_delete_rollup_metrics,
+ background_task=False,
+)
@background_task()
def test_wt_set_get_delete():
set_background_task(False)
client = bmemcached.Client([MEMCACHED_ADDR])
- key = MEMCACHED_NAMESPACE + 'key'
+ key = f"{MEMCACHED_NAMESPACE}key"
- client.set(key, 'value')
+ client.set(key, "value")
value = client.get(key)
client.delete(key)
- assert value == 'value'
+ assert value == "value"
diff --git a/tests/datastore_elasticsearch/conftest.py b/tests/datastore_elasticsearch/conftest.py
index 53fa6fcdc3..e70dde884f 100644
--- a/tests/datastore_elasticsearch/conftest.py
+++ b/tests/datastore_elasticsearch/conftest.py
@@ -14,13 +14,15 @@
import pytest
from testing_support.db_settings import elasticsearch_settings
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from newrelic.common.package_version_utils import get_package_version
-
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
@@ -37,7 +39,7 @@
ES_VERSION = tuple([int(n) for n in get_package_version("elasticsearch").split(".")])
ES_SETTINGS = elasticsearch_settings()[0]
ES_MULTIPLE_SETTINGS = elasticsearch_settings()
-ES_URL = "http://%s:%s" % (ES_SETTINGS["host"], ES_SETTINGS["port"])
+ES_URL = f"http://{ES_SETTINGS['host']}:{ES_SETTINGS['port']}"
@pytest.fixture(scope="session")
diff --git a/tests/datastore_elasticsearch/test_connection.py b/tests/datastore_elasticsearch/test_connection.py
index 2e888af9b5..9e8f17b4c1 100644
--- a/tests/datastore_elasticsearch/test_connection.py
+++ b/tests/datastore_elasticsearch/test_connection.py
@@ -36,7 +36,7 @@ def test_connection_default():
else:
conn = Connection(**HOST)
- assert conn._nr_host_port == ("localhost", ES_SETTINGS["port"])
+ assert conn._nr_host_port == (ES_SETTINGS["host"], ES_SETTINGS["port"])
@SKIP_IF_V7
diff --git a/tests/datastore_elasticsearch/test_elasticsearch.py b/tests/datastore_elasticsearch/test_elasticsearch.py
index d2c892ea92..294118192a 100644
--- a/tests/datastore_elasticsearch/test_elasticsearch.py
+++ b/tests/datastore_elasticsearch/test_elasticsearch.py
@@ -138,7 +138,7 @@ def is_importable(module_path):
_host = instance_hostname(ES_SETTINGS["host"])
_port = ES_SETTINGS["port"]
-_instance_metric_name = "Datastore/instance/Elasticsearch/%s/%s" % (_host, _port)
+_instance_metric_name = f"Datastore/instance/Elasticsearch/{_host}/{_port}"
_enable_rollup_metrics.append((_instance_metric_name, _all_count))
diff --git a/tests/datastore_elasticsearch/test_instrumented_methods.py b/tests/datastore_elasticsearch/test_instrumented_methods.py
index 4ad88c2a58..7c38bcaa8b 100644
--- a/tests/datastore_elasticsearch/test_instrumented_methods.py
+++ b/tests/datastore_elasticsearch/test_instrumented_methods.py
@@ -71,7 +71,7 @@ def client(client):
],
)
def test_method_on_client_datastore_trace_inputs(client, sub_module, method, args, kwargs, expected_index):
- expected_operation = "%s.%s" % (sub_module, method) if sub_module else method
+ expected_operation = f"{sub_module}.{method}" if sub_module else method
@validate_datastore_trace_inputs(target=expected_index, operation=expected_operation)
@background_task()
@@ -93,7 +93,7 @@ def is_wrapped(m):
methods = {m for m in dir(_object) if not m[0] == "_"}
uninstrumented = {m for m in (methods - ignored_methods) if not is_wrapped(m)}
- assert not uninstrumented, "There are uninstrumented methods: %s" % uninstrumented
+ assert not uninstrumented, f"There are uninstrumented methods: {uninstrumented}"
@RUN_IF_V8
diff --git a/tests/datastore_elasticsearch/test_mget.py b/tests/datastore_elasticsearch/test_mget.py
index f3f7c09790..5058146fe6 100644
--- a/tests/datastore_elasticsearch/test_mget.py
+++ b/tests/datastore_elasticsearch/test_mget.py
@@ -68,8 +68,8 @@
host_2 = instance_hostname(es_2["host"])
port_2 = es_2["port"]
- instance_metric_name_1 = "Datastore/instance/Elasticsearch/%s/%s" % (host_1, port_1)
- instance_metric_name_2 = "Datastore/instance/Elasticsearch/%s/%s" % (host_2, port_2)
+ instance_metric_name_1 = f"Datastore/instance/Elasticsearch/{host_1}/{port_1}"
+ instance_metric_name_2 = f"Datastore/instance/Elasticsearch/{host_2}/{port_2}"
_enable_rollup_metrics.extend(
[
@@ -88,7 +88,7 @@
@pytest.fixture(scope="module")
def client():
- urls = ["http://%s:%s" % (db["host"], db["port"]) for db in ES_MULTIPLE_SETTINGS]
+ urls = [f"http://{db['host']}:{db['port']}" for db in ES_MULTIPLE_SETTINGS]
# When selecting a connection from the pool, use the round robin method.
# This is actually the default already. Using round robin will ensure that
# doing two db calls will mean elastic search is talking to two different
diff --git a/tests/datastore_elasticsearch/test_multiple_dbs.py b/tests/datastore_elasticsearch/test_multiple_dbs.py
index 71c47b1685..b427c90a12 100644
--- a/tests/datastore_elasticsearch/test_multiple_dbs.py
+++ b/tests/datastore_elasticsearch/test_multiple_dbs.py
@@ -61,8 +61,8 @@
host_2 = instance_hostname(es_2["host"])
port_2 = es_2["port"]
- instance_metric_name_1 = "Datastore/instance/Elasticsearch/%s/%s" % (host_1, port_1)
- instance_metric_name_2 = "Datastore/instance/Elasticsearch/%s/%s" % (host_2, port_2)
+ instance_metric_name_1 = f"Datastore/instance/Elasticsearch/{host_1}/{port_1}"
+ instance_metric_name_2 = f"Datastore/instance/Elasticsearch/{host_2}/{port_2}"
_enable_rollup_metrics.extend(
[
@@ -104,7 +104,7 @@ def _exercise_es(es):
@background_task()
def test_multiple_dbs_enabled():
for db in ES_MULTIPLE_SETTINGS:
- es_url = "http://%s:%s" % (db["host"], db["port"])
+ es_url = f"http://{db['host']}:{db['port']}"
client = Elasticsearch(es_url)
_exercise_es(client)
@@ -120,6 +120,6 @@ def test_multiple_dbs_enabled():
@background_task()
def test_multiple_dbs_disabled():
for db in ES_MULTIPLE_SETTINGS:
- es_url = "http://%s:%s" % (db["host"], db["port"])
+ es_url = f"http://{db['host']}:{db['port']}"
client = Elasticsearch(es_url)
_exercise_es(client)
diff --git a/tests/datastore_firestore/conftest.py b/tests/datastore_firestore/conftest.py
new file mode 100644
index 0000000000..6fd1550753
--- /dev/null
+++ b/tests/datastore_firestore/conftest.py
@@ -0,0 +1,128 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import os
+import uuid
+
+import pytest
+from google.cloud.firestore import AsyncClient, Client
+from testing_support.db_settings import firestore_settings
+from testing_support.fixture.event_loop import ( # noqa: F401; pylint: disable=W0611
+ event_loop as loop,
+)
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
+
+from newrelic.api.datastore_trace import DatastoreTrace
+from newrelic.api.time_trace import current_trace
+from newrelic.common.system_info import LOCALHOST_EQUIVALENTS, gethostname
+
+DB_SETTINGS = firestore_settings()[0]
+FIRESTORE_HOST = DB_SETTINGS["host"]
+FIRESTORE_PORT = DB_SETTINGS["port"]
+
+_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
+}
+
+collector_agent_registration = collector_agent_registration_fixture(
+ app_name="Python Agent Test (datastore_firestore)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
+
+
+@pytest.fixture()
+def instance_info():
+ host = gethostname() if FIRESTORE_HOST in LOCALHOST_EQUIVALENTS else FIRESTORE_HOST
+ return {
+ "host": host,
+ "port_path_or_id": str(FIRESTORE_PORT),
+ "db.instance": "projects/google-cloud-firestore-emulator/databases/(default)",
+ }
+
+
+@pytest.fixture(scope="session")
+def client():
+ os.environ["FIRESTORE_EMULATOR_HOST"] = f"{FIRESTORE_HOST}:{FIRESTORE_PORT}"
+ client = Client()
+ # Ensure connection is available
+ client.collection("healthcheck").document("healthcheck").set({}, retry=None, timeout=5)
+ return client
+
+
+@pytest.fixture(scope="function")
+def collection(client):
+ collection_ = client.collection(f"firestore_collection_{str(uuid.uuid4())}")
+ yield collection_
+ client.recursive_delete(collection_)
+
+
+@pytest.fixture(scope="session")
+def async_client(loop):
+ os.environ["FIRESTORE_EMULATOR_HOST"] = f"{FIRESTORE_HOST}:{FIRESTORE_PORT}"
+ client = AsyncClient()
+ loop.run_until_complete(
+ client.collection("healthcheck").document("healthcheck").set({}, retry=None, timeout=5)
+ ) # Ensure connection is available
+ return client
+
+
+@pytest.fixture(scope="function")
+def async_collection(async_client, collection):
+ # Use the same collection name as the collection fixture
+ yield async_client.collection(collection.id)
+
+
+@pytest.fixture(scope="session")
+def assert_trace_for_generator():
+ def _assert_trace_for_generator(generator_func, *args, **kwargs):
+ txn = current_trace()
+ assert not isinstance(txn, DatastoreTrace)
+
+ # Check for generator trace on collections
+ _trace_check = []
+ for _ in generator_func(*args, **kwargs):
+ _trace_check.append(isinstance(current_trace(), DatastoreTrace))
+ assert _trace_check and all(_trace_check) # All checks are True, and at least 1 is present.
+ assert current_trace() is txn # Generator trace has exited.
+
+ return _assert_trace_for_generator
+
+
+@pytest.fixture(scope="session")
+def assert_trace_for_async_generator(loop):
+ def _assert_trace_for_async_generator(generator_func, *args, **kwargs):
+ _trace_check = []
+ txn = current_trace()
+ assert not isinstance(txn, DatastoreTrace)
+
+ async def coro():
+ # Check for generator trace on collections
+ async for _ in generator_func(*args, **kwargs):
+ _trace_check.append(isinstance(current_trace(), DatastoreTrace))
+
+ loop.run_until_complete(coro())
+
+ assert _trace_check and all(_trace_check) # All checks are True, and at least 1 is present.
+ assert current_trace() is txn # Generator trace has exited.
+
+ return _assert_trace_for_async_generator
diff --git a/tests/datastore_firestore/test_async_batching.py b/tests/datastore_firestore/test_async_batching.py
new file mode 100644
index 0000000000..5e6fbd3c7d
--- /dev/null
+++ b/tests/datastore_firestore/test_async_batching.py
@@ -0,0 +1,73 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture()
+def exercise_async_write_batch(async_client, async_collection):
+ async def _exercise_async_write_batch():
+ docs = [async_collection.document(str(x)) for x in range(1, 4)]
+ async_batch = async_client.batch()
+ for doc in docs:
+ async_batch.set(doc, {})
+
+ await async_batch.commit()
+
+ return _exercise_async_write_batch
+
+
+def test_firestore_async_write_batch(loop, exercise_async_write_batch, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/commit", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/all", 1),
+ ("Datastore/allOther", 1),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 1),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_write_batch",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_write_batch")
+ def _test():
+ loop.run_until_complete(exercise_async_write_batch())
+
+ _test()
+
+
+def test_firestore_async_write_batch_trace_node_datastore_params(loop, exercise_async_write_batch, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_write_batch())
+
+ _test()
\ No newline at end of file
diff --git a/tests/datastore_firestore/test_async_client.py b/tests/datastore_firestore/test_async_client.py
new file mode 100644
index 0000000000..236d9c2161
--- /dev/null
+++ b/tests/datastore_firestore/test_async_client.py
@@ -0,0 +1,87 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture()
+def existing_document(collection):
+ doc = collection.document("document")
+ doc.set({"x": 1})
+ return doc
+
+
+@pytest.fixture()
+def exercise_async_client(async_client, existing_document):
+ async def _exercise_async_client():
+ assert len([_ async for _ in async_client.collections()]) >= 1
+ doc = [_ async for _ in async_client.get_all([existing_document])][0]
+ assert doc.to_dict()["x"] == 1
+
+ return _exercise_async_client
+
+
+def test_firestore_async_client(loop, exercise_async_client, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/collections", 1),
+ ("Datastore/operation/Firestore/get_all", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_client",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_client")
+ def _test():
+ loop.run_until_complete(exercise_async_client())
+
+ _test()
+
+
+@background_task()
+def test_firestore_async_client_generators(async_client, collection, assert_trace_for_async_generator):
+ doc = collection.document("test")
+ doc.set({})
+
+ assert_trace_for_async_generator(async_client.collections)
+ assert_trace_for_async_generator(async_client.get_all, [doc])
+
+
+def test_firestore_async_client_trace_node_datastore_params(loop, exercise_async_client, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_client())
+
+ _test()
\ No newline at end of file
diff --git a/tests/datastore_firestore/test_async_collections.py b/tests/datastore_firestore/test_async_collections.py
new file mode 100644
index 0000000000..c1658d18b3
--- /dev/null
+++ b/tests/datastore_firestore/test_async_collections.py
@@ -0,0 +1,94 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture()
+def exercise_async_collections(async_collection):
+ async def _exercise_async_collections():
+ async_collection.document("DoesNotExist")
+ await async_collection.add({"capital": "Rome", "currency": "Euro", "language": "Italian"}, "Italy")
+ await async_collection.add({"capital": "Mexico City", "currency": "Peso", "language": "Spanish"}, "Mexico")
+
+ documents_get = await async_collection.get()
+ assert len(documents_get) == 2
+ documents_stream = [_ async for _ in async_collection.stream()]
+ assert len(documents_stream) == 2
+ documents_list = [_ async for _ in async_collection.list_documents()]
+ assert len(documents_list) == 2
+
+ return _exercise_async_collections
+
+
+def test_firestore_async_collections(loop, exercise_async_collections, async_collection, instance_info):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{async_collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/get", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/list_documents", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/add", 2),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/add", 2),
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/operation/Firestore/list_documents", 1),
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 5),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_collections",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_collections")
+ def _test():
+ loop.run_until_complete(exercise_async_collections())
+
+ _test()
+
+
+@background_task()
+def test_firestore_async_collections_generators(collection, async_collection, assert_trace_for_async_generator):
+ collection.add({})
+ collection.add({})
+ assert len([_ for _ in collection.list_documents()]) == 2
+
+ assert_trace_for_async_generator(async_collection.stream)
+ assert_trace_for_async_generator(async_collection.list_documents)
+
+
+def test_firestore_async_collections_trace_node_datastore_params(loop, exercise_async_collections, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_collections())
+
+ _test()
\ No newline at end of file
diff --git a/tests/datastore_firestore/test_async_documents.py b/tests/datastore_firestore/test_async_documents.py
new file mode 100644
index 0000000000..2a0d5e9b81
--- /dev/null
+++ b/tests/datastore_firestore/test_async_documents.py
@@ -0,0 +1,108 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture()
+def exercise_async_documents(async_collection):
+ async def _exercise_async_documents():
+ italy_doc = async_collection.document("Italy")
+ await italy_doc.set({"capital": "Rome", "currency": "Euro", "language": "Italian"})
+ await italy_doc.get()
+ italian_cities = italy_doc.collection("cities")
+ await italian_cities.add({"capital": "Rome"})
+ retrieved_coll = [_ async for _ in italy_doc.collections()]
+ assert len(retrieved_coll) == 1
+
+ usa_doc = async_collection.document("USA")
+ await usa_doc.create({"capital": "Washington D.C.", "currency": "Dollar", "language": "English"})
+ await usa_doc.update({"president": "Joe Biden"})
+
+ await async_collection.document("USA").delete()
+
+ return _exercise_async_documents
+
+
+def test_firestore_async_documents(loop, exercise_async_documents, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/statement/Firestore/Italy/set", 1),
+ ("Datastore/statement/Firestore/Italy/get", 1),
+ ("Datastore/statement/Firestore/Italy/collections", 1),
+ ("Datastore/statement/Firestore/cities/add", 1),
+ ("Datastore/statement/Firestore/USA/create", 1),
+ ("Datastore/statement/Firestore/USA/update", 1),
+ ("Datastore/statement/Firestore/USA/delete", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/set", 1),
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/add", 1),
+ ("Datastore/operation/Firestore/collections", 1),
+ ("Datastore/operation/Firestore/create", 1),
+ ("Datastore/operation/Firestore/update", 1),
+ ("Datastore/operation/Firestore/delete", 1),
+ ("Datastore/all", 7),
+ ("Datastore/allOther", 7),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 7),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_documents",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_documents")
+ def _test():
+ loop.run_until_complete(exercise_async_documents())
+
+ _test()
+
+
+@background_task()
+def test_firestore_async_documents_generators(
+ collection, async_collection, assert_trace_for_async_generator, instance_info
+):
+ subcollection_doc = collection.document("SubCollections")
+ subcollection_doc.set({})
+ subcollection_doc.collection("collection1").add({})
+ subcollection_doc.collection("collection2").add({})
+ assert len([_ for _ in subcollection_doc.collections()]) == 2
+
+ async_subcollection = async_collection.document(subcollection_doc.id)
+
+ assert_trace_for_async_generator(async_subcollection.collections)
+
+
+def test_firestore_async_documents_trace_node_datastore_params(loop, exercise_async_documents, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_documents())
+
+ _test()
\ No newline at end of file
diff --git a/tests/datastore_firestore/test_async_query.py b/tests/datastore_firestore/test_async_query.py
new file mode 100644
index 0000000000..4d0267e90c
--- /dev/null
+++ b/tests/datastore_firestore/test_async_query.py
@@ -0,0 +1,249 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture(autouse=True)
+def sample_data(collection):
+ for x in range(1, 6):
+ collection.add({"x": x})
+
+ subcollection_doc = collection.document("subcollection")
+ subcollection_doc.set({})
+ subcollection_doc.collection("subcollection1").add({})
+
+
+# ===== AsyncQuery =====
+
+
+@pytest.fixture()
+def exercise_async_query(async_collection):
+ async def _exercise_async_query():
+ async_query = (
+ async_collection.select("x").limit(10).order_by("x").where(field_path="x", op_string="<=", value=3)
+ )
+ assert len(await async_query.get()) == 3
+ assert len([_ async for _ in async_query.stream()]) == 3
+
+ return _exercise_async_query
+
+
+def test_firestore_async_query(loop, exercise_async_query, async_collection, instance_info):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{async_collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/get", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ # @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_query",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_query")
+ def _test():
+ loop.run_until_complete(exercise_async_query())
+
+ _test()
+
+
+@background_task()
+def test_firestore_async_query_generators(async_collection, assert_trace_for_async_generator):
+ async_query = async_collection.select("x").where(field_path="x", op_string="<=", value=3)
+ assert_trace_for_async_generator(async_query.stream)
+
+
+def test_firestore_async_query_trace_node_datastore_params(loop, exercise_async_query, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_query())
+
+ _test()
+
+
+# ===== AsyncAggregationQuery =====
+
+
+@pytest.fixture()
+def exercise_async_aggregation_query(async_collection):
+ async def _exercise_async_aggregation_query():
+ async_aggregation_query = async_collection.select("x").where(field_path="x", op_string="<=", value=3).count()
+ assert (await async_aggregation_query.get())[0][0].value == 3
+ assert [_ async for _ in async_aggregation_query.stream()][0][0].value == 3
+
+ return _exercise_async_aggregation_query
+
+
+def test_firestore_async_aggregation_query(loop, exercise_async_aggregation_query, async_collection, instance_info):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{async_collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/get", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_aggregation_query",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_aggregation_query")
+ def _test():
+ loop.run_until_complete(exercise_async_aggregation_query())
+
+ _test()
+
+
+@background_task()
+def test_firestore_async_aggregation_query_generators(async_collection, assert_trace_for_async_generator):
+ async_aggregation_query = async_collection.select("x").where(field_path="x", op_string="<=", value=3).count()
+ assert_trace_for_async_generator(async_aggregation_query.stream)
+
+
+def test_firestore_async_aggregation_query_trace_node_datastore_params(
+ loop, exercise_async_aggregation_query, instance_info
+):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_aggregation_query())
+
+ _test()
+
+
+# ===== CollectionGroup =====
+
+
+@pytest.fixture()
+def patch_partition_queries(monkeypatch, async_client, collection, sample_data):
+ """
+ Partitioning is not implemented in the Firestore emulator.
+
+ Ordinarily this method would return a coroutine that returns an async_generator of Cursor objects.
+ Each Cursor must point at a valid document path. To test this, we can patch the RPC to return 1 Cursor
+ which is pointed at any document available. The get_partitions will take that and make 2 QueryPartition
+ objects out of it, which should be enough to ensure we can exercise the generator's tracing.
+ """
+ from google.cloud.firestore_v1.types.document import Value
+ from google.cloud.firestore_v1.types.query import Cursor
+
+ subcollection = collection.document("subcollection").collection("subcollection1")
+ documents = [d for d in subcollection.list_documents()]
+
+ async def mock_partition_query(*args, **kwargs):
+ async def _mock_partition_query():
+ yield Cursor(before=False, values=[Value(reference_value=documents[0].path)])
+
+ return _mock_partition_query()
+
+ monkeypatch.setattr(async_client._firestore_api, "partition_query", mock_partition_query)
+ yield
+
+
+@pytest.fixture()
+def exercise_async_collection_group(async_client, async_collection):
+ async def _exercise_async_collection_group():
+ async_collection_group = async_client.collection_group(async_collection.id)
+ assert len(await async_collection_group.get())
+ assert len([d async for d in async_collection_group.stream()])
+
+ partitions = [p async for p in async_collection_group.get_partitions(1)]
+ assert len(partitions) == 2
+ documents = []
+ while partitions:
+ documents.extend(await partitions.pop().query().get())
+ assert len(documents) == 6
+
+ return _exercise_async_collection_group
+
+
+def test_firestore_async_collection_group(
+ loop, exercise_async_collection_group, async_collection, patch_partition_queries, instance_info
+):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{async_collection.id}/get", 3),
+ (f"Datastore/statement/Firestore/{async_collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/get_partitions", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/get", 3),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/operation/Firestore/get_partitions", 1),
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 5),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_collection_group",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_collection_group")
+ def _test():
+ loop.run_until_complete(exercise_async_collection_group())
+
+ _test()
+
+
+@background_task()
+def test_firestore_async_collection_group_generators(
+ async_client, async_collection, assert_trace_for_async_generator, patch_partition_queries
+):
+ async_collection_group = async_client.collection_group(async_collection.id)
+ assert_trace_for_async_generator(async_collection_group.get_partitions, 1)
+
+
+def test_firestore_async_collection_group_trace_node_datastore_params(
+ loop, exercise_async_collection_group, instance_info, patch_partition_queries
+):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_collection_group())
+
+ _test()
\ No newline at end of file
diff --git a/tests/datastore_firestore/test_async_transaction.py b/tests/datastore_firestore/test_async_transaction.py
new file mode 100644
index 0000000000..37a5cc76bd
--- /dev/null
+++ b/tests/datastore_firestore/test_async_transaction.py
@@ -0,0 +1,169 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture(autouse=True)
+def sample_data(collection):
+ for x in range(1, 4):
+ collection.add({"x": x}, f"doc{x}")
+
+
+@pytest.fixture()
+def exercise_async_transaction_commit(async_client, async_collection):
+ async def _exercise_async_transaction_commit():
+ from google.cloud.firestore import async_transactional
+
+ @async_transactional
+ async def _exercise(async_transaction):
+ # get a DocumentReference
+ with pytest.raises(
+ TypeError
+ ): # get is currently broken. It attempts to await an async_generator instead of consuming it.
+ [_ async for _ in async_transaction.get(async_collection.document("doc1"))]
+
+ # get a Query
+ with pytest.raises(
+ TypeError
+ ): # get is currently broken. It attempts to await an async_generator instead of consuming it.
+ async_query = async_collection.select("x").where(field_path="x", op_string=">", value=2)
+ assert len([_ async for _ in async_transaction.get(async_query)]) == 1
+
+ # get_all on a list of DocumentReferences
+ with pytest.raises(
+ TypeError
+ ): # get_all is currently broken. It attempts to await an async_generator instead of consuming it.
+ all_docs = async_transaction.get_all([async_collection.document(f"doc{x}") for x in range(1, 4)])
+ assert len([_ async for _ in all_docs]) == 3
+
+ # set and delete methods
+ async_transaction.set(async_collection.document("doc2"), {"x": 0})
+ async_transaction.delete(async_collection.document("doc3"))
+
+ await _exercise(async_client.transaction())
+ assert len([_ async for _ in async_collection.list_documents()]) == 2
+
+ return _exercise_async_transaction_commit
+
+
+@pytest.fixture()
+def exercise_async_transaction_rollback(async_client, async_collection):
+ async def _exercise_async_transaction_rollback():
+ from google.cloud.firestore import async_transactional
+
+ @async_transactional
+ async def _exercise(async_transaction):
+ # set and delete methods
+ async_transaction.set(async_collection.document("doc2"), {"x": 99})
+ async_transaction.delete(async_collection.document("doc1"))
+ raise RuntimeError()
+
+ with pytest.raises(RuntimeError):
+ await _exercise(async_client.transaction())
+ assert len([_ async for _ in async_collection.list_documents()]) == 3
+
+ return _exercise_async_transaction_rollback
+
+
+def test_firestore_async_transaction_commit(loop, exercise_async_transaction_commit, async_collection, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/commit", 1),
+ # ("Datastore/operation/Firestore/get_all", 2),
+ # (f"Datastore/statement/Firestore/{async_collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/list_documents", 1),
+ ]
+
+ _test_rollup_metrics = [
+ # ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/operation/Firestore/list_documents", 1),
+ ("Datastore/all", 2), # Should be 5 if not for broken APIs
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_transaction",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_transaction")
+ def _test():
+ loop.run_until_complete(exercise_async_transaction_commit())
+
+ _test()
+
+
+def test_firestore_async_transaction_rollback(
+ loop, exercise_async_transaction_rollback, async_collection, instance_info
+):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/rollback", 1),
+ (f"Datastore/statement/Firestore/{async_collection.id}/list_documents", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/list_documents", 1),
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_async_transaction",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_async_transaction")
+ def _test():
+ loop.run_until_complete(exercise_async_transaction_rollback())
+
+ _test()
+
+
+def test_firestore_async_transaction_commit_trace_node_datastore_params(
+ loop, exercise_async_transaction_commit, instance_info
+):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_transaction_commit())
+
+ _test()
+
+
+def test_firestore_async_transaction_rollback_trace_node_datastore_params(
+ loop, exercise_async_transaction_rollback, instance_info
+):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ loop.run_until_complete(exercise_async_transaction_rollback())
+
+ _test()
diff --git a/tests/datastore_firestore/test_batching.py b/tests/datastore_firestore/test_batching.py
new file mode 100644
index 0000000000..67b1b28a0b
--- /dev/null
+++ b/tests/datastore_firestore/test_batching.py
@@ -0,0 +1,127 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+# ===== WriteBatch =====
+
+
+@pytest.fixture()
+def exercise_write_batch(client, collection):
+ def _exercise_write_batch():
+ docs = [collection.document(str(x)) for x in range(1, 4)]
+ batch = client.batch()
+ for doc in docs:
+ batch.set(doc, {})
+
+ batch.commit()
+
+ return _exercise_write_batch
+
+
+def test_firestore_write_batch(exercise_write_batch, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/commit", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/all", 1),
+ ("Datastore/allOther", 1),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 1),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_write_batch",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_write_batch")
+ def _test():
+ exercise_write_batch()
+
+ _test()
+
+
+def test_firestore_write_batch_trace_node_datastore_params(exercise_write_batch, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_write_batch()
+
+ _test()
+
+
+# ===== BulkWriteBatch =====
+
+
+@pytest.fixture()
+def exercise_bulk_write_batch(client, collection):
+ def _exercise_bulk_write_batch():
+ from google.cloud.firestore_v1.bulk_batch import BulkWriteBatch
+
+ docs = [collection.document(str(x)) for x in range(1, 4)]
+ batch = BulkWriteBatch(client)
+ for doc in docs:
+ batch.set(doc, {})
+
+ batch.commit()
+
+ return _exercise_bulk_write_batch
+
+
+def test_firestore_bulk_write_batch(exercise_bulk_write_batch, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/commit", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/all", 1),
+ ("Datastore/allOther", 1),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 1),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_bulk_write_batch",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_bulk_write_batch")
+ def _test():
+ exercise_bulk_write_batch()
+
+ _test()
+
+
+def test_firestore_bulk_write_batch_trace_node_datastore_params(exercise_bulk_write_batch, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_bulk_write_batch()
+
+ _test()
diff --git a/tests/datastore_firestore/test_client.py b/tests/datastore_firestore/test_client.py
new file mode 100644
index 0000000000..3e00d4d335
--- /dev/null
+++ b/tests/datastore_firestore/test_client.py
@@ -0,0 +1,83 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture()
+def sample_data(collection):
+ doc = collection.document("document")
+ doc.set({"x": 1})
+ return doc
+
+
+@pytest.fixture()
+def exercise_client(client, sample_data):
+ def _exercise_client():
+ assert len([_ for _ in client.collections()])
+ doc = [_ for _ in client.get_all([sample_data])][0]
+ assert doc.to_dict()["x"] == 1
+
+ return _exercise_client
+
+
+def test_firestore_client(exercise_client, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/collections", 1),
+ ("Datastore/operation/Firestore/get_all", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_client",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_client")
+ def _test():
+ exercise_client()
+
+ _test()
+
+
+@background_task()
+def test_firestore_client_generators(client, sample_data, assert_trace_for_generator):
+ assert_trace_for_generator(client.collections)
+ assert_trace_for_generator(client.get_all, [sample_data])
+
+
+def test_firestore_client_trace_node_datastore_params(exercise_client, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_client()
+
+ _test()
\ No newline at end of file
diff --git a/tests/datastore_firestore/test_collections.py b/tests/datastore_firestore/test_collections.py
new file mode 100644
index 0000000000..8597cdd5e9
--- /dev/null
+++ b/tests/datastore_firestore/test_collections.py
@@ -0,0 +1,94 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture()
+def exercise_collections(collection):
+ def _exercise_collections():
+ collection.document("DoesNotExist")
+ collection.add({"capital": "Rome", "currency": "Euro", "language": "Italian"}, "Italy")
+ collection.add({"capital": "Mexico City", "currency": "Peso", "language": "Spanish"}, "Mexico")
+
+ documents_get = collection.get()
+ assert len(documents_get) == 2
+ documents_stream = [_ for _ in collection.stream()]
+ assert len(documents_stream) == 2
+ documents_list = [_ for _ in collection.list_documents()]
+ assert len(documents_list) == 2
+
+ return _exercise_collections
+
+
+def test_firestore_collections(exercise_collections, collection, instance_info):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/get", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/list_documents", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/add", 2),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/add", 2),
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/operation/Firestore/list_documents", 1),
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 5),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_collections",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_collections")
+ def _test():
+ exercise_collections()
+
+ _test()
+
+
+@background_task()
+def test_firestore_collections_generators(collection, assert_trace_for_generator):
+ collection.add({})
+ collection.add({})
+ assert len([_ for _ in collection.list_documents()]) == 2
+
+ assert_trace_for_generator(collection.stream)
+ assert_trace_for_generator(collection.list_documents)
+
+
+def test_firestore_collections_trace_node_datastore_params(exercise_collections, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_collections()
+
+ _test()
diff --git a/tests/datastore_firestore/test_documents.py b/tests/datastore_firestore/test_documents.py
new file mode 100644
index 0000000000..11a737cbc8
--- /dev/null
+++ b/tests/datastore_firestore/test_documents.py
@@ -0,0 +1,104 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture()
+def exercise_documents(collection):
+ def _exercise_documents():
+ italy_doc = collection.document("Italy")
+ italy_doc.set({"capital": "Rome", "currency": "Euro", "language": "Italian"})
+ italy_doc.get()
+ italian_cities = italy_doc.collection("cities")
+ italian_cities.add({"capital": "Rome"})
+ retrieved_coll = [_ for _ in italy_doc.collections()]
+ assert len(retrieved_coll) == 1
+
+ usa_doc = collection.document("USA")
+ usa_doc.create({"capital": "Washington D.C.", "currency": "Dollar", "language": "English"})
+ usa_doc.update({"president": "Joe Biden"})
+
+ collection.document("USA").delete()
+
+ return _exercise_documents
+
+
+def test_firestore_documents(exercise_documents, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/statement/Firestore/Italy/set", 1),
+ ("Datastore/statement/Firestore/Italy/get", 1),
+ ("Datastore/statement/Firestore/Italy/collections", 1),
+ ("Datastore/statement/Firestore/cities/add", 1),
+ ("Datastore/statement/Firestore/USA/create", 1),
+ ("Datastore/statement/Firestore/USA/update", 1),
+ ("Datastore/statement/Firestore/USA/delete", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/set", 1),
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/add", 1),
+ ("Datastore/operation/Firestore/collections", 1),
+ ("Datastore/operation/Firestore/create", 1),
+ ("Datastore/operation/Firestore/update", 1),
+ ("Datastore/operation/Firestore/delete", 1),
+ ("Datastore/all", 7),
+ ("Datastore/allOther", 7),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 7),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_documents",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_documents")
+ def _test():
+ exercise_documents()
+
+ _test()
+
+
+@background_task()
+def test_firestore_documents_generators(collection, assert_trace_for_generator):
+ subcollection_doc = collection.document("SubCollections")
+ subcollection_doc.set({})
+ subcollection_doc.collection("collection1").add({})
+ subcollection_doc.collection("collection2").add({})
+ assert len([_ for _ in subcollection_doc.collections()]) == 2
+
+ assert_trace_for_generator(subcollection_doc.collections)
+
+
+def test_firestore_documents_trace_node_datastore_params(exercise_documents, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_documents()
+
+ _test()
diff --git a/tests/datastore_firestore/test_query.py b/tests/datastore_firestore/test_query.py
new file mode 100644
index 0000000000..6ec576e4a5
--- /dev/null
+++ b/tests/datastore_firestore/test_query.py
@@ -0,0 +1,236 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture(autouse=True)
+def sample_data(collection):
+ for x in range(1, 6):
+ collection.add({"x": x})
+
+ subcollection_doc = collection.document("subcollection")
+ subcollection_doc.set({})
+ subcollection_doc.collection("subcollection1").add({})
+
+
+# ===== Query =====
+
+
+@pytest.fixture()
+def exercise_query(collection):
+ def _exercise_query():
+ query = collection.select("x").limit(10).order_by("x").where(field_path="x", op_string="<=", value=3)
+ assert len(query.get()) == 3
+ assert len([_ for _ in query.stream()]) == 3
+
+ return _exercise_query
+
+
+def test_firestore_query(exercise_query, collection, instance_info):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/get", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_query",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_query")
+ def _test():
+ exercise_query()
+
+ _test()
+
+
+@background_task()
+def test_firestore_query_generators(collection, assert_trace_for_generator):
+ query = collection.select("x").where(field_path="x", op_string="<=", value=3)
+ assert_trace_for_generator(query.stream)
+
+
+def test_firestore_query_trace_node_datastore_params(exercise_query, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_query()
+
+ _test()
+
+
+# ===== AggregationQuery =====
+
+
+@pytest.fixture()
+def exercise_aggregation_query(collection):
+ def _exercise_aggregation_query():
+ aggregation_query = collection.select("x").where(field_path="x", op_string="<=", value=3).count()
+ assert aggregation_query.get()[0][0].value == 3
+ assert [_ for _ in aggregation_query.stream()][0][0].value == 3
+
+ return _exercise_aggregation_query
+
+
+def test_firestore_aggregation_query(exercise_aggregation_query, collection, instance_info):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/get", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/get", 1),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_aggregation_query",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_aggregation_query")
+ def _test():
+ exercise_aggregation_query()
+
+ _test()
+
+
+@background_task()
+def test_firestore_aggregation_query_generators(collection, assert_trace_for_generator):
+ aggregation_query = collection.select("x").where(field_path="x", op_string="<=", value=3).count()
+ assert_trace_for_generator(aggregation_query.stream)
+
+
+def test_firestore_aggregation_query_trace_node_datastore_params(exercise_aggregation_query, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_aggregation_query()
+
+ _test()
+
+
+# ===== CollectionGroup =====
+
+
+@pytest.fixture()
+def patch_partition_queries(monkeypatch, client, collection, sample_data):
+ """
+ Partitioning is not implemented in the Firestore emulator.
+
+ Ordinarily this method would return a generator of Cursor objects. Each Cursor must point at a valid document path.
+ To test this, we can patch the RPC to return 1 Cursor which is pointed at any document available.
+ The get_partitions will take that and make 2 QueryPartition objects out of it, which should be enough to ensure
+ we can exercise the generator's tracing.
+ """
+ from google.cloud.firestore_v1.types.document import Value
+ from google.cloud.firestore_v1.types.query import Cursor
+
+ subcollection = collection.document("subcollection").collection("subcollection1")
+ documents = [d for d in subcollection.list_documents()]
+
+ def mock_partition_query(*args, **kwargs):
+ yield Cursor(before=False, values=[Value(reference_value=documents[0].path)])
+
+ monkeypatch.setattr(client._firestore_api, "partition_query", mock_partition_query)
+ yield
+
+
+@pytest.fixture()
+def exercise_collection_group(client, collection, patch_partition_queries):
+ def _exercise_collection_group():
+ collection_group = client.collection_group(collection.id)
+ assert len(collection_group.get())
+ assert len([d for d in collection_group.stream()])
+
+ partitions = [p for p in collection_group.get_partitions(1)]
+ assert len(partitions) == 2
+ documents = []
+ while partitions:
+ documents.extend(partitions.pop().query().get())
+ assert len(documents) == 6
+
+ return _exercise_collection_group
+
+
+def test_firestore_collection_group(exercise_collection_group, client, collection, instance_info):
+ _test_scoped_metrics = [
+ (f"Datastore/statement/Firestore/{collection.id}/get", 3),
+ (f"Datastore/statement/Firestore/{collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/get_partitions", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/get", 3),
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/operation/Firestore/get_partitions", 1),
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 5),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_collection_group",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_collection_group")
+ def _test():
+ exercise_collection_group()
+
+ _test()
+
+
+@background_task()
+def test_firestore_collection_group_generators(client, collection, assert_trace_for_generator, patch_partition_queries):
+ collection_group = client.collection_group(collection.id)
+ assert_trace_for_generator(collection_group.get_partitions, 1)
+
+
+def test_firestore_collection_group_trace_node_datastore_params(exercise_collection_group, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_collection_group()
+
+ _test()
diff --git a/tests/datastore_firestore/test_transaction.py b/tests/datastore_firestore/test_transaction.py
new file mode 100644
index 0000000000..495c61e767
--- /dev/null
+++ b/tests/datastore_firestore/test_transaction.py
@@ -0,0 +1,153 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+from testing_support.validators.validate_database_duration import (
+ validate_database_duration,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@pytest.fixture(autouse=True)
+def sample_data(collection):
+ for x in range(1, 4):
+ collection.add({"x": x}, f"doc{x}")
+
+
+@pytest.fixture()
+def exercise_transaction_commit(client, collection):
+ def _exercise_transaction_commit():
+ from google.cloud.firestore_v1.transaction import transactional
+
+ @transactional
+ def _exercise(transaction):
+ # get a DocumentReference
+ [_ for _ in transaction.get(collection.document("doc1"))]
+
+ # get a Query
+ query = collection.select("x").where(field_path="x", op_string=">", value=2)
+ assert len([_ for _ in transaction.get(query)]) == 1
+
+ # get_all on a list of DocumentReferences
+ all_docs = transaction.get_all([collection.document(f"doc{x}") for x in range(1, 4)])
+ assert len([_ for _ in all_docs]) == 3
+
+ # set and delete methods
+ transaction.set(collection.document("doc2"), {"x": 0})
+ transaction.delete(collection.document("doc3"))
+
+ _exercise(client.transaction())
+ assert len([_ for _ in collection.list_documents()]) == 2
+
+ return _exercise_transaction_commit
+
+
+@pytest.fixture()
+def exercise_transaction_rollback(client, collection):
+ def _exercise_transaction_rollback():
+ from google.cloud.firestore_v1.transaction import transactional
+
+ @transactional
+ def _exercise(transaction):
+ # set and delete methods
+ transaction.set(collection.document("doc2"), {"x": 99})
+ transaction.delete(collection.document("doc1"))
+ raise RuntimeError()
+
+ with pytest.raises(RuntimeError):
+ _exercise(client.transaction())
+ assert len([_ for _ in collection.list_documents()]) == 3
+
+ return _exercise_transaction_rollback
+
+
+def test_firestore_transaction_commit(exercise_transaction_commit, collection, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/commit", 1),
+ ("Datastore/operation/Firestore/get_all", 2),
+ (f"Datastore/statement/Firestore/{collection.id}/stream", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/list_documents", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/stream", 1),
+ ("Datastore/operation/Firestore/list_documents", 1),
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 5),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_transaction",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_transaction")
+ def _test():
+ exercise_transaction_commit()
+
+ _test()
+
+
+def test_firestore_transaction_rollback(exercise_transaction_rollback, collection, instance_info):
+ _test_scoped_metrics = [
+ ("Datastore/operation/Firestore/rollback", 1),
+ (f"Datastore/statement/Firestore/{collection.id}/list_documents", 1),
+ ]
+
+ _test_rollup_metrics = [
+ ("Datastore/operation/Firestore/list_documents", 1),
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ (f"Datastore/instance/Firestore/{instance_info['host']}/{instance_info['port_path_or_id']}", 2),
+ ]
+
+ @validate_database_duration()
+ @validate_transaction_metrics(
+ "test_firestore_transaction",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+ )
+ @background_task(name="test_firestore_transaction")
+ def _test():
+ exercise_transaction_rollback()
+
+ _test()
+
+
+def test_firestore_transaction_commit_trace_node_datastore_params(exercise_transaction_commit, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_transaction_commit()
+
+ _test()
+
+
+def test_firestore_transaction_rollback_trace_node_datastore_params(exercise_transaction_rollback, instance_info):
+ @validate_tt_collector_json(datastore_params=instance_info)
+ @background_task()
+ def _test():
+ exercise_transaction_rollback()
+
+ _test()
diff --git a/tests/datastore_memcache/conftest.py b/tests/datastore_memcache/conftest.py
index 835e895bd8..e63f9c0274 100644
--- a/tests/datastore_memcache/conftest.py
+++ b/tests/datastore_memcache/conftest.py
@@ -14,32 +14,36 @@
import random
import string
-import pytest
-import memcache
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+import memcache
+import pytest
from testing_support.db_settings import memcached_settings
-
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_memcache)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_memcache)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
+
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def memcached_multi():
"""Generate keys that will go onto different servers"""
DB_SETTINGS = memcached_settings()
- db_servers = ['%s:%s' % (s['host'], s['port']) for s in DB_SETTINGS]
+ db_servers = [f"{s['host']}:{s['port']}" for s in DB_SETTINGS]
clients = [memcache.Client([s]) for s in db_servers]
client_all = memcache.Client(db_servers)
@@ -48,9 +52,8 @@ def memcached_multi():
for try_num in range(10 * num_servers):
multi_dict = {}
for i in range(num_servers):
- random_chars = (random.choice(string.ascii_uppercase)
- for _ in range(10))
- key_candidate = ''.join(random_chars)
+ random_chars = (random.choice(string.ascii_uppercase) for _ in range(10))
+ key_candidate = "".join(random_chars)
multi_dict[key_candidate] = key_candidate
client_all.set_multi(multi_dict)
diff --git a/tests/datastore_memcache/test_memcache.py b/tests/datastore_memcache/test_memcache.py
index a66c114eef..dba37d6071 100644
--- a/tests/datastore_memcache/test_memcache.py
+++ b/tests/datastore_memcache/test_memcache.py
@@ -23,7 +23,7 @@
from newrelic.common.object_wrapper import wrap_function_wrapper
DB_SETTINGS = memcached_settings()[0]
-MEMCACHED_ADDR = '%s:%s' % (DB_SETTINGS['host'], DB_SETTINGS['port'])
+MEMCACHED_ADDR = f"{DB_SETTINGS['host']}:{DB_SETTINGS['port']}"
# Settings
@@ -59,7 +59,7 @@
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Memcached/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Memcached/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 3)
@@ -72,7 +72,7 @@
# Query
def _exercise_db(client):
- key = DB_SETTINGS['namespace'] + 'key'
+ key = f"{DB_SETTINGS['namespace']}key"
client.set(key, 'value')
value = client.get(key)
client.delete(key)
diff --git a/tests/datastore_memcache/test_multiple_dbs.py b/tests/datastore_memcache/test_multiple_dbs.py
index dbc3ea2b3b..c2b656775a 100644
--- a/tests/datastore_memcache/test_multiple_dbs.py
+++ b/tests/datastore_memcache/test_multiple_dbs.py
@@ -65,10 +65,8 @@
host_2 = instance_hostname(memcached_2['host'])
port_2 = memcached_2['port']
- instance_metric_name_1 = 'Datastore/instance/Memcached/%s/%s' % (host_1,
- port_1)
- instance_metric_name_2 = 'Datastore/instance/Memcached/%s/%s' % (host_2,
- port_2)
+ instance_metric_name_1 = f'Datastore/instance/Memcached/{host_1}/{port_1}'
+ instance_metric_name_2 = f'Datastore/instance/Memcached/{host_2}/{port_2}'
_enable_rollup_metrics.extend([
(instance_metric_name_1, None),
@@ -89,7 +87,7 @@ def exercise_memcached(client, multi_dict):
@pytest.mark.skipif(len(DB_MULTIPLE_SETTINGS) < 2,
reason='Test environment not configured with multiple databases.')
@override_application_settings(_enable_instance_settings)
-@validate_transaction_metrics(transaction_metric_prefix+'_enabled',
+@validate_transaction_metrics(f"{transaction_metric_prefix}_enabled",
scoped_metrics=_enable_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@@ -98,7 +96,7 @@ def test_multiple_datastores_enabled(memcached_multi):
memcached1 = DB_MULTIPLE_SETTINGS[0]
memcached2 = DB_MULTIPLE_SETTINGS[1]
settings = [memcached1, memcached2]
- servers = ["%s:%s" % (x['host'], x['port']) for x in settings]
+ servers = [f"{x['host']}:{x['port']}" for x in settings]
client = memcache.Client(servers=servers)
@@ -107,7 +105,7 @@ def test_multiple_datastores_enabled(memcached_multi):
@pytest.mark.skipif(len(DB_MULTIPLE_SETTINGS) < 2,
reason='Test environment not configured with multiple databases.')
@override_application_settings(_disable_instance_settings)
-@validate_transaction_metrics(transaction_metric_prefix+'_disabled',
+@validate_transaction_metrics(f"{transaction_metric_prefix}_disabled",
scoped_metrics=_disable_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@@ -116,7 +114,7 @@ def test_multiple_datastores_disabled(memcached_multi):
memcached1 = DB_MULTIPLE_SETTINGS[0]
memcached2 = DB_MULTIPLE_SETTINGS[1]
settings = [memcached1, memcached2]
- servers = ["%s:%s" % (x['host'], x['port']) for x in settings]
+ servers = [f"{x['host']}:{x['port']}" for x in settings]
client = memcache.Client(servers=servers)
diff --git a/tests/datastore_memcache/test_span_event.py b/tests/datastore_memcache/test_span_event.py
index a8da4d0e56..cea8a06e14 100644
--- a/tests/datastore_memcache/test_span_event.py
+++ b/tests/datastore_memcache/test_span_event.py
@@ -25,7 +25,7 @@
from newrelic.api.background_task import background_task
DB_SETTINGS = memcached_settings()[0]
-MEMCACHED_ADDR = '%s:%s' % (DB_SETTINGS['host'], DB_SETTINGS['port'])
+MEMCACHED_ADDR = f"{DB_SETTINGS['host']}:{DB_SETTINGS['port']}"
# Settings
@@ -44,7 +44,7 @@
# Query
def _exercise_db(client):
- key = DB_SETTINGS['namespace'] + 'key'
+ key = f"{DB_SETTINGS['namespace']}key"
client.set(key, 'value')
value = client.get(key)
client.delete(key)
@@ -73,7 +73,7 @@ def test_span_events(instance_enabled):
settings = _enable_instance_settings
hostname = instance_hostname(DB_SETTINGS['host'])
exact_agents.update({
- 'peer.address': '%s:%s' % (hostname, DB_SETTINGS['port']),
+ 'peer.address': f"{hostname}:{DB_SETTINGS['port']}",
'peer.hostname': hostname,
})
else:
diff --git a/tests/datastore_mysql/conftest.py b/tests/datastore_mysql/conftest.py
index a2f74c398f..fa2b0df727 100644
--- a/tests/datastore_mysql/conftest.py
+++ b/tests/datastore_mysql/conftest.py
@@ -12,26 +12,31 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
import os
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+import pytest
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
- 'debug.log_explain_plan_queries': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_mysql)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_mysql)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
+
@pytest.fixture(scope="session")
def table_name():
- return str("datastore_mysql_%d" % os.getpid())
+ return str(f"datastore_mysql_{os.getpid()}")
diff --git a/tests/datastore_mysql/test_database.py b/tests/datastore_mysql/test_database.py
index 2fc8ca129b..d0cbfbdaaa 100644
--- a/tests/datastore_mysql/test_database.py
+++ b/tests/datastore_mysql/test_database.py
@@ -13,173 +13,214 @@
# limitations under the License.
import mysql.connector
-
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_database_trace_inputs import validate_database_trace_inputs
-
from testing_support.db_settings import mysql_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_database_trace_inputs import (
+ validate_database_trace_inputs,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
DB_SETTINGS = mysql_settings()
DB_SETTINGS = DB_SETTINGS[0]
DB_NAMESPACE = DB_SETTINGS["namespace"]
-DB_PROCEDURE = "hello_" + DB_NAMESPACE
+DB_PROCEDURE = f"hello_{DB_NAMESPACE}"
+
+mysql_version = get_package_version_tuple("mysql.connector")
-mysql_version = tuple(int(x) for x in mysql.connector.__version__.split(".")[:3])
if mysql_version >= (8, 0, 30):
- _connector_metric_name = 'Function/mysql.connector.pooling:connect'
+ _connector_metric_name = "Function/mysql.connector.pooling:connect"
else:
- _connector_metric_name = 'Function/mysql.connector:connect'
+ _connector_metric_name = "Function/mysql.connector:connect"
_test_execute_via_cursor_scoped_metrics = [
- (_connector_metric_name, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/select' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/insert' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/update' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/delete' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/statement/MySQL/%s/call' % DB_PROCEDURE, 1),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
+ (_connector_metric_name, 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/select", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/insert", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/update", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/delete", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ (f"Datastore/statement/MySQL/{DB_PROCEDURE}/call", 1),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+]
_test_execute_via_cursor_rollup_metrics = [
- ('Datastore/all', 13),
- ('Datastore/allOther', 13),
- ('Datastore/MySQL/all', 13),
- ('Datastore/MySQL/allOther', 13),
- ('Datastore/operation/MySQL/select', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/select' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/insert', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/insert' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/update', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/update' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/delete', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/delete' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/%s/call' % DB_PROCEDURE, 1),
- ('Datastore/operation/MySQL/call', 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
-
-@validate_transaction_metrics('test_database:test_execute_via_cursor',
- scoped_metrics=_test_execute_via_cursor_scoped_metrics,
- rollup_metrics=_test_execute_via_cursor_rollup_metrics,
- background_task=True)
+ ("Datastore/all", 13),
+ ("Datastore/allOther", 13),
+ ("Datastore/MySQL/all", 13),
+ ("Datastore/MySQL/allOther", 13),
+ ("Datastore/operation/MySQL/select", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/select", 1),
+ ("Datastore/operation/MySQL/insert", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/insert", 1),
+ ("Datastore/operation/MySQL/update", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/update", 1),
+ ("Datastore/operation/MySQL/delete", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/delete", 1),
+ (f"Datastore/statement/MySQL/{DB_PROCEDURE}/call", 1),
+ ("Datastore/operation/MySQL/call", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+ (f"Datastore/instance/MySQL/{instance_hostname(DB_SETTINGS['host'])}/{DB_SETTINGS['port']}", 12),
+]
+
+
+@validate_transaction_metrics(
+ "test_database:test_execute_via_cursor",
+ scoped_metrics=_test_execute_via_cursor_scoped_metrics,
+ rollup_metrics=_test_execute_via_cursor_rollup_metrics,
+ background_task=True,
+)
+@validate_transaction_metrics(
+ "test_database:test_execute_via_cursor",
+ scoped_metrics=_test_execute_via_cursor_scoped_metrics,
+ rollup_metrics=_test_execute_via_cursor_rollup_metrics,
+ background_task=True,
+)
@validate_database_trace_inputs(sql_parameters_type=dict)
@background_task()
def test_execute_via_cursor(table_name):
-
- connection = mysql.connector.connect(db=DB_SETTINGS['name'],
- user=DB_SETTINGS['user'], passwd=DB_SETTINGS['password'],
- host=DB_SETTINGS['host'], port=DB_SETTINGS['port'])
+ assert mysql_version is not None
+ connection = mysql.connector.connect(
+ db=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ passwd=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
cursor = connection.cursor()
- cursor.execute("""drop table if exists `%s`""" % table_name)
+ cursor.execute(f"""drop table if exists `{table_name}`""")
- cursor.execute("""create table %s """
- """(a integer, b real, c text)""" % table_name)
+ cursor.execute(f"""create table {table_name} (a integer, b real, c text)""")
- cursor.executemany("""insert into `%s` """ % table_name +
- """values (%(a)s, %(b)s, %(c)s)""", [dict(a=1, b=1.0, c='1.0'),
- dict(a=2, b=2.2, c='2.2'), dict(a=3, b=3.3, c='3.3')])
+ cursor.executemany(
+ f"insert into `{table_name}` values (%(a)s, %(b)s, %(c)s)",
+ [{"a": 1, "b": 1.0, "c": "1.0"}, {"a": 2, "b": 2.2, "c": "2.2"}, {"a": 3, "b": 3.3, "c": "3.3"}],
+ )
- cursor.execute("""select * from %s""" % table_name)
+ cursor.execute(f"""select * from {table_name}""")
- for row in cursor: pass
+ for row in cursor:
+ pass
- cursor.execute("""update `%s` """ % table_name +
- """set a=%(a)s, b=%(b)s, c=%(c)s where a=%(old_a)s""",
- dict(a=4, b=4.0, c='4.0', old_a=1))
+ cursor.execute(
+ f"update `{table_name}` set a=%(a)s, b=%(b)s, c=%(c)s where a=%(old_a)s",
+ {"a": 4, "b": 4.0, "c": "4.0", "old_a": 1},
+ )
- cursor.execute("""delete from `%s` where a=2""" % table_name)
+ cursor.execute(f"""delete from `{table_name}` where a=2""")
- cursor.execute("""drop procedure if exists %s""" % DB_PROCEDURE)
- cursor.execute("""CREATE PROCEDURE %s()
+ cursor.execute(f"""drop procedure if exists {DB_PROCEDURE}""")
+ cursor.execute(
+ f"""CREATE PROCEDURE {DB_PROCEDURE}()
BEGIN
SELECT 'Hello World!';
- END""" % DB_PROCEDURE)
+ END"""
+ )
- cursor.callproc("%s" % DB_PROCEDURE)
+ cursor.callproc(f"{DB_PROCEDURE}")
connection.commit()
connection.rollback()
connection.commit()
+
_test_connect_using_alias_scoped_metrics = [
- (_connector_metric_name, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/select' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/insert' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/update' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/delete' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/statement/MySQL/%s/call' % DB_PROCEDURE, 1),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
+ (_connector_metric_name, 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/select", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/insert", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/update", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/delete", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ (f"Datastore/statement/MySQL/{DB_PROCEDURE}/call", 1),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+]
_test_connect_using_alias_rollup_metrics = [
- ('Datastore/all', 13),
- ('Datastore/allOther', 13),
- ('Datastore/MySQL/all', 13),
- ('Datastore/MySQL/allOther', 13),
- ('Datastore/operation/MySQL/select', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/select' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/insert', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/insert' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/update', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/update' % DB_NAMESPACE, 1),
- ('Datastore/operation/MySQL/delete', 1),
- ('Datastore/statement/MySQL/datastore_mysql_%s/delete' % DB_NAMESPACE, 1),
- ('Datastore/statement/MySQL/%s/call' % DB_PROCEDURE, 1),
- ('Datastore/operation/MySQL/call', 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
-
-@validate_transaction_metrics('test_database:test_connect_using_alias',
- scoped_metrics=_test_connect_using_alias_scoped_metrics,
- rollup_metrics=_test_connect_using_alias_rollup_metrics,
- background_task=True)
+ ("Datastore/all", 13),
+ ("Datastore/allOther", 13),
+ ("Datastore/MySQL/all", 13),
+ ("Datastore/MySQL/allOther", 13),
+ ("Datastore/operation/MySQL/select", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/select", 1),
+ ("Datastore/operation/MySQL/insert", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/insert", 1),
+ ("Datastore/operation/MySQL/update", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/update", 1),
+ ("Datastore/operation/MySQL/delete", 1),
+ (f"Datastore/statement/MySQL/datastore_mysql_{DB_NAMESPACE}/delete", 1),
+ (f"Datastore/statement/MySQL/{DB_PROCEDURE}/call", 1),
+ ("Datastore/operation/MySQL/call", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+ (f"Datastore/instance/MySQL/{instance_hostname(DB_SETTINGS['host'])}/{DB_SETTINGS['port']}", 12),
+]
+
+
+@validate_transaction_metrics(
+ "test_database:test_connect_using_alias",
+ scoped_metrics=_test_connect_using_alias_scoped_metrics,
+ rollup_metrics=_test_connect_using_alias_rollup_metrics,
+ background_task=True,
+)
@validate_database_trace_inputs(sql_parameters_type=dict)
@background_task()
def test_connect_using_alias(table_name):
-
- connection = mysql.connector.connect(db=DB_SETTINGS['name'],
- user=DB_SETTINGS['user'], passwd=DB_SETTINGS['password'],
- host=DB_SETTINGS['host'], port=DB_SETTINGS['port'])
+ assert mysql_version is not None
+ connection = mysql.connector.connect(
+ db=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ passwd=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
cursor = connection.cursor()
- cursor.execute("""drop table if exists `%s`""" % table_name)
+ cursor.execute(f"""drop table if exists `{table_name}`""")
- cursor.execute("""create table %s """
- """(a integer, b real, c text)""" % table_name)
+ cursor.execute(f"""create table {table_name} (a integer, b real, c text)""")
- cursor.executemany("""insert into `%s` """ % table_name +
- """values (%(a)s, %(b)s, %(c)s)""", [dict(a=1, b=1.0, c='1.0'),
- dict(a=2, b=2.2, c='2.2'), dict(a=3, b=3.3, c='3.3')])
+ cursor.executemany(
+ f"insert into `{table_name}` values (%(a)s, %(b)s, %(c)s)",
+ [{"a": 1, "b": 1.0, "c": "1.0"}, {"a": 2, "b": 2.2, "c": "2.2"}, {"a": 3, "b": 3.3, "c": "3.3"}],
+ )
- cursor.execute("""select * from %s""" % table_name)
+ cursor.execute(f"""select * from {table_name}""")
- for row in cursor: pass
+ for row in cursor:
+ pass
- cursor.execute("""update `%s` """ % table_name +
- """set a=%(a)s, b=%(b)s, c=%(c)s where a=%(old_a)s""",
- dict(a=4, b=4.0, c='4.0', old_a=1))
+ cursor.execute(
+ f"update `{table_name}` set a=%(a)s, b=%(b)s, c=%(c)s where a=%(old_a)s",
+ {"a": 4, "b": 4.0, "c": "4.0", "old_a": 1},
+ )
- cursor.execute("""delete from `%s` where a=2""" % table_name)
+ cursor.execute(f"""delete from `{table_name}` where a=2""")
- cursor.execute("""drop procedure if exists %s""" % DB_PROCEDURE)
- cursor.execute("""CREATE PROCEDURE %s()
+ cursor.execute(f"""drop procedure if exists {DB_PROCEDURE}""")
+ cursor.execute(
+ f"""CREATE PROCEDURE {DB_PROCEDURE}()
BEGIN
SELECT 'Hello World!';
- END""" % DB_PROCEDURE)
+ END"""
+ )
- cursor.callproc("%s" % DB_PROCEDURE)
+ cursor.callproc(f"{DB_PROCEDURE}")
connection.commit()
connection.rollback()
diff --git a/tests/datastore_postgresql/conftest.py b/tests/datastore_postgresql/conftest.py
index 624fb47262..545de0f7d0 100644
--- a/tests/datastore_postgresql/conftest.py
+++ b/tests/datastore_postgresql/conftest.py
@@ -12,21 +12,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
- 'debug.log_explain_plan_queries': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_postgresql)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_postgresql)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_postgresql/test_database.py b/tests/datastore_postgresql/test_database.py
index 2ea930b051..c7c9ba6dc3 100644
--- a/tests/datastore_postgresql/test_database.py
+++ b/tests/datastore_postgresql/test_database.py
@@ -13,15 +13,14 @@
# limitations under the License.
import postgresql.driver.dbapi20
-
-
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-
+from testing_support.db_settings import postgresql_settings
+from testing_support.util import instance_hostname
from testing_support.validators.validate_database_trace_inputs import (
validate_database_trace_inputs,
)
-
-from testing_support.db_settings import postgresql_settings
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
@@ -31,31 +30,32 @@
("Function/postgresql.driver.dbapi20:connect", 1),
("Function/postgresql.driver.dbapi20:Connection.__enter__", 1),
("Function/postgresql.driver.dbapi20:Connection.__exit__", 1),
- ("Datastore/statement/Postgres/%s/select" % DB_SETTINGS["table_name"], 1),
- ("Datastore/statement/Postgres/%s/insert" % DB_SETTINGS["table_name"], 1),
- ("Datastore/statement/Postgres/%s/update" % DB_SETTINGS["table_name"], 1),
- ("Datastore/statement/Postgres/%s/delete" % DB_SETTINGS["table_name"], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
("Datastore/statement/Postgres/now/call", 1),
("Datastore/statement/Postgres/pg_sleep/call", 1),
("Datastore/operation/Postgres/drop", 1),
("Datastore/operation/Postgres/create", 1),
("Datastore/operation/Postgres/commit", 3),
("Datastore/operation/Postgres/rollback", 1),
+ ("Datastore/operation/Postgres/other", 1),
]
_test_execute_via_cursor_rollup_metrics = [
- ("Datastore/all", 13),
- ("Datastore/allOther", 13),
- ("Datastore/Postgres/all", 13),
- ("Datastore/Postgres/allOther", 13),
+ ("Datastore/all", 14),
+ ("Datastore/allOther", 14),
+ ("Datastore/Postgres/all", 14),
+ ("Datastore/Postgres/allOther", 14),
("Datastore/operation/Postgres/select", 1),
- ("Datastore/statement/Postgres/%s/select" % DB_SETTINGS["table_name"], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
("Datastore/operation/Postgres/insert", 1),
- ("Datastore/statement/Postgres/%s/insert" % DB_SETTINGS["table_name"], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
("Datastore/operation/Postgres/update", 1),
- ("Datastore/statement/Postgres/%s/update" % DB_SETTINGS["table_name"], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
("Datastore/operation/Postgres/delete", 1),
- ("Datastore/statement/Postgres/%s/delete" % DB_SETTINGS["table_name"], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
("Datastore/operation/Postgres/drop", 1),
("Datastore/operation/Postgres/create", 1),
("Datastore/statement/Postgres/now/call", 1),
@@ -63,6 +63,11 @@
("Datastore/operation/Postgres/call", 2),
("Datastore/operation/Postgres/commit", 3),
("Datastore/operation/Postgres/rollback", 1),
+ ("Datastore/operation/Postgres/other", 1),
+ (f"Datastore/instance/Postgres/{instance_hostname(DB_SETTINGS['host'])}/{DB_SETTINGS['port']}", 13),
+ ("Function/postgresql.driver.dbapi20:connect", 1),
+ ("Function/postgresql.driver.dbapi20:Connection.__enter__", 1),
+ ("Function/postgresql.driver.dbapi20:Connection.__exit__", 1),
]
@@ -82,34 +87,29 @@ def test_execute_via_cursor():
host=DB_SETTINGS["host"],
port=DB_SETTINGS["port"],
) as connection:
-
cursor = connection.cursor()
- cursor.execute("""drop table if exists %s""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""drop table if exists {DB_SETTINGS['table_name']}""")
- cursor.execute(
- """create table %s """ % DB_SETTINGS["table_name"]
- + """(a integer, b real, c text)"""
- )
+ cursor.execute(f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)")
cursor.executemany(
- """insert into %s """ % DB_SETTINGS["table_name"]
- + """values (%s, %s, %s)""",
+ f"insert into {DB_SETTINGS['table_name']} values (%s, %s, %s)",
[(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")],
)
- cursor.execute("""select * from %s""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""select * from {DB_SETTINGS['table_name']}""")
- for row in cursor:
- pass
+ cursor.execute(
+ f"with temporaryTable (averageValue) as (select avg(b) from {DB_SETTINGS['table_name']}) select * from {DB_SETTINGS['table_name']},temporaryTable where {DB_SETTINGS['table_name']}.b > temporaryTable.averageValue"
+ )
cursor.execute(
- """update %s """ % DB_SETTINGS["table_name"]
- + """set a=%s, b=%s, c=%s where a=%s""",
+ f"update {DB_SETTINGS['table_name']} set a=%s, b=%s, c=%s where a=%s",
(4, 4.0, "4.0", 1),
)
- cursor.execute("""delete from %s where a=2""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""delete from {DB_SETTINGS['table_name']} where a=2""")
connection.commit()
@@ -152,7 +152,6 @@ def test_rollback_on_exception():
host=DB_SETTINGS["host"],
port=DB_SETTINGS["port"],
):
-
raise RuntimeError("error")
except RuntimeError:
diff --git a/tests/datastore_psycopg/conftest.py b/tests/datastore_psycopg/conftest.py
new file mode 100644
index 0000000000..dd6a174d9e
--- /dev/null
+++ b/tests/datastore_psycopg/conftest.py
@@ -0,0 +1,117 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from testing_support.db_settings import postgresql_settings
+from testing_support.fixture.event_loop import event_loop as loop # noqa: F401
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
+
+_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
+}
+
+collector_agent_registration = collector_agent_registration_fixture(
+ app_name="Python Agent Test (datastore_psycopg)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
+
+
+DB_MULTIPLE_SETTINGS = postgresql_settings()
+DB_SETTINGS = DB_MULTIPLE_SETTINGS[0]
+
+
+@pytest.fixture(scope="session", params=["sync", "async"])
+def is_async(request):
+ return request.param == "async"
+
+
+@pytest.fixture(scope="function")
+def connection(loop, is_async):
+ import psycopg
+
+ if not is_async:
+ connection = psycopg.connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
+ else:
+ connection = loop.run_until_complete(
+ psycopg.AsyncConnection.connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
+ )
+
+ yield connection
+ loop.run_until_complete(maybe_await(connection.close()))
+
+
+@pytest.fixture(scope="function")
+def multiple_connections(loop, is_async):
+ import psycopg
+
+ if len(DB_MULTIPLE_SETTINGS) < 2:
+ pytest.skip(reason="Test environment not configured with multiple databases.")
+
+ connections = []
+ for DB_SETTINGS in DB_MULTIPLE_SETTINGS:
+ if not is_async:
+ connections.append(
+ psycopg.connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
+ )
+ else:
+ connections.append(
+ loop.run_until_complete(
+ psycopg.AsyncConnection.connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
+ )
+ )
+
+ yield connections
+ for connection in connections:
+ loop.run_until_complete(maybe_await(connection.close()))
+
+
+async def maybe_await(value):
+ if hasattr(value, "__await__"):
+ return await value
+
+ return value
diff --git a/tests/datastore_psycopg/test_as_string.py b/tests/datastore_psycopg/test_as_string.py
new file mode 100644
index 0000000000..675ce2c909
--- /dev/null
+++ b/tests/datastore_psycopg/test_as_string.py
@@ -0,0 +1,112 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+try:
+ from psycopg import sql
+except ImportError:
+ sql = None
+
+from newrelic.api.background_task import background_task
+
+
+@background_task()
+def test_as_string_1(connection):
+
+ # All of these are similar to those described in the doctests in
+ # psycopg/lib/sql.py
+
+ comp = sql.Composed([sql.SQL("insert into "), sql.Identifier("table")])
+ result = comp.as_string(connection)
+ assert result == 'insert into "table"'
+
+
+@background_task()
+def test_as_string_2(connection):
+ fields = sql.Identifier("foo") + sql.Identifier("bar") # a Composed
+ result = fields.join(", ").as_string(connection)
+ assert result == '"foo", "bar"'
+
+
+@background_task()
+def test_as_string_3(connection):
+ query = sql.SQL("select {0} from {1}").format(
+ sql.SQL(", ").join([sql.Identifier("foo"), sql.Identifier("bar")]), sql.Identifier("table")
+ )
+ result = query.as_string(connection)
+ assert result == 'select "foo", "bar" from "table"'
+
+
+@background_task()
+def test_as_string_4(connection):
+ result = (
+ sql.SQL("select * from {0} where {1} = %s")
+ .format(sql.Identifier("people"), sql.Identifier("id"))
+ .as_string(connection)
+ )
+ assert result == 'select * from "people" where "id" = %s'
+
+
+@background_task()
+def test_as_string_5(connection):
+ result = (
+ sql.SQL("select * from {tbl} where {pkey} = %s")
+ .format(tbl=sql.Identifier("people"), pkey=sql.Identifier("id"))
+ .as_string(connection)
+ )
+ assert result == 'select * from "people" where "id" = %s'
+
+
+@background_task()
+def test_as_string_6(connection):
+ snip = sql.SQL(", ").join(sql.Identifier(n) for n in ["foo", "bar", "baz"])
+ result = snip.as_string(connection)
+ assert result == '"foo", "bar", "baz"'
+
+
+@background_task()
+def test_as_string_7(connection):
+ t1 = sql.Identifier("foo")
+ t2 = sql.Identifier("ba'r")
+ t3 = sql.Identifier('ba"z')
+ result = sql.SQL(", ").join([t1, t2, t3]).as_string(connection)
+ assert result == '"foo", "ba\'r", "ba""z"'
+
+
+@background_task()
+def test_as_string_8(connection):
+ s1 = sql.Literal("foo")
+ s2 = sql.Literal("ba'r")
+ s3 = sql.Literal(42)
+ result = sql.SQL(", ").join([s1, s2, s3]).as_string(connection)
+ assert result == "'foo', 'ba''r', 42"
+
+
+@background_task()
+def test_as_string_9(connection):
+ names = ["foo", "bar", "baz"]
+ q1 = sql.SQL("insert into table ({0}) values ({1})").format(
+ sql.SQL(", ").join(map(sql.Identifier, names)), sql.SQL(", ").join(sql.Placeholder() * len(names))
+ )
+ result = q1.as_string(connection)
+ assert result == 'insert into table ("foo", "bar", "baz") values (%s, %s, %s)'
+
+
+@background_task()
+def test_as_string_10(connection):
+ names = ["foo", "bar", "baz"]
+ q2 = sql.SQL("insert into table ({0}) values ({1})").format(
+ sql.SQL(", ").join(map(sql.Identifier, names)), sql.SQL(", ").join(map(sql.Placeholder, names))
+ )
+ result = q2.as_string(connection)
+ assert result == 'insert into table ("foo", "bar", "baz") ' "values (%(foo)s, %(bar)s, %(baz)s)"
diff --git a/tests/datastore_psycopg/test_connection.py b/tests/datastore_psycopg/test_connection.py
new file mode 100644
index 0000000000..c554e30acf
--- /dev/null
+++ b/tests/datastore_psycopg/test_connection.py
@@ -0,0 +1,220 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import psycopg
+import pytest
+
+try:
+ from psycopg import sql
+except ImportError:
+ sql = None
+
+from conftest import DB_SETTINGS, maybe_await
+from testing_support.fixtures import override_application_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_database_trace_inputs import (
+ validate_database_trace_inputs,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+# Settings
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+}
+
+
+# Metrics
+_base_scoped_metrics = (
+ ("Datastore/operation/Postgres/commit", 2),
+ ("Datastore/operation/Postgres/create", 2),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/rollback", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['procedure_name']}/call", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 3),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+)
+
+_base_rollup_metrics = (
+ ("Datastore/all", 14),
+ ("Datastore/allOther", 14),
+ ("Datastore/Postgres/all", 14),
+ ("Datastore/Postgres/allOther", 14),
+ ("Datastore/operation/Postgres/call", 1),
+ ("Datastore/operation/Postgres/commit", 2),
+ ("Datastore/operation/Postgres/create", 2),
+ ("Datastore/operation/Postgres/delete", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/insert", 3),
+ ("Datastore/operation/Postgres/rollback", 1),
+ ("Datastore/operation/Postgres/select", 1),
+ ("Datastore/operation/Postgres/update", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['procedure_name']}/call", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 3),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+)
+
+_disable_scoped_metrics = list(_base_scoped_metrics)
+_disable_rollup_metrics = list(_base_rollup_metrics)
+
+_enable_scoped_metrics = list(_base_scoped_metrics)
+_enable_rollup_metrics = list(_base_rollup_metrics)
+
+_host = instance_hostname(DB_SETTINGS["host"])
+_port = DB_SETTINGS["port"]
+
+_instance_metric_name = f"Datastore/instance/Postgres/{_host}/{_port}"
+
+_enable_rollup_metrics.append((_instance_metric_name, 13))
+
+_disable_rollup_metrics.append((_instance_metric_name, None))
+
+
+# Query
+async def _execute(connection, row_type, wrapper):
+ sql = f"drop table if exists {DB_SETTINGS['table_name']}"
+ await maybe_await(connection.execute(wrapper(sql)))
+
+ sql = f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)"
+ await maybe_await(connection.execute(wrapper(sql)))
+
+ for params in [(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")]:
+ sql = f"insert into {DB_SETTINGS['table_name']} values (%s, %s, %s)"
+ await maybe_await(connection.execute(wrapper(sql), params))
+
+ sql = f"select * from {DB_SETTINGS['table_name']}"
+ cursor = await maybe_await(connection.execute(wrapper(sql)))
+
+ if hasattr(cursor, "__aiter__"):
+ async for row in cursor:
+ assert isinstance(row, row_type)
+ else:
+ for row in cursor:
+ assert isinstance(row, row_type)
+
+ # Reuse cursor to ensure it is also wrapped
+ sql = f"update {DB_SETTINGS['table_name']} set a=%s, b=%s, c=%s where a=%s"
+ params = (4, 4.0, "4.0", 1)
+ await maybe_await(cursor.execute(wrapper(sql), params))
+
+ sql = f"delete from {DB_SETTINGS['table_name']} where a=2"
+ await maybe_await(connection.execute(wrapper(sql)))
+
+ await maybe_await(connection.commit())
+
+ await maybe_await(
+ connection.execute(
+ f"create or replace procedure {DB_SETTINGS['procedure_name']}() \nlanguage plpgsql as $$ begin perform now(); end; $$"
+ )
+ )
+ await maybe_await(connection.execute(f"call {DB_SETTINGS['procedure_name']}()"))
+
+ await maybe_await(connection.rollback())
+ await maybe_await(connection.commit())
+
+
+async def _exercise_db(is_async, row_type=tuple, wrapper=str):
+ # Connect here instead of using the fixture to capture the FunctionTrace around connect
+ if not is_async:
+ connection = psycopg.connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
+ else:
+ connection = await psycopg.AsyncConnection.connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
+
+ try:
+ await _execute(connection, row_type, wrapper)
+ finally:
+ await maybe_await(connection.close())
+
+
+_test_matrix = [
+ "wrapper",
+ [
+ str,
+ sql.SQL,
+ lambda q: sql.Composed([sql.SQL(q)]),
+ ],
+]
+
+
+# Tests
+@pytest.mark.parametrize(*_test_matrix)
+@override_application_settings(_enable_instance_settings)
+def test_execute_via_connection_enable_instance(loop, is_async, wrapper):
+ if not is_async:
+ connect_metric = ("Function/psycopg:Connection.connect", 1)
+ else:
+ connect_metric = ("Function/psycopg:AsyncConnection.connect", 1)
+
+ _scoped_metrics = list(_enable_scoped_metrics)
+ _scoped_metrics.append(connect_metric)
+
+ @validate_transaction_metrics(
+ "test_execute_via_connection_enable_instance",
+ scoped_metrics=_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+ )
+ @validate_database_trace_inputs(sql_parameters_type=tuple)
+ @background_task(name="test_execute_via_connection_enable_instance")
+ def test():
+ loop.run_until_complete(_exercise_db(is_async, row_type=tuple, wrapper=wrapper))
+
+ test()
+
+
+@pytest.mark.parametrize(*_test_matrix)
+@override_application_settings(_disable_instance_settings)
+def test_execute_via_connection_disable_instance(loop, is_async, wrapper):
+ if not is_async:
+ connect_metric = ("Function/psycopg:Connection.connect", 1)
+ else:
+ connect_metric = ("Function/psycopg:AsyncConnection.connect", 1)
+
+ _scoped_metrics = list(_disable_scoped_metrics)
+ _scoped_metrics.append(connect_metric)
+
+ @validate_transaction_metrics(
+ "test_execute_via_connection_disable_instance",
+ scoped_metrics=_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+ )
+ @validate_database_trace_inputs(sql_parameters_type=tuple)
+ @background_task(name="test_execute_via_connection_disable_instance")
+ def test():
+ loop.run_until_complete(_exercise_db(is_async, row_type=tuple, wrapper=wrapper))
+
+ test()
diff --git a/tests/datastore_psycopg/test_cursor.py b/tests/datastore_psycopg/test_cursor.py
new file mode 100644
index 0000000000..3eff3ed7ab
--- /dev/null
+++ b/tests/datastore_psycopg/test_cursor.py
@@ -0,0 +1,240 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import psycopg
+import pytest
+
+try:
+ from psycopg import sql
+except ImportError:
+ sql = None
+
+from conftest import DB_SETTINGS, maybe_await
+from testing_support.fixtures import override_application_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_database_trace_inputs import (
+ validate_database_trace_inputs,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+# Settings
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+}
+
+
+# Metrics
+_base_scoped_metrics = (
+ ("Datastore/operation/Postgres/commit", 2),
+ ("Datastore/operation/Postgres/create", 2),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/rollback", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['procedure_name']}/call", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+)
+
+_base_rollup_metrics = (
+ ("Datastore/all", 11),
+ ("Datastore/allOther", 11),
+ ("Datastore/Postgres/all", 11),
+ ("Datastore/Postgres/allOther", 11),
+ ("Datastore/operation/Postgres/call", 1),
+ ("Datastore/operation/Postgres/commit", 2),
+ ("Datastore/operation/Postgres/create", 2),
+ ("Datastore/operation/Postgres/delete", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/insert", 1),
+ ("Datastore/operation/Postgres/rollback", 1),
+ ("Datastore/operation/Postgres/select", 1),
+ ("Datastore/operation/Postgres/update", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['procedure_name']}/call", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+)
+
+_disable_scoped_metrics = list(_base_scoped_metrics)
+_disable_rollup_metrics = list(_base_rollup_metrics)
+
+_enable_scoped_metrics = list(_base_scoped_metrics)
+_enable_rollup_metrics = list(_base_rollup_metrics)
+
+_host = instance_hostname(DB_SETTINGS["host"])
+_port = DB_SETTINGS["port"]
+
+_instance_metric_name = f"Datastore/instance/Postgres/{_host}/{_port}"
+
+_enable_rollup_metrics.append((_instance_metric_name, 11))
+
+_disable_rollup_metrics.append((_instance_metric_name, None))
+
+
+# Query
+async def _execute(connection, cursor, row_type, wrapper):
+ sql = f"drop table if exists {DB_SETTINGS['table_name']}"
+ await maybe_await(cursor.execute(wrapper(sql)))
+
+ sql = f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)"
+ await maybe_await(cursor.execute(wrapper(sql)))
+
+ sql = f"insert into {DB_SETTINGS['table_name']} values (%s, %s, %s) returning a, b, c"
+ params = [(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")]
+ await maybe_await(cursor.executemany(wrapper(sql), params, returning=True))
+
+ # Consume inserted records to check that returning param functions
+ records = []
+ while True:
+ records.append(cursor.fetchone())
+ if not cursor.nextset():
+ break
+ assert len(records) == len(params)
+
+ sql = f"select * from {DB_SETTINGS['table_name']}"
+ await maybe_await(cursor.execute(wrapper(sql)))
+
+ if hasattr(cursor, "__aiter__"):
+ async for row in cursor:
+ assert isinstance(row, row_type)
+ else:
+ # Iterate on sync cursor
+ for row in cursor:
+ assert isinstance(row, row_type)
+
+ sql = f"update {DB_SETTINGS['table_name']} set a=%s, b=%s, c=%s where a=%s"
+ params = (4, 4.0, "4.0", 1)
+ await maybe_await(cursor.execute(wrapper(sql), params))
+
+ sql = f"delete from {DB_SETTINGS['table_name']} where a=2"
+ await maybe_await(cursor.execute(wrapper(sql)))
+
+ await maybe_await(connection.commit())
+
+ await maybe_await(
+ cursor.execute(
+ f"create or replace procedure {DB_SETTINGS['procedure_name']}() \nlanguage plpgsql as $$ begin perform now(); end; $$"
+ )
+ )
+ await maybe_await(cursor.execute(f"call {DB_SETTINGS['procedure_name']}()"))
+
+ await maybe_await(connection.rollback())
+ await maybe_await(connection.commit())
+
+
+async def _exercise_db(connection, row_factory=None, use_cur_context=False, row_type=tuple, wrapper=str):
+ kwargs = {"row_factory": row_factory} if row_factory else {}
+
+ try:
+ cursor = connection.cursor(**kwargs)
+ if use_cur_context:
+ if hasattr(cursor, "__aenter__"):
+ async with cursor:
+ await _execute(connection, cursor, row_type, wrapper)
+ else:
+ with cursor:
+ await _execute(connection, cursor, row_type, wrapper)
+ else:
+ await _execute(connection, cursor, row_type, wrapper)
+ finally:
+ await maybe_await(connection.close())
+
+
+_test_matrix = [
+ "wrapper,use_cur_context",
+ [
+ (str, False),
+ (str, True),
+ (sql.SQL, False),
+ (sql.SQL, True),
+ (lambda q: sql.Composed([sql.SQL(q)]), False),
+ (lambda q: sql.Composed([sql.SQL(q)]), True),
+ ],
+]
+
+
+# Tests
+@pytest.mark.parametrize(*_test_matrix)
+@override_application_settings(_enable_instance_settings)
+@validate_transaction_metrics(
+ "test_cursor:test_execute_via_cursor_enable_instance",
+ scoped_metrics=_enable_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_execute_via_cursor_enable_instance(loop, connection, wrapper, use_cur_context):
+ loop.run_until_complete(_exercise_db(connection, use_cur_context=use_cur_context, row_type=tuple, wrapper=wrapper))
+
+
+@pytest.mark.parametrize(*_test_matrix)
+@override_application_settings(_disable_instance_settings)
+@validate_transaction_metrics(
+ "test_cursor:test_execute_via_cursor_disable_instance",
+ scoped_metrics=_disable_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_execute_via_cursor_disable_instance(loop, connection, wrapper, use_cur_context):
+ loop.run_until_complete(_exercise_db(connection, use_cur_context=use_cur_context, row_type=tuple, wrapper=wrapper))
+
+
+@pytest.mark.parametrize(*_test_matrix)
+@override_application_settings(_enable_instance_settings)
+@validate_transaction_metrics(
+ "test_cursor:test_execute_via_cursor_dict_enable_instance",
+ scoped_metrics=_enable_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_execute_via_cursor_dict_enable_instance(loop, connection, wrapper, use_cur_context):
+ dict_factory = psycopg.rows.dict_row
+ loop.run_until_complete(
+ _exercise_db(
+ connection, row_factory=dict_factory, use_cur_context=use_cur_context, row_type=dict, wrapper=wrapper
+ )
+ )
+
+
+@pytest.mark.parametrize(*_test_matrix)
+@override_application_settings(_disable_instance_settings)
+@validate_transaction_metrics(
+ "test_cursor:test_execute_via_cursor_dict_disable_instance",
+ scoped_metrics=_disable_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_execute_via_cursor_dict_disable_instance(loop, connection, wrapper, use_cur_context):
+ dict_factory = psycopg.rows.dict_row
+ loop.run_until_complete(
+ _exercise_db(
+ connection, row_factory=dict_factory, use_cur_context=use_cur_context, row_type=dict, wrapper=wrapper
+ )
+ )
diff --git a/tests/datastore_psycopg/test_database_instance_info.py b/tests/datastore_psycopg/test_database_instance_info.py
new file mode 100644
index 0000000000..1caa950471
--- /dev/null
+++ b/tests/datastore_psycopg/test_database_instance_info.py
@@ -0,0 +1,226 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from newrelic.hooks.database_psycopg import (
+ _add_defaults,
+ _parse_connect_params,
+ instance_info,
+)
+
+
+def test_kwargs():
+ connect_params = ((), {"dbname": "foo", "host": "1.2.3.4", "port": 1234})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("1.2.3.4", None, "1234", "foo")
+
+
+def test_arg_str():
+ connect_params = (("host=foobar port=9876",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("foobar", None, "9876", None)
+
+
+def test_bind_conninfo():
+ connect_params = ((), {"conninfo": "host=foobar port=9876"})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("foobar", None, "9876", None)
+
+
+def test_bind_conninfo_ignore_kwargs():
+ connect_params = ((), {"conninfo": "host=foobar", "port": 1234})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("foobar", None, None, None)
+
+
+def test_kwargs_str_for_port():
+ connect_params = ((), {"dbname": "foo", "host": "1.2.3.4", "port": "1234"})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("1.2.3.4", None, "1234", "foo")
+
+
+def test_arg_str_missing_port():
+ connect_params = (("host=foobar",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("foobar", None, None, None)
+
+
+def test_arg_str_multiple_host():
+ connect_params = (("host=foobar host=barbaz",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("barbaz", None, None, None)
+
+
+def test_arg_str_multiple_port():
+ connect_params = (("port=5555 port=7777",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == (None, None, "7777", None)
+
+
+def test_arg_str_missing_host():
+ connect_params = (("port=5555",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == (None, None, "5555", None)
+
+
+def test_arg_str_missing_host_and_port():
+ connect_params = (("nothing=here",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == (None, None, None, None)
+
+
+def test_malformed_arg_str():
+ connect_params = (("this_is_malformed",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("unknown", "unknown", "unknown", "unknown")
+
+
+def test_str_in_port_arg_str():
+ connect_params = (("port=foobar",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == (None, None, "foobar", None)
+
+
+def test_host_and_hostaddr_in_arg_str():
+ connect_params = (("host=foobar hostaddr=1.2.3.4",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("foobar", "1.2.3.4", None, None)
+
+
+def test_host_and_hostaddr_in_kwarg():
+ connect_params = ((), {"host": "foobar", "hostaddr": "1.2.3.4"})
+ output = _parse_connect_params(*connect_params)
+ assert output == ("foobar", "1.2.3.4", None, None)
+
+
+def test_only_hostaddr_in_kwarg():
+ connect_params = ((), {"hostaddr": "1.2.3.4"})
+ output = _parse_connect_params(*connect_params)
+ assert output == (None, "1.2.3.4", None, None)
+
+
+def test_only_hostaddr_in_arg_str():
+ connect_params = (("hostaddr=1.2.3.4",), {})
+ output = _parse_connect_params(*connect_params)
+ assert output == (None, "1.2.3.4", None, None)
+
+
+def test_env_var_default_host(monkeypatch):
+ monkeypatch.setenv("PGHOST", "envfoo")
+ output = _add_defaults(None, None, "1234", "foo")
+ assert output == ("envfoo", "1234", "foo")
+
+
+def test_env_var_default_hostaddr(monkeypatch):
+ monkeypatch.setenv("PGHOSTADDR", "1.2.3.4")
+ output = _add_defaults(None, None, "1234", "foo")
+ assert output == ("1.2.3.4", "1234", "foo")
+
+
+def test_env_var_default_database(monkeypatch):
+ monkeypatch.setenv("PGDATABASE", "dbenvfoo")
+ output = _add_defaults("foo", None, "1234", None)
+ assert output == ("foo", "1234", "dbenvfoo")
+
+
+def test_env_var_default_port(monkeypatch):
+ monkeypatch.setenv("PGPORT", "9999")
+ output = _add_defaults("foo", None, None, "bar")
+ assert output == ("foo", "9999", "bar")
+
+
+@pytest.mark.parametrize(
+ "connect_params,expected",
+ [
+ ((("postgresql://",), {}), ("localhost", "default", "default")),
+ ((("postgresql://localhost",), {}), ("localhost", "5432", "default")),
+ ((("postgresql://localhost:5433",), {}), ("localhost", "5433", "default")),
+ ((("postgresql://localhost/mydb",), {}), ("localhost", "5432", "mydb")),
+ ((("postgresql://user@localhost",), {}), ("localhost", "5432", "default")),
+ ((("postgresql://user:secret@localhost",), {}), ("localhost", "5432", "default")),
+ ((("postgresql://[2001:db8::1234]/database",), {}), ("2001:db8::1234", "5432", "database")),
+ ((("postgresql://[2001:db8::1234]:2222/database",), {}), ("2001:db8::1234", "2222", "database")),
+ (
+ (("postgresql:///dbname?host=/var/lib/postgresql",), {}),
+ ("localhost", "/var/lib/postgresql/.s.PGSQL.5432", "dbname"),
+ ),
+ (
+ (("postgresql://%2Fvar%2Flib%2Fpostgresql/dbname",), {}),
+ ("localhost", "/var/lib/postgresql/.s.PGSQL.5432", "dbname"),
+ ),
+ ((("postgresql://other@localhost/otherdb?c=10&a=myapp",), {}), ("localhost", "5432", "otherdb")),
+ ((("postgresql:///",), {}), ("localhost", "default", "default")),
+ ((("postgresql:///dbname?host=foo",), {}), ("foo", "5432", "dbname")),
+ ((("postgresql:///dbname?port=1234",), {}), ("localhost", "default", "dbname")),
+ ((("postgresql:///dbname?host=foo&port=1234",), {}), ("foo", "1234", "dbname")),
+ ((("postgres:///dbname?host=foo&port=1234",), {}), ("foo", "1234", "dbname")),
+ ((("postgres://localhost:5444/blah?host=::1",), {}), ("::1", "5444", "blah")),
+ ((("postgresql:///dbname?host=foo&port=1234&host=bar",), {}), ("bar", "1234", "dbname")),
+ ((("postgresql://%2Ftmp:1234",), {}), ("localhost", "/tmp/.s.PGSQL.1234", "default")),
+ ((("postgresql:///foo?dbname=bar",), {}), ("localhost", "default", "bar")),
+ ((("postgresql://example.com/foo?hostaddr=1.2.3.4&host=bar",), {}), ("1.2.3.4", "5432", "foo")),
+ ],
+)
+def test_uri(connect_params, expected):
+ output = instance_info(*connect_params)
+ assert output == expected
+
+
+@pytest.mark.parametrize(
+ "connect_params,expected",
+ [
+ ((("postgresql://user:password@/?dbname=bar",), {}), ("localhost", "default", "bar")),
+ ((("postgresql://user:pass@host/?dbname=bar",), {}), ("host", "5432", "bar")),
+ ((("postgresql://user:password@@/?dbname=bar",), {}), ("localhost", "default", "bar")),
+ ((("postgresql://@",), {}), ("localhost", "default", "default")),
+ ((("postgresql://@@localhost",), {}), ("localhost", "5432", "default")),
+ ],
+)
+def test_security_sensitive_uri(connect_params, expected):
+ output = instance_info(*connect_params)
+ assert output == expected
+
+
+def test_bad_uri():
+ connect_params = (("blah:///foo",), {})
+ output = instance_info(*connect_params)
+ assert output == ("unknown", "unknown", "unknown")
+
+
+_test_add_defaults = [
+ # TCP/IP
+ [("otherhost.com", None, "8888", "foobar"), ("otherhost.com", "8888", "foobar")],
+ [("otherhost.com", None, None, "foobar"), ("otherhost.com", "5432", "foobar")],
+ [("localhost", None, "8888", "foobar"), ("localhost", "8888", "foobar")],
+ [("localhost", None, None, "foobar"), ("localhost", "5432", "foobar")],
+ [("127.0.0.1", None, "8888", "foobar"), ("127.0.0.1", "8888", "foobar")],
+ [("127.0.0.1", None, None, "foobar"), ("127.0.0.1", "5432", "foobar")],
+ [("::1", None, "8888", None), ("::1", "8888", "default")],
+ [("::1", None, None, None), ("::1", "5432", "default")],
+ [("::1", None, None, ""), ("::1", "5432", "default")],
+ # Unix Domain Socket
+ [(None, None, None, None), ("localhost", "default", "default")],
+ [(None, None, "5432", None), ("localhost", "default", "default")],
+ [(None, None, "8888", None), ("localhost", "default", "default")],
+ [("/tmp", None, None, "cat"), ("localhost", "/tmp/.s.PGSQL.5432", "cat")],
+ [("/tmp", None, "5432", "dog"), ("localhost", "/tmp/.s.PGSQL.5432", "dog")],
+ [("/tmp", None, "8888", "db"), ("localhost", "/tmp/.s.PGSQL.8888", "db")],
+]
+
+
+@pytest.mark.parametrize("host_port,expected", _test_add_defaults)
+def test_add_defaults(host_port, expected):
+ actual = _add_defaults(*host_port)
+ assert actual == expected
diff --git a/tests/datastore_psycopg/test_explain_plans.py b/tests/datastore_psycopg/test_explain_plans.py
new file mode 100644
index 0000000000..e02a04693f
--- /dev/null
+++ b/tests/datastore_psycopg/test_explain_plans.py
@@ -0,0 +1,201 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import threading
+
+import psycopg
+import pytest
+from conftest import DB_SETTINGS, maybe_await
+from testing_support.fixtures import override_application_settings
+from testing_support.validators.validate_database_node import validate_database_node
+from testing_support.validators.validate_transaction_slow_sql_count import (
+ validate_transaction_slow_sql_count,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.core.database_utils import SQLConnections
+
+
+class CustomCursor(psycopg.Cursor):
+ event = threading.Event()
+
+ def execute(self, *args, **kwargs):
+ self.event.set()
+ return super().execute(*args, **kwargs)
+
+
+class CustomAsyncCursor(psycopg.AsyncCursor):
+ event = threading.Event()
+
+ async def execute(self, *args, **kwargs):
+ self.event.set()
+ return await super().execute(*args, **kwargs)
+
+
+class CustomConnection(psycopg.Connection):
+ event = threading.Event()
+
+ def cursor(self, *args, **kwargs):
+ self.event.set()
+ return super().cursor(*args, **kwargs)
+
+
+class CustomAsyncConnection(psycopg.AsyncConnection):
+ event = threading.Event()
+
+ def cursor(self, *args, **kwargs):
+ self.event.set()
+ return super().cursor(*args, **kwargs)
+
+
+def reset_events():
+ # Reset all event flags
+ CustomCursor.event.clear()
+ CustomAsyncCursor.event.clear()
+ CustomConnection.event.clear()
+ CustomAsyncConnection.event.clear()
+
+
+async def _exercise_db(connection, cursor_kwargs=None):
+ cursor_kwargs = cursor_kwargs or {}
+
+ try:
+ cursor = connection.cursor(**cursor_kwargs)
+
+ await maybe_await(cursor.execute("SELECT setting from pg_settings where name=%s", ("server_version",)))
+ finally:
+ await maybe_await(connection.close())
+
+
+# Tests
+
+
+def explain_plan_is_not_none(node):
+ with SQLConnections() as connections:
+ explain_plan = node.explain_plan(connections)
+
+ assert explain_plan is not None
+
+
+SCROLLABLE = (True, False)
+WITHHOLD = (True, False)
+
+
+@pytest.mark.parametrize("withhold", WITHHOLD)
+@pytest.mark.parametrize("scrollable", SCROLLABLE)
+@override_application_settings(
+ {
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.record_sql": "raw",
+ }
+)
+@validate_database_node(explain_plan_is_not_none)
+@validate_transaction_slow_sql_count(1)
+@background_task(name="test_explain_plan_unnamed_cursors")
+def test_explain_plan_unnamed_cursors(loop, connection, withhold, scrollable):
+ cursor_kwargs = {}
+
+ if withhold:
+ cursor_kwargs["withhold"] = withhold
+
+ if scrollable:
+ cursor_kwargs["scrollable"] = scrollable
+
+ loop.run_until_complete(_exercise_db(connection, cursor_kwargs=cursor_kwargs))
+
+
+@pytest.mark.parametrize("withhold", WITHHOLD)
+@pytest.mark.parametrize("scrollable", SCROLLABLE)
+@override_application_settings(
+ {
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.record_sql": "raw",
+ }
+)
+@validate_database_node(explain_plan_is_not_none)
+@validate_transaction_slow_sql_count(1)
+@background_task(name="test_explain_plan_named_cursors")
+def test_explain_plan_named_cursors(loop, connection, withhold, scrollable):
+ cursor_kwargs = {
+ "name": "test_explain_plan_named_cursors",
+ }
+
+ if withhold:
+ cursor_kwargs["withhold"] = withhold
+
+ if scrollable:
+ cursor_kwargs["scrollable"] = scrollable
+
+ loop.run_until_complete(_exercise_db(connection, cursor_kwargs=cursor_kwargs))
+
+
+# This test validates that any combination of sync or async, and default or custom connection and cursor classes will work with
+# the explain plan feature. The agent should always use psycopg.connect to open a new explain plan connection and only
+# use custom cursors from synchronous connections, as async cursors will not be compatible.
+@pytest.mark.parametrize(
+ "connection_cls,cursor_cls",
+ [
+ (psycopg.Connection, psycopg.Cursor),
+ (psycopg.Connection, CustomCursor),
+ (CustomConnection, psycopg.Cursor),
+ (CustomConnection, CustomCursor),
+ (psycopg.AsyncConnection, psycopg.AsyncCursor),
+ (psycopg.AsyncConnection, CustomAsyncCursor),
+ (CustomAsyncConnection, psycopg.AsyncCursor),
+ (CustomAsyncConnection, CustomAsyncCursor),
+ ],
+)
+@override_application_settings(
+ {
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.record_sql": "raw",
+ }
+)
+def test_explain_plan_on_custom_classes(loop, connection_cls, cursor_cls):
+ @validate_database_node(explain_plan_is_not_none)
+ @validate_transaction_slow_sql_count(1)
+ @background_task(name="test_explain_plan_on_custom_connect_class")
+ def test():
+ async def coro():
+ # Connect using custom Connection classes, so connect here without the fixture.
+ connection = await maybe_await(
+ connection_cls.connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ cursor_factory=cursor_cls,
+ )
+ )
+ await _exercise_db(connection)
+ reset_events()
+
+ loop.run_until_complete(coro())
+
+ test()
+
+ # Check that the correct classes were used AFTER the explain plan validator has run
+ if hasattr(connection_cls, "event"):
+ assert not connection_cls.event.is_set(), "Custom connection class should not be used."
+ if hasattr(cursor_cls, "event"):
+ if cursor_cls is not CustomAsyncCursor:
+ assert cursor_cls.event.is_set(), "Custom cursor class was not used."
+ else:
+ assert not cursor_cls.event.is_set(), "Custom async cursor class should not be used."
+
+
+# This test will verify that arguments are preserved for an explain
+# plan by forcing a failure to be generated when explain plans are created and
+# arguments are preserved
diff --git a/tests/datastore_psycopg/test_forward_compat.py b/tests/datastore_psycopg/test_forward_compat.py
new file mode 100644
index 0000000000..520dd35df3
--- /dev/null
+++ b/tests/datastore_psycopg/test_forward_compat.py
@@ -0,0 +1,41 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import psycopg
+
+from newrelic.common.object_wrapper import wrap_function_wrapper
+from newrelic.hooks.database_psycopg import wrapper_psycopg_as_string
+
+
+class TestCompatability():
+ def as_string(self, giraffe, lion, tiger=None):
+ assert type(giraffe) in (psycopg.Cursor, psycopg.AsyncCursor)
+ return "PASS"
+
+
+wrap_function_wrapper(__name__, "TestCompatability.as_string", wrapper_psycopg_as_string)
+
+
+def test_forward_compat_args(connection):
+ cursor = connection.cursor()
+ query = TestCompatability()
+ result = query.as_string(cursor, "giraffe-nomming-leaves")
+ assert result == "PASS"
+
+
+def test_forward_compat_kwargs(connection):
+ cursor = connection.cursor()
+ query = TestCompatability()
+ result = query.as_string(cursor, lion="eats tiger", tiger="eats giraffe")
+ assert result == "PASS"
diff --git a/tests/datastore_psycopg/test_multiple_dbs.py b/tests/datastore_psycopg/test_multiple_dbs.py
new file mode 100644
index 0000000000..ae720c6611
--- /dev/null
+++ b/tests/datastore_psycopg/test_multiple_dbs.py
@@ -0,0 +1,138 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from conftest import DB_MULTIPLE_SETTINGS, DB_SETTINGS, maybe_await
+from testing_support.fixtures import override_application_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_database_trace_inputs import (
+ validate_database_trace_inputs,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+# Settings
+
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+}
+
+
+# Metrics
+
+_base_scoped_metrics = [
+ ("Datastore/statement/Postgres/pg_settings/select", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/create", 1),
+ ("Datastore/operation/Postgres/commit", 2),
+]
+
+_base_rollup_metrics = [
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ ("Datastore/Postgres/all", 5),
+ ("Datastore/Postgres/allOther", 5),
+ ("Datastore/statement/Postgres/pg_settings/select", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/create", 1),
+ ("Datastore/operation/Postgres/commit", 2),
+]
+
+_enable_scoped_metrics = list(_base_scoped_metrics)
+_enable_rollup_metrics = list(_base_rollup_metrics)
+
+_disable_scoped_metrics = list(_base_scoped_metrics)
+_disable_rollup_metrics = list(_base_rollup_metrics)
+
+_postgresql_1 = DB_MULTIPLE_SETTINGS[0]
+_host_1 = instance_hostname(_postgresql_1["host"])
+_port_1 = _postgresql_1["port"]
+
+_postgresql_2 = DB_MULTIPLE_SETTINGS[1]
+_host_2 = instance_hostname(_postgresql_2["host"])
+_port_2 = _postgresql_2["port"]
+
+_instance_metric_name_1 = f"Datastore/instance/Postgres/{_host_1}/{_port_1}"
+_instance_metric_name_2 = f"Datastore/instance/Postgres/{_host_2}/{_port_2}"
+
+_enable_rollup_metrics.extend(
+ [
+ (_instance_metric_name_1, 2),
+ (_instance_metric_name_2, 3),
+ ]
+)
+_disable_rollup_metrics.extend(
+ [
+ (_instance_metric_name_1, None),
+ (_instance_metric_name_2, None),
+ ]
+)
+
+
+# Query
+
+
+async def _exercise_db(multiple_connections):
+ connection = multiple_connections[0]
+ try:
+ cursor = connection.cursor()
+ await maybe_await(cursor.execute("SELECT setting from pg_settings where name=%s", ("server_version",)))
+ await maybe_await(connection.commit())
+ finally:
+ await maybe_await(connection.close())
+
+ connection = multiple_connections[1]
+ try:
+ cursor = connection.cursor()
+ await maybe_await(cursor.execute(f"drop table if exists {DB_SETTINGS['table_name']}"))
+ await maybe_await(
+ cursor.execute(f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)")
+ )
+ await maybe_await(connection.commit())
+ finally:
+ await maybe_await(connection.close())
+
+
+# Tests
+
+
+@override_application_settings(_enable_instance_settings)
+@validate_transaction_metrics(
+ "test_multiple_dbs:test_multiple_databases_enable_instance",
+ scoped_metrics=_enable_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_multiple_databases_enable_instance(loop, multiple_connections):
+ loop.run_until_complete(_exercise_db(multiple_connections))
+
+
+@override_application_settings(_disable_instance_settings)
+@validate_transaction_metrics(
+ "test_multiple_dbs:test_multiple_databases_disable_instance",
+ scoped_metrics=_disable_scoped_metrics,
+ rollup_metrics=_disable_scoped_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_multiple_databases_disable_instance(loop, multiple_connections):
+ loop.run_until_complete(_exercise_db(multiple_connections))
diff --git a/tests/datastore_psycopg/test_obfuscation.py b/tests/datastore_psycopg/test_obfuscation.py
new file mode 100644
index 0000000000..1a656967a9
--- /dev/null
+++ b/tests/datastore_psycopg/test_obfuscation.py
@@ -0,0 +1,139 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from conftest import DB_SETTINGS, maybe_await
+from testing_support.validators.validate_database_node import validate_database_node
+from testing_support.validators.validate_sql_obfuscation import validate_sql_obfuscation
+
+from newrelic.api.background_task import background_task
+from newrelic.core.database_utils import SQLConnections
+
+
+@pytest.fixture()
+def cursor(loop, connection):
+ try:
+ cursor = connection.cursor()
+
+ loop.run_until_complete(maybe_await(cursor.execute(f"drop table if exists {DB_SETTINGS['table_name']}")))
+ loop.run_until_complete(
+ maybe_await(cursor.execute(f"create table {DB_SETTINGS['table_name']} (b text, c text)"))
+ )
+
+ yield cursor
+
+ finally:
+ loop.run_until_complete(maybe_await(connection.close()))
+
+
+_quoting_style_tests = [
+ (
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b='2'",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=?",
+ ),
+ (
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=$func$2$func$",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=?",
+ ),
+ (
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=U&'2'",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=U&?",
+ ),
+]
+
+
+@pytest.mark.parametrize("sql,obfuscated", _quoting_style_tests)
+def test_obfuscation_quoting_styles(loop, cursor, sql, obfuscated):
+ @validate_sql_obfuscation([obfuscated])
+ @background_task()
+ def test():
+ loop.run_until_complete(maybe_await(cursor.execute(sql)))
+
+ test()
+
+
+_parameter_tests = [
+ (
+ f"SELECT * FROM {DB_SETTINGS['table_name']} where b=%s",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} where b=%s",
+ ),
+]
+
+
+@pytest.mark.parametrize("sql,obfuscated", _parameter_tests)
+def test_obfuscation_parameters(loop, cursor, sql, obfuscated):
+ @validate_sql_obfuscation([obfuscated])
+ @background_task()
+ def test():
+ loop.run_until_complete(maybe_await(cursor.execute(sql, ("hello",))))
+
+ test()
+
+
+def no_explain_plan(node):
+ sql_connections = SQLConnections()
+ explain_plan = node.explain_plan(sql_connections)
+ assert explain_plan is None
+
+
+def any_length_explain_plan(node):
+ if node.statement.operation != "select":
+ return
+
+ sql_connections = SQLConnections()
+ explain_plan = node.explain_plan(sql_connections)
+ assert explain_plan and len(explain_plan) > 0
+
+
+_test_explain_plans = [
+ (
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']} ; SELECT (b, c) FROM {DB_SETTINGS['table_name']}",
+ no_explain_plan,
+ ),
+ (
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']} ; SELECT (b, c) FROM {DB_SETTINGS['table_name']};",
+ no_explain_plan,
+ ),
+ (f"SELECT (b, c) FROM {DB_SETTINGS['table_name']} WHERE b=';'", no_explain_plan),
+ (f";SELECT (b, c) FROM {DB_SETTINGS['table_name']}", no_explain_plan),
+ (f"SELECT (b, c) FROM {DB_SETTINGS['table_name']}", any_length_explain_plan),
+ (f"SELECT (b, c) FROM {DB_SETTINGS['table_name']};", any_length_explain_plan),
+ (
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']};;;;;;",
+ any_length_explain_plan,
+ ),
+ (
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']};\n\n",
+ any_length_explain_plan,
+ ),
+]
+
+
+@pytest.mark.parametrize("sql,validator", _test_explain_plans)
+def test_obfuscation_explain_plans(loop, connection, sql, validator):
+ @validate_database_node(validator)
+ @background_task()
+ async def test():
+ try:
+ cursor = connection.cursor()
+ await maybe_await(cursor.execute(f"drop table if exists {DB_SETTINGS['table_name']}"))
+ await maybe_await(cursor.execute(f"create table {DB_SETTINGS['table_name']} (b text, c text)"))
+
+ await maybe_await(cursor.execute(sql))
+
+ finally:
+ await maybe_await(connection.commit())
+ await maybe_await(connection.close())
+
+ loop.run_until_complete(test())
diff --git a/tests/datastore_psycopg/test_register.py b/tests/datastore_psycopg/test_register.py
new file mode 100644
index 0000000000..4a5113baea
--- /dev/null
+++ b/tests/datastore_psycopg/test_register.py
@@ -0,0 +1,85 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+import psycopg
+from conftest import maybe_await
+from testing_support.validators.validate_transaction_errors import (
+ validate_transaction_errors,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+
+@validate_transaction_metrics("test_register:test_register_json", background_task=True)
+@validate_transaction_errors(errors=[])
+@background_task()
+def test_register_json(loop, connection):
+ def test():
+ cursor = connection.cursor()
+
+ psycopg.types.json.set_json_loads(loads=lambda x: x, context=connection)
+ psycopg.types.json.set_json_loads(loads=lambda x: x, context=cursor)
+
+ if hasattr(connection, "__aenter__"):
+
+ async def coro():
+ async with connection:
+ test()
+
+ loop.run_until_complete(coro())
+ else:
+ with connection:
+ test()
+
+
+@validate_transaction_metrics("test_register:test_register_range", background_task=True)
+@validate_transaction_errors(errors=[])
+@background_task()
+def test_register_range(loop, connection):
+ async def test():
+ type_name = f"floatrange_{str(os.getpid())}"
+
+ create_sql = f"CREATE TYPE {type_name} AS RANGE (subtype = float8,subtype_diff = float8mi)"
+
+ cursor = connection.cursor()
+
+ await maybe_await(cursor.execute(f"DROP TYPE if exists {type_name}"))
+ await maybe_await(cursor.execute(create_sql))
+
+ range_type_info = await maybe_await(psycopg.types.range.RangeInfo.fetch(connection, type_name))
+ range_type_info.register(connection)
+
+ await maybe_await(cursor.execute(f"DROP TYPE if exists {type_name}"))
+ await maybe_await(cursor.execute(create_sql))
+
+ range_type_info = await maybe_await(psycopg.types.range.RangeInfo.fetch(connection, type_name))
+ range_type_info.register(cursor)
+
+ await maybe_await(cursor.execute(f"DROP TYPE if exists {type_name}"))
+
+ if hasattr(connection, "__aenter__"):
+
+ async def coro():
+ async with connection:
+ await test()
+
+ loop.run_until_complete(coro())
+ else:
+ with connection:
+ loop.run_until_complete(test())
diff --git a/tests/datastore_psycopg/test_rollback.py b/tests/datastore_psycopg/test_rollback.py
new file mode 100644
index 0000000000..57f01ac3f6
--- /dev/null
+++ b/tests/datastore_psycopg/test_rollback.py
@@ -0,0 +1,104 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from conftest import DB_SETTINGS
+from testing_support.fixtures import override_application_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_database_trace_inputs import (
+ validate_database_trace_inputs,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+# Settings
+
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+}
+
+# Metrics
+
+_base_scoped_metrics = (("Datastore/operation/Postgres/rollback", 1),)
+
+_base_rollup_metrics = (
+ ("Datastore/all", 1),
+ ("Datastore/allOther", 1),
+ ("Datastore/Postgres/all", 1),
+ ("Datastore/Postgres/allOther", 1),
+ ("Datastore/operation/Postgres/rollback", 1),
+)
+
+_enable_scoped_metrics = list(_base_scoped_metrics)
+_enable_rollup_metrics = list(_base_rollup_metrics)
+
+_disable_scoped_metrics = list(_base_scoped_metrics)
+_disable_rollup_metrics = list(_base_rollup_metrics)
+
+_host = instance_hostname(DB_SETTINGS["host"])
+_port = DB_SETTINGS["port"]
+
+_instance_metric_name = f"Datastore/instance/Postgres/{_host}/{_port}"
+
+_enable_rollup_metrics.append((_instance_metric_name, 1))
+
+_disable_rollup_metrics.append((_instance_metric_name, None))
+
+# Query
+
+
+async def _exercise_db(connection):
+ try:
+ if hasattr(connection, "__aenter__"):
+ async with connection:
+ raise RuntimeError("error")
+ else:
+ with connection:
+ raise RuntimeError("error")
+ except RuntimeError:
+ pass
+
+
+# Tests
+
+
+@override_application_settings(_enable_instance_settings)
+@validate_transaction_metrics(
+ "test_rollback:test_rollback_on_exception_enable_instance",
+ scoped_metrics=_enable_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_rollback_on_exception_enable_instance(loop, connection):
+ loop.run_until_complete(_exercise_db(connection))
+
+
+@override_application_settings(_disable_instance_settings)
+@validate_transaction_metrics(
+ "test_rollback:test_rollback_on_exception_disable_instance",
+ scoped_metrics=_disable_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_rollback_on_exception_disable_instance(loop, connection):
+ loop.run_until_complete(_exercise_db(connection))
diff --git a/tests/datastore_psycopg/test_slow_sql.py b/tests/datastore_psycopg/test_slow_sql.py
new file mode 100644
index 0000000000..abd2c31cc1
--- /dev/null
+++ b/tests/datastore_psycopg/test_slow_sql.py
@@ -0,0 +1,135 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from conftest import maybe_await
+from testing_support.fixtures import override_application_settings
+from testing_support.validators.validate_slow_sql_collector_json import (
+ validate_slow_sql_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.api.transaction import current_transaction
+
+# Settings
+
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+ "datastore_tracer.database_name_reporting.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+ "datastore_tracer.database_name_reporting.enabled": False,
+}
+
+# Expected parameters
+
+_enabled_required = set(["host", "port_path_or_id", "database_name"])
+_enabled_forgone = set()
+
+_disabled_required = set()
+_disabled_forgone = set(["host", "port_path_or_id", "database_name"])
+
+# Guid is always required, regardless of DT status.
+# It should be excluded from the forgone params set.
+_distributed_tracing_required_params = set(["guid", "traceId", "priority", "sampled"])
+_distributed_tracing_forgone_params = set(["traceId", "priority", "sampled"])
+_distributed_tracing_payload_received_params = set(
+ ["parent.type", "parent.app", "parent.account", "parent.transportType", "parent.transportDuration"]
+)
+
+_transaction_guid = "1234567890"
+_distributed_tracing_exact_params = {"guid": _transaction_guid}
+
+
+# Query
+
+
+async def _exercise_db(connection):
+ try:
+ cursor = connection.cursor()
+ await maybe_await(cursor.execute("SELECT setting from pg_settings where name=%s", ("server_version",)))
+ finally:
+ await maybe_await(connection.close())
+
+
+# Tests
+
+
+@pytest.mark.parametrize("instance_enabled", (True, False))
+@pytest.mark.parametrize(
+ "distributed_tracing_enabled,payload_received",
+ [
+ (True, True),
+ (True, False),
+ (False, False),
+ ],
+)
+def test_slow_sql_json(loop, connection, instance_enabled, distributed_tracing_enabled, payload_received):
+
+ exact_params = None
+
+ if instance_enabled:
+ settings = _enable_instance_settings.copy()
+ required_params = set(_enabled_required)
+ forgone_params = set(_enabled_forgone)
+ else:
+ settings = _disable_instance_settings.copy()
+ required_params = set(_disabled_required)
+ forgone_params = set(_disabled_forgone)
+
+ if distributed_tracing_enabled:
+ required_params.update(_distributed_tracing_required_params)
+ exact_params = _distributed_tracing_exact_params
+ settings["distributed_tracing.enabled"] = True
+ if payload_received:
+ required_params.update(_distributed_tracing_payload_received_params)
+ else:
+ forgone_params.update(_distributed_tracing_payload_received_params)
+ else:
+ forgone_params.update(_distributed_tracing_forgone_params)
+ forgone_params.update(_distributed_tracing_payload_received_params)
+ settings["distributed_tracing.enabled"] = False
+
+ @override_application_settings(settings)
+ @validate_slow_sql_collector_json(
+ required_params=required_params, forgone_params=forgone_params, exact_params=exact_params
+ )
+ @background_task()
+ def _test():
+ transaction = current_transaction()
+ transaction.guid = _transaction_guid
+
+ loop.run_until_complete(_exercise_db(connection))
+
+ if payload_received:
+
+ payload = {
+ "v": [0, 1],
+ "d": {
+ "ty": "Mobile",
+ "ac": transaction.settings.account_id,
+ "tk": transaction.settings.trusted_account_key,
+ "ap": "2827902",
+ "pa": "5e5733a911cfbc73",
+ "id": "7d3efb1b173fecfa",
+ "tr": "d6b4ba0c3a712ca",
+ "ti": 1518469636035,
+ "tx": "8703ff3d88eefe9d",
+ },
+ }
+
+ transaction.accept_distributed_trace_payload(payload)
+
+ _test()
diff --git a/tests/datastore_psycopg/test_span_event.py b/tests/datastore_psycopg/test_span_event.py
new file mode 100644
index 0000000000..df68f79662
--- /dev/null
+++ b/tests/datastore_psycopg/test_span_event.py
@@ -0,0 +1,136 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from conftest import DB_SETTINGS, maybe_await
+from testing_support.fixtures import override_application_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_span_events import validate_span_events
+
+from newrelic.api.background_task import background_task
+from newrelic.api.transaction import current_transaction
+
+# Settings
+
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+ "datastore_tracer.database_name_reporting.enabled": True,
+ "distributed_tracing.enabled": True,
+ "span_events.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+ "datastore_tracer.database_name_reporting.enabled": False,
+ "distributed_tracing.enabled": True,
+ "span_events.enabled": True,
+}
+
+
+async def _exercise_db(connection):
+ try:
+ cursor = connection.cursor()
+ await maybe_await(cursor.execute("SELECT setting from pg_settings where name=%s", ("server_version",)))
+
+ # No target
+ await maybe_await(cursor.execute("SELECT 1"))
+ finally:
+ await maybe_await(connection.close())
+
+
+# Tests
+
+
+@pytest.mark.parametrize("db_instance_enabled", (True, False))
+@pytest.mark.parametrize("instance_enabled", (True, False))
+def test_span_events(loop, connection, instance_enabled, db_instance_enabled):
+ guid = "dbb533c53b749e0b"
+ priority = 0.5
+
+ common_intrinsics = {
+ "type": "Span",
+ "transactionId": guid,
+ "priority": priority,
+ "sampled": True,
+ "category": "datastore",
+ "component": "Postgres",
+ "span.kind": "client",
+ }
+
+ exact_agents = {}
+
+ if instance_enabled:
+ settings = _enable_instance_settings.copy()
+ hostname = instance_hostname(DB_SETTINGS["host"])
+ exact_agents.update(
+ {
+ "peer.address": f"{hostname}:{DB_SETTINGS['port']}",
+ "peer.hostname": hostname,
+ }
+ )
+ else:
+ settings = _disable_instance_settings.copy()
+ exact_agents.update(
+ {
+ "peer.address": "Unknown:Unknown",
+ "peer.hostname": "Unknown",
+ }
+ )
+
+ if db_instance_enabled and instance_enabled:
+ exact_agents.update(
+ {
+ "db.instance": DB_SETTINGS["name"],
+ }
+ )
+ unexpected_agents = ()
+ else:
+ settings["attributes.exclude"] = ["db.instance"]
+ unexpected_agents = ("db.instance",)
+
+ query_1_intrinsics = common_intrinsics.copy()
+ query_1_intrinsics["name"] = "Datastore/statement/Postgres/pg_settings/select"
+
+ query_1_agents = exact_agents.copy()
+ query_1_agents["db.statement"] = "SELECT setting from pg_settings where name=%s"
+
+ query_2_intrinsics = common_intrinsics.copy()
+ query_2_intrinsics["name"] = "Datastore/operation/Postgres/select"
+
+ query_2_agents = exact_agents.copy()
+ query_2_agents["db.statement"] = "SELECT ?"
+
+ @validate_span_events(
+ count=1,
+ exact_intrinsics=query_1_intrinsics,
+ unexpected_intrinsics=("db.instance", "db.statement"),
+ exact_agents=query_1_agents,
+ unexpected_agents=unexpected_agents,
+ )
+ @validate_span_events(
+ count=1,
+ exact_intrinsics=query_2_intrinsics,
+ unexpected_intrinsics=("db.instance", "db.statement"),
+ exact_agents=query_2_agents,
+ unexpected_agents=unexpected_agents,
+ )
+ @override_application_settings(settings)
+ @background_task(name="span_events")
+ def _test():
+ txn = current_transaction()
+ txn.guid = guid
+ txn._priority = priority
+ txn._sampled = True
+ loop.run_until_complete(_exercise_db(connection))
+
+ _test()
diff --git a/tests/datastore_psycopg/test_trace_node.py b/tests/datastore_psycopg/test_trace_node.py
new file mode 100644
index 0000000000..584e27bbfe
--- /dev/null
+++ b/tests/datastore_psycopg/test_trace_node.py
@@ -0,0 +1,104 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import psycopg
+from conftest import DB_SETTINGS, maybe_await
+from testing_support.fixtures import (
+ override_application_settings,
+ validate_tt_parenting,
+)
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_tt_collector_json import (
+ validate_tt_collector_json,
+)
+
+from newrelic.api.background_task import background_task
+
+# Settings
+
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+ "datastore_tracer.database_name_reporting.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+ "datastore_tracer.database_name_reporting.enabled": False,
+}
+
+# Expected parameters
+
+_enabled_required = {
+ "host": instance_hostname(DB_SETTINGS["host"]),
+ "port_path_or_id": str(DB_SETTINGS["port"]),
+ "db.instance": DB_SETTINGS["name"],
+}
+_enabled_forgone = {}
+
+_disabled_required = {}
+_disabled_forgone = {
+ "host": "VALUE NOT USED",
+ "port_path_or_id": "VALUE NOT USED",
+ "db.instance": "VALUE NOT USED",
+}
+
+_tt_parenting = (
+ "TransactionNode",
+ [
+ ("FunctionNode", []),
+ ("DatabaseNode", []),
+ ],
+)
+
+
+# Query
+
+
+async def _exercise_db(async_=False):
+ # Connect here without using fixture to assert on the parenting of the FunctionTrace for Connection.connect()
+ # This is only possible when the connection is done inside a transaction, so connect after the test starts.
+ connect = psycopg.Connection.connect if async_ else psycopg.AsyncConnection.connect
+ connection = await maybe_await(
+ connect(
+ dbname=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
+ )
+
+ try:
+ cursor = connection.cursor()
+ await maybe_await(cursor.execute("SELECT setting from pg_settings where name=%s", ("server_version",)))
+ finally:
+ await maybe_await(connection.close())
+
+
+# Tests
+
+
+@override_application_settings(_enable_instance_settings)
+@validate_tt_collector_json(datastore_params=_enabled_required, datastore_forgone_params=_enabled_forgone)
+@validate_tt_parenting(_tt_parenting)
+@background_task()
+def test_trace_node_datastore_params_enable_instance(loop, is_async):
+ loop.run_until_complete(_exercise_db(is_async))
+
+
+@override_application_settings(_disable_instance_settings)
+@validate_tt_collector_json(datastore_params=_disabled_required, datastore_forgone_params=_disabled_forgone)
+@validate_tt_parenting(_tt_parenting)
+@background_task()
+def test_trace_node_datastore_params_disable_instance(loop, is_async):
+ loop.run_until_complete(_exercise_db(is_async))
diff --git a/tests/datastore_psycopg2/conftest.py b/tests/datastore_psycopg2/conftest.py
index dd271909d7..7fe6cb7e89 100644
--- a/tests/datastore_psycopg2/conftest.py
+++ b/tests/datastore_psycopg2/conftest.py
@@ -12,21 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
- 'debug.log_explain_plan_queries': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_psycopg2)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_psycopg2)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_psycopg2/test_async.py b/tests/datastore_psycopg2/test_async.py
index 7af9adc6a5..ca281084e5 100644
--- a/tests/datastore_psycopg2/test_async.py
+++ b/tests/datastore_psycopg2/test_async.py
@@ -44,8 +44,8 @@
# Metrics
_base_scoped_metrics = (
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS['table_name'], 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS['table_name'], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
('Datastore/operation/Postgres/drop', 1),
('Datastore/operation/Postgres/create', 1)
)
@@ -56,9 +56,9 @@
('Datastore/Postgres/all', 5),
('Datastore/Postgres/allOther', 5),
('Datastore/operation/Postgres/select', 1),
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS['table_name'], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
('Datastore/operation/Postgres/insert', 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS['table_name'], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
('Datastore/operation/Postgres/drop', 1),
('Datastore/operation/Postgres/create', 1)
)
@@ -75,7 +75,7 @@
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Postgres/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Postgres/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 4)
@@ -102,18 +102,16 @@ def _exercise_db(async_keyword):
wait(async_conn)
async_cur = async_conn.cursor()
- async_cur.execute("""drop table if exists %s""" % DB_SETTINGS['table_name'])
+ async_cur.execute(f"""drop table if exists {DB_SETTINGS['table_name']}""")
wait(async_cur.connection)
- async_cur.execute("""create table %s """ % DB_SETTINGS['table_name'] +
- """(a integer, b real, c text)""")
+ async_cur.execute(f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)")
wait(async_cur.connection)
- async_cur.execute("""insert into %s """ % DB_SETTINGS['table_name'] +
- """values (%s, %s, %s)""", (1, 1.0, '1.0'))
+ async_cur.execute(f"insert into {DB_SETTINGS['table_name']} values (%s, %s, %s)", (1, 1.0, '1.0'))
wait(async_cur.connection)
- async_cur.execute("""select * from %s""" % DB_SETTINGS['table_name'])
+ async_cur.execute(f"""select * from {DB_SETTINGS['table_name']}""")
wait(async_cur.connection)
for row in async_cur:
diff --git a/tests/datastore_psycopg2/test_cursor.py b/tests/datastore_psycopg2/test_cursor.py
index d66d73ff84..a534e8e643 100644
--- a/tests/datastore_psycopg2/test_cursor.py
+++ b/tests/datastore_psycopg2/test_cursor.py
@@ -42,10 +42,10 @@
# Metrics
_base_scoped_metrics = (
('Function/psycopg2:connect', 1),
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS['table_name'], 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS['table_name'], 1),
- ('Datastore/statement/Postgres/%s/update' % DB_SETTINGS['table_name'], 1),
- ('Datastore/statement/Postgres/%s/delete' % DB_SETTINGS['table_name'], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
('Datastore/statement/Postgres/now/call', 1),
('Datastore/statement/Postgres/pg_sleep/call', 1),
('Datastore/operation/Postgres/drop', 1),
@@ -59,10 +59,10 @@
('Datastore/allOther', 12),
('Datastore/Postgres/all', 12),
('Datastore/Postgres/allOther', 12),
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS['table_name'], 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS['table_name'], 1),
- ('Datastore/statement/Postgres/%s/update' % DB_SETTINGS['table_name'], 1),
- ('Datastore/statement/Postgres/%s/delete' % DB_SETTINGS['table_name'], 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
('Datastore/operation/Postgres/select', 1),
('Datastore/operation/Postgres/insert', 1),
('Datastore/operation/Postgres/update', 1),
@@ -85,7 +85,7 @@
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Postgres/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Postgres/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 11)
@@ -103,27 +103,27 @@ def _execute(connection, cursor, row_type, wrapper):
psycopg2.extensions.register_type(unicode_type, connection)
psycopg2.extensions.register_type(unicode_type, cursor)
- sql = """drop table if exists %s""" % DB_SETTINGS["table_name"]
+ sql = f"""drop table if exists {DB_SETTINGS['table_name']}"""
cursor.execute(wrapper(sql))
- sql = """create table %s (a integer, b real, c text)""" % DB_SETTINGS["table_name"]
+ sql = f"""create table {DB_SETTINGS['table_name']} (a integer, b real, c text)"""
cursor.execute(wrapper(sql))
- sql = """insert into %s """ % DB_SETTINGS["table_name"] + """values (%s, %s, %s)"""
+ sql = f"insert into {DB_SETTINGS['table_name']} values (%s, %s, %s)"
params = [(1, 1.0, '1.0'), (2, 2.2, '2.2'), (3, 3.3, '3.3')]
cursor.executemany(wrapper(sql), params)
- sql = """select * from %s""" % DB_SETTINGS["table_name"]
+ sql = f"""select * from {DB_SETTINGS['table_name']}"""
cursor.execute(wrapper(sql))
for row in cursor:
assert isinstance(row, row_type)
- sql = """update %s""" % DB_SETTINGS["table_name"] + """ set a=%s, b=%s, c=%s where a=%s"""
+ sql = f"update {DB_SETTINGS['table_name']} set a=%s, b=%s, c=%s where a=%s"
params = (4, 4.0, '4.0', 1)
cursor.execute(wrapper(sql), params)
- sql = """delete from %s where a=2""" % DB_SETTINGS["table_name"]
+ sql = f"""delete from {DB_SETTINGS['table_name']} where a=2"""
cursor.execute(wrapper(sql))
connection.commit()
@@ -162,9 +162,7 @@ def _exercise_db(cursor_factory=None, use_cur_context=False, row_type=tuple,
_test_matrix[1].append((str, True))
# Composable SQL is expected to be available in versions 2.7 and up
-assert sql, (
- "Composable sql (from psycopg2 import sql) is expected to load"
- "but is not loading")
+assert sql, "Composable sql (from psycopg2 import sql) is expected to load but is not loading"
# exercise with regular SQL wrapper
_test_matrix[1].append((sql.SQL, True))
diff --git a/tests/datastore_psycopg2/test_forward_compat.py b/tests/datastore_psycopg2/test_forward_compat.py
index d150943288..35611f0e00 100644
--- a/tests/datastore_psycopg2/test_forward_compat.py
+++ b/tests/datastore_psycopg2/test_forward_compat.py
@@ -20,7 +20,7 @@
from newrelic.hooks.database_psycopg2 import wrapper_psycopg2_as_string
-class TestCompatability(object):
+class TestCompatability():
def as_string(self, giraffe, lion, tiger=None):
assert isinstance(giraffe, ext.cursor)
return "PASS"
diff --git a/tests/datastore_psycopg2/test_multiple_dbs.py b/tests/datastore_psycopg2/test_multiple_dbs.py
index afbdd66f2d..f69288cb85 100644
--- a/tests/datastore_psycopg2/test_multiple_dbs.py
+++ b/tests/datastore_psycopg2/test_multiple_dbs.py
@@ -71,10 +71,8 @@
_host_2 = instance_hostname(_postgresql_2['host'])
_port_2 = _postgresql_2['port']
- _instance_metric_name_1 = 'Datastore/instance/Postgres/%s/%s' % (
- _host_1, _port_1)
- _instance_metric_name_2 = 'Datastore/instance/Postgres/%s/%s' % (
- _host_2, _port_2)
+ _instance_metric_name_1 = f'Datastore/instance/Postgres/{_host_1}/{_port_1}'
+ _instance_metric_name_2 = f'Datastore/instance/Postgres/{_host_2}/{_port_2}'
_enable_rollup_metrics.extend([
(_instance_metric_name_1, 2),
@@ -111,9 +109,8 @@ def _exercise_db():
port=postgresql2['port'])
try:
cursor = connection.cursor()
- cursor.execute("""drop table if exists %s""" % postgresql2["table_name"])
- cursor.execute("""create table %s """ % postgresql2["table_name"] +
- """(a integer, b real, c text)""")
+ cursor.execute(f"""drop table if exists {postgresql2['table_name']}""")
+ cursor.execute(f"create table {postgresql2['table_name']} (a integer, b real, c text)")
connection.commit()
finally:
connection.close()
diff --git a/tests/datastore_psycopg2/test_obfuscation.py b/tests/datastore_psycopg2/test_obfuscation.py
index 90f15d375b..69d2e00142 100644
--- a/tests/datastore_psycopg2/test_obfuscation.py
+++ b/tests/datastore_psycopg2/test_obfuscation.py
@@ -37,8 +37,8 @@ def psycopg2_cursor():
try:
cursor = connection.cursor()
- cursor.execute("drop table if exists %s" % DB_SETTINGS["table_name"])
- cursor.execute("create table %s (b text, c text)" % DB_SETTINGS["table_name"])
+ cursor.execute(f"drop table if exists {DB_SETTINGS['table_name']}")
+ cursor.execute(f"create table {DB_SETTINGS['table_name']} (b text, c text)")
yield cursor
@@ -48,16 +48,16 @@ def psycopg2_cursor():
_quoting_style_tests = [
(
- "SELECT * FROM %s WHERE b='2'" % DB_SETTINGS["table_name"],
- "SELECT * FROM %s WHERE b=?" % DB_SETTINGS["table_name"],
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b='2'",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=?",
),
(
- "SELECT * FROM %s WHERE b=$func$2$func$" % DB_SETTINGS["table_name"],
- "SELECT * FROM %s WHERE b=?" % DB_SETTINGS["table_name"],
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=$func$2$func$",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=?",
),
(
- "SELECT * FROM %s WHERE b=U&'2'" % DB_SETTINGS["table_name"],
- "SELECT * FROM %s WHERE b=U&?" % DB_SETTINGS["table_name"],
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=U&'2'",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} WHERE b=U&?",
),
]
@@ -74,8 +74,8 @@ def test():
_parameter_tests = [
(
- "SELECT * FROM " + DB_SETTINGS["table_name"] + " where b=%s",
- "SELECT * FROM " + DB_SETTINGS["table_name"] + " where b=%s",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} where b=%s",
+ f"SELECT * FROM {DB_SETTINGS['table_name']} where b=%s",
),
]
@@ -107,25 +107,23 @@ def any_length_explain_plan(node):
_test_explain_plans = [
(
- "SELECT (b, c) FROM %s ; SELECT (b, c) FROM %s"
- % (DB_SETTINGS["table_name"], DB_SETTINGS["table_name"]),
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']} ; SELECT (b, c) FROM {DB_SETTINGS['table_name']}",
no_explain_plan,
),
(
- "SELECT (b, c) FROM %s ; SELECT (b, c) FROM %s;"
- % (DB_SETTINGS["table_name"], DB_SETTINGS["table_name"]),
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']} ; SELECT (b, c) FROM {DB_SETTINGS['table_name']};",
no_explain_plan,
),
- ("SELECT (b, c) FROM %s WHERE b=';'" % DB_SETTINGS["table_name"], no_explain_plan),
- (";SELECT (b, c) FROM %s" % DB_SETTINGS["table_name"], no_explain_plan),
- ("SELECT (b, c) FROM %s" % DB_SETTINGS["table_name"], any_length_explain_plan),
- ("SELECT (b, c) FROM %s;" % DB_SETTINGS["table_name"], any_length_explain_plan),
+ (f"SELECT (b, c) FROM {DB_SETTINGS['table_name']} WHERE b=';'", no_explain_plan),
+ (f";SELECT (b, c) FROM {DB_SETTINGS['table_name']}", no_explain_plan),
+ (f"SELECT (b, c) FROM {DB_SETTINGS['table_name']}", any_length_explain_plan),
+ (f"SELECT (b, c) FROM {DB_SETTINGS['table_name']};", any_length_explain_plan),
(
- "SELECT (b, c) FROM %s;;;;;;" % DB_SETTINGS["table_name"],
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']};;;;;;",
any_length_explain_plan,
),
(
- "SELECT (b, c) FROM %s;\n\n" % DB_SETTINGS["table_name"],
+ f"SELECT (b, c) FROM {DB_SETTINGS['table_name']};\n\n",
any_length_explain_plan,
),
]
@@ -148,9 +146,9 @@ def test():
try:
cursor = connection.cursor()
- cursor.execute("drop table if exists %s" % DB_SETTINGS["table_name"])
+ cursor.execute(f"drop table if exists {DB_SETTINGS['table_name']}")
cursor.execute(
- "create table %s (b text, c text)" % DB_SETTINGS["table_name"]
+ f"create table {DB_SETTINGS['table_name']} (b text, c text)"
)
cursor.execute(sql)
diff --git a/tests/datastore_psycopg2/test_register.py b/tests/datastore_psycopg2/test_register.py
index b5450c3588..61cba82758 100644
--- a/tests/datastore_psycopg2/test_register.py
+++ b/tests/datastore_psycopg2/test_register.py
@@ -48,24 +48,22 @@ def test_register_range():
password=DB_SETTINGS['password'], host=DB_SETTINGS['host'],
port=DB_SETTINGS['port']) as connection:
- type_name = "floatrange_" + str(os.getpid())
+ type_name = f"floatrange_{str(os.getpid())}"
- create_sql = ('CREATE TYPE %s AS RANGE (' % type_name +
- 'subtype = float8,'
- 'subtype_diff = float8mi)')
+ create_sql = f"CREATE TYPE {type_name} AS RANGE (subtype = float8,subtype_diff = float8mi)"
cursor = connection.cursor()
- cursor.execute("DROP TYPE if exists %s" % type_name)
+ cursor.execute(f"DROP TYPE if exists {type_name}")
cursor.execute(create_sql)
psycopg2.extras.register_range(type_name,
psycopg2.extras.NumericRange, connection)
- cursor.execute("DROP TYPE if exists %s" % type_name)
+ cursor.execute(f"DROP TYPE if exists {type_name}")
cursor.execute(create_sql)
psycopg2.extras.register_range(type_name,
psycopg2.extras.NumericRange, cursor)
- cursor.execute("DROP TYPE if exists %s" % type_name)
+ cursor.execute(f"DROP TYPE if exists {type_name}")
diff --git a/tests/datastore_psycopg2/test_rollback.py b/tests/datastore_psycopg2/test_rollback.py
index 0a23b1005e..248edee43b 100644
--- a/tests/datastore_psycopg2/test_rollback.py
+++ b/tests/datastore_psycopg2/test_rollback.py
@@ -57,7 +57,7 @@
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Postgres/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Postgres/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 1)
diff --git a/tests/datastore_psycopg2/test_slow_sql.py b/tests/datastore_psycopg2/test_slow_sql.py
index aea45dd183..8feeef4d2d 100644
--- a/tests/datastore_psycopg2/test_slow_sql.py
+++ b/tests/datastore_psycopg2/test_slow_sql.py
@@ -14,71 +14,78 @@
import psycopg2
import pytest
-
from testing_support.fixtures import override_application_settings
-from testing_support.validators.validate_slow_sql_collector_json import validate_slow_sql_collector_json
-
+from testing_support.validators.validate_slow_sql_collector_json import (
+ validate_slow_sql_collector_json,
+)
from utils import DB_SETTINGS
from newrelic.api.background_task import background_task
from newrelic.api.transaction import current_transaction
-
# Settings
_enable_instance_settings = {
- 'datastore_tracer.instance_reporting.enabled': True,
- 'datastore_tracer.database_name_reporting.enabled': True,
+ "datastore_tracer.instance_reporting.enabled": True,
+ "datastore_tracer.database_name_reporting.enabled": True,
}
_disable_instance_settings = {
- 'datastore_tracer.instance_reporting.enabled': False,
- 'datastore_tracer.database_name_reporting.enabled': False,
+ "datastore_tracer.instance_reporting.enabled": False,
+ "datastore_tracer.database_name_reporting.enabled": False,
}
# Expected parameters
-_enabled_required = set(['host', 'port_path_or_id', 'database_name'])
+_enabled_required = set(["host", "port_path_or_id", "database_name"])
_enabled_forgone = set()
_disabled_required = set()
-_disabled_forgone = set(['host', 'port_path_or_id', 'database_name'])
+_disabled_forgone = set(["host", "port_path_or_id", "database_name"])
-_distributed_tracing_always_params = set(['guid', 'traceId', 'priority',
- 'sampled'])
-_distributed_tracing_payload_received_params = set(['parent.type',
- 'parent.app', 'parent.account', 'parent.transportType',
- 'parent.transportDuration'])
+# Guid is always required, regardless of DT status.
+# It should be excluded from the forgone params set.
+_distributed_tracing_required_params = set(["guid", "traceId", "priority", "sampled"])
+_distributed_tracing_forgone_params = set(["traceId", "priority", "sampled"])
+_distributed_tracing_payload_received_params = set(
+ ["parent.type", "parent.app", "parent.account", "parent.transportType", "parent.transportDuration"]
+)
-_transaction_guid = '1234567890'
-_distributed_tracing_exact_params = {'guid': _transaction_guid}
+_transaction_guid = "1234567890"
+_distributed_tracing_exact_params = {"guid": _transaction_guid}
# Query
+
def _exercise_db():
connection = psycopg2.connect(
- database=DB_SETTINGS['name'], user=DB_SETTINGS['user'],
- password=DB_SETTINGS['password'], host=DB_SETTINGS['host'],
- port=DB_SETTINGS['port'])
+ database=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
try:
cursor = connection.cursor()
- cursor.execute("""SELECT setting from pg_settings where name=%s""",
- ('server_version',))
+ cursor.execute("""SELECT setting from pg_settings where name=%s""", ("server_version",))
finally:
connection.close()
# Tests
-@pytest.mark.parametrize('instance_enabled', (True, False))
-@pytest.mark.parametrize('distributed_tracing_enabled,payload_received', [
+
+@pytest.mark.parametrize("instance_enabled", (True, False))
+@pytest.mark.parametrize(
+ "distributed_tracing_enabled,payload_received",
+ [
(True, True),
(True, False),
(False, False),
-])
-def test_slow_sql_json(instance_enabled, distributed_tracing_enabled,
- payload_received):
+ ],
+)
+def test_slow_sql_json(instance_enabled, distributed_tracing_enabled, payload_received):
exact_params = None
@@ -92,25 +99,22 @@ def test_slow_sql_json(instance_enabled, distributed_tracing_enabled,
forgone_params = set(_disabled_forgone)
if distributed_tracing_enabled:
- required_params.update(_distributed_tracing_always_params)
+ required_params.update(_distributed_tracing_required_params)
exact_params = _distributed_tracing_exact_params
- settings['distributed_tracing.enabled'] = True
+ settings["distributed_tracing.enabled"] = True
if payload_received:
- required_params.update(
- _distributed_tracing_payload_received_params)
+ required_params.update(_distributed_tracing_payload_received_params)
else:
- forgone_params.update(
- _distributed_tracing_payload_received_params)
+ forgone_params.update(_distributed_tracing_payload_received_params)
else:
- forgone_params.update(_distributed_tracing_always_params)
+ forgone_params.update(_distributed_tracing_forgone_params)
forgone_params.update(_distributed_tracing_payload_received_params)
- settings['distributed_tracing.enabled'] = False
+ settings["distributed_tracing.enabled"] = False
@override_application_settings(settings)
@validate_slow_sql_collector_json(
- required_params=required_params,
- forgone_params=forgone_params,
- exact_params=exact_params)
+ required_params=required_params, forgone_params=forgone_params, exact_params=exact_params
+ )
@background_task()
def _test():
transaction = current_transaction()
@@ -121,18 +125,18 @@ def _test():
if payload_received:
payload = {
- 'v': [0, 1],
- 'd': {
- 'ty': 'Mobile',
- 'ac': transaction.settings.account_id,
- 'tk': transaction.settings.trusted_account_key,
- 'ap': '2827902',
- 'pa': '5e5733a911cfbc73',
- 'id': '7d3efb1b173fecfa',
- 'tr': 'd6b4ba0c3a712ca',
- 'ti': 1518469636035,
- 'tx': '8703ff3d88eefe9d',
- }
+ "v": [0, 1],
+ "d": {
+ "ty": "Mobile",
+ "ac": transaction.settings.account_id,
+ "tk": transaction.settings.trusted_account_key,
+ "ap": "2827902",
+ "pa": "5e5733a911cfbc73",
+ "id": "7d3efb1b173fecfa",
+ "tr": "d6b4ba0c3a712ca",
+ "ti": 1518469636035,
+ "tx": "8703ff3d88eefe9d",
+ },
}
transaction.accept_distributed_trace_payload(payload)
diff --git a/tests/datastore_psycopg2/test_span_event.py b/tests/datastore_psycopg2/test_span_event.py
index 0834061c70..019194ce01 100644
--- a/tests/datastore_psycopg2/test_span_event.py
+++ b/tests/datastore_psycopg2/test_span_event.py
@@ -82,7 +82,7 @@ def test_span_events(instance_enabled, db_instance_enabled):
settings = _enable_instance_settings.copy()
hostname = instance_hostname(DB_SETTINGS['host'])
exact_agents.update({
- 'peer.address': '%s:%s' % (hostname, DB_SETTINGS['port']),
+ 'peer.address': f"{hostname}:{DB_SETTINGS['port']}",
'peer.hostname': hostname,
})
else:
diff --git a/tests/datastore_psycopg2cffi/conftest.py b/tests/datastore_psycopg2cffi/conftest.py
index c9df1369bb..28ef3ca984 100644
--- a/tests/datastore_psycopg2cffi/conftest.py
+++ b/tests/datastore_psycopg2cffi/conftest.py
@@ -12,21 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
- 'debug.log_explain_plan_queries': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_psycopg2cffi)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_psycopg2cffi)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_psycopg2cffi/test_database.py b/tests/datastore_psycopg2cffi/test_database.py
index 54ff6ad09d..9b6bb64d29 100644
--- a/tests/datastore_psycopg2cffi/test_database.py
+++ b/tests/datastore_psycopg2cffi/test_database.py
@@ -15,199 +15,223 @@
import psycopg2cffi
import psycopg2cffi.extensions
import psycopg2cffi.extras
-
-from testing_support.fixtures import validate_stats_engine_explain_plan_output_is_none
-from testing_support.validators.validate_transaction_errors import validate_transaction_errors
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_transaction_slow_sql_count import \
- validate_transaction_slow_sql_count
-from testing_support.validators.validate_database_trace_inputs import validate_database_trace_inputs
-
from testing_support.db_settings import postgresql_settings
+from testing_support.fixtures import validate_stats_engine_explain_plan_output_is_none
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_database_trace_inputs import (
+ validate_database_trace_inputs,
+)
+from testing_support.validators.validate_transaction_errors import (
+ validate_transaction_errors,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_transaction_slow_sql_count import (
+ validate_transaction_slow_sql_count,
+)
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
DB_SETTINGS = postgresql_settings()[0]
_test_execute_via_cursor_scoped_metrics = [
- ('Function/psycopg2cffi:connect', 1),
- ('Function/psycopg2cffi._impl.connection:Connection.__enter__', 1),
- ('Function/psycopg2cffi._impl.connection:Connection.__exit__', 1),
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS["table_name"], 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS["table_name"], 1),
- ('Datastore/statement/Postgres/%s/update' % DB_SETTINGS["table_name"], 1),
- ('Datastore/statement/Postgres/%s/delete' % DB_SETTINGS["table_name"], 1),
- ('Datastore/statement/Postgres/now/call', 1),
- ('Datastore/statement/Postgres/pg_sleep/call', 1),
- ('Datastore/operation/Postgres/drop', 1),
- ('Datastore/operation/Postgres/create', 1),
- ('Datastore/operation/Postgres/commit', 3),
- ('Datastore/operation/Postgres/rollback', 1)]
+ ("Function/psycopg2cffi:connect", 1),
+ ("Function/psycopg2cffi._impl.connection:Connection.__enter__", 1),
+ ("Function/psycopg2cffi._impl.connection:Connection.__exit__", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
+ ("Datastore/statement/Postgres/now/call", 1),
+ ("Datastore/statement/Postgres/pg_sleep/call", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/create", 1),
+ ("Datastore/operation/Postgres/commit", 3),
+ ("Datastore/operation/Postgres/rollback", 1),
+]
_test_execute_via_cursor_rollup_metrics = [
- ('Datastore/all', 13),
- ('Datastore/allOther', 13),
- ('Datastore/Postgres/all', 13),
- ('Datastore/Postgres/allOther', 13),
- ('Datastore/operation/Postgres/select', 1),
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS["table_name"], 1),
- ('Datastore/operation/Postgres/insert', 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS["table_name"], 1),
- ('Datastore/operation/Postgres/update', 1),
- ('Datastore/statement/Postgres/%s/update' % DB_SETTINGS["table_name"], 1),
- ('Datastore/operation/Postgres/delete', 1),
- ('Datastore/statement/Postgres/%s/delete' % DB_SETTINGS["table_name"], 1),
- ('Datastore/operation/Postgres/drop', 1),
- ('Datastore/operation/Postgres/create', 1),
- ('Datastore/statement/Postgres/now/call', 1),
- ('Datastore/statement/Postgres/pg_sleep/call', 1),
- ('Datastore/operation/Postgres/call', 2),
- ('Datastore/operation/Postgres/commit', 3),
- ('Datastore/operation/Postgres/rollback', 1)]
-
-
-@validate_transaction_metrics('test_database:test_execute_via_cursor',
- scoped_metrics=_test_execute_via_cursor_scoped_metrics,
- rollup_metrics=_test_execute_via_cursor_rollup_metrics,
- background_task=True)
+ ("Datastore/all", 13),
+ ("Datastore/allOther", 13),
+ ("Datastore/Postgres/all", 13),
+ ("Datastore/Postgres/allOther", 13),
+ ("Datastore/operation/Postgres/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ ("Datastore/operation/Postgres/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ ("Datastore/operation/Postgres/update", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/update", 1),
+ ("Datastore/operation/Postgres/delete", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/delete", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/create", 1),
+ ("Datastore/statement/Postgres/now/call", 1),
+ ("Datastore/statement/Postgres/pg_sleep/call", 1),
+ ("Datastore/operation/Postgres/call", 2),
+ ("Datastore/operation/Postgres/commit", 3),
+ ("Datastore/operation/Postgres/rollback", 1),
+ (f"Datastore/instance/Postgres/{instance_hostname(DB_SETTINGS['host'])}/{DB_SETTINGS['port']}", 12),
+]
+
+
+@validate_transaction_metrics(
+ "test_database:test_execute_via_cursor",
+ scoped_metrics=_test_execute_via_cursor_scoped_metrics,
+ rollup_metrics=_test_execute_via_cursor_rollup_metrics,
+ background_task=True,
+)
@validate_database_trace_inputs(sql_parameters_type=tuple)
@background_task()
def test_execute_via_cursor():
with psycopg2cffi.connect(
- database=DB_SETTINGS['name'], user=DB_SETTINGS['user'],
- password=DB_SETTINGS['password'], host=DB_SETTINGS['host'],
- port=DB_SETTINGS['port']) as connection:
-
+ database=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ ) as connection:
cursor = connection.cursor()
psycopg2cffi.extensions.register_type(psycopg2cffi.extensions.UNICODE)
- psycopg2cffi.extensions.register_type(
- psycopg2cffi.extensions.UNICODE,
- connection)
- psycopg2cffi.extensions.register_type(
- psycopg2cffi.extensions.UNICODE,
- cursor)
+ psycopg2cffi.extensions.register_type(psycopg2cffi.extensions.UNICODE, connection)
+ psycopg2cffi.extensions.register_type(psycopg2cffi.extensions.UNICODE, cursor)
- cursor.execute("""drop table if exists %s""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""drop table if exists {DB_SETTINGS['table_name']}""")
- cursor.execute("""create table %s """ % DB_SETTINGS["table_name"] +
- """(a integer, b real, c text)""")
+ cursor.execute(f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)")
- cursor.executemany("""insert into %s """ % DB_SETTINGS["table_name"] +
- """values (%s, %s, %s)""", [(1, 1.0, '1.0'),
- (2, 2.2, '2.2'), (3, 3.3, '3.3')])
+ cursor.executemany(
+ f"insert into {DB_SETTINGS['table_name']} values (%s, %s, %s)",
+ [(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")],
+ )
- cursor.execute("""select * from %s""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""select * from {DB_SETTINGS['table_name']}""")
for row in cursor:
pass
- cursor.execute("""update %s""" % DB_SETTINGS["table_name"] + """ set a=%s, b=%s, """
- """c=%s where a=%s""", (4, 4.0, '4.0', 1))
+ cursor.execute(
+ f"update {DB_SETTINGS['table_name']} set a=%s, b=%s, c=%s where a=%s",
+ (4, 4.0, "4.0", 1),
+ )
- cursor.execute("""delete from %s where a=2""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""delete from {DB_SETTINGS['table_name']} where a=2""")
connection.commit()
- cursor.callproc('now')
- cursor.callproc('pg_sleep', (0,))
+ cursor.callproc("now")
+ cursor.callproc("pg_sleep", (0,))
connection.rollback()
connection.commit()
_test_rollback_on_exception_scoped_metrics = [
- ('Function/psycopg2cffi:connect', 1),
- ('Function/psycopg2cffi._impl.connection:Connection.__enter__', 1),
- ('Function/psycopg2cffi._impl.connection:Connection.__exit__', 1),
- ('Datastore/operation/Postgres/rollback', 1)]
+ ("Function/psycopg2cffi:connect", 1),
+ ("Function/psycopg2cffi._impl.connection:Connection.__enter__", 1),
+ ("Function/psycopg2cffi._impl.connection:Connection.__exit__", 1),
+ ("Datastore/operation/Postgres/rollback", 1),
+]
_test_rollback_on_exception_rollup_metrics = [
- ('Datastore/all', 2),
- ('Datastore/allOther', 2),
- ('Datastore/Postgres/all', 2),
- ('Datastore/Postgres/allOther', 2)]
-
-
-@validate_transaction_metrics('test_database:test_rollback_on_exception',
- scoped_metrics=_test_rollback_on_exception_scoped_metrics,
- rollup_metrics=_test_rollback_on_exception_rollup_metrics,
- background_task=True)
+ ("Datastore/all", 2),
+ ("Datastore/allOther", 2),
+ ("Datastore/Postgres/all", 2),
+ ("Datastore/Postgres/allOther", 2),
+]
+
+
+@validate_transaction_metrics(
+ "test_database:test_rollback_on_exception",
+ scoped_metrics=_test_rollback_on_exception_scoped_metrics,
+ rollup_metrics=_test_rollback_on_exception_rollup_metrics,
+ background_task=True,
+)
@validate_database_trace_inputs(sql_parameters_type=tuple)
@background_task()
def test_rollback_on_exception():
try:
with psycopg2cffi.connect(
- database=DB_SETTINGS['name'], user=DB_SETTINGS['user'],
- password=DB_SETTINGS['password'], host=DB_SETTINGS['host'],
- port=DB_SETTINGS['port']):
-
- raise RuntimeError('error')
+ database=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ ):
+ raise RuntimeError("error")
except RuntimeError:
pass
_test_async_mode_scoped_metrics = [
- ('Function/psycopg2cffi:connect', 1),
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS["table_name"], 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS["table_name"], 1),
- ('Datastore/operation/Postgres/drop', 1),
- ('Datastore/operation/Postgres/create', 1)]
+ ("Function/psycopg2cffi:connect", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/create", 1),
+]
_test_async_mode_rollup_metrics = [
- ('Datastore/all', 5),
- ('Datastore/allOther', 5),
- ('Datastore/Postgres/all', 5),
- ('Datastore/Postgres/allOther', 5),
- ('Datastore/operation/Postgres/select', 1),
- ('Datastore/statement/Postgres/%s/select' % DB_SETTINGS["table_name"], 1),
- ('Datastore/operation/Postgres/insert', 1),
- ('Datastore/statement/Postgres/%s/insert' % DB_SETTINGS["table_name"], 1),
- ('Datastore/operation/Postgres/drop', 1),
- ('Datastore/operation/Postgres/create', 1)]
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ ("Datastore/Postgres/all", 5),
+ ("Datastore/Postgres/allOther", 5),
+ ("Datastore/operation/Postgres/select", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/select", 1),
+ ("Datastore/operation/Postgres/insert", 1),
+ (f"Datastore/statement/Postgres/{DB_SETTINGS['table_name']}/insert", 1),
+ ("Datastore/operation/Postgres/drop", 1),
+ ("Datastore/operation/Postgres/create", 1),
+ (f"Datastore/instance/Postgres/{instance_hostname(DB_SETTINGS['host'])}/{DB_SETTINGS['port']}", 4),
+]
@validate_stats_engine_explain_plan_output_is_none()
@validate_transaction_slow_sql_count(num_slow_sql=4)
@validate_database_trace_inputs(sql_parameters_type=tuple)
-@validate_transaction_metrics('test_database:test_async_mode',
- scoped_metrics=_test_async_mode_scoped_metrics,
- rollup_metrics=_test_async_mode_rollup_metrics,
- background_task=True)
+@validate_transaction_metrics(
+ "test_database:test_async_mode",
+ scoped_metrics=_test_async_mode_scoped_metrics,
+ rollup_metrics=_test_async_mode_rollup_metrics,
+ background_task=True,
+)
@validate_transaction_errors(errors=[])
@background_task()
def test_async_mode():
-
wait = psycopg2cffi.extras.wait_select
kwargs = {}
- version = tuple(int(_) for _ in psycopg2cffi.__version__.split('.'))
+ version = get_package_version_tuple("psycopg2cffi")
+ assert version is not None
if version >= (2, 8):
- kwargs['async_'] = 1
+ kwargs["async_"] = 1
else:
- kwargs['async'] = 1
+ kwargs["async"] = 1
async_conn = psycopg2cffi.connect(
- database=DB_SETTINGS['name'], user=DB_SETTINGS['user'],
- password=DB_SETTINGS['password'], host=DB_SETTINGS['host'],
- port=DB_SETTINGS['port'], **kwargs
+ database=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ password=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ **kwargs
)
wait(async_conn)
async_cur = async_conn.cursor()
- async_cur.execute("""drop table if exists %s""" % DB_SETTINGS["table_name"])
+ async_cur.execute(f"""drop table if exists {DB_SETTINGS['table_name']}""")
wait(async_cur.connection)
- async_cur.execute("""create table %s """ % DB_SETTINGS["table_name"] +
- """(a integer, b real, c text)""")
+ async_cur.execute(f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)")
wait(async_cur.connection)
- async_cur.execute("""insert into %s """ % DB_SETTINGS["table_name"] +
- """values (%s, %s, %s)""", (1, 1.0, '1.0'))
+ async_cur.execute(f"insert into {DB_SETTINGS['table_name']} values (%s, %s, %s)", (1, 1.0, "1.0"))
wait(async_cur.connection)
- async_cur.execute("""select * from %s""" % DB_SETTINGS["table_name"])
+ async_cur.execute(f"""select * from {DB_SETTINGS['table_name']}""")
wait(async_cur.connection)
for row in async_cur:
diff --git a/tests/datastore_pylibmc/conftest.py b/tests/datastore_pylibmc/conftest.py
index 40970bdcae..093a522cb8 100644
--- a/tests/datastore_pylibmc/conftest.py
+++ b/tests/datastore_pylibmc/conftest.py
@@ -12,20 +12,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_pylibmc)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_pylibmc)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_pylibmc/test_memcache.py b/tests/datastore_pylibmc/test_memcache.py
index 769f3b483c..dd4cb9413e 100644
--- a/tests/datastore_pylibmc/test_memcache.py
+++ b/tests/datastore_pylibmc/test_memcache.py
@@ -12,85 +12,92 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
-
import pylibmc
-
from testing_support.db_settings import memcached_settings
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
from newrelic.api.transaction import set_background_task
-
DB_SETTINGS = memcached_settings()[0]
MEMCACHED_HOST = DB_SETTINGS["host"]
MEMCACHED_PORT = DB_SETTINGS["port"]
MEMCACHED_NAMESPACE = DB_SETTINGS["namespace"]
-MEMCACHED_ADDR = '%s:%s' % (MEMCACHED_HOST, MEMCACHED_PORT)
+MEMCACHED_ADDR = f"{MEMCACHED_HOST}:{MEMCACHED_PORT}"
_test_bt_set_get_delete_scoped_metrics = [
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
_test_bt_set_get_delete_rollup_metrics = [
- ('Datastore/all', 3),
- ('Datastore/allOther', 3),
- ('Datastore/Memcached/all', 3),
- ('Datastore/Memcached/allOther', 3),
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/all", 3),
+ ("Datastore/allOther", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allOther", 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
@validate_transaction_metrics(
- 'test_memcache:test_bt_set_get_delete',
- scoped_metrics=_test_bt_set_get_delete_scoped_metrics,
- rollup_metrics=_test_bt_set_get_delete_rollup_metrics,
- background_task=True)
+ "test_memcache:test_bt_set_get_delete",
+ scoped_metrics=_test_bt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_bt_set_get_delete_rollup_metrics,
+ background_task=True,
+)
@background_task()
def test_bt_set_get_delete():
set_background_task(True)
client = pylibmc.Client([MEMCACHED_ADDR])
- key = MEMCACHED_NAMESPACE + 'key'
+ key = f"{MEMCACHED_NAMESPACE}key"
- client.set(key, 'value')
+ client.set(key, "value")
value = client.get(key)
client.delete(key)
- assert value == 'value'
+ assert value == "value"
+
_test_wt_set_get_delete_scoped_metrics = [
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
_test_wt_set_get_delete_rollup_metrics = [
- ('Datastore/all', 3),
- ('Datastore/allWeb', 3),
- ('Datastore/Memcached/all', 3),
- ('Datastore/Memcached/allWeb', 3),
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/all", 3),
+ ("Datastore/allWeb", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allWeb", 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
+
@validate_transaction_metrics(
- 'test_memcache:test_wt_set_get_delete',
- scoped_metrics=_test_wt_set_get_delete_scoped_metrics,
- rollup_metrics=_test_wt_set_get_delete_rollup_metrics,
- background_task=False)
+ "test_memcache:test_wt_set_get_delete",
+ scoped_metrics=_test_wt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_wt_set_get_delete_rollup_metrics,
+ background_task=False,
+)
@background_task()
def test_wt_set_get_delete():
set_background_task(False)
client = pylibmc.Client([MEMCACHED_ADDR])
- key = MEMCACHED_NAMESPACE + 'key'
+ key = f"{MEMCACHED_NAMESPACE}key"
- client.set(key, 'value')
+ client.set(key, "value")
value = client.get(key)
client.delete(key)
- assert value == 'value'
+ assert value == "value"
diff --git a/tests/datastore_pymemcache/conftest.py b/tests/datastore_pymemcache/conftest.py
index 3d4e1ce766..22252eb9ab 100644
--- a/tests/datastore_pymemcache/conftest.py
+++ b/tests/datastore_pymemcache/conftest.py
@@ -12,20 +12,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_pymemcache)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_pymemcache)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_pymemcache/test_memcache.py b/tests/datastore_pymemcache/test_memcache.py
index 9aeea4d54d..66033af445 100644
--- a/tests/datastore_pymemcache/test_memcache.py
+++ b/tests/datastore_pymemcache/test_memcache.py
@@ -12,84 +12,98 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
-
import pymemcache.client
-
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
from testing_support.db_settings import memcached_settings
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
from newrelic.api.transaction import set_background_task
+from newrelic.common import system_info
+
DB_SETTINGS = memcached_settings()[0]
MEMCACHED_HOST = DB_SETTINGS["host"]
MEMCACHED_PORT = DB_SETTINGS["port"]
MEMCACHED_NAMESPACE = DB_SETTINGS["namespace"]
-
MEMCACHED_ADDR = (MEMCACHED_HOST, int(MEMCACHED_PORT))
+INSTANCE_METRIC_HOST = system_info.gethostname() if MEMCACHED_HOST == "127.0.0.1" else MEMCACHED_HOST
+INSTANCE_METRIC_NAME = f"Datastore/instance/Memcached/{INSTANCE_METRIC_HOST}/{MEMCACHED_PORT}"
+
_test_bt_set_get_delete_scoped_metrics = [
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
_test_bt_set_get_delete_rollup_metrics = [
- ('Datastore/all', 3),
- ('Datastore/allOther', 3),
- ('Datastore/Memcached/all', 3),
- ('Datastore/Memcached/allOther', 3),
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/all", 3),
+ ("Datastore/allOther", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allOther", 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+ (INSTANCE_METRIC_NAME, 3),
+]
+
@validate_transaction_metrics(
- 'test_memcache:test_bt_set_get_delete',
- scoped_metrics=_test_bt_set_get_delete_scoped_metrics,
- rollup_metrics=_test_bt_set_get_delete_rollup_metrics,
- background_task=True)
+ "test_memcache:test_bt_set_get_delete",
+ scoped_metrics=_test_bt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_bt_set_get_delete_rollup_metrics,
+ background_task=True,
+)
@background_task()
def test_bt_set_get_delete():
set_background_task(True)
client = pymemcache.client.Client(MEMCACHED_ADDR)
- key = MEMCACHED_NAMESPACE + 'key'
+ key = f"{MEMCACHED_NAMESPACE}key"
- client.set(key, b'value')
+ client.set(key, b"value")
value = client.get(key)
client.delete(key)
- assert value == b'value'
+ assert value == b"value"
+
_test_wt_set_get_delete_scoped_metrics = [
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+]
_test_wt_set_get_delete_rollup_metrics = [
- ('Datastore/all', 3),
- ('Datastore/allWeb', 3),
- ('Datastore/Memcached/all', 3),
- ('Datastore/Memcached/allWeb', 3),
- ('Datastore/operation/Memcached/set', 1),
- ('Datastore/operation/Memcached/get', 1),
- ('Datastore/operation/Memcached/delete', 1)]
+ ("Datastore/all", 3),
+ ("Datastore/allWeb", 3),
+ ("Datastore/Memcached/all", 3),
+ ("Datastore/Memcached/allWeb", 3),
+ ("Datastore/operation/Memcached/set", 1),
+ ("Datastore/operation/Memcached/get", 1),
+ ("Datastore/operation/Memcached/delete", 1),
+ (INSTANCE_METRIC_NAME, 3),
+]
+
@validate_transaction_metrics(
- 'test_memcache:test_wt_set_get_delete',
- scoped_metrics=_test_wt_set_get_delete_scoped_metrics,
- rollup_metrics=_test_wt_set_get_delete_rollup_metrics,
- background_task=False)
+ "test_memcache:test_wt_set_get_delete",
+ scoped_metrics=_test_wt_set_get_delete_scoped_metrics,
+ rollup_metrics=_test_wt_set_get_delete_rollup_metrics,
+ background_task=False,
+)
@background_task()
def test_wt_set_get_delete():
set_background_task(False)
client = pymemcache.client.Client(MEMCACHED_ADDR)
- key = MEMCACHED_NAMESPACE + 'key'
+ key = f"{MEMCACHED_NAMESPACE}key"
- client.set(key, b'value')
+ client.set(key, b"value")
value = client.get(key)
client.delete(key)
- assert value == b'value'
+ assert value == b"value"
diff --git a/tests/datastore_pymongo/conftest.py b/tests/datastore_pymongo/conftest.py
index d269182b03..0579578328 100644
--- a/tests/datastore_pymongo/conftest.py
+++ b/tests/datastore_pymongo/conftest.py
@@ -12,10 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
diff --git a/tests/datastore_pymongo/test_pymongo.py b/tests/datastore_pymongo/test_pymongo.py
index 4649062cee..6a0dfe5ef4 100644
--- a/tests/datastore_pymongo/test_pymongo.py
+++ b/tests/datastore_pymongo/test_pymongo.py
@@ -22,7 +22,6 @@
from testing_support.validators.validate_transaction_errors import validate_transaction_errors
from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
from newrelic.api.background_task import background_task
-from newrelic.packages import six
DB_SETTINGS = mongodb_settings()[0]
MONGODB_HOST = DB_SETTINGS["host"]
@@ -30,6 +29,15 @@
MONGODB_COLLECTION = DB_SETTINGS["collection"]
+# Find correct metric name based on import availability.
+try:
+ from pymongo.synchronous.mongo_client import MongoClient # noqa
+ INIT_FUNCTION_METRIC = "Function/pymongo.synchronous.mongo_client:MongoClient.__init__"
+except ImportError:
+ from pymongo.mongo_client import MongoClient # noqa
+ INIT_FUNCTION_METRIC = "Function/pymongo.mongo_client:MongoClient.__init__"
+
+
def _exercise_mongo_v3(db):
db[MONGODB_COLLECTION].save({"x": 10})
db[MONGODB_COLLECTION].save({"x": 8})
@@ -115,56 +123,56 @@ def _exercise_mongo(db):
_test_pymongo_scoped_metrics_v3 = [
- ("Function/pymongo.mongo_client:MongoClient.__init__", 1),
- ("Datastore/statement/MongoDB/%s/create_index" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find" % MONGODB_COLLECTION, 3),
- ("Datastore/statement/MongoDB/%s/find_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/save" % MONGODB_COLLECTION, 3),
- ("Datastore/statement/MongoDB/%s" % MONGODB_COLLECTION + "/initialize_unordered_bulk_op", 1),
- ("Datastore/statement/MongoDB/%s" % MONGODB_COLLECTION + "/initialize_ordered_bulk_op", 1),
- ("Datastore/statement/MongoDB/%s/parallel_scan" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/insert_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/bulk_write" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/insert_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/replace_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/update_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/create_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/list_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_delete" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_replace" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_update" % MONGODB_COLLECTION, 1),
+ (INIT_FUNCTION_METRIC, 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_index", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find", 3),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/save", 3),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/initialize_unordered_bulk_op", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/initialize_ordered_bulk_op", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/parallel_scan", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/bulk_write", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/replace_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/update_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/list_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_delete", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_replace", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_update", 1),
]
_test_pymongo_scoped_metrics_v4 = [
- ("Function/pymongo.mongo_client:MongoClient.__init__", 1),
- ("Datastore/statement/MongoDB/%s/create_index" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find" % MONGODB_COLLECTION, 3),
- ("Datastore/statement/MongoDB/%s/find_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/insert_one" % MONGODB_COLLECTION, 4),
- ("Datastore/statement/MongoDB/%s/bulk_write" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/insert_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/replace_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/update_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/create_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/list_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_delete" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_replace" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_update" % MONGODB_COLLECTION, 1),
+ (INIT_FUNCTION_METRIC, 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_index", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find", 3),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_one", 4),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/bulk_write", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/replace_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/update_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/list_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_delete", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_replace", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_update", 1),
]
_test_pymongo_rollup_metrics_v3 = [
- ("Function/pymongo.mongo_client:MongoClient.__init__", 1),
+ (INIT_FUNCTION_METRIC, 1),
("Datastore/all", 28),
("Datastore/allOther", 28),
("Datastore/MongoDB/all", 28),
@@ -172,17 +180,17 @@ def _exercise_mongo(db):
("Datastore/operation/MongoDB/create_index", 1),
("Datastore/operation/MongoDB/find", 3),
("Datastore/operation/MongoDB/find_one", 1),
- ("Datastore/statement/MongoDB/%s/create_index" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find" % MONGODB_COLLECTION, 3),
- ("Datastore/statement/MongoDB/%s/find_one" % MONGODB_COLLECTION, 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_index", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find", 3),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one", 1),
("Datastore/operation/MongoDB/save", 3),
("Datastore/operation/MongoDB/initialize_unordered_bulk_op", 1),
("Datastore/operation/MongoDB/initialize_ordered_bulk_op", 1),
("Datastore/operation/MongoDB/parallel_scan", 1),
- ("Datastore/statement/MongoDB/%s/save" % MONGODB_COLLECTION, 3),
- (("Datastore/statement/MongoDB/%s" % MONGODB_COLLECTION + "/initialize_unordered_bulk_op"), 1),
- (("Datastore/statement/MongoDB/%s" % MONGODB_COLLECTION + "/initialize_ordered_bulk_op"), 1),
- ("Datastore/statement/MongoDB/%s/parallel_scan" % MONGODB_COLLECTION, 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/save", 3),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/initialize_unordered_bulk_op", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/initialize_ordered_bulk_op", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/parallel_scan", 1),
("Datastore/operation/MongoDB/bulk_write", 1),
("Datastore/operation/MongoDB/insert_one", 1),
("Datastore/operation/MongoDB/insert_many", 1),
@@ -198,25 +206,25 @@ def _exercise_mongo(db):
("Datastore/operation/MongoDB/find_one_and_delete", 1),
("Datastore/operation/MongoDB/find_one_and_replace", 1),
("Datastore/operation/MongoDB/find_one_and_update", 1),
- ("Datastore/statement/MongoDB/%s/bulk_write" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/insert_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/insert_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/replace_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/update_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/create_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/list_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_delete" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_replace" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_update" % MONGODB_COLLECTION, 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/bulk_write", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/replace_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/update_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/list_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_delete", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_replace", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_update", 1),
]
_test_pymongo_rollup_metrics_v4 = [
- ("Function/pymongo.mongo_client:MongoClient.__init__", 1),
+ (INIT_FUNCTION_METRIC, 1),
("Datastore/all", 25),
("Datastore/allOther", 25),
("Datastore/MongoDB/all", 25),
@@ -224,9 +232,9 @@ def _exercise_mongo(db):
("Datastore/operation/MongoDB/create_index", 1),
("Datastore/operation/MongoDB/find", 3),
("Datastore/operation/MongoDB/find_one", 1),
- ("Datastore/statement/MongoDB/%s/create_index" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find" % MONGODB_COLLECTION, 3),
- ("Datastore/statement/MongoDB/%s/find_one" % MONGODB_COLLECTION, 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_index", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find", 3),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one", 1),
("Datastore/operation/MongoDB/bulk_write", 1),
("Datastore/operation/MongoDB/insert_one", 4),
("Datastore/operation/MongoDB/insert_many", 1),
@@ -242,21 +250,21 @@ def _exercise_mongo(db):
("Datastore/operation/MongoDB/find_one_and_delete", 1),
("Datastore/operation/MongoDB/find_one_and_replace", 1),
("Datastore/operation/MongoDB/find_one_and_update", 1),
- ("Datastore/statement/MongoDB/%s/bulk_write" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/insert_one" % MONGODB_COLLECTION, 4),
- ("Datastore/statement/MongoDB/%s/insert_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/replace_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/update_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_one" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/delete_many" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/create_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/list_indexes" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/aggregate_raw_batches" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_delete" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_replace" % MONGODB_COLLECTION, 1),
- ("Datastore/statement/MongoDB/%s/find_one_and_update" % MONGODB_COLLECTION, 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/bulk_write", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_one", 4),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/insert_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/replace_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/update_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_one", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/delete_many", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/create_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/list_indexes", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/aggregate_raw_batches", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_delete", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_replace", 1),
+ (f"Datastore/statement/MongoDB/{MONGODB_COLLECTION}/find_one_and_update", 1),
]
@@ -268,18 +276,16 @@ def test_mongodb_client_operation():
_test_pymongo_client_scoped_metrics = _test_pymongo_scoped_metrics_v4
_test_pymongo_client_rollup_metrics = _test_pymongo_rollup_metrics_v4
- txn_name = "test_pymongo:test_mongodb_client_operation.._test" if six.PY3 else "test_pymongo:_test"
-
@validate_transaction_errors(errors=[])
@validate_transaction_metrics(
- txn_name,
+ "test_pymongo:test_mongodb_client_operation.._test",
scoped_metrics=_test_pymongo_client_scoped_metrics,
rollup_metrics=_test_pymongo_client_rollup_metrics,
background_task=True,
)
@background_task()
def _test():
- client = pymongo.MongoClient(MONGODB_HOST, MONGODB_PORT)
+ client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client.test
_exercise_mongo(db)
@@ -289,7 +295,7 @@ def _test():
@validate_database_duration()
@background_task()
def test_mongodb_database_duration():
- client = pymongo.MongoClient(MONGODB_HOST, MONGODB_PORT)
+ client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client.test
_exercise_mongo(db)
@@ -300,7 +306,7 @@ def test_mongodb_and_sqlite_database_duration():
# Make mongodb queries
- client = pymongo.MongoClient(MONGODB_HOST, MONGODB_PORT)
+ client = MongoClient(MONGODB_HOST, MONGODB_PORT)
db = client.test
_exercise_mongo(db)
diff --git a/tests/datastore_pymssql/conftest.py b/tests/datastore_pymssql/conftest.py
new file mode 100644
index 0000000000..70285fe136
--- /dev/null
+++ b/tests/datastore_pymssql/conftest.py
@@ -0,0 +1,34 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
+
+_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
+}
+
+collector_agent_registration = collector_agent_registration_fixture(
+ app_name="Python Agent Test (datastore_pymssql)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_pymssql/test_database.py b/tests/datastore_pymssql/test_database.py
new file mode 100644
index 0000000000..ca11863b3b
--- /dev/null
+++ b/tests/datastore_pymssql/test_database.py
@@ -0,0 +1,114 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pymssql
+
+from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.validators.validate_database_trace_inputs import validate_database_trace_inputs
+
+from testing_support.db_settings import mssql_settings
+
+from newrelic.api.background_task import background_task
+
+DB_SETTINGS = mssql_settings()[0]
+TABLE_NAME = f"datastore_pymssql_{DB_SETTINGS['namespace']}"
+PROCEDURE_NAME = f"hello_{DB_SETTINGS['namespace']}"
+
+
+def execute_db_calls_with_cursor(cursor):
+ cursor.execute(f"""drop table if exists {TABLE_NAME}""")
+
+ cursor.execute(f"create table {TABLE_NAME} (a integer, b real, c text)")
+
+ cursor.executemany(
+ f"insert into {TABLE_NAME} values (%s, %s, %s)",
+ [(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")],
+ )
+
+ cursor.execute(f"""select * from {TABLE_NAME}""")
+
+ for row in cursor:
+ pass
+
+ cursor.execute(f"update {TABLE_NAME} set a=%s, b=%s, c=%s where a=%s", (4, 4.0, "4.0", 1))
+
+ cursor.execute(f"""delete from {TABLE_NAME} where a=2""")
+ cursor.execute(f"""drop procedure if exists {PROCEDURE_NAME}""")
+ cursor.execute(
+ f"""CREATE PROCEDURE {PROCEDURE_NAME} AS
+ BEGIN
+ SELECT 'Hello World!';
+ END"""
+ )
+
+ cursor.callproc(PROCEDURE_NAME)
+
+
+_test_scoped_metrics = [
+ ("Function/pymssql._pymssql:connect", 1),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/select", 1),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/insert", 1),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/update", 1),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/delete", 1),
+ ("Datastore/operation/MSSQL/drop", 2),
+ ("Datastore/operation/MSSQL/create", 2),
+ (f"Datastore/statement/MSSQL/{PROCEDURE_NAME}/call", 1),
+ ("Datastore/operation/MSSQL/commit", 2),
+ ("Datastore/operation/MSSQL/rollback", 1),
+]
+
+_test_rollup_metrics = [
+ ("Datastore/all", 13),
+ ("Datastore/allOther", 13),
+ ("Datastore/MSSQL/all", 13),
+ ("Datastore/MSSQL/allOther", 13),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/select", 1),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/insert", 1),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/update", 1),
+ (f"Datastore/statement/MSSQL/{TABLE_NAME}/delete", 1),
+ ("Datastore/operation/MSSQL/select", 1),
+ ("Datastore/operation/MSSQL/insert", 1),
+ ("Datastore/operation/MSSQL/update", 1),
+ ("Datastore/operation/MSSQL/delete", 1),
+ (f"Datastore/statement/MSSQL/{PROCEDURE_NAME}/call", 1),
+ ("Datastore/operation/MSSQL/call", 1),
+ ("Datastore/operation/MSSQL/drop", 2),
+ ("Datastore/operation/MSSQL/create", 2),
+ ("Datastore/operation/MSSQL/commit", 2),
+ ("Datastore/operation/MSSQL/rollback", 1),
+]
+
+
+@validate_transaction_metrics(
+ "test_database:test_execute_via_cursor_context_manager",
+ scoped_metrics=_test_scoped_metrics,
+ rollup_metrics=_test_rollup_metrics,
+ background_task=True,
+)
+@validate_database_trace_inputs(sql_parameters_type=tuple)
+@background_task()
+def test_execute_via_cursor_context_manager():
+ connection = pymssql.connect(
+ user=DB_SETTINGS["user"], password=DB_SETTINGS["password"], host=DB_SETTINGS["host"], port=DB_SETTINGS["port"]
+ )
+
+ with connection:
+ cursor = connection.cursor()
+
+ with cursor:
+ execute_db_calls_with_cursor(cursor)
+
+ connection.commit()
+ connection.rollback()
+ connection.commit()
diff --git a/tests/datastore_pymysql/conftest.py b/tests/datastore_pymysql/conftest.py
index 51d037432d..45d6efc895 100644
--- a/tests/datastore_pymysql/conftest.py
+++ b/tests/datastore_pymysql/conftest.py
@@ -12,21 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
- 'debug.log_explain_plan_queries': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_pymysql)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_pymysql)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_pymysql/test_database.py b/tests/datastore_pymysql/test_database.py
index 5943b12665..ef55592f0c 100644
--- a/tests/datastore_pymysql/test_database.py
+++ b/tests/datastore_pymysql/test_database.py
@@ -13,88 +13,106 @@
# limitations under the License.
import pymysql
-
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_database_trace_inputs import validate_database_trace_inputs
-
from testing_support.db_settings import mysql_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_database_trace_inputs import (
+ validate_database_trace_inputs,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
DB_SETTINGS = mysql_settings()[0]
-TABLE_NAME = "datastore_pymysql_" + DB_SETTINGS["namespace"]
-PROCEDURE_NAME = "hello_" + DB_SETTINGS["namespace"]
+TABLE_NAME = f"datastore_pymysql_{DB_SETTINGS['namespace']}"
+PROCEDURE_NAME = f"hello_{DB_SETTINGS['namespace']}"
+
+HOST = instance_hostname(DB_SETTINGS["host"])
+PORT = DB_SETTINGS["port"]
def execute_db_calls_with_cursor(cursor):
- cursor.execute("""drop table if exists %s""" % TABLE_NAME)
+ cursor.execute(f"""drop table if exists {TABLE_NAME}""")
- cursor.execute("""create table %s """ % TABLE_NAME +
- """(a integer, b real, c text)""")
+ cursor.execute(f"create table {TABLE_NAME} (a integer, b real, c text)")
- cursor.executemany("""insert into %s """ % TABLE_NAME +
- """values (%s, %s, %s)""", [(1, 1.0, '1.0'),
- (2, 2.2, '2.2'), (3, 3.3, '3.3')])
+ cursor.executemany(
+ f"insert into {TABLE_NAME} values (%s, %s, %s)",
+ [(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")],
+ )
- cursor.execute("""select * from %s""" % TABLE_NAME)
+ cursor.execute(f"""select * from {TABLE_NAME}""")
- for row in cursor: pass
+ for row in cursor:
+ pass
- cursor.execute("""update %s""" % TABLE_NAME + """ set a=%s, b=%s, """
- """c=%s where a=%s""", (4, 4.0, '4.0', 1))
+ cursor.execute(f"update {TABLE_NAME} set a=%s, b=%s, c=%s where a=%s", (4, 4.0, "4.0", 1))
- cursor.execute("""delete from %s where a=2""" % TABLE_NAME)
- cursor.execute("""drop procedure if exists %s""" % PROCEDURE_NAME)
- cursor.execute("""CREATE PROCEDURE %s()
+ cursor.execute(f"""delete from {TABLE_NAME} where a=2""")
+ cursor.execute(f"""drop procedure if exists {PROCEDURE_NAME}""")
+ cursor.execute(
+ f"""CREATE PROCEDURE {PROCEDURE_NAME}()
BEGIN
SELECT 'Hello World!';
- END""" % PROCEDURE_NAME)
+ END"""
+ )
cursor.callproc(PROCEDURE_NAME)
_test_execute_via_cursor_scoped_metrics = [
- ('Function/pymysql:Connect', 1),
- ('Datastore/statement/MySQL/%s/select' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/insert' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/update' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/delete' % TABLE_NAME, 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/statement/MySQL/%s/call' % PROCEDURE_NAME, 1),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
+ ("Function/pymysql:Connect", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/select", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/insert", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/update", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/delete", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ (f"Datastore/statement/MySQL/{PROCEDURE_NAME}/call", 1),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+]
_test_execute_via_cursor_rollup_metrics = [
- ('Datastore/all', 13),
- ('Datastore/allOther', 13),
- ('Datastore/MySQL/all', 13),
- ('Datastore/MySQL/allOther', 13),
- ('Datastore/statement/MySQL/%s/select' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/insert' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/update' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/delete' % TABLE_NAME, 1),
- ('Datastore/operation/MySQL/select', 1),
- ('Datastore/operation/MySQL/insert', 1),
- ('Datastore/operation/MySQL/update', 1),
- ('Datastore/operation/MySQL/delete', 1),
- ('Datastore/statement/MySQL/%s/call' % PROCEDURE_NAME, 1),
- ('Datastore/operation/MySQL/call', 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
-
-@validate_transaction_metrics('test_database:test_execute_via_cursor',
- scoped_metrics=_test_execute_via_cursor_scoped_metrics,
- rollup_metrics=_test_execute_via_cursor_rollup_metrics,
- background_task=True)
+ ("Datastore/all", 13),
+ ("Datastore/allOther", 13),
+ ("Datastore/MySQL/all", 13),
+ ("Datastore/MySQL/allOther", 13),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/select", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/insert", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/update", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/delete", 1),
+ ("Datastore/operation/MySQL/select", 1),
+ ("Datastore/operation/MySQL/insert", 1),
+ ("Datastore/operation/MySQL/update", 1),
+ ("Datastore/operation/MySQL/delete", 1),
+ (f"Datastore/statement/MySQL/{PROCEDURE_NAME}/call", 1),
+ ("Datastore/operation/MySQL/call", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+ (f"Datastore/instance/MySQL/{HOST}/{PORT}", 12),
+]
+
+
+@validate_transaction_metrics(
+ "test_database:test_execute_via_cursor",
+ scoped_metrics=_test_execute_via_cursor_scoped_metrics,
+ rollup_metrics=_test_execute_via_cursor_rollup_metrics,
+ background_task=True,
+)
@validate_database_trace_inputs(sql_parameters_type=tuple)
@background_task()
def test_execute_via_cursor():
- connection = pymysql.connect(db=DB_SETTINGS['name'],
- user=DB_SETTINGS['user'], passwd=DB_SETTINGS['password'],
- host=DB_SETTINGS['host'], port=DB_SETTINGS['port'])
+ connection = pymysql.connect(
+ db=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ passwd=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
with connection.cursor() as cursor:
execute_db_calls_with_cursor(cursor)
@@ -105,49 +123,57 @@ def test_execute_via_cursor():
_test_execute_via_cursor_context_mangaer_scoped_metrics = [
- ('Function/pymysql:Connect', 1),
- ('Datastore/statement/MySQL/%s/select' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/insert' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/update' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/delete' % TABLE_NAME, 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/statement/MySQL/%s/call' % PROCEDURE_NAME, 1),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
+ ("Function/pymysql:Connect", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/select", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/insert", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/update", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/delete", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ (f"Datastore/statement/MySQL/{PROCEDURE_NAME}/call", 1),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+]
_test_execute_via_cursor_context_mangaer_rollup_metrics = [
- ('Datastore/all', 13),
- ('Datastore/allOther', 13),
- ('Datastore/MySQL/all', 13),
- ('Datastore/MySQL/allOther', 13),
- ('Datastore/statement/MySQL/%s/select' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/insert' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/update' % TABLE_NAME, 1),
- ('Datastore/statement/MySQL/%s/delete' % TABLE_NAME, 1),
- ('Datastore/operation/MySQL/select', 1),
- ('Datastore/operation/MySQL/insert', 1),
- ('Datastore/operation/MySQL/update', 1),
- ('Datastore/operation/MySQL/delete', 1),
- ('Datastore/statement/MySQL/%s/call' % PROCEDURE_NAME, 1),
- ('Datastore/operation/MySQL/call', 1),
- ('Datastore/operation/MySQL/drop', 2),
- ('Datastore/operation/MySQL/create', 2),
- ('Datastore/operation/MySQL/commit', 2),
- ('Datastore/operation/MySQL/rollback', 1)]
+ ("Datastore/all", 13),
+ ("Datastore/allOther", 13),
+ ("Datastore/MySQL/all", 13),
+ ("Datastore/MySQL/allOther", 13),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/select", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/insert", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/update", 1),
+ (f"Datastore/statement/MySQL/{TABLE_NAME}/delete", 1),
+ ("Datastore/operation/MySQL/select", 1),
+ ("Datastore/operation/MySQL/insert", 1),
+ ("Datastore/operation/MySQL/update", 1),
+ ("Datastore/operation/MySQL/delete", 1),
+ (f"Datastore/statement/MySQL/{PROCEDURE_NAME}/call", 1),
+ ("Datastore/operation/MySQL/call", 1),
+ ("Datastore/operation/MySQL/drop", 2),
+ ("Datastore/operation/MySQL/create", 2),
+ ("Datastore/operation/MySQL/commit", 2),
+ ("Datastore/operation/MySQL/rollback", 1),
+ (f"Datastore/instance/MySQL/{HOST}/{PORT}", 12),
+]
@validate_transaction_metrics(
- 'test_database:test_execute_via_cursor_context_manager',
- scoped_metrics=_test_execute_via_cursor_context_mangaer_scoped_metrics,
- rollup_metrics=_test_execute_via_cursor_context_mangaer_rollup_metrics,
- background_task=True)
+ "test_database:test_execute_via_cursor_context_manager",
+ scoped_metrics=_test_execute_via_cursor_context_mangaer_scoped_metrics,
+ rollup_metrics=_test_execute_via_cursor_context_mangaer_rollup_metrics,
+ background_task=True,
+)
@validate_database_trace_inputs(sql_parameters_type=tuple)
@background_task()
def test_execute_via_cursor_context_manager():
- connection = pymysql.connect(db=DB_SETTINGS['name'],
- user=DB_SETTINGS['user'], passwd=DB_SETTINGS['password'],
- host=DB_SETTINGS['host'], port=DB_SETTINGS['port'])
+ connection = pymysql.connect(
+ db=DB_SETTINGS["name"],
+ user=DB_SETTINGS["user"],
+ passwd=DB_SETTINGS["password"],
+ host=DB_SETTINGS["host"],
+ port=DB_SETTINGS["port"],
+ )
cursor = connection.cursor()
with cursor:
diff --git a/tests/datastore_pyodbc/conftest.py b/tests/datastore_pyodbc/conftest.py
index b00a0a663f..dd646b0625 100644
--- a/tests/datastore_pyodbc/conftest.py
+++ b/tests/datastore_pyodbc/conftest.py
@@ -18,6 +18,7 @@
)
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
diff --git a/tests/datastore_pyodbc/test_pyodbc.py b/tests/datastore_pyodbc/test_pyodbc.py
index 119908e4db..96f9b12416 100644
--- a/tests/datastore_pyodbc/test_pyodbc.py
+++ b/tests/datastore_pyodbc/test_pyodbc.py
@@ -13,6 +13,7 @@
# limitations under the License.
import pytest
from testing_support.db_settings import postgresql_settings
+from testing_support.util import instance_hostname
from testing_support.validators.validate_database_trace_inputs import (
validate_database_trace_inputs,
)
@@ -55,20 +56,20 @@ def test_execute_via_cursor(pyodbc_driver):
)
) as connection:
cursor = connection.cursor()
- cursor.execute("""drop table if exists %s""" % DB_SETTINGS["table_name"])
- cursor.execute("""create table %s """ % DB_SETTINGS["table_name"] + """(a integer, b real, c text)""")
+ cursor.execute(f"""drop table if exists {DB_SETTINGS['table_name']}""")
+ cursor.execute(f"create table {DB_SETTINGS['table_name']} (a integer, b real, c text)")
cursor.executemany(
- """insert into %s """ % DB_SETTINGS["table_name"] + """values (?, ?, ?)""",
+ f"insert into {DB_SETTINGS['table_name']} values (?, ?, ?)",
[(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")],
)
- cursor.execute("""select * from %s""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""select * from {DB_SETTINGS['table_name']}""")
for row in cursor:
pass
cursor.execute(
- """update %s """ % DB_SETTINGS["table_name"] + """set a=?, b=?, c=? where a=?""",
+ f"update {DB_SETTINGS['table_name']} set a=?, b=?, c=? where a=?",
(4, 4.0, "4.0", 1),
)
- cursor.execute("""delete from %s where a=2""" % DB_SETTINGS["table_name"])
+ cursor.execute(f"""delete from {DB_SETTINGS['table_name']} where a=2""")
connection.commit()
cursor.execute("SELECT now()")
diff --git a/tests/datastore_pysolr/conftest.py b/tests/datastore_pysolr/conftest.py
index 07851b6981..6886f506a8 100644
--- a/tests/datastore_pysolr/conftest.py
+++ b/tests/datastore_pysolr/conftest.py
@@ -12,20 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_pysolr)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_pysolr)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_pysolr/test_solr.py b/tests/datastore_pysolr/test_solr.py
index a987a29ac9..b47fa4e11b 100644
--- a/tests/datastore_pysolr/test_solr.py
+++ b/tests/datastore_pysolr/test_solr.py
@@ -13,48 +13,57 @@
# limitations under the License.
from pysolr import Solr
-
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
from testing_support.db_settings import solr_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
DB_SETTINGS = solr_settings()[0]
SOLR_HOST = DB_SETTINGS["host"]
SOLR_PORT = DB_SETTINGS["port"]
-SOLR_URL = 'http://%s:%s/solr/collection' % (DB_SETTINGS["host"], DB_SETTINGS["port"])
+SOLR_URL = f"http://{DB_SETTINGS['host']}:{DB_SETTINGS['port']}/solr/collection"
+
def _exercise_solr(solr):
# Construct document names within namespace
documents = ["pysolr_doc_1", "pysolr_doc_2"]
- documents = [x + "_" + DB_SETTINGS["namespace"] for x in documents]
+ documents = [f"{x}_{DB_SETTINGS['namespace']}" for x in documents]
solr.add([{"id": x} for x in documents])
- solr.search('id:%s' % documents[0])
+ solr.search(f"id:{documents[0]}")
solr.delete(id=documents[0])
# Delete all documents.
- solr.delete(q='id:*_%s' % DB_SETTINGS["namespace"])
+ solr.delete(q=f"id:*_{DB_SETTINGS['namespace']}")
+
_test_solr_search_scoped_metrics = [
- ('Datastore/operation/Solr/add', 1),
- ('Datastore/operation/Solr/delete', 2),
- ('Datastore/operation/Solr/search', 1)]
+ ("Datastore/operation/Solr/add", 1),
+ ("Datastore/operation/Solr/delete", 2),
+ ("Datastore/operation/Solr/search", 1),
+]
_test_solr_search_rollup_metrics = [
- ('Datastore/all', 4),
- ('Datastore/allOther', 4),
- ('Datastore/Solr/all', 4),
- ('Datastore/Solr/allOther', 4),
- ('Datastore/operation/Solr/add', 1),
- ('Datastore/operation/Solr/search', 1),
- ('Datastore/operation/Solr/delete', 2)]
-
-@validate_transaction_metrics('test_solr:test_solr_search',
+ ("Datastore/all", 4),
+ ("Datastore/allOther", 4),
+ ("Datastore/Solr/all", 4),
+ ("Datastore/Solr/allOther", 4),
+ ("Datastore/operation/Solr/add", 1),
+ ("Datastore/operation/Solr/search", 1),
+ ("Datastore/operation/Solr/delete", 2),
+]
+
+
+@validate_transaction_metrics(
+ "test_solr:test_solr_search",
scoped_metrics=_test_solr_search_scoped_metrics,
rollup_metrics=_test_solr_search_rollup_metrics,
- background_task=True)
+ background_task=True,
+)
@background_task()
def test_solr_search():
s = Solr(SOLR_URL)
diff --git a/tests/datastore_redis/conftest.py b/tests/datastore_redis/conftest.py
index 53ff2658de..e939e15c32 100644
--- a/tests/datastore_redis/conftest.py
+++ b/tests/datastore_redis/conftest.py
@@ -12,20 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixture.event_loop import ( # noqa: F401; pylint: disable=W0611
+ event_loop as loop,
+)
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_redis)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_redis)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_redis/test_asyncio.py b/tests/datastore_redis/test_asyncio.py
new file mode 100644
index 0000000000..47eec2d1d9
--- /dev/null
+++ b/tests/datastore_redis/test_asyncio.py
@@ -0,0 +1,160 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import asyncio
+
+import pytest
+from testing_support.db_settings import redis_settings
+from testing_support.fixture.event_loop import event_loop as loop # noqa: F401
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
+
+# Settings
+
+DB_SETTINGS = redis_settings()[0]
+REDIS_PY_VERSION = get_package_version_tuple("redis")
+
+# Metrics for publish test
+
+datastore_all_metric_count = 5 if REDIS_PY_VERSION >= (5, 0) else 3
+
+_base_scoped_metrics = [("Datastore/operation/Redis/publish", 3)]
+
+if REDIS_PY_VERSION >= (5, 0):
+ _base_scoped_metrics.append(
+ ("Datastore/operation/Redis/client_setinfo", 2),
+ )
+
+_base_rollup_metrics = [
+ ("Datastore/all", datastore_all_metric_count),
+ ("Datastore/allOther", datastore_all_metric_count),
+ ("Datastore/Redis/all", datastore_all_metric_count),
+ ("Datastore/Redis/allOther", datastore_all_metric_count),
+ ("Datastore/operation/Redis/publish", 3),
+ (
+ f"Datastore/instance/Redis/{instance_hostname(DB_SETTINGS['host'])}/{DB_SETTINGS['port']}",
+ datastore_all_metric_count,
+ ),
+]
+if REDIS_PY_VERSION >= (5, 0):
+ _base_rollup_metrics.append(
+ ("Datastore/operation/Redis/client_setinfo", 2),
+ )
+
+
+# Metrics for connection pool test
+
+_base_pool_scoped_metrics = [
+ ("Datastore/operation/Redis/get", 1),
+ ("Datastore/operation/Redis/set", 1),
+ ("Datastore/operation/Redis/client_list", 1),
+]
+
+_base_pool_rollup_metrics = [
+ ("Datastore/all", 3),
+ ("Datastore/allOther", 3),
+ ("Datastore/Redis/all", 3),
+ ("Datastore/Redis/allOther", 3),
+ ("Datastore/operation/Redis/get", 1),
+ ("Datastore/operation/Redis/set", 1),
+ ("Datastore/operation/Redis/client_list", 1),
+ (f"Datastore/instance/Redis/{instance_hostname(DB_SETTINGS['host'])}/{DB_SETTINGS['port']}", 3),
+]
+
+
+# Tests
+
+
+@pytest.fixture()
+def client(loop): # noqa
+ import redis.asyncio
+
+ return loop.run_until_complete(redis.asyncio.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0))
+
+
+@pytest.fixture()
+def client_pool(loop): # noqa
+ import redis.asyncio
+
+ connection_pool = redis.asyncio.ConnectionPool(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
+ return loop.run_until_complete(redis.asyncio.Redis(connection_pool=connection_pool))
+
+
+@pytest.mark.skipif(REDIS_PY_VERSION < (4, 2), reason="This functionality exists in Redis 4.2+")
+@validate_transaction_metrics(
+ "test_asyncio:test_async_connection_pool",
+ scoped_metrics=_base_pool_scoped_metrics,
+ rollup_metrics=_base_pool_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_async_connection_pool(client_pool, loop): # noqa
+ async def _test_async_pool(client_pool):
+ await client_pool.set("key1", "value1")
+ await client_pool.get("key1")
+ await client_pool.execute_command("CLIENT", "LIST")
+
+ loop.run_until_complete(_test_async_pool(client_pool))
+
+
+@pytest.mark.skipif(REDIS_PY_VERSION < (4, 2), reason="This functionality exists in Redis 4.2+")
+@validate_transaction_metrics("test_asyncio:test_async_pipeline", background_task=True)
+@background_task()
+def test_async_pipeline(client, loop): # noqa
+ async def _test_pipeline(client):
+ async with client.pipeline(transaction=True) as pipe:
+ await pipe.set("key1", "value1")
+ await pipe.execute()
+
+ loop.run_until_complete(_test_pipeline(client))
+
+
+@pytest.mark.skipif(REDIS_PY_VERSION < (4, 2), reason="This functionality exists in Redis 4.2+")
+@validate_transaction_metrics(
+ "test_asyncio:test_async_pubsub",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_base_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_async_pubsub(client, loop): # noqa
+ messages_received = []
+
+ async def reader(pubsub):
+ while True:
+ message = await pubsub.get_message(ignore_subscribe_messages=True)
+ if message:
+ messages_received.append(message["data"].decode())
+ if message["data"].decode() == "NOPE":
+ break
+
+ async def _test_pubsub():
+ async with client.pubsub() as pubsub:
+ await pubsub.psubscribe("channel:*")
+
+ future = asyncio.create_task(reader(pubsub))
+
+ await client.publish("channel:1", "Hello")
+ await client.publish("channel:2", "World")
+ await client.publish("channel:1", "NOPE")
+
+ await future
+
+ loop.run_until_complete(_test_pubsub())
+ assert messages_received == ["Hello", "World", "NOPE"]
diff --git a/tests/datastore_redis/test_custom_conn_pool.py b/tests/datastore_redis/test_custom_conn_pool.py
index 156c9ce31f..70954f2ce3 100644
--- a/tests/datastore_redis/test_custom_conn_pool.py
+++ b/tests/datastore_redis/test_custom_conn_pool.py
@@ -12,26 +12,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-''' The purpose of these tests is to confirm that using a non-standard
+""" The purpose of these tests is to confirm that using a non-standard
connection pool that does not have a `connection_kwargs` attribute
will not result in an error.
-'''
+"""
import pytest
import redis
-
-from newrelic.api.background_task import background_task
-
-from testing_support.fixtures import override_application_settings
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
from testing_support.db_settings import redis_settings
+from testing_support.fixtures import override_application_settings
from testing_support.util import instance_hostname
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
DB_SETTINGS = redis_settings()[0]
-REDIS_PY_VERSION = redis.VERSION
+REDIS_PY_VERSION = get_package_version_tuple("redis")
-class FakeConnectionPool(object):
+class FakeConnectionPool():
"""Connection Pool without connection_kwargs attribute."""
def __init__(self, connection):
@@ -43,112 +45,120 @@ def get_connection(self, name, *keys, **options):
def release(self, connection):
self.connection.disconnect()
+ def disconnect(self):
+ self.connection.disconnect()
+
+
# Settings
_enable_instance_settings = {
- 'datastore_tracer.instance_reporting.enabled': True,
+ "datastore_tracer.instance_reporting.enabled": True,
}
_disable_instance_settings = {
- 'datastore_tracer.instance_reporting.enabled': False,
+ "datastore_tracer.instance_reporting.enabled": False,
}
# Metrics
# We don't record instance metrics when using redis blaster,
# so we just check for base metrics.
-
-_base_scoped_metrics = (
- ('Datastore/operation/Redis/get', 1),
- ('Datastore/operation/Redis/set', 1),
- ('Datastore/operation/Redis/client_list', 1),
-)
-
-_base_rollup_metrics = (
- ('Datastore/all', 3),
- ('Datastore/allOther', 3),
- ('Datastore/Redis/all', 3),
- ('Datastore/Redis/allOther', 3),
- ('Datastore/operation/Redis/get', 1),
- ('Datastore/operation/Redis/set', 1),
- ('Datastore/operation/Redis/client_list', 1),
-)
-
-_disable_scoped_metrics = list(_base_scoped_metrics)
-_disable_rollup_metrics = list(_base_rollup_metrics)
-
-_enable_scoped_metrics = list(_base_scoped_metrics)
-_enable_rollup_metrics = list(_base_rollup_metrics)
-
-_host = instance_hostname(DB_SETTINGS['host'])
-_port = DB_SETTINGS['port']
-
-_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
-
-_enable_rollup_metrics.append(
- (_instance_metric_name, 3)
-)
-
-_disable_rollup_metrics.append(
- (_instance_metric_name, None)
-)
+datastore_all_metric_count = 5 if REDIS_PY_VERSION >= (5, 0) else 3
+
+_base_scoped_metrics = [
+ ("Datastore/operation/Redis/get", 1),
+ ("Datastore/operation/Redis/set", 1),
+ ("Datastore/operation/Redis/client_list", 1),
+]
+# client_setinfo was introduced in v5.0.0 and assigns info displayed in client_list output
+if REDIS_PY_VERSION >= (5, 0):
+ _base_scoped_metrics.append(
+ ("Datastore/operation/Redis/client_setinfo", 2),
+ )
+
+_base_rollup_metrics = [
+ ("Datastore/all", datastore_all_metric_count),
+ ("Datastore/allOther", datastore_all_metric_count),
+ ("Datastore/Redis/all", datastore_all_metric_count),
+ ("Datastore/Redis/allOther", datastore_all_metric_count),
+ ("Datastore/operation/Redis/get", 1),
+ ("Datastore/operation/Redis/set", 1),
+ ("Datastore/operation/Redis/client_list", 1),
+]
+if REDIS_PY_VERSION >= (5, 0):
+ _base_rollup_metrics.append(
+ ("Datastore/operation/Redis/client_setinfo", 2),
+ )
+
+_host = instance_hostname(DB_SETTINGS["host"])
+_port = DB_SETTINGS["port"]
+
+_instance_metric_name = f"Datastore/instance/Redis/{_host}/{_port}"
+
+instance_metric_count = 5 if REDIS_PY_VERSION >= (5, 0) else 3
+
+_enable_rollup_metrics = _base_rollup_metrics.append((_instance_metric_name, instance_metric_count))
+
+_disable_rollup_metrics = _base_rollup_metrics.append((_instance_metric_name, None))
# Operations
+
def exercise_redis(client):
- client.set('key', 'value')
- client.get('key')
- client.execute_command('CLIENT', 'LIST', parse='LIST')
+ client.set("key", "value")
+ client.get("key")
+ client.execute_command("CLIENT", "LIST", parse="LIST")
+
# Tests
-@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7),
- reason='Client list command introduced in 2.7')
+
+@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7), reason="Client list command introduced in 2.7")
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
- 'test_custom_conn_pool:test_fake_conn_pool_enable_instance',
- scoped_metrics=_enable_scoped_metrics,
- rollup_metrics=_enable_rollup_metrics,
- background_task=True)
+ "test_custom_conn_pool:test_fake_conn_pool_enable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
@background_task()
def test_fake_conn_pool_enable_instance():
- client = redis.StrictRedis(host=DB_SETTINGS['host'],
- port=DB_SETTINGS['port'], db=0)
+ client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
# Get a real connection
- conn = client.connection_pool.get_connection('GET')
+ conn = client.connection_pool.get_connection("GET")
# Replace the original connection pool with one that doesn't
# have the `connection_kwargs` attribute.
fake_pool = FakeConnectionPool(conn)
client.connection_pool = fake_pool
- assert not hasattr(client.connection_pool, 'connection_kwargs')
+ assert not hasattr(client.connection_pool, "connection_kwargs")
exercise_redis(client)
-@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7),
- reason='Client list command introduced in 2.7')
+
+@pytest.mark.skipif(REDIS_PY_VERSION < (2, 7), reason="Client list command introduced in 2.7")
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
- 'test_custom_conn_pool:test_fake_conn_pool_disable_instance',
- scoped_metrics=_disable_scoped_metrics,
- rollup_metrics=_disable_rollup_metrics,
- background_task=True)
+ "test_custom_conn_pool:test_fake_conn_pool_disable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+)
@background_task()
def test_fake_conn_pool_disable_instance():
- client = redis.StrictRedis(host=DB_SETTINGS['host'],
- port=DB_SETTINGS['port'], db=0)
+ client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
# Get a real connection
- conn = client.connection_pool.get_connection('GET')
+ conn = client.connection_pool.get_connection("GET")
# Replace the original connection pool with one that doesn't
# have the `connection_kwargs` attribute.
fake_pool = FakeConnectionPool(conn)
client.connection_pool = fake_pool
- assert not hasattr(client.connection_pool, 'connection_kwargs')
+ assert not hasattr(client.connection_pool, "connection_kwargs")
exercise_redis(client)
diff --git a/tests/datastore_redis/test_execute_command.py b/tests/datastore_redis/test_execute_command.py
index 7475880725..ebc52d32e0 100644
--- a/tests/datastore_redis/test_execute_command.py
+++ b/tests/datastore_redis/test_execute_command.py
@@ -16,6 +16,7 @@
import redis
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
from testing_support.fixtures import override_application_settings
from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
@@ -23,7 +24,8 @@
from testing_support.util import instance_hostname
DB_SETTINGS = redis_settings()[0]
-REDIS_PY_VERSION = redis.VERSION
+REDIS_PY_VERSION = get_package_version_tuple("redis")
+
# Settings
@@ -36,34 +38,34 @@
# Metrics
-_base_scoped_metrics = (
+_base_scoped_metrics = [
('Datastore/operation/Redis/client_list', 1),
-)
-
-_base_rollup_metrics = (
- ('Datastore/all', 1),
- ('Datastore/allOther', 1),
- ('Datastore/Redis/all', 1),
- ('Datastore/Redis/allOther', 1),
+]
+if REDIS_PY_VERSION >= (5, 0):
+ _base_scoped_metrics.append(('Datastore/operation/Redis/client_setinfo', 2),)
+
+_base_rollup_metrics = [
+ ('Datastore/all', 3),
+ ('Datastore/allOther', 3),
+ ('Datastore/Redis/all', 3),
+ ('Datastore/Redis/allOther', 3),
('Datastore/operation/Redis/client_list', 1),
-)
-
-_disable_scoped_metrics = list(_base_scoped_metrics)
-_disable_rollup_metrics = list(_base_rollup_metrics)
-
-_enable_scoped_metrics = list(_base_scoped_metrics)
-_enable_rollup_metrics = list(_base_rollup_metrics)
+]
+if REDIS_PY_VERSION >= (5, 0):
+ _base_rollup_metrics.append(('Datastore/operation/Redis/client_setinfo', 2),)
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Redis/{_host}/{_port}'
+
+instance_metric_count = 3 if REDIS_PY_VERSION >= (5, 0) else 1
-_enable_rollup_metrics.append(
- (_instance_metric_name, 1)
+_enable_rollup_metrics = _base_rollup_metrics.append(
+ (_instance_metric_name, instance_metric_count)
)
-_disable_rollup_metrics.append(
+_disable_rollup_metrics = _base_rollup_metrics.append(
(_instance_metric_name, None)
)
@@ -76,7 +78,7 @@ def exercise_redis_single_arg(client):
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_strict_redis_execute_command_two_args_enable',
- scoped_metrics=_enable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@background_task()
@@ -88,7 +90,7 @@ def test_strict_redis_execute_command_two_args_enable():
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_strict_redis_execute_command_two_args_disabled',
- scoped_metrics=_disable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@background_task()
@@ -100,7 +102,7 @@ def test_strict_redis_execute_command_two_args_disabled():
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_redis_execute_command_two_args_enable',
- scoped_metrics=_enable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@background_task()
@@ -112,7 +114,7 @@ def test_redis_execute_command_two_args_enable():
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_redis_execute_command_two_args_disabled',
- scoped_metrics=_disable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@background_task()
@@ -126,7 +128,7 @@ def test_redis_execute_command_two_args_disabled():
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_strict_redis_execute_command_as_one_arg_enable',
- scoped_metrics=_enable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@background_task()
@@ -140,7 +142,7 @@ def test_strict_redis_execute_command_as_one_arg_enable():
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_strict_redis_execute_command_as_one_arg_disabled',
- scoped_metrics=_disable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@background_task()
@@ -154,7 +156,7 @@ def test_strict_redis_execute_command_as_one_arg_disabled():
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_redis_execute_command_as_one_arg_enable',
- scoped_metrics=_enable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@background_task()
@@ -168,7 +170,7 @@ def test_redis_execute_command_as_one_arg_enable():
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
'test_execute_command:test_redis_execute_command_as_one_arg_disabled',
- scoped_metrics=_disable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@background_task()
diff --git a/tests/datastore_redis/test_generators.py b/tests/datastore_redis/test_generators.py
new file mode 100644
index 0000000000..13593c1576
--- /dev/null
+++ b/tests/datastore_redis/test_generators.py
@@ -0,0 +1,258 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+import redis
+from testing_support.db_settings import redis_settings
+from testing_support.fixtures import override_application_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.api.datastore_trace import DatastoreTrace
+from newrelic.api.time_trace import current_trace
+from newrelic.common.package_version_utils import get_package_version_tuple
+
+DB_SETTINGS = redis_settings()[0]
+REDIS_PY_VERSION = get_package_version_tuple("redis")
+
+# Settings
+
+_enable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": True,
+}
+_disable_instance_settings = {
+ "datastore_tracer.instance_reporting.enabled": False,
+}
+
+# Metrics
+
+_base_scoped_metrics = (
+ ("Datastore/operation/Redis/scan_iter", 1),
+ ("Datastore/operation/Redis/sscan_iter", 1),
+ ("Datastore/operation/Redis/zscan_iter", 1),
+ ("Datastore/operation/Redis/hscan_iter", 1),
+ ("Datastore/operation/Redis/set", 1),
+ ("Datastore/operation/Redis/sadd", 1),
+ ("Datastore/operation/Redis/zadd", 1),
+ ("Datastore/operation/Redis/hset", 1),
+)
+
+_base_rollup_metrics = (
+ ("Datastore/all", 8),
+ ("Datastore/allOther", 8),
+ ("Datastore/Redis/all", 8),
+ ("Datastore/Redis/allOther", 8),
+ ("Datastore/operation/Redis/scan_iter", 1),
+ ("Datastore/operation/Redis/sscan_iter", 1),
+ ("Datastore/operation/Redis/zscan_iter", 1),
+ ("Datastore/operation/Redis/hscan_iter", 1),
+ ("Datastore/operation/Redis/set", 1),
+ ("Datastore/operation/Redis/sadd", 1),
+ ("Datastore/operation/Redis/zadd", 1),
+ ("Datastore/operation/Redis/hset", 1),
+)
+
+_disable_rollup_metrics = list(_base_rollup_metrics)
+_enable_rollup_metrics = list(_base_rollup_metrics)
+
+_host = instance_hostname(DB_SETTINGS["host"])
+_port = DB_SETTINGS["port"]
+
+_instance_metric_name = f"Datastore/instance/Redis/{_host}/{_port}"
+
+_enable_rollup_metrics.append((_instance_metric_name, 8))
+
+_disable_rollup_metrics.append((_instance_metric_name, None))
+
+# Operations
+
+
+def exercise_redis(client):
+ """
+ Exercise client generators by iterating on various methods and ensuring they are
+ non-empty, and that traces are started and stopped with the generator.
+ """
+
+ # Set existing values
+ client.set("scan-key", "value")
+ client.sadd("sscan-key", "value")
+ client.zadd("zscan-key", {"value": 1})
+ client.hset("hscan-key", "field", "value")
+
+ # Check generators
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ for k in client.scan_iter("scan-*"):
+ assert k == b"scan-key"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ for k in client.sscan_iter("sscan-key"):
+ assert k == b"value"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ for k, _ in client.zscan_iter("zscan-key"):
+ assert k == b"value"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ for f, v in client.hscan_iter("hscan-key"):
+ assert f == b"field"
+ assert v == b"value"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+
+async def exercise_redis_async(client):
+ """
+ Exercise client generators by iterating on various methods and ensuring they are
+ non-empty, and that traces are started and stopped with the generator.
+ """
+
+ # Set existing values
+ await client.set("scan-key", "value")
+ await client.sadd("sscan-key", "value")
+ await client.zadd("zscan-key", {"value": 1})
+ await client.hset("hscan-key", "field", "value")
+
+ # Check generators
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ async for k in client.scan_iter("scan-*"):
+ assert k == b"scan-key"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ async for k in client.sscan_iter("sscan-key"):
+ assert k == b"value"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ async for k, _ in client.zscan_iter("zscan-key"):
+ assert k == b"value"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+ flag = False
+ assert not isinstance(current_trace(), DatastoreTrace) # Assert no active DatastoreTrace
+ async for f, v in client.hscan_iter("hscan-key"):
+ assert f == b"field"
+ assert v == b"value"
+ assert isinstance(current_trace(), DatastoreTrace) # Assert DatastoreTrace now active
+ flag = True
+ assert flag
+
+
+# Tests
+
+
+@override_application_settings(_enable_instance_settings)
+@validate_transaction_metrics(
+ "test_generators:test_strict_redis_generator_enable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_strict_redis_generator_enable_instance():
+ client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
+ exercise_redis(client)
+
+
+@override_application_settings(_disable_instance_settings)
+@validate_transaction_metrics(
+ "test_generators:test_strict_redis_generator_disable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_strict_redis_generator_disable_instance():
+ client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
+ exercise_redis(client)
+
+
+@override_application_settings(_enable_instance_settings)
+@validate_transaction_metrics(
+ "test_generators:test_redis_generator_enable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_redis_generator_enable_instance():
+ client = redis.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
+ exercise_redis(client)
+
+
+@override_application_settings(_disable_instance_settings)
+@validate_transaction_metrics(
+ "test_generators:test_redis_generator_disable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_redis_generator_disable_instance():
+ client = redis.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
+ exercise_redis(client)
+
+
+@pytest.mark.skipif(REDIS_PY_VERSION < (4, 2), reason="Redis.asyncio was not added until v4.2")
+@override_application_settings(_enable_instance_settings)
+@validate_transaction_metrics(
+ "test_generators:test_redis_async_generator_enable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_enable_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_redis_async_generator_enable_instance(loop):
+ client = redis.asyncio.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
+ loop.run_until_complete(exercise_redis_async(client))
+
+
+@pytest.mark.skipif(REDIS_PY_VERSION < (4, 2), reason="Redis.asyncio was not added until v4.2")
+@override_application_settings(_disable_instance_settings)
+@validate_transaction_metrics(
+ "test_generators:test_redis_async_generator_disable_instance",
+ scoped_metrics=_base_scoped_metrics,
+ rollup_metrics=_disable_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_redis_async_generator_disable_instance(loop):
+ client = redis.asyncio.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0)
+ loop.run_until_complete(exercise_redis_async(client))
diff --git a/tests/datastore_redis/test_get_and_set.py b/tests/datastore_redis/test_get_and_set.py
index 0e2df4bb1d..3a38ef37e1 100644
--- a/tests/datastore_redis/test_get_and_set.py
+++ b/tests/datastore_redis/test_get_and_set.py
@@ -48,16 +48,13 @@
('Datastore/operation/Redis/set', 1),
)
-_disable_scoped_metrics = list(_base_scoped_metrics)
_disable_rollup_metrics = list(_base_rollup_metrics)
-
-_enable_scoped_metrics = list(_base_scoped_metrics)
_enable_rollup_metrics = list(_base_rollup_metrics)
_host = instance_hostname(DB_SETTINGS['host'])
_port = DB_SETTINGS['port']
-_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
+_instance_metric_name = f'Datastore/instance/Redis/{_host}/{_port}'
_enable_rollup_metrics.append(
(_instance_metric_name, 2)
@@ -78,7 +75,7 @@ def exercise_redis(client):
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
'test_get_and_set:test_strict_redis_operation_enable_instance',
- scoped_metrics=_enable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@background_task()
@@ -90,7 +87,7 @@ def test_strict_redis_operation_enable_instance():
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
'test_get_and_set:test_strict_redis_operation_disable_instance',
- scoped_metrics=_disable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@background_task()
@@ -102,7 +99,7 @@ def test_strict_redis_operation_disable_instance():
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics(
'test_get_and_set:test_redis_operation_enable_instance',
- scoped_metrics=_enable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@background_task()
@@ -114,7 +111,7 @@ def test_redis_operation_enable_instance():
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics(
'test_get_and_set:test_redis_operation_disable_instance',
- scoped_metrics=_disable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@background_task()
diff --git a/tests/datastore_redis/test_instance_info.py b/tests/datastore_redis/test_instance_info.py
index b3e9a0d5d7..211e96169a 100644
--- a/tests/datastore_redis/test_instance_info.py
+++ b/tests/datastore_redis/test_instance_info.py
@@ -15,9 +15,10 @@
import pytest
import redis
+from newrelic.common.package_version_utils import get_package_version_tuple
from newrelic.hooks.datastore_redis import _conn_attrs_to_dict, _instance_info
-REDIS_PY_VERSION = redis.VERSION
+REDIS_PY_VERSION = get_package_version_tuple("redis")
_instance_info_tests = [
((), {}, ("localhost", "6379", "0")),
diff --git a/tests/datastore_redis/test_multiple_dbs.py b/tests/datastore_redis/test_multiple_dbs.py
index 15777cc38f..f183e5cc17 100644
--- a/tests/datastore_redis/test_multiple_dbs.py
+++ b/tests/datastore_redis/test_multiple_dbs.py
@@ -16,6 +16,7 @@
import redis
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
from testing_support.fixtures import override_application_settings
from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
@@ -23,6 +24,8 @@
from testing_support.util import instance_hostname
DB_MULTIPLE_SETTINGS = redis_settings()
+REDIS_PY_VERSION = get_package_version_tuple("redis")
+
# Settings
@@ -35,27 +38,31 @@
# Metrics
-_base_scoped_metrics = (
+_base_scoped_metrics = [
('Datastore/operation/Redis/get', 1),
('Datastore/operation/Redis/set', 1),
('Datastore/operation/Redis/client_list', 1),
-)
-
-_base_rollup_metrics = (
- ('Datastore/all', 3),
- ('Datastore/allOther', 3),
- ('Datastore/Redis/all', 3),
- ('Datastore/Redis/allOther', 3),
+]
+# client_setinfo was introduced in v5.0.0 and assigns info displayed in client_list output
+if REDIS_PY_VERSION >= (5, 0):
+ _base_scoped_metrics.append(('Datastore/operation/Redis/client_setinfo', 2),)
+
+datastore_all_metric_count = 5 if REDIS_PY_VERSION >= (5, 0) else 3
+
+_base_rollup_metrics = [
+ ('Datastore/all', datastore_all_metric_count),
+ ('Datastore/allOther', datastore_all_metric_count),
+ ('Datastore/Redis/all', datastore_all_metric_count),
+ ('Datastore/Redis/allOther', datastore_all_metric_count),
('Datastore/operation/Redis/get', 1),
('Datastore/operation/Redis/set', 1),
('Datastore/operation/Redis/client_list', 1),
-)
+]
-_disable_scoped_metrics = list(_base_scoped_metrics)
-_disable_rollup_metrics = list(_base_rollup_metrics)
+# client_setinfo was introduced in v5.0.0 and assigns info displayed in client_list output
+if REDIS_PY_VERSION >= (5, 0):
+ _base_rollup_metrics.append(('Datastore/operation/Redis/client_setinfo', 2),)
-_enable_scoped_metrics = list(_base_scoped_metrics)
-_enable_rollup_metrics = list(_base_rollup_metrics)
if len(DB_MULTIPLE_SETTINGS) > 1:
redis_1 = DB_MULTIPLE_SETTINGS[0]
@@ -67,19 +74,23 @@
host_2 = instance_hostname(redis_2['host'])
port_2 = redis_2['port']
- instance_metric_name_1 = 'Datastore/instance/Redis/%s/%s' % (host_1, port_1)
- instance_metric_name_2 = 'Datastore/instance/Redis/%s/%s' % (host_2, port_2)
+ instance_metric_name_1 = f'Datastore/instance/Redis/{host_1}/{port_1}'
+ instance_metric_name_2 = f'Datastore/instance/Redis/{host_2}/{port_2}'
- _enable_rollup_metrics.extend([
- (instance_metric_name_1, 2),
- (instance_metric_name_2, 1),
+ instance_metric_name_1_count = 2 if REDIS_PY_VERSION >= (5, 0) else 2
+ instance_metric_name_2_count = 3 if REDIS_PY_VERSION >= (5, 0) else 1
+
+ _enable_rollup_metrics = _base_rollup_metrics.extend([
+ (instance_metric_name_1, instance_metric_name_1_count),
+ (instance_metric_name_2, instance_metric_name_2_count),
])
- _disable_rollup_metrics.extend([
+ _disable_rollup_metrics = _base_rollup_metrics.extend([
(instance_metric_name_1, None),
(instance_metric_name_2, None),
])
+
def exercise_redis(client_1, client_2):
client_1.set('key', 'value')
client_1.get('key')
@@ -90,7 +101,7 @@ def exercise_redis(client_1, client_2):
reason='Test environment not configured with multiple databases.')
@override_application_settings(_enable_instance_settings)
@validate_transaction_metrics('test_multiple_dbs:test_multiple_datastores_enabled',
- scoped_metrics=_enable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_enable_rollup_metrics,
background_task=True)
@background_task()
@@ -106,7 +117,7 @@ def test_multiple_datastores_enabled():
reason='Test environment not configured with multiple databases.')
@override_application_settings(_disable_instance_settings)
@validate_transaction_metrics('test_multiple_dbs:test_multiple_datastores_disabled',
- scoped_metrics=_disable_scoped_metrics,
+ scoped_metrics=_base_scoped_metrics,
rollup_metrics=_disable_rollup_metrics,
background_task=True)
@background_task()
diff --git a/tests/datastore_redis/test_rb.py b/tests/datastore_redis/test_rb.py
deleted file mode 100644
index 5678c2787f..0000000000
--- a/tests/datastore_redis/test_rb.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# Copyright 2010 New Relic, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-''' The purpose of these tests is to confirm that we will record
-record instance info for Redis Blaster commands that go through
-redis.Connection:send_command(). Commands that don't use send_command,
-like the one that use the fanout client, won't have instance info.
-'''
-
-import pytest
-import redis
-import six
-
-from newrelic.api.background_task import background_task
-
-from testing_support.fixtures import override_application_settings
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.db_settings import redis_settings
-from testing_support.util import instance_hostname
-
-DB_SETTINGS = redis_settings()[0]
-REDIS_PY_VERSION = redis.VERSION
-
-
-# Settings
-
-_enable_instance_settings = {
- 'datastore_tracer.instance_reporting.enabled': True,
-}
-_disable_instance_settings = {
- 'datastore_tracer.instance_reporting.enabled': False,
-}
-
-# Metrics
-
-# We don't record instance metrics when using redis blaster,
-# so we just check for base metrics.
-
-_base_scoped_metrics = (
- ('Datastore/operation/Redis/get', 1),
- ('Datastore/operation/Redis/set', 1),
-)
-
-_base_rollup_metrics = (
- ('Datastore/all', 2),
- ('Datastore/allOther', 2),
- ('Datastore/Redis/all', 2),
- ('Datastore/Redis/allOther', 2),
- ('Datastore/operation/Redis/get', 1),
- ('Datastore/operation/Redis/set', 1),
-)
-
-_disable_scoped_metrics = list(_base_scoped_metrics)
-_disable_rollup_metrics = list(_base_rollup_metrics)
-
-_enable_scoped_metrics = list(_base_scoped_metrics)
-_enable_rollup_metrics = list(_base_rollup_metrics)
-
-_host = instance_hostname(DB_SETTINGS['host'])
-_port = DB_SETTINGS['port']
-
-_instance_metric_name = 'Datastore/instance/Redis/%s/%s' % (_host, _port)
-
-_enable_rollup_metrics.append(
- (_instance_metric_name, 2)
-)
-
-_disable_rollup_metrics.append(
- (_instance_metric_name, None)
-)
-
-# Operations
-
-def exercise_redis(routing_client):
- routing_client.set('key', 'value')
- routing_client.get('key')
-
-def exercise_fanout(cluster):
- with cluster.fanout(hosts='all') as client:
- client.execute_command('CLIENT', 'LIST')
-
-# Tests
-
-@pytest.mark.skipif(six.PY3, reason='Redis Blaster is Python 2 only.')
-@pytest.mark.skipif(REDIS_PY_VERSION < (2, 10, 2),
- reason='Redis Blaster requires redis>=2.10.2')
-@override_application_settings(_enable_instance_settings)
-@validate_transaction_metrics(
- 'test_rb:test_redis_blaster_operation_enable_instance',
- scoped_metrics=_enable_scoped_metrics,
- rollup_metrics=_enable_rollup_metrics,
- background_task=True)
-@background_task()
-def test_redis_blaster_operation_enable_instance():
- from rb import Cluster
-
- cluster = Cluster(
- hosts={0: {'port': DB_SETTINGS['port']}},
- host_defaults={'host': DB_SETTINGS['host']}
- )
- exercise_fanout(cluster)
-
- client = cluster.get_routing_client()
- exercise_redis(client)
-
-
-@pytest.mark.skipif(six.PY3, reason='Redis Blaster is Python 2 only.')
-@pytest.mark.skipif(REDIS_PY_VERSION < (2, 10,2 ),
- reason='Redis Blaster requires redis>=2.10.2')
-@override_application_settings(_disable_instance_settings)
-@validate_transaction_metrics(
- 'test_rb:test_redis_blaster_operation_disable_instance',
- scoped_metrics=_disable_scoped_metrics,
- rollup_metrics=_disable_rollup_metrics,
- background_task=True)
-@background_task()
-def test_redis_blaster_operation_disable_instance():
- from rb import Cluster
-
- cluster = Cluster(
- hosts={0: {'port': DB_SETTINGS['port']}},
- host_defaults={'host': DB_SETTINGS['host']}
- )
- exercise_fanout(cluster)
-
- client = cluster.get_routing_client()
- exercise_redis(client)
diff --git a/tests/datastore_redis/test_span_event.py b/tests/datastore_redis/test_span_event.py
index 27103e971f..36293a6f8b 100644
--- a/tests/datastore_redis/test_span_event.py
+++ b/tests/datastore_redis/test_span_event.py
@@ -78,7 +78,7 @@ def test_span_events(instance_enabled, db_instance_enabled):
settings = _enable_instance_settings.copy()
hostname = instance_hostname(DB_SETTINGS['host'])
exact_agents.update({
- 'peer.address': '%s:%s' % (hostname, DB_SETTINGS['port']),
+ 'peer.address': f"{hostname}:{DB_SETTINGS['port']}",
'peer.hostname': hostname,
})
else:
diff --git a/tests/datastore_redis/test_uninstrumented_methods.py b/tests/datastore_redis/test_uninstrumented_methods.py
index ccf5a096df..a70dede7f3 100644
--- a/tests/datastore_redis/test_uninstrumented_methods.py
+++ b/tests/datastore_redis/test_uninstrumented_methods.py
@@ -39,6 +39,7 @@
"append_no_scale",
"append_values_and_weights",
"append_weights",
+ "auto_close_connection_pool",
"batch_indexer",
"BatchIndexer",
"bulk",
@@ -55,7 +56,9 @@
"edges",
"execute_command",
"flush",
+ "from_pool",
"from_url",
+ "get_cache",
"get_connection_kwargs",
"get_encoder",
"get_label",
@@ -63,7 +66,6 @@
"get_property",
"get_relation",
"get_retry",
- "hscan_iter",
"index_name",
"labels",
"list_keys",
@@ -116,4 +118,4 @@ def test_uninstrumented_methods(client):
is_wrapped = lambda m: hasattr(getattr(module_client, m), "__wrapped__")
uninstrumented |= {m for m in module_methods - IGNORED_METHODS if not is_wrapped(m)}
- assert not uninstrumented, "Uninstrumented methods: %s" % sorted(uninstrumented)
+ assert not uninstrumented, f"Uninstrumented methods: {sorted(uninstrumented)}"
diff --git a/tests/datastore_rediscluster/conftest.py b/tests/datastore_rediscluster/conftest.py
new file mode 100644
index 0000000000..424ffc6f63
--- /dev/null
+++ b/tests/datastore_rediscluster/conftest.py
@@ -0,0 +1,33 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
+
+_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+}
+
+collector_agent_registration = collector_agent_registration_fixture(
+ app_name="Python Agent Test (datastore_redis)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_rediscluster/test_uninstrumented_rediscluster_methods.py b/tests/datastore_rediscluster/test_uninstrumented_rediscluster_methods.py
new file mode 100644
index 0000000000..d4c02c690c
--- /dev/null
+++ b/tests/datastore_rediscluster/test_uninstrumented_rediscluster_methods.py
@@ -0,0 +1,168 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import redis
+from testing_support.db_settings import redis_cluster_settings
+
+DB_CLUSTER_SETTINGS = redis_cluster_settings()[0]
+
+# Set socket_timeout to 5s for fast fail, otherwise the default is to wait forever.
+client = redis.RedisCluster(host=DB_CLUSTER_SETTINGS["host"], port=DB_CLUSTER_SETTINGS["port"], socket_timeout=5)
+
+IGNORED_METHODS = {
+ "MODULE_CALLBACKS",
+ "MODULE_VERSION",
+ "NAME",
+ "add_edge",
+ "add_node",
+ "append_bucket_size",
+ "append_capacity",
+ "append_error",
+ "append_expansion",
+ "append_items_and_increments",
+ "append_items",
+ "append_max_iterations",
+ "append_no_create",
+ "append_no_scale",
+ "append_values_and_weights",
+ "append_weights",
+ "batch_indexer",
+ "BatchIndexer",
+ "bulk",
+ "call_procedure",
+ "client_tracking_off",
+ "client_tracking_on",
+ "client",
+ "close",
+ "commandmixin",
+ "connection_pool",
+ "connection",
+ "debug_segfault",
+ "edges",
+ "execute_command",
+ "flush",
+ "from_url",
+ "get_connection_kwargs",
+ "get_encoder",
+ "get_label",
+ "get_params_args",
+ "get_property",
+ "get_relation",
+ "get_retry",
+ "hscan_iter",
+ "index_name",
+ "labels",
+ "list_keys",
+ "load_document",
+ "load_external_module",
+ "lock",
+ "name",
+ "nodes",
+ "parse_response",
+ "pipeline",
+ "property_keys",
+ "register_script",
+ "relationship_types",
+ "response_callbacks",
+ "RESPONSE_CALLBACKS",
+ "sentinel",
+ "set_file",
+ "set_path",
+ "set_response_callback",
+ "set_retry",
+ "transaction",
+ "version",
+ "ALL_NODES",
+ "CLUSTER_COMMANDS_RESPONSE_CALLBACKS",
+ "COMMAND_FLAGS",
+ "DEFAULT_NODE",
+ "ERRORS_ALLOW_RETRY",
+ "NODE_FLAGS",
+ "PRIMARIES",
+ "RANDOM",
+ "REPLICAS",
+ "RESULT_CALLBACKS",
+ "RedisClusterRequestTTL",
+ "SEARCH_COMMANDS",
+ "client_no_touch",
+ "cluster_addslotsrange",
+ "cluster_bumpepoch",
+ "cluster_delslotsrange",
+ "cluster_error_retry_attempts",
+ "cluster_flushslots",
+ "cluster_links",
+ "cluster_myid",
+ "cluster_myshardid",
+ "cluster_replicas",
+ "cluster_response_callbacks",
+ "cluster_setslot_stable",
+ "cluster_shards",
+ "command_flags",
+ "commands_parser",
+ "determine_slot",
+ "disconnect_connection_pools",
+ "encoder",
+ "get_default_node",
+ "get_node",
+ "get_node_from_key",
+ "get_nodes",
+ "get_primaries",
+ "get_random_node",
+ "get_redis_connection",
+ "get_replicas",
+ "keyslot",
+ "mget_nonatomic",
+ "monitor",
+ "mset_nonatomic",
+ "node_flags",
+ "nodes_manager",
+ "on_connect",
+ "pubsub",
+ "read_from_replicas",
+ "reinitialize_counter",
+ "reinitialize_steps",
+ "replace_default_node",
+ "result_callbacks",
+ "set_default_node",
+ "user_on_connect_func",
+}
+
+REDIS_MODULES = {
+ "bf",
+ "cf",
+ "cms",
+ "ft",
+ "graph",
+ "json",
+ "tdigest",
+ "topk",
+ "ts",
+}
+
+IGNORED_METHODS |= REDIS_MODULES
+
+
+def test_uninstrumented_methods():
+ methods = {m for m in dir(client) if not m[0] == "_"}
+ is_wrapped = lambda m: hasattr(getattr(client, m), "__wrapped__")
+ uninstrumented = {m for m in methods - IGNORED_METHODS if not is_wrapped(m)}
+
+ for module in REDIS_MODULES:
+ if hasattr(client, module):
+ module_client = getattr(client, module)()
+ module_methods = {m for m in dir(module_client) if not m[0] == "_"}
+ is_wrapped = lambda m: hasattr(getattr(module_client, m), "__wrapped__")
+ uninstrumented |= {m for m in module_methods - IGNORED_METHODS if not is_wrapped(m)}
+
+ assert not uninstrumented, f"Uninstrumented methods: {sorted(uninstrumented)}"
diff --git a/tests/datastore_solrpy/conftest.py b/tests/datastore_solrpy/conftest.py
index 4418e5d9a4..9fa03f531f 100644
--- a/tests/datastore_solrpy/conftest.py
+++ b/tests/datastore_solrpy/conftest.py
@@ -12,20 +12,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_solrpy)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_solrpy)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/datastore_solrpy/test_solr.py b/tests/datastore_solrpy/test_solr.py
index ee1a7e91ef..3e02f0e401 100644
--- a/tests/datastore_solrpy/test_solr.py
+++ b/tests/datastore_solrpy/test_solr.py
@@ -13,48 +13,58 @@
# limitations under the License.
from solr import SolrConnection
-
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
from testing_support.db_settings import solr_settings
+from testing_support.util import instance_hostname
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
DB_SETTINGS = solr_settings()[0]
SOLR_HOST = DB_SETTINGS["host"]
SOLR_PORT = DB_SETTINGS["port"]
-SOLR_URL = 'http://%s:%s/solr/collection' % (DB_SETTINGS["host"], DB_SETTINGS["port"])
+SOLR_URL = f"http://{DB_SETTINGS['host']}:{DB_SETTINGS['port']}/solr/collection"
+
def _exercise_solr(solr):
# Construct document names within namespace
documents = ["pysolr_doc_1", "pysolr_doc_2"]
- documents = [x + "_" + DB_SETTINGS["namespace"] for x in documents]
+ documents = [f"{x}_{DB_SETTINGS['namespace']}" for x in documents]
solr.add_many([{"id": x} for x in documents])
solr.commit()
- solr.query('id:%s' % documents[0]).results
- solr.delete('id:*_%s' % DB_SETTINGS["namespace"])
+ solr.query(f"id:{documents[0]}").results
+ solr.delete(f"id:*_{DB_SETTINGS['namespace']}")
solr.commit()
+
_test_solr_search_scoped_metrics = [
- ('Datastore/operation/Solr/add_many', 1),
- ('Datastore/operation/Solr/delete', 1),
- ('Datastore/operation/Solr/commit', 2),
- ('Datastore/operation/Solr/query', 1)]
+ ("Datastore/operation/Solr/add_many", 1),
+ ("Datastore/operation/Solr/delete", 1),
+ ("Datastore/operation/Solr/commit", 2),
+ ("Datastore/operation/Solr/query", 1),
+]
_test_solr_search_rollup_metrics = [
- ('Datastore/all', 5),
- ('Datastore/allOther', 5),
- ('Datastore/Solr/all', 5),
- ('Datastore/Solr/allOther', 5),
- ('Datastore/operation/Solr/add_many', 1),
- ('Datastore/operation/Solr/query', 1),
- ('Datastore/operation/Solr/commit', 2),
- ('Datastore/operation/Solr/delete', 1)]
-
-@validate_transaction_metrics('test_solr:test_solr_search',
+ ("Datastore/all", 5),
+ ("Datastore/allOther", 5),
+ ("Datastore/Solr/all", 5),
+ ("Datastore/Solr/allOther", 5),
+ (f"Datastore/instance/Solr/{instance_hostname(SOLR_HOST)}/{SOLR_PORT}", 3),
+ ("Datastore/operation/Solr/add_many", 1),
+ ("Datastore/operation/Solr/query", 1),
+ ("Datastore/operation/Solr/commit", 2),
+ ("Datastore/operation/Solr/delete", 1),
+]
+
+
+@validate_transaction_metrics(
+ "test_solr:test_solr_search",
scoped_metrics=_test_solr_search_scoped_metrics,
rollup_metrics=_test_solr_search_rollup_metrics,
- background_task=True)
+ background_task=True,
+)
@background_task()
def test_solr_search():
s = SolrConnection(SOLR_URL)
diff --git a/tests/datastore_sqlite/conftest.py b/tests/datastore_sqlite/conftest.py
index ed695b251f..499dd7b9f8 100644
--- a/tests/datastore_sqlite/conftest.py
+++ b/tests/datastore_sqlite/conftest.py
@@ -12,21 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
- 'debug.log_explain_plan_queries': True
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_explain_plan_queries": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (datastore_sqlite)',
- default_settings=_default_settings,
- linked_applications=['Python Agent Test (datastore)'])
+ app_name="Python Agent Test (datastore_sqlite)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (datastore)"],
+)
diff --git a/tests/external_aiobotocore/conftest.py b/tests/external_aiobotocore/conftest.py
new file mode 100644
index 0000000000..2c54daf31f
--- /dev/null
+++ b/tests/external_aiobotocore/conftest.py
@@ -0,0 +1,152 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import functools
+import logging
+import socket
+import threading
+
+import moto.server
+import werkzeug.serving
+from testing_support.fixture.event_loop import ( # noqa: F401, pylint: disable=W0611
+ event_loop as loop,
+)
+from testing_support.fixtures import ( # noqa: F401, pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
+
+PORT = 4443
+AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
+AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec
+HOST = "127.0.0.1"
+
+
+_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+}
+collector_agent_registration = collector_agent_registration_fixture(
+ app_name="Python Agent Test (external_aiobotocore)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (external_aiobotocore)"],
+)
+
+
+def get_free_tcp_port(release_socket: bool = False):
+ sckt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sckt.bind((HOST, 0))
+ _, port = sckt.getsockname() # address, port
+ if release_socket:
+ sckt.close()
+ return port
+
+ return sckt, port
+
+
+class MotoService:
+ """Will Create MotoService.
+ Service is ref-counted so there will only be one per process. Real Service will
+ be returned by `__aenter__`."""
+
+ _services = {} # {name: instance}
+
+ def __init__(self, service_name: str, port: int = None, ssl: bool = False):
+ self._service_name = service_name
+
+ if port:
+ self._socket = None
+ self._port = port
+ else:
+ self._socket, self._port = get_free_tcp_port()
+
+ self._thread = None
+ self._logger = logging.getLogger("MotoService")
+ self._refcount = None
+ self._ip_address = HOST
+ self._server = None
+ self._ssl_ctx = werkzeug.serving.generate_adhoc_ssl_context() if ssl else None
+ self._schema = "http" if not self._ssl_ctx else "https"
+
+ @property
+ def endpoint_url(self):
+ return f"{self._schema}://{self._ip_address}:{self._port}"
+
+ def __call__(self, func):
+ async def wrapper(*args, **kwargs):
+ await self._start()
+ try:
+ result = await func(*args, **kwargs)
+ finally:
+ await self._stop()
+ return result
+
+ functools.update_wrapper(wrapper, func)
+ wrapper.__wrapped__ = func
+ return wrapper
+
+ async def __aenter__(self):
+ svc = self._services.get(self._service_name)
+ if svc is None:
+ self._services[self._service_name] = self
+ self._refcount = 1
+ await self._start()
+ return self
+ else:
+ svc._refcount += 1
+ return svc
+
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
+ self._refcount -= 1
+
+ if self._socket:
+ self._socket.close()
+ self._socket = None
+
+ if self._refcount == 0:
+ del self._services[self._service_name]
+ await self._stop()
+
+ def _server_entry(self):
+ self._main_app = moto.server.DomainDispatcherApplication(
+ moto.server.create_backend_app # , service=self._service_name
+ )
+ self._main_app.debug = True
+
+ if self._socket:
+ self._socket.close() # release right before we use it
+ self._socket = None
+
+ self._server = werkzeug.serving.make_server(
+ self._ip_address,
+ self._port,
+ self._main_app,
+ True,
+ ssl_context=self._ssl_ctx,
+ )
+ self._server.serve_forever()
+
+ async def _start(self):
+ self._thread = threading.Thread(target=self._server_entry, daemon=True)
+ self._thread.start()
+
+ async def _stop(self):
+ if self._server:
+ self._server.shutdown()
+
+ self._thread.join()
diff --git a/tests/external_aiobotocore/test_aiobotocore_dynamodb.py b/tests/external_aiobotocore/test_aiobotocore_dynamodb.py
new file mode 100644
index 0000000000..e4b59513e7
--- /dev/null
+++ b/tests/external_aiobotocore/test_aiobotocore_dynamodb.py
@@ -0,0 +1,167 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from aiobotocore.session import get_session
+from conftest import ( # noqa: F401, pylint: disable=W0611
+ AWS_ACCESS_KEY_ID,
+ AWS_SECRET_ACCESS_KEY,
+ PORT,
+ MotoService,
+ loop,
+)
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+TEST_TABLE = "python-agent-test"
+
+_dynamodb_scoped_metrics = [
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/create_table", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/put_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/get_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/update_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/query", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/scan", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/delete_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/delete_table", 1),
+]
+
+_dynamodb_rollup_metrics = [
+ ("Datastore/all", 8),
+ ("Datastore/allOther", 8),
+ ("Datastore/DynamoDB/all", 8),
+ ("Datastore/DynamoDB/allOther", 8),
+]
+
+
+# aws.requestId count disabled due to variability in count.
+# Flaky due to waiter function, which "aws.operation" == "DescribeTable"
+# This is a polling function, so in real time, this value could fluctuate
+# @validate_span_events(expected_agents=("aws.requestId",), count=9)
+# @validate_span_events(exact_agents={"aws.operation": "DescribeTable"}, count=2)
+@validate_span_events(exact_agents={"aws.operation": "PutItem"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "GetItem"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DeleteItem"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "CreateTable"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DeleteTable"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Query"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Scan"}, count=1)
+@validate_transaction_metrics(
+ "test_aiobotocore_dynamodb:test_aiobotocore_dynamodb",
+ scoped_metrics=_dynamodb_scoped_metrics,
+ rollup_metrics=_dynamodb_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_aiobotocore_dynamodb(loop):
+ async def _test():
+ async with MotoService("dynamodb", port=PORT):
+ session = get_session()
+
+ async with session.create_client(
+ "dynamodb",
+ region_name="us-east-1",
+ endpoint_url=f"http://localhost:{PORT}",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ ) as client:
+
+ resp = await client.create_table(
+ TableName=TEST_TABLE,
+ AttributeDefinitions=[
+ {"AttributeName": "Id", "AttributeType": "N"},
+ {"AttributeName": "Foo", "AttributeType": "S"},
+ ],
+ KeySchema=[
+ {"AttributeName": "Id", "KeyType": "HASH"},
+ {"AttributeName": "Foo", "KeyType": "RANGE"},
+ ],
+ ProvisionedThroughput={
+ "ReadCapacityUnits": 5,
+ "WriteCapacityUnits": 5,
+ },
+ )
+ assert resp["TableDescription"]["TableName"] == TEST_TABLE
+
+ # Wait for table to be created
+ waiter = client.get_waiter("table_exists")
+ await waiter.wait(TableName=TEST_TABLE)
+
+ # Put item
+ resp = await client.put_item(
+ TableName=TEST_TABLE,
+ Item={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ },
+ )
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Get item
+ resp = await client.get_item(
+ TableName=TEST_TABLE,
+ Key={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ },
+ )
+ assert resp["Item"]["Foo"]["S"] == "hello_world"
+
+ # Update item
+ resp = await client.update_item(
+ TableName=TEST_TABLE,
+ Key={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ },
+ AttributeUpdates={
+ "Foo2": {"Value": {"S": "hello_world2"}, "Action": "PUT"},
+ },
+ ReturnValues="ALL_NEW",
+ )
+ assert resp["Attributes"]["Foo2"]
+
+ # Query for item
+ resp = await client.query(
+ TableName=TEST_TABLE,
+ Select="ALL_ATTRIBUTES",
+ KeyConditionExpression="#Id = :v_id",
+ ExpressionAttributeNames={"#Id": "Id"},
+ ExpressionAttributeValues={":v_id": {"N": "101"}},
+ )
+ assert len(resp["Items"]) == 1
+ assert resp["Items"][0]["Foo"]["S"] == "hello_world"
+
+ # Scan
+ resp = await client.scan(TableName=TEST_TABLE)
+ assert len(resp["Items"]) == 1
+
+ # Delete item
+ resp = await client.delete_item(
+ TableName=TEST_TABLE,
+ Key={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ },
+ )
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Delete table
+ resp = await client.delete_table(TableName=TEST_TABLE)
+ assert resp["TableDescription"]["TableName"] == TEST_TABLE
+
+ loop.run_until_complete(_test())
diff --git a/tests/external_aiobotocore/test_aiobotocore_s3.py b/tests/external_aiobotocore/test_aiobotocore_s3.py
new file mode 100644
index 0000000000..8c8e7eeab8
--- /dev/null
+++ b/tests/external_aiobotocore/test_aiobotocore_s3.py
@@ -0,0 +1,123 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import aiobotocore
+from conftest import ( # noqa: F401, pylint: disable=W0611
+ AWS_ACCESS_KEY_ID,
+ AWS_SECRET_ACCESS_KEY,
+ PORT,
+ MotoService,
+ loop,
+)
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+TEST_BUCKET = "python-agent-test"
+FILENAME = "dummy.bin"
+FOLDER = "aiobotocore"
+ENDPOINT = f"localhost:{PORT}"
+KEY = f"{FOLDER}/{FILENAME}"
+EXPECTED_BUCKET_URL = f"http://{ENDPOINT}/{TEST_BUCKET}"
+EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}/{KEY}"
+
+
+_s3_scoped_metrics = [
+ (f"External/{ENDPOINT}/aiobotocore/GET", 5),
+ (f"External/{ENDPOINT}/aiobotocore/PUT", 2),
+ (f"External/{ENDPOINT}/aiobotocore/DELETE", 2),
+]
+
+_s3_rollup_metrics = [
+ ("External/all", 9),
+ ("External/allOther", 9),
+ (f"External/{ENDPOINT}/all", 9),
+ (f"External/{ENDPOINT}/aiobotocore/GET", 5),
+ (f"External/{ENDPOINT}/aiobotocore/PUT", 2),
+ (f"External/{ENDPOINT}/aiobotocore/DELETE", 2),
+]
+
+
+@validate_span_events(exact_agents={"aws.operation": "CreateBucket"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "PutObject"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "ListObjects"}, count=2)
+@validate_span_events(exact_agents={"aws.operation": "GetObject"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DeleteBucket"}, count=1)
+@validate_span_events(exact_agents={"http.url": EXPECTED_BUCKET_URL}, count=4)
+@validate_span_events(exact_agents={"http.url": EXPECTED_KEY_URL}, count=4)
+@validate_transaction_metrics(
+ "test_aiobotocore_s3:test_aiobotocore_s3",
+ scoped_metrics=_s3_scoped_metrics,
+ rollup_metrics=_s3_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_aiobotocore_s3(loop):
+ async def _test():
+
+ data = b"hello_world"
+
+ async with MotoService("s3", port=PORT):
+
+ session = aiobotocore.session.get_session()
+
+ async with session.create_client( # nosec
+ "s3",
+ region_name="us-east-1",
+ endpoint_url=f"http://localhost:{PORT}",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ ) as client:
+
+ # Create bucket
+ await client.create_bucket(
+ Bucket=TEST_BUCKET,
+ )
+
+ # List buckets
+ await client.list_buckets()
+
+ # Upload object to s3
+ resp = await client.put_object(Bucket=TEST_BUCKET, Key=KEY, Body=data)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # List objects from bucket
+ await client.list_objects(Bucket=TEST_BUCKET)
+
+ # Getting s3 object properties of uploaded file
+ resp = await client.get_object_acl(Bucket=TEST_BUCKET, Key=KEY)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Get object from s3
+ response = await client.get_object(Bucket=TEST_BUCKET, Key=KEY)
+ # this will ensure the connection is correctly re-used/closed
+ async with response["Body"] as stream:
+ assert await stream.read() == data
+
+ # List s3 objects using paginator
+ paginator = client.get_paginator("list_objects")
+ async for result in paginator.paginate(Bucket=TEST_BUCKET, Prefix=FOLDER):
+ for content in result.get("Contents", []):
+ assert content
+
+ # Delete object from s3
+ await client.delete_object(Bucket=TEST_BUCKET, Key=KEY)
+
+ # Delete bucket from s3
+ await client.delete_bucket(Bucket=TEST_BUCKET)
+
+ loop.run_until_complete(_test())
diff --git a/tests/external_aiobotocore/test_aiobotocore_sns.py b/tests/external_aiobotocore/test_aiobotocore_sns.py
new file mode 100644
index 0000000000..31e0db92f7
--- /dev/null
+++ b/tests/external_aiobotocore/test_aiobotocore_sns.py
@@ -0,0 +1,73 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from aiobotocore.session import get_session
+from conftest import ( # noqa: F401, pylint: disable=W0611
+ AWS_ACCESS_KEY_ID,
+ AWS_SECRET_ACCESS_KEY,
+ PORT,
+ MotoService,
+ loop,
+)
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+TOPIC = "arn:aws:sns:us-east-1:123456789012:some-topic"
+sns_metrics = [
+ (f"MessageBroker/SNS/Topic/Produce/Named/{TOPIC}", 1),
+ ("MessageBroker/SNS/Topic/Produce/Named/PhoneNumber", 1),
+]
+
+
+@validate_span_events(expected_agents=("aws.requestId",), count=4)
+@validate_span_events(exact_agents={"aws.operation": "CreateTopic"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Publish"}, count=2)
+@validate_transaction_metrics(
+ "test_aiobotocore_sns:test_publish_to_sns",
+ scoped_metrics=sns_metrics,
+ rollup_metrics=sns_metrics,
+ background_task=True,
+)
+@background_task()
+def test_publish_to_sns(loop):
+ async def _test():
+
+ async with MotoService("sns", port=PORT):
+ session = get_session()
+
+ async with session.create_client(
+ "sns",
+ region_name="us-east-1",
+ endpoint_url=f"http://localhost:{PORT}",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ ) as client:
+
+ topic_arn = await client.create_topic(Name="some-topic")
+ topic_arn_name = topic_arn["TopicArn"]
+
+ kwargs = {"TopicArn": topic_arn_name}
+ published_message = await client.publish(Message="my message", **kwargs)
+ assert "MessageId" in published_message
+
+ await client.subscribe(TopicArn=topic_arn_name, Protocol="sms", Endpoint="5555555555")
+
+ published_message = await client.publish(PhoneNumber="5555555555", Message="my msg")
+ assert "MessageId" in published_message
+
+ loop.run_until_complete(_test())
diff --git a/tests/external_aiobotocore/test_aiobotocore_sqs.py b/tests/external_aiobotocore/test_aiobotocore_sqs.py
new file mode 100644
index 0000000000..bb76a0d12e
--- /dev/null
+++ b/tests/external_aiobotocore/test_aiobotocore_sqs.py
@@ -0,0 +1,114 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from aiobotocore.session import get_session
+from conftest import ( # noqa: F401, pylint: disable=W0611
+ AWS_ACCESS_KEY_ID,
+ AWS_SECRET_ACCESS_KEY,
+ PORT,
+ MotoService,
+ loop,
+)
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+
+URL = f"localhost:{PORT}"
+TEST_QUEUE = "python-agent-test"
+
+_sqs_scoped_metrics = [
+ (f"MessageBroker/SQS/Queue/Produce/Named/{TEST_QUEUE}", 2),
+ (f"External/{URL}/aiobotocore/POST", 7),
+]
+
+_sqs_rollup_metrics = [
+ (f"MessageBroker/SQS/Queue/Produce/Named/{TEST_QUEUE}", 2),
+ (f"MessageBroker/SQS/Queue/Consume/Named/{TEST_QUEUE}", 1),
+ ("External/all", 7),
+ ("External/allOther", 7),
+ (f"External/{URL}/all", 7),
+ (f"External/{URL}/aiobotocore/POST", 7),
+]
+
+
+@validate_span_events(exact_agents={"aws.operation": "CreateQueue"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "ListQueues"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "SendMessage"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "ReceiveMessage"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "SendMessageBatch"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "PurgeQueue"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DeleteQueue"}, count=1)
+@validate_transaction_metrics(
+ "test_aiobotocore_sqs:test_aiobotocore_sqs",
+ scoped_metrics=_sqs_scoped_metrics,
+ rollup_metrics=_sqs_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+def test_aiobotocore_sqs(loop):
+ async def _test():
+ async with MotoService("sqs", port=PORT):
+ session = get_session()
+
+ async with session.create_client(
+ "sqs",
+ region_name="us-east-1",
+ endpoint_url=f"http://localhost:{PORT}",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ ) as client:
+
+ response = await client.create_queue(QueueName=TEST_QUEUE)
+
+ queue_url = response["QueueUrl"]
+
+ # List queues
+ response = await client.list_queues()
+ for queue_name in response.get("QueueUrls", []):
+ assert queue_name
+
+ # Send message
+ resp = await client.send_message(
+ QueueUrl=queue_url,
+ MessageBody="hello_world",
+ )
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Receive message
+ resp = await client.receive_message(
+ QueueUrl=queue_url,
+ )
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Send message batch
+ messages = [
+ {"Id": "1", "MessageBody": "message 1"},
+ {"Id": "2", "MessageBody": "message 2"},
+ {"Id": "3", "MessageBody": "message 3"},
+ ]
+ resp = await client.send_message_batch(QueueUrl=queue_url, Entries=messages)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Purge queue
+ resp = await client.purge_queue(QueueUrl=queue_url)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Delete queue
+ resp = await client.delete_queue(QueueUrl=queue_url)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ loop.run_until_complete(_test())
diff --git a/tests/external_boto3/test_boto3_iam.py b/tests/external_boto3/test_boto3_iam.py
deleted file mode 100644
index ac49214f44..0000000000
--- a/tests/external_boto3/test_boto3_iam.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright 2010 New Relic, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import sys
-import uuid
-
-import boto3
-import moto
-
-from newrelic.api.background_task import background_task
-from testing_support.fixtures import (
- validate_tt_segment_params, override_application_settings)
-from testing_support.validators.validate_span_events import (
- validate_span_events)
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-
-MOTO_VERSION = tuple(int(v) for v in moto.__version__.split('.')[:3])
-
-# patch earlier versions of moto to support py37
-if sys.version_info >= (3, 7) and MOTO_VERSION <= (1, 3, 1):
- import re
- moto.packages.responses.responses.re._pattern_type = re.Pattern
-
-AWS_ACCESS_KEY_ID = 'AAAAAAAAAAAACCESSKEY'
-AWS_SECRET_ACCESS_KEY = 'AAAAAASECRETKEY'
-
-TEST_USER = 'python-agent-test-%s' % uuid.uuid4()
-
-_iam_scoped_metrics = [
- ('External/iam.amazonaws.com/botocore/POST', 3),
-]
-
-_iam_rollup_metrics = [
- ('External/all', 3),
- ('External/allOther', 3),
- ('External/iam.amazonaws.com/all', 3),
- ('External/iam.amazonaws.com/botocore/POST', 3),
-]
-
-
-@override_application_settings({'distributed_tracing.enabled': True})
-@validate_span_events(
- exact_agents={'http.url': 'https://iam.amazonaws.com/'}, count=3)
-@validate_span_events(expected_agents=('aws.requestId',), count=3)
-@validate_span_events(exact_agents={'aws.operation': 'CreateUser'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'GetUser'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'DeleteUser'}, count=1)
-@validate_tt_segment_params(present_params=('aws.requestId',))
-@validate_transaction_metrics(
- 'test_boto3_iam:test_iam',
- scoped_metrics=_iam_scoped_metrics,
- rollup_metrics=_iam_rollup_metrics,
- background_task=True)
-@background_task()
-@moto.mock_iam
-def test_iam():
- iam = boto3.client(
- 'iam',
- aws_access_key_id=AWS_ACCESS_KEY_ID,
- aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
- )
-
- # Create user
- resp = iam.create_user(UserName=TEST_USER)
- assert resp['ResponseMetadata']['HTTPStatusCode'] == 200
-
- # Get the user
- resp = iam.get_user(UserName=TEST_USER)
- assert resp['ResponseMetadata']['HTTPStatusCode'] == 200
- assert resp['User']['UserName'] == TEST_USER
-
- # Delete the user
- resp = iam.delete_user(UserName=TEST_USER)
- assert resp['ResponseMetadata']['HTTPStatusCode'] == 200
diff --git a/tests/external_boto3/test_boto3_sns.py b/tests/external_boto3/test_boto3_sns.py
deleted file mode 100644
index 3718d52924..0000000000
--- a/tests/external_boto3/test_boto3_sns.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright 2010 New Relic, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import sys
-import boto3
-import moto
-import pytest
-
-from newrelic.api.background_task import background_task
-from testing_support.fixtures import (
- validate_tt_segment_params, override_application_settings)
-from testing_support.validators.validate_span_events import validate_span_events
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-
-MOTO_VERSION = tuple(int(v) for v in moto.__version__.split('.')[:3])
-
-# patch earlier versions of moto to support py37
-if sys.version_info >= (3, 7) and MOTO_VERSION <= (1, 3, 1):
- import re
- moto.packages.responses.responses.re._pattern_type = re.Pattern
-
-AWS_ACCESS_KEY_ID = 'AAAAAAAAAAAACCESSKEY'
-AWS_SECRET_ACCESS_KEY = 'AAAAAASECRETKEY'
-AWS_REGION_NAME = 'us-east-1'
-SNS_URL = 'sns-us-east-1.amazonaws.com'
-TOPIC = 'arn:aws:sns:us-east-1:123456789012:some-topic'
-sns_metrics = [
- ('MessageBroker/SNS/Topic'
- '/Produce/Named/%s' % TOPIC, 1)]
-sns_metrics_phone = [
- ('MessageBroker/SNS/Topic'
- '/Produce/Named/PhoneNumber', 1)]
-
-
-@override_application_settings({'distributed_tracing.enabled': True})
-@validate_span_events(expected_agents=('aws.requestId',), count=2)
-@validate_span_events(exact_agents={'aws.operation': 'CreateTopic'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'Publish'}, count=1)
-@validate_tt_segment_params(present_params=('aws.requestId',))
-@pytest.mark.parametrize('topic_argument', ('TopicArn', 'TargetArn'))
-@validate_transaction_metrics('test_boto3_sns:test_publish_to_sns_topic',
- scoped_metrics=sns_metrics, rollup_metrics=sns_metrics,
- background_task=True)
-@background_task()
-@moto.mock_sns
-def test_publish_to_sns_topic(topic_argument):
- conn = boto3.client('sns',
- aws_access_key_id=AWS_ACCESS_KEY_ID,
- aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
- region_name=AWS_REGION_NAME)
-
- topic_arn = conn.create_topic(Name='some-topic')['TopicArn']
-
- kwargs = {topic_argument: topic_arn}
- published_message = conn.publish(Message='my msg', **kwargs)
- assert 'MessageId' in published_message
-
-
-@override_application_settings({'distributed_tracing.enabled': True})
-@validate_span_events(expected_agents=('aws.requestId',), count=3)
-@validate_span_events(exact_agents={'aws.operation': 'CreateTopic'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'Subscribe'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'Publish'}, count=1)
-@validate_tt_segment_params(present_params=('aws.requestId',))
-@validate_transaction_metrics('test_boto3_sns:test_publish_to_sns_phone',
- scoped_metrics=sns_metrics_phone, rollup_metrics=sns_metrics_phone,
- background_task=True)
-@background_task()
-@moto.mock_sns
-def test_publish_to_sns_phone():
- conn = boto3.client('sns',
- aws_access_key_id=AWS_ACCESS_KEY_ID,
- aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
- region_name=AWS_REGION_NAME)
-
- topic_arn = conn.create_topic(Name='some-topic')['TopicArn']
- conn.subscribe(TopicArn=topic_arn, Protocol='sms', Endpoint='5555555555')
-
- published_message = conn.publish(
- PhoneNumber='5555555555', Message='my msg')
- assert 'MessageId' in published_message
diff --git a/tests/external_botocore/_mock_bedrock_encoding_utils.py b/tests/external_botocore/_mock_bedrock_encoding_utils.py
new file mode 100644
index 0000000000..6144ebb6a8
--- /dev/null
+++ b/tests/external_botocore/_mock_bedrock_encoding_utils.py
@@ -0,0 +1,84 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Utilities for encoding and decoding streaming payloads from Bedrock. """
+import base64
+import binascii
+import json
+
+
+def crc(b):
+ """Encode the crc32 of the bytes stream into a 4 byte sequence."""
+ return int_to_escaped_bytes(binascii.crc32(b), 4)
+
+
+def int_to_escaped_bytes(i, num_bytes=1):
+ """Convert an integer into an arbitrary number of bytes."""
+ return bytes.fromhex(f"{{:0{str(num_bytes * 2)}x}}".format(i))
+
+
+def encode_headers(headers):
+ """Encode a dictionary of headers into bedrock's binary format."""
+ new_headers = []
+ for h, v in headers.items():
+ if not h.startswith(":"):
+ h = f":{h}"
+ h = h.encode("utf-8")
+ v = v.encode("utf-8")
+ new_headers.append(b"".join((int_to_escaped_bytes(len(h)), h, b"\x07\x00", int_to_escaped_bytes(len(v)), v)))
+ return b"".join(new_headers)
+
+
+def decode_body(body):
+ """Decode the mixed JSON and base64 encoded body of a streaming response into a dictionary."""
+ body = body.decode("utf-8")
+ body = json.loads(body)
+ body = body["bytes"]
+ body = base64.b64decode(body)
+ body = body.decode("utf-8")
+ return json.loads(body)
+
+
+def encode_body(body, malformed_body=False):
+ """Encode a dictionary body into JSON, base64, then JSON again under a bytes key."""
+
+ body = json.dumps(body, separators=(",", ":"))
+ if malformed_body:
+ # Remove characters from end of body to make it unreadable
+ body = body[:-4]
+
+ body = body.encode("utf-8")
+ body = base64.b64encode(body)
+ body = body.decode("utf-8")
+ body = {"bytes": body}
+ body = json.dumps(body, separators=(",", ":"))
+ body = body.encode("utf-8")
+ return body
+
+
+def encode_streaming_payload(headers, body, malformed_body=False):
+ """Encode dictionary headers and dictionary body into bedrock's binary payload format including calculated lengths and CRC32."""
+ headers = encode_headers(headers)
+ body = encode_body(body, malformed_body=malformed_body)
+
+ header_length = len(headers)
+ payload_length = len(body)
+ total_length = 16 + payload_length + header_length
+
+ prelude = int_to_escaped_bytes(total_length, 4) + int_to_escaped_bytes(header_length, 4)
+ prelude_crc = crc(prelude)
+
+ payload = prelude + prelude_crc + headers + body
+ payload_crc = crc(payload)
+
+ return payload + payload_crc
diff --git a/tests/external_botocore/_mock_external_bedrock_server.py b/tests/external_botocore/_mock_external_bedrock_server.py
new file mode 100644
index 0000000000..5974cdb406
--- /dev/null
+++ b/tests/external_botocore/_mock_external_bedrock_server.py
@@ -0,0 +1,6756 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import re
+
+from _mock_bedrock_encoding_utils import encode_streaming_payload
+from testing_support.mock_external_http_server import MockExternalHTTPServer
+
+# This defines an external server test apps can make requests to instead of
+# the real Bedrock backend. This provides 3 features:
+#
+# 1) This removes dependencies on external websites.
+# 2) Provides a better mechanism for making an external call in a test app than
+# simple calling another endpoint the test app makes available because this
+# server will not be instrumented meaning we don't have to sort through
+# transactions to separate the ones created in the test app and the ones
+# created by an external call.
+# 3) This app runs on a separate thread meaning it won't block the test app.
+
+STREAMED_RESPONSES = {
+ "mistral.mistral-7b-instruct-v0%3A2::[INST] What is 212 degrees Fahrenheit converted to Celsius? [/INST]": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ },
+ 200,
+ [
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494652764969776963335276634639795a57467a623234694f6d3531624778395858303d227d30693c02",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949474e76626e5a6c636e51694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227dd3bacdba",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494745694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d7e1af014",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694948526c6258426c636d463064584a6c4969776963335276634639795a57467a623234694f6d3531624778395858303d227d1ac96f27",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949475a79623230694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d79a7bac3",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494559694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d0d3a538e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69595768795a5734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d800cf6a1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696147567064434973496e4e3062334266636d566863323975496a70756457787366563139227d9261e38d",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494852764969776963335276634639795a57467a623234694f6d3531624778395858303d227d580defbc",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949454d694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227dd42b6945",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f695a57787a4969776963335276634639795a57467a623234694f6d3531624778395858303d227df0e3aab1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696158567a4969776963335276634639795a57467a623234694f6d3531624778395858303d227d47cb033c",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694c434973496e4e3062334266636d566863323975496a70756457787366563139227db86ac617",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949486c7664534973496e4e3062334266636d566863323975496a70756457787366563139227d111efa00",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949474e6862694973496e4e3062334266636d566863323975496a70756457787366563139227d33a08812",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694948567a5a534973496e4e3062334266636d566863323975496a70756457787366563139227d60f7f5e7",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694948526f5a534973496e4e3062334266636d566863323975496a70756457787366563139227d5059ad51",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949475a766247787664326c755a794973496e4e3062334266636d566863323975496a70756457787366563139227da7af16c9",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949475a76636d3131624745694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d76a80396",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694f694973496e4e3062334266636d566863323975496a70756457787366563139227dbc0b9457",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949454d694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227dd42b6945",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f695a57787a4969776963335276634639795a57467a623234694f6d3531624778395858303d227df0e3aab1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696158567a4969776963335276634639795a57467a623234694f6d3531624778395858303d227d47cb033c",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494430694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d420e6621",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494367694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227df82acb39",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6952694973496e4e3062334266636d566863323975496a70756457787366563139227dd05517eb",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69595768795a5734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d800cf6a1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696147567064434973496e4e3062334266636d566863323975496a70756457787366563139227d9261e38d",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494330694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d763c0ffe",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949434973496e4e3062334266636d566863323975496a70756457787366563139227d0ee1d3f9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d794973496e4e3062334266636d566863323975496a70756457787366563139227d2cc43589",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694b534973496e4e3062334266636d566863323975496a70756457787366563139227d9e2e7227",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494d4f584969776963335276634639795a57467a623234694f6d3531624778395858303d227d1309a28c",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949434973496e4e3062334266636d566863323975496a70756457787366563139227d0ee1d3f9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694e534973496e4e3062334266636d566863323975496a70756457787366563139227d28a567c9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694c794973496e4e3062334266636d566863323975496a70756457787366563139227d084664bf",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694f534973496e4e3062334266636d566863323975496a70756457787366563139227d0c2736ff",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694c694973496e4e3062334266636d566863323975496a70756457787366563139227dd18d670d",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69584734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d18050f59",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69584734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d18050f59",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69553238694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d2acbcc0d",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694c434973496e4e3062334266636d566863323975496a70756457787366563139227db86ac617",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494852764969776963335276634639795a57467a623234694f6d3531624778395858303d227d580defbc",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949474e76626e5a6c636e51694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227dd3bacdba",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949434973496e4e3062334266636d566863323975496a70756457787366563139227d0ee1d3f9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d534973496e4e3062334266636d566863323975496a70756457787366563139227d45239493",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694947526c5a334a6c5a584d694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d3ac15b52",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494559694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d0d3a538e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69595768795a5734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d800cf6a1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696147567064434973496e4e3062334266636d566863323975496a70756457787366563139227d9261e38d",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494852764969776963335276634639795a57467a623234694f6d3531624778395858303d227d580defbc",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949454d694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227dd42b6945",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f695a57787a4969776963335276634639795a57467a623234694f6d3531624778395858303d227df0e3aab1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696158567a4969776963335276634639795a57467a623234694f6d3531624778395858303d227d47cb033c",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694c434973496e4e3062334266636d566863323975496a70756457787366563139227db86ac617",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494752764969776963335276634639795a57467a623234694f6d3531624778395858303d227db0992b1d",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694948526f5a534973496e4e3062334266636d566863323975496a70756457787366563139227d5059ad51",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949475a766247787664326c755a794973496e4e3062334266636d566863323975496a70756457787366563139227da7af16c9",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949474e6862474e3162474630615739754969776963335276634639795a57467a623234694f6d3531624778395858303d227dfaa5d044",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694f694973496e4e3062334266636d566863323975496a70756457787366563139227dbc0b9457",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69584734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d18050f59",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69584734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d18050f59",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6951794973496e4e3062334266636d566863323975496a70756457787366563139227d6418e703",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f695a57787a4969776963335276634639795a57467a623234694f6d3531624778395858303d227df0e3aab1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696158567a4969776963335276634639795a57467a623234694f6d3531624778395858303d227d47cb033c",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494430694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d420e6621",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494367694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227df82acb39",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d534973496e4e3062334266636d566863323975496a70756457787366563139227d45239493",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69777241694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d27bae54a",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6952694973496e4e3062334266636d566863323975496a70756457787366563139227dd05517eb",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494330694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d763c0ffe",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949434973496e4e3062334266636d566863323975496a70756457787366563139227d0ee1d3f9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d794973496e4e3062334266636d566863323975496a70756457787366563139227d2cc43589",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694b534973496e4e3062334266636d566863323975496a70756457787366563139227d9e2e7227",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494d4f584969776963335276634639795a57467a623234694f6d3531624778395858303d227d1309a28c",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949434973496e4e3062334266636d566863323975496a70756457787366563139227d0ee1d3f9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694e534973496e4e3062334266636d566863323975496a70756457787366563139227d28a567c9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694c794973496e4e3062334266636d566863323975496a70756457787366563139227d084664bf",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694f534973496e4e3062334266636d566863323975496a70756457787366563139227d0c2736ff",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69584734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d18050f59",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69584734694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d18050f59",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6951794973496e4e3062334266636d566863323975496a70756457787366563139227d6418e703",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f695a57787a4969776963335276634639795a57467a623234694f6d3531624778395858303d227df0e3aab1",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f696158567a4969776963335276634639795a57467a623234694f6d3531624778395858303d227d47cb033c",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494430694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d420e6621",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494367694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227df82acb39",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d534973496e4e3062334266636d566863323975496a70756457787366563139227d45239493",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000000a70000004b462b04f70b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f69494330694c434a7a6447397758334a6c59584e7662694936626e56736248316466513d3d227d763c0ffe",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f6949434973496e4e3062334266636d566863323975496a70756457787366563139227d0ee1d3f9",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d794973496e4e3062334266636d566863323975496a70756457787366563139227d2cc43589",
+ "000000a30000004bb3aba2370b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694d694973496e4e3062334266636d566863323975496a70756457787366563139227df50f363b",
+ "000001570000004bc543870f0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a76645852776458527a496a706265794a305a586830496a6f694b534973496e4e3062334266636d566863323975496a6f69624756755a33526f496e31644c434a686257463662323474596d566b636d396a61793170626e5a76593246306157397554575630636d6c6a6379493665794a70626e4231644652766132567551323931626e51694f6a49314c434a7664585277645852556232746c626b4e7664573530496a6f784d444173496d6c75646d396a595852706232354d5958526c626d4e35496a6f784e4451774c434a6d61584a7a64454a356447564d5958526c626d4e35496a6f7a4d6a643966513d3d227d9b82aeca",
+ ],
+ ],
+ "amazon.titan-text-express-v1::User: The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "884db5c9-18ab-4f27-8892-33656176a2e6",
+ },
+ 200,
+ [
+ "000001d30000004b8125915d0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a7664585277645852555a586830496a6f694945686c624778764c43426f623363675932467549456b676147567363434235623355676447396b59586b2f496977696157356b5a5867694f6a4173496e527664474673543356306348563056475634644652766132567551323931626e51694f6a45784c434a6a623231776247563061573975556d566863323975496a6f69526b6c4f53564e494969776961573577645852555a586830564739725a57354462335675644349364e7a4973496d467459587076626931695a57527962324e724c576c75646d396a595852706232354e5a58527961574e7a496a7037496d6c7563485630564739725a57354462335675644349364e7a4973496d393164484231644652766132567551323931626e51694f6a45784c434a70626e5a765932463061573975544746305a57356a655349364e7a59774c434a6d61584a7a64454a356447564d5958526c626d4e35496a6f334e6a423966513d3d227db357f684"
+ ],
+ ],
+ "anthropic.claude-instant-v1::The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "1a72a1f6-310f-469c-af1d-2c59eb600089",
+ },
+ 200,
+ [
+ "000001a70000004b8d77d7520b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f696257567a6332466e5a56397a6447467964434973496d316c63334e685a3255694f6e7369615751694f694a6a62323177624638774d56684b4e315a4c4d3052574e304931556e4a6c516e7077595649795a4845694c434a306558426c496a6f696257567a6332466e5a534973496e4a76624755694f694a6863334e7063335268626e51694c434a6a623235305a573530496a7062585377696257396b5a5777694f694a6a624746315a4755746157357a64474675644330784c6a49694c434a7a6447397758334a6c59584e7662694936626e567362437769633352766346397a5a5846315a57356a5a534936626e56736243776964584e685a3255694f6e73696157357764585266644739725a57357a496a6f334d79776962335630634856305833527661325675637949364d58313966513d3d227dd65d4fce",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131397a6447467964434973496d6c755a475634496a6f774c434a6a623235305a57353058324a7362324e72496a7037496e5235634755694f694a305a58683049697769644756346443493649694a3966513d3d227dcdbf661d",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694a495a57787362794a3966513d3d227d335563af",
+ "000000d30000004b4a7942f80b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694968496e3139227d2d9e8eb6",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496754586b696658303d227d0d0604c6",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694967626d46745a534a3966513d3d227d625a1a55",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496761584d696658303d227dbcc5266b",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694967513278686457526c496e3139227dafd2aab1",
+ "000000d30000004b4a7942f80b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694975496e3139227d2b9773b8",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694a63626c787553446f696658303d227d796313c0",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694967546d6c6a5a534a3966513d3d227d3fc8c903",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694967644738696658303d227dcd25ff7b",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f6949676257566c64434a3966513d3d227d9daba9d4",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496765573931496e3139227d35994406",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694967513278686457526c496e3139227dafd2aab1",
+ "000000d30000004b4a7942f80b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694975496e3139227d2b9773b8",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496751324675496e3139227d874170f9",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496765573931496e3139227d35994406",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f6949676447567362434a3966513d3d227de17e04f2",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694967625755696658303d227d27157827",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496759534a3966513d3d227d93de2078",
+ "000000d70000004bbff9e4380b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f694967596d6c30496e3139227d47f89aea",
+ "000000db0000004b7a0909390b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496759574a76645851696658303d227defe54875",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69496765573931636e4e6c624759696658303d227d2c5dd674",
+ "000000d30000004b4a7942f80b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131396b5a57783059534973496d6c755a475634496a6f774c434a6b5a5778305953493665794a306558426c496a6f69644756346446396b5a57783059534973496e526c654851694f69492f496e3139227dac32c541",
+ "0000009b0000004b22fa51700b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a306558426c496a6f695932397564475675644639696247396a6131397a64473977496977696157356b5a5867694f6a4239227dc0567ebe",
+ ],
+ ],
+ "meta.llama2-13b-chat-v1::[INST] The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "cce6b34c-812c-4f97-8885-515829aa9639",
+ },
+ 200,
+ [
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a62335675644349364e7a5973496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4573496e4e3062334266636d566863323975496a70756457787366513d3d227d37a74e44",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694945686c624778764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f794c434a7a6447397758334a6c59584e7662694936626e56736248303d227d82bd6987",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d79776963335276634639795a57467a623234694f6d353162477839227d69a22395",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949456c304969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f304c434a7a6447397758334a6c59584e7662694936626e56736248303d227d0c311931",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694a794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e53776963335276634639795a57467a623234694f6d353162477839227d208768c0",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6963794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e69776963335276634639795a57467a623234694f6d353162477839227da857c5f9",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494764795a5746304969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f334c434a7a6447397758334a6c59584e7662694936626e56736248303d227d1c1ffb32",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f344c434a7a6447397758334a6c59584e7662694936626e56736248303d227dc6a01d05",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949485268624773694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a6b73496e4e3062334266636d566863323975496a70756457787366513d3d227d1ade656f",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f784d43776963335276634639795a57467a623234694f6d353162477839227df38e415b",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949486c7664534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d544573496e4e3062334266636d566863323975496a70756457787366513d3d227df8043fc6",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d544973496e4e3062334266636d566863323975496a70756457787366513d3d227da3876c32",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949456b694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a457a4c434a7a6447397758334a6c59584e7662694936626e56736248303d227df2429ce3",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694a794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d545173496e4e3062334266636d566863323975496a70756457787366513d3d227d3e44d0ad",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6962534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d545573496e4e3062334266636d566863323975496a70756457787366513d3d227d43dcbe46",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494746754969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f784e69776963335276634639795a57467a623234694f6d353162477839227ddefd896e",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494545694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a45334c434a7a6447397758334a6c59584e7662694936626e56736248303d227d865b949f",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6953534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d546773496e4e3062334266636d566863323975496a70756457787366513d3d227d87c627db",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d546b73496e4e3062334266636d566863323975496a70756457787366513d3d227d4efcc97d",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494746755a434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a4173496e4e3062334266636d566863323975496a70756457787366513d3d227dc9fb2b36",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949456b694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a49784c434a7a6447397758334a6c59584e7662694936626e56736248303d227d7d13633f",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694a794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a4973496e4e3062334266636d566863323975496a70756457787366513d3d227dabef6b74",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6962534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a4d73496e4e3062334266636d566863323975496a70756457787366513d3d227dd677059f",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947686c636d55694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a49304c434a7a6447397758334a6c59584e7662694936626e56736248303d227d778b6773",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f794e53776963335276634639795a57467a623234694f6d353162477839227d3f000197",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947686c624841694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a49324c434a7a6447397758334a6c59584e7662694936626e56736248303d227d262f2f7c",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494746756333646c63694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a6373496e4e3062334266636d566863323975496a70756457787366513d3d227dd2632962",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947467565534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a6773496e4e3062334266636d566863323975496a70756457787366513d3d227d2ca266eb",
+ "000000ef0000004b2ea8177f0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494846315a584e306157397563794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a6b73496e4e3062334266636d566863323975496a70756457787366513d3d227d420dc939",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949486c7664534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d7a4173496e4e3062334266636d566863323975496a70756457787366513d3d227d3ba3c73a",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494731705a3268304969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f7a4d53776963335276634639795a57467a623234694f6d353162477839227d742bb4c0",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949476868646d55694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d794c434a7a6447397758334a6c59584e7662694936626e56736248303d227d3ba80967",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d7a4d73496e4e3062334266636d566863323975496a70756457787366513d3d227d7d7ec395",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694946646f595851694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d304c434a7a6447397758334a6c59584e7662694936626e56736248303d227d2aff1e3e",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694a794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d7a5573496e4e3062334266636d566863323975496a70756457787366513d3d227dfde32851",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6963794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d7a5973496e4e3062334266636d566863323975496a70756457787366513d3d227de1891392",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494739754969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f7a4e79776963335276634639795a57467a623234694f6d353162477839227d1923b2d1",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949486c76645849694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d344c434a7a6447397758334a6c59584e7662694936626e56736248303d227d0475ce8e",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949473170626d51694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d354c434a7a6447397758334a6c59584e7662694936626e56736248303d227d39ee9d93",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6950794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e444173496e4e3062334266636d566863323975496a70756457787366513d3d227d23f8fbfc",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e444573496e4e3062334266636d566863323975496a70756457787366513d3d227d034c8c7e",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f304d69776963335276634639795a57467a623234694f6d353162477839227dd1325fa5",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f304d79776963335276634639795a57467a623234694f6d353162477839227d57fad2a0",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f304e43776963335276634639795a57467a623234694f6d353162477839227d2ba0111c",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69384a2b6b6c434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e445573496e4e3062334266636d566863323975496a70756457787366513d3d227d5170fd7c",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494552764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f304e69776963335276634639795a57467a623234694f6d353162477839227d20bec256",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949486c7664534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e446373496e4e3062334266636d566863323975496a70756457787366513d3d227d3fb405c3",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949476868646d55694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a51344c434a7a6447397758334a6c59584e7662694936626e56736248303d227df9307c2f",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494745694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a51354c434a7a6447397758334a6c59584e7662694936626e56736248303d227daac7d4f1",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949484e775a574e705a6d6c6a4969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f314d43776963335276634639795a57467a623234694f6d353162477839227d8320a489",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694948527663476c6a4969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f314d53776963335276634639795a57467a623234694f6d353162477839227d8422eb4f",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949486c7664534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e544973496e4e3062334266636d566863323975496a70756457787366513d3d227d071ce2e2",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694a794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e544d73496e4e3062334266636d566863323975496a70756457787366513d3d227ddc056360",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f695a434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e545173496e4e3062334266636d566863323975496a70756457787366513d3d227d41081c0a",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949477870613255694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a55314c434a7a6447397758334a6c59584e7662694936626e56736248303d227d25fd8278",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f314e69776963335276634639795a57467a623234694f6d353162477839227d7fdc9979",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947527063324e3163334d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a55334c434a7a6447397758334a6c59584e7662694936626e56736248303d227dbdcf1f52",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e546773496e4e3062334266636d566863323975496a70756457787366513d3d227db1e41459",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494739794969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f314f53776963335276634639795a57467a623234694f6d353162477839227d95ce8435",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494752764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f324d43776963335276634639795a57467a623234694f6d353162477839227da1fc1b06",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949486c7664534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a4573496e4e3062334266636d566863323975496a70756457787366513d3d227dfe642df8",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949477031633351694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a59794c434a7a6447397758334a6c59584e7662694936626e56736248303d227d87d3b447",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949486468626e51694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a597a4c434a7a6447397758334a6c59584e7662694936626e56736248303d227db7527265",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f324e43776963335276634639795a57467a623234694f6d353162477839227dd2c7cbab",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949474e6f595851694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a59314c434a7a6447397758334a6c59584e7662694936626e56736248303d227d20af0e31",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6950794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a5973496e4e3062334266636d566863323975496a70756457787366513d3d227d6f984397",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a6373496e4e3062334266636d566863323975496a70756457787366513d3d227dc29ca445",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f324f43776963335276634639795a57467a623234694f6d353162477839227d0a0f23b6",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f324f53776963335276634639795a57467a623234694f6d353162477839227d8cc7aeb3",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f334d43776963335276634639795a57467a623234694f6d353162477839227d7c290d77",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69384a2b5372434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a4573496e4e3062334266636d566863323975496a70756457787366513d3d227dc3364864",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949456b694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a63794c434a7a6447397758334a6c59584e7662694936626e56736248303d227d92976f7d",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694a794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a4d73496e4e3062334266636d566863323975496a70756457787366513d3d227d6e2881b6",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6962534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a5173496e4e3062334266636d566863323975496a70756457787366513d3d227dedfdb5e0",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947467362434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a5573496e4e3062334266636d566863323975496a70756457787366513d3d227dc682e026",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949475668636e4d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a63324c434a7a6447397758334a6c59584e7662694936626e56736248303d227d8ee6d357",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a6373496e4e3062334266636d566863323975496a70756457787366513d3d227d9d4f992d",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a6773496e4e3062334266636d566863323975496a70756457787366513d3d227d6addbddd",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f334f53776963335276634639795a57467a623234694f6d353162477839227d82204662",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f344d43776963335276634639795a57467a623234694f6d353162477839227d549e9740",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f344d53776963335276634639795a57467a623234694f6d353162477839227dd2561a45",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69384a2b5267694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f444973496e4e3062334266636d566863323975496a70756457787366513d3d227da5139607",
+ "0000018f0000004b7cc6b3970b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f344d79776963335276634639795a57467a623234694f694a7a644739774969776959573168656d39754c574a6c5a484a76593273746157353262324e6864476c76626b316c64484a7059334d694f6e736961573577645852556232746c626b4e7664573530496a6f334e6977696233563063485630564739725a57354462335675644349364f444d73496d6c75646d396a595852706232354d5958526c626d4e35496a6f794d7a63314c434a6d61584a7a64454a356447564d5958526c626d4e35496a6f7a4e6a5a3966513d3d227d34e01b75",
+ ],
+ ],
+ "amazon.titan-text-express-v1::What is 212 degrees Fahrenheit converted to Celsius?": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "b427270f-371a-458d-81b6-a05aafb2704c",
+ },
+ 200,
+ [
+ "000002370000004bdae582ec0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a7664585277645852555a586830496a6f69584734784947526c5a334a6c5a534247595768795a57356f5a576c3049476c7a494441754e5459675a47566e636d566c637942445a57787a6158567a4c694255614756795a575a76636d5573494449784d69426b5a5764795a575567526d466f636d56756147567064434270626942445a57787a6158567a494864766457786b49474a6c494445784e5334334d6934694c434a70626d526c654349364d437769644739305957785064585277645852555a586830564739725a57354462335675644349364d7a5573496d4e76625842735a585270623235535a57467a623234694f694a475355354a553067694c434a70626e42316446526c654852556232746c626b4e7664573530496a6f784d69776959573168656d39754c574a6c5a484a76593273746157353262324e6864476c76626b316c64484a7059334d694f6e736961573577645852556232746c626b4e7664573530496a6f784d6977696233563063485630564739725a57354462335675644349364d7a5573496d6c75646d396a595852706232354d5958526c626d4e35496a6f794d7a4d354c434a6d61584a7a64454a356447564d5958526c626d4e35496a6f794d7a4d356658303d227d358ac004"
+ ],
+ ],
+ "anthropic.claude-instant-v1::Human: What is 212 degrees Fahrenheit converted to Celsius? Assistant:": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "a645548f-0b3a-47ce-a675-f51e6e9037de",
+ },
+ 200,
+ [
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694945686c636d55694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d71ffbf2d",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494746795a534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d9f82f061",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694948526f5a534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dee0662df",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949484e305a58427a4969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d76bf1639",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494852764969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227daf097af1",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949474e76626e5a6c636e51694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d5955803a",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494449784d694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dfa89690e",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694947526c5a334a6c5a584d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dbe5287e4",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494559694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d8732a806",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69595768795a57356f5a576c304969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d066744eb",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494852764969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227daf097af1",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949454e6c62484e7064584d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227df62aca9e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694f694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227de96ff0b6",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6958473563626a45694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227df6e5e085",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694b534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d82afca0e",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694946526f5a534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d73834b92",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949475a76636d3131624745694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d3ad98743",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494852764969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227daf097af1",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949474e76626e5a6c636e51694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d5955803a",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949474a6c6448646c5a5734694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227deb6a0bd6",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494559694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d8732a806",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69595768795a57356f5a576c304969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d066744eb",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494746755a434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d7e666d0f",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949454e6c62484e7064584d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227df62aca9e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949476c7a4969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d9a64e3c4",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694f694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227de96ff0b6",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f695847346749434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d4292c7bb",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949454d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d1c09da34",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494430694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227db45e8aa5",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494367694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d1f5f0f41",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6952694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dc48bec13",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494330694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dae344b5e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949444d794969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d8d3ee747",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694b534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d82afca0e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949436f694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d29a16fe0",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494455694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dc85354c4",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694c794973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d26f20099",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694f534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dfff8a709",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6958473563626a49694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d36dc3354",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694b534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d82afca0e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494642734969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227de1cc18f5",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69645763694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d7e451c81",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949476c754969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227da2508214",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494449784d694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dfa89690e",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694947526c5a334a6c5a584d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dbe5287e4",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494559694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d8732a806",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69595768795a57356f5a576c304969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d066744eb",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949475a7663694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dd72b242b",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494559694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d8732a806",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694f694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227de96ff0b6",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f695847346749434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d4292c7bb",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949454d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d1c09da34",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494430694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227db45e8aa5",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494367694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d1f5f0f41",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694d6a45794969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d184cd7ac",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494330694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dae344b5e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949444d794969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d8d3ee747",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694b534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d82afca0e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949436f694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d29a16fe0",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494455694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dc85354c4",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694c794973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d26f20099",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694f534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dfff8a709",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f695847346749434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d4292c7bb",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949454d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d1c09da34",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494430694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227db45e8aa5",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494445344d434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227ddeedbeac",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949436f694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d29a16fe0",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494455694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dc85354c4",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694c794973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d26f20099",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694f534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dfff8a709",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f695847346749434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d4292c7bb",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949454d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d1c09da34",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494430694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227db45e8aa5",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494445774d434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d0c0fabb4",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6958473563626a4d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dc01b8024",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694b534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d82afca0e",
+ "000000b70000004b26cb93750b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694946526f5a584a6c5a6d39795a534973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d8f97117a",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694c434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d30655726",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494449784d694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dfa89690e",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694947526c5a334a6c5a584d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dbe5287e4",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494559694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227d8732a806",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69595768795a57356f5a576c304969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d066744eb",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949476c7a4969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d9a64e3c4",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949475678645746734969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227d08092f6c",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494852764969776963335276634639795a57467a623234694f6d353162477773496e4e30623341694f6d353162477839227daf097af1",
+ "000000af0000004b765b4f360b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f69494445774d434973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227d0c0fabb4",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694947526c5a334a6c5a584d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227dbe5287e4",
+ "000000b30000004bd34b35b50b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f6949454e6c62484e7064584d694c434a7a6447397758334a6c59584e7662694936626e5673624377696333527663434936626e56736248303d227df62aca9e",
+ "000000ab0000004b83dbe9f60b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694c694973496e4e3062334266636d566863323975496a7075645778734c434a7a64473977496a70756457787366513d3d227dbe4ddce4",
+ "0000016b0000004ba192d2880b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6a623231776247563061573975496a6f694969776963335276634639795a57467a623234694f694a7a6447397758334e6c6358566c626d4e6c496977696333527663434936496c78755847354964573168626a6f694c434a686257463662323474596d566b636d396a61793170626e5a76593246306157397554575630636d6c6a6379493665794a70626e4231644652766132567551323931626e51694f6a45354c434a7664585277645852556232746c626b4e7664573530496a6f354f5377696157353262324e6864476c76626b78686447567559336b694f6a45314e7a4173496d5a70636e4e30516e6c305a5578686447567559336b694f6a51784d583139227d9a4fc171",
+ ],
+ ],
+ "cohere.command-text-v14::What is 212 degrees Fahrenheit converted to Celsius?": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "4f8ab6c5-42d1-4e35-9573-30f9f41f821e",
+ },
+ 200,
+ [
+ "000003f70000004bf8acb9920b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d46306157397563794936573373695a6d6c7561584e6f58334a6c59584e7662694936496b4e505456424d5256524649697769615751694f694a695a5463794e475a6d5953316c5a6a63344c54517a4d444974595464684e7930794e6a4a6a4e4463784f54566a4e5755694c434a305a586830496a6f694946527649474e76626e5a6c636e5167526d466f636d56756147567064434230627942445a57787a6158567a4c43423562335567593246754948567a5a534230614755675a6d39796258567359547063626c78755132567363326c316379413949436847595768795a57356f5a576c30494330674d7a497049436f674e53383558473563626b6c754948526f61584d675932467a5a537767615759676557393149476868646d55674d6a45794947526c5a334a6c5a584d67526d466f636d567561475670644377676557393149474e686269423163325567644768706379426d62334a74645778684948527649474e6862474e31624746305a534230614755675a58463161585a6862475675644342305a5731775a584a68644856795a534270626942445a57787a6158567a4f6c7875584735445a57787a6158567a494430674b4449784d69417449444d794b534171494455764f534139494445774d434171494455764f5341394944557758473563626c526f5a584a6c5a6d39795a5377674d6a45794947526c5a334a6c5a584d67526d466f636d567561475670644342706379426c63585670646d46735a57353049485276494455774947526c5a334a6c5a584d675132567363326c316379346966563073496d6c6b496a6f694e47593459574932597a55744e444a6b4d5330305a544d314c546b314e7a4d744d7a426d4f5759304d5759344d6a466c4969776963484a7662584230496a6f695632686864434270637941794d5449675a47566e636d566c63794247595768795a57356f5a576c3049474e76626e5a6c636e526c5a434230627942445a57787a6158567a50794973496d467459587076626931695a57527962324e724c576c75646d396a595852706232354e5a58527961574e7a496a7037496d6c7563485630564739725a57354462335675644349364f5377696233563063485630564739725a57354462335675644349364f544573496d6c75646d396a595852706232354d5958526c626d4e35496a6f794f5463794c434a6d61584a7a64454a356447564d5958526c626d4e35496a6f794f5463796658303d227deba065e0"
+ ],
+ ],
+ "meta.llama2-13b-chat-v1::What is 212 degrees Fahrenheit converted to Celsius?": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "6dd99878-0919-4f92-850c-48f50f923b76",
+ },
+ 200,
+ [
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a62335675644349364d546373496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4573496e4e3062334266636d566863323975496a70756457787366513d3d227d8ad5573b",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694946646f595851694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4973496e4e3062334266636d566863323975496a70756457787366513d3d227dc79406b1",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949476c7a4969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f7a4c434a7a6447397758334a6c59584e7662694936626e56736248303d227d03c98d5f",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694948526f5a534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e43776963335276634639795a57467a623234694f6d353162477839227d87f1a596",
+ "000000ef0000004b2ea8177f0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949474e76626e5a6c636e4e70623234694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a5573496e4e3062334266636d566863323975496a70756457787366513d3d227d10bef8bd",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949475a76636d3131624745694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a5973496e4e3062334266636d566863323975496a70756457787366513d3d227d587688a5",
+ "000000df0000004b8f89aff90b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6950794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e79776963335276634639795a57467a623234694f6d353162477839227d2a55ad0a",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a6773496e4e3062334266636d566863323975496a70756457787366513d3d227d6967bb80",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a6b73496e4e3062334266636d566863323975496a70756457787366513d3d227dfbf995fe",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d544173496e4e3062334266636d566863323975496a70756457787366513d3d227d2d794c92",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d544573496e4e3062334266636d566863323975496a70756457787366513d3d227d28b94ab1",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d544973496e4e3062334266636d566863323975496a70756457787366513d3d227dce6d78c6",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947526c5a334a6c5a584d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a457a4c434a7a6447397758334a6c59584e7662694936626e56736248303d227d4f48e7b9",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494559694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a45304c434a7a6447397758334a6c59584e7662694936626e56736248303d227d0992a7e0",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69595768795a5734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a45314c434a7a6447397758334a6c59584e7662694936626e56736248303d227ddcb2ffa3",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f696147567064434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d545973496e4e3062334266636d566863323975496a70756457787366513d3d227d53be207a",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949476c7a4969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f784e79776963335276634639795a57467a623234694f6d353162477839227d14ca1e11",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949475678645746734969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f784f43776963335276634639795a57467a623234694f6d353162477839227de610d9c7",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f784f53776963335276634639795a57467a623234694f6d353162477839227d0d870a4e",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a4173496e4e3062334266636d566863323975496a70756457787366513d3d227dad6d87d8",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a4573496e4e3062334266636d566863323975496a70756457787366513d3d227d435fabd5",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a4973496e4e3062334266636d566863323975496a70756457787366513d3d227d57d9bb71",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a4d73496e4e3062334266636d566863323975496a70756457787366513d3d227d2653a15b",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947526c5a334a6c5a584d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a49304c434a7a6447397758334a6c59584e7662694936626e56736248303d227db83e662f",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949454e6c62434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a5573496e4e3062334266636d566863323975496a70756457787366513d3d227d49763324",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6963326b694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a49324c434a7a6447397758334a6c59584e7662694936626e56736248303d227d57ef43c7",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6964584d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a49334c434a7a6447397758334a6c59584e7662694936626e56736248303d227d31fa2ded",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d6a6773496e4e3062334266636d566863323975496a70756457787366513d3d227d45d624b4",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a49354c434a7a6447397758334a6c59584e7662694936626e56736248303d227d50170c09",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d774c434a7a6447397758334a6c59584e7662694936626e56736248303d227d334aff43",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f695647686c4969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f7a4d53776963335276634639795a57467a623234694f6d353162477839227da3b5dcb6",
+ "000000ef0000004b2ea8177f0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949474e76626e5a6c636e4e70623234694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d794c434a7a6447397758334a6c59584e7662694936626e56736248303d227d04d2363e",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949475a76636d3131624745694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d7a4c434a7a6447397758334a6c59584e7662694936626e56736248303d227dcdac512b",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949476c7a4969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f7a4e43776963335276634639795a57467a623234694f6d353162477839227d31fe2917",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694f694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d7a5573496e4e3062334266636d566863323975496a70756457787366513d3d227d12ef5dd9",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d324c434a7a6447397758334a6c59584e7662694936626e56736248303d227d092a8c14",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d334c434a7a6447397758334a6c59584e7662694936626e56736248303d227d07cd64c5",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69777241694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a4d344c434a7a6447397758334a6c59584e7662694936626e56736248303d227d3ac46cfa",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6951794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364d7a6b73496e4e3062334266636d566863323975496a70756457787366513d3d227db44f4efe",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494430694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a51774c434a7a6447397758334a6c59584e7662694936626e56736248303d227d20eaba75",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494367694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a51784c434a7a6447397758334a6c59584e7662694936626e56736248303d227d7e72f783",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69777241694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a51794c434a7a6447397758334a6c59584e7662694936626e56736248303d227df85c19b2",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6952694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e444d73496e4e3062334266636d566863323975496a70756457787366513d3d227dd6166c06",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494330694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a51304c434a7a6447397758334a6c59584e7662694936626e56736248303d227d73d3f258",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e445573496e4e3062334266636d566863323975496a70756457787366513d3d227d1e15e297",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e445973496e4e3062334266636d566863323975496a70756457787366513d3d227de161d81d",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e446373496e4e3062334266636d566863323975496a70756457787366513d3d227d9b436cbd",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694b534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e446773496e4e3062334266636d566863323975496a70756457787366513d3d227d664be53d",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494d4f584969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f304f53776963335276634639795a57467a623234694f6d353162477839227d437bffb5",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e544173496e4e3062334266636d566863323975496a70756457787366513d3d227dab0d95e6",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694e534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e544573496e4e3062334266636d566863323975496a70756457787366513d3d227dd4e37d1a",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e544973496e4e3062334266636d566863323975496a70756457787366513d3d227d5547f6e9",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694f534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e544d73496e4e3062334266636d566863323975496a70756457787366513d3d227d47430ae1",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a55304c434a7a6447397758334a6c59584e7662694936626e56736248303d227deaa8070b",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a55314c434a7a6447397758334a6c59584e7662694936626e56736248303d227de44fefda",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69553238694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a55324c434a7a6447397758334a6c59584e7662694936626e56736248303d227dcc1d90ca",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e546373496e4e3062334266636d566863323975496a70756457787366513d3d227dc06e0e73",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f314f43776963335276634639795a57467a623234694f6d353162477839227dfd8f11d0",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949474e76626e5a6c636e51694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a55354c434a7a6447397758334a6c59584e7662694936626e56736248303d227d9fe07832",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a4173496e4e3062334266636d566863323975496a70756457787366513d3d227dc0eb7482",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a4573496e4e3062334266636d566863323975496a70756457787366513d3d227d5a934486",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a4973496e4e3062334266636d566863323975496a70756457787366513d3d227dbc4776f1",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a4d73496e4e3062334266636d566863323975496a70756457787366513d3d227db98770d2",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694947526c5a334a6c5a584d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a59304c434a7a6447397758334a6c59584e7662694936626e56736248303d227da56708c6",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494559694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a59314c434a7a6447397758334a6c59584e7662694936626e56736248303d227d88b20fa6",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69595768795a5734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a59324c434a7a6447397758334a6c59584e7662694936626e56736248303d227d405d8647",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f696147567064434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a6373496e4e3062334266636d566863323975496a70756457787366513d3d227da9e4b83e",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494852764969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f324f43776963335276634639795a57467a623234694f6d353162477839227deea728a3",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949454e6c62434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e6a6b73496e4e3062334266636d566863323975496a70756457787366513d3d227da940502e",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6963326b694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a63774c434a7a6447397758334a6c59584e7662694936626e56736248303d227d8cecd403",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6964584d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a63784c434a7a6447397758334a6c59584e7662694936626e56736248303d227dbb968e47",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a4973496e4e3062334266636d566863323975496a70756457787366513d3d227d93200836",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694948646c4969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f334d79776963335276634639795a57467a623234694f6d353162477839227dd66d2ee7",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949474e6862694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a5173496e4e3062334266636d566863323975496a70756457787366513d3d227dec7f3121",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694948567a5a534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a5573496e4e3062334266636d566863323975496a70756457787366513d3d227dbb1f0174",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694948526f5a534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364e7a5973496e4e3062334266636d566863323975496a70756457787366513d3d227db9f2b3dd",
+ "000000eb0000004bdb28b1bf0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949475a76636d3131624745694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a63334c434a7a6447397758334a6c59584e7662694936626e56736248303d227d2b853909",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949477870613255694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a63344c434a7a6447397758334a6c59584e7662694936626e56736248303d227d7e216de9",
+ "000000e70000004b1ed85cbe0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694948526f61584d694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a63354c434a7a6447397758334a6c59584e7662694936626e56736248303d227db006b745",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694f694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f444173496e4e3062334266636d566863323975496a70756457787366513d3d227d5cde6a12",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a67784c434a7a6447397758334a6c59584e7662694936626e56736248303d227d8da1c76f",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a67794c434a7a6447397758334a6c59584e7662694936626e56736248303d227d83462fbe",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69777241694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a677a4c434a7a6447397758334a6c59584e7662694936626e56736248303d227d85d084c5",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6951794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f445173496e4e3062334266636d566863323975496a70756457787366513d3d227d6a97878c",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494430694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a67314c434a7a6447397758334a6c59584e7662694936626e56736248303d227d67065455",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494367694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a67324c434a7a6447397758334a6c59584e7662694936626e56736248303d227d753efc6f",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f446373496e4e3062334266636d566863323975496a70756457787366513d3d227dbfc13d8b",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f446773496e4e3062334266636d566863323975496a70756457787366513d3d227dba013ba8",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f446b73496e4e3062334266636d566863323975496a70756457787366513d3d227d5cd509df",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494330694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a6b774c434a7a6447397758334a6c59584e7662694936626e56736248303d227da846dad7",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f544573496e4e3062334266636d566863323975496a70756457787366513d3d227dfe05defa",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f544973496e4e3062334266636d566863323975496a70756457787366513d3d227d0171e470",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694d694973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f544d73496e4e3062334266636d566863323975496a70756457787366513d3d227df6e3c080",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694b534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f545173496e4e3062334266636d566863323975496a70756457787366513d3d227df5a613bd",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69494d4f584969776963484a7662584230583352766132567558324e7664573530496a7075645778734c434a6e5a57356c636d463061573975583352766132567558324e7664573530496a6f354e53776963335276634639795a57467a623234694f6d353162477839227d4a635728",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f6949434973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f545973496e4e3062334266636d566863323975496a70756457787366513d3d227d71c29e6d",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694e534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f546373496e4e3062334266636d566863323975496a70756457787366513d3d227d839ce6c1",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694c794973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f546773496e4e3062334266636d566863323975496a70756457787366513d3d227de12c5966",
+ "000000e30000004beb58fa7e0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f694f534973496e427962323177644639306232746c626c396a6233567564434936626e5673624377695a3256755a584a6864476c76626c39306232746c626c396a62335675644349364f546b73496e4e3062334266636d566863323975496a70756457787366513d3d227d103c913a",
+ "000001970000004b2c566fd40b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a6e5a57356c636d463061573975496a6f69584734694c434a77636d397463485266644739725a57356659323931626e51694f6d353162477773496d646c626d56795958527062323566644739725a57356659323931626e51694f6a45774d43776963335276634639795a57467a623234694f694a735a57356e644767694c434a686257463662323474596d566b636d396a61793170626e5a76593246306157397554575630636d6c6a6379493665794a70626e4231644652766132567551323931626e51694f6a45334c434a7664585277645852556232746c626b4e7664573530496a6f784d444173496d6c75646d396a595852706232354d5958526c626d4e35496a6f794e7a59304c434a6d61584a7a64454a356447564d5958526c626d4e35496a6f7a4d7a683966513d3d227d7f984a9b",
+ ],
+ ],
+ "amazon.titan-text-express-v1::Malformed Streaming Chunk": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "a5a8cebb-fd33-4437-8168-5667fbdfc1fb",
+ },
+ 200,
+ [
+ # Payload is intentionally damaged to throw an exception
+ "00004bdae582ec0b3a6576656e742d747970650700056368756e6b0d3a636f6e74656e742d747970650700106170706c69636174696f6e2f6a736f6e0d3a6d6573736167652d747970650700056576656e747b226279746573223a2265794a7664585277645852555a586830496a6f69584734784947526c5a334a6c5a534247595768795a57356f5a576c3049476c7a494441754e5459675a47566e636d566c637942445a57787a6158567a4c694255614756795a575a76636d5573494449784d69426b5a5764795a575567526d466f636d56756147567064434270626942445a57787a6158567a494864766457786b49474a6c494445784e5334334d6934694c434a70626d526c654349364d437769644739305957785064585277645852555a586830564739725a57354462335675644349364d7a5573496d4e76625842735a585270623235535a57467a623234694f694a475355354a553067694c434a70626e42316446526c654852556232746c626b4e7664573530496a6f784d69776959573168656d39754c574a6c5a484a76593273746157353262324e6864476c76626b316c64484a7059334d694f6e736961573577645852556232746c626b4e7664573530496a6f784d6977696233563063485630564739725a57354462335675644349364d7a5573496d6c75646d396a595852706232354d5958526c626d4e35496a6f794d7a4d354c434a6d61584a7a64454a356447564d5958526c626d4e35496a6f794d7a4d356658303d227d358ac004"
+ ],
+ ],
+ "amazon.titan-text-express-v1::Malformed Streaming Body": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "a5a8cebb-fd33-4437-8168-5667fbdfc1fb",
+ "x-amzn-errortype": "ValidationException:http://internal.amazon.com/coral/com.amazon.bedrock/",
+ },
+ 200,
+ [
+ # Computes an example payload for an error inside a stream, from human readable format to a hex string.
+ encode_streaming_payload(
+ {"event-type": "chunk", "content-type": "application/json"},
+ {
+ "outputText": "ValidationException",
+ "index": 0,
+ "totalOutputTextTokenCount": 35,
+ "completionReason": "FINISH",
+ "inputTextTokenCount": 12,
+ "amazon-bedrock-invocationMetrics": {
+ "inputTokenCount": 12,
+ "outputTokenCount": 35,
+ "invocationLatency": 2339,
+ "firstByteLatency": 2339,
+ },
+ },
+ malformed_body=True,
+ ).hex()
+ ],
+ ],
+ "amazon.titan-text-express-v1::Streaming Exception": [
+ {
+ "Content-Type": "application/vnd.amazon.eventstream",
+ "x-amzn-RequestId": "a5a8cebb-fd33-4437-8168-5667fbdfc1fb",
+ "x-amzn-errortype": "ValidationException:http://internal.amazon.com/coral/com.amazon.bedrock/",
+ },
+ 200,
+ [
+ # Computes an example payload for an error inside a stream, from human readable format to a hex string.
+ encode_streaming_payload(
+ {
+ "event-type": "chunk",
+ "content-type": "application/json",
+ "message-type": "error",
+ "exception-type": "ValidationException",
+ "error-code": "ValidationException",
+ "error-message": "Malformed input request, please reformat your input and try again.",
+ },
+ {
+ "outputText": "ValidationException",
+ "index": 0,
+ "totalOutputTextTokenCount": 35,
+ "completionReason": "FINISH",
+ "inputTextTokenCount": 12,
+ "amazon-bedrock-invocationMetrics": {
+ "inputTokenCount": 12,
+ "outputTokenCount": 35,
+ "invocationLatency": 2339,
+ "firstByteLatency": 2339,
+ },
+ },
+ ).hex()
+ ],
+ ],
+}
+
+RESPONSES = {
+ "mistral.mistral-7b-instruct-v0%3A2::[INST] What is 212 degrees Fahrenheit converted to Celsius? [/INST]": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "48c7ee13-7790-461f-959f-04b0a4cf91c8"},
+ 200,
+ {
+ "outputs": [
+ {
+ "text": " To convert a temperature from Fahrenheit to Celsius, you can use the following formula:\n\nCelsius = (Fahrenheit - 32) \u00d7 5/9\n\nSo, to convert 212 degrees Fahrenheit to Celsius:\n\nCelsius = (212 - 32) \u00d7 5/9\nCelsius = 180.56 \u00d7 5/9\nCelsius",
+ "stop_reason": "length",
+ }
+ ]
+ },
+ ],
+ "amazon.titan-text-express-v1::User: The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "884db5c9-18ab-4f27-8892-33656176a2e6"},
+ 200,
+ {
+ "inputTextTokenCount": 72,
+ "results": [
+ {"tokenCount": 23, "outputText": " Hello, how can I help you today?", "completionReason": "FINISH"}
+ ],
+ },
+ ],
+ "anthropic.claude-instant-v1::The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "1a72a1f6-310f-469c-af1d-2c59eb600089"},
+ 200,
+ {
+ "id": "compl_01EGAoDn3azRGBGFLADWEzn7",
+ "type": "message",
+ "role": "assistant",
+ "content": [{"type": "text", "text": "Hello! It's nice to meet you."}],
+ "model": "claude-instant-1.2",
+ "stop_reason": "end_turn",
+ "stop_sequence": None,
+ "usage": {"input_tokens": 73, "output_tokens": 13},
+ },
+ ],
+ "meta.llama2-13b-chat-v1::[INST] The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "cce6b34c-812c-4f97-8885-515829aa9639"},
+ 200,
+ {
+ "generation": " Hello! It's great to talk to you! I'm an AI, and I'm here to help answer any questions you might have. What's on your mind? \ud83e\udd14 Do you have a specific topic you'd like to discuss, or is there something you'd like to know? \ud83e\udd13 I'm all ears! \ud83d\udc42",
+ "prompt_token_count": 76,
+ "generation_token_count": 86,
+ "stop_reason": "stop",
+ },
+ ],
+ "ai21.j2-mid-v1::What is 212 degrees Fahrenheit converted to Celsius?": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "228ee63f-4eca-4b7d-b679-bc920de63525"},
+ 200,
+ {
+ "id": 1234,
+ "prompt": {
+ "text": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "tokens": [
+ {
+ "generatedToken": {
+ "token": "\u2581What\u2581is",
+ "logprob": -7.703575134277344,
+ "raw_logprob": -7.703575134277344,
+ },
+ "topTokens": None,
+ "textRange": {"start": 0, "end": 7},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581",
+ "logprob": -3.4491159915924072,
+ "raw_logprob": -3.4491159915924072,
+ },
+ "topTokens": None,
+ "textRange": {"start": 7, "end": 8},
+ },
+ {
+ "generatedToken": {
+ "token": "212",
+ "logprob": -9.432294845581055,
+ "raw_logprob": -9.432294845581055,
+ },
+ "topTokens": None,
+ "textRange": {"start": 8, "end": 11},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581degrees\u2581Fahrenheit",
+ "logprob": -9.64009952545166,
+ "raw_logprob": -9.64009952545166,
+ },
+ "topTokens": None,
+ "textRange": {"start": 11, "end": 30},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581converted\u2581to",
+ "logprob": -8.4347505569458,
+ "raw_logprob": -8.4347505569458,
+ },
+ "topTokens": None,
+ "textRange": {"start": 30, "end": 43},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581Celsius",
+ "logprob": -0.17065171897411346,
+ "raw_logprob": -0.17065171897411346,
+ },
+ "topTokens": None,
+ "textRange": {"start": 43, "end": 51},
+ },
+ {
+ "generatedToken": {
+ "token": "?",
+ "logprob": -8.000349998474121,
+ "raw_logprob": -8.000349998474121,
+ },
+ "topTokens": None,
+ "textRange": {"start": 51, "end": 52},
+ },
+ ],
+ },
+ "completions": [
+ {
+ "data": {
+ "text": "\n212 degrees Fahrenheit is equal to 100 degrees Celsius.",
+ "tokens": [
+ {
+ "generatedToken": {
+ "token": "<|newline|>",
+ "logprob": 0.0,
+ "raw_logprob": -1.0609570381348021e-05,
+ },
+ "topTokens": None,
+ "textRange": {"start": 0, "end": 1},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581",
+ "logprob": -0.0012434140080586076,
+ "raw_logprob": -0.017860885709524155,
+ },
+ "topTokens": None,
+ "textRange": {"start": 1, "end": 1},
+ },
+ {
+ "generatedToken": {
+ "token": "212",
+ "logprob": -0.047134462743997574,
+ "raw_logprob": -0.12055955827236176,
+ },
+ "topTokens": None,
+ "textRange": {"start": 1, "end": 4},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581degrees\u2581Fahrenheit",
+ "logprob": -0.006817296147346497,
+ "raw_logprob": -0.052842844277620316,
+ },
+ "topTokens": None,
+ "textRange": {"start": 4, "end": 23},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581is\u2581equal\u2581to",
+ "logprob": -0.008958976715803146,
+ "raw_logprob": -0.0576501227915287,
+ },
+ "topTokens": None,
+ "textRange": {"start": 23, "end": 35},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581",
+ "logprob": -4.756337511935271e-05,
+ "raw_logprob": -0.002072569215670228,
+ },
+ "topTokens": None,
+ "textRange": {"start": 35, "end": 36},
+ },
+ {
+ "generatedToken": {
+ "token": "100",
+ "logprob": -1.6689286894688848e-06,
+ "raw_logprob": -0.00014327930693980306,
+ },
+ "topTokens": None,
+ "textRange": {"start": 36, "end": 39},
+ },
+ {
+ "generatedToken": {
+ "token": "\u2581degrees\u2581Celsius",
+ "logprob": -0.0009832315845414996,
+ "raw_logprob": -0.009537134319543839,
+ },
+ "topTokens": None,
+ "textRange": {"start": 39, "end": 55},
+ },
+ {
+ "generatedToken": {
+ "token": ".",
+ "logprob": -0.009822133928537369,
+ "raw_logprob": -0.04313727468252182,
+ },
+ "topTokens": None,
+ "textRange": {"start": 55, "end": 56},
+ },
+ {
+ "generatedToken": {
+ "token": "<|endoftext|>",
+ "logprob": -0.10973381996154785,
+ "raw_logprob": -0.2600202262401581,
+ },
+ "topTokens": None,
+ "textRange": {"start": 56, "end": 56},
+ },
+ ],
+ },
+ "finishReason": {"reason": "endoftext"},
+ }
+ ],
+ },
+ ],
+ "amazon.titan-text-express-v1::What is 212 degrees Fahrenheit converted to Celsius?": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "81508a1c-33a8-4294-8743-f0c629af2f49"},
+ 200,
+ {
+ "inputTextTokenCount": 12,
+ "results": [
+ {
+ "tokenCount": 32,
+ "outputText": "\n1 degree Fahrenheit is 0.56 Celsius. Therefore, 212 degree Fahrenheit in Celsius would be 115.42.",
+ "completionReason": "FINISH",
+ }
+ ],
+ },
+ ],
+ "anthropic.claude-instant-v1::Human: What is 212 degrees Fahrenheit converted to Celsius? Assistant:": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "6a886158-b39f-46ce-b214-97458ab76f2f"},
+ 200,
+ {
+ "completion": " Okay, here are the steps to convert 212 degrees Fahrenheit to Celsius:\n\n1) The formula to convert between Fahrenheit and Celsius is:\n C = (F - 32) * 5/9\n\n2) Plug in 212 degrees Fahrenheit for F:\n C = (212 - 32) * 5/9\n C = 180 * 5/9\n C = 100\n\n3) Therefore, 212 degrees Fahrenheit converted to Celsius is 100 degrees Celsius.",
+ "stop_reason": "max_tokens",
+ "stop": None,
+ },
+ ],
+ "cohere.command-text-v14::What is 212 degrees Fahrenheit converted to Celsius?": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "12912a17-aa13-45f3-914c-cc82166f3601"},
+ 200,
+ {
+ "generations": [
+ {
+ "finish_reason": "MAX_TOKENS",
+ "id": "deafebbd-7cdb-461f-8d6a-846602141f8f",
+ "text": " To convert from Fahrenheit to Celsius, you can use the following formula:\n\nCelsius = (Fahrenheit - 32) * 5/9\n\nIn this case, 212 degrees Fahrenheit is converted to Celsius as follows:\n\nCelsius = (212 - 32) * 5/9 = (180) * 5/9 = (180/9) = 20 degrees Celsius\n\nTherefore, 212 degrees Fahrenheit is equivalent to 20 degrees Celsius.\n\nIt's important to note that",
+ }
+ ],
+ "id": "12912a17-aa13-45f3-914c-cc82166f3601",
+ "prompt": "What is 212 degrees Fahrenheit converted to Celsius?",
+ },
+ ],
+ "meta.llama2-13b-chat-v1::What is 212 degrees Fahrenheit converted to Celsius?": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "a168214d-742d-4244-bd7f-62214ffa07df"},
+ 200,
+ {
+ "generation": "\n\n212\u00b0F = ?\u00b0C\n\nPlease help! I'm stuck!\n\nThank you!\n\nI hope this is the correct place to ask this question. Please let me know if it isn't.\n\nI appreciate your help!\n\nBest regards,\n\n[Your Name]",
+ "prompt_token_count": 17,
+ "generation_token_count": 69,
+ "stop_reason": "stop",
+ },
+ ],
+ "amazon.titan-embed-g1-text-02::This is an embedding test.": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "b10ac895-eae3-4f07-b926-10b2866c55ed"},
+ 200,
+ {
+ "embedding": [
+ -0.14160156,
+ 0.034423828,
+ 0.54296875,
+ 0.10986328,
+ 0.053466797,
+ 0.3515625,
+ 0.12988281,
+ -0.0002708435,
+ -0.21484375,
+ 0.060302734,
+ 0.58984375,
+ -0.5859375,
+ 0.52734375,
+ 0.82421875,
+ -0.91015625,
+ -0.19628906,
+ 0.45703125,
+ 0.609375,
+ -0.67578125,
+ 0.39453125,
+ -0.46875,
+ -0.25390625,
+ -0.21191406,
+ 0.114746094,
+ 0.31640625,
+ -0.41015625,
+ -0.32617188,
+ -0.43554688,
+ 0.4765625,
+ -0.4921875,
+ 0.40429688,
+ 0.06542969,
+ 0.859375,
+ -0.008056641,
+ -0.19921875,
+ 0.072753906,
+ 0.33203125,
+ 0.69921875,
+ 0.39453125,
+ 0.15527344,
+ 0.08886719,
+ -0.25,
+ 0.859375,
+ 0.22949219,
+ -0.19042969,
+ 0.13769531,
+ -0.078125,
+ 0.41210938,
+ 0.875,
+ 0.5234375,
+ 0.59765625,
+ -0.22949219,
+ -0.22558594,
+ -0.47460938,
+ 0.37695312,
+ 0.51953125,
+ -0.5703125,
+ 0.46679688,
+ 0.43554688,
+ 0.17480469,
+ -0.080566406,
+ -0.16699219,
+ -0.734375,
+ -1.0625,
+ -0.33984375,
+ 0.390625,
+ -0.18847656,
+ -0.5234375,
+ -0.48828125,
+ 0.44921875,
+ -0.09814453,
+ -0.3359375,
+ 0.087402344,
+ 0.36914062,
+ 1.3203125,
+ 0.25585938,
+ 0.14746094,
+ -0.059570312,
+ -0.15820312,
+ -0.037353516,
+ -0.61328125,
+ -0.6484375,
+ -0.35351562,
+ 0.55078125,
+ -0.26953125,
+ 0.90234375,
+ 0.3671875,
+ 0.31054688,
+ 0.00014019012,
+ -0.171875,
+ 0.025512695,
+ 0.5078125,
+ 0.11621094,
+ 0.33203125,
+ 0.8125,
+ -0.3046875,
+ -1.078125,
+ -0.5703125,
+ 0.26171875,
+ -0.4609375,
+ 0.203125,
+ 0.44726562,
+ -0.5078125,
+ 0.41601562,
+ -0.1953125,
+ 0.028930664,
+ -0.57421875,
+ 0.2265625,
+ 0.13574219,
+ -0.040039062,
+ -0.22949219,
+ -0.515625,
+ -0.19042969,
+ -0.30078125,
+ 0.10058594,
+ -0.66796875,
+ 0.6015625,
+ 0.296875,
+ -0.765625,
+ -0.87109375,
+ 0.2265625,
+ 0.068847656,
+ -0.088378906,
+ -0.1328125,
+ -0.796875,
+ -0.37304688,
+ 0.47460938,
+ -0.3515625,
+ -0.8125,
+ -0.32226562,
+ 0.265625,
+ 0.3203125,
+ -0.4140625,
+ -0.49023438,
+ 0.859375,
+ -0.19140625,
+ -0.6328125,
+ 0.10546875,
+ -0.5625,
+ 0.66015625,
+ 0.26171875,
+ -0.2109375,
+ 0.421875,
+ -0.82421875,
+ 0.29296875,
+ 0.17773438,
+ 0.24023438,
+ 0.5078125,
+ -0.49804688,
+ -0.10205078,
+ 0.10498047,
+ -0.36132812,
+ -0.47460938,
+ -0.20996094,
+ 0.010070801,
+ -0.546875,
+ 0.66796875,
+ -0.123046875,
+ -0.75390625,
+ 0.19628906,
+ 0.17480469,
+ 0.18261719,
+ -0.96875,
+ -0.26171875,
+ 0.4921875,
+ -0.40039062,
+ 0.296875,
+ 0.1640625,
+ -0.20507812,
+ -0.36132812,
+ 0.76171875,
+ -1.234375,
+ -0.625,
+ 0.060058594,
+ -0.09375,
+ -0.14746094,
+ 1.09375,
+ 0.057861328,
+ 0.22460938,
+ -0.703125,
+ 0.07470703,
+ 0.23828125,
+ -0.083984375,
+ -0.54296875,
+ 0.5546875,
+ -0.5,
+ -0.390625,
+ 0.106933594,
+ 0.6640625,
+ 0.27734375,
+ -0.953125,
+ 0.35351562,
+ -0.7734375,
+ -0.77734375,
+ 0.16503906,
+ -0.42382812,
+ 0.36914062,
+ 0.020141602,
+ -1.3515625,
+ 0.18847656,
+ 0.13476562,
+ -0.034179688,
+ -0.03930664,
+ -0.03857422,
+ -0.027954102,
+ 0.73828125,
+ -0.18945312,
+ -0.09814453,
+ -0.46289062,
+ 0.36914062,
+ 0.033203125,
+ 0.020874023,
+ -0.703125,
+ 0.91796875,
+ 0.38671875,
+ 0.625,
+ -0.19335938,
+ -0.16796875,
+ -0.58203125,
+ 0.21386719,
+ -0.032470703,
+ -0.296875,
+ -0.15625,
+ -0.1640625,
+ -0.74609375,
+ 0.328125,
+ 0.5546875,
+ -0.1953125,
+ 1.0546875,
+ 0.171875,
+ -0.099609375,
+ 0.5234375,
+ 0.05078125,
+ -0.35742188,
+ -0.2734375,
+ -1.3203125,
+ -0.8515625,
+ -0.16015625,
+ 0.01574707,
+ 0.29296875,
+ 0.18457031,
+ -0.265625,
+ 0.048339844,
+ 0.045654297,
+ -0.32226562,
+ 0.087890625,
+ -0.0047302246,
+ 0.38671875,
+ 0.10644531,
+ -0.06225586,
+ 1.03125,
+ 0.94140625,
+ -0.3203125,
+ 0.20800781,
+ -1.171875,
+ 0.48046875,
+ -0.091796875,
+ 0.20800781,
+ -0.1328125,
+ -0.20507812,
+ 0.28125,
+ -0.47070312,
+ -0.09033203,
+ 0.0013809204,
+ -0.08203125,
+ 0.43359375,
+ -0.03100586,
+ -0.060791016,
+ -0.53515625,
+ -1.46875,
+ 0.000101566315,
+ 0.515625,
+ 0.40625,
+ -0.10498047,
+ -0.15820312,
+ -0.009460449,
+ -0.77734375,
+ -0.5859375,
+ 0.9765625,
+ 0.099609375,
+ 0.51953125,
+ 0.38085938,
+ -0.09667969,
+ -0.100097656,
+ -0.5,
+ -1.3125,
+ -0.18066406,
+ -0.099121094,
+ 0.26171875,
+ -0.14453125,
+ -0.546875,
+ 0.17578125,
+ 0.484375,
+ 0.765625,
+ 0.45703125,
+ 0.2734375,
+ 0.0028076172,
+ 0.17089844,
+ -0.32421875,
+ -0.37695312,
+ 0.30664062,
+ -0.48046875,
+ 0.07128906,
+ 0.031982422,
+ -0.31054688,
+ -0.055419922,
+ -0.29296875,
+ 0.3359375,
+ -0.296875,
+ 0.47851562,
+ -0.05126953,
+ 0.18457031,
+ -0.01953125,
+ -0.35742188,
+ 0.017944336,
+ -0.25,
+ 0.10595703,
+ 0.17382812,
+ -0.73828125,
+ 0.36914062,
+ -0.15234375,
+ -0.8125,
+ 0.17382812,
+ 0.048095703,
+ 0.5625,
+ -0.33789062,
+ 0.023071289,
+ -0.21972656,
+ 0.16015625,
+ 0.032958984,
+ -1.1171875,
+ -0.984375,
+ 0.83984375,
+ 0.009033203,
+ -0.042236328,
+ -0.46484375,
+ -0.08203125,
+ 0.44726562,
+ -0.765625,
+ -0.3984375,
+ -0.40820312,
+ -0.234375,
+ 0.044189453,
+ 0.119628906,
+ -0.7578125,
+ -0.55078125,
+ -0.4453125,
+ 0.7578125,
+ 0.34960938,
+ 0.96484375,
+ 0.35742188,
+ 0.36914062,
+ -0.35351562,
+ -0.36132812,
+ 1.109375,
+ 0.5859375,
+ 0.85546875,
+ -0.10644531,
+ -0.6953125,
+ -0.0066833496,
+ 0.042236328,
+ -0.06689453,
+ 0.36914062,
+ 0.9765625,
+ -0.3046875,
+ 0.59765625,
+ -0.6640625,
+ 0.21484375,
+ -0.07128906,
+ 1.1328125,
+ -0.51953125,
+ 0.86328125,
+ -0.11328125,
+ 0.15722656,
+ -0.36328125,
+ -0.04638672,
+ 1.4375,
+ 0.18457031,
+ -0.18359375,
+ 0.10595703,
+ -0.49023438,
+ -0.07324219,
+ -0.73046875,
+ -0.119140625,
+ 0.021118164,
+ 0.4921875,
+ -0.46875,
+ 0.28710938,
+ 0.3359375,
+ 0.11767578,
+ -0.2109375,
+ -0.14550781,
+ 0.39648438,
+ -0.27734375,
+ 0.48046875,
+ 0.12988281,
+ 0.45507812,
+ -0.375,
+ -0.84765625,
+ 0.25585938,
+ -0.36523438,
+ 0.8046875,
+ 0.42382812,
+ -0.24511719,
+ 0.54296875,
+ 0.71875,
+ 0.010009766,
+ -0.04296875,
+ 0.083984375,
+ -0.52734375,
+ 0.13964844,
+ -0.27539062,
+ -0.30273438,
+ 1.1484375,
+ -0.515625,
+ -0.19335938,
+ 0.58984375,
+ 0.049072266,
+ 0.703125,
+ -0.04272461,
+ 0.5078125,
+ 0.34960938,
+ -0.3359375,
+ -0.47460938,
+ 0.049316406,
+ 0.36523438,
+ 0.7578125,
+ -0.022827148,
+ -0.71484375,
+ 0.21972656,
+ 0.09716797,
+ -0.203125,
+ -0.36914062,
+ 1.34375,
+ 0.34179688,
+ 0.46679688,
+ 1.078125,
+ 0.26171875,
+ 0.41992188,
+ 0.22363281,
+ -0.515625,
+ -0.5703125,
+ 0.13378906,
+ 0.26757812,
+ -0.22558594,
+ -0.5234375,
+ 0.06689453,
+ 0.08251953,
+ -0.625,
+ 0.16796875,
+ 0.43164062,
+ -0.55859375,
+ 0.28125,
+ 0.078125,
+ 0.6328125,
+ 0.23242188,
+ -0.064941406,
+ -0.004486084,
+ -0.20703125,
+ 0.2734375,
+ 0.453125,
+ -0.734375,
+ 0.04272461,
+ 0.36132812,
+ -0.19628906,
+ -0.12402344,
+ 1.3515625,
+ 0.25585938,
+ 0.4921875,
+ -0.29296875,
+ -0.58984375,
+ 0.021240234,
+ -0.044677734,
+ 0.7578125,
+ -0.7890625,
+ 0.10253906,
+ -0.15820312,
+ -0.5078125,
+ -0.39453125,
+ -0.453125,
+ 0.35742188,
+ 0.921875,
+ 0.44335938,
+ -0.49804688,
+ 0.44335938,
+ 0.31445312,
+ 0.58984375,
+ -1.0078125,
+ -0.22460938,
+ 0.24121094,
+ 0.87890625,
+ 0.66015625,
+ -0.390625,
+ -0.05053711,
+ 0.059570312,
+ 0.36132812,
+ -0.00038719177,
+ -0.017089844,
+ 0.62890625,
+ 0.203125,
+ 0.17480469,
+ 0.025512695,
+ 0.47460938,
+ 0.3125,
+ 1.140625,
+ 0.32421875,
+ -0.057861328,
+ 0.36914062,
+ -0.7265625,
+ -0.51953125,
+ 0.26953125,
+ 0.42773438,
+ 0.064453125,
+ 0.6328125,
+ 0.27148438,
+ -0.11767578,
+ 0.66796875,
+ -0.38671875,
+ 0.5234375,
+ -0.59375,
+ 0.5078125,
+ 0.008239746,
+ -0.34179688,
+ -0.27539062,
+ 0.5234375,
+ 1.296875,
+ 0.29492188,
+ -0.010986328,
+ -0.41210938,
+ 0.59375,
+ 0.061767578,
+ -0.33398438,
+ -2.03125,
+ 0.87890625,
+ -0.010620117,
+ 0.53125,
+ 0.14257812,
+ -0.515625,
+ -1.03125,
+ 0.578125,
+ 0.1875,
+ 0.44335938,
+ -0.33203125,
+ -0.36328125,
+ -0.3203125,
+ 0.29296875,
+ -0.8203125,
+ 0.41015625,
+ -0.48242188,
+ 0.66015625,
+ 0.5625,
+ -0.16503906,
+ -0.54296875,
+ -0.38085938,
+ 0.26171875,
+ 0.62109375,
+ 0.29101562,
+ -0.31054688,
+ 0.23730469,
+ -0.8515625,
+ 0.5234375,
+ 0.15332031,
+ 0.52734375,
+ -0.079589844,
+ -0.080566406,
+ -0.15527344,
+ -0.022827148,
+ 0.030517578,
+ -0.1640625,
+ -0.421875,
+ 0.09716797,
+ 0.03930664,
+ -0.055908203,
+ -0.546875,
+ -0.47851562,
+ 0.091796875,
+ 0.32226562,
+ -0.94140625,
+ -0.04638672,
+ -1.203125,
+ -0.39648438,
+ 0.45507812,
+ 0.296875,
+ -0.45703125,
+ 0.37890625,
+ -0.122558594,
+ 0.28320312,
+ -0.01965332,
+ -0.11669922,
+ -0.34570312,
+ -0.53515625,
+ -0.091308594,
+ -0.9375,
+ -0.32617188,
+ 0.095214844,
+ -0.4765625,
+ 0.37890625,
+ -0.859375,
+ 1.1015625,
+ -0.08935547,
+ 0.46484375,
+ -0.19238281,
+ 0.7109375,
+ 0.040039062,
+ -0.5390625,
+ 0.22363281,
+ -0.70703125,
+ 0.4921875,
+ -0.119140625,
+ -0.26757812,
+ -0.08496094,
+ 0.0859375,
+ -0.00390625,
+ -0.013366699,
+ -0.03955078,
+ 0.07421875,
+ -0.13085938,
+ 0.29101562,
+ -0.12109375,
+ 0.45703125,
+ 0.021728516,
+ 0.38671875,
+ -0.3671875,
+ -0.52734375,
+ -0.115722656,
+ 0.125,
+ 0.5703125,
+ -1.234375,
+ 0.06298828,
+ -0.55859375,
+ 0.60546875,
+ 0.8125,
+ -0.0032958984,
+ -0.068359375,
+ -0.21191406,
+ 0.56640625,
+ 0.17285156,
+ -0.3515625,
+ 0.36328125,
+ -0.99609375,
+ 0.43554688,
+ -0.1015625,
+ 0.07080078,
+ -0.66796875,
+ 1.359375,
+ 0.41601562,
+ 0.15917969,
+ 0.17773438,
+ -0.28710938,
+ 0.021850586,
+ -0.46289062,
+ 0.17578125,
+ -0.03955078,
+ -0.026855469,
+ 0.5078125,
+ -0.65625,
+ 0.0012512207,
+ 0.044433594,
+ -0.18652344,
+ 0.4921875,
+ -0.75390625,
+ 0.0072021484,
+ 0.4375,
+ -0.31445312,
+ 0.20214844,
+ 0.15039062,
+ -0.63671875,
+ -0.296875,
+ -0.375,
+ -0.027709961,
+ 0.013427734,
+ 0.17089844,
+ 0.89453125,
+ 0.11621094,
+ -0.43945312,
+ -0.30859375,
+ 0.02709961,
+ 0.23242188,
+ -0.64453125,
+ -0.859375,
+ 0.22167969,
+ -0.023071289,
+ -0.052734375,
+ 0.3671875,
+ -0.18359375,
+ 0.81640625,
+ -0.11816406,
+ 0.028320312,
+ 0.19042969,
+ 0.012817383,
+ -0.43164062,
+ 0.55859375,
+ -0.27929688,
+ 0.14257812,
+ -0.140625,
+ -0.048583984,
+ -0.014526367,
+ 0.35742188,
+ 0.22753906,
+ 0.13183594,
+ 0.04638672,
+ 0.03930664,
+ -0.29296875,
+ -0.2109375,
+ -0.16308594,
+ -0.48046875,
+ -0.13378906,
+ -0.39257812,
+ 0.29296875,
+ -0.047851562,
+ -0.5546875,
+ 0.08300781,
+ -0.14941406,
+ -0.07080078,
+ 0.12451172,
+ 0.1953125,
+ -0.51171875,
+ -0.048095703,
+ 0.1953125,
+ -0.37695312,
+ 0.46875,
+ -0.084472656,
+ 0.19042969,
+ -0.39453125,
+ 0.69921875,
+ -0.0065307617,
+ 0.25390625,
+ -0.16992188,
+ -0.5078125,
+ 0.016845703,
+ 0.27929688,
+ -0.22070312,
+ 0.671875,
+ 0.18652344,
+ 0.25,
+ -0.046875,
+ -0.012023926,
+ -0.36523438,
+ 0.36523438,
+ -0.11279297,
+ 0.421875,
+ 0.079589844,
+ -0.100097656,
+ 0.37304688,
+ 0.29882812,
+ -0.10546875,
+ -0.36523438,
+ 0.040039062,
+ 0.546875,
+ 0.12890625,
+ -0.06542969,
+ -0.38085938,
+ -0.35742188,
+ -0.6484375,
+ -0.28515625,
+ 0.0107421875,
+ -0.055664062,
+ 0.45703125,
+ 0.33984375,
+ 0.26367188,
+ -0.23144531,
+ 0.012878418,
+ -0.875,
+ 0.11035156,
+ 0.33984375,
+ 0.203125,
+ 0.38867188,
+ 0.24902344,
+ -0.37304688,
+ -0.98046875,
+ -0.122558594,
+ -0.17871094,
+ -0.09277344,
+ 0.1796875,
+ 0.4453125,
+ -0.66796875,
+ 0.78515625,
+ 0.12988281,
+ 0.35546875,
+ 0.44140625,
+ 0.58984375,
+ 0.29492188,
+ 0.7734375,
+ -0.21972656,
+ -0.40234375,
+ -0.22265625,
+ 0.18359375,
+ 0.54296875,
+ 0.17382812,
+ 0.59375,
+ -0.390625,
+ -0.92578125,
+ -0.017456055,
+ -0.25,
+ 0.73828125,
+ 0.7578125,
+ -0.3828125,
+ -0.25976562,
+ 0.049072266,
+ 0.046875,
+ -0.3515625,
+ 0.30078125,
+ -1.03125,
+ -0.48828125,
+ 0.0017929077,
+ -0.26171875,
+ 0.20214844,
+ 0.29882812,
+ 0.064941406,
+ 0.21484375,
+ -0.55078125,
+ -0.021362305,
+ 0.12988281,
+ 0.27148438,
+ 0.38867188,
+ -0.19726562,
+ -0.55078125,
+ 0.1640625,
+ 0.32226562,
+ -0.72265625,
+ 0.36132812,
+ 1.21875,
+ -0.22070312,
+ -0.32421875,
+ -0.29882812,
+ 0.0024414062,
+ 0.19921875,
+ 0.734375,
+ 0.16210938,
+ 0.17871094,
+ -0.19140625,
+ 0.38476562,
+ -0.06591797,
+ -0.47070312,
+ -0.040039062,
+ -0.33007812,
+ -0.07910156,
+ -0.2890625,
+ 0.00970459,
+ 0.12695312,
+ -0.12060547,
+ -0.18847656,
+ 1.015625,
+ -0.032958984,
+ 0.12451172,
+ -0.38476562,
+ 0.063964844,
+ 1.0859375,
+ 0.067871094,
+ -0.24511719,
+ 0.125,
+ 0.10546875,
+ -0.22460938,
+ -0.29101562,
+ 0.24414062,
+ -0.017944336,
+ -0.15625,
+ -0.60546875,
+ -0.25195312,
+ -0.46875,
+ 0.80859375,
+ -0.34960938,
+ 0.42382812,
+ 0.796875,
+ 0.296875,
+ -0.067871094,
+ 0.39453125,
+ 0.07470703,
+ 0.033935547,
+ 0.24414062,
+ 0.32617188,
+ 0.023925781,
+ 0.73046875,
+ 0.2109375,
+ -0.43164062,
+ 0.14453125,
+ 0.63671875,
+ 0.21972656,
+ -0.1875,
+ -0.18066406,
+ -0.22167969,
+ -1.3359375,
+ 0.52734375,
+ -0.40625,
+ -0.12988281,
+ 0.17480469,
+ -0.18066406,
+ 0.58984375,
+ -0.32421875,
+ -0.13476562,
+ 0.39257812,
+ -0.19238281,
+ 0.068359375,
+ 0.7265625,
+ -0.7109375,
+ -0.125,
+ 0.328125,
+ 0.34179688,
+ -0.48828125,
+ -0.10058594,
+ -0.83984375,
+ 0.30273438,
+ 0.008239746,
+ -1.390625,
+ 0.171875,
+ 0.34960938,
+ 0.44921875,
+ 0.22167969,
+ 0.60546875,
+ -0.36914062,
+ -0.028808594,
+ -0.19921875,
+ 0.6875,
+ 0.52734375,
+ -0.07421875,
+ 0.35546875,
+ 0.546875,
+ 0.08691406,
+ 0.23339844,
+ -0.984375,
+ -0.20507812,
+ 0.08544922,
+ 0.453125,
+ -0.07421875,
+ -0.953125,
+ 0.74609375,
+ -0.796875,
+ 0.47851562,
+ 0.81640625,
+ -0.44921875,
+ -0.33398438,
+ -0.54296875,
+ 0.46484375,
+ -0.390625,
+ -0.24121094,
+ -0.0115356445,
+ 1.1328125,
+ 1.0390625,
+ 0.6484375,
+ 0.35742188,
+ -0.29492188,
+ -0.0007095337,
+ -0.060302734,
+ 0.21777344,
+ 0.15136719,
+ -0.6171875,
+ 0.11328125,
+ -0.025878906,
+ 0.19238281,
+ 0.140625,
+ 0.171875,
+ 0.25195312,
+ 0.10546875,
+ 0.0008354187,
+ -0.13476562,
+ -0.26953125,
+ 0.025024414,
+ -0.28320312,
+ -0.107910156,
+ 1.015625,
+ 0.05493164,
+ -0.12988281,
+ 0.30859375,
+ 0.22558594,
+ -0.60546875,
+ 0.11328125,
+ -1.203125,
+ 0.6484375,
+ 0.087402344,
+ 0.32226562,
+ 0.63671875,
+ -0.07714844,
+ -1.390625,
+ -0.71875,
+ -0.34179688,
+ -0.10546875,
+ -0.37304688,
+ -0.09863281,
+ -0.41210938,
+ -0.14941406,
+ 0.41210938,
+ -0.20898438,
+ 0.18261719,
+ 0.67578125,
+ 0.41601562,
+ 0.32617188,
+ 0.2421875,
+ -0.14257812,
+ -0.6796875,
+ 0.01953125,
+ 0.34179688,
+ 0.20800781,
+ -0.123046875,
+ 0.087402344,
+ 0.85546875,
+ 0.33984375,
+ 0.33203125,
+ -0.68359375,
+ 0.44921875,
+ 0.50390625,
+ 0.083496094,
+ 0.10888672,
+ -0.09863281,
+ 0.55078125,
+ 0.09765625,
+ -0.50390625,
+ 0.13378906,
+ -0.29882812,
+ 0.030761719,
+ -0.64453125,
+ 0.22949219,
+ 0.43945312,
+ 0.16503906,
+ 0.10888672,
+ -0.12792969,
+ -0.039794922,
+ -0.111328125,
+ -0.35742188,
+ 0.053222656,
+ -0.78125,
+ -0.4375,
+ 0.359375,
+ -0.88671875,
+ -0.21972656,
+ -0.053710938,
+ 0.91796875,
+ -0.10644531,
+ 0.55859375,
+ -0.7734375,
+ 0.5078125,
+ 0.46484375,
+ 0.32226562,
+ 0.16796875,
+ -0.28515625,
+ 0.045410156,
+ -0.45117188,
+ 0.38867188,
+ -0.33398438,
+ -0.5234375,
+ 0.296875,
+ 0.6015625,
+ 0.3515625,
+ -0.734375,
+ 0.3984375,
+ -0.08251953,
+ 0.359375,
+ -0.28515625,
+ -0.88671875,
+ 0.0051879883,
+ 0.045166016,
+ -0.7421875,
+ -0.36523438,
+ 0.140625,
+ 0.18066406,
+ -0.171875,
+ -0.15625,
+ -0.53515625,
+ 0.2421875,
+ -0.19140625,
+ -0.18066406,
+ 0.25390625,
+ 0.6875,
+ -0.01965332,
+ -0.33203125,
+ 0.29492188,
+ 0.107421875,
+ -0.048339844,
+ -0.82421875,
+ 0.52734375,
+ 0.78125,
+ 0.8203125,
+ -0.90625,
+ 0.765625,
+ 0.0390625,
+ 0.045410156,
+ 0.26367188,
+ -0.14355469,
+ -0.26367188,
+ 0.390625,
+ -0.10888672,
+ 0.33007812,
+ -0.5625,
+ 0.08105469,
+ -0.13769531,
+ 0.8515625,
+ -0.14453125,
+ 0.77734375,
+ -0.48046875,
+ -0.3515625,
+ -0.25390625,
+ -0.09277344,
+ 0.23925781,
+ -0.022338867,
+ -0.45898438,
+ 0.36132812,
+ -0.23828125,
+ 0.265625,
+ -0.48632812,
+ -0.46875,
+ -0.75390625,
+ 1.3125,
+ 0.78125,
+ -0.63671875,
+ -1.21875,
+ 0.5078125,
+ -0.27734375,
+ -0.118652344,
+ 0.041992188,
+ -0.14648438,
+ -0.8046875,
+ 0.21679688,
+ -0.79296875,
+ 0.28320312,
+ -0.09667969,
+ 0.42773438,
+ 0.49414062,
+ 0.44726562,
+ 0.21972656,
+ -0.02746582,
+ -0.03540039,
+ -0.14941406,
+ -0.515625,
+ -0.27929688,
+ 0.9609375,
+ -0.007598877,
+ 0.34765625,
+ -0.060546875,
+ -0.44726562,
+ 0.7421875,
+ 0.15332031,
+ 0.45117188,
+ -0.4921875,
+ 0.07080078,
+ 0.5625,
+ 0.3984375,
+ -0.20019531,
+ 0.014892578,
+ 0.63671875,
+ -0.0071411133,
+ 0.016357422,
+ 1.0625,
+ 0.049316406,
+ 0.18066406,
+ 0.09814453,
+ -0.52734375,
+ -0.359375,
+ -0.072265625,
+ -0.41992188,
+ 0.39648438,
+ 0.38671875,
+ -0.30273438,
+ -0.056640625,
+ -0.640625,
+ -0.44921875,
+ 0.49414062,
+ 0.29101562,
+ 0.49609375,
+ 0.40429688,
+ -0.10205078,
+ 0.49414062,
+ -0.28125,
+ -0.12695312,
+ -0.0022735596,
+ -0.37304688,
+ 0.122558594,
+ 0.07519531,
+ -0.12597656,
+ -0.38085938,
+ -0.19824219,
+ -0.40039062,
+ 0.56640625,
+ -1.140625,
+ -0.515625,
+ -0.17578125,
+ -0.765625,
+ -0.43945312,
+ 0.3359375,
+ -0.24707031,
+ 0.32617188,
+ -0.45117188,
+ -0.37109375,
+ 0.45117188,
+ -0.27539062,
+ -0.38867188,
+ 0.09082031,
+ 0.17675781,
+ 0.49414062,
+ 0.19921875,
+ 0.17480469,
+ 0.8515625,
+ -0.23046875,
+ -0.234375,
+ -0.28515625,
+ 0.10253906,
+ 0.29101562,
+ -0.3359375,
+ -0.203125,
+ 0.6484375,
+ 0.11767578,
+ -0.20214844,
+ -0.42382812,
+ 0.26367188,
+ 0.6328125,
+ 0.0059509277,
+ 0.08691406,
+ -1.5625,
+ -0.43554688,
+ 0.17675781,
+ 0.091796875,
+ -0.5234375,
+ -0.09863281,
+ 0.20605469,
+ 0.16601562,
+ -0.578125,
+ 0.017700195,
+ 0.41015625,
+ 1.03125,
+ -0.55078125,
+ 0.21289062,
+ -0.35351562,
+ 0.24316406,
+ -0.123535156,
+ 0.11035156,
+ -0.48242188,
+ -0.34179688,
+ 0.45117188,
+ 0.3125,
+ -0.071777344,
+ 0.12792969,
+ 0.55859375,
+ 0.063964844,
+ -0.21191406,
+ 0.01965332,
+ -1.359375,
+ -0.21582031,
+ -0.019042969,
+ 0.16308594,
+ -0.3671875,
+ -0.40625,
+ -1.0234375,
+ -0.21289062,
+ 0.24023438,
+ -0.28125,
+ 0.26953125,
+ -0.14550781,
+ -0.087890625,
+ 0.16113281,
+ -0.49804688,
+ -0.17675781,
+ -0.890625,
+ 0.27929688,
+ 0.484375,
+ 0.27148438,
+ 0.11816406,
+ 0.83984375,
+ 0.029052734,
+ -0.890625,
+ 0.66796875,
+ 0.78515625,
+ -0.953125,
+ 0.49414062,
+ -0.546875,
+ 0.106933594,
+ -0.08251953,
+ 0.2890625,
+ -0.1484375,
+ -0.85546875,
+ 0.32421875,
+ -0.0040893555,
+ -0.16601562,
+ -0.16699219,
+ 0.24414062,
+ -0.5078125,
+ 0.25390625,
+ -0.10253906,
+ 0.15625,
+ 0.140625,
+ -0.27539062,
+ -0.546875,
+ -0.5546875,
+ -0.71875,
+ 0.37304688,
+ 0.060058594,
+ -0.076171875,
+ 0.44921875,
+ 0.06933594,
+ -0.28710938,
+ -0.22949219,
+ 0.17578125,
+ 0.09814453,
+ 0.4765625,
+ -0.95703125,
+ -0.03540039,
+ 0.21289062,
+ -0.7578125,
+ -0.07373047,
+ 0.10546875,
+ 0.07128906,
+ 0.76171875,
+ 0.4296875,
+ -0.09375,
+ 0.27539062,
+ -0.55078125,
+ 0.29882812,
+ -0.42382812,
+ 0.32617188,
+ -0.39648438,
+ 0.12451172,
+ 0.16503906,
+ -0.22460938,
+ -0.65625,
+ -0.022094727,
+ 0.61328125,
+ -0.024780273,
+ 0.62109375,
+ -0.033447266,
+ 0.515625,
+ 0.12890625,
+ -0.21875,
+ -0.08642578,
+ 0.49804688,
+ -0.2265625,
+ -0.29296875,
+ 0.19238281,
+ 0.3515625,
+ -1.265625,
+ 0.57421875,
+ 0.20117188,
+ -0.28320312,
+ 0.1953125,
+ -0.30664062,
+ 0.2265625,
+ -0.11230469,
+ 0.83984375,
+ 0.111328125,
+ 0.265625,
+ 0.71484375,
+ -0.625,
+ 0.38867188,
+ 0.47070312,
+ -0.32617188,
+ -0.171875,
+ 1.0078125,
+ 0.19726562,
+ -0.118652344,
+ 0.63671875,
+ -0.068359375,
+ -0.25585938,
+ 0.4140625,
+ -0.29296875,
+ 0.21386719,
+ -0.064453125,
+ 0.15820312,
+ -0.89453125,
+ -0.16308594,
+ 0.48046875,
+ 0.14648438,
+ -0.5703125,
+ 0.84765625,
+ -0.19042969,
+ 0.03515625,
+ 0.42578125,
+ -0.27539062,
+ -0.5390625,
+ 0.95703125,
+ 0.2734375,
+ 0.16699219,
+ -0.328125,
+ 0.11279297,
+ 0.003250122,
+ 0.47265625,
+ -0.31640625,
+ 0.546875,
+ 0.55859375,
+ 0.06933594,
+ -0.61328125,
+ -0.16210938,
+ -0.375,
+ 0.100097656,
+ -0.088378906,
+ 0.12695312,
+ 0.079589844,
+ 0.123535156,
+ -1.0078125,
+ 0.6875,
+ 0.022949219,
+ -0.40039062,
+ -0.09863281,
+ 0.29101562,
+ -1.2890625,
+ -0.20996094,
+ 0.36328125,
+ -0.3515625,
+ 0.7890625,
+ 0.12207031,
+ 0.48046875,
+ -0.13671875,
+ -0.041015625,
+ 0.19824219,
+ 0.19921875,
+ 0.01171875,
+ -0.37695312,
+ -0.62890625,
+ 0.9375,
+ -0.671875,
+ 0.24609375,
+ 0.6484375,
+ -0.29101562,
+ 0.076171875,
+ 0.62109375,
+ -0.5546875,
+ 0.36523438,
+ 0.75390625,
+ -0.19140625,
+ -0.875,
+ -0.8203125,
+ -0.24414062,
+ -0.625,
+ 0.1796875,
+ -0.40039062,
+ 0.25390625,
+ -0.14550781,
+ -0.21679688,
+ -0.828125,
+ 0.3359375,
+ 0.43554688,
+ 0.55078125,
+ -0.44921875,
+ -0.28710938,
+ 0.24023438,
+ 0.18066406,
+ -0.6953125,
+ 0.020385742,
+ -0.11376953,
+ 0.13867188,
+ -0.92578125,
+ 0.33398438,
+ -0.328125,
+ 0.78125,
+ -0.45507812,
+ -0.07470703,
+ 0.34179688,
+ 0.07080078,
+ 0.76171875,
+ 0.37890625,
+ -0.10644531,
+ 0.90234375,
+ -0.21875,
+ -0.15917969,
+ -0.36132812,
+ 0.2109375,
+ -0.45703125,
+ -0.76953125,
+ 0.21289062,
+ 0.26367188,
+ 0.49804688,
+ 0.35742188,
+ -0.20019531,
+ 0.31054688,
+ 0.34179688,
+ 0.17089844,
+ -0.15429688,
+ 0.39648438,
+ -0.5859375,
+ 0.20996094,
+ -0.40039062,
+ 0.5703125,
+ -0.515625,
+ 0.5234375,
+ 0.049560547,
+ 0.328125,
+ 0.24804688,
+ 0.42578125,
+ 0.609375,
+ 0.19238281,
+ 0.27929688,
+ 0.19335938,
+ 0.78125,
+ -0.9921875,
+ 0.23925781,
+ -1.3828125,
+ -0.22949219,
+ -0.578125,
+ -0.13964844,
+ -0.17382812,
+ -0.011169434,
+ 0.26171875,
+ -0.73046875,
+ -1.4375,
+ 0.6953125,
+ -0.7421875,
+ 0.052246094,
+ 0.12207031,
+ 1.3046875,
+ 0.38867188,
+ 0.040283203,
+ -0.546875,
+ -0.0021514893,
+ 0.18457031,
+ -0.5546875,
+ -0.51171875,
+ -0.16308594,
+ -0.104003906,
+ -0.38867188,
+ -0.20996094,
+ -0.8984375,
+ 0.6015625,
+ -0.30078125,
+ -0.13769531,
+ 0.16113281,
+ 0.58203125,
+ -0.23730469,
+ -0.125,
+ -1.0234375,
+ 0.875,
+ -0.7109375,
+ 0.29101562,
+ 0.09667969,
+ -0.3203125,
+ -0.48046875,
+ 0.37890625,
+ 0.734375,
+ -0.28710938,
+ -0.29882812,
+ -0.05493164,
+ 0.34765625,
+ -0.84375,
+ 0.65625,
+ 0.578125,
+ -0.20019531,
+ 0.13769531,
+ 0.10058594,
+ -0.37109375,
+ 0.36523438,
+ -0.22167969,
+ 0.72265625,
+ ],
+ "inputTextTokenCount": 6,
+ },
+ ],
+ "amazon.titan-embed-text-v1::This is an embedding test.": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "11233989-07e8-4ecb-9ba6-79601ba6d8cc"},
+ 200,
+ {
+ "embedding": [
+ -0.14160156,
+ 0.034423828,
+ 0.54296875,
+ 0.10986328,
+ 0.053466797,
+ 0.3515625,
+ 0.12988281,
+ -0.0002708435,
+ -0.21484375,
+ 0.060302734,
+ 0.58984375,
+ -0.5859375,
+ 0.52734375,
+ 0.82421875,
+ -0.91015625,
+ -0.19628906,
+ 0.45703125,
+ 0.609375,
+ -0.67578125,
+ 0.39453125,
+ -0.46875,
+ -0.25390625,
+ -0.21191406,
+ 0.114746094,
+ 0.31640625,
+ -0.41015625,
+ -0.32617188,
+ -0.43554688,
+ 0.4765625,
+ -0.4921875,
+ 0.40429688,
+ 0.06542969,
+ 0.859375,
+ -0.008056641,
+ -0.19921875,
+ 0.072753906,
+ 0.33203125,
+ 0.69921875,
+ 0.39453125,
+ 0.15527344,
+ 0.08886719,
+ -0.25,
+ 0.859375,
+ 0.22949219,
+ -0.19042969,
+ 0.13769531,
+ -0.078125,
+ 0.41210938,
+ 0.875,
+ 0.5234375,
+ 0.59765625,
+ -0.22949219,
+ -0.22558594,
+ -0.47460938,
+ 0.37695312,
+ 0.51953125,
+ -0.5703125,
+ 0.46679688,
+ 0.43554688,
+ 0.17480469,
+ -0.080566406,
+ -0.16699219,
+ -0.734375,
+ -1.0625,
+ -0.33984375,
+ 0.390625,
+ -0.18847656,
+ -0.5234375,
+ -0.48828125,
+ 0.44921875,
+ -0.09814453,
+ -0.3359375,
+ 0.087402344,
+ 0.36914062,
+ 1.3203125,
+ 0.25585938,
+ 0.14746094,
+ -0.059570312,
+ -0.15820312,
+ -0.037353516,
+ -0.61328125,
+ -0.6484375,
+ -0.35351562,
+ 0.55078125,
+ -0.26953125,
+ 0.90234375,
+ 0.3671875,
+ 0.31054688,
+ 0.00014019012,
+ -0.171875,
+ 0.025512695,
+ 0.5078125,
+ 0.11621094,
+ 0.33203125,
+ 0.8125,
+ -0.3046875,
+ -1.078125,
+ -0.5703125,
+ 0.26171875,
+ -0.4609375,
+ 0.203125,
+ 0.44726562,
+ -0.5078125,
+ 0.41601562,
+ -0.1953125,
+ 0.028930664,
+ -0.57421875,
+ 0.2265625,
+ 0.13574219,
+ -0.040039062,
+ -0.22949219,
+ -0.515625,
+ -0.19042969,
+ -0.30078125,
+ 0.10058594,
+ -0.66796875,
+ 0.6015625,
+ 0.296875,
+ -0.765625,
+ -0.87109375,
+ 0.2265625,
+ 0.068847656,
+ -0.088378906,
+ -0.1328125,
+ -0.796875,
+ -0.37304688,
+ 0.47460938,
+ -0.3515625,
+ -0.8125,
+ -0.32226562,
+ 0.265625,
+ 0.3203125,
+ -0.4140625,
+ -0.49023438,
+ 0.859375,
+ -0.19140625,
+ -0.6328125,
+ 0.10546875,
+ -0.5625,
+ 0.66015625,
+ 0.26171875,
+ -0.2109375,
+ 0.421875,
+ -0.82421875,
+ 0.29296875,
+ 0.17773438,
+ 0.24023438,
+ 0.5078125,
+ -0.49804688,
+ -0.10205078,
+ 0.10498047,
+ -0.36132812,
+ -0.47460938,
+ -0.20996094,
+ 0.010070801,
+ -0.546875,
+ 0.66796875,
+ -0.123046875,
+ -0.75390625,
+ 0.19628906,
+ 0.17480469,
+ 0.18261719,
+ -0.96875,
+ -0.26171875,
+ 0.4921875,
+ -0.40039062,
+ 0.296875,
+ 0.1640625,
+ -0.20507812,
+ -0.36132812,
+ 0.76171875,
+ -1.234375,
+ -0.625,
+ 0.060058594,
+ -0.09375,
+ -0.14746094,
+ 1.09375,
+ 0.057861328,
+ 0.22460938,
+ -0.703125,
+ 0.07470703,
+ 0.23828125,
+ -0.083984375,
+ -0.54296875,
+ 0.5546875,
+ -0.5,
+ -0.390625,
+ 0.106933594,
+ 0.6640625,
+ 0.27734375,
+ -0.953125,
+ 0.35351562,
+ -0.7734375,
+ -0.77734375,
+ 0.16503906,
+ -0.42382812,
+ 0.36914062,
+ 0.020141602,
+ -1.3515625,
+ 0.18847656,
+ 0.13476562,
+ -0.034179688,
+ -0.03930664,
+ -0.03857422,
+ -0.027954102,
+ 0.73828125,
+ -0.18945312,
+ -0.09814453,
+ -0.46289062,
+ 0.36914062,
+ 0.033203125,
+ 0.020874023,
+ -0.703125,
+ 0.91796875,
+ 0.38671875,
+ 0.625,
+ -0.19335938,
+ -0.16796875,
+ -0.58203125,
+ 0.21386719,
+ -0.032470703,
+ -0.296875,
+ -0.15625,
+ -0.1640625,
+ -0.74609375,
+ 0.328125,
+ 0.5546875,
+ -0.1953125,
+ 1.0546875,
+ 0.171875,
+ -0.099609375,
+ 0.5234375,
+ 0.05078125,
+ -0.35742188,
+ -0.2734375,
+ -1.3203125,
+ -0.8515625,
+ -0.16015625,
+ 0.01574707,
+ 0.29296875,
+ 0.18457031,
+ -0.265625,
+ 0.048339844,
+ 0.045654297,
+ -0.32226562,
+ 0.087890625,
+ -0.0047302246,
+ 0.38671875,
+ 0.10644531,
+ -0.06225586,
+ 1.03125,
+ 0.94140625,
+ -0.3203125,
+ 0.20800781,
+ -1.171875,
+ 0.48046875,
+ -0.091796875,
+ 0.20800781,
+ -0.1328125,
+ -0.20507812,
+ 0.28125,
+ -0.47070312,
+ -0.09033203,
+ 0.0013809204,
+ -0.08203125,
+ 0.43359375,
+ -0.03100586,
+ -0.060791016,
+ -0.53515625,
+ -1.46875,
+ 0.000101566315,
+ 0.515625,
+ 0.40625,
+ -0.10498047,
+ -0.15820312,
+ -0.009460449,
+ -0.77734375,
+ -0.5859375,
+ 0.9765625,
+ 0.099609375,
+ 0.51953125,
+ 0.38085938,
+ -0.09667969,
+ -0.100097656,
+ -0.5,
+ -1.3125,
+ -0.18066406,
+ -0.099121094,
+ 0.26171875,
+ -0.14453125,
+ -0.546875,
+ 0.17578125,
+ 0.484375,
+ 0.765625,
+ 0.45703125,
+ 0.2734375,
+ 0.0028076172,
+ 0.17089844,
+ -0.32421875,
+ -0.37695312,
+ 0.30664062,
+ -0.48046875,
+ 0.07128906,
+ 0.031982422,
+ -0.31054688,
+ -0.055419922,
+ -0.29296875,
+ 0.3359375,
+ -0.296875,
+ 0.47851562,
+ -0.05126953,
+ 0.18457031,
+ -0.01953125,
+ -0.35742188,
+ 0.017944336,
+ -0.25,
+ 0.10595703,
+ 0.17382812,
+ -0.73828125,
+ 0.36914062,
+ -0.15234375,
+ -0.8125,
+ 0.17382812,
+ 0.048095703,
+ 0.5625,
+ -0.33789062,
+ 0.023071289,
+ -0.21972656,
+ 0.16015625,
+ 0.032958984,
+ -1.1171875,
+ -0.984375,
+ 0.83984375,
+ 0.009033203,
+ -0.042236328,
+ -0.46484375,
+ -0.08203125,
+ 0.44726562,
+ -0.765625,
+ -0.3984375,
+ -0.40820312,
+ -0.234375,
+ 0.044189453,
+ 0.119628906,
+ -0.7578125,
+ -0.55078125,
+ -0.4453125,
+ 0.7578125,
+ 0.34960938,
+ 0.96484375,
+ 0.35742188,
+ 0.36914062,
+ -0.35351562,
+ -0.36132812,
+ 1.109375,
+ 0.5859375,
+ 0.85546875,
+ -0.10644531,
+ -0.6953125,
+ -0.0066833496,
+ 0.042236328,
+ -0.06689453,
+ 0.36914062,
+ 0.9765625,
+ -0.3046875,
+ 0.59765625,
+ -0.6640625,
+ 0.21484375,
+ -0.07128906,
+ 1.1328125,
+ -0.51953125,
+ 0.86328125,
+ -0.11328125,
+ 0.15722656,
+ -0.36328125,
+ -0.04638672,
+ 1.4375,
+ 0.18457031,
+ -0.18359375,
+ 0.10595703,
+ -0.49023438,
+ -0.07324219,
+ -0.73046875,
+ -0.119140625,
+ 0.021118164,
+ 0.4921875,
+ -0.46875,
+ 0.28710938,
+ 0.3359375,
+ 0.11767578,
+ -0.2109375,
+ -0.14550781,
+ 0.39648438,
+ -0.27734375,
+ 0.48046875,
+ 0.12988281,
+ 0.45507812,
+ -0.375,
+ -0.84765625,
+ 0.25585938,
+ -0.36523438,
+ 0.8046875,
+ 0.42382812,
+ -0.24511719,
+ 0.54296875,
+ 0.71875,
+ 0.010009766,
+ -0.04296875,
+ 0.083984375,
+ -0.52734375,
+ 0.13964844,
+ -0.27539062,
+ -0.30273438,
+ 1.1484375,
+ -0.515625,
+ -0.19335938,
+ 0.58984375,
+ 0.049072266,
+ 0.703125,
+ -0.04272461,
+ 0.5078125,
+ 0.34960938,
+ -0.3359375,
+ -0.47460938,
+ 0.049316406,
+ 0.36523438,
+ 0.7578125,
+ -0.022827148,
+ -0.71484375,
+ 0.21972656,
+ 0.09716797,
+ -0.203125,
+ -0.36914062,
+ 1.34375,
+ 0.34179688,
+ 0.46679688,
+ 1.078125,
+ 0.26171875,
+ 0.41992188,
+ 0.22363281,
+ -0.515625,
+ -0.5703125,
+ 0.13378906,
+ 0.26757812,
+ -0.22558594,
+ -0.5234375,
+ 0.06689453,
+ 0.08251953,
+ -0.625,
+ 0.16796875,
+ 0.43164062,
+ -0.55859375,
+ 0.28125,
+ 0.078125,
+ 0.6328125,
+ 0.23242188,
+ -0.064941406,
+ -0.004486084,
+ -0.20703125,
+ 0.2734375,
+ 0.453125,
+ -0.734375,
+ 0.04272461,
+ 0.36132812,
+ -0.19628906,
+ -0.12402344,
+ 1.3515625,
+ 0.25585938,
+ 0.4921875,
+ -0.29296875,
+ -0.58984375,
+ 0.021240234,
+ -0.044677734,
+ 0.7578125,
+ -0.7890625,
+ 0.10253906,
+ -0.15820312,
+ -0.5078125,
+ -0.39453125,
+ -0.453125,
+ 0.35742188,
+ 0.921875,
+ 0.44335938,
+ -0.49804688,
+ 0.44335938,
+ 0.31445312,
+ 0.58984375,
+ -1.0078125,
+ -0.22460938,
+ 0.24121094,
+ 0.87890625,
+ 0.66015625,
+ -0.390625,
+ -0.05053711,
+ 0.059570312,
+ 0.36132812,
+ -0.00038719177,
+ -0.017089844,
+ 0.62890625,
+ 0.203125,
+ 0.17480469,
+ 0.025512695,
+ 0.47460938,
+ 0.3125,
+ 1.140625,
+ 0.32421875,
+ -0.057861328,
+ 0.36914062,
+ -0.7265625,
+ -0.51953125,
+ 0.26953125,
+ 0.42773438,
+ 0.064453125,
+ 0.6328125,
+ 0.27148438,
+ -0.11767578,
+ 0.66796875,
+ -0.38671875,
+ 0.5234375,
+ -0.59375,
+ 0.5078125,
+ 0.008239746,
+ -0.34179688,
+ -0.27539062,
+ 0.5234375,
+ 1.296875,
+ 0.29492188,
+ -0.010986328,
+ -0.41210938,
+ 0.59375,
+ 0.061767578,
+ -0.33398438,
+ -2.03125,
+ 0.87890625,
+ -0.010620117,
+ 0.53125,
+ 0.14257812,
+ -0.515625,
+ -1.03125,
+ 0.578125,
+ 0.1875,
+ 0.44335938,
+ -0.33203125,
+ -0.36328125,
+ -0.3203125,
+ 0.29296875,
+ -0.8203125,
+ 0.41015625,
+ -0.48242188,
+ 0.66015625,
+ 0.5625,
+ -0.16503906,
+ -0.54296875,
+ -0.38085938,
+ 0.26171875,
+ 0.62109375,
+ 0.29101562,
+ -0.31054688,
+ 0.23730469,
+ -0.8515625,
+ 0.5234375,
+ 0.15332031,
+ 0.52734375,
+ -0.079589844,
+ -0.080566406,
+ -0.15527344,
+ -0.022827148,
+ 0.030517578,
+ -0.1640625,
+ -0.421875,
+ 0.09716797,
+ 0.03930664,
+ -0.055908203,
+ -0.546875,
+ -0.47851562,
+ 0.091796875,
+ 0.32226562,
+ -0.94140625,
+ -0.04638672,
+ -1.203125,
+ -0.39648438,
+ 0.45507812,
+ 0.296875,
+ -0.45703125,
+ 0.37890625,
+ -0.122558594,
+ 0.28320312,
+ -0.01965332,
+ -0.11669922,
+ -0.34570312,
+ -0.53515625,
+ -0.091308594,
+ -0.9375,
+ -0.32617188,
+ 0.095214844,
+ -0.4765625,
+ 0.37890625,
+ -0.859375,
+ 1.1015625,
+ -0.08935547,
+ 0.46484375,
+ -0.19238281,
+ 0.7109375,
+ 0.040039062,
+ -0.5390625,
+ 0.22363281,
+ -0.70703125,
+ 0.4921875,
+ -0.119140625,
+ -0.26757812,
+ -0.08496094,
+ 0.0859375,
+ -0.00390625,
+ -0.013366699,
+ -0.03955078,
+ 0.07421875,
+ -0.13085938,
+ 0.29101562,
+ -0.12109375,
+ 0.45703125,
+ 0.021728516,
+ 0.38671875,
+ -0.3671875,
+ -0.52734375,
+ -0.115722656,
+ 0.125,
+ 0.5703125,
+ -1.234375,
+ 0.06298828,
+ -0.55859375,
+ 0.60546875,
+ 0.8125,
+ -0.0032958984,
+ -0.068359375,
+ -0.21191406,
+ 0.56640625,
+ 0.17285156,
+ -0.3515625,
+ 0.36328125,
+ -0.99609375,
+ 0.43554688,
+ -0.1015625,
+ 0.07080078,
+ -0.66796875,
+ 1.359375,
+ 0.41601562,
+ 0.15917969,
+ 0.17773438,
+ -0.28710938,
+ 0.021850586,
+ -0.46289062,
+ 0.17578125,
+ -0.03955078,
+ -0.026855469,
+ 0.5078125,
+ -0.65625,
+ 0.0012512207,
+ 0.044433594,
+ -0.18652344,
+ 0.4921875,
+ -0.75390625,
+ 0.0072021484,
+ 0.4375,
+ -0.31445312,
+ 0.20214844,
+ 0.15039062,
+ -0.63671875,
+ -0.296875,
+ -0.375,
+ -0.027709961,
+ 0.013427734,
+ 0.17089844,
+ 0.89453125,
+ 0.11621094,
+ -0.43945312,
+ -0.30859375,
+ 0.02709961,
+ 0.23242188,
+ -0.64453125,
+ -0.859375,
+ 0.22167969,
+ -0.023071289,
+ -0.052734375,
+ 0.3671875,
+ -0.18359375,
+ 0.81640625,
+ -0.11816406,
+ 0.028320312,
+ 0.19042969,
+ 0.012817383,
+ -0.43164062,
+ 0.55859375,
+ -0.27929688,
+ 0.14257812,
+ -0.140625,
+ -0.048583984,
+ -0.014526367,
+ 0.35742188,
+ 0.22753906,
+ 0.13183594,
+ 0.04638672,
+ 0.03930664,
+ -0.29296875,
+ -0.2109375,
+ -0.16308594,
+ -0.48046875,
+ -0.13378906,
+ -0.39257812,
+ 0.29296875,
+ -0.047851562,
+ -0.5546875,
+ 0.08300781,
+ -0.14941406,
+ -0.07080078,
+ 0.12451172,
+ 0.1953125,
+ -0.51171875,
+ -0.048095703,
+ 0.1953125,
+ -0.37695312,
+ 0.46875,
+ -0.084472656,
+ 0.19042969,
+ -0.39453125,
+ 0.69921875,
+ -0.0065307617,
+ 0.25390625,
+ -0.16992188,
+ -0.5078125,
+ 0.016845703,
+ 0.27929688,
+ -0.22070312,
+ 0.671875,
+ 0.18652344,
+ 0.25,
+ -0.046875,
+ -0.012023926,
+ -0.36523438,
+ 0.36523438,
+ -0.11279297,
+ 0.421875,
+ 0.079589844,
+ -0.100097656,
+ 0.37304688,
+ 0.29882812,
+ -0.10546875,
+ -0.36523438,
+ 0.040039062,
+ 0.546875,
+ 0.12890625,
+ -0.06542969,
+ -0.38085938,
+ -0.35742188,
+ -0.6484375,
+ -0.28515625,
+ 0.0107421875,
+ -0.055664062,
+ 0.45703125,
+ 0.33984375,
+ 0.26367188,
+ -0.23144531,
+ 0.012878418,
+ -0.875,
+ 0.11035156,
+ 0.33984375,
+ 0.203125,
+ 0.38867188,
+ 0.24902344,
+ -0.37304688,
+ -0.98046875,
+ -0.122558594,
+ -0.17871094,
+ -0.09277344,
+ 0.1796875,
+ 0.4453125,
+ -0.66796875,
+ 0.78515625,
+ 0.12988281,
+ 0.35546875,
+ 0.44140625,
+ 0.58984375,
+ 0.29492188,
+ 0.7734375,
+ -0.21972656,
+ -0.40234375,
+ -0.22265625,
+ 0.18359375,
+ 0.54296875,
+ 0.17382812,
+ 0.59375,
+ -0.390625,
+ -0.92578125,
+ -0.017456055,
+ -0.25,
+ 0.73828125,
+ 0.7578125,
+ -0.3828125,
+ -0.25976562,
+ 0.049072266,
+ 0.046875,
+ -0.3515625,
+ 0.30078125,
+ -1.03125,
+ -0.48828125,
+ 0.0017929077,
+ -0.26171875,
+ 0.20214844,
+ 0.29882812,
+ 0.064941406,
+ 0.21484375,
+ -0.55078125,
+ -0.021362305,
+ 0.12988281,
+ 0.27148438,
+ 0.38867188,
+ -0.19726562,
+ -0.55078125,
+ 0.1640625,
+ 0.32226562,
+ -0.72265625,
+ 0.36132812,
+ 1.21875,
+ -0.22070312,
+ -0.32421875,
+ -0.29882812,
+ 0.0024414062,
+ 0.19921875,
+ 0.734375,
+ 0.16210938,
+ 0.17871094,
+ -0.19140625,
+ 0.38476562,
+ -0.06591797,
+ -0.47070312,
+ -0.040039062,
+ -0.33007812,
+ -0.07910156,
+ -0.2890625,
+ 0.00970459,
+ 0.12695312,
+ -0.12060547,
+ -0.18847656,
+ 1.015625,
+ -0.032958984,
+ 0.12451172,
+ -0.38476562,
+ 0.063964844,
+ 1.0859375,
+ 0.067871094,
+ -0.24511719,
+ 0.125,
+ 0.10546875,
+ -0.22460938,
+ -0.29101562,
+ 0.24414062,
+ -0.017944336,
+ -0.15625,
+ -0.60546875,
+ -0.25195312,
+ -0.46875,
+ 0.80859375,
+ -0.34960938,
+ 0.42382812,
+ 0.796875,
+ 0.296875,
+ -0.067871094,
+ 0.39453125,
+ 0.07470703,
+ 0.033935547,
+ 0.24414062,
+ 0.32617188,
+ 0.023925781,
+ 0.73046875,
+ 0.2109375,
+ -0.43164062,
+ 0.14453125,
+ 0.63671875,
+ 0.21972656,
+ -0.1875,
+ -0.18066406,
+ -0.22167969,
+ -1.3359375,
+ 0.52734375,
+ -0.40625,
+ -0.12988281,
+ 0.17480469,
+ -0.18066406,
+ 0.58984375,
+ -0.32421875,
+ -0.13476562,
+ 0.39257812,
+ -0.19238281,
+ 0.068359375,
+ 0.7265625,
+ -0.7109375,
+ -0.125,
+ 0.328125,
+ 0.34179688,
+ -0.48828125,
+ -0.10058594,
+ -0.83984375,
+ 0.30273438,
+ 0.008239746,
+ -1.390625,
+ 0.171875,
+ 0.34960938,
+ 0.44921875,
+ 0.22167969,
+ 0.60546875,
+ -0.36914062,
+ -0.028808594,
+ -0.19921875,
+ 0.6875,
+ 0.52734375,
+ -0.07421875,
+ 0.35546875,
+ 0.546875,
+ 0.08691406,
+ 0.23339844,
+ -0.984375,
+ -0.20507812,
+ 0.08544922,
+ 0.453125,
+ -0.07421875,
+ -0.953125,
+ 0.74609375,
+ -0.796875,
+ 0.47851562,
+ 0.81640625,
+ -0.44921875,
+ -0.33398438,
+ -0.54296875,
+ 0.46484375,
+ -0.390625,
+ -0.24121094,
+ -0.0115356445,
+ 1.1328125,
+ 1.0390625,
+ 0.6484375,
+ 0.35742188,
+ -0.29492188,
+ -0.0007095337,
+ -0.060302734,
+ 0.21777344,
+ 0.15136719,
+ -0.6171875,
+ 0.11328125,
+ -0.025878906,
+ 0.19238281,
+ 0.140625,
+ 0.171875,
+ 0.25195312,
+ 0.10546875,
+ 0.0008354187,
+ -0.13476562,
+ -0.26953125,
+ 0.025024414,
+ -0.28320312,
+ -0.107910156,
+ 1.015625,
+ 0.05493164,
+ -0.12988281,
+ 0.30859375,
+ 0.22558594,
+ -0.60546875,
+ 0.11328125,
+ -1.203125,
+ 0.6484375,
+ 0.087402344,
+ 0.32226562,
+ 0.63671875,
+ -0.07714844,
+ -1.390625,
+ -0.71875,
+ -0.34179688,
+ -0.10546875,
+ -0.37304688,
+ -0.09863281,
+ -0.41210938,
+ -0.14941406,
+ 0.41210938,
+ -0.20898438,
+ 0.18261719,
+ 0.67578125,
+ 0.41601562,
+ 0.32617188,
+ 0.2421875,
+ -0.14257812,
+ -0.6796875,
+ 0.01953125,
+ 0.34179688,
+ 0.20800781,
+ -0.123046875,
+ 0.087402344,
+ 0.85546875,
+ 0.33984375,
+ 0.33203125,
+ -0.68359375,
+ 0.44921875,
+ 0.50390625,
+ 0.083496094,
+ 0.10888672,
+ -0.09863281,
+ 0.55078125,
+ 0.09765625,
+ -0.50390625,
+ 0.13378906,
+ -0.29882812,
+ 0.030761719,
+ -0.64453125,
+ 0.22949219,
+ 0.43945312,
+ 0.16503906,
+ 0.10888672,
+ -0.12792969,
+ -0.039794922,
+ -0.111328125,
+ -0.35742188,
+ 0.053222656,
+ -0.78125,
+ -0.4375,
+ 0.359375,
+ -0.88671875,
+ -0.21972656,
+ -0.053710938,
+ 0.91796875,
+ -0.10644531,
+ 0.55859375,
+ -0.7734375,
+ 0.5078125,
+ 0.46484375,
+ 0.32226562,
+ 0.16796875,
+ -0.28515625,
+ 0.045410156,
+ -0.45117188,
+ 0.38867188,
+ -0.33398438,
+ -0.5234375,
+ 0.296875,
+ 0.6015625,
+ 0.3515625,
+ -0.734375,
+ 0.3984375,
+ -0.08251953,
+ 0.359375,
+ -0.28515625,
+ -0.88671875,
+ 0.0051879883,
+ 0.045166016,
+ -0.7421875,
+ -0.36523438,
+ 0.140625,
+ 0.18066406,
+ -0.171875,
+ -0.15625,
+ -0.53515625,
+ 0.2421875,
+ -0.19140625,
+ -0.18066406,
+ 0.25390625,
+ 0.6875,
+ -0.01965332,
+ -0.33203125,
+ 0.29492188,
+ 0.107421875,
+ -0.048339844,
+ -0.82421875,
+ 0.52734375,
+ 0.78125,
+ 0.8203125,
+ -0.90625,
+ 0.765625,
+ 0.0390625,
+ 0.045410156,
+ 0.26367188,
+ -0.14355469,
+ -0.26367188,
+ 0.390625,
+ -0.10888672,
+ 0.33007812,
+ -0.5625,
+ 0.08105469,
+ -0.13769531,
+ 0.8515625,
+ -0.14453125,
+ 0.77734375,
+ -0.48046875,
+ -0.3515625,
+ -0.25390625,
+ -0.09277344,
+ 0.23925781,
+ -0.022338867,
+ -0.45898438,
+ 0.36132812,
+ -0.23828125,
+ 0.265625,
+ -0.48632812,
+ -0.46875,
+ -0.75390625,
+ 1.3125,
+ 0.78125,
+ -0.63671875,
+ -1.21875,
+ 0.5078125,
+ -0.27734375,
+ -0.118652344,
+ 0.041992188,
+ -0.14648438,
+ -0.8046875,
+ 0.21679688,
+ -0.79296875,
+ 0.28320312,
+ -0.09667969,
+ 0.42773438,
+ 0.49414062,
+ 0.44726562,
+ 0.21972656,
+ -0.02746582,
+ -0.03540039,
+ -0.14941406,
+ -0.515625,
+ -0.27929688,
+ 0.9609375,
+ -0.007598877,
+ 0.34765625,
+ -0.060546875,
+ -0.44726562,
+ 0.7421875,
+ 0.15332031,
+ 0.45117188,
+ -0.4921875,
+ 0.07080078,
+ 0.5625,
+ 0.3984375,
+ -0.20019531,
+ 0.014892578,
+ 0.63671875,
+ -0.0071411133,
+ 0.016357422,
+ 1.0625,
+ 0.049316406,
+ 0.18066406,
+ 0.09814453,
+ -0.52734375,
+ -0.359375,
+ -0.072265625,
+ -0.41992188,
+ 0.39648438,
+ 0.38671875,
+ -0.30273438,
+ -0.056640625,
+ -0.640625,
+ -0.44921875,
+ 0.49414062,
+ 0.29101562,
+ 0.49609375,
+ 0.40429688,
+ -0.10205078,
+ 0.49414062,
+ -0.28125,
+ -0.12695312,
+ -0.0022735596,
+ -0.37304688,
+ 0.122558594,
+ 0.07519531,
+ -0.12597656,
+ -0.38085938,
+ -0.19824219,
+ -0.40039062,
+ 0.56640625,
+ -1.140625,
+ -0.515625,
+ -0.17578125,
+ -0.765625,
+ -0.43945312,
+ 0.3359375,
+ -0.24707031,
+ 0.32617188,
+ -0.45117188,
+ -0.37109375,
+ 0.45117188,
+ -0.27539062,
+ -0.38867188,
+ 0.09082031,
+ 0.17675781,
+ 0.49414062,
+ 0.19921875,
+ 0.17480469,
+ 0.8515625,
+ -0.23046875,
+ -0.234375,
+ -0.28515625,
+ 0.10253906,
+ 0.29101562,
+ -0.3359375,
+ -0.203125,
+ 0.6484375,
+ 0.11767578,
+ -0.20214844,
+ -0.42382812,
+ 0.26367188,
+ 0.6328125,
+ 0.0059509277,
+ 0.08691406,
+ -1.5625,
+ -0.43554688,
+ 0.17675781,
+ 0.091796875,
+ -0.5234375,
+ -0.09863281,
+ 0.20605469,
+ 0.16601562,
+ -0.578125,
+ 0.017700195,
+ 0.41015625,
+ 1.03125,
+ -0.55078125,
+ 0.21289062,
+ -0.35351562,
+ 0.24316406,
+ -0.123535156,
+ 0.11035156,
+ -0.48242188,
+ -0.34179688,
+ 0.45117188,
+ 0.3125,
+ -0.071777344,
+ 0.12792969,
+ 0.55859375,
+ 0.063964844,
+ -0.21191406,
+ 0.01965332,
+ -1.359375,
+ -0.21582031,
+ -0.019042969,
+ 0.16308594,
+ -0.3671875,
+ -0.40625,
+ -1.0234375,
+ -0.21289062,
+ 0.24023438,
+ -0.28125,
+ 0.26953125,
+ -0.14550781,
+ -0.087890625,
+ 0.16113281,
+ -0.49804688,
+ -0.17675781,
+ -0.890625,
+ 0.27929688,
+ 0.484375,
+ 0.27148438,
+ 0.11816406,
+ 0.83984375,
+ 0.029052734,
+ -0.890625,
+ 0.66796875,
+ 0.78515625,
+ -0.953125,
+ 0.49414062,
+ -0.546875,
+ 0.106933594,
+ -0.08251953,
+ 0.2890625,
+ -0.1484375,
+ -0.85546875,
+ 0.32421875,
+ -0.0040893555,
+ -0.16601562,
+ -0.16699219,
+ 0.24414062,
+ -0.5078125,
+ 0.25390625,
+ -0.10253906,
+ 0.15625,
+ 0.140625,
+ -0.27539062,
+ -0.546875,
+ -0.5546875,
+ -0.71875,
+ 0.37304688,
+ 0.060058594,
+ -0.076171875,
+ 0.44921875,
+ 0.06933594,
+ -0.28710938,
+ -0.22949219,
+ 0.17578125,
+ 0.09814453,
+ 0.4765625,
+ -0.95703125,
+ -0.03540039,
+ 0.21289062,
+ -0.7578125,
+ -0.07373047,
+ 0.10546875,
+ 0.07128906,
+ 0.76171875,
+ 0.4296875,
+ -0.09375,
+ 0.27539062,
+ -0.55078125,
+ 0.29882812,
+ -0.42382812,
+ 0.32617188,
+ -0.39648438,
+ 0.12451172,
+ 0.16503906,
+ -0.22460938,
+ -0.65625,
+ -0.022094727,
+ 0.61328125,
+ -0.024780273,
+ 0.62109375,
+ -0.033447266,
+ 0.515625,
+ 0.12890625,
+ -0.21875,
+ -0.08642578,
+ 0.49804688,
+ -0.2265625,
+ -0.29296875,
+ 0.19238281,
+ 0.3515625,
+ -1.265625,
+ 0.57421875,
+ 0.20117188,
+ -0.28320312,
+ 0.1953125,
+ -0.30664062,
+ 0.2265625,
+ -0.11230469,
+ 0.83984375,
+ 0.111328125,
+ 0.265625,
+ 0.71484375,
+ -0.625,
+ 0.38867188,
+ 0.47070312,
+ -0.32617188,
+ -0.171875,
+ 1.0078125,
+ 0.19726562,
+ -0.118652344,
+ 0.63671875,
+ -0.068359375,
+ -0.25585938,
+ 0.4140625,
+ -0.29296875,
+ 0.21386719,
+ -0.064453125,
+ 0.15820312,
+ -0.89453125,
+ -0.16308594,
+ 0.48046875,
+ 0.14648438,
+ -0.5703125,
+ 0.84765625,
+ -0.19042969,
+ 0.03515625,
+ 0.42578125,
+ -0.27539062,
+ -0.5390625,
+ 0.95703125,
+ 0.2734375,
+ 0.16699219,
+ -0.328125,
+ 0.11279297,
+ 0.003250122,
+ 0.47265625,
+ -0.31640625,
+ 0.546875,
+ 0.55859375,
+ 0.06933594,
+ -0.61328125,
+ -0.16210938,
+ -0.375,
+ 0.100097656,
+ -0.088378906,
+ 0.12695312,
+ 0.079589844,
+ 0.123535156,
+ -1.0078125,
+ 0.6875,
+ 0.022949219,
+ -0.40039062,
+ -0.09863281,
+ 0.29101562,
+ -1.2890625,
+ -0.20996094,
+ 0.36328125,
+ -0.3515625,
+ 0.7890625,
+ 0.12207031,
+ 0.48046875,
+ -0.13671875,
+ -0.041015625,
+ 0.19824219,
+ 0.19921875,
+ 0.01171875,
+ -0.37695312,
+ -0.62890625,
+ 0.9375,
+ -0.671875,
+ 0.24609375,
+ 0.6484375,
+ -0.29101562,
+ 0.076171875,
+ 0.62109375,
+ -0.5546875,
+ 0.36523438,
+ 0.75390625,
+ -0.19140625,
+ -0.875,
+ -0.8203125,
+ -0.24414062,
+ -0.625,
+ 0.1796875,
+ -0.40039062,
+ 0.25390625,
+ -0.14550781,
+ -0.21679688,
+ -0.828125,
+ 0.3359375,
+ 0.43554688,
+ 0.55078125,
+ -0.44921875,
+ -0.28710938,
+ 0.24023438,
+ 0.18066406,
+ -0.6953125,
+ 0.020385742,
+ -0.11376953,
+ 0.13867188,
+ -0.92578125,
+ 0.33398438,
+ -0.328125,
+ 0.78125,
+ -0.45507812,
+ -0.07470703,
+ 0.34179688,
+ 0.07080078,
+ 0.76171875,
+ 0.37890625,
+ -0.10644531,
+ 0.90234375,
+ -0.21875,
+ -0.15917969,
+ -0.36132812,
+ 0.2109375,
+ -0.45703125,
+ -0.76953125,
+ 0.21289062,
+ 0.26367188,
+ 0.49804688,
+ 0.35742188,
+ -0.20019531,
+ 0.31054688,
+ 0.34179688,
+ 0.17089844,
+ -0.15429688,
+ 0.39648438,
+ -0.5859375,
+ 0.20996094,
+ -0.40039062,
+ 0.5703125,
+ -0.515625,
+ 0.5234375,
+ 0.049560547,
+ 0.328125,
+ 0.24804688,
+ 0.42578125,
+ 0.609375,
+ 0.19238281,
+ 0.27929688,
+ 0.19335938,
+ 0.78125,
+ -0.9921875,
+ 0.23925781,
+ -1.3828125,
+ -0.22949219,
+ -0.578125,
+ -0.13964844,
+ -0.17382812,
+ -0.011169434,
+ 0.26171875,
+ -0.73046875,
+ -1.4375,
+ 0.6953125,
+ -0.7421875,
+ 0.052246094,
+ 0.12207031,
+ 1.3046875,
+ 0.38867188,
+ 0.040283203,
+ -0.546875,
+ -0.0021514893,
+ 0.18457031,
+ -0.5546875,
+ -0.51171875,
+ -0.16308594,
+ -0.104003906,
+ -0.38867188,
+ -0.20996094,
+ -0.8984375,
+ 0.6015625,
+ -0.30078125,
+ -0.13769531,
+ 0.16113281,
+ 0.58203125,
+ -0.23730469,
+ -0.125,
+ -1.0234375,
+ 0.875,
+ -0.7109375,
+ 0.29101562,
+ 0.09667969,
+ -0.3203125,
+ -0.48046875,
+ 0.37890625,
+ 0.734375,
+ -0.28710938,
+ -0.29882812,
+ -0.05493164,
+ 0.34765625,
+ -0.84375,
+ 0.65625,
+ 0.578125,
+ -0.20019531,
+ 0.13769531,
+ 0.10058594,
+ -0.37109375,
+ 0.36523438,
+ -0.22167969,
+ 0.72265625,
+ ],
+ "inputTextTokenCount": 6,
+ },
+ ],
+ "cohere.embed-english-v3::This is an embedding test.": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "11233989-07e8-4ecb-9ba6-79601ba6d8cc"},
+ 200,
+ {
+ "embeddings": [
+ [
+ 0.03390503,
+ 0.010032654,
+ 0.020904541,
+ -0.017105103,
+ -0.020050049,
+ -0.015411377,
+ -0.012001038,
+ -0.019744873,
+ -0.0107803345,
+ 0.012702942,
+ -0.043273926,
+ 0.003583908,
+ -0.023422241,
+ -0.017440796,
+ 0.03277588,
+ -0.04321289,
+ 0.018661499,
+ 0.05697632,
+ 0.041107178,
+ -0.007549286,
+ -0.026916504,
+ 0.012214661,
+ 0.00012886524,
+ -0.03010559,
+ -0.011680603,
+ -0.008392334,
+ 0.06222534,
+ -0.026260376,
+ 0.026947021,
+ -0.018692017,
+ 0.0016307831,
+ -0.018798828,
+ -0.00025224686,
+ 0.03982544,
+ -0.017501831,
+ 0.03665161,
+ -0.040985107,
+ 0.029296875,
+ 0.025283813,
+ -0.013618469,
+ 0.0038471222,
+ -0.007751465,
+ 0.017501831,
+ -0.03756714,
+ -0.012863159,
+ -0.03781128,
+ 0.043945312,
+ -0.018112183,
+ 0.01713562,
+ -0.04736328,
+ 0.005264282,
+ 0.0018386841,
+ -0.0018186569,
+ 0.016540527,
+ -0.012268066,
+ 0.061462402,
+ 0.006629944,
+ 0.02671814,
+ 0.039733887,
+ -0.0026226044,
+ -0.037628174,
+ 0.007675171,
+ 0.02418518,
+ -0.039855957,
+ 0.016586304,
+ -0.034820557,
+ -0.016113281,
+ 0.03262329,
+ 0.07537842,
+ -0.00554657,
+ -0.014450073,
+ 0.013061523,
+ 0.0056991577,
+ 0.007396698,
+ -0.055114746,
+ 0.032684326,
+ 0.0051460266,
+ 0.046173096,
+ 0.009277344,
+ -0.052337646,
+ -0.022872925,
+ 0.013946533,
+ -0.03643799,
+ 0.004562378,
+ -0.018218994,
+ 0.019851685,
+ 0.0038719177,
+ -0.032958984,
+ 0.04147339,
+ -0.016616821,
+ -0.010231018,
+ 0.099121094,
+ 0.0015497208,
+ 0.06021118,
+ -0.045562744,
+ 0.010559082,
+ 0.021408081,
+ -0.009635925,
+ -0.04067993,
+ -0.03060913,
+ -0.020248413,
+ -0.049346924,
+ 0.017562866,
+ -0.019943237,
+ 0.008331299,
+ -0.027755737,
+ -0.016693115,
+ -0.052368164,
+ 0.044647217,
+ 0.027954102,
+ -0.034332275,
+ 0.09460449,
+ -0.013832092,
+ -0.022888184,
+ 0.0033721924,
+ 0.015457153,
+ -0.03062439,
+ 0.026794434,
+ 0.0104904175,
+ -0.018737793,
+ 0.0060920715,
+ 0.027297974,
+ 0.027786255,
+ -0.016799927,
+ 0.022872925,
+ 0.043640137,
+ 0.0036582947,
+ -0.05267334,
+ 0.010169983,
+ -0.030258179,
+ -0.021530151,
+ 0.0496521,
+ -0.05883789,
+ -0.03439331,
+ -0.017944336,
+ -0.006149292,
+ -0.066223145,
+ -0.017593384,
+ 0.0016317368,
+ -0.0027751923,
+ -0.0028533936,
+ -0.057434082,
+ 0.04800415,
+ 0.01374054,
+ -0.06756592,
+ 0.087768555,
+ 0.04269409,
+ -0.032226562,
+ -0.04321289,
+ -0.08850098,
+ -0.022201538,
+ 0.0009198189,
+ -0.0046043396,
+ 0.029724121,
+ 0.01979065,
+ 0.03753662,
+ -0.05343628,
+ 0.033111572,
+ 0.034332275,
+ 0.071777344,
+ 0.0063934326,
+ -0.034606934,
+ -0.003545761,
+ 0.008972168,
+ 0.008232117,
+ 0.033447266,
+ 0.015823364,
+ 0.027297974,
+ 0.018981934,
+ 0.120910645,
+ -0.037872314,
+ 0.0038814545,
+ -0.0031642914,
+ 0.0071029663,
+ -0.022644043,
+ -0.008758545,
+ 0.0023460388,
+ -0.025802612,
+ 0.034332275,
+ -0.0021533966,
+ 0.02268982,
+ -0.008506775,
+ 0.010147095,
+ 0.022827148,
+ 0.0007414818,
+ -0.055999756,
+ 0.03237915,
+ -0.01083374,
+ -0.014343262,
+ 0.028793335,
+ 0.0068511963,
+ 0.031402588,
+ 0.023269653,
+ -0.013748169,
+ 0.00014042854,
+ 0.0007624626,
+ -0.03111267,
+ 0.007549286,
+ -0.0236969,
+ -0.00043439865,
+ -0.0058670044,
+ 0.013587952,
+ -0.0029067993,
+ -0.0052948,
+ 0.015701294,
+ -0.005924225,
+ 0.032104492,
+ -0.0017576218,
+ 0.052947998,
+ 0.011299133,
+ -0.03152466,
+ -0.027526855,
+ 0.031051636,
+ -0.04232788,
+ -0.048217773,
+ 0.04055786,
+ -0.038482666,
+ -0.06088257,
+ -0.016540527,
+ -0.027114868,
+ 0.008636475,
+ 0.06008911,
+ -0.038513184,
+ 0.023330688,
+ 0.0054473877,
+ 0.018325806,
+ -0.017288208,
+ -7.2062016e-05,
+ 0.0064430237,
+ 0.02357483,
+ 0.02166748,
+ -0.043060303,
+ -0.009613037,
+ 0.013504028,
+ -0.010856628,
+ -0.018585205,
+ -0.00041294098,
+ -0.012687683,
+ 0.019302368,
+ 0.03250122,
+ 0.03503418,
+ 0.037353516,
+ -0.01272583,
+ -0.039215088,
+ 0.05230713,
+ 0.008918762,
+ 0.020614624,
+ -0.012039185,
+ -0.041534424,
+ 0.0317688,
+ 0.012168884,
+ -0.0027694702,
+ -0.023773193,
+ 0.0068855286,
+ -0.04309082,
+ 0.034820557,
+ 0.018463135,
+ 0.048736572,
+ -0.0016841888,
+ 0.032836914,
+ -0.070617676,
+ 0.04473877,
+ 0.052581787,
+ -0.042114258,
+ -0.017456055,
+ -0.03945923,
+ -0.0040626526,
+ 0.016433716,
+ 0.02368164,
+ -0.04034424,
+ 0.006038666,
+ 0.005634308,
+ -5.722046e-06,
+ -0.01864624,
+ 0.0635376,
+ -0.041229248,
+ -0.026809692,
+ -0.009262085,
+ 0.0011701584,
+ -0.0053367615,
+ 0.020935059,
+ 0.04473877,
+ 0.03665161,
+ 0.01121521,
+ 0.017486572,
+ 0.061920166,
+ 0.020812988,
+ 0.013786316,
+ 0.0006785393,
+ 0.0027122498,
+ 0.012237549,
+ 0.07446289,
+ -0.021011353,
+ 0.06921387,
+ 0.046966553,
+ 0.028945923,
+ 0.00044202805,
+ 0.03488159,
+ 0.0034942627,
+ -0.0038585663,
+ -0.023269653,
+ 0.04852295,
+ -0.01525116,
+ 0.032836914,
+ 0.013153076,
+ 0.0014123917,
+ -0.005718231,
+ 0.038024902,
+ 0.015182495,
+ -0.0143585205,
+ -0.008659363,
+ 0.024093628,
+ 0.008972168,
+ -0.011962891,
+ 0.005367279,
+ -0.027297974,
+ 0.02696228,
+ -0.0063972473,
+ -0.008087158,
+ -0.015899658,
+ 0.07122803,
+ 0.0463562,
+ -0.06713867,
+ -0.02230835,
+ 0.011940002,
+ -0.0015964508,
+ -0.049438477,
+ -0.04864502,
+ -0.06262207,
+ -0.015029907,
+ -0.0049057007,
+ -0.084472656,
+ -0.011177063,
+ -0.031555176,
+ 0.0035552979,
+ -0.028427124,
+ 0.021759033,
+ 0.016174316,
+ 0.0390625,
+ 0.04168701,
+ 0.07714844,
+ -0.0064086914,
+ 0.013000488,
+ -0.011512756,
+ -0.0021686554,
+ -0.032196045,
+ -0.057678223,
+ 0.010017395,
+ -0.06793213,
+ -0.04220581,
+ 0.06793213,
+ -0.029144287,
+ -0.02229309,
+ 0.03074646,
+ 0.03265381,
+ -0.020050049,
+ 0.021911621,
+ 0.055236816,
+ 0.05480957,
+ -0.015823364,
+ 0.04815674,
+ 0.009384155,
+ 0.024383545,
+ -0.034484863,
+ -0.042114258,
+ -0.06744385,
+ -0.011207581,
+ 0.010749817,
+ 0.005012512,
+ 0.029510498,
+ 0.04977417,
+ 0.0070648193,
+ 0.00050497055,
+ -0.005710602,
+ -0.063964844,
+ -0.030807495,
+ -0.0013856888,
+ -0.026794434,
+ -0.024383545,
+ -0.025817871,
+ 0.00945282,
+ -0.008171082,
+ 0.071777344,
+ -0.018493652,
+ 0.041778564,
+ 0.0012550354,
+ 0.024902344,
+ 0.07366943,
+ 0.02381897,
+ -0.0016851425,
+ -0.015945435,
+ 0.035461426,
+ -0.038391113,
+ -0.02961731,
+ 0.020401001,
+ 0.0063171387,
+ 0.035308838,
+ 0.016586304,
+ -0.036590576,
+ -0.04522705,
+ 0.046722412,
+ -0.04901123,
+ -0.028076172,
+ -0.025787354,
+ -0.022567749,
+ -0.00843811,
+ 0.03778076,
+ 0.00020611286,
+ -0.006668091,
+ 0.027648926,
+ 0.027008057,
+ -0.011711121,
+ -0.0019445419,
+ 0.030456543,
+ 0.0038223267,
+ -0.037872314,
+ -0.019805908,
+ 0.017333984,
+ -0.023986816,
+ 0.0012874603,
+ -0.0053596497,
+ 0.02305603,
+ -0.03012085,
+ -0.013389587,
+ 0.016159058,
+ 0.020629883,
+ 0.04159546,
+ -0.008338928,
+ 0.029571533,
+ -0.0005707741,
+ 0.0231781,
+ -0.040863037,
+ -0.012886047,
+ -0.011627197,
+ -0.0574646,
+ 0.0011692047,
+ -0.0060691833,
+ -0.010749817,
+ 0.03567505,
+ -0.051757812,
+ 0.009735107,
+ 0.016159058,
+ 0.037139893,
+ -0.013214111,
+ 0.013938904,
+ -0.025482178,
+ 0.04647827,
+ 0.016418457,
+ -0.018936157,
+ 0.040008545,
+ -0.054595947,
+ 0.007865906,
+ -0.022872925,
+ 0.02508545,
+ -0.033935547,
+ 0.004310608,
+ 0.027008057,
+ 0.03010559,
+ 0.020736694,
+ 0.020111084,
+ 0.037719727,
+ -0.015487671,
+ -0.04598999,
+ 0.016189575,
+ -0.009643555,
+ 0.022399902,
+ 0.027786255,
+ 0.013580322,
+ -0.013595581,
+ -0.004825592,
+ 0.039855957,
+ -0.05834961,
+ -0.016906738,
+ -0.016235352,
+ -0.01826477,
+ -0.0053520203,
+ 0.031402588,
+ 0.023986816,
+ -0.012367249,
+ -0.02835083,
+ -0.004310608,
+ -0.025115967,
+ -0.05899048,
+ -0.036987305,
+ 0.01574707,
+ -0.007926941,
+ -0.030853271,
+ 0.04458618,
+ 0.00818634,
+ 0.017059326,
+ 4.7802925e-05,
+ 0.0062294006,
+ 0.028930664,
+ -0.027618408,
+ -0.013557434,
+ -0.0093307495,
+ -0.012741089,
+ -0.009307861,
+ 0.0032444,
+ -0.09460449,
+ -0.0552063,
+ 0.034576416,
+ 0.02178955,
+ 0.024612427,
+ 0.013587952,
+ -0.041656494,
+ -0.029647827,
+ 0.010848999,
+ 0.045959473,
+ -0.001698494,
+ 0.031341553,
+ 0.016693115,
+ 0.027145386,
+ -0.029541016,
+ -0.011222839,
+ 0.08703613,
+ -0.017303467,
+ -0.009376526,
+ 0.025436401,
+ -0.020217896,
+ 0.06939697,
+ 0.023651123,
+ 0.05065918,
+ -0.010749817,
+ -8.738041e-05,
+ 0.019195557,
+ 0.024917603,
+ -0.009590149,
+ -0.033172607,
+ -0.025314331,
+ 0.0049819946,
+ -0.0070266724,
+ 0.019622803,
+ -0.023605347,
+ 0.030258179,
+ 0.03869629,
+ -0.036834717,
+ -0.0025596619,
+ 0.007320404,
+ -0.021438599,
+ -0.0044021606,
+ -0.0052604675,
+ -0.050109863,
+ 0.0051498413,
+ -0.011734009,
+ -0.027770996,
+ -0.0043258667,
+ 0.07495117,
+ 0.007820129,
+ 0.03930664,
+ 0.058563232,
+ -0.006385803,
+ -0.04055786,
+ -0.02609253,
+ -0.03265381,
+ -0.02670288,
+ -0.013587952,
+ 0.015548706,
+ -0.047790527,
+ -0.010292053,
+ -0.02508545,
+ -0.005592346,
+ -0.025299072,
+ 0.023254395,
+ 0.0043945312,
+ 0.0062408447,
+ 0.006996155,
+ -0.015060425,
+ -0.0059814453,
+ -0.033325195,
+ -0.024520874,
+ -0.015472412,
+ 0.01676941,
+ -0.011817932,
+ 0.03173828,
+ 0.018981934,
+ -0.03488159,
+ -0.005340576,
+ -0.003358841,
+ 0.045715332,
+ 0.03314209,
+ 0.050964355,
+ -0.018859863,
+ 0.0541687,
+ 0.025115967,
+ 0.025894165,
+ -0.028366089,
+ -0.0070533752,
+ -0.022506714,
+ 0.018463135,
+ 0.0068588257,
+ -0.023742676,
+ -0.011627197,
+ -0.05935669,
+ 0.026000977,
+ -0.013893127,
+ 0.06555176,
+ -0.010292053,
+ -0.020202637,
+ 0.018432617,
+ -0.0043754578,
+ 0.030548096,
+ 0.0262146,
+ 0.027801514,
+ -0.039001465,
+ 0.026412964,
+ 0.028793335,
+ 0.0063476562,
+ -0.0027694702,
+ -0.014305115,
+ 0.022003174,
+ 0.0017242432,
+ -0.02116394,
+ 0.028152466,
+ -0.027023315,
+ -0.008705139,
+ -0.0037574768,
+ 0.048034668,
+ 0.010238647,
+ -0.020324707,
+ 0.03086853,
+ -0.031066895,
+ 0.0146102905,
+ 0.014930725,
+ -0.014785767,
+ -0.010292053,
+ 0.017929077,
+ -0.010429382,
+ -0.00019311905,
+ -0.0012149811,
+ -0.026733398,
+ -0.026031494,
+ -0.050048828,
+ -0.07861328,
+ -0.017684937,
+ 0.061706543,
+ -0.011001587,
+ -0.041168213,
+ 0.003314972,
+ 0.029876709,
+ -0.009559631,
+ 0.032348633,
+ 0.0635376,
+ -0.040252686,
+ 0.056365967,
+ -0.0446167,
+ 0.026031494,
+ 0.017089844,
+ -0.04397583,
+ 0.044311523,
+ 0.0068740845,
+ 0.034454346,
+ 0.025848389,
+ 0.02027893,
+ 0.005153656,
+ 0.04159546,
+ -0.008239746,
+ 0.0056381226,
+ -0.0033721924,
+ -0.0692749,
+ -0.0038280487,
+ 0.022140503,
+ -0.008087158,
+ 0.0051727295,
+ -0.0102005005,
+ 0.0009098053,
+ 0.04067993,
+ -0.0065193176,
+ 0.026031494,
+ -0.08728027,
+ -0.027648926,
+ 0.04373169,
+ -0.048187256,
+ -0.033233643,
+ -0.014953613,
+ 0.022720337,
+ -0.004333496,
+ 0.02609253,
+ 0.0017251968,
+ -0.017868042,
+ -0.036956787,
+ -0.01838684,
+ 0.06665039,
+ 0.0259552,
+ -0.053497314,
+ -0.03111267,
+ 0.050872803,
+ 0.036895752,
+ -0.030944824,
+ -0.031921387,
+ -0.0569458,
+ 0.020248413,
+ -0.02229309,
+ 0.002916336,
+ 0.0076942444,
+ -0.0060691833,
+ -0.0317688,
+ -0.013793945,
+ 0.015068054,
+ -0.004508972,
+ -0.0047798157,
+ -0.0021457672,
+ 0.0003311634,
+ -0.036346436,
+ 0.0023174286,
+ 0.018096924,
+ 0.0063323975,
+ -0.014152527,
+ 0.0023460388,
+ 0.019836426,
+ -0.00233078,
+ 0.009048462,
+ -0.04812622,
+ -0.028442383,
+ 0.04925537,
+ 0.0043754578,
+ 0.04650879,
+ 0.055358887,
+ 0.036499023,
+ -0.044677734,
+ -0.012786865,
+ -0.013916016,
+ 0.025985718,
+ -0.033691406,
+ 0.010375977,
+ 0.036590576,
+ -0.036376953,
+ 0.009384155,
+ 0.0012626648,
+ -0.017623901,
+ -0.00032114983,
+ -0.026428223,
+ 0.018112183,
+ -0.016098022,
+ 0.0066375732,
+ -0.08355713,
+ 0.024291992,
+ 0.043670654,
+ -0.0067367554,
+ 0.01763916,
+ -0.0057640076,
+ -0.0154953,
+ 0.04196167,
+ 0.005542755,
+ 0.026901245,
+ 0.06427002,
+ 0.010612488,
+ 0.040222168,
+ 0.033966064,
+ 0.017028809,
+ 0.02748108,
+ 0.007980347,
+ -0.045013428,
+ -0.001121521,
+ 0.001408577,
+ 0.037750244,
+ 0.013549805,
+ -0.016967773,
+ -0.047729492,
+ 0.027496338,
+ -0.064331055,
+ 0.010917664,
+ -0.013870239,
+ 0.03668213,
+ 0.0055236816,
+ 0.0087509155,
+ -0.0847168,
+ -0.009521484,
+ 0.0703125,
+ -0.03338623,
+ -0.011062622,
+ 0.06555176,
+ -0.011268616,
+ -0.08477783,
+ 0.014633179,
+ 0.0045928955,
+ -0.0029029846,
+ -0.0050849915,
+ -0.016082764,
+ 0.037017822,
+ 0.023406982,
+ 0.03765869,
+ -0.032714844,
+ -0.03692627,
+ -0.0057411194,
+ -0.026748657,
+ 0.0107040405,
+ 0.033050537,
+ 0.018829346,
+ 0.058685303,
+ 0.0005726814,
+ 0.026947021,
+ 0.004272461,
+ -0.006614685,
+ -0.0018100739,
+ -0.024353027,
+ -0.007835388,
+ 0.0016746521,
+ 0.00806427,
+ -0.008636475,
+ 0.031188965,
+ -0.08416748,
+ 0.05014038,
+ 0.0073242188,
+ 0.017822266,
+ -0.08282471,
+ 0.010810852,
+ 0.07312012,
+ 0.014053345,
+ 0.00025081635,
+ 0.0015468597,
+ 0.00020134449,
+ -0.0043296814,
+ -0.050750732,
+ -0.05758667,
+ 0.002746582,
+ 0.030395508,
+ 0.014060974,
+ -0.047302246,
+ -0.045776367,
+ 0.0045928955,
+ 0.01739502,
+ 0.010063171,
+ 0.0031433105,
+ -0.005428314,
+ 0.0031604767,
+ 0.018371582,
+ -0.025680542,
+ 0.0076446533,
+ 0.0026683807,
+ 0.025604248,
+ -0.025741577,
+ 0.05001831,
+ 0.06768799,
+ 0.049713135,
+ 0.016220093,
+ -0.06008911,
+ -0.034942627,
+ 0.024490356,
+ -0.01651001,
+ 0.026443481,
+ -0.06097412,
+ 0.04675293,
+ 0.034240723,
+ -0.06555176,
+ 0.02267456,
+ 0.012382507,
+ -0.023132324,
+ 0.015914917,
+ 0.027236938,
+ 0.033081055,
+ 0.025436401,
+ -0.018951416,
+ 0.015510559,
+ 0.0289917,
+ 0.06317139,
+ 0.02935791,
+ -0.03189087,
+ -0.015930176,
+ 0.0011873245,
+ -0.028625488,
+ 0.013977051,
+ -0.0012779236,
+ 0.04220581,
+ 0.025772095,
+ 0.009117126,
+ -0.052642822,
+ -0.009880066,
+ -0.032836914,
+ -0.028945923,
+ 0.027267456,
+ 0.07165527,
+ -0.005748749,
+ 0.01701355,
+ 0.0049972534,
+ -0.005130768,
+ 0.049835205,
+ -0.02015686,
+ 0.03857422,
+ 0.014305115,
+ 0.022415161,
+ 0.025924683,
+ 0.04031372,
+ -0.00015962124,
+ -0.02267456,
+ 0.003648758,
+ -0.008026123,
+ 0.042755127,
+ -0.004512787,
+ -0.022079468,
+ 0.010383606,
+ -0.014602661,
+ 0.026138306,
+ 0.020751953,
+ -0.025787354,
+ -0.000538826,
+ -0.013442993,
+ -0.00869751,
+ -0.017547607,
+ -0.03704834,
+ -0.010871887,
+ -0.0012283325,
+ 0.008880615,
+ -0.047088623,
+ -0.008216858,
+ 0.014083862,
+ -0.0015964508,
+ -0.028839111,
+ -0.00017225742,
+ -0.038604736,
+ 0.00187397,
+ 0.00504303,
+ 0.017990112,
+ 0.036224365,
+ -0.011581421,
+ -0.01436615,
+ 0.01626587,
+ 0.0026187897,
+ 0.064086914,
+ 0.016433716,
+ -0.010345459,
+ -0.036102295,
+ 0.025878906,
+ -0.04260254,
+ -0.0109939575,
+ 0.010246277,
+ 0.006877899,
+ -0.04071045,
+ -0.021224976,
+ -0.003982544,
+ 0.010421753,
+ -0.0345459,
+ -0.073791504,
+ -0.008987427,
+ 0.01260376,
+ -0.043762207,
+ 0.01210022,
+ -0.011390686,
+ -0.0007429123,
+ -0.027786255,
+ -0.023620605,
+ 0.019165039,
+ -0.010894775,
+ 0.004272461,
+ -0.0597229,
+ 0.036499023,
+ -0.049224854,
+ -0.04663086,
+ -0.02243042,
+ -0.0018253326,
+ 0.027572632,
+ -0.015159607,
+ -0.014411926,
+ -0.0033721924,
+ 0.032470703,
+ 0.041168213,
+ -0.021713257,
+ -0.027160645,
+ 0.025726318,
+ 0.048431396,
+ -0.031829834,
+ 0.037841797,
+ 0.04638672,
+ 0.014976501,
+ -0.024612427,
+ 0.0014600754,
+ -0.04031372,
+ -0.0011501312,
+ 0.004142761,
+ 0.012207031,
+ -0.00806427,
+ -0.009025574,
+ -0.051513672,
+ 0.030807495,
+ 0.016998291,
+ -0.049194336,
+ 0.0038776398,
+ -0.0042533875,
+ -0.04260254,
+ -0.008239746,
+ -0.0060195923,
+ 0.01473999,
+ 0.0034885406,
+ -0.0063171387,
+ -0.048614502,
+ 0.037628174,
+ -0.022247314,
+ -0.018951416,
+ 0.02192688,
+ -0.0065994263,
+ -0.02519226,
+ -0.0004734993,
+ -0.036102295,
+ 0.009109497,
+ -0.0029640198,
+ -0.012290955,
+ 0.011711121,
+ -0.034942627,
+ 0.043273926,
+ 0.022644043,
+ -0.026351929,
+ -0.014381409,
+ 0.044433594,
+ -0.04949951,
+ -0.025878906,
+ -0.01890564,
+ 0.010566711,
+ -0.017684937,
+ -0.06555176,
+ 0.047912598,
+ -0.031921387,
+ 0.047943115,
+ -0.061584473,
+ 0.051605225,
+ 0.009773254,
+ 0.016525269,
+ 0.0025367737,
+ -0.064086914,
+ 0.031311035,
+ -0.041778564,
+ -0.03250122,
+ -0.044158936,
+ -0.0135650635,
+ 0.008224487,
+ ]
+ ],
+ "id": "d26b1832-cd83-40cf-91e9-d96505b89ae8",
+ "response_type": "embeddings_floats",
+ "texts": ["This is an embedding test."],
+ },
+ ],
+ "does-not-exist::": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "f4908827-3db9-4742-9103-2bbc34578b03",
+ "x-amzn-ErrorType": "ValidationException:http://internal.amazon.com/coral/com.amazon.bedrock/",
+ },
+ 400,
+ {"message": "The provided model identifier is invalid."},
+ ],
+ "mistral.mistral-7b-instruct-v0%3A2::[INST] Invalid Token [/INST]": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "ai21.j2-mid-v1::Invalid Token": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "9021791d-3797-493d-9277-e33aa6f6d544",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "amazon.titan-embed-g1-text-02::Invalid Token": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "73328313-506e-4da8-af0f-51017fa6ca3f",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "amazon.titan-embed-text-v1::Invalid Token": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "aece6ad7-e2ff-443b-a953-ba7d385fd0cc",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "cohere.embed-english-v3::Invalid Token": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "73328313-506e-4da8-af0f-51017fa6ca3f",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "amazon.titan-text-express-v1::Invalid Token": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "15b39c8b-8e85-42c9-9623-06720301bda3",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "anthropic.claude-instant-v1::Human: Invalid Token Assistant:": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "37396f55-b721-4bae-9461-4c369f5a080d",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "cohere.command-text-v14::Invalid Token": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "22476490-a0d6-42db-b5ea-32d0b8a7f751",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "meta.llama2-13b-chat-v1::Invalid Token": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "22476490-a0d6-42db-b5ea-32d0b8a7f751",
+ "x-amzn-ErrorType": "UnrecognizedClientException:http://internal.amazon.com/coral/com.amazon.coral.service/",
+ },
+ 403,
+ {"message": "The security token included in the request is invalid."},
+ ],
+ "amazon.titan-text-express-v1::Malformed Body": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "81508a1c-33a8-4294-8743-f0c629af2f49"},
+ 200,
+ {
+ "inputTextTokenCount": 12,
+ "results": [
+ {
+ "tokenCount": 32,
+ "outputText": "\n1 degree Fahrenheit is 0.56 Celsius. Therefore, 212 degree Fahrenheit in Celsius would be 115.42.",
+ "completionReason": "FINISH",
+ }
+ ],
+ },
+ ],
+ "amazon.titan-embed-g1-text-02::Malformed Body": [
+ {"Content-Type": "application/json", "x-amzn-RequestId": "b10ac895-eae3-4f07-b926-10b2866c55ed"},
+ 200,
+ {
+ "embedding": [
+ -0.14160156,
+ 0.034423828,
+ 0.54296875,
+ 0.10986328,
+ 0.053466797,
+ 0.3515625,
+ 0.12988281,
+ -0.0002708435,
+ -0.21484375,
+ 0.060302734,
+ 0.58984375,
+ -0.5859375,
+ 0.52734375,
+ 0.82421875,
+ -0.91015625,
+ -0.19628906,
+ 0.45703125,
+ 0.609375,
+ -0.67578125,
+ 0.39453125,
+ -0.46875,
+ -0.25390625,
+ -0.21191406,
+ 0.114746094,
+ 0.31640625,
+ -0.41015625,
+ -0.32617188,
+ -0.43554688,
+ 0.4765625,
+ -0.4921875,
+ 0.40429688,
+ 0.06542969,
+ 0.859375,
+ -0.008056641,
+ -0.19921875,
+ 0.072753906,
+ 0.33203125,
+ 0.69921875,
+ 0.39453125,
+ 0.15527344,
+ 0.08886719,
+ -0.25,
+ 0.859375,
+ 0.22949219,
+ -0.19042969,
+ 0.13769531,
+ -0.078125,
+ 0.41210938,
+ 0.875,
+ 0.5234375,
+ 0.59765625,
+ -0.22949219,
+ -0.22558594,
+ -0.47460938,
+ 0.37695312,
+ 0.51953125,
+ -0.5703125,
+ 0.46679688,
+ 0.43554688,
+ 0.17480469,
+ -0.080566406,
+ -0.16699219,
+ -0.734375,
+ -1.0625,
+ -0.33984375,
+ 0.390625,
+ -0.18847656,
+ -0.5234375,
+ -0.48828125,
+ 0.44921875,
+ -0.09814453,
+ -0.3359375,
+ 0.087402344,
+ 0.36914062,
+ 1.3203125,
+ 0.25585938,
+ 0.14746094,
+ -0.059570312,
+ -0.15820312,
+ -0.037353516,
+ -0.61328125,
+ -0.6484375,
+ -0.35351562,
+ 0.55078125,
+ -0.26953125,
+ 0.90234375,
+ 0.3671875,
+ 0.31054688,
+ 0.00014019012,
+ -0.171875,
+ 0.025512695,
+ 0.5078125,
+ 0.11621094,
+ 0.33203125,
+ 0.8125,
+ -0.3046875,
+ -1.078125,
+ -0.5703125,
+ 0.26171875,
+ -0.4609375,
+ 0.203125,
+ 0.44726562,
+ -0.5078125,
+ 0.41601562,
+ -0.1953125,
+ 0.028930664,
+ -0.57421875,
+ 0.2265625,
+ 0.13574219,
+ -0.040039062,
+ -0.22949219,
+ -0.515625,
+ -0.19042969,
+ -0.30078125,
+ 0.10058594,
+ -0.66796875,
+ 0.6015625,
+ 0.296875,
+ -0.765625,
+ -0.87109375,
+ 0.2265625,
+ 0.068847656,
+ -0.088378906,
+ -0.1328125,
+ -0.796875,
+ -0.37304688,
+ 0.47460938,
+ -0.3515625,
+ -0.8125,
+ -0.32226562,
+ 0.265625,
+ 0.3203125,
+ -0.4140625,
+ -0.49023438,
+ 0.859375,
+ -0.19140625,
+ -0.6328125,
+ 0.10546875,
+ -0.5625,
+ 0.66015625,
+ 0.26171875,
+ -0.2109375,
+ 0.421875,
+ -0.82421875,
+ 0.29296875,
+ 0.17773438,
+ 0.24023438,
+ 0.5078125,
+ -0.49804688,
+ -0.10205078,
+ 0.10498047,
+ -0.36132812,
+ -0.47460938,
+ -0.20996094,
+ 0.010070801,
+ -0.546875,
+ 0.66796875,
+ -0.123046875,
+ -0.75390625,
+ 0.19628906,
+ 0.17480469,
+ 0.18261719,
+ -0.96875,
+ -0.26171875,
+ 0.4921875,
+ -0.40039062,
+ 0.296875,
+ 0.1640625,
+ -0.20507812,
+ -0.36132812,
+ 0.76171875,
+ -1.234375,
+ -0.625,
+ 0.060058594,
+ -0.09375,
+ -0.14746094,
+ 1.09375,
+ 0.057861328,
+ 0.22460938,
+ -0.703125,
+ 0.07470703,
+ 0.23828125,
+ -0.083984375,
+ -0.54296875,
+ 0.5546875,
+ -0.5,
+ -0.390625,
+ 0.106933594,
+ 0.6640625,
+ 0.27734375,
+ -0.953125,
+ 0.35351562,
+ -0.7734375,
+ -0.77734375,
+ 0.16503906,
+ -0.42382812,
+ 0.36914062,
+ 0.020141602,
+ -1.3515625,
+ 0.18847656,
+ 0.13476562,
+ -0.034179688,
+ -0.03930664,
+ -0.03857422,
+ -0.027954102,
+ 0.73828125,
+ -0.18945312,
+ -0.09814453,
+ -0.46289062,
+ 0.36914062,
+ 0.033203125,
+ 0.020874023,
+ -0.703125,
+ 0.91796875,
+ 0.38671875,
+ 0.625,
+ -0.19335938,
+ -0.16796875,
+ -0.58203125,
+ 0.21386719,
+ -0.032470703,
+ -0.296875,
+ -0.15625,
+ -0.1640625,
+ -0.74609375,
+ 0.328125,
+ 0.5546875,
+ -0.1953125,
+ 1.0546875,
+ 0.171875,
+ -0.099609375,
+ 0.5234375,
+ 0.05078125,
+ -0.35742188,
+ -0.2734375,
+ -1.3203125,
+ -0.8515625,
+ -0.16015625,
+ 0.01574707,
+ 0.29296875,
+ 0.18457031,
+ -0.265625,
+ 0.048339844,
+ 0.045654297,
+ -0.32226562,
+ 0.087890625,
+ -0.0047302246,
+ 0.38671875,
+ 0.10644531,
+ -0.06225586,
+ 1.03125,
+ 0.94140625,
+ -0.3203125,
+ 0.20800781,
+ -1.171875,
+ 0.48046875,
+ -0.091796875,
+ 0.20800781,
+ -0.1328125,
+ -0.20507812,
+ 0.28125,
+ -0.47070312,
+ -0.09033203,
+ 0.0013809204,
+ -0.08203125,
+ 0.43359375,
+ -0.03100586,
+ -0.060791016,
+ -0.53515625,
+ -1.46875,
+ 0.000101566315,
+ 0.515625,
+ 0.40625,
+ -0.10498047,
+ -0.15820312,
+ -0.009460449,
+ -0.77734375,
+ -0.5859375,
+ 0.9765625,
+ 0.099609375,
+ 0.51953125,
+ 0.38085938,
+ -0.09667969,
+ -0.100097656,
+ -0.5,
+ -1.3125,
+ -0.18066406,
+ -0.099121094,
+ 0.26171875,
+ -0.14453125,
+ -0.546875,
+ 0.17578125,
+ 0.484375,
+ 0.765625,
+ 0.45703125,
+ 0.2734375,
+ 0.0028076172,
+ 0.17089844,
+ -0.32421875,
+ -0.37695312,
+ 0.30664062,
+ -0.48046875,
+ 0.07128906,
+ 0.031982422,
+ -0.31054688,
+ -0.055419922,
+ -0.29296875,
+ 0.3359375,
+ -0.296875,
+ 0.47851562,
+ -0.05126953,
+ 0.18457031,
+ -0.01953125,
+ -0.35742188,
+ 0.017944336,
+ -0.25,
+ 0.10595703,
+ 0.17382812,
+ -0.73828125,
+ 0.36914062,
+ -0.15234375,
+ -0.8125,
+ 0.17382812,
+ 0.048095703,
+ 0.5625,
+ -0.33789062,
+ 0.023071289,
+ -0.21972656,
+ 0.16015625,
+ 0.032958984,
+ -1.1171875,
+ -0.984375,
+ 0.83984375,
+ 0.009033203,
+ -0.042236328,
+ -0.46484375,
+ -0.08203125,
+ 0.44726562,
+ -0.765625,
+ -0.3984375,
+ -0.40820312,
+ -0.234375,
+ 0.044189453,
+ 0.119628906,
+ -0.7578125,
+ -0.55078125,
+ -0.4453125,
+ 0.7578125,
+ 0.34960938,
+ 0.96484375,
+ 0.35742188,
+ 0.36914062,
+ -0.35351562,
+ -0.36132812,
+ 1.109375,
+ 0.5859375,
+ 0.85546875,
+ -0.10644531,
+ -0.6953125,
+ -0.0066833496,
+ 0.042236328,
+ -0.06689453,
+ 0.36914062,
+ 0.9765625,
+ -0.3046875,
+ 0.59765625,
+ -0.6640625,
+ 0.21484375,
+ -0.07128906,
+ 1.1328125,
+ -0.51953125,
+ 0.86328125,
+ -0.11328125,
+ 0.15722656,
+ -0.36328125,
+ -0.04638672,
+ 1.4375,
+ 0.18457031,
+ -0.18359375,
+ 0.10595703,
+ -0.49023438,
+ -0.07324219,
+ -0.73046875,
+ -0.119140625,
+ 0.021118164,
+ 0.4921875,
+ -0.46875,
+ 0.28710938,
+ 0.3359375,
+ 0.11767578,
+ -0.2109375,
+ -0.14550781,
+ 0.39648438,
+ -0.27734375,
+ 0.48046875,
+ 0.12988281,
+ 0.45507812,
+ -0.375,
+ -0.84765625,
+ 0.25585938,
+ -0.36523438,
+ 0.8046875,
+ 0.42382812,
+ -0.24511719,
+ 0.54296875,
+ 0.71875,
+ 0.010009766,
+ -0.04296875,
+ 0.083984375,
+ -0.52734375,
+ 0.13964844,
+ -0.27539062,
+ -0.30273438,
+ 1.1484375,
+ -0.515625,
+ -0.19335938,
+ 0.58984375,
+ 0.049072266,
+ 0.703125,
+ -0.04272461,
+ 0.5078125,
+ 0.34960938,
+ -0.3359375,
+ -0.47460938,
+ 0.049316406,
+ 0.36523438,
+ 0.7578125,
+ -0.022827148,
+ -0.71484375,
+ 0.21972656,
+ 0.09716797,
+ -0.203125,
+ -0.36914062,
+ 1.34375,
+ 0.34179688,
+ 0.46679688,
+ 1.078125,
+ 0.26171875,
+ 0.41992188,
+ 0.22363281,
+ -0.515625,
+ -0.5703125,
+ 0.13378906,
+ 0.26757812,
+ -0.22558594,
+ -0.5234375,
+ 0.06689453,
+ 0.08251953,
+ -0.625,
+ 0.16796875,
+ 0.43164062,
+ -0.55859375,
+ 0.28125,
+ 0.078125,
+ 0.6328125,
+ 0.23242188,
+ -0.064941406,
+ -0.004486084,
+ -0.20703125,
+ 0.2734375,
+ 0.453125,
+ -0.734375,
+ 0.04272461,
+ 0.36132812,
+ -0.19628906,
+ -0.12402344,
+ 1.3515625,
+ 0.25585938,
+ 0.4921875,
+ -0.29296875,
+ -0.58984375,
+ 0.021240234,
+ -0.044677734,
+ 0.7578125,
+ -0.7890625,
+ 0.10253906,
+ -0.15820312,
+ -0.5078125,
+ -0.39453125,
+ -0.453125,
+ 0.35742188,
+ 0.921875,
+ 0.44335938,
+ -0.49804688,
+ 0.44335938,
+ 0.31445312,
+ 0.58984375,
+ -1.0078125,
+ -0.22460938,
+ 0.24121094,
+ 0.87890625,
+ 0.66015625,
+ -0.390625,
+ -0.05053711,
+ 0.059570312,
+ 0.36132812,
+ -0.00038719177,
+ -0.017089844,
+ 0.62890625,
+ 0.203125,
+ 0.17480469,
+ 0.025512695,
+ 0.47460938,
+ 0.3125,
+ 1.140625,
+ 0.32421875,
+ -0.057861328,
+ 0.36914062,
+ -0.7265625,
+ -0.51953125,
+ 0.26953125,
+ 0.42773438,
+ 0.064453125,
+ 0.6328125,
+ 0.27148438,
+ -0.11767578,
+ 0.66796875,
+ -0.38671875,
+ 0.5234375,
+ -0.59375,
+ 0.5078125,
+ 0.008239746,
+ -0.34179688,
+ -0.27539062,
+ 0.5234375,
+ 1.296875,
+ 0.29492188,
+ -0.010986328,
+ -0.41210938,
+ 0.59375,
+ 0.061767578,
+ -0.33398438,
+ -2.03125,
+ 0.87890625,
+ -0.010620117,
+ 0.53125,
+ 0.14257812,
+ -0.515625,
+ -1.03125,
+ 0.578125,
+ 0.1875,
+ 0.44335938,
+ -0.33203125,
+ -0.36328125,
+ -0.3203125,
+ 0.29296875,
+ -0.8203125,
+ 0.41015625,
+ -0.48242188,
+ 0.66015625,
+ 0.5625,
+ -0.16503906,
+ -0.54296875,
+ -0.38085938,
+ 0.26171875,
+ 0.62109375,
+ 0.29101562,
+ -0.31054688,
+ 0.23730469,
+ -0.8515625,
+ 0.5234375,
+ 0.15332031,
+ 0.52734375,
+ -0.079589844,
+ -0.080566406,
+ -0.15527344,
+ -0.022827148,
+ 0.030517578,
+ -0.1640625,
+ -0.421875,
+ 0.09716797,
+ 0.03930664,
+ -0.055908203,
+ -0.546875,
+ -0.47851562,
+ 0.091796875,
+ 0.32226562,
+ -0.94140625,
+ -0.04638672,
+ -1.203125,
+ -0.39648438,
+ 0.45507812,
+ 0.296875,
+ -0.45703125,
+ 0.37890625,
+ -0.122558594,
+ 0.28320312,
+ -0.01965332,
+ -0.11669922,
+ -0.34570312,
+ -0.53515625,
+ -0.091308594,
+ -0.9375,
+ -0.32617188,
+ 0.095214844,
+ -0.4765625,
+ 0.37890625,
+ -0.859375,
+ 1.1015625,
+ -0.08935547,
+ 0.46484375,
+ -0.19238281,
+ 0.7109375,
+ 0.040039062,
+ -0.5390625,
+ 0.22363281,
+ -0.70703125,
+ 0.4921875,
+ -0.119140625,
+ -0.26757812,
+ -0.08496094,
+ 0.0859375,
+ -0.00390625,
+ -0.013366699,
+ -0.03955078,
+ 0.07421875,
+ -0.13085938,
+ 0.29101562,
+ -0.12109375,
+ 0.45703125,
+ 0.021728516,
+ 0.38671875,
+ -0.3671875,
+ -0.52734375,
+ -0.115722656,
+ 0.125,
+ 0.5703125,
+ -1.234375,
+ 0.06298828,
+ -0.55859375,
+ 0.60546875,
+ 0.8125,
+ -0.0032958984,
+ -0.068359375,
+ -0.21191406,
+ 0.56640625,
+ 0.17285156,
+ -0.3515625,
+ 0.36328125,
+ -0.99609375,
+ 0.43554688,
+ -0.1015625,
+ 0.07080078,
+ -0.66796875,
+ 1.359375,
+ 0.41601562,
+ 0.15917969,
+ 0.17773438,
+ -0.28710938,
+ 0.021850586,
+ -0.46289062,
+ 0.17578125,
+ -0.03955078,
+ -0.026855469,
+ 0.5078125,
+ -0.65625,
+ 0.0012512207,
+ 0.044433594,
+ -0.18652344,
+ 0.4921875,
+ -0.75390625,
+ 0.0072021484,
+ 0.4375,
+ -0.31445312,
+ 0.20214844,
+ 0.15039062,
+ -0.63671875,
+ -0.296875,
+ -0.375,
+ -0.027709961,
+ 0.013427734,
+ 0.17089844,
+ 0.89453125,
+ 0.11621094,
+ -0.43945312,
+ -0.30859375,
+ 0.02709961,
+ 0.23242188,
+ -0.64453125,
+ -0.859375,
+ 0.22167969,
+ -0.023071289,
+ -0.052734375,
+ 0.3671875,
+ -0.18359375,
+ 0.81640625,
+ -0.11816406,
+ 0.028320312,
+ 0.19042969,
+ 0.012817383,
+ -0.43164062,
+ 0.55859375,
+ -0.27929688,
+ 0.14257812,
+ -0.140625,
+ -0.048583984,
+ -0.014526367,
+ 0.35742188,
+ 0.22753906,
+ 0.13183594,
+ 0.04638672,
+ 0.03930664,
+ -0.29296875,
+ -0.2109375,
+ -0.16308594,
+ -0.48046875,
+ -0.13378906,
+ -0.39257812,
+ 0.29296875,
+ -0.047851562,
+ -0.5546875,
+ 0.08300781,
+ -0.14941406,
+ -0.07080078,
+ 0.12451172,
+ 0.1953125,
+ -0.51171875,
+ -0.048095703,
+ 0.1953125,
+ -0.37695312,
+ 0.46875,
+ -0.084472656,
+ 0.19042969,
+ -0.39453125,
+ 0.69921875,
+ -0.0065307617,
+ 0.25390625,
+ -0.16992188,
+ -0.5078125,
+ 0.016845703,
+ 0.27929688,
+ -0.22070312,
+ 0.671875,
+ 0.18652344,
+ 0.25,
+ -0.046875,
+ -0.012023926,
+ -0.36523438,
+ 0.36523438,
+ -0.11279297,
+ 0.421875,
+ 0.079589844,
+ -0.100097656,
+ 0.37304688,
+ 0.29882812,
+ -0.10546875,
+ -0.36523438,
+ 0.040039062,
+ 0.546875,
+ 0.12890625,
+ -0.06542969,
+ -0.38085938,
+ -0.35742188,
+ -0.6484375,
+ -0.28515625,
+ 0.0107421875,
+ -0.055664062,
+ 0.45703125,
+ 0.33984375,
+ 0.26367188,
+ -0.23144531,
+ 0.012878418,
+ -0.875,
+ 0.11035156,
+ 0.33984375,
+ 0.203125,
+ 0.38867188,
+ 0.24902344,
+ -0.37304688,
+ -0.98046875,
+ -0.122558594,
+ -0.17871094,
+ -0.09277344,
+ 0.1796875,
+ 0.4453125,
+ -0.66796875,
+ 0.78515625,
+ 0.12988281,
+ 0.35546875,
+ 0.44140625,
+ 0.58984375,
+ 0.29492188,
+ 0.7734375,
+ -0.21972656,
+ -0.40234375,
+ -0.22265625,
+ 0.18359375,
+ 0.54296875,
+ 0.17382812,
+ 0.59375,
+ -0.390625,
+ -0.92578125,
+ -0.017456055,
+ -0.25,
+ 0.73828125,
+ 0.7578125,
+ -0.3828125,
+ -0.25976562,
+ 0.049072266,
+ 0.046875,
+ -0.3515625,
+ 0.30078125,
+ -1.03125,
+ -0.48828125,
+ 0.0017929077,
+ -0.26171875,
+ 0.20214844,
+ 0.29882812,
+ 0.064941406,
+ 0.21484375,
+ -0.55078125,
+ -0.021362305,
+ 0.12988281,
+ 0.27148438,
+ 0.38867188,
+ -0.19726562,
+ -0.55078125,
+ 0.1640625,
+ 0.32226562,
+ -0.72265625,
+ 0.36132812,
+ 1.21875,
+ -0.22070312,
+ -0.32421875,
+ -0.29882812,
+ 0.0024414062,
+ 0.19921875,
+ 0.734375,
+ 0.16210938,
+ 0.17871094,
+ -0.19140625,
+ 0.38476562,
+ -0.06591797,
+ -0.47070312,
+ -0.040039062,
+ -0.33007812,
+ -0.07910156,
+ -0.2890625,
+ 0.00970459,
+ 0.12695312,
+ -0.12060547,
+ -0.18847656,
+ 1.015625,
+ -0.032958984,
+ 0.12451172,
+ -0.38476562,
+ 0.063964844,
+ 1.0859375,
+ 0.067871094,
+ -0.24511719,
+ 0.125,
+ 0.10546875,
+ -0.22460938,
+ -0.29101562,
+ 0.24414062,
+ -0.017944336,
+ -0.15625,
+ -0.60546875,
+ -0.25195312,
+ -0.46875,
+ 0.80859375,
+ -0.34960938,
+ 0.42382812,
+ 0.796875,
+ 0.296875,
+ -0.067871094,
+ 0.39453125,
+ 0.07470703,
+ 0.033935547,
+ 0.24414062,
+ 0.32617188,
+ 0.023925781,
+ 0.73046875,
+ 0.2109375,
+ -0.43164062,
+ 0.14453125,
+ 0.63671875,
+ 0.21972656,
+ -0.1875,
+ -0.18066406,
+ -0.22167969,
+ -1.3359375,
+ 0.52734375,
+ -0.40625,
+ -0.12988281,
+ 0.17480469,
+ -0.18066406,
+ 0.58984375,
+ -0.32421875,
+ -0.13476562,
+ 0.39257812,
+ -0.19238281,
+ 0.068359375,
+ 0.7265625,
+ -0.7109375,
+ -0.125,
+ 0.328125,
+ 0.34179688,
+ -0.48828125,
+ -0.10058594,
+ -0.83984375,
+ 0.30273438,
+ 0.008239746,
+ -1.390625,
+ 0.171875,
+ 0.34960938,
+ 0.44921875,
+ 0.22167969,
+ 0.60546875,
+ -0.36914062,
+ -0.028808594,
+ -0.19921875,
+ 0.6875,
+ 0.52734375,
+ -0.07421875,
+ 0.35546875,
+ 0.546875,
+ 0.08691406,
+ 0.23339844,
+ -0.984375,
+ -0.20507812,
+ 0.08544922,
+ 0.453125,
+ -0.07421875,
+ -0.953125,
+ 0.74609375,
+ -0.796875,
+ 0.47851562,
+ 0.81640625,
+ -0.44921875,
+ -0.33398438,
+ -0.54296875,
+ 0.46484375,
+ -0.390625,
+ -0.24121094,
+ -0.0115356445,
+ 1.1328125,
+ 1.0390625,
+ 0.6484375,
+ 0.35742188,
+ -0.29492188,
+ -0.0007095337,
+ -0.060302734,
+ 0.21777344,
+ 0.15136719,
+ -0.6171875,
+ 0.11328125,
+ -0.025878906,
+ 0.19238281,
+ 0.140625,
+ 0.171875,
+ 0.25195312,
+ 0.10546875,
+ 0.0008354187,
+ -0.13476562,
+ -0.26953125,
+ 0.025024414,
+ -0.28320312,
+ -0.107910156,
+ 1.015625,
+ 0.05493164,
+ -0.12988281,
+ 0.30859375,
+ 0.22558594,
+ -0.60546875,
+ 0.11328125,
+ -1.203125,
+ 0.6484375,
+ 0.087402344,
+ 0.32226562,
+ 0.63671875,
+ -0.07714844,
+ -1.390625,
+ -0.71875,
+ -0.34179688,
+ -0.10546875,
+ -0.37304688,
+ -0.09863281,
+ -0.41210938,
+ -0.14941406,
+ 0.41210938,
+ -0.20898438,
+ 0.18261719,
+ 0.67578125,
+ 0.41601562,
+ 0.32617188,
+ 0.2421875,
+ -0.14257812,
+ -0.6796875,
+ 0.01953125,
+ 0.34179688,
+ 0.20800781,
+ -0.123046875,
+ 0.087402344,
+ 0.85546875,
+ 0.33984375,
+ 0.33203125,
+ -0.68359375,
+ 0.44921875,
+ 0.50390625,
+ 0.083496094,
+ 0.10888672,
+ -0.09863281,
+ 0.55078125,
+ 0.09765625,
+ -0.50390625,
+ 0.13378906,
+ -0.29882812,
+ 0.030761719,
+ -0.64453125,
+ 0.22949219,
+ 0.43945312,
+ 0.16503906,
+ 0.10888672,
+ -0.12792969,
+ -0.039794922,
+ -0.111328125,
+ -0.35742188,
+ 0.053222656,
+ -0.78125,
+ -0.4375,
+ 0.359375,
+ -0.88671875,
+ -0.21972656,
+ -0.053710938,
+ 0.91796875,
+ -0.10644531,
+ 0.55859375,
+ -0.7734375,
+ 0.5078125,
+ 0.46484375,
+ 0.32226562,
+ 0.16796875,
+ -0.28515625,
+ 0.045410156,
+ -0.45117188,
+ 0.38867188,
+ -0.33398438,
+ -0.5234375,
+ 0.296875,
+ 0.6015625,
+ 0.3515625,
+ -0.734375,
+ 0.3984375,
+ -0.08251953,
+ 0.359375,
+ -0.28515625,
+ -0.88671875,
+ 0.0051879883,
+ 0.045166016,
+ -0.7421875,
+ -0.36523438,
+ 0.140625,
+ 0.18066406,
+ -0.171875,
+ -0.15625,
+ -0.53515625,
+ 0.2421875,
+ -0.19140625,
+ -0.18066406,
+ 0.25390625,
+ 0.6875,
+ -0.01965332,
+ -0.33203125,
+ 0.29492188,
+ 0.107421875,
+ -0.048339844,
+ -0.82421875,
+ 0.52734375,
+ 0.78125,
+ 0.8203125,
+ -0.90625,
+ 0.765625,
+ 0.0390625,
+ 0.045410156,
+ 0.26367188,
+ -0.14355469,
+ -0.26367188,
+ 0.390625,
+ -0.10888672,
+ 0.33007812,
+ -0.5625,
+ 0.08105469,
+ -0.13769531,
+ 0.8515625,
+ -0.14453125,
+ 0.77734375,
+ -0.48046875,
+ -0.3515625,
+ -0.25390625,
+ -0.09277344,
+ 0.23925781,
+ -0.022338867,
+ -0.45898438,
+ 0.36132812,
+ -0.23828125,
+ 0.265625,
+ -0.48632812,
+ -0.46875,
+ -0.75390625,
+ 1.3125,
+ 0.78125,
+ -0.63671875,
+ -1.21875,
+ 0.5078125,
+ -0.27734375,
+ -0.118652344,
+ 0.041992188,
+ -0.14648438,
+ -0.8046875,
+ 0.21679688,
+ -0.79296875,
+ 0.28320312,
+ -0.09667969,
+ 0.42773438,
+ 0.49414062,
+ 0.44726562,
+ 0.21972656,
+ -0.02746582,
+ -0.03540039,
+ -0.14941406,
+ -0.515625,
+ -0.27929688,
+ 0.9609375,
+ -0.007598877,
+ 0.34765625,
+ -0.060546875,
+ -0.44726562,
+ 0.7421875,
+ 0.15332031,
+ 0.45117188,
+ -0.4921875,
+ 0.07080078,
+ 0.5625,
+ 0.3984375,
+ -0.20019531,
+ 0.014892578,
+ 0.63671875,
+ -0.0071411133,
+ 0.016357422,
+ 1.0625,
+ 0.049316406,
+ 0.18066406,
+ 0.09814453,
+ -0.52734375,
+ -0.359375,
+ -0.072265625,
+ -0.41992188,
+ 0.39648438,
+ 0.38671875,
+ -0.30273438,
+ -0.056640625,
+ -0.640625,
+ -0.44921875,
+ 0.49414062,
+ 0.29101562,
+ 0.49609375,
+ 0.40429688,
+ -0.10205078,
+ 0.49414062,
+ -0.28125,
+ -0.12695312,
+ -0.0022735596,
+ -0.37304688,
+ 0.122558594,
+ 0.07519531,
+ -0.12597656,
+ -0.38085938,
+ -0.19824219,
+ -0.40039062,
+ 0.56640625,
+ -1.140625,
+ -0.515625,
+ -0.17578125,
+ -0.765625,
+ -0.43945312,
+ 0.3359375,
+ -0.24707031,
+ 0.32617188,
+ -0.45117188,
+ -0.37109375,
+ 0.45117188,
+ -0.27539062,
+ -0.38867188,
+ 0.09082031,
+ 0.17675781,
+ 0.49414062,
+ 0.19921875,
+ 0.17480469,
+ 0.8515625,
+ -0.23046875,
+ -0.234375,
+ -0.28515625,
+ 0.10253906,
+ 0.29101562,
+ -0.3359375,
+ -0.203125,
+ 0.6484375,
+ 0.11767578,
+ -0.20214844,
+ -0.42382812,
+ 0.26367188,
+ 0.6328125,
+ 0.0059509277,
+ 0.08691406,
+ -1.5625,
+ -0.43554688,
+ 0.17675781,
+ 0.091796875,
+ -0.5234375,
+ -0.09863281,
+ 0.20605469,
+ 0.16601562,
+ -0.578125,
+ 0.017700195,
+ 0.41015625,
+ 1.03125,
+ -0.55078125,
+ 0.21289062,
+ -0.35351562,
+ 0.24316406,
+ -0.123535156,
+ 0.11035156,
+ -0.48242188,
+ -0.34179688,
+ 0.45117188,
+ 0.3125,
+ -0.071777344,
+ 0.12792969,
+ 0.55859375,
+ 0.063964844,
+ -0.21191406,
+ 0.01965332,
+ -1.359375,
+ -0.21582031,
+ -0.019042969,
+ 0.16308594,
+ -0.3671875,
+ -0.40625,
+ -1.0234375,
+ -0.21289062,
+ 0.24023438,
+ -0.28125,
+ 0.26953125,
+ -0.14550781,
+ -0.087890625,
+ 0.16113281,
+ -0.49804688,
+ -0.17675781,
+ -0.890625,
+ 0.27929688,
+ 0.484375,
+ 0.27148438,
+ 0.11816406,
+ 0.83984375,
+ 0.029052734,
+ -0.890625,
+ 0.66796875,
+ 0.78515625,
+ -0.953125,
+ 0.49414062,
+ -0.546875,
+ 0.106933594,
+ -0.08251953,
+ 0.2890625,
+ -0.1484375,
+ -0.85546875,
+ 0.32421875,
+ -0.0040893555,
+ -0.16601562,
+ -0.16699219,
+ 0.24414062,
+ -0.5078125,
+ 0.25390625,
+ -0.10253906,
+ 0.15625,
+ 0.140625,
+ -0.27539062,
+ -0.546875,
+ -0.5546875,
+ -0.71875,
+ 0.37304688,
+ 0.060058594,
+ -0.076171875,
+ 0.44921875,
+ 0.06933594,
+ -0.28710938,
+ -0.22949219,
+ 0.17578125,
+ 0.09814453,
+ 0.4765625,
+ -0.95703125,
+ -0.03540039,
+ 0.21289062,
+ -0.7578125,
+ -0.07373047,
+ 0.10546875,
+ 0.07128906,
+ 0.76171875,
+ 0.4296875,
+ -0.09375,
+ 0.27539062,
+ -0.55078125,
+ 0.29882812,
+ -0.42382812,
+ 0.32617188,
+ -0.39648438,
+ 0.12451172,
+ 0.16503906,
+ -0.22460938,
+ -0.65625,
+ -0.022094727,
+ 0.61328125,
+ -0.024780273,
+ 0.62109375,
+ -0.033447266,
+ 0.515625,
+ 0.12890625,
+ -0.21875,
+ -0.08642578,
+ 0.49804688,
+ -0.2265625,
+ -0.29296875,
+ 0.19238281,
+ 0.3515625,
+ -1.265625,
+ 0.57421875,
+ 0.20117188,
+ -0.28320312,
+ 0.1953125,
+ -0.30664062,
+ 0.2265625,
+ -0.11230469,
+ 0.83984375,
+ 0.111328125,
+ 0.265625,
+ 0.71484375,
+ -0.625,
+ 0.38867188,
+ 0.47070312,
+ -0.32617188,
+ -0.171875,
+ 1.0078125,
+ 0.19726562,
+ -0.118652344,
+ 0.63671875,
+ -0.068359375,
+ -0.25585938,
+ 0.4140625,
+ -0.29296875,
+ 0.21386719,
+ -0.064453125,
+ 0.15820312,
+ -0.89453125,
+ -0.16308594,
+ 0.48046875,
+ 0.14648438,
+ -0.5703125,
+ 0.84765625,
+ -0.19042969,
+ 0.03515625,
+ 0.42578125,
+ -0.27539062,
+ -0.5390625,
+ 0.95703125,
+ 0.2734375,
+ 0.16699219,
+ -0.328125,
+ 0.11279297,
+ 0.003250122,
+ 0.47265625,
+ -0.31640625,
+ 0.546875,
+ 0.55859375,
+ 0.06933594,
+ -0.61328125,
+ -0.16210938,
+ -0.375,
+ 0.100097656,
+ -0.088378906,
+ 0.12695312,
+ 0.079589844,
+ 0.123535156,
+ -1.0078125,
+ 0.6875,
+ 0.022949219,
+ -0.40039062,
+ -0.09863281,
+ 0.29101562,
+ -1.2890625,
+ -0.20996094,
+ 0.36328125,
+ -0.3515625,
+ 0.7890625,
+ 0.12207031,
+ 0.48046875,
+ -0.13671875,
+ -0.041015625,
+ 0.19824219,
+ 0.19921875,
+ 0.01171875,
+ -0.37695312,
+ -0.62890625,
+ 0.9375,
+ -0.671875,
+ 0.24609375,
+ 0.6484375,
+ -0.29101562,
+ 0.076171875,
+ 0.62109375,
+ -0.5546875,
+ 0.36523438,
+ 0.75390625,
+ -0.19140625,
+ -0.875,
+ -0.8203125,
+ -0.24414062,
+ -0.625,
+ 0.1796875,
+ -0.40039062,
+ 0.25390625,
+ -0.14550781,
+ -0.21679688,
+ -0.828125,
+ 0.3359375,
+ 0.43554688,
+ 0.55078125,
+ -0.44921875,
+ -0.28710938,
+ 0.24023438,
+ 0.18066406,
+ -0.6953125,
+ 0.020385742,
+ -0.11376953,
+ 0.13867188,
+ -0.92578125,
+ 0.33398438,
+ -0.328125,
+ 0.78125,
+ -0.45507812,
+ -0.07470703,
+ 0.34179688,
+ 0.07080078,
+ 0.76171875,
+ 0.37890625,
+ -0.10644531,
+ 0.90234375,
+ -0.21875,
+ -0.15917969,
+ -0.36132812,
+ 0.2109375,
+ -0.45703125,
+ -0.76953125,
+ 0.21289062,
+ 0.26367188,
+ 0.49804688,
+ 0.35742188,
+ -0.20019531,
+ 0.31054688,
+ 0.34179688,
+ 0.17089844,
+ -0.15429688,
+ 0.39648438,
+ -0.5859375,
+ 0.20996094,
+ -0.40039062,
+ 0.5703125,
+ -0.515625,
+ 0.5234375,
+ 0.049560547,
+ 0.328125,
+ 0.24804688,
+ 0.42578125,
+ 0.609375,
+ 0.19238281,
+ 0.27929688,
+ 0.19335938,
+ 0.78125,
+ -0.9921875,
+ 0.23925781,
+ -1.3828125,
+ -0.22949219,
+ -0.578125,
+ -0.13964844,
+ -0.17382812,
+ -0.011169434,
+ 0.26171875,
+ -0.73046875,
+ -1.4375,
+ 0.6953125,
+ -0.7421875,
+ 0.052246094,
+ 0.12207031,
+ 1.3046875,
+ 0.38867188,
+ 0.040283203,
+ -0.546875,
+ -0.0021514893,
+ 0.18457031,
+ -0.5546875,
+ -0.51171875,
+ -0.16308594,
+ -0.104003906,
+ -0.38867188,
+ -0.20996094,
+ -0.8984375,
+ 0.6015625,
+ -0.30078125,
+ -0.13769531,
+ 0.16113281,
+ 0.58203125,
+ -0.23730469,
+ -0.125,
+ -1.0234375,
+ 0.875,
+ -0.7109375,
+ 0.29101562,
+ 0.09667969,
+ -0.3203125,
+ -0.48046875,
+ 0.37890625,
+ 0.734375,
+ -0.28710938,
+ -0.29882812,
+ -0.05493164,
+ 0.34765625,
+ -0.84375,
+ 0.65625,
+ 0.578125,
+ -0.20019531,
+ 0.13769531,
+ 0.10058594,
+ -0.37109375,
+ 0.36523438,
+ -0.22167969,
+ 0.72265625,
+ ],
+ "inputTextTokenCount": 6,
+ },
+ ],
+ "amazon.titan-text-express-v1::{ Malformed Request Body": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "e72d1b46-9f16-4bf0-8eee-f7778f32e5a5",
+ "x-amzn-ErrorType": "ValidationException:http://internal.amazon.com/coral/com.amazon.bedrock/",
+ },
+ 400,
+ {"message": "Malformed input request, please reformat your input and try again."},
+ ],
+ "amazon.titan-embed-g1-text-02::{ Malformed Request Body": [
+ {
+ "Content-Type": "application/json",
+ "x-amzn-RequestId": "b3646569-18c5-4173-a9fa-bbe9c648f636",
+ "x-amzn-ErrorType": "ValidationException:http://internal.amazon.com/coral/com.amazon.bedrock/",
+ },
+ 400,
+ {"message": "Malformed input request, please reformat your input and try again."},
+ ],
+}
+
+
+MODEL_PATH_RE = re.compile(r"/model/([^/]+)/invoke")
+
+
+def simple_get(self):
+ content_len = int(self.headers.get("content-length"))
+ body = self.rfile.read(content_len).decode("utf-8")
+ try:
+ content = json.loads(body)
+ except Exception:
+ content = body
+
+ stream = self.path.endswith("invoke-with-response-stream")
+ model = MODEL_PATH_RE.match(self.path).group(1)
+ prompt = extract_shortened_prompt(content, model)
+ if not prompt:
+ self.send_response(500)
+ self.end_headers()
+ self.wfile.write("Could not parse prompt.".encode("utf-8"))
+ return
+
+ headers, status_code, response = ({}, 0, "")
+ if stream:
+ for k, v in STREAMED_RESPONSES.items():
+ if prompt.startswith(k):
+ headers, status_code, response = v
+ break
+ if not response:
+ for k, v in RESPONSES.items():
+ # Only look for error responses returned immediately instead of in a stream
+ if prompt.startswith(k) and v[1] >= 400:
+ headers, status_code, response = v
+ stream = False # Response will not be streamed
+ break
+ else:
+ for k, v in RESPONSES.items():
+ if prompt.startswith(k):
+ headers, status_code, response = v
+ break
+
+ if not response:
+ # If no matches found
+ self.send_response(500)
+ self.end_headers()
+ self.wfile.write(f"Unknown Prompt:\n{prompt}".encode("utf-8"))
+ return
+
+ if stream:
+ # Send response code
+ self.send_response(status_code)
+
+ # Send headers
+ for k, v in headers.items():
+ self.send_header(k, v)
+ self.end_headers()
+
+ # Send response body
+ for resp in response:
+ self.wfile.write(bytes.fromhex(resp))
+ else:
+ # Send response code
+ self.send_response(status_code)
+
+ # Send headers
+ for k, v in headers.items():
+ self.send_header(k, v)
+ self.end_headers()
+
+ # Send response body
+ response_body = json.dumps(response).encode("utf-8")
+
+ if "Malformed Body" in prompt:
+ # Remove end of response to make invalid JSON
+ response_body = response_body[:-4]
+
+ self.wfile.write(response_body)
+ return
+
+
+def extract_shortened_prompt(content, model):
+ if isinstance(content, str):
+ prompt = content
+ elif "messages" in content:
+ prompt = content["messages"][0].get("content")
+ else:
+ prompt = content.get("inputText", "") or content.get("prompt", "") or content.get("texts", [""])[0]
+ # Sometimes there are leading whitespaces in the prompt.
+ prompt = prompt.strip()
+ prompt = f"{model}::{prompt}" # Prepend model name to prompt key to keep separate copies
+ return prompt.lstrip().split("\n")[0]
+
+
+class MockExternalBedrockServer(MockExternalHTTPServer):
+ # To use this class in a test one needs to start and stop this server
+ # before and after making requests to the test app that makes the external
+ # calls.
+
+ def __init__(self, handler=simple_get, port=None, *args, **kwargs):
+ super(MockExternalBedrockServer, self).__init__(handler=handler, port=port, *args, **kwargs)
+
+
+if __name__ == "__main__":
+ # Use this to sort dict for easier future incremental updates
+ print(f"RESPONSES = {dict(sorted(RESPONSES.items(), key=lambda i: (i[1][1], i[0])))}")
+
+ with MockExternalBedrockServer() as server:
+ print(f"MockExternalBedrockServer serving on port {str(server.port)}")
+ while True:
+ pass # Serve forever
diff --git a/tests/external_botocore/_test_bedrock_chat_completion.py b/tests/external_botocore/_test_bedrock_chat_completion.py
new file mode 100644
index 0000000000..f7c536c4e7
--- /dev/null
+++ b/tests/external_botocore/_test_bedrock_chat_completion.py
@@ -0,0 +1,1587 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+chat_completion_payload_templates = {
+ "amazon.titan-text-express-v1": '{ "inputText": "%s", "textGenerationConfig": {"temperature": %f, "maxTokenCount": %d }}',
+ "ai21.j2-mid-v1": '{"prompt": "%s", "temperature": %f, "maxTokens": %d}',
+ "anthropic.claude-instant-v1": '{"prompt": "Human: %s Assistant:", "temperature": %f, "max_tokens_to_sample": %d}',
+ "cohere.command-text-v14": '{"prompt": "%s", "temperature": %f, "max_tokens": %d}',
+ "meta.llama2-13b-chat-v1": '{"prompt": "%s", "temperature": %f, "max_gen_len": %d}',
+ "mistral.mistral-7b-instruct-v0:2": '{"prompt": "[INST] %s [/INST]", "temperature": %f, "max_tokens": %d}',
+}
+
+chat_completion_expected_events = {
+ "mistral.mistral-7b-instruct-v0:2": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "duration": None, # Response time varies each test run
+ "request.model": "mistral.mistral-7b-instruct-v0:2",
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "length",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "[INST] What is 212 degrees Fahrenheit converted to Celsius? [/INST]",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " To convert a temperature from Fahrenheit to Celsius, you can use the following formula:\n\nCelsius = (Fahrenheit - 32) \u00d7 5/9\n\nSo, to convert 212 degrees Fahrenheit to Celsius:\n\nCelsius = (212 - 32) \u00d7 5/9\nCelsius = 180.56 \u00d7 5/9\nCelsius",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "amazon.titan-text-express-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "81508a1c-33a8-4294-8743-f0c629af2f49",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "FINISH",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "81508a1c-33a8-4294-8743-f0c629af2f49",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "81508a1c-33a8-4294-8743-f0c629af2f49",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "\n1 degree Fahrenheit is 0.56 Celsius. Therefore, 212 degree Fahrenheit in Celsius would be 115.42.",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "ai21.j2-mid-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "228ee63f-4eca-4b7d-b679-bc920de63525",
+ "response_id": "1234",
+ "duration": None, # Response time varies each test run
+ "request.model": "ai21.j2-mid-v1",
+ "response.model": "ai21.j2-mid-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "endoftext",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": "1234-0",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "228ee63f-4eca-4b7d-b679-bc920de63525",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "ai21.j2-mid-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": "1234-1",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "228ee63f-4eca-4b7d-b679-bc920de63525",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "\n212 degrees Fahrenheit is equal to 100 degrees Celsius.",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "ai21.j2-mid-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "anthropic.claude-instant-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "6a886158-b39f-46ce-b214-97458ab76f2f",
+ "duration": None, # Response time varies each test run
+ "request.model": "anthropic.claude-instant-v1",
+ "response.model": "anthropic.claude-instant-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "max_tokens",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "6a886158-b39f-46ce-b214-97458ab76f2f",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Human: What is 212 degrees Fahrenheit converted to Celsius? Assistant:",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "6a886158-b39f-46ce-b214-97458ab76f2f",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " Okay, here are the steps to convert 212 degrees Fahrenheit to Celsius:\n\n1) The formula to convert between Fahrenheit and Celsius is:\n C = (F - 32) * 5/9\n\n2) Plug in 212 degrees Fahrenheit for F:\n C = (212 - 32) * 5/9\n C = 180 * 5/9\n C = 100\n\n3) Therefore, 212 degrees Fahrenheit converted to Celsius is 100 degrees Celsius.",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "cohere.command-text-v14": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "12912a17-aa13-45f3-914c-cc82166f3601",
+ "response_id": None, # UUID that varies with each run
+ "duration": None, # Response time varies each test run
+ "request.model": "cohere.command-text-v14",
+ "response.model": "cohere.command-text-v14",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "MAX_TOKENS",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "12912a17-aa13-45f3-914c-cc82166f3601",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "cohere.command-text-v14",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "12912a17-aa13-45f3-914c-cc82166f3601",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " To convert from Fahrenheit to Celsius, you can use the following formula:\n\nCelsius = (Fahrenheit - 32) * 5/9\n\nIn this case, 212 degrees Fahrenheit is converted to Celsius as follows:\n\nCelsius = (212 - 32) * 5/9 = (180) * 5/9 = (180/9) = 20 degrees Celsius\n\nTherefore, 212 degrees Fahrenheit is equivalent to 20 degrees Celsius.\n\nIt's important to note that",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "cohere.command-text-v14",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "meta.llama2-13b-chat-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "a168214d-742d-4244-bd7f-62214ffa07df",
+ "duration": None, # Response time varies each test run
+ "request.model": "meta.llama2-13b-chat-v1",
+ "response.model": "meta.llama2-13b-chat-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "stop",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "a168214d-742d-4244-bd7f-62214ffa07df",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "a168214d-742d-4244-bd7f-62214ffa07df",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "\n\n212°F = ?°C\n\nPlease help! I'm stuck!\n\nThank you!\n\nI hope this is the correct place to ask this question. Please let me know if it isn't.\n\nI appreciate your help!\n\nBest regards,\n\n[Your Name]",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+}
+chat_completion_langchain_expected_streaming_events = {
+ "mistral.mistral-7b-instruct-v0:2": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "duration": None, # Response time varies each test run
+ "request.model": "mistral.mistral-7b-instruct-v0:2",
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "length",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "[INST] What is 212 degrees Fahrenheit converted to Celsius? [/INST]",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " To convert a temperature from Fahrenheit to Celsius, you can use the following formula: Celsius = (Fahrenheit - 32) × 5/9.\n\nSo, to convert 212 degrees Fahrenheit to Celsius, do the following calculation:\n\nCelsius = (212°F - 32) × 5/9\n\nCelsius = (212 - 32)",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "amazon.titan-text-express-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "884db5c9-18ab-4f27-8892-33656176a2e6",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "response.choices.finish_reason": "FINISH",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "884db5c9-18ab-4f27-8892-33656176a2e6",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "\n\nUser: The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n\nCurrent conversation:\n\nHuman: Hi there!\nAI:\n\nBot:",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "884db5c9-18ab-4f27-8892-33656176a2e6",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " Hello, how can I help you today?",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "anthropic.claude-instant-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "1a72a1f6-310f-469c-af1d-2c59eb600089",
+ "duration": None, # Response time varies each test run
+ "request.model": "anthropic.claude-instant-v1",
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "1a72a1f6-310f-469c-af1d-2c59eb600089",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n\nCurrent conversation:\n\nHuman: Hi there!\nAI:",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "1a72a1f6-310f-469c-af1d-2c59eb600089",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Hello! My name is Claude.\n\nH: Nice to meet you Claude. Can you tell me a bit about yourself?",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "meta.llama2-13b-chat-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "cce6b34c-812c-4f97-8885-515829aa9639",
+ "duration": None, # Response time varies each test run
+ "request.model": "meta.llama2-13b-chat-v1",
+ "response.model": "meta.llama2-13b-chat-v1",
+ "response.choices.finish_reason": "stop",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "cce6b34c-812c-4f97-8885-515829aa9639",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "[INST] The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n\nCurrent conversation:\n\nHuman: Hi there!\nAI: [/INST]",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "cce6b34c-812c-4f97-8885-515829aa9639",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " Hello! It's great to talk to you! I'm an AI, and I'm here to help answer any questions you might have. What's on your mind? 🤔 Do you have a specific topic you'd like to discuss, or do you just want to chat? 💬 I'm all ears! 👂",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+}
+chat_completion_langchain_expected_events = {
+ "mistral.mistral-7b-instruct-v0:2": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "duration": None, # Response time varies each test run
+ "request.model": "mistral.mistral-7b-instruct-v0:2",
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "length",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "[INST] What is 212 degrees Fahrenheit converted to Celsius? [/INST]",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " To convert a temperature from Fahrenheit to Celsius, you can use the following formula:\n\nCelsius = (Fahrenheit - 32) \u00d7 5/9\n\nSo, to convert 212 degrees Fahrenheit to Celsius:\n\nCelsius = (212 - 32) \u00d7 5/9\nCelsius = 180.56 \u00d7 5/9\nCelsius",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "amazon.titan-text-express-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "884db5c9-18ab-4f27-8892-33656176a2e6",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "response.choices.finish_reason": "FINISH",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "884db5c9-18ab-4f27-8892-33656176a2e6",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "\n\nUser: The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n\nCurrent conversation:\n\nHuman: Hi there!\nAI:\n\nBot:",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "884db5c9-18ab-4f27-8892-33656176a2e6",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " Hello, how can I help you today?",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "anthropic.claude-instant-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "1a72a1f6-310f-469c-af1d-2c59eb600089",
+ "duration": None, # Response time varies each test run
+ "request.model": "anthropic.claude-instant-v1",
+ "response.model": "anthropic.claude-instant-v1",
+ "response.choices.finish_reason": "end_turn",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "1a72a1f6-310f-469c-af1d-2c59eb600089",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n\nCurrent conversation:\n\nHuman: Hi there!\nAI:",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "1a72a1f6-310f-469c-af1d-2c59eb600089",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "{'type': 'text', 'text': \"Hello! It's nice to meet you.\"}",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "meta.llama2-13b-chat-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "cce6b34c-812c-4f97-8885-515829aa9639",
+ "duration": None, # Response time varies each test run
+ "request.model": "meta.llama2-13b-chat-v1",
+ "response.model": "meta.llama2-13b-chat-v1",
+ "response.choices.finish_reason": "stop",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "cce6b34c-812c-4f97-8885-515829aa9639",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "[INST] The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n\nCurrent conversation:\n\nHuman: Hi there!\nAI: [/INST]",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "cce6b34c-812c-4f97-8885-515829aa9639",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " Hello! It's great to talk to you! I'm an AI, and I'm here to help answer any questions you might have. What's on your mind? 🤔 Do you have a specific topic you'd like to discuss, or is there something you'd like to know? 🤓 I'm all ears! 👂",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+}
+
+chat_completion_streaming_expected_events = {
+ "mistral.mistral-7b-instruct-v0:2": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "duration": None, # Response time varies each test run
+ "request.model": "mistral.mistral-7b-instruct-v0:2",
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "length",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "[INST] What is 212 degrees Fahrenheit converted to Celsius? [/INST]",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": " To convert a temperature from Fahrenheit to Celsius, you can use the following formula: Celsius = (Fahrenheit - 32) × 5/9.\n\nSo, to convert 212 degrees Fahrenheit to Celsius, do the following calculation:\n\nCelsius = (212°F - 32) × 5/9\n\nCelsius = (212 - 32)",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "amazon.titan-text-express-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "b427270f-371a-458d-81b6-a05aafb2704c",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "FINISH",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "b427270f-371a-458d-81b6-a05aafb2704c",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "b427270f-371a-458d-81b6-a05aafb2704c",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": "\n1 degree Fahrenheit is 0.56 degrees Celsius. Therefore, 212 degree Fahrenheit in Celsius would be 115.72.",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "anthropic.claude-instant-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "a645548f-0b3a-47ce-a675-f51e6e9037de",
+ "duration": None, # Response time varies each test run
+ "request.model": "anthropic.claude-instant-v1",
+ "response.model": "anthropic.claude-instant-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "stop_sequence",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "a645548f-0b3a-47ce-a675-f51e6e9037de",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": "Human: What is 212 degrees Fahrenheit converted to Celsius? Assistant:",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "a645548f-0b3a-47ce-a675-f51e6e9037de",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": " Here are the steps to convert 212 degrees Fahrenheit to Celsius:\n\n1) The formula to convert between Fahrenheit and Celsius is:\n C = (F - 32) * 5/9\n\n2) Plug in 212 degrees Fahrenheit for F:\n C = (212 - 32) * 5/9\n C = 180 * 5/9\n C = 100\n\n3) Therefore, 212 degrees Fahrenheit is equal to 100 degrees Celsius.",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "cohere.command-text-v14": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "4f8ab6c5-42d1-4e35-9573-30f9f41f821e",
+ "response_id": None, # UUID that varies with each run
+ "duration": None, # Response time varies each test run
+ "request.model": "cohere.command-text-v14",
+ "response.model": "cohere.command-text-v14",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "COMPLETE",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "4f8ab6c5-42d1-4e35-9573-30f9f41f821e",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "cohere.command-text-v14",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "4f8ab6c5-42d1-4e35-9573-30f9f41f821e",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": " To convert Fahrenheit to Celsius, you can use the formula:\n\nCelsius = (Fahrenheit - 32) * 5/9\n\nIn this case, if you have 212 degrees Fahrenheit, you can use this formula to calculate the equivalent temperature in Celsius:\n\nCelsius = (212 - 32) * 5/9 = 100 * 5/9 = 50\n\nTherefore, 212 degrees Fahrenheit is equivalent to 50 degrees Celsius.",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "cohere.command-text-v14",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+ "meta.llama2-13b-chat-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "6dd99878-0919-4f92-850c-48f50f923b76",
+ "duration": None, # Response time varies each test run
+ "request.model": "meta.llama2-13b-chat-v1",
+ "response.model": "meta.llama2-13b-chat-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "response.choices.finish_reason": "length",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 2,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "6dd99878-0919-4f92-850c-48f50f923b76",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": "What is 212 degrees Fahrenheit converted to Celsius?",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "request_id": "6dd99878-0919-4f92-850c-48f50f923b76",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "content": " What is the conversion formula?\n\n212 degrees Fahrenheit is equal to 100 degrees Celsius.\n\nThe conversion formula is:\n\n°C = (°F - 32) × 5/9\n\nSo, to convert 212 degrees Fahrenheit to Celsius, we can use the formula like this:\n\n°C = (212 - 32) × 5/9\n",
+ "role": "assistant",
+ "completion_id": None,
+ "sequence": 1,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "is_response": True,
+ },
+ ),
+ ],
+}
+
+chat_completion_invalid_model_error_events = [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "f4908827-3db9-4742-9103-2bbc34578b03",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "duration": None, # Response time varies each test run
+ "request.model": "does-not-exist",
+ "response.model": "does-not-exist",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "error": True,
+ },
+ ),
+]
+
+chat_completion_invalid_access_key_error_events = {
+ "mistral.mistral-7b-instruct-v0:2": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "duration": None, # Response time varies each test run
+ "request.model": "mistral.mistral-7b-instruct-v0:2",
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ "error": True,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "48c7ee13-7790-461f-959f-04b0a4cf91c8",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "[INST] Invalid Token [/INST]",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "mistral.mistral-7b-instruct-v0:2",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+ "amazon.titan-text-express-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "15b39c8b-8e85-42c9-9623-06720301bda3",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ "error": True,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "15b39c8b-8e85-42c9-9623-06720301bda3",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Invalid Token",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+ "ai21.j2-mid-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "9021791d-3797-493d-9277-e33aa6f6d544",
+ "duration": None, # Response time varies each test run
+ "request.model": "ai21.j2-mid-v1",
+ "response.model": "ai21.j2-mid-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ "error": True,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None,
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "9021791d-3797-493d-9277-e33aa6f6d544",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Invalid Token",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "ai21.j2-mid-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+ "anthropic.claude-instant-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "37396f55-b721-4bae-9461-4c369f5a080d",
+ "duration": None, # Response time varies each test run
+ "request.model": "anthropic.claude-instant-v1",
+ "response.model": "anthropic.claude-instant-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ "error": True,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "37396f55-b721-4bae-9461-4c369f5a080d",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Human: Invalid Token Assistant:",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "anthropic.claude-instant-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+ "cohere.command-text-v14": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "22476490-a0d6-42db-b5ea-32d0b8a7f751",
+ "duration": None, # Response time varies each test run
+ "request.model": "cohere.command-text-v14",
+ "response.model": "cohere.command-text-v14",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ "error": True,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "22476490-a0d6-42db-b5ea-32d0b8a7f751",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Invalid Token",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "cohere.command-text-v14",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+ "meta.llama2-13b-chat-v1": [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "22476490-a0d6-42db-b5ea-32d0b8a7f751",
+ "duration": None, # Response time varies each test run
+ "request.model": "meta.llama2-13b-chat-v1",
+ "response.model": "meta.llama2-13b-chat-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ "error": True,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "22476490-a0d6-42db-b5ea-32d0b8a7f751",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Invalid Token",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "meta.llama2-13b-chat-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+}
+
+
+chat_completion_expected_malformed_request_body_events = [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "e72d1b46-9f16-4bf0-8eee-f7778f32e5a5",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "error": True,
+ },
+ ),
+]
+
+chat_completion_expected_malformed_response_body_events = [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "81508a1c-33a8-4294-8743-f0c629af2f49",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "81508a1c-33a8-4294-8743-f0c629af2f49",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Malformed Body",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+]
+
+chat_completion_expected_malformed_response_streaming_body_events = [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "a5a8cebb-fd33-4437-8168-5667fbdfc1fb",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "a5a8cebb-fd33-4437-8168-5667fbdfc1fb",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Malformed Streaming Body",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+]
+
+chat_completion_expected_malformed_response_streaming_chunk_events = [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "request_id": "a5a8cebb-fd33-4437-8168-5667fbdfc1fb",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "request_id": "a5a8cebb-fd33-4437-8168-5667fbdfc1fb",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Malformed Streaming Chunk",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+]
+
+
+chat_completion_expected_streaming_error_events = [
+ (
+ {"type": "LlmChatCompletionSummary"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-text-express-v1",
+ "response.model": "amazon.titan-text-express-v1",
+ "request.temperature": 0.7,
+ "request.max_tokens": 100,
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "response.number_of_messages": 1,
+ "error": True,
+ },
+ ),
+ (
+ {"type": "LlmChatCompletionMessage"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "content": "Streaming Exception",
+ "role": "user",
+ "completion_id": None,
+ "sequence": 0,
+ "response.model": "amazon.titan-text-express-v1",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+]
diff --git a/tests/external_botocore/_test_bedrock_embeddings.py b/tests/external_botocore/_test_bedrock_embeddings.py
new file mode 100644
index 0000000000..66f609f7bc
--- /dev/null
+++ b/tests/external_botocore/_test_bedrock_embeddings.py
@@ -0,0 +1,200 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+embedding_payload_templates = {
+ "amazon.titan-embed-text-v1": '{ "inputText": "%s" }',
+ "amazon.titan-embed-g1-text-02": '{ "inputText": "%s" }',
+ "cohere.embed-english-v3": '{"texts": ["%s"], "input_type": "search_document"}',
+}
+
+embedding_expected_events = {
+ "amazon.titan-embed-text-v1": [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "input": "This is an embedding test.",
+ "duration": None, # Response time varies each test run
+ "response.model": "amazon.titan-embed-text-v1",
+ "request.model": "amazon.titan-embed-text-v1",
+ "request_id": "11233989-07e8-4ecb-9ba6-79601ba6d8cc",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+ "amazon.titan-embed-g1-text-02": [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "input": "This is an embedding test.",
+ "duration": None, # Response time varies each test run
+ "response.model": "amazon.titan-embed-g1-text-02",
+ "request.model": "amazon.titan-embed-g1-text-02",
+ "request_id": "b10ac895-eae3-4f07-b926-10b2866c55ed",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+ "cohere.embed-english-v3": [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "input": "['This is an embedding test.']",
+ "duration": None, # Response time varies each test run
+ "response.model": "cohere.embed-english-v3",
+ "request.model": "cohere.embed-english-v3",
+ "request_id": "11233989-07e8-4ecb-9ba6-79601ba6d8cc",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+ ],
+}
+
+embedding_invalid_access_key_error_events = {
+ "amazon.titan-embed-text-v1": [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "input": "Invalid Token",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-embed-text-v1",
+ "response.model": "amazon.titan-embed-text-v1",
+ "request_id": "aece6ad7-e2ff-443b-a953-ba7d385fd0cc",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "error": True,
+ },
+ ),
+ ],
+ "amazon.titan-embed-g1-text-02": [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "input": "Invalid Token",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-embed-g1-text-02",
+ "response.model": "amazon.titan-embed-g1-text-02",
+ "request_id": "73328313-506e-4da8-af0f-51017fa6ca3f",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "error": True,
+ },
+ ),
+ ],
+ "cohere.embed-english-v3": [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "input": "['Invalid Token']",
+ "duration": None, # Response time varies each test run
+ "request.model": "cohere.embed-english-v3",
+ "response.model": "cohere.embed-english-v3",
+ "request_id": "73328313-506e-4da8-af0f-51017fa6ca3f",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "error": True,
+ },
+ ),
+ ],
+}
+
+embedding_expected_client_errors = {
+ "amazon.titan-embed-text-v1": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ "amazon.titan-embed-g1-text-02": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ "cohere.embed-english-v3": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+}
+
+embedding_expected_malformed_request_body_events = [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-embed-g1-text-02",
+ "response.model": "amazon.titan-embed-g1-text-02",
+ "request_id": "b3646569-18c5-4173-a9fa-bbe9c648f636",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ "error": True,
+ },
+ ),
+]
+
+embedding_expected_malformed_response_body_events = [
+ (
+ {"type": "LlmEmbedding"},
+ {
+ "id": None, # UUID that varies with each run
+ "llm.conversation_id": "my-awesome-id",
+ "llm.foo": "bar",
+ "span_id": None,
+ "trace_id": "trace-id",
+ "input": "Malformed Body",
+ "duration": None, # Response time varies each test run
+ "request.model": "amazon.titan-embed-g1-text-02",
+ "response.model": "amazon.titan-embed-g1-text-02",
+ "request_id": "b10ac895-eae3-4f07-b926-10b2866c55ed",
+ "vendor": "bedrock",
+ "ingest_source": "Python",
+ },
+ ),
+]
diff --git a/tests/external_botocore/_test_file.txt b/tests/external_botocore/_test_file.txt
new file mode 100644
index 0000000000..c57eff55eb
--- /dev/null
+++ b/tests/external_botocore/_test_file.txt
@@ -0,0 +1 @@
+Hello World!
\ No newline at end of file
diff --git a/tests/external_botocore/conftest.py b/tests/external_botocore/conftest.py
index e5cf155336..08ed863818 100644
--- a/tests/external_botocore/conftest.py
+++ b/tests/external_botocore/conftest.py
@@ -12,19 +12,170 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import io
+import json
+import os
+import re
+
import pytest
+from _mock_external_bedrock_server import (
+ MockExternalBedrockServer,
+ extract_shortened_prompt,
+)
+from botocore.response import StreamingBody
+from testing_support.fixtures import ( # noqa: F401, pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+ override_application_settings,
+)
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from newrelic.common.object_wrapper import wrap_function_wrapper
+from newrelic.common.package_version_utils import (
+ get_package_version,
+ get_package_version_tuple,
+)
+from newrelic.common.signature import bind_args
+BOTOCORE_VERSION = get_package_version("botocore")
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "custom_insights_events.max_attribute_value": 4096,
+ "ai_monitoring.enabled": True,
}
-
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (external_botocore)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (external_botocore)",
+ default_settings=_default_settings,
+ linked_applications=["Python Agent Test (external_botocore)"],
+)
+
+
+# Bedrock Fixtures
+BEDROCK_AUDIT_LOG_FILE = os.path.join(os.path.realpath(os.path.dirname(__file__)), "bedrock_audit.log")
+BEDROCK_AUDIT_LOG_CONTENTS = {}
+
+
+@pytest.fixture(scope="session")
+def bedrock_server():
+ """
+ This fixture will either create a mocked backend for testing purposes, or will
+ set up an audit log file to log responses of the real Bedrock backend to a file.
+ The behavior can be controlled by setting NEW_RELIC_TESTING_RECORD_BEDROCK_RESPONSES=1 as
+ an environment variable to run using the real Bedrock backend. (Default: mocking)
+ """
+ import boto3
+
+ from newrelic.core.config import _environ_as_bool
+
+ if get_package_version_tuple("botocore") < (1, 31, 57):
+ pytest.skip(reason="Bedrock Runtime not available.")
+
+ if not _environ_as_bool("NEW_RELIC_TESTING_RECORD_BEDROCK_RESPONSES", False):
+ # Use mocked Bedrock backend and prerecorded responses
+ with MockExternalBedrockServer() as server:
+ client = boto3.client( # nosec
+ "bedrock-runtime",
+ "us-east-1",
+ endpoint_url=f"http://localhost:{server.port}",
+ aws_access_key_id="NOT-A-REAL-SECRET",
+ aws_secret_access_key="NOT-A-REAL-SECRET",
+ )
+
+ yield client
+ else:
+ # Use real Bedrock backend and record responses
+ assert (
+ os.environ["AWS_ACCESS_KEY_ID"] and os.environ["AWS_SECRET_ACCESS_KEY"]
+ ), "AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required."
+
+ # Construct real client
+ client = boto3.client(
+ "bedrock-runtime",
+ "us-east-1",
+ )
+
+ # Apply function wrappers to record data
+ wrap_function_wrapper(
+ "botocore.endpoint", "Endpoint._do_get_response", wrap_botocore_endpoint_Endpoint__do_get_response
+ )
+ wrap_function_wrapper(
+ "botocore.eventstream",
+ "EventStreamBuffer.add_data",
+ wrap_botocore_eventstream_add_data,
+ )
+ yield client # Run tests
+
+ # Write responses to audit log
+ bedrock_audit_log_contents = dict(sorted(BEDROCK_AUDIT_LOG_CONTENTS.items(), key=lambda i: (i[1][1], i[0])))
+ with open(BEDROCK_AUDIT_LOG_FILE, "w") as audit_log_fp:
+ json.dump(bedrock_audit_log_contents, fp=audit_log_fp, indent=4)
+
+
+# Intercept outgoing requests and log to file for mocking
+RECORDED_HEADERS = set(["x-amzn-requestid", "x-amzn-errortype", "content-type"])
+
+
+def wrap_botocore_endpoint_Endpoint__do_get_response(wrapped, instance, args, kwargs):
+ request = bind__do_get_response(*args, **kwargs)
+ if not request:
+ return wrapped(*args, **kwargs)
+
+ match = re.search(r"/model/([0-9a-zA-Z%.-]+)/", request.url)
+ model = match.group(1)
+
+ # Send request
+ result = wrapped(*args, **kwargs)
+
+ # Unpack response
+ success, exception = result
+ response = (success or exception)[0]
+
+ if isinstance(request.body, io.BytesIO):
+ request.body.seek(0)
+ body = request.body.read()
+ else:
+ body = request.body
+
+ try:
+ content = json.loads(body)
+ except Exception:
+ content = body.decode("utf-8")
+
+ prompt = extract_shortened_prompt(content, model)
+ headers = dict(response.headers.items())
+ headers = dict(
+ filter(
+ lambda k: k[0].lower() in RECORDED_HEADERS or k[0].startswith("x-ratelimit"),
+ headers.items(),
+ )
+ )
+ status_code = response.status_code
+
+ # Log response
+ if response.raw.chunked:
+ # Log response
+ BEDROCK_AUDIT_LOG_CONTENTS[prompt] = headers, status_code, [] # Append response data to audit log
+ else:
+ # Clean up data
+ response_content = response.content
+ data = json.loads(response_content.decode("utf-8"))
+ result[0][1]["body"] = StreamingBody(io.BytesIO(response_content), len(response_content))
+ BEDROCK_AUDIT_LOG_CONTENTS[prompt] = headers, status_code, data # Append response data to audit log
+ return result
+
+
+def bind__do_get_response(request, operation_model, context):
+ return request
+
+
+def wrap_botocore_eventstream_add_data(wrapped, instance, args, kwargs):
+ bound_args = bind_args(wrapped, args, kwargs)
+ data = bound_args["data"].hex() # convert bytes to hex for storage
+ prompt = [k for k in BEDROCK_AUDIT_LOG_CONTENTS.keys()][-1]
+ BEDROCK_AUDIT_LOG_CONTENTS[prompt][2].append(data)
+ return wrapped(*args, **kwargs)
diff --git a/tests/external_botocore/test_bedrock_chat_completion.py b/tests/external_botocore/test_bedrock_chat_completion.py
new file mode 100644
index 0000000000..be0226e55c
--- /dev/null
+++ b/tests/external_botocore/test_bedrock_chat_completion.py
@@ -0,0 +1,962 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import json
+import os
+from io import BytesIO
+
+import boto3
+import botocore.errorfactory
+import botocore.eventstream
+import botocore.exceptions
+import pytest
+from _test_bedrock_chat_completion import (
+ chat_completion_expected_events,
+ chat_completion_expected_malformed_request_body_events,
+ chat_completion_expected_malformed_response_body_events,
+ chat_completion_expected_malformed_response_streaming_body_events,
+ chat_completion_expected_malformed_response_streaming_chunk_events,
+ chat_completion_expected_streaming_error_events,
+ chat_completion_invalid_access_key_error_events,
+ chat_completion_invalid_model_error_events,
+ chat_completion_payload_templates,
+ chat_completion_streaming_expected_events,
+)
+from conftest import BOTOCORE_VERSION # pylint: disable=E0611
+from testing_support.fixtures import (
+ override_llm_token_callback_settings,
+ reset_core_stats_engine,
+ validate_attributes,
+)
+from testing_support.ml_testing_utils import ( # noqa: F401
+ add_token_count_to_events,
+ disabled_ai_monitoring_record_content_settings,
+ disabled_ai_monitoring_settings,
+ disabled_ai_monitoring_streaming_settings,
+ events_sans_content,
+ events_sans_llm_metadata,
+ events_with_context_attrs,
+ llm_token_count_callback,
+ set_trace_info,
+)
+from testing_support.validators.validate_custom_event import validate_custom_event_count
+from testing_support.validators.validate_custom_events import validate_custom_events
+from testing_support.validators.validate_error_trace_attributes import (
+ validate_error_trace_attributes,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.api.llm_custom_attributes import WithLlmCustomAttributes
+from newrelic.api.transaction import add_custom_attribute
+from newrelic.common.object_names import callable_name
+from newrelic.hooks.external_botocore import MODEL_EXTRACTORS
+
+
+@pytest.fixture(scope="session", params=[False, True], ids=["ResponseStandard", "ResponseStreaming"])
+def response_streaming(request):
+ return request.param
+
+
+@pytest.fixture(scope="session", params=[False, True], ids=["RequestStandard", "RequestStreaming"])
+def request_streaming(request):
+ return request.param
+
+
+@pytest.fixture(
+ scope="module",
+ params=[
+ "amazon.titan-text-express-v1",
+ "ai21.j2-mid-v1",
+ "anthropic.claude-instant-v1",
+ "cohere.command-text-v14",
+ "meta.llama2-13b-chat-v1",
+ "mistral.mistral-7b-instruct-v0:2",
+ ],
+)
+def model_id(request, response_streaming):
+ model = request.param
+ if response_streaming and model == "ai21.j2-mid-v1":
+ pytest.skip(reason="Streaming not supported.")
+
+ return model
+
+
+@pytest.fixture(scope="module")
+def exercise_model(bedrock_server, model_id, request_streaming, response_streaming):
+ payload_template = chat_completion_payload_templates[model_id]
+
+ def _exercise_model(prompt, temperature=0.7, max_tokens=100):
+ body = (payload_template % (prompt, temperature, max_tokens)).encode("utf-8")
+ if request_streaming:
+ body = BytesIO(body)
+
+ response = bedrock_server.invoke_model(
+ body=body,
+ modelId=model_id,
+ accept="application/json",
+ contentType="application/json",
+ )
+ response_body = json.loads(response.get("body").read())
+ assert response_body
+
+ return response_body
+
+ def _exercise_streaming_model(prompt, temperature=0.7, max_tokens=100):
+ body = (payload_template % (prompt, temperature, max_tokens)).encode("utf-8")
+ if request_streaming:
+ body = BytesIO(body)
+
+ response = bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model_id,
+ accept="application/json",
+ contentType="application/json",
+ )
+ body = response.get("body")
+ for resp in body:
+ assert resp
+
+ if response_streaming:
+ return _exercise_streaming_model
+ else:
+ return _exercise_model
+
+
+@pytest.fixture(scope="module")
+def expected_events(model_id, response_streaming):
+ if response_streaming:
+ return chat_completion_streaming_expected_events[model_id]
+ else:
+ return chat_completion_expected_events[model_id]
+
+
+@pytest.fixture(scope="module")
+def expected_metrics(response_streaming):
+ if response_streaming:
+ return [("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)]
+ else:
+ return [("Llm/completion/Bedrock/invoke_model", 1)]
+
+
+@pytest.fixture(scope="module")
+def expected_invalid_access_key_error_events(model_id):
+ return chat_completion_invalid_access_key_error_events[model_id]
+
+
+_test_bedrock_chat_completion_prompt = "What is 212 degrees Fahrenheit converted to Celsius?"
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_in_txn_with_llm_metadata(
+ set_trace_info, exercise_model, expected_events, expected_metrics
+):
+ @validate_custom_events(events_with_context_attrs(expected_events))
+ # One summary event, one user message, and one response message from the assistant
+ @validate_custom_event_count(count=3)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion_in_txn_with_llm_metadata",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @validate_attributes("agent", ["llm"])
+ @background_task(name="test_bedrock_chat_completion_in_txn_with_llm_metadata")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+ with WithLlmCustomAttributes({"context": "attr"}):
+ exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100)
+
+ _test()
+
+
+@disabled_ai_monitoring_record_content_settings
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_no_content(set_trace_info, exercise_model, expected_events, expected_metrics):
+ @validate_custom_events(events_sans_content(expected_events))
+ # One summary event, one user message, and one response message from the assistant
+ @validate_custom_event_count(count=3)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion_no_content",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @validate_attributes("agent", ["llm"])
+ @background_task(name="test_bedrock_chat_completion_no_content")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+ exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100)
+
+ _test()
+
+
+@reset_core_stats_engine()
+@override_llm_token_callback_settings(llm_token_count_callback)
+def test_bedrock_chat_completion_with_token_count(set_trace_info, exercise_model, expected_events, expected_metrics):
+ @validate_custom_events(add_token_count_to_events(expected_events))
+ # One summary event, one user message, and one response message from the assistant
+ @validate_custom_event_count(count=3)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion_with_token_count",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @validate_attributes("agent", ["llm"])
+ @background_task(name="test_bedrock_chat_completion_with_token_count")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+ exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100)
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_no_llm_metadata(set_trace_info, exercise_model, expected_events, expected_metrics):
+ @validate_custom_events(events_sans_llm_metadata(expected_events))
+ # One summary event, one user message, and one response message from the assistant
+ @validate_custom_event_count(count=3)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion_in_txn_no_llm_metadata",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion_in_txn_no_llm_metadata")
+ def _test():
+ set_trace_info()
+ exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100)
+
+ _test()
+
+
+@reset_core_stats_engine()
+@validate_custom_event_count(count=0)
+def test_bedrock_chat_completion_outside_txn(exercise_model):
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100)
+
+
+@disabled_ai_monitoring_settings
+@reset_core_stats_engine()
+@validate_custom_event_count(count=0)
+@background_task(name="test_bedrock_chat_completion_disabled_ai_monitoring_setting")
+def test_bedrock_chat_completion_disabled_ai_monitoring_settings(set_trace_info, exercise_model):
+ set_trace_info()
+ exercise_model(prompt=_test_bedrock_chat_completion_prompt, temperature=0.7, max_tokens=100)
+
+
+@reset_core_stats_engine()
+@disabled_ai_monitoring_streaming_settings
+def test_bedrock_chat_completion_streaming_disabled(
+ bedrock_server,
+):
+ """Streaming is disabled, but the rest of the AI settings are enabled. Custom events should not be collected."""
+
+ @validate_custom_event_count(count=0)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ rollup_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ model = "amazon.titan-text-express-v1"
+ body = (chat_completion_payload_templates[model] % (_test_bedrock_chat_completion_prompt, 0.7, 100)).encode(
+ "utf-8"
+ )
+
+ response = bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ list(response["body"]) # Iterate
+
+ _test()
+
+
+_client_error = botocore.exceptions.ClientError
+_client_error_name = callable_name(_client_error)
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_error_invalid_model(
+ bedrock_server, set_trace_info, response_streaming, expected_metrics
+):
+ @validate_custom_events(events_with_context_attrs(chat_completion_invalid_model_error_events))
+ @validate_error_trace_attributes(
+ "botocore.errorfactory:ValidationException",
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 400,
+ "error.message": "The provided model identifier is invalid.",
+ "error.code": "ValidationException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion_error_invalid_model",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion_error_invalid_model")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ with pytest.raises(_client_error):
+ with WithLlmCustomAttributes({"context": "attr"}):
+ if response_streaming:
+ stream = bedrock_server.invoke_model_with_response_stream(
+ body=b"{}",
+ modelId="does-not-exist",
+ accept="application/json",
+ contentType="application/json",
+ )
+ for _ in stream:
+ pass
+ else:
+ bedrock_server.invoke_model(
+ body=b"{}",
+ modelId="does-not-exist",
+ accept="application/json",
+ contentType="application/json",
+ )
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_error_incorrect_access_key(
+ monkeypatch,
+ bedrock_server,
+ exercise_model,
+ set_trace_info,
+ expected_invalid_access_key_error_events,
+ expected_metrics,
+):
+ """
+ A request is made to the server with invalid credentials. botocore will reach out to the server and receive an
+ UnrecognizedClientException as a response. Information from the request will be parsed and reported in customer
+ events. The error response can also be parsed, and will be included as attributes on the recorded exception.
+ """
+
+ @validate_custom_events(expected_invalid_access_key_error_events)
+ @validate_error_trace_attributes(
+ _client_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
+
+ with pytest.raises(_client_error):
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ exercise_model(prompt="Invalid Token", temperature=0.7, max_tokens=100)
+
+ _test()
+
+
+@reset_core_stats_engine()
+@disabled_ai_monitoring_record_content_settings
+def test_bedrock_chat_completion_error_incorrect_access_key_no_content(
+ monkeypatch,
+ bedrock_server,
+ exercise_model,
+ set_trace_info,
+ expected_invalid_access_key_error_events,
+ expected_metrics,
+):
+ """
+ Duplicate of test_bedrock_chat_completion_error_incorrect_access_key, but with content recording disabled.
+
+ See the original test for a description of the error case.
+ """
+
+ @validate_custom_events(events_sans_content(expected_invalid_access_key_error_events))
+ @validate_error_trace_attributes(
+ _client_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
+
+ with pytest.raises(_client_error):
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ exercise_model(prompt="Invalid Token", temperature=0.7, max_tokens=100)
+
+ _test()
+
+
+@reset_core_stats_engine()
+@override_llm_token_callback_settings(llm_token_count_callback)
+def test_bedrock_chat_completion_error_incorrect_access_key_with_token(
+ monkeypatch,
+ bedrock_server,
+ exercise_model,
+ set_trace_info,
+ expected_invalid_access_key_error_events,
+ expected_metrics,
+):
+ @validate_custom_events(add_token_count_to_events(expected_invalid_access_key_error_events))
+ @validate_error_trace_attributes(
+ _client_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
+
+ with pytest.raises(_client_error): # not sure where this exception actually comes from
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ exercise_model(prompt="Invalid Token", temperature=0.7, max_tokens=100)
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_error_malformed_request_body(
+ bedrock_server,
+ set_trace_info,
+ response_streaming,
+ expected_metrics,
+):
+ """
+ A request was made to the server, but the request body contains invalid JSON. The library will accept the invalid
+ payload, and still send a request. Our instrumentation will be unable to read it. As a result, no request
+ information will be recorded in custom events. This includes the initial prompt message event, which cannot be read
+ so it cannot be captured. The server will then respond with a ValidationException response immediately due to the
+ bad request. The response can still be parsed, so error information from the response will be recorded as normal.
+ """
+
+ @validate_custom_events(chat_completion_expected_malformed_request_body_events)
+ @validate_custom_event_count(count=1)
+ @validate_error_trace_attributes(
+ "botocore.errorfactory:ValidationException",
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 400,
+ "error.message": "Malformed input request, please reformat your input and try again.",
+ "error.code": "ValidationException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ model = "amazon.titan-text-express-v1"
+ body = "{ Malformed Request Body".encode("utf-8")
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ with pytest.raises(_client_error):
+ if response_streaming:
+ bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ else:
+ bedrock_server.invoke_model(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_error_malformed_response_body(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ After a non-streaming request was made to the server, the server responded with a response body that contains
+ invalid JSON. Since the JSON body is not parsed by botocore and just returned to the user as bytes, no parsing
+ exceptions will be raised. Instrumentation will attempt to parse the invalid body, and should not raise an
+ exception when it fails to do so. As a result, recorded events will not contain the streamed response data but will contain the request data.
+ """
+
+ @validate_custom_events(chat_completion_expected_malformed_response_body_events)
+ @validate_custom_event_count(count=2)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=[("Llm/completion/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/completion/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ model = "amazon.titan-text-express-v1"
+ body = (chat_completion_payload_templates[model] % ("Malformed Body", 0.7, 100)).encode("utf-8")
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ response = bedrock_server.invoke_model(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ assert response
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_error_malformed_response_streaming_body(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ A chunk in the stream returned by the server is valid, but contains a body with JSON that cannot be parsed.
+ Since the JSON body is not parsed by botocore and just returned to the user as bytes, no parsing exceptions will
+ be raised. Instrumentation will attempt to parse the invalid body, and should not raise an exception when it fails
+ to do so. The result should be all streamed response data missing from the recorded events, but request and summary
+ events are recorded as normal.
+ """
+
+ @validate_custom_events(chat_completion_expected_malformed_response_streaming_body_events)
+ @validate_custom_event_count(count=2)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ rollup_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ model = "amazon.titan-text-express-v1"
+ body = (chat_completion_payload_templates[model] % ("Malformed Streaming Body", 0.7, 100)).encode("utf-8")
+
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ response = bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+
+ chunks = list(response["body"])
+ assert chunks, "No response chunks returned"
+ for chunk in chunks:
+ with pytest.raises(json.decoder.JSONDecodeError):
+ json.loads(chunk["chunk"]["bytes"])
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_error_malformed_response_streaming_chunk(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ A chunk in the stream returned by the server is missing the prelude which causes an InvalidHeadersLength exception
+ to be raised during parsing of the chunk. Since the streamed chunk is not able to be parsed, the response
+ attribute on the raised exception is not present. This means all streamed response data will be missing from the
+ recorded events.
+ """
+
+ @validate_custom_events(chat_completion_expected_malformed_response_streaming_chunk_events)
+ @validate_custom_event_count(count=2)
+ @validate_error_trace_attributes(
+ "botocore.eventstream:ChecksumMismatch",
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "llm.conversation_id": "my-awesome-id",
+ },
+ },
+ forgone_params={
+ "agent": (),
+ "intrinsic": (),
+ "user": ("http.statusCode", "error.message", "error.code"),
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ rollup_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ model = "amazon.titan-text-express-v1"
+ body = (chat_completion_payload_templates[model] % ("Malformed Streaming Chunk", 0.7, 100)).encode("utf-8")
+ with pytest.raises(botocore.eventstream.ChecksumMismatch):
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ response = bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ response = "".join(chunk for chunk in response["body"])
+ assert response
+
+ _test()
+
+
+_event_stream_error = botocore.exceptions.EventStreamError
+_event_stream_error_name = "botocore.exceptions:EventStreamError"
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_error_streaming_exception(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ During a streaming call, the streamed chunk's headers indicate an error. These headers are not HTTP headers, but
+ headers embedded in the binary format of the response from the server. The streamed chunk's response body is not
+ required to contain any information regarding the exception, the headers are sufficient to cause botocore's
+ parser to raise an actual exception based on the error code. The response attribute on the raised exception will
+ contain the error information. This means error data will be reported for the response, but all response message
+ data will be missing from the recorded events since the server returned an error instead of message data inside
+ the streamed response.
+ """
+
+ @validate_custom_events(chat_completion_expected_streaming_error_events)
+ @validate_custom_event_count(count=2)
+ @validate_error_trace_attributes(
+ _event_stream_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "error.message": "Malformed input request, please reformat your input and try again.",
+ "error.code": "ValidationException",
+ },
+ },
+ forgone_params={
+ "agent": (),
+ "intrinsic": (),
+ "user": ("http.statusCode"),
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ rollup_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ with pytest.raises(_event_stream_error):
+ model = "amazon.titan-text-express-v1"
+ body = (chat_completion_payload_templates[model] % ("Streaming Exception", 0.7, 100)).encode("utf-8")
+
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ response = bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ list(response["body"]) # Iterate
+
+ _test()
+
+
+@reset_core_stats_engine()
+@disabled_ai_monitoring_record_content_settings
+def test_bedrock_chat_completion_error_streaming_exception_no_content(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ Duplicate of test_bedrock_chat_completion_error_streaming_exception, but with content recording disabled.
+
+ See the original test for a description of the error case.
+ """
+
+ @validate_custom_events(events_sans_content(chat_completion_expected_streaming_error_events))
+ @validate_custom_event_count(count=2)
+ @validate_error_trace_attributes(
+ _event_stream_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "error.message": "Malformed input request, please reformat your input and try again.",
+ "error.code": "ValidationException",
+ },
+ },
+ forgone_params={
+ "agent": (),
+ "intrinsic": (),
+ "user": ("http.statusCode"),
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ rollup_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ with pytest.raises(_event_stream_error):
+ model = "amazon.titan-text-express-v1"
+ body = (chat_completion_payload_templates[model] % ("Streaming Exception", 0.7, 100)).encode("utf-8")
+
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ response = bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ list(response["body"]) # Iterate
+
+ _test()
+
+
+@reset_core_stats_engine()
+@override_llm_token_callback_settings(llm_token_count_callback)
+def test_bedrock_chat_completion_error_streaming_exception_with_token_count(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ Duplicate of test_bedrock_chat_completion_error_streaming_exception, but with token callback being set.
+
+ See the original test for a description of the error case.
+ """
+
+ @validate_custom_events(add_token_count_to_events(chat_completion_expected_streaming_error_events))
+ @validate_custom_event_count(count=2)
+ @validate_error_trace_attributes(
+ _event_stream_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "error.message": "Malformed input request, please reformat your input and try again.",
+ "error.code": "ValidationException",
+ },
+ },
+ forgone_params={
+ "agent": (),
+ "intrinsic": (),
+ "user": ("http.statusCode"),
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion",
+ scoped_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ rollup_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_chat_completion")
+ def _test():
+ with pytest.raises(_event_stream_error):
+ model = "amazon.titan-text-express-v1"
+ body = (chat_completion_payload_templates[model] % ("Streaming Exception", 0.7, 100)).encode("utf-8")
+
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ response = bedrock_server.invoke_model_with_response_stream(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ list(response["body"]) # Iterate
+
+ _test()
+
+
+def test_bedrock_chat_completion_functions_marked_as_wrapped_for_sdk_compatibility(bedrock_server):
+ assert bedrock_server._nr_wrapped
+
+
+def test_chat_models_instrumented():
+ SUPPORTED_MODELS = [model for model, _, _, _ in MODEL_EXTRACTORS if "embed" not in model]
+
+ _id = os.environ.get("AWS_ACCESS_KEY_ID")
+ key = os.environ.get("AWS_SECRET_ACCESS_KEY")
+ if not _id or not key:
+ pytest.skip(reason="Credentials not available.")
+
+ client = boto3.client(
+ "bedrock",
+ "us-east-1",
+ )
+ response = client.list_foundation_models(byOutputModality="TEXT")
+ models = [model["modelId"] for model in response["modelSummaries"]]
+ not_supported = []
+ for model in models:
+ is_supported = any([model.startswith(supported_model) for supported_model in SUPPORTED_MODELS])
+ if not is_supported:
+ not_supported.append(model)
+
+ assert not not_supported, f"The following unsupported models were found: {not_supported}"
diff --git a/tests/external_botocore/test_bedrock_chat_completion_via_langchain.py b/tests/external_botocore/test_bedrock_chat_completion_via_langchain.py
new file mode 100644
index 0000000000..3bd18764fa
--- /dev/null
+++ b/tests/external_botocore/test_bedrock_chat_completion_via_langchain.py
@@ -0,0 +1,134 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+from _test_bedrock_chat_completion import (
+ chat_completion_langchain_expected_events,
+ chat_completion_langchain_expected_streaming_events,
+)
+from conftest import BOTOCORE_VERSION # pylint: disable=E0611
+from testing_support.fixtures import reset_core_stats_engine, validate_attributes
+from testing_support.ml_testing_utils import ( # noqa: F401
+ events_with_context_attrs,
+ set_trace_info,
+)
+from testing_support.validators.validate_custom_event import validate_custom_event_count
+from testing_support.validators.validate_custom_events import validate_custom_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.api.llm_custom_attributes import WithLlmCustomAttributes
+from newrelic.api.transaction import add_custom_attribute
+
+UNSUPPORTED_LANGCHAIN_MODELS = [
+ "ai21.j2-mid-v1",
+ "cohere.command-text-v14",
+]
+
+
+@pytest.fixture(
+ scope="module",
+ params=[
+ "amazon.titan-text-express-v1",
+ "ai21.j2-mid-v1",
+ "anthropic.claude-instant-v1",
+ "cohere.command-text-v14",
+ "meta.llama2-13b-chat-v1",
+ ],
+)
+def model_id(request):
+ model = request.param
+ if model in UNSUPPORTED_LANGCHAIN_MODELS:
+ pytest.skip(reason="Not supported by Langchain.")
+ return model
+
+
+@pytest.fixture(scope="session", params=[False, True], ids=["ResponseStandard", "ResponseStreaming"])
+def response_streaming(request):
+ return request.param
+
+
+@pytest.fixture(scope="module")
+def exercise_model(bedrock_server, model_id, response_streaming):
+ try:
+ # These are only available in certain botocore environments.
+ from langchain.chains import ConversationChain
+ from langchain_community.chat_models import BedrockChat
+ except ImportError:
+ pytest.skip(reason="Langchain not installed.")
+
+ def _exercise_model(prompt):
+ bedrock_llm = BedrockChat(
+ model_id=model_id,
+ client=bedrock_server,
+ streaming=response_streaming,
+ )
+ conversation = ConversationChain(llm=bedrock_llm)
+ result = conversation.predict(input=prompt)
+ if response_streaming:
+ for r in result:
+ assert r
+ else:
+ assert result
+
+ return _exercise_model
+
+
+@pytest.fixture(scope="module")
+def expected_events(model_id, response_streaming):
+ if response_streaming:
+ return chat_completion_langchain_expected_streaming_events[model_id]
+ return chat_completion_langchain_expected_events[model_id]
+
+
+@pytest.fixture(scope="module")
+def expected_metrics(response_streaming):
+ if response_streaming:
+ return [("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)]
+ return [("Llm/completion/Bedrock/invoke_model", 1)]
+
+
+@reset_core_stats_engine()
+def test_bedrock_chat_completion_in_txn_with_llm_metadata(
+ set_trace_info,
+ exercise_model,
+ expected_events,
+ expected_metrics,
+ response_streaming,
+):
+ @validate_custom_events(events_with_context_attrs(expected_events))
+ # One summary event, one user message, and one response message from the assistant
+ @validate_custom_event_count(count=6)
+ @validate_transaction_metrics(
+ name="test_bedrock_chat_completion_in_txn_with_llm_metadata",
+ scoped_metrics=expected_metrics,
+ rollup_metrics=expected_metrics,
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @validate_attributes("agent", ["llm"])
+ @background_task(name="test_bedrock_chat_completion_in_txn_with_llm_metadata")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+ with WithLlmCustomAttributes({"context": "attr"}):
+ exercise_model(prompt="Hi there!")
+
+ _test()
diff --git a/tests/external_botocore/test_bedrock_embeddings.py b/tests/external_botocore/test_bedrock_embeddings.py
new file mode 100644
index 0000000000..8ed17fa4f7
--- /dev/null
+++ b/tests/external_botocore/test_bedrock_embeddings.py
@@ -0,0 +1,485 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import json
+import os
+from io import BytesIO
+
+import boto3
+import botocore.exceptions
+import pytest
+from _test_bedrock_embeddings import (
+ embedding_expected_events,
+ embedding_expected_malformed_request_body_events,
+ embedding_expected_malformed_response_body_events,
+ embedding_invalid_access_key_error_events,
+ embedding_payload_templates,
+)
+from conftest import BOTOCORE_VERSION # pylint: disable=E0611
+from testing_support.fixtures import (
+ override_llm_token_callback_settings,
+ reset_core_stats_engine,
+ validate_attributes,
+)
+from testing_support.ml_testing_utils import ( # noqa: F401
+ add_token_count_to_events,
+ disabled_ai_monitoring_record_content_settings,
+ disabled_ai_monitoring_settings,
+ events_sans_content,
+ events_sans_llm_metadata,
+ llm_token_count_callback,
+ set_trace_info,
+)
+from testing_support.validators.validate_custom_event import validate_custom_event_count
+from testing_support.validators.validate_custom_events import validate_custom_events
+from testing_support.validators.validate_error_trace_attributes import (
+ validate_error_trace_attributes,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.api.transaction import add_custom_attribute
+from newrelic.common.object_names import callable_name
+from newrelic.hooks.external_botocore import MODEL_EXTRACTORS
+
+
+@pytest.fixture(scope="session", params=[False, True], ids=["RequestStandard", "RequestStreaming"])
+def request_streaming(request):
+ return request.param
+
+
+@pytest.fixture(
+ scope="module",
+ params=[
+ "amazon.titan-embed-text-v1",
+ "amazon.titan-embed-g1-text-02",
+ "cohere.embed-english-v3",
+ ],
+)
+def model_id(request):
+ return request.param
+
+
+@pytest.fixture(scope="module")
+def exercise_model(bedrock_server, model_id, request_streaming):
+ payload_template = embedding_payload_templates[model_id]
+
+ def _exercise_model(prompt):
+ body = (payload_template % prompt).encode("utf-8")
+ if request_streaming:
+ body = BytesIO(body)
+
+ response = bedrock_server.invoke_model(
+ body=body,
+ modelId=model_id,
+ accept="application/json",
+ contentType="application/json",
+ )
+ response_body = json.loads(response.get("body").read())
+ assert response_body
+
+ return response_body
+
+ return _exercise_model
+
+
+@pytest.fixture(scope="module")
+def expected_events(model_id):
+ return embedding_expected_events[model_id]
+
+
+@pytest.fixture(scope="module")
+def expected_invalid_access_key_error_events(model_id):
+ return embedding_invalid_access_key_error_events[model_id]
+
+
+_test_bedrock_embedding_prompt = "This is an embedding test."
+
+
+@reset_core_stats_engine()
+def test_bedrock_embedding_with_llm_metadata(set_trace_info, exercise_model, expected_events):
+ @validate_custom_events(expected_events)
+ @validate_custom_event_count(count=1)
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @validate_attributes("agent", ["llm"])
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+ exercise_model(prompt=_test_bedrock_embedding_prompt)
+
+ _test()
+
+
+@reset_core_stats_engine()
+@disabled_ai_monitoring_record_content_settings
+def test_bedrock_embedding_no_content(set_trace_info, exercise_model, model_id):
+ @validate_custom_events(events_sans_content(embedding_expected_events[model_id]))
+ @validate_custom_event_count(count=1)
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @validate_attributes("agent", ["llm"])
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+ exercise_model(prompt=_test_bedrock_embedding_prompt)
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_embedding_no_llm_metadata(set_trace_info, exercise_model, expected_events):
+ @validate_custom_events(events_sans_llm_metadata(expected_events))
+ @validate_custom_event_count(count=1)
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding_no_llm_metadata",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_embedding_no_llm_metadata")
+ def _test():
+ set_trace_info()
+ exercise_model(prompt=_test_bedrock_embedding_prompt)
+
+ _test()
+
+
+@reset_core_stats_engine()
+@override_llm_token_callback_settings(llm_token_count_callback)
+def test_bedrock_embedding_with_token_count(set_trace_info, exercise_model, expected_events):
+ @validate_custom_events(add_token_count_to_events(expected_events))
+ @validate_custom_event_count(count=1)
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @validate_attributes("agent", ["llm"])
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+
+ exercise_model(prompt="This is an embedding test.")
+
+ _test()
+
+
+@reset_core_stats_engine()
+@validate_custom_event_count(count=0)
+def test_bedrock_embedding_outside_txn(exercise_model):
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ exercise_model(prompt=_test_bedrock_embedding_prompt)
+
+
+@disabled_ai_monitoring_settings
+@reset_core_stats_engine()
+@validate_custom_event_count(count=0)
+@background_task(name="test_bedrock_embedding_disabled_ai_monitoring_setting")
+def test_bedrock_embedding_disabled_ai_monitoring_settings(set_trace_info, exercise_model):
+ set_trace_info()
+ exercise_model(prompt=_test_bedrock_embedding_prompt)
+
+
+_client_error = botocore.exceptions.ClientError
+_client_error_name = callable_name(_client_error)
+
+
+@reset_core_stats_engine()
+def test_bedrock_embedding_error_incorrect_access_key(
+ monkeypatch,
+ bedrock_server,
+ exercise_model,
+ set_trace_info,
+ expected_invalid_access_key_error_events,
+):
+ """
+ A request is made to the server with invalid credentials. botocore will reach out to the server and receive an
+ UnrecognizedClientException as a response. Information from the request will be parsed and reported in customer
+ events. The error response can also be parsed, and will be included as attributes on the recorded exception.
+ """
+
+ @validate_custom_events(expected_invalid_access_key_error_events)
+ @validate_error_trace_attributes(
+ _client_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
+
+ with pytest.raises(_client_error):
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ exercise_model(prompt="Invalid Token")
+
+ _test()
+
+
+@reset_core_stats_engine()
+@disabled_ai_monitoring_record_content_settings
+def test_bedrock_embedding_error_incorrect_access_key_no_content(
+ monkeypatch,
+ bedrock_server,
+ exercise_model,
+ set_trace_info,
+ expected_invalid_access_key_error_events,
+):
+ @validate_custom_events(events_sans_content(expected_invalid_access_key_error_events))
+ @validate_error_trace_attributes(
+ _client_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
+
+ with pytest.raises(_client_error):
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ exercise_model(prompt="Invalid Token")
+
+ _test()
+
+
+@reset_core_stats_engine()
+@override_llm_token_callback_settings(llm_token_count_callback)
+def test_bedrock_embedding_error_incorrect_access_key_with_token_count(
+ monkeypatch,
+ bedrock_server,
+ exercise_model,
+ set_trace_info,
+ expected_invalid_access_key_error_events,
+):
+ @validate_custom_events(add_token_count_to_events(expected_invalid_access_key_error_events))
+ @validate_error_trace_attributes(
+ _client_error_name,
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 403,
+ "error.message": "The security token included in the request is invalid.",
+ "error.code": "UnrecognizedClientException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
+
+ with pytest.raises(_client_error): # not sure where this exception actually comes from
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ exercise_model(prompt="Invalid Token")
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_embedding_error_malformed_request_body(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ A request was made to the server, but the request body contains invalid JSON. The library will accept the invalid
+ payload, and still send a request. Our instrumentation will be unable to read it. As a result, no request
+ information will be recorded in custom events. This includes the initial prompt message event, which cannot be read
+ so it cannot be captured. The server will then respond with a ValidationException response immediately due to the
+ bad request. The response can still be parsed, so error information from the response will be recorded as normal.
+ """
+
+ @validate_custom_events(embedding_expected_malformed_request_body_events)
+ @validate_custom_event_count(count=1)
+ @validate_error_trace_attributes(
+ "botocore.errorfactory:ValidationException",
+ exact_attrs={
+ "agent": {},
+ "intrinsic": {},
+ "user": {
+ "http.statusCode": 400,
+ "error.message": "Malformed input request, please reformat your input and try again.",
+ "error.code": "ValidationException",
+ },
+ },
+ )
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ model = "amazon.titan-embed-g1-text-02"
+ body = "{ Malformed Request Body".encode("utf-8")
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ with pytest.raises(_client_error):
+ bedrock_server.invoke_model(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+
+ _test()
+
+
+@reset_core_stats_engine()
+def test_bedrock_embedding_error_malformed_response_body(
+ bedrock_server,
+ set_trace_info,
+):
+ """
+ After a non-streaming request was made to the server, the server responded with a response body that contains
+ invalid JSON. Since the JSON body is not parsed by botocore and just returned to the user as bytes, no parsing
+ exceptions will be raised. Instrumentation will attempt to parse the invalid body, and should not raise an
+ exception when it fails to do so. As a result, recorded events will not contain the streamed response data but will contain the request data.
+ """
+
+ @validate_custom_events(embedding_expected_malformed_response_body_events)
+ @validate_custom_event_count(count=1)
+ @validate_transaction_metrics(
+ name="test_bedrock_embedding",
+ scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
+ custom_metrics=[
+ (f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1),
+ ],
+ background_task=True,
+ )
+ @background_task(name="test_bedrock_embedding")
+ def _test():
+ model = "amazon.titan-embed-g1-text-02"
+ body = (embedding_payload_templates[model] % "Malformed Body").encode("utf-8")
+ set_trace_info()
+ add_custom_attribute("llm.conversation_id", "my-awesome-id")
+ add_custom_attribute("llm.foo", "bar")
+ add_custom_attribute("non_llm_attr", "python-agent")
+
+ response = bedrock_server.invoke_model(
+ body=body,
+ modelId=model,
+ accept="application/json",
+ contentType="application/json",
+ )
+ assert response
+
+ _test()
+
+
+def test_embedding_models_instrumented():
+ SUPPORTED_MODELS = [model for model, _, _, _ in MODEL_EXTRACTORS if "embed" in model]
+
+ _id = os.environ.get("AWS_ACCESS_KEY_ID")
+ key = os.environ.get("AWS_SECRET_ACCESS_KEY")
+ if not _id or not key:
+ pytest.skip(reason="Credentials not available.")
+
+ client = boto3.client(
+ "bedrock",
+ "us-east-1",
+ )
+ response = client.list_foundation_models(byOutputModality="EMBEDDING")
+ models = [model["modelId"] for model in response["modelSummaries"]]
+ not_supported = []
+ for model in models:
+ is_supported = any([model.startswith(supported_model) for supported_model in SUPPORTED_MODELS])
+ if not is_supported:
+ not_supported.append(model)
+
+ assert not not_supported, f"The following unsupported models were found: {not_supported}"
diff --git a/tests/external_botocore/test_boto3_iam.py b/tests/external_botocore/test_boto3_iam.py
new file mode 100644
index 0000000000..ae1f5e466f
--- /dev/null
+++ b/tests/external_botocore/test_boto3_iam.py
@@ -0,0 +1,83 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import uuid
+
+import boto3
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_segment_params import (
+ validate_tt_segment_params,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
+
+MOTO_VERSION = get_package_version_tuple("moto")
+
+AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
+AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec (This is fine for testing purposes)
+
+TEST_USER = f"python-agent-test-{uuid.uuid4()}"
+
+_iam_scoped_metrics = [
+ ("External/iam.amazonaws.com/botocore/POST", 3),
+]
+
+_iam_rollup_metrics = [
+ ("External/all", 3),
+ ("External/allOther", 3),
+ ("External/iam.amazonaws.com/all", 3),
+ ("External/iam.amazonaws.com/botocore/POST", 3),
+]
+
+
+@dt_enabled
+@validate_span_events(exact_agents={"http.url": "https://iam.amazonaws.com/"}, count=3)
+@validate_span_events(expected_agents=("aws.requestId",), count=3)
+@validate_span_events(exact_agents={"aws.operation": "CreateUser"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "GetUser"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DeleteUser"}, count=1)
+@validate_tt_segment_params(present_params=("aws.requestId",))
+@validate_transaction_metrics(
+ "test_boto3_iam:test_iam",
+ scoped_metrics=_iam_scoped_metrics,
+ rollup_metrics=_iam_rollup_metrics,
+ background_task=True,
+)
+@background_task()
+@mock_aws
+def test_iam():
+ iam = boto3.client(
+ "iam",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ )
+
+ # Create user
+ resp = iam.create_user(UserName=TEST_USER)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Get the user
+ resp = iam.get_user(UserName=TEST_USER)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+ assert resp["User"]["UserName"] == TEST_USER
+
+ # Delete the user
+ resp = iam.delete_user(UserName=TEST_USER)
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
diff --git a/tests/external_boto3/test_boto3_s3.py b/tests/external_botocore/test_boto3_s3.py
similarity index 72%
rename from tests/external_boto3/test_boto3_s3.py
rename to tests/external_botocore/test_boto3_s3.py
index a7ecf034ab..e2c57b2eb2 100644
--- a/tests/external_boto3/test_boto3_s3.py
+++ b/tests/external_botocore/test_boto3_s3.py
@@ -12,68 +12,60 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
import uuid
import boto3
import botocore
-import moto
-from testing_support.fixtures import override_application_settings
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
from testing_support.validators.validate_span_events import validate_span_events
from testing_support.validators.validate_transaction_metrics import (
validate_transaction_metrics,
)
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
-MOTO_VERSION = tuple(int(v) for v in moto.__version__.split(".")[:3])
-
-# patch earlier versions of moto to support py37
-if sys.version_info >= (3, 7) and MOTO_VERSION <= (1, 3, 1):
- import re
-
- moto.packages.responses.responses.re._pattern_type = re.Pattern
+MOTO_VERSION = get_package_version_tuple("moto")
+BOTOCORE_VERSION = get_package_version_tuple("botocore")
AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec
AWS_REGION_NAME = "us-west-2"
-TEST_BUCKET = "python-agent-test-%s" % uuid.uuid4()
-
-BOTOCORE_VERSION = tuple(map(int, botocore.__version__.split(".")))
-
+TEST_BUCKET = f"python-agent-test-{uuid.uuid4()}"
if BOTOCORE_VERSION < (1, 7, 41):
S3_URL = "s3-us-west-2.amazonaws.com"
- EXPECTED_BUCKET_URL = "https://%s/%s" % (S3_URL, TEST_BUCKET)
- EXPECTED_KEY_URL = EXPECTED_BUCKET_URL + "/hello_world"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/{TEST_BUCKET}"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}/hello_world"
elif BOTOCORE_VERSION < (1, 28):
S3_URL = "s3.us-west-2.amazonaws.com"
- EXPECTED_BUCKET_URL = "https://%s/%s" % (S3_URL, TEST_BUCKET)
- EXPECTED_KEY_URL = EXPECTED_BUCKET_URL + "/hello_world"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/{TEST_BUCKET}"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}/hello_world"
else:
- S3_URL = "%s.s3.us-west-2.amazonaws.com" % TEST_BUCKET
- EXPECTED_BUCKET_URL = "https://%s/" % S3_URL
- EXPECTED_KEY_URL = EXPECTED_BUCKET_URL + "hello_world"
+ S3_URL = f"{TEST_BUCKET}.s3.us-west-2.amazonaws.com"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}hello_world"
_s3_scoped_metrics = [
- ("External/%s/botocore/GET" % S3_URL, 2),
- ("External/%s/botocore/PUT" % S3_URL, 2),
- ("External/%s/botocore/DELETE" % S3_URL, 2),
+ (f"External/{S3_URL}/botocore/GET", 2),
+ (f"External/{S3_URL}/botocore/PUT", 2),
+ (f"External/{S3_URL}/botocore/DELETE", 2),
]
_s3_rollup_metrics = [
("External/all", 6),
("External/allOther", 6),
- ("External/%s/all" % S3_URL, 6),
- ("External/%s/botocore/GET" % S3_URL, 2),
- ("External/%s/botocore/PUT" % S3_URL, 2),
- ("External/%s/botocore/DELETE" % S3_URL, 2),
+ (f"External/{S3_URL}/all", 6),
+ (f"External/{S3_URL}/botocore/GET", 2),
+ (f"External/{S3_URL}/botocore/PUT", 2),
+ (f"External/{S3_URL}/botocore/DELETE", 2),
]
-@override_application_settings({"distributed_tracing.enabled": True})
+@dt_enabled
@validate_span_events(exact_agents={"aws.operation": "CreateBucket"}, count=1)
@validate_span_events(exact_agents={"aws.operation": "PutObject"}, count=1)
@validate_span_events(exact_agents={"aws.operation": "ListObjects"}, count=1)
@@ -86,7 +78,7 @@
"test_boto3_s3:test_s3", scoped_metrics=_s3_scoped_metrics, rollup_metrics=_s3_rollup_metrics, background_task=True
)
@background_task()
-@moto.mock_s3
+@mock_aws
def test_s3():
client = boto3.client(
"s3",
diff --git a/tests/external_botocore/test_boto3_sns.py b/tests/external_botocore/test_boto3_sns.py
new file mode 100644
index 0000000000..141d675670
--- /dev/null
+++ b/tests/external_botocore/test_boto3_sns.py
@@ -0,0 +1,95 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import boto3
+import pytest
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_segment_params import (
+ validate_tt_segment_params,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
+
+MOTO_VERSION = get_package_version_tuple("moto")
+AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
+AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec (This is fine for testing purposes)
+AWS_REGION_NAME = "us-east-1"
+SNS_URL = "sns-us-east-1.amazonaws.com"
+TOPIC = "arn:aws:sns:us-east-1:123456789012:some-topic"
+sns_metrics = [(f"MessageBroker/SNS/Topic/Produce/Named/{TOPIC}", 1)]
+sns_metrics_phone = [("MessageBroker/SNS/Topic" "/Produce/Named/PhoneNumber", 1)]
+
+
+@dt_enabled
+@validate_span_events(expected_agents=("aws.requestId",), count=2)
+@validate_span_events(exact_agents={"aws.operation": "CreateTopic"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Publish"}, count=1)
+@validate_tt_segment_params(present_params=("aws.requestId",))
+@pytest.mark.parametrize("topic_argument", ("TopicArn", "TargetArn"))
+@validate_transaction_metrics(
+ "test_boto3_sns:test_publish_to_sns_topic",
+ scoped_metrics=sns_metrics,
+ rollup_metrics=sns_metrics,
+ background_task=True,
+)
+@background_task()
+@mock_aws
+def test_publish_to_sns_topic(topic_argument):
+ conn = boto3.client(
+ "sns",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ region_name=AWS_REGION_NAME,
+ )
+
+ topic_arn = conn.create_topic(Name="some-topic")["TopicArn"]
+
+ kwargs = {topic_argument: topic_arn}
+ published_message = conn.publish(Message="my msg", **kwargs)
+ assert "MessageId" in published_message
+
+
+@dt_enabled
+@validate_span_events(expected_agents=("aws.requestId",), count=3)
+@validate_span_events(exact_agents={"aws.operation": "CreateTopic"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Subscribe"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Publish"}, count=1)
+@validate_tt_segment_params(present_params=("aws.requestId",))
+@validate_transaction_metrics(
+ "test_boto3_sns:test_publish_to_sns_phone",
+ scoped_metrics=sns_metrics_phone,
+ rollup_metrics=sns_metrics_phone,
+ background_task=True,
+)
+@background_task()
+@mock_aws
+def test_publish_to_sns_phone():
+ conn = boto3.client(
+ "sns",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ region_name=AWS_REGION_NAME,
+ )
+
+ topic_arn = conn.create_topic(Name="some-topic")["TopicArn"]
+ conn.subscribe(TopicArn=topic_arn, Protocol="sms", Endpoint="5555555555")
+
+ published_message = conn.publish(PhoneNumber="5555555555", Message="my msg")
+ assert "MessageId" in published_message
diff --git a/tests/external_botocore/test_botocore_dynamodb.py b/tests/external_botocore/test_botocore_dynamodb.py
index 44862d827d..c031f543f6 100644
--- a/tests/external_botocore/test_botocore_dynamodb.py
+++ b/tests/external_botocore/test_botocore_dynamodb.py
@@ -12,96 +12,93 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
import uuid
import botocore.session
-import moto
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_segment_params import (
+ validate_tt_segment_params,
+)
from newrelic.api.background_task import background_task
-from testing_support.fixtures import (
- validate_tt_segment_params, override_application_settings)
-from testing_support.validators.validate_span_events import (
- validate_span_events)
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from newrelic.common.package_version_utils import get_package_version_tuple
-MOTO_VERSION = tuple(int(v) for v in moto.__version__.split('.')[:3])
+MOTO_VERSION = get_package_version_tuple("moto")
+AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
+AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec (This is fine for testing purposes)
+AWS_REGION = "us-east-1"
-# patch earlier versions of moto to support py37
-if sys.version_info >= (3, 7) and MOTO_VERSION <= (1, 3, 1):
- import re
- moto.packages.responses.responses.re._pattern_type = re.Pattern
-
-
-AWS_ACCESS_KEY_ID = 'AAAAAAAAAAAACCESSKEY'
-AWS_SECRET_ACCESS_KEY = 'AAAAAASECRETKEY'
-AWS_REGION = 'us-east-1'
-
-TEST_TABLE = 'python-agent-test-%s' % uuid.uuid4()
+TEST_TABLE = f"python-agent-test-{uuid.uuid4()}"
_dynamodb_scoped_metrics = [
- ('Datastore/statement/DynamoDB/%s/create_table' % TEST_TABLE, 1),
- ('Datastore/statement/DynamoDB/%s/put_item' % TEST_TABLE, 1),
- ('Datastore/statement/DynamoDB/%s/get_item' % TEST_TABLE, 1),
- ('Datastore/statement/DynamoDB/%s/update_item' % TEST_TABLE, 1),
- ('Datastore/statement/DynamoDB/%s/query' % TEST_TABLE, 1),
- ('Datastore/statement/DynamoDB/%s/scan' % TEST_TABLE, 1),
- ('Datastore/statement/DynamoDB/%s/delete_item' % TEST_TABLE, 1),
- ('Datastore/statement/DynamoDB/%s/delete_table' % TEST_TABLE, 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/create_table", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/put_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/get_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/update_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/query", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/scan", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/delete_item", 1),
+ (f"Datastore/statement/DynamoDB/{TEST_TABLE}/delete_table", 1),
]
_dynamodb_rollup_metrics = [
- ('Datastore/all', 8),
- ('Datastore/allOther', 8),
- ('Datastore/DynamoDB/all', 8),
- ('Datastore/DynamoDB/allOther', 8),
+ ("Datastore/all", 8),
+ ("Datastore/allOther", 8),
+ ("Datastore/DynamoDB/all", 8),
+ ("Datastore/DynamoDB/allOther", 8),
]
-@override_application_settings({'distributed_tracing.enabled': True})
-@validate_span_events(expected_agents=('aws.requestId',), count=8)
-@validate_span_events(exact_agents={'aws.operation': 'PutItem'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'GetItem'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'DeleteItem'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'CreateTable'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'DeleteTable'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'Query'}, count=1)
-@validate_span_events(exact_agents={'aws.operation': 'Scan'}, count=1)
-@validate_tt_segment_params(present_params=('aws.requestId',))
+@dt_enabled
+@validate_span_events(expected_agents=("aws.requestId",), count=8)
+@validate_span_events(exact_agents={"aws.operation": "PutItem"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "GetItem"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DeleteItem"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "CreateTable"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DeleteTable"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Query"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "Scan"}, count=1)
+@validate_tt_segment_params(present_params=("aws.requestId",))
@validate_transaction_metrics(
- 'test_botocore_dynamodb:test_dynamodb',
- scoped_metrics=_dynamodb_scoped_metrics,
- rollup_metrics=_dynamodb_rollup_metrics,
- background_task=True)
+ "test_botocore_dynamodb:test_dynamodb",
+ scoped_metrics=_dynamodb_scoped_metrics,
+ rollup_metrics=_dynamodb_rollup_metrics,
+ background_task=True,
+)
@background_task()
-@moto.mock_dynamodb2
+@mock_aws
def test_dynamodb():
session = botocore.session.get_session()
client = session.create_client(
- 'dynamodb',
- region_name=AWS_REGION,
- aws_access_key_id=AWS_ACCESS_KEY_ID,
- aws_secret_access_key=AWS_SECRET_ACCESS_KEY
+ "dynamodb",
+ region_name=AWS_REGION,
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
)
# Create table
resp = client.create_table(
- TableName=TEST_TABLE,
- AttributeDefinitions=[
- {'AttributeName': 'Id', 'AttributeType': 'N'},
- {'AttributeName': 'Foo', 'AttributeType': 'S'},
- ],
- KeySchema=[
- {'AttributeName': 'Id', 'KeyType': 'HASH'},
- {'AttributeName': 'Foo', 'KeyType': 'RANGE'},
- ],
- ProvisionedThroughput={
- 'ReadCapacityUnits': 5,
- 'WriteCapacityUnits': 5,
- },
+ TableName=TEST_TABLE,
+ AttributeDefinitions=[
+ {"AttributeName": "Id", "AttributeType": "N"},
+ {"AttributeName": "Foo", "AttributeType": "S"},
+ ],
+ KeySchema=[
+ {"AttributeName": "Id", "KeyType": "HASH"},
+ {"AttributeName": "Foo", "KeyType": "RANGE"},
+ ],
+ ProvisionedThroughput={
+ "ReadCapacityUnits": 5,
+ "WriteCapacityUnits": 5,
+ },
)
- assert resp['TableDescription']['TableName'] == TEST_TABLE
+ assert resp["TableDescription"]["TableName"] == TEST_TABLE
# moto response is ACTIVE, AWS response is CREATING
# assert resp['TableDescription']['TableStatus'] == 'ACTIVE'
@@ -111,73 +108,68 @@ def test_dynamodb():
# Put item
resp = client.put_item(
- TableName=TEST_TABLE,
- Item={
- 'Id': {'N': '101'},
- 'Foo': {'S': 'hello_world'},
- 'SomeValue': {'S': 'some_random_attribute'},
- }
+ TableName=TEST_TABLE,
+ Item={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ "SomeValue": {"S": "some_random_attribute"},
+ },
)
# No checking response, due to inconsistent return values.
# moto returns resp['Attributes']. AWS returns resp['ResponseMetadata']
# Get item
resp = client.get_item(
- TableName=TEST_TABLE,
- Key={
- 'Id': {'N': '101'},
- 'Foo': {'S': 'hello_world'},
- 'SomeValue': {'S': 'some_random_attribute'},
- }
+ TableName=TEST_TABLE,
+ Key={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ },
)
- assert resp['Item']['SomeValue']['S'] == 'some_random_attribute'
+ assert resp["Item"]["SomeValue"]["S"] == "some_random_attribute"
# Update item
resp = client.update_item(
- TableName=TEST_TABLE,
- Key={
- 'Id': {'N': '101'},
- 'Foo': {'S': 'hello_world'},
- 'SomeValue': {'S': 'some_random_attribute'},
- },
- AttributeUpdates={
- 'Foo2': {
- 'Value': {'S': 'hello_world2'},
- 'Action': 'PUT'
- },
- },
- ReturnValues='ALL_NEW',
+ TableName=TEST_TABLE,
+ Key={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ },
+ AttributeUpdates={
+ "Foo2": {"Value": {"S": "hello_world2"}, "Action": "PUT"},
+ },
+ ReturnValues="ALL_NEW",
)
- assert resp['Attributes']['Foo2']
+ assert resp["Attributes"]["Foo2"]
# Query for item
resp = client.query(
- TableName=TEST_TABLE,
- Select='ALL_ATTRIBUTES',
- KeyConditionExpression='#Id = :v_id',
- ExpressionAttributeNames={'#Id': 'Id'},
- ExpressionAttributeValues={':v_id': {'N': '101'}},
+ TableName=TEST_TABLE,
+ Select="ALL_ATTRIBUTES",
+ KeyConditionExpression="#Id = :v_id",
+ ExpressionAttributeNames={"#Id": "Id"},
+ ExpressionAttributeValues={":v_id": {"N": "101"}},
)
- assert len(resp['Items']) == 1
- assert resp['Items'][0]['SomeValue']['S'] == 'some_random_attribute'
+ assert len(resp["Items"]) == 1
+ assert resp["Items"][0]["SomeValue"]["S"] == "some_random_attribute"
# Scan
resp = client.scan(TableName=TEST_TABLE)
- assert len(resp['Items']) == 1
+ assert len(resp["Items"]) == 1
# Delete item
resp = client.delete_item(
- TableName=TEST_TABLE,
- Key={
- 'Id': {'N': '101'},
- 'Foo': {'S': 'hello_world'},
- },
+ TableName=TEST_TABLE,
+ Key={
+ "Id": {"N": "101"},
+ "Foo": {"S": "hello_world"},
+ },
)
# No checking response, due to inconsistent return values.
# moto returns resp['Attributes']. AWS returns resp['ResponseMetadata']
# Delete table
resp = client.delete_table(TableName=TEST_TABLE)
- assert resp['TableDescription']['TableName'] == TEST_TABLE
+ assert resp["TableDescription"]["TableName"] == TEST_TABLE
# moto response is ACTIVE, AWS response is DELETING
# assert resp['TableDescription']['TableStatus'] == 'DELETING'
diff --git a/tests/external_botocore/test_botocore_ec2.py b/tests/external_botocore/test_botocore_ec2.py
index 0cfd09b6fd..4154f61a26 100644
--- a/tests/external_botocore/test_botocore_ec2.py
+++ b/tests/external_botocore/test_botocore_ec2.py
@@ -12,86 +12,79 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
import uuid
import botocore.session
-import moto
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_segment_params import (
+ validate_tt_segment_params,
+)
from newrelic.api.background_task import background_task
-from testing_support.fixtures import (
- validate_tt_segment_params, override_application_settings)
-from testing_support.validators.validate_span_events import (
- validate_span_events)
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from newrelic.common.package_version_utils import get_package_version_tuple
-MOTO_VERSION = tuple(int(v) for v in moto.__version__.split('.')[:3])
+MOTO_VERSION = get_package_version_tuple("moto")
+AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
+AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec (This is fine for testing purposes)
+AWS_REGION = "us-east-1"
+UBUNTU_14_04_PARAVIRTUAL_AMI = "ami-c65be9ae"
-# patch earlier versions of moto to support py37
-if sys.version_info >= (3, 7) and MOTO_VERSION <= (1, 3, 1):
- import re
- moto.packages.responses.responses.re._pattern_type = re.Pattern
-
-AWS_ACCESS_KEY_ID = 'AAAAAAAAAAAACCESSKEY'
-AWS_SECRET_ACCESS_KEY = 'AAAAAASECRETKEY'
-AWS_REGION = 'us-east-1'
-UBUNTU_14_04_PARAVIRTUAL_AMI = 'ami-c65be9ae'
-
-TEST_INSTANCE = 'python-agent-test-%s' % uuid.uuid4()
+TEST_INSTANCE = f"python-agent-test-{uuid.uuid4()}"
_ec2_scoped_metrics = [
- ('External/ec2.us-east-1.amazonaws.com/botocore/POST', 3),
+ ("External/ec2.us-east-1.amazonaws.com/botocore/POST", 3),
]
_ec2_rollup_metrics = [
- ('External/all', 3),
- ('External/allOther', 3),
- ('External/ec2.us-east-1.amazonaws.com/all', 3),
- ('External/ec2.us-east-1.amazonaws.com/botocore/POST', 3),
+ ("External/all", 3),
+ ("External/allOther", 3),
+ ("External/ec2.us-east-1.amazonaws.com/all", 3),
+ ("External/ec2.us-east-1.amazonaws.com/botocore/POST", 3),
]
-@override_application_settings({'distributed_tracing.enabled': True})
-@validate_span_events(expected_agents=('aws.requestId',), count=3)
-@validate_span_events(exact_agents={'aws.operation': 'RunInstances'}, count=1)
-@validate_span_events(
- exact_agents={'aws.operation': 'DescribeInstances'}, count=1)
-@validate_span_events(
- exact_agents={'aws.operation': 'TerminateInstances'}, count=1)
-@validate_tt_segment_params(present_params=('aws.requestId',))
+@dt_enabled
+@validate_span_events(expected_agents=("aws.requestId",), count=3)
+@validate_span_events(exact_agents={"aws.operation": "RunInstances"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "DescribeInstances"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "TerminateInstances"}, count=1)
+@validate_tt_segment_params(present_params=("aws.requestId",))
@validate_transaction_metrics(
- 'test_botocore_ec2:test_ec2',
- scoped_metrics=_ec2_scoped_metrics,
- rollup_metrics=_ec2_rollup_metrics,
- background_task=True)
+ "test_botocore_ec2:test_ec2",
+ scoped_metrics=_ec2_scoped_metrics,
+ rollup_metrics=_ec2_rollup_metrics,
+ background_task=True,
+)
@background_task()
-@moto.mock_ec2
+@mock_aws
def test_ec2():
session = botocore.session.get_session()
client = session.create_client(
- 'ec2',
- region_name=AWS_REGION,
- aws_access_key_id=AWS_ACCESS_KEY_ID,
- aws_secret_access_key=AWS_SECRET_ACCESS_KEY
+ "ec2", region_name=AWS_REGION, aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY
)
# Create instance
resp = client.run_instances(
- ImageId=UBUNTU_14_04_PARAVIRTUAL_AMI,
- InstanceType='m1.small',
- MinCount=1,
- MaxCount=1,
+ ImageId=UBUNTU_14_04_PARAVIRTUAL_AMI,
+ InstanceType="m1.small",
+ MinCount=1,
+ MaxCount=1,
)
- assert resp['ResponseMetadata']['HTTPStatusCode'] == 200
- assert len(resp['Instances']) == 1
- instance_id = resp['Instances'][0]['InstanceId']
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+ assert len(resp["Instances"]) == 1
+ instance_id = resp["Instances"][0]["InstanceId"]
# Describe instance
resp = client.describe_instances(InstanceIds=[instance_id])
- assert resp['ResponseMetadata']['HTTPStatusCode'] == 200
- assert resp['Reservations'][0]['Instances'][0]['InstanceId'] == instance_id
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+ assert resp["Reservations"][0]["Instances"][0]["InstanceId"] == instance_id
# Delete instance
resp = client.terminate_instances(InstanceIds=[instance_id])
- assert resp['ResponseMetadata']['HTTPStatusCode'] == 200
- assert resp['TerminatingInstances'][0]['InstanceId'] == instance_id
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+ assert resp["TerminatingInstances"][0]["InstanceId"] == instance_id
diff --git a/tests/external_botocore/test_botocore_s3.py b/tests/external_botocore/test_botocore_s3.py
index 1984d8103e..2805c343bd 100644
--- a/tests/external_botocore/test_botocore_s3.py
+++ b/tests/external_botocore/test_botocore_s3.py
@@ -12,62 +12,55 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
import uuid
import botocore
import botocore.session
-import moto
-from testing_support.fixtures import override_application_settings
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
from testing_support.validators.validate_span_events import validate_span_events
from testing_support.validators.validate_transaction_metrics import (
validate_transaction_metrics,
)
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
-MOTO_VERSION = tuple(int(v) for v in moto.__version__.split(".")[:3])
-BOTOCORE_VERSION = tuple(int(v) for v in botocore.__version__.split(".")[:3])
-
-
-# patch earlier versions of moto to support py37
-if sys.version_info >= (3, 7) and MOTO_VERSION <= (1, 3, 1):
- import re
-
- moto.packages.responses.responses.re._pattern_type = re.Pattern
+MOTO_VERSION = MOTO_VERSION = get_package_version_tuple("moto")
+BOTOCORE_VERSION = get_package_version_tuple("botocore")
AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec
AWS_REGION = "us-east-1"
-TEST_BUCKET = "python-agent-test-%s" % uuid.uuid4()
+TEST_BUCKET = f"python-agent-test-{uuid.uuid4()}"
if BOTOCORE_VERSION >= (1, 28):
- S3_URL = "%s.s3.amazonaws.com" % TEST_BUCKET
- EXPECTED_BUCKET_URL = "https://%s/" % S3_URL
- EXPECTED_KEY_URL = EXPECTED_BUCKET_URL + "hello_world"
+ S3_URL = f"{TEST_BUCKET}.s3.amazonaws.com"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}hello_world"
else:
S3_URL = "s3.amazonaws.com"
- EXPECTED_BUCKET_URL = "https://%s/%s" % (S3_URL, TEST_BUCKET)
- EXPECTED_KEY_URL = EXPECTED_BUCKET_URL + "/hello_world"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/{TEST_BUCKET}"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}/hello_world"
_s3_scoped_metrics = [
- ("External/%s/botocore/GET" % S3_URL, 2),
- ("External/%s/botocore/PUT" % S3_URL, 2),
- ("External/%s/botocore/DELETE" % S3_URL, 2),
+ (f"External/{S3_URL}/botocore/GET", 2),
+ (f"External/{S3_URL}/botocore/PUT", 2),
+ (f"External/{S3_URL}/botocore/DELETE", 2),
]
_s3_rollup_metrics = [
("External/all", 6),
("External/allOther", 6),
- ("External/%s/all" % S3_URL, 6),
- ("External/%s/botocore/GET" % S3_URL, 2),
- ("External/%s/botocore/PUT" % S3_URL, 2),
- ("External/%s/botocore/DELETE" % S3_URL, 2),
+ (f"External/{S3_URL}/all", 6),
+ (f"External/{S3_URL}/botocore/GET", 2),
+ (f"External/{S3_URL}/botocore/PUT", 2),
+ (f"External/{S3_URL}/botocore/DELETE", 2),
]
-@override_application_settings({"distributed_tracing.enabled": True})
+@dt_enabled
@validate_span_events(exact_agents={"aws.operation": "CreateBucket"}, count=1)
@validate_span_events(exact_agents={"aws.operation": "PutObject"}, count=1)
@validate_span_events(exact_agents={"aws.operation": "ListObjects"}, count=1)
@@ -83,7 +76,7 @@
background_task=True,
)
@background_task()
-@moto.mock_s3
+@mock_aws
def test_s3():
session = botocore.session.get_session()
client = session.create_client(
diff --git a/tests/external_botocore/test_botocore_sqs.py b/tests/external_botocore/test_botocore_sqs.py
index 3f7d8c0220..b3a6e17578 100644
--- a/tests/external_botocore/test_botocore_sqs.py
+++ b/tests/external_botocore/test_botocore_sqs.py
@@ -12,53 +12,89 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
import uuid
import botocore.session
-import moto
import pytest
-from testing_support.fixtures import override_application_settings
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
from testing_support.validators.validate_span_events import validate_span_events
from testing_support.validators.validate_transaction_metrics import (
validate_transaction_metrics,
)
from newrelic.api.background_task import background_task
-from newrelic.common.package_version_utils import get_package_version
+from newrelic.common.package_version_utils import get_package_version_tuple
-MOTO_VERSION = tuple(int(v) for v in moto.__version__.split(".")[:3])
-
-# patch earlier versions of moto to support py37
-if sys.version_info >= (3, 7) and MOTO_VERSION <= (1, 3, 1):
- import re
-
- moto.packages.responses.responses.re._pattern_type = re.Pattern
+MOTO_VERSION = get_package_version_tuple("moto")
+BOTOCORE_VERSION = get_package_version_tuple("botocore")
url = "sqs.us-east-1.amazonaws.com"
-botocore_version = tuple([int(n) for n in get_package_version("botocore").split(".")])
-if botocore_version < (1, 29, 0):
+EXPECTED_SEND_MESSAGE_AGENT_ATTRS = {
+ "expected_agents": ["messaging.destination.name"],
+ "exact_agents": {
+ "aws.operation": "SendMessage",
+ "cloud.account.id": "123456789012",
+ "cloud.region": "us-east-1",
+ "messaging.system": "aws_sqs",
+ },
+}
+EXPECTED_RECIEVE_MESSAGE_AGENT_ATTRS = {
+ "expected_agents": ["messaging.destination.name"],
+ "exact_agents": {
+ "aws.operation": "ReceiveMessage",
+ "cloud.account.id": "123456789012",
+ "cloud.region": "us-east-1",
+ "messaging.system": "aws_sqs",
+ },
+}
+EXPECTED_SEND_MESSAGE_BATCH_AGENT_ATTRS = required = {
+ "expected_agents": ["messaging.destination.name"],
+ "exact_agents": {
+ "aws.operation": "SendMessageBatch",
+ "cloud.account.id": "123456789012",
+ "cloud.region": "us-east-1",
+ "messaging.system": "aws_sqs",
+ },
+}
+if BOTOCORE_VERSION < (1, 29, 0):
url = "queue.amazonaws.com"
+ # The old style url does not contain the necessary AWS info.
+ EXPECTED_SEND_MESSAGE_AGENT_ATTRS = {
+ "exact_agents": {
+ "aws.operation": "SendMessage",
+ },
+ }
+ EXPECTED_RECIEVE_MESSAGE_AGENT_ATTRS = {
+ "exact_agents": {
+ "aws.operation": "ReceiveMessage",
+ },
+ }
+ EXPECTED_SEND_MESSAGE_BATCH_AGENT_ATTRS = {
+ "exact_agents": {
+ "aws.operation": "SendMessageBatch",
+ },
+ }
AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec
AWS_REGION = "us-east-1"
-TEST_QUEUE = "python-agent-test-%s" % uuid.uuid4()
+TEST_QUEUE = f"python-agent-test-{uuid.uuid4()}"
_sqs_scoped_metrics = [
- ("MessageBroker/SQS/Queue/Produce/Named/%s" % TEST_QUEUE, 2),
- ("External/%s/botocore/POST" % url, 3),
+ (f"MessageBroker/SQS/Queue/Produce/Named/{TEST_QUEUE}", 2),
+ (f"External/{url}/botocore/POST", 3),
]
_sqs_rollup_metrics = [
- ("MessageBroker/SQS/Queue/Produce/Named/%s" % TEST_QUEUE, 2),
- ("MessageBroker/SQS/Queue/Consume/Named/%s" % TEST_QUEUE, 1),
+ (f"MessageBroker/SQS/Queue/Produce/Named/{TEST_QUEUE}", 2),
+ (f"MessageBroker/SQS/Queue/Consume/Named/{TEST_QUEUE}", 1),
("External/all", 3),
("External/allOther", 3),
- ("External/%s/all" % url, 3),
- ("External/%s/botocore/POST" % url, 3),
+ (f"External/{url}/all", 3),
+ (f"External/{url}/botocore/POST", 3),
]
_sqs_scoped_metrics_malformed = [
@@ -70,11 +106,20 @@
]
-@override_application_settings({"distributed_tracing.enabled": True})
+@dt_enabled
@validate_span_events(exact_agents={"aws.operation": "CreateQueue"}, count=1)
-@validate_span_events(exact_agents={"aws.operation": "SendMessage"}, count=1)
-@validate_span_events(exact_agents={"aws.operation": "ReceiveMessage"}, count=1)
-@validate_span_events(exact_agents={"aws.operation": "SendMessageBatch"}, count=1)
+@validate_span_events(
+ **EXPECTED_SEND_MESSAGE_AGENT_ATTRS,
+ count=1,
+)
+@validate_span_events(
+ **EXPECTED_RECIEVE_MESSAGE_AGENT_ATTRS,
+ count=1,
+)
+@validate_span_events(
+ **EXPECTED_SEND_MESSAGE_BATCH_AGENT_ATTRS,
+ count=1,
+)
@validate_span_events(exact_agents={"aws.operation": "PurgeQueue"}, count=1)
@validate_span_events(exact_agents={"aws.operation": "DeleteQueue"}, count=1)
@validate_transaction_metrics(
@@ -84,7 +129,7 @@
background_task=True,
)
@background_task()
-@moto.mock_sqs
+@mock_aws
def test_sqs():
session = botocore.session.get_session()
client = session.create_client(
@@ -124,7 +169,7 @@ def test_sqs():
assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
-@override_application_settings({"distributed_tracing.enabled": True})
+@dt_enabled
@validate_transaction_metrics(
"test_botocore_sqs:test_sqs_malformed",
scoped_metrics=_sqs_scoped_metrics_malformed,
@@ -132,7 +177,7 @@ def test_sqs():
background_task=True,
)
@background_task()
-@moto.mock_sqs
+@mock_aws
def test_sqs_malformed():
session = botocore.session.get_session()
client = session.create_client(
diff --git a/tests/external_botocore/test_s3transfer.py b/tests/external_botocore/test_s3transfer.py
new file mode 100644
index 0000000000..2503d0ba1c
--- /dev/null
+++ b/tests/external_botocore/test_s3transfer.py
@@ -0,0 +1,84 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import uuid
+
+import boto3
+import botocore
+from moto import mock_aws
+from testing_support.fixtures import dt_enabled
+from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
+from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
+
+MOTO_VERSION = get_package_version_tuple("moto")
+BOTOCORE_VERSION = get_package_version_tuple("botocore")
+
+AWS_ACCESS_KEY_ID = "AAAAAAAAAAAACCESSKEY"
+AWS_SECRET_ACCESS_KEY = "AAAAAASECRETKEY" # nosec
+AWS_REGION_NAME = "us-west-2"
+
+TEST_BUCKET = f"python-agent-test-{uuid.uuid4()}"
+
+if BOTOCORE_VERSION < (1, 7, 41):
+ S3_URL = "s3-us-west-2.amazonaws.com"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/{TEST_BUCKET}"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}/hello_world"
+elif BOTOCORE_VERSION < (1, 28):
+ S3_URL = "s3.us-west-2.amazonaws.com"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/{TEST_BUCKET}"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}/hello_world"
+else:
+ S3_URL = f"{TEST_BUCKET}.s3.us-west-2.amazonaws.com"
+ EXPECTED_BUCKET_URL = f"https://{S3_URL}/"
+ EXPECTED_KEY_URL = f"{EXPECTED_BUCKET_URL}hello_world"
+
+
+@dt_enabled
+@validate_span_events(exact_agents={"aws.operation": "CreateBucket"}, count=1)
+@validate_span_events(exact_agents={"aws.operation": "PutObject"}, count=1)
+@validate_transaction_metrics(
+ "test_s3transfer:test_s3_context_propagation",
+ scoped_metrics=[
+ (f"External/{S3_URL}/botocore/PUT", 2),
+ ],
+ rollup_metrics=[
+ ("External/all", 2),
+ ("External/allOther", 2),
+ (f"External/{S3_URL}/all", 2),
+ (f"External/{S3_URL}/botocore/PUT", 2),
+ ],
+ background_task=True,
+)
+@background_task()
+@mock_aws
+def test_s3_context_propagation():
+ client = boto3.client(
+ "s3",
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
+ region_name=AWS_REGION_NAME,
+ )
+
+ # Create bucket
+ resp = client.create_bucket(Bucket=TEST_BUCKET, CreateBucketConfiguration={"LocationConstraint": AWS_REGION_NAME})
+ assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
+
+ # Upload file
+ client.upload_file(Filename="_test_file.txt", Bucket=TEST_BUCKET, Key="_test_file.txt")
+ # No return value to check for this function currently
diff --git a/tests/external_feedparser/conftest.py b/tests/external_feedparser/conftest.py
index 11d19f1cd5..136cfa8787 100644
--- a/tests/external_feedparser/conftest.py
+++ b/tests/external_feedparser/conftest.py
@@ -13,21 +13,24 @@
# limitations under the License.
import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from testing_support.mock_external_http_server import MockExternalHTTPServer
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (external_feedparser)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (external_feedparser)", default_settings=_default_settings
+)
def create_handler(response):
@@ -35,8 +38,10 @@ def handler(self):
self.send_response(200)
self.end_headers()
self.wfile.write(response)
+
return handler
+
@pytest.fixture(scope="session")
def server():
with open("packages.xml", "rb") as f:
diff --git a/tests/external_feedparser/test_feedparser.py b/tests/external_feedparser/test_feedparser.py
index 5e515cfc30..36833dff41 100644
--- a/tests/external_feedparser/test_feedparser.py
+++ b/tests/external_feedparser/test_feedparser.py
@@ -29,17 +29,17 @@ def feedparser():
"feed://localhost",
))
def test_feedparser_external(feedparser, server, url):
- url = url + ':' + str(server.port)
+ url = f"{url}:{str(server.port)}"
@validate_transaction_metrics(
"test_feedparser_external",
background_task=True,
- scoped_metrics=(("External/localhost:%d/feedparser/GET" % server.port, 1),),
+ scoped_metrics=((f"External/localhost:{server.port}/feedparser/GET", 1),),
)
@background_task(name="test_feedparser_external")
def _test():
feed = feedparser.parse(url)
- assert feed["feed"]["link"] == u"https://pypi.org/"
+ assert feed["feed"]["link"] == "https://pypi.org/"
_test()
@@ -50,7 +50,7 @@ def test_feedparser_file(feedparser, stream, server):
@validate_transaction_metrics(
"test_feedparser_file",
background_task=True,
- scoped_metrics=(("External/localhost:%d/feedparser/GET" % server.port, None),),
+ scoped_metrics=((f"External/localhost:{server.port}/feedparser/GET", None),),
)
@background_task(name="test_feedparser_file")
def _test():
@@ -59,7 +59,7 @@ def _test():
feed = feedparser.parse(f)
else:
feed = feedparser.parse("packages.xml")
- assert feed["feed"]["link"] == u"https://pypi.org/"
+ assert feed["feed"]["link"] == "https://pypi.org/"
_test()
@@ -70,6 +70,6 @@ def _test():
))
def test_feedparser_no_transaction(feedparser, server, url):
if url.startswith('http://'):
- url = url + ':' + str(server.port)
+ url = f"{url}:{str(server.port)}"
feed = feedparser.parse(url)
- assert feed["feed"]["link"] == u"https://pypi.org/"
+ assert feed["feed"]["link"] == "https://pypi.org/"
diff --git a/tests/external_http/conftest.py b/tests/external_http/conftest.py
index f8afb49f3b..5d18222499 100644
--- a/tests/external_http/conftest.py
+++ b/tests/external_http/conftest.py
@@ -13,25 +13,29 @@
# limitations under the License.
import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from testing_support.mock_external_http_server import (
- MockExternalHTTPHResponseHeadersServer)
-
+ MockExternalHTTPHResponseHeadersServer,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (external_http)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (external_http)", default_settings=_default_settings
+)
+
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def server():
with MockExternalHTTPHResponseHeadersServer() as _server:
yield _server
diff --git a/tests/external_http/test_http.py b/tests/external_http/test_http.py
index e08518f5ff..d3ff94c1ea 100644
--- a/tests/external_http/test_http.py
+++ b/tests/external_http/test_http.py
@@ -13,44 +13,36 @@
# limitations under the License.
import pytest
-import six
from testing_support.external_fixtures import (
cache_outgoing_headers,
insert_incoming_headers,
)
-from testing_support.fixtures import (
- cat_enabled,
- override_application_settings,
-)
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.fixtures import cat_enabled, override_application_settings
from testing_support.validators.validate_cross_process_headers import (
validate_cross_process_headers,
)
from testing_support.validators.validate_external_node_params import (
validate_external_node_params,
)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from newrelic.api.background_task import background_task
-if six.PY2:
- import httplib
-else:
- import http.client as httplib
+import http.client
@pytest.fixture(scope="session")
def metrics(server):
- if six.PY2:
- _external_metric = "External/localhost:%s/httplib/" % server.port
- else:
- _external_metric = "External/localhost:%s/http/" % server.port
+ _external_metric = f"External/localhost:{server.port}/http/"
scoped = [(_external_metric, 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%s/all" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
(_external_metric, 1),
]
@@ -63,7 +55,7 @@ def test_http_http_request(server, metrics):
)
@background_task(name="test_http:test_http_http_request")
def _test():
- connection = httplib.HTTPConnection("localhost", server.port)
+ connection = http.client.HTTPConnection("localhost", server.port)
connection.request("GET", "/")
response = connection.getresponse()
response.read()
@@ -78,7 +70,7 @@ def test_http_https_request(server, metrics):
)
@background_task(name="test_http:test_http_https_request")
def _test():
- connection = httplib.HTTPSConnection("localhost", server.port)
+ connection = http.client.HTTPSConnection("localhost", server.port)
try:
connection.request("GET", "/")
except Exception:
@@ -101,7 +93,7 @@ def test_http_cross_process_request(distributed_tracing, span_events, server):
@cache_outgoing_headers
@validate_cross_process_headers
def _test():
- connection = httplib.HTTPConnection("localhost", server.port)
+ connection = http.client.HTTPConnection("localhost", server.port)
connection.request("GET", "/")
response = connection.getresponse()
response.read()
@@ -120,14 +112,14 @@ def _test():
@cat_enabled
def test_http_cross_process_response(server):
- _test_http_cross_process_response_scoped_metrics = [("ExternalTransaction/localhost:%s/1#2/test" % server.port, 1)]
+ _test_http_cross_process_response_scoped_metrics = [(f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)]
_test_http_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%s/all" % server.port, 1),
- ("ExternalApp/localhost:%s/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%s/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
_test_http_cross_process_response_external_node_params = [
@@ -146,7 +138,7 @@ def test_http_cross_process_response(server):
@validate_external_node_params(params=_test_http_cross_process_response_external_node_params)
@background_task(name="test_http:test_http_cross_process_response")
def _test():
- connection = httplib.HTTPConnection("localhost", server.port)
+ connection = http.client.HTTPConnection("localhost", server.port)
connection.request("GET", "/")
response = connection.getresponse()
response.read()
diff --git a/tests/external_httplib/conftest.py b/tests/external_httplib/conftest.py
index 2edbeab911..729523599a 100644
--- a/tests/external_httplib/conftest.py
+++ b/tests/external_httplib/conftest.py
@@ -13,26 +13,29 @@
# limitations under the License.
import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from testing_support.mock_external_http_server import (
- MockExternalHTTPHResponseHeadersServer)
-
+ MockExternalHTTPHResponseHeadersServer,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (external_httplib)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (external_httplib)", default_settings=_default_settings
+)
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def server():
with MockExternalHTTPHResponseHeadersServer() as _server:
yield _server
diff --git a/tests/external_httplib/test_httplib.py b/tests/external_httplib/test_httplib.py
index c7747f8ff7..634bb61731 100644
--- a/tests/external_httplib/test_httplib.py
+++ b/tests/external_httplib/test_httplib.py
@@ -14,21 +14,13 @@
import pytest
-try:
- import http.client as httplib
-except ImportError:
- import httplib
+import http.client as httplib
from testing_support.external_fixtures import (
cache_outgoing_headers,
insert_incoming_headers,
)
-from testing_support.fixtures import (
- cat_enabled,
- override_application_settings,
- validate_tt_segment_params,
-)
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.fixtures import cat_enabled, override_application_settings
from testing_support.validators.validate_cross_process_headers import (
validate_cross_process_headers,
)
@@ -36,32 +28,27 @@
validate_external_node_params,
)
from testing_support.validators.validate_span_events import validate_span_events
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_segment_params import (
+ validate_tt_segment_params,
+)
from newrelic.api.background_task import background_task
from newrelic.common.encoding_utils import DistributedTracePayload
-from newrelic.packages import six
-
-
-def select_python_version(py2, py3):
- return six.PY3 and py3 or py2
def test_httplib_http_request(server):
scoped = [
- select_python_version(
- py2=("External/localhost:%d/httplib/" % server.port, 1),
- py3=("External/localhost:%d/http/" % server.port, 1),
- )
+ (f"External/localhost:{server.port}/http/", 1),
]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- select_python_version(
- py2=("External/localhost:%d/httplib/" % server.port, 1),
- py3=("External/localhost:%d/http/" % server.port, 1),
- ),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/http/", 1),
]
@validate_transaction_metrics(
@@ -80,20 +67,14 @@ def _test():
def test_httplib_https_request(server):
_test_httplib_https_request_scoped_metrics = [
- select_python_version(
- py2=("External/localhost:%d/httplib/" % server.port, 1),
- py3=("External/localhost:%d/http/" % server.port, 1),
- )
+ (f"External/localhost:{server.port}/http/", 1),
]
_test_httplib_https_request_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- select_python_version(
- py2=("External/localhost:%d/httplib/" % server.port, 1),
- py3=("External/localhost:%d/http/" % server.port, 1),
- ),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/http/", 1),
]
@validate_transaction_metrics(
@@ -104,7 +85,8 @@ def test_httplib_https_request(server):
)
@background_task(name="test_httplib:test_httplib_https_request")
def _test():
- connection = httplib.HTTPSConnection("localhost", server.port)
+ # fix HTTPSConnection: https://wiki.openstack.org/wiki/OSSN/OSSN-0033
+ connection = httplib.HTTPSConnection("localhost", server.port) # nosec
# It doesn't matter that a SSL exception is raised here because the
# agent still records this as an external request
try:
@@ -119,20 +101,14 @@ def _test():
def test_httplib_http_with_port_request(server):
scoped = [
- select_python_version(
- py2=("External/localhost:%d/httplib/" % server.port, 1),
- py3=("External/localhost:%d/http/" % server.port, 1),
- )
+ (f"External/localhost:{server.port}/http/", 1),
]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- select_python_version(
- py2=("External/localhost:%d/httplib/" % server.port, 1),
- py3=("External/localhost:%d/http/" % server.port, 1),
- ),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/http/", 1),
]
@validate_transaction_metrics(
@@ -192,14 +168,14 @@ def _test():
@cat_enabled
@insert_incoming_headers
def test_httplib_cross_process_response(server):
- scoped = [("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)]
+ scoped = [(f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
@validate_transaction_metrics(
@@ -224,14 +200,14 @@ def _test():
def test_httplib_multiple_requests_cross_process_response(server):
connection = httplib.HTTPConnection("localhost", server.port)
- scoped = [("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)]
+ scoped = [(f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
@validate_transaction_metrics(
@@ -324,17 +300,15 @@ def test_span_events(server):
"span_events.enabled": True,
}
- uri = "http://localhost:%d" % server.port
+ uri = f"http://localhost:{server.port}"
exact_intrinsics = {
- "name": select_python_version(
- py2="External/localhost:%d/httplib/" % server.port, py3="External/localhost:%d/http/" % server.port
- ),
+ "name": f"External/localhost:{server.port}/http/",
"type": "Span",
"sampled": True,
"category": "http",
"span.kind": "client",
- "component": select_python_version(py2="httplib", py3="http"),
+ "component": "http",
}
exact_agents = {
"http.url": uri,
diff --git a/tests/external_httplib/test_urllib.py b/tests/external_httplib/test_urllib.py
index cea88a8dde..90ee87d079 100644
--- a/tests/external_httplib/test_urllib.py
+++ b/tests/external_httplib/test_urllib.py
@@ -39,13 +39,13 @@
@pytest.fixture(scope="session")
def metrics(server):
- scoped = [("External/localhost:%d/urllib/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/urllib/", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/urllib/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/urllib/", 1),
]
return scoped, rollup
@@ -61,7 +61,7 @@ def test_urlopener_http_request(server, metrics):
@background_task(name="test_urllib:test_urlopener_http_request")
def _test():
opener = urllib.URLopener()
- opener.open("http://localhost:%d/" % server.port)
+ opener.open(f"http://localhost:{server.port}/")
_test()
@@ -77,7 +77,7 @@ def test_urlopener_https_request(server, metrics):
def _test():
opener = urllib.URLopener()
try:
- opener.open("https://localhost:%d/" % server.port)
+ opener.open(f"https://localhost:{server.port}/")
except Exception:
pass
@@ -85,13 +85,13 @@ def _test():
def test_urlopener_http_request_with_port(server):
- scoped = [("External/localhost:%d/urllib/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/urllib/", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/urllib/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/urllib/", 1),
]
@validate_transaction_metrics(
@@ -103,7 +103,7 @@ def test_urlopener_http_request_with_port(server):
@background_task(name="test_urllib:test_urlopener_http_request_with_port")
def _test():
opener = urllib.URLopener()
- opener.open("http://localhost:%d/" % server.port)
+ opener.open(f"http://localhost:{server.port}/")
_test()
@@ -135,21 +135,21 @@ def test_urlopener_file_request():
@validate_cross_process_headers
def test_urlopener_cross_process_request(server):
opener = urllib.URLopener()
- opener.open("http://localhost:%d/" % server.port)
+ opener.open(f"http://localhost:{server.port}/")
@cat_enabled
def test_urlopener_cross_process_response(server):
_test_urlopener_cross_process_response_scoped_metrics = [
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)
]
_test_urlopener_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
_test_urlopener_cross_process_response_external_node_params = [
@@ -169,7 +169,7 @@ def test_urlopener_cross_process_response(server):
@background_task(name="test_urllib:test_urlopener_cross_process_response")
def _test():
opener = urllib.URLopener()
- opener.open("http://localhost:%d/" % server.port)
+ opener.open(f"http://localhost:{server.port}/")
_test()
@@ -183,7 +183,7 @@ def test_urlretrieve_http_request(server, metrics):
)
@background_task(name="test_urllib:test_urlretrieve_http_request")
def _test():
- urllib.urlretrieve("http://localhost:%d/" % server.port)
+ urllib.urlretrieve(f"http://localhost:{server.port}/")
_test()
@@ -198,7 +198,7 @@ def test_urlretrieve_https_request(server, metrics):
@background_task(name="test_urllib:test_urlretrieve_https_request")
def _test():
try:
- urllib.urlretrieve("https://localhost:%d/" % server.port)
+ urllib.urlretrieve(f"https://localhost:{server.port}/")
except Exception:
pass
@@ -209,21 +209,21 @@ def _test():
@cache_outgoing_headers
@validate_cross_process_headers
def test_urlretrieve_cross_process_request(server):
- urllib.urlretrieve("http://localhost:%d/" % server.port)
+ urllib.urlretrieve(f"http://localhost:{server.port}/")
@cat_enabled
def test_urlretrieve_cross_process_response(server):
_test_urlretrieve_cross_process_response_scoped_metrics = [
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)
]
_test_urlretrieve_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
_test_urlretrieve_cross_process_response_external_node_params = [
@@ -242,6 +242,6 @@ def test_urlretrieve_cross_process_response(server):
@validate_external_node_params(params=_test_urlretrieve_cross_process_response_external_node_params)
@background_task(name="test_urllib:test_urlretrieve_cross_process_response")
def _test():
- urllib.urlretrieve("http://localhost:%d/" % server.port)
+ urllib.urlretrieve(f"http://localhost:{server.port}/")
_test()
diff --git a/tests/external_httplib/test_urllib2.py b/tests/external_httplib/test_urllib2.py
index 62ed230745..c236d77071 100644
--- a/tests/external_httplib/test_urllib2.py
+++ b/tests/external_httplib/test_urllib2.py
@@ -16,10 +16,7 @@
import pytest
-try:
- import urllib.request as urllib2
-except:
- import urllib2
+import urllib.request as urllib2
from testing_support.external_fixtures import (
cache_outgoing_headers,
@@ -39,13 +36,13 @@
@pytest.fixture(scope="session")
def metrics(server):
- scoped = [("External/localhost:%d/urllib2/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/urllib2/", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/urllib2/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/urllib2/", 1),
]
return scoped, rollup
@@ -60,7 +57,7 @@ def test_urlopen_http_request(server, metrics):
)
@background_task(name="test_urllib2:test_urlopen_http_request")
def _test():
- urllib2.urlopen("http://localhost:%d/" % server.port)
+ urllib2.urlopen(f"http://localhost:{server.port}/")
_test()
@@ -75,7 +72,7 @@ def test_urlopen_https_request(server, metrics):
@background_task(name="test_urllib2:test_urlopen_https_request")
def _test():
try:
- urllib2.urlopen("https://localhost:%d/" % server.port)
+ urllib2.urlopen(f"https://localhost:{server.port}/")
except Exception:
pass
@@ -83,13 +80,13 @@ def _test():
def test_urlopen_http_request_with_port(server):
- scoped = [("External/localhost:%d/urllib2/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/urllib2/", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/urllib2/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/urllib2/", 1),
]
@validate_transaction_metrics(
@@ -100,7 +97,7 @@ def test_urlopen_http_request_with_port(server):
)
@background_task(name="test_urllib2:test_urlopen_http_request_with_port")
def _test():
- urllib2.urlopen("http://localhost:%d/" % server.port)
+ urllib2.urlopen(f"http://localhost:{server.port}/")
_test()
@@ -123,7 +120,7 @@ def _test():
@background_task()
def test_urlopen_file_request():
path = os.path.abspath(__file__)
- file_uri = "file://%s" % path
+ file_uri = f"file://{path}"
urllib2.urlopen(file_uri)
@@ -131,21 +128,21 @@ def test_urlopen_file_request():
@cache_outgoing_headers
@validate_cross_process_headers
def test_urlopen_cross_process_request(server):
- urllib2.urlopen("http://localhost:%d/" % server.port)
+ urllib2.urlopen(f"http://localhost:{server.port}/")
@cat_enabled
def test_urlopen_cross_process_response(server):
_test_urlopen_cross_process_response_scoped_metrics = [
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)
]
_test_urlopen_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
_test_urlopen_cross_process_response_external_node_params = [
@@ -164,6 +161,6 @@ def test_urlopen_cross_process_response(server):
@validate_external_node_params(params=_test_urlopen_cross_process_response_external_node_params)
@background_task(name="test_urllib2:test_urlopen_cross_process_response")
def _test():
- urllib2.urlopen("http://localhost:%d/" % server.port)
+ urllib2.urlopen(f"http://localhost:{server.port}/")
_test()
diff --git a/tests/external_httplib2/conftest.py b/tests/external_httplib2/conftest.py
index cf3501da50..55797d84a2 100644
--- a/tests/external_httplib2/conftest.py
+++ b/tests/external_httplib2/conftest.py
@@ -13,26 +13,29 @@
# limitations under the License.
import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from testing_support.mock_external_http_server import (
- MockExternalHTTPHResponseHeadersServer)
-
+ MockExternalHTTPHResponseHeadersServer,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (external_httplib2)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (external_httplib2)", default_settings=_default_settings
+)
+
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def server():
with MockExternalHTTPHResponseHeadersServer() as _server:
yield _server
diff --git a/tests/external_httplib2/test_httplib2.py b/tests/external_httplib2/test_httplib2.py
index 288aa84ee9..4fc8e2b0d9 100644
--- a/tests/external_httplib2/test_httplib2.py
+++ b/tests/external_httplib2/test_httplib2.py
@@ -35,13 +35,13 @@
@pytest.fixture(scope="session")
def metrics(server):
- scoped = [("External/localhost:%d/httplib2/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/httplib2/", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/httplib2/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/httplib2/", 1),
]
return scoped, rollup
@@ -94,7 +94,7 @@ def test_httplib2_http_request(server, metrics):
@background_task(name="test_httplib2:test_httplib2_http_request")
def _test():
connection = httplib2.Http()
- response, content = connection.request("http://localhost:%d" % server.port, "GET")
+ response, content = connection.request(f"http://localhost:{server.port}", "GET")
_test()
@@ -132,15 +132,15 @@ def _test():
@cat_enabled
def test_httplib2_cross_process_response(server):
_test_httplib2_cross_process_response_scoped_metrics = [
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)
]
_test_httplib2_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
_test_httplib2_cross_process_response_external_node_params = [
diff --git a/tests/external_httpx/conftest.py b/tests/external_httpx/conftest.py
index 87ea1bec06..760e18cde1 100644
--- a/tests/external_httpx/conftest.py
+++ b/tests/external_httpx/conftest.py
@@ -12,14 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import asyncio
-
import pytest
-from testing_support.fixture.event_loop import event_loop as loop
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+from testing_support.fixture.event_loop import ( # noqa: F401; pylint: disable=W0611
+ event_loop as loop,
+)
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
diff --git a/tests/external_httpx/test_client.py b/tests/external_httpx/test_client.py
index 87a1bc7d01..756f7d9773 100644
--- a/tests/external_httpx/test_client.py
+++ b/tests/external_httpx/test_client.py
@@ -19,7 +19,6 @@
dt_enabled,
override_application_settings,
override_generic_settings,
- validate_tt_segment_params,
)
from testing_support.mock_external_http_server import (
MockExternalHTTPHResponseHeadersServer,
@@ -28,8 +27,15 @@
validate_cross_process_headers,
)
from testing_support.validators.validate_span_events import validate_span_events
-from testing_support.validators.validate_transaction_errors import validate_transaction_errors
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.validators.validate_transaction_errors import (
+ validate_transaction_errors,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+from testing_support.validators.validate_tt_segment_params import (
+ validate_tt_segment_params,
+)
from newrelic.api.background_task import background_task
from newrelic.api.time_trace import current_trace
@@ -76,14 +82,14 @@ def server():
def populate_metrics(server, request):
SCOPED_METRICS[:] = []
method = request.getfixturevalue("method").upper()
- SCOPED_METRICS.append(("External/localhost:%d/httpx/%s" % (server.port, method), 2))
+ SCOPED_METRICS.append((f"External/localhost:{server.port}/httpx/{method}", 2))
def exercise_sync_client(server, client, method):
with client as client:
resolved_method = getattr(client, method)
- resolved_method("http://localhost:%s" % server.port)
- response = resolved_method("http://localhost:%s" % server.port)
+ resolved_method(f"http://localhost:{server.port}")
+ response = resolved_method(f"http://localhost:{server.port}")
return response
@@ -118,8 +124,8 @@ async def exercise_async_client(server, client, method):
async with client as client:
resolved_method = getattr(client, method)
responses = await asyncio.gather(
- resolved_method("http://localhost:%s" % server.port),
- resolved_method("http://localhost:%s" % server.port),
+ resolved_method(f"http://localhost:{server.port}"),
+ resolved_method(f"http://localhost:{server.port}"),
)
return responses
@@ -178,7 +184,7 @@ def _test():
transaction = current_transaction()
with httpx.Client() as client:
- response = client.get("http://localhost:%s" % server.port)
+ response = client.get(f"http://localhost:{server.port}")
transaction._test_request_headers = response.request.headers
@@ -210,7 +216,7 @@ def test_async_cross_process_request(httpx, server, loop, distributed_tracing, s
)
async def _test():
async with httpx.AsyncClient() as client:
- response = await client.get("http://localhost:%s" % server.port)
+ response = await client.get(f"http://localhost:{server.port}")
return response
@@ -237,7 +243,7 @@ def test_sync_cross_process_override_headers(httpx, server, loop):
transaction = current_transaction()
with httpx.Client() as client:
- response = client.get("http://localhost:%s" % server.port, headers={"newrelic": "1234"})
+ response = client.get(f"http://localhost:{server.port}", headers={"newrelic": "1234"})
transaction._test_request_headers = response.request.headers
@@ -259,7 +265,7 @@ def test_async_cross_process_override_headers(httpx, server, loop):
async def _test():
async with httpx.AsyncClient() as client:
- response = await client.get("http://localhost:%s" % server.port, headers={"newrelic": "1234"})
+ response = await client.get(f"http://localhost:{server.port}", headers={"newrelic": "1234"})
return response
@@ -286,7 +292,7 @@ def test_sync_client_cat_response_processing(cat_enabled, response_code, server,
expected_metrics = [
(
- "ExternalTransaction/localhost:%s/1#1/WebTransaction/Function/app:beep" % server.port,
+ f"ExternalTransaction/localhost:{server.port}/1#1/WebTransaction/Function/app:beep",
1 if cat_enabled else None,
),
]
@@ -302,7 +308,7 @@ def test_sync_client_cat_response_processing(cat_enabled, response_code, server,
@background_task(name="test_sync_client_cat_response_processing")
def _test():
with httpx.Client() as client:
- response = client.get("http://localhost:%s" % server.port)
+ response = client.get(f"http://localhost:{server.port}")
_test()
@@ -324,7 +330,7 @@ def test_async_client_cat_response_processing(cat_enabled, response_code, httpx,
expected_metrics = [
(
- "ExternalTransaction/localhost:%s/1#1/WebTransaction/Function/app:beep" % server.port,
+ f"ExternalTransaction/localhost:{server.port}/1#1/WebTransaction/Function/app:beep",
1 if cat_enabled else None,
),
]
@@ -341,7 +347,7 @@ def test_async_client_cat_response_processing(cat_enabled, response_code, httpx,
def _test():
async def coro():
async with httpx.AsyncClient() as client:
- response = await client.get("http://localhost:%s" % server.port)
+ response = await client.get(f"http://localhost:{server.port}")
return response
@@ -364,16 +370,16 @@ def empty_hook(response):
@validate_span_events(
count=1,
- exact_intrinsics={"name": "External/localhost:%d/httpx/GET" % server.port},
+ exact_intrinsics={"name": f"External/localhost:{server.port}/httpx/GET"},
exact_agents={"http.statusCode": CAT_RESPONSE_CODE},
)
@background_task(name="test_sync_client_event_hook_exception")
def make_request(client, exc_expected=True):
if exc_expected:
with pytest.raises(RuntimeError):
- client.get("http://localhost:%s" % server.port)
+ client.get(f"http://localhost:{server.port}")
else:
- client.get("http://localhost:%s" % server.port)
+ client.get(f"http://localhost:{server.port}")
with httpx.Client(event_hooks={"response": [exception_event_hook]}) as client:
# Test client init
@@ -410,7 +416,7 @@ def empty_hook(response):
@validate_span_events(
count=1,
- exact_intrinsics={"name": "External/localhost:%d/httpx/GET" % server.port},
+ exact_intrinsics={"name": f"External/localhost:{server.port}/httpx/GET"},
exact_agents={"http.statusCode": CAT_RESPONSE_CODE},
)
@background_task(name="test_sync_client_event_hook_exception")
@@ -418,9 +424,9 @@ def make_request(client, exc_expected=True):
async def coro():
if exc_expected:
with pytest.raises(RuntimeError):
- await client.get("http://localhost:%s" % server.port)
+ await client.get(f"http://localhost:{server.port}")
else:
- await client.get("http://localhost:%s" % server.port)
+ await client.get(f"http://localhost:{server.port}")
loop.run_until_complete(coro())
@@ -458,7 +464,7 @@ def test_sync_nr_disabled(httpx, server):
with httpx.Client() as client:
trace = current_trace()
- response = client.get("http://localhost:%s" % server.port)
+ response = client.get(f"http://localhost:{server.port}")
assert response.status_code == 200
assert trace is None
@@ -476,7 +482,7 @@ def test_async_nr_disabled(httpx, server, loop):
async def _test():
async with httpx.AsyncClient() as client:
- response = await client.get("http://localhost:%s" % server.port)
+ response = await client.get(f"http://localhost:{server.port}")
return response
diff --git a/tests/external_requests/conftest.py b/tests/external_requests/conftest.py
index 10a2ccf051..15426c3a56 100644
--- a/tests/external_requests/conftest.py
+++ b/tests/external_requests/conftest.py
@@ -13,25 +13,29 @@
# limitations under the License.
import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from testing_support.mock_external_http_server import (
- MockExternalHTTPHResponseHeadersServer)
-
+ MockExternalHTTPHResponseHeadersServer,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (external_requests)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (external_requests)", default_settings=_default_settings
+)
+
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def server():
with MockExternalHTTPHResponseHeadersServer() as _server:
yield _server
diff --git a/tests/external_requests/test_requests.py b/tests/external_requests/test_requests.py
index f6f4506e51..228429e3f4 100644
--- a/tests/external_requests/test_requests.py
+++ b/tests/external_requests/test_requests.py
@@ -30,24 +30,30 @@
from testing_support.validators.validate_external_node_params import (
validate_external_node_params,
)
-from testing_support.validators.validate_transaction_errors import validate_transaction_errors
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.validators.validate_transaction_errors import (
+ validate_transaction_errors,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
def get_requests_version():
- return tuple(map(int, requests.__version__.split(".")[:2]))
+ return get_package_version_tuple("requests")
@pytest.fixture(scope="session")
def metrics(server):
- scoped = [("External/localhost:%d/requests/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/requests/", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/requests/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/requests/", 1),
]
return scoped, rollup
@@ -72,7 +78,7 @@ def test_http_request_get(server, metrics):
)
@background_task(name="test_requests:test_http_request_get")
def _test():
- requests.get("http://localhost:%d/" % server.port)
+ requests.get(f"http://localhost:{server.port}/")
_test()
@@ -89,7 +95,7 @@ def test_https_request_get(server, metrics):
@background_task(name="test_requests:test_https_request_get")
def _test():
try:
- requests.get("https://localhost:%d/" % server.port, verify=False)
+ requests.get(f"https://localhost:{server.port}/", verify=False) # nosec
except Exception:
pass
@@ -108,7 +114,7 @@ def test_http_session_send(server, metrics):
@background_task(name="test_requests:test_http_session_send")
def _test():
session = requests.Session()
- req = requests.Request("GET", "http://localhost:%d/" % server.port)
+ req = requests.Request("GET", f"http://localhost:{server.port}/")
prep_req = req.prepare()
session.send(prep_req)
@@ -183,7 +189,7 @@ def test_requests_cross_process_request(distributed_tracing, span_events, server
@cache_outgoing_headers
@validate_cross_process_headers
def _test():
- requests.get("http://localhost:%d/" % server.port)
+ requests.get(f"http://localhost:{server.port}/")
_test = override_application_settings(
{
@@ -199,15 +205,15 @@ def _test():
@cat_enabled
def test_requests_cross_process_response(server):
_test_requests_cross_process_response_scoped_metrics = [
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)
]
_test_requests_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
_test_requests_cross_process_response_external_node_params = [
@@ -227,6 +233,6 @@ def test_requests_cross_process_response(server):
@validate_external_node_params(params=_test_requests_cross_process_response_external_node_params)
@background_task(name="test_requests:test_requests_cross_process_response")
def _test():
- requests.get("http://localhost:%d/" % server.port)
+ requests.get(f"http://localhost:{server.port}/")
_test()
diff --git a/tests/external_requests/test_span_event.py b/tests/external_requests/test_span_event.py
index 575b2d52b7..0eedb99a55 100644
--- a/tests/external_requests/test_span_event.py
+++ b/tests/external_requests/test_span_event.py
@@ -29,14 +29,14 @@ def server():
yield _server
-@pytest.mark.parametrize('path', ('', '/foo', '/' + 'a' * 256))
+@pytest.mark.parametrize('path', ('', '/foo', f"/{'a' * 256}"))
def test_span_events(server, path):
_settings = {
'distributed_tracing.enabled': True,
'span_events.enabled': True,
}
- uri = 'http://localhost:%d' % server.port
+ uri = f'http://localhost:{server.port}'
if path:
uri += path
@@ -44,7 +44,7 @@ def test_span_events(server, path):
expected_uri = uri[:255]
exact_intrinsics = {
- 'name': 'External/localhost:%d/requests/' % server.port,
+ 'name': f'External/localhost:{server.port}/requests/',
'type': 'Span',
'sampled': True,
'priority': 0.5,
diff --git a/tests/external_urllib3/conftest.py b/tests/external_urllib3/conftest.py
index 19d3f394bd..e380eda1be 100644
--- a/tests/external_urllib3/conftest.py
+++ b/tests/external_urllib3/conftest.py
@@ -13,26 +13,29 @@
# limitations under the License.
import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from testing_support.mock_external_http_server import (
- MockExternalHTTPHResponseHeadersServer)
-
+ MockExternalHTTPHResponseHeadersServer,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (external_urllib3)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (external_urllib3)", default_settings=_default_settings
+)
+
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def server():
with MockExternalHTTPHResponseHeadersServer() as _server:
yield _server
diff --git a/tests/external_urllib3/test_urllib3.py b/tests/external_urllib3/test_urllib3.py
index 68e15d4634..afb1e4a105 100644
--- a/tests/external_urllib3/test_urllib3.py
+++ b/tests/external_urllib3/test_urllib3.py
@@ -25,31 +25,33 @@
cache_outgoing_headers,
insert_incoming_headers,
)
-from testing_support.fixtures import (
- cat_enabled,
- override_application_settings,
-)
-from testing_support.util import version2tuple
+from testing_support.fixtures import cat_enabled, override_application_settings
from testing_support.validators.validate_cross_process_headers import (
validate_cross_process_headers,
)
from testing_support.validators.validate_external_node_params import (
validate_external_node_params,
)
-from testing_support.validators.validate_transaction_errors import validate_transaction_errors
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.validators.validate_transaction_errors import (
+ validate_transaction_errors,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
+
from newrelic.api.background_task import background_task
+from newrelic.common.package_version_utils import get_package_version_tuple
@pytest.fixture(scope="session")
def metrics(server):
- scoped = [("External/localhost:%d/urllib3/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/urllib3/GET", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/urllib3/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/urllib3/GET", 1),
]
return scoped, rollup
@@ -65,7 +67,7 @@ def test_http_request_connection_pool_urlopen(server, metrics):
)
@background_task(name="test_urllib3:test_http_request_connection_pool_urlopen")
def _test():
- pool = urllib3.HTTPConnectionPool("localhost:%d" % server.port)
+ pool = urllib3.HTTPConnectionPool(f"localhost:{server.port}")
pool.urlopen("GET", "/")
_test()
@@ -81,7 +83,7 @@ def test_http_request_connection_pool_request(server, metrics):
)
@background_task(name="test_urllib3:test_http_request_connection_pool_request")
def _test():
- pool = urllib3.HTTPConnectionPool("localhost:%d" % server.port)
+ pool = urllib3.HTTPConnectionPool(f"localhost:{server.port}")
pool.request("GET", "/")
_test()
@@ -97,7 +99,7 @@ def test_http_request_connection_from_url_request(server, metrics):
)
@background_task(name="test_urllib3:test_http_request_connection_from_url_request")
def _test():
- conn = urllib3.connection_from_url("http://localhost:%d" % server.port)
+ conn = urllib3.connection_from_url(f"http://localhost:{server.port}")
conn.request("GET", "/")
_test()
@@ -114,7 +116,7 @@ def test_http_request_pool_manager_urlopen(server, metrics):
@background_task(name="test_urllib3:test_http_request_pool_manager_urlopen")
def _test():
pool = urllib3.PoolManager(5)
- pool.urlopen("GET", "http://localhost:%d/" % server.port)
+ pool.urlopen("GET", f"http://localhost:{server.port}/")
_test()
@@ -130,7 +132,7 @@ def test_https_request_connection_pool_urlopen(server, metrics):
@background_task(name="test_urllib3:test_https_request_connection_pool_urlopen")
def _test():
# Setting retries to 0 so that metrics are recorded only once
- pool = urllib3.HTTPSConnectionPool("localhost:%d" % server.port, retries=0)
+ pool = urllib3.HTTPSConnectionPool(f"localhost:{server.port}", retries=0)
try:
pool.urlopen("GET", "/")
except Exception:
@@ -150,7 +152,7 @@ def test_https_request_connection_pool_request(server, metrics):
@background_task(name="test_urllib3:test_https_request_connection_pool_request")
def _test():
# Setting retries to 0 so that metrics are recorded only once
- pool = urllib3.HTTPSConnectionPool("localhost:%d" % server.port, retries=0)
+ pool = urllib3.HTTPSConnectionPool(f"localhost:{server.port}", retries=0)
try:
pool.request("GET", "/")
except Exception:
@@ -160,13 +162,13 @@ def _test():
def test_port_included(server):
- scoped = [("External/localhost:%d/urllib3/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/urllib3/GET", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/urllib3/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/urllib3/GET", 1),
]
@validate_transaction_errors(errors=[])
@@ -175,7 +177,7 @@ def test_port_included(server):
)
@background_task(name="test_urllib3:test_port_included")
def _test():
- conn = urllib3.connection_from_url("http://localhost:%d" % server.port)
+ conn = urllib3.connection_from_url(f"http://localhost:{server.port}")
conn.request("GET", "/")
_test()
@@ -185,16 +187,16 @@ def _test():
# HTTPConnection class. Previously the httplib/http.client HTTPConnection class
# was used. We test httplib in a different test directory so we skip this test.
@pytest.mark.skipif(
- version2tuple(urllib3.__version__) < (1, 8), reason="urllib3.connection.HTTPConnection added in 1.8"
+ get_package_version_tuple("urllib3") < (1, 8), reason="urllib3.connection.HTTPConnection added in 1.8"
)
def test_HTTPConnection_port_included(server):
- scoped = [("External/localhost:%d/urllib3/" % server.port, 1)]
+ scoped = [(f"External/localhost:{server.port}/urllib3/", 1)]
rollup = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("External/localhost:%d/urllib3/" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"External/localhost:{server.port}/urllib3/", 1),
]
@validate_transaction_errors(errors=[])
@@ -206,7 +208,7 @@ def test_HTTPConnection_port_included(server):
)
@background_task(name="test_urllib3:test_HTTPConnection_port_included")
def _test():
- conn = urllib3.connection.HTTPConnection("localhost:%d" % server.port)
+ conn = urllib3.connection.HTTPConnection(f"localhost:{server.port}")
conn.request("GET", "/")
_test()
@@ -226,7 +228,7 @@ def test_urlopen_cross_process_request(distributed_tracing, span_events, server)
@cache_outgoing_headers
@validate_cross_process_headers
def _test():
- pool = urllib3.HTTPConnectionPool("localhost:%d" % server.port)
+ pool = urllib3.HTTPConnectionPool(f"localhost:{server.port}")
pool.urlopen("GET", "/")
_test = override_application_settings(
@@ -243,15 +245,15 @@ def _test():
@cat_enabled
def test_urlopen_cross_process_response(server):
_test_urlopen_cross_process_response_scoped_metrics = [
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1)
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1)
]
_test_urlopen_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/localhost:%d/all" % server.port, 1),
- ("ExternalApp/localhost:%d/1#2/all" % server.port, 1),
- ("ExternalTransaction/localhost:%d/1#2/test" % server.port, 1),
+ (f"External/localhost:{server.port}/all", 1),
+ (f"ExternalApp/localhost:{server.port}/1#2/all", 1),
+ (f"ExternalTransaction/localhost:{server.port}/1#2/test", 1),
]
_test_urlopen_cross_process_response_external_node_params = [
@@ -271,7 +273,7 @@ def test_urlopen_cross_process_response(server):
@validate_external_node_params(params=_test_urlopen_cross_process_response_external_node_params)
@background_task(name="test_urllib3:test_urlopen_cross_process_response")
def _test():
- pool = urllib3.HTTPConnectionPool("localhost:%d" % server.port)
+ pool = urllib3.HTTPConnectionPool(f"localhost:{server.port}")
pool.urlopen("GET", "/")
_test()
diff --git a/tests/framework_aiohttp/_target_application.py b/tests/framework_aiohttp/_target_application.py
index f15e7fd65b..7dde99ce0b 100644
--- a/tests/framework_aiohttp/_target_application.py
+++ b/tests/framework_aiohttp/_target_application.py
@@ -113,7 +113,7 @@ async def websocket_handler(request):
while not ws.closed:
msg = await ws.receive()
if msg.type == WSMsgType.TEXT:
- result = ws.send_str("/" + msg.data)
+ result = ws.send_str(f"/{msg.data}")
if hasattr(result, "__await__"):
await result
diff --git a/tests/framework_aiohttp/conftest.py b/tests/framework_aiohttp/conftest.py
index 3bb814a9b2..ed4c0963b4 100644
--- a/tests/framework_aiohttp/conftest.py
+++ b/tests/framework_aiohttp/conftest.py
@@ -22,15 +22,17 @@
from testing_support.fixture.event_loop import ( # noqa: F401 pylint: disable=W0611
event_loop,
)
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
from testing_support.mock_external_http_server import (
MockExternalHTTPHResponseHeadersServer,
MockExternalHTTPServer,
)
-
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
@@ -135,7 +137,7 @@ def respond_with_cat_header(self):
@pytest.fixture(scope="session")
def local_server_info(mock_header_server):
- host_port = "127.0.0.1:%d" % mock_header_server.port
- metric = "External/%s/aiohttp/" % host_port
- url = "http://" + host_port
+ host_port = f"127.0.0.1:{mock_header_server.port}"
+ metric = f"External/{host_port}/aiohttp/"
+ url = f"http://{host_port}"
return ServerInfo(metric, url)
diff --git a/tests/framework_aiohttp/test_client.py b/tests/framework_aiohttp/test_client.py
index 96bbb46f01..932c46dfb4 100644
--- a/tests/framework_aiohttp/test_client.py
+++ b/tests/framework_aiohttp/test_client.py
@@ -198,10 +198,10 @@ def test_ws_connect_yield_from(event_loop, local_server_info, method, exc_expect
"fetch_multiple",
background_task=True,
scoped_metrics=[
- (local_server_info.base_metric + "GET", 2),
+ (f"{local_server_info.base_metric}GET", 2),
],
rollup_metrics=[
- (local_server_info.base_metric + "GET", 2),
+ (f"{local_server_info.base_metric}GET", 2),
],
)
def task_test():
diff --git a/tests/framework_aiohttp/test_client_async_await.py b/tests/framework_aiohttp/test_client_async_await.py
index dedc64c9db..87337648b7 100644
--- a/tests/framework_aiohttp/test_client_async_await.py
+++ b/tests/framework_aiohttp/test_client_async_await.py
@@ -17,7 +17,9 @@
import aiohttp
import pytest
from testing_support.fixtures import cat_enabled
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
from yarl import URL
from newrelic.api.background_task import background_task
@@ -114,68 +116,6 @@ def task_test():
task_test()
-@pytest.mark.parametrize("method,exc_expected", test_matrix)
-def test_client_throw_async_await(event_loop, local_server_info, method, exc_expected):
- class ThrowerException(ValueError):
- pass
-
- @background_task(name="test_client_throw_async_await")
- async def self_driving_thrower():
- async with aiohttp.ClientSession() as session:
- coro = session._request(method.upper(), local_server_info.url)
-
- # activate the coroutine
- coro.send(None)
-
- # inject error
- coro.throw(ThrowerException())
-
- @validate_transaction_metrics(
- "test_client_throw_async_await",
- background_task=True,
- scoped_metrics=[
- (local_server_info.base_metric + method.upper(), 1),
- ],
- rollup_metrics=[
- (local_server_info.base_metric + method.upper(), 1),
- ],
- )
- def task_test():
- with pytest.raises(ThrowerException):
- event_loop.run_until_complete(self_driving_thrower())
-
- task_test()
-
-
-@pytest.mark.parametrize("method,exc_expected", test_matrix)
-def test_client_close_async_await(event_loop, local_server_info, method, exc_expected):
- @background_task(name="test_client_close_async_await")
- async def self_driving_closer():
- async with aiohttp.ClientSession() as session:
- coro = session._request(method.upper(), local_server_info.url)
-
- # activate the coroutine
- coro.send(None)
-
- # force close
- coro.close()
-
- @validate_transaction_metrics(
- "test_client_close_async_await",
- background_task=True,
- scoped_metrics=[
- (local_server_info.base_metric + method.upper(), 1),
- ],
- rollup_metrics=[
- (local_server_info.base_metric + method.upper(), 1),
- ],
- )
- def task_test():
- event_loop.run_until_complete(self_driving_closer())
-
- task_test()
-
-
@pytest.mark.parametrize("method,exc_expected", test_matrix)
@cat_enabled
def test_await_request_async_await(event_loop, local_server_info, method, exc_expected):
@@ -225,10 +165,10 @@ def test_ws_connect_async_await(event_loop, local_server_info, method, exc_expec
"fetch_multiple",
background_task=True,
scoped_metrics=[
- (local_server_info.base_metric + "GET", 2),
+ (f"{local_server_info.base_metric}GET", 2),
],
rollup_metrics=[
- (local_server_info.base_metric + "GET", 2),
+ (f"{local_server_info.base_metric}GET", 2),
],
)
def task_test():
@@ -240,7 +180,6 @@ def task_test():
@pytest.mark.parametrize("method,exc_expected", test_matrix)
@cat_enabled
def test_create_task_async_await(event_loop, local_server_info, method, exc_expected):
-
# `loop.create_task` returns a Task object which uses the coroutine's
# `send` method, not `__next__`
diff --git a/tests/framework_aiohttp/test_client_cat.py b/tests/framework_aiohttp/test_client_cat.py
index 8877434299..edd14498db 100644
--- a/tests/framework_aiohttp/test_client_cat.py
+++ b/tests/framework_aiohttp/test_client_cat.py
@@ -41,7 +41,6 @@
async def fetch(url, headers=None, raise_for_status=False, connector=None):
-
kwargs = {}
if version_info >= (2, 0):
kwargs = {"raise_for_status": raise_for_status}
@@ -77,7 +76,7 @@ async def fetch(url, headers=None, raise_for_status=False, connector=None):
def test_outbound_cross_process_headers(event_loop, cat_enabled, distributed_tracing, span_events, mock_header_server):
@background_task(name="test_outbound_cross_process_headers")
async def _test():
- headers = await fetch("http://127.0.0.1:%d" % mock_header_server.port)
+ headers = await fetch(f"http://127.0.0.1:{mock_header_server.port}")
transaction = current_transaction()
transaction._test_request_headers = headers
@@ -122,9 +121,8 @@ def test():
@pytest.mark.parametrize("customer_headers", _customer_headers_tests)
def test_outbound_cross_process_headers_custom_headers(event_loop, customer_headers, mock_header_server):
-
headers = event_loop.run_until_complete(
- background_task()(fetch)("http://127.0.0.1:%d" % mock_header_server.port, customer_headers.copy())
+ background_task()(fetch)(f"http://127.0.0.1:{mock_header_server.port}", customer_headers.copy())
)
# always honor customer headers
@@ -133,7 +131,7 @@ def test_outbound_cross_process_headers_custom_headers(event_loop, customer_head
def test_outbound_cross_process_headers_no_txn(event_loop, mock_header_server):
- headers = event_loop.run_until_complete(fetch("http://127.0.0.1:%d" % mock_header_server.port))
+ headers = event_loop.run_until_complete(fetch(f"http://127.0.0.1:{mock_header_server.port}"))
assert not headers.get(ExternalTrace.cat_id_key)
assert not headers.get(ExternalTrace.cat_transaction_key)
@@ -148,7 +146,7 @@ async def test():
delattr(transaction, "guid")
try:
- headers = await fetch("http://127.0.0.1:%d" % mock_header_server.port)
+ headers = await fetch(f"http://127.0.0.1:{mock_header_server.port}")
assert not headers.get(ExternalTrace.cat_id_key)
assert not headers.get(ExternalTrace.cat_transaction_key)
@@ -174,7 +172,6 @@ async def _resolve_host(self, host, port, *args, **kwargs):
def test_process_incoming_headers(
event_loop, cat_enabled, response_code, raise_for_status, connector_class, mock_external_http_server
):
-
# It was discovered via packnsend that the `throw` method of the `_request`
# coroutine is used in the case of poorly resolved hosts. An older version
# of the instrumentation ended the ExternalTrace anytime `throw` was called
@@ -183,19 +180,19 @@ def test_process_incoming_headers(
# always called and thus makes sure the trace is not ended before
# StopIteration is called.
server, response_values = mock_external_http_server
- address = "http://127.0.0.1:%d" % server.port
+ address = f"http://127.0.0.1:{server.port}"
port = server.port
_test_cross_process_response_scoped_metrics = [
- ("ExternalTransaction/127.0.0.1:%d/1#2/test" % port, 1 if cat_enabled else None)
+ (f"ExternalTransaction/127.0.0.1:{port}/1#2/test", 1 if cat_enabled else None)
]
_test_cross_process_response_rollup_metrics = [
("External/all", 1),
("External/allOther", 1),
- ("External/127.0.0.1:%d/all" % port, 1),
- ("ExternalApp/127.0.0.1:%d/1#2/all" % port, 1 if cat_enabled else None),
- ("ExternalTransaction/127.0.0.1:%d/1#2/test" % port, 1 if cat_enabled else None),
+ (f"External/127.0.0.1:{port}/all", 1),
+ (f"ExternalApp/127.0.0.1:{port}/1#2/all", 1 if cat_enabled else None),
+ (f"ExternalTransaction/127.0.0.1:{port}/1#2/test", 1 if cat_enabled else None),
]
_test_cross_process_response_external_node_params = [
@@ -208,8 +205,6 @@ def test_process_incoming_headers(
k for k, v in _test_cross_process_response_external_node_params
]
- connector = connector_class() if connector_class else None
-
@background_task(name="test_process_incoming_headers")
async def _test():
transaction = current_transaction()
@@ -217,6 +212,8 @@ async def _test():
response_values.append((headers, response_code))
+ connector = connector_class() if connector_class else None
+
await fetch(address, raise_for_status=raise_for_status, connector=connector)
@override_application_settings(
diff --git a/tests/framework_aiohttp/test_middleware.py b/tests/framework_aiohttp/test_middleware.py
index 6cbf86677a..246ae1ad09 100644
--- a/tests/framework_aiohttp/test_middleware.py
+++ b/tests/framework_aiohttp/test_middleware.py
@@ -74,7 +74,7 @@ def _test():
rollup_metrics = [
("Function/_target_application:index", 1),
(metric, 1),
- ("Python/Framework/aiohttp/%s" % aiohttp.__version__, 1),
+ (f"Python/Framework/aiohttp/{aiohttp.__version__}", 1),
]
_test = validate_transaction_metrics(
diff --git a/tests/framework_aiohttp/test_server.py b/tests/framework_aiohttp/test_server.py
index 6a5ef0d10e..69ebd4b21b 100644
--- a/tests/framework_aiohttp/test_server.py
+++ b/tests/framework_aiohttp/test_server.py
@@ -87,11 +87,11 @@ async def fetch():
@validate_transaction_metrics(
metric_name,
scoped_metrics=[
- ("Function/%s" % metric_name, 1),
+ (f"Function/{metric_name}", 1),
],
rollup_metrics=[
- ("Function/%s" % metric_name, 1),
- ("Python/Framework/aiohttp/%s" % aiohttp.__version__, 1),
+ (f"Function/{metric_name}", 1),
+ (f"Python/Framework/aiohttp/{aiohttp.__version__}", 1),
],
)
@validate_transaction_event_attributes(
@@ -184,11 +184,11 @@ async def multi_fetch(loop):
@validate_transaction_metrics(
metric_name,
scoped_metrics=[
- ("Function/%s" % metric_name, 1),
+ (f"Function/{metric_name}", 1),
],
rollup_metrics=[
- ("Function/%s" % metric_name, 1),
- ("Python/Framework/aiohttp/%s" % aiohttp.__version__, 1),
+ (f"Function/{metric_name}", 1),
+ (f"Python/Framework/aiohttp/{aiohttp.__version__}", 1),
],
)
@validate_transaction_event_attributes(
diff --git a/tests/framework_aiohttp/test_server_cat.py b/tests/framework_aiohttp/test_server_cat.py
index 44b5c72174..8b18074558 100644
--- a/tests/framework_aiohttp/test_server_cat.py
+++ b/tests/framework_aiohttp/test_server_cat.py
@@ -37,7 +37,7 @@
def record_aiohttp1_raw_headers(raw_headers):
try:
- import aiohttp.protocol # noqa: F401
+ import aiohttp.protocol # noqa: F401, pylint: disable=W0611
except ImportError:
def pass_through(function):
@@ -113,7 +113,7 @@ async def fetch():
app_data = json.loads(deobfuscate(raw_headers["X-NewRelic-App-Data"], ENCODING_KEY))
assert app_data[0] == cat_id
- assert app_data[1] == ("WebTransaction/Function/%s" % metric_name)
+ assert app_data[1] == f"WebTransaction/Function/{metric_name}"
else:
assert "X-NewRelic-App-Data" not in resp.headers
@@ -135,7 +135,7 @@ async def fetch():
# a fixture from conftest.py/_target_application.py
@validate_analytics_catmap_data(
- "WebTransaction/Function/%s" % metric_name,
+ f"WebTransaction/Function/{metric_name}",
expected_attributes=expected_intrinsics,
non_expected_attributes=forgone_intrinsics,
)
diff --git a/tests/framework_ariadne/__init__.py b/tests/framework_ariadne/__init__.py
new file mode 100644
index 0000000000..8030baccf7
--- /dev/null
+++ b/tests/framework_ariadne/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tests/framework_ariadne/_target_application.py b/tests/framework_ariadne/_target_application.py
index 94bc0710f5..fef7826086 100644
--- a/tests/framework_ariadne/_target_application.py
+++ b/tests/framework_ariadne/_target_application.py
@@ -12,140 +12,125 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
-
-from ariadne import (
- MutationType,
- QueryType,
- UnionType,
- load_schema_from_path,
- make_executable_schema,
+
+import asyncio
+import json
+
+from framework_ariadne._target_schema_async import (
+ target_asgi_application as target_asgi_application_async,
+)
+from framework_ariadne._target_schema_async import target_schema as target_schema_async
+from framework_ariadne._target_schema_sync import (
+ target_asgi_application as target_asgi_application_sync,
+)
+from framework_ariadne._target_schema_sync import target_schema as target_schema_sync
+from framework_ariadne._target_schema_sync import (
+ target_wsgi_application as target_wsgi_application_sync,
)
-from ariadne.asgi import GraphQL as GraphQLASGI
-from ariadne.wsgi import GraphQL as GraphQLWSGI
+from framework_ariadne._target_schema_sync import ariadne_version_tuple
+from graphql import MiddlewareManager
-schema_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "schema.graphql")
-type_defs = load_schema_from_path(schema_file)
-
-
-authors = [
- {
- "first_name": "New",
- "last_name": "Relic",
- },
- {
- "first_name": "Bob",
- "last_name": "Smith",
- },
- {
- "first_name": "Leslie",
- "last_name": "Jones",
- },
-]
-books = [
- {
- "id": 1,
- "name": "Python Agent: The Book",
- "isbn": "a-fake-isbn",
- "author": authors[0],
- "branch": "riverside",
- },
- {
- "id": 2,
- "name": "Ollies for O11y: A Sk8er's Guide to Observability",
- "isbn": "a-second-fake-isbn",
- "author": authors[1],
- "branch": "downtown",
- },
- {
- "id": 3,
- "name": "[Redacted]",
- "isbn": "a-third-fake-isbn",
- "author": authors[2],
- "branch": "riverside",
- },
-]
-magazines = [
- {"id": 1, "name": "Reli Updates Weekly", "issue": 1, "branch": "riverside"},
- {"id": 2, "name": "Reli Updates Weekly", "issue": 2, "branch": "downtown"},
- {"id": 3, "name": "Node Weekly", "issue": 1, "branch": "riverside"},
-]
+def check_response(query, success, response):
+ if isinstance(query, str) and "error" not in query:
+ assert success and "errors" not in response, response
+ assert response.get("data", None), response
+ else:
+ assert "errors" in response, response
-libraries = ["riverside", "downtown"]
-libraries = [
- {
- "id": i + 1,
- "branch": branch,
- "magazine": [m for m in magazines if m["branch"] == branch],
- "book": [b for b in books if b["branch"] == branch],
- }
- for i, branch in enumerate(libraries)
-]
+def run_sync(schema):
+ def _run_sync(query, middleware=None):
+ from ariadne import graphql_sync
-storage = []
+ if ariadne_version_tuple < (0, 18):
+ if middleware:
+ middleware = MiddlewareManager(*middleware)
+ success, response = graphql_sync(schema, {"query": query}, middleware=middleware)
+ check_response(query, success, response)
-mutation = MutationType()
+ return response.get("data", {})
+ return _run_sync
-@mutation.field("storage_add")
-def mutate(self, info, string):
- storage.append(string)
- return {"string": string}
+def run_async(schema):
+ def _run_async(query, middleware=None):
+ from ariadne import graphql
-item = UnionType("Item")
+ #Later versions of ariadne directly accept a list of middleware while older versions require the MiddlewareManager
+ if ariadne_version_tuple < (0, 18):
+ if middleware:
+ middleware = MiddlewareManager(*middleware)
+ loop = asyncio.get_event_loop()
+ success, response = loop.run_until_complete(graphql(schema, {"query": query}, middleware=middleware))
+ check_response(query, success, response)
-@item.type_resolver
-def resolve_type(obj, *args):
- if "isbn" in obj:
- return "Book"
- elif "issue" in obj: # pylint: disable=R1705
- return "Magazine"
+ return response.get("data", {})
- return None
+ return _run_async
-query = QueryType()
+def run_wsgi(app):
+ def _run_asgi(query, middleware=None):
+ if not isinstance(query, str) or "error" in query:
+ expect_errors = True
+ else:
+ expect_errors = False
+ app.app.middleware = middleware
-@query.field("library")
-def resolve_library(self, info, index):
- return libraries[index]
+ response = app.post(
+ "/", json.dumps({"query": query}), headers={"Content-Type": "application/json"}, expect_errors=expect_errors
+ )
+ body = json.loads(response.body.decode("utf-8"))
+ if expect_errors:
+ assert body["errors"]
+ else:
+ assert "errors" not in body or not body["errors"]
-@query.field("storage")
-def resolve_storage(self, info):
- return storage
+ return body.get("data", {})
+ return _run_asgi
-@query.field("search")
-def resolve_search(self, info, contains):
- search_books = [b for b in books if contains in b["name"]]
- search_magazines = [m for m in magazines if contains in m["name"]]
- return search_books + search_magazines
+def run_asgi(app):
+ def _run_asgi(query, middleware=None):
+ if ariadne_version_tuple < (0, 16):
+ app.asgi_application.middleware = middleware
-@query.field("hello")
-def resolve_hello(self, info):
- return "Hello!"
+ #In ariadne v0.16.0, the middleware attribute was removed from the GraphQL class in favor of the http_handler
+ elif ariadne_version_tuple >= (0, 16):
+ app.asgi_application.http_handler.middleware = middleware
+ response = app.make_request(
+ "POST", "/", body=json.dumps({"query": query}), headers={"Content-Type": "application/json"}
+ )
+ body = json.loads(response.body.decode("utf-8"))
-@query.field("echo")
-def resolve_echo(self, info, echo):
- return echo
+ if not isinstance(query, str) or "error" in query:
+ try:
+ assert response.status != 200
+ except AssertionError:
+ assert body["errors"]
+ else:
+ assert response.status == 200
+ assert "errors" not in body or not body["errors"]
+ return body.get("data", {})
-@query.field("error_non_null")
-@query.field("error")
-def resolve_error(self, info):
- raise RuntimeError("Runtime Error!")
+ return _run_asgi
-_target_application = make_executable_schema(type_defs, query, mutation, item)
-_target_asgi_application = GraphQLASGI(_target_application)
-_target_wsgi_application = GraphQLWSGI(_target_application)
+target_application = {
+ "sync-sync": run_sync(target_schema_sync),
+ "async-sync": run_async(target_schema_sync),
+ "async-async": run_async(target_schema_async),
+ "wsgi-sync": run_wsgi(target_wsgi_application_sync),
+ "asgi-sync": run_asgi(target_asgi_application_sync),
+ "asgi-async": run_asgi(target_asgi_application_async),
+}
diff --git a/tests/framework_ariadne/_target_schema_async.py b/tests/framework_ariadne/_target_schema_async.py
new file mode 100644
index 0000000000..076475628d
--- /dev/null
+++ b/tests/framework_ariadne/_target_schema_async.py
@@ -0,0 +1,94 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+from ariadne import (
+ MutationType,
+ QueryType,
+ UnionType,
+ load_schema_from_path,
+ make_executable_schema,
+)
+from ariadne.asgi import GraphQL as GraphQLASGI
+from framework_graphql._target_schema_sync import books, magazines, libraries
+
+from testing_support.asgi_testing import AsgiTest
+
+schema_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "schema.graphql")
+type_defs = load_schema_from_path(schema_file)
+
+storage = []
+
+mutation = MutationType()
+
+
+@mutation.field("storage_add")
+async def resolve_storage_add(self, info, string):
+ storage.append(string)
+ return string
+
+
+item = UnionType("Item")
+
+
+@item.type_resolver
+async def resolve_type(obj, *args):
+ if "isbn" in obj:
+ return "Book"
+ elif "issue" in obj: # pylint: disable=R1705
+ return "Magazine"
+
+ return None
+
+
+query = QueryType()
+
+
+@query.field("library")
+async def resolve_library(self, info, index):
+ return libraries[index]
+
+
+@query.field("storage")
+async def resolve_storage(self, info):
+ return [storage.pop()]
+
+
+@query.field("search")
+async def resolve_search(self, info, contains):
+ search_books = [b for b in books if contains in b["name"]]
+ search_magazines = [m for m in magazines if contains in m["name"]]
+ return search_books + search_magazines
+
+
+@query.field("hello")
+@query.field("error_middleware")
+async def resolve_hello(self, info):
+ return "Hello!"
+
+
+@query.field("echo")
+async def resolve_echo(self, info, echo):
+ return echo
+
+
+@query.field("error_non_null")
+@query.field("error")
+async def resolve_error(self, info):
+ raise RuntimeError("Runtime Error!")
+
+
+target_schema = make_executable_schema(type_defs, query, mutation, item)
+target_asgi_application = AsgiTest(GraphQLASGI(target_schema))
diff --git a/tests/framework_ariadne/_target_schema_sync.py b/tests/framework_ariadne/_target_schema_sync.py
new file mode 100644
index 0000000000..8860e71ac5
--- /dev/null
+++ b/tests/framework_ariadne/_target_schema_sync.py
@@ -0,0 +1,106 @@
+# Copyright 2010 New Relic, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import webtest
+
+from ariadne import (
+ MutationType,
+ QueryType,
+ UnionType,
+ load_schema_from_path,
+ make_executable_schema,
+)
+from ariadne.wsgi import GraphQL as GraphQLWSGI
+from framework_graphql._target_schema_sync import books, magazines, libraries
+
+from testing_support.asgi_testing import AsgiTest
+from framework_ariadne.test_application import ARIADNE_VERSION
+
+ariadne_version_tuple = tuple(map(int, ARIADNE_VERSION.split(".")))
+
+if ariadne_version_tuple < (0, 16):
+ from ariadne.asgi import GraphQL as GraphQLASGI
+elif ariadne_version_tuple >= (0, 16):
+ from ariadne.asgi.graphql import GraphQL as GraphQLASGI
+
+
+schema_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "schema.graphql")
+type_defs = load_schema_from_path(schema_file)
+
+storage = []
+
+mutation = MutationType()
+
+
+
+@mutation.field("storage_add")
+def resolve_storage_add(self, info, string):
+ storage.append(string)
+ return string
+
+
+item = UnionType("Item")
+
+
+@item.type_resolver
+def resolve_type(obj, *args):
+ if "isbn" in obj:
+ return "Book"
+ elif "issue" in obj: # pylint: disable=R1705
+ return "Magazine"
+
+ return None
+
+
+query = QueryType()
+
+
+@query.field("library")
+def resolve_library(self, info, index):
+ return libraries[index]
+
+
+@query.field("storage")
+def resolve_storage(self, info):
+ return [storage.pop()]
+
+
+@query.field("search")
+def resolve_search(self, info, contains):
+ search_books = [b for b in books if contains in b["name"]]
+ search_magazines = [m for m in magazines if contains in m["name"]]
+ return search_books + search_magazines
+
+
+@query.field("hello")
+@query.field("error_middleware")
+def resolve_hello(self, info):
+ return "Hello!"
+
+
+@query.field("echo")
+def resolve_echo(self, info, echo):
+ return echo
+
+
+@query.field("error_non_null")
+@query.field("error")
+def resolve_error(self, info):
+ raise RuntimeError("Runtime Error!")
+
+
+target_schema = make_executable_schema(type_defs, query, mutation, item)
+target_asgi_application = AsgiTest(GraphQLASGI(target_schema))
+target_wsgi_application = webtest.TestApp(GraphQLWSGI(target_schema))
\ No newline at end of file
diff --git a/tests/framework_ariadne/conftest.py b/tests/framework_ariadne/conftest.py
index 93623a6852..cbb4448cd5 100644
--- a/tests/framework_ariadne/conftest.py
+++ b/tests/framework_ariadne/conftest.py
@@ -12,12 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-import six
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
"transaction_tracer.explain_threshold": 0.0,
"transaction_tracer.transaction_threshold": 0.0,
"transaction_tracer.stack_trace_threshold": 0.0,
@@ -29,14 +31,3 @@
app_name="Python Agent Test (framework_ariadne)",
default_settings=_default_settings,
)
-
-
-@pytest.fixture(scope="session")
-def app():
- from _target_application import _target_application
-
- return _target_application
-
-
-if six.PY2:
- collect_ignore = ["test_application_async.py"]
diff --git a/tests/framework_ariadne/schema.graphql b/tests/framework_ariadne/schema.graphql
index 4c76e0b88b..8bf64af512 100644
--- a/tests/framework_ariadne/schema.graphql
+++ b/tests/framework_ariadne/schema.graphql
@@ -33,7 +33,7 @@ type Magazine {
}
type Mutation {
- storage_add(string: String!): StorageAdd
+ storage_add(string: String!): String
}
type Query {
@@ -44,8 +44,5 @@ type Query {
echo(echo: String!): String
error: String
error_non_null: String!
-}
-
-type StorageAdd {
- string: String
+ error_middleware: String
}
diff --git a/tests/framework_ariadne/test_application.py b/tests/framework_ariadne/test_application.py
index cf8501a7af..0b7bf24898 100644
--- a/tests/framework_ariadne/test_application.py
+++ b/tests/framework_ariadne/test_application.py
@@ -11,526 +11,27 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
import pytest
-from testing_support.fixtures import dt_enabled, override_application_settings
-from testing_support.validators.validate_span_events import validate_span_events
-from testing_support.validators.validate_transaction_count import (
- validate_transaction_count,
-)
-from testing_support.validators.validate_transaction_errors import (
- validate_transaction_errors,
-)
-from testing_support.validators.validate_transaction_metrics import (
- validate_transaction_metrics,
-)
-
-from newrelic.api.background_task import background_task
-from newrelic.common.object_names import callable_name
-from newrelic.common.package_version_utils import get_package_version_tuple
-
-
-@pytest.fixture(scope="session")
-def is_graphql_2():
- from graphql import __version__ as version
-
- major_version = int(version.split(".")[0])
- return major_version == 2
-
-
-@pytest.fixture(scope="session")
-def graphql_run():
- """Wrapper function to simulate framework_graphql test behavior."""
-
- def execute(schema, query, *args, **kwargs):
- from ariadne import graphql_sync
-
- return graphql_sync(schema, {"query": query}, *args, **kwargs)
-
- return execute
-
-
-def to_graphql_source(query):
- def delay_import():
- try:
- from graphql import Source
- except ImportError:
- # Fallback if Source is not implemented
- return query
-
- from graphql import __version__ as version
-
- # For graphql2, Source objects aren't acceptable input
- major_version = int(version.split(".")[0])
- if major_version == 2:
- return query
-
- return Source(query)
-
- return delay_import
-
-
-def example_middleware(next, root, info, **args): # pylint: disable=W0622
- return_value = next(root, info, **args)
- return return_value
-
-
-def error_middleware(next, root, info, **args): # pylint: disable=W0622
- raise RuntimeError("Runtime Error!")
-
-
-_runtime_error_name = callable_name(RuntimeError)
-_test_runtime_error = [(_runtime_error_name, "Runtime Error!")]
-_graphql_base_rollup_metrics = [
- ("OtherTransaction/all", 1),
- ("GraphQL/all", 1),
- ("GraphQL/allOther", 1),
- ("GraphQL/Ariadne/all", 1),
- ("GraphQL/Ariadne/allOther", 1),
-]
-
-
-def test_basic(app, graphql_run):
- from graphql import __version__ as version
-
- FRAMEWORK_METRICS = [
- ("Python/Framework/Ariadne/None", 1),
- ("Python/Framework/GraphQL/%s" % version, 1),
- ]
-
- @validate_transaction_metrics(
- "query//hello",
- "GraphQL",
- rollup_metrics=_graphql_base_rollup_metrics + FRAMEWORK_METRICS,
- background_task=True,
- )
- @background_task()
- def _test():
- ok, response = graphql_run(app, "{ hello }")
- assert ok and not response.get("errors")
-
- _test()
-
-
-@dt_enabled
-def test_query_and_mutation(app, graphql_run):
- from graphql import __version__ as version
-
- FRAMEWORK_METRICS = [
- ("Python/Framework/Ariadne/None", 1),
- ("Python/Framework/GraphQL/%s" % version, 1),
- ]
- _test_mutation_scoped_metrics = [
- ("GraphQL/resolve/Ariadne/storage", 1),
- ("GraphQL/resolve/Ariadne/storage_add", 1),
- ("GraphQL/operation/Ariadne/query//storage", 1),
- ("GraphQL/operation/Ariadne/mutation//storage_add.string", 1),
- ]
- _test_mutation_unscoped_metrics = [
- ("OtherTransaction/all", 1),
- ("GraphQL/all", 2),
- ("GraphQL/Ariadne/all", 2),
- ("GraphQL/allOther", 2),
- ("GraphQL/Ariadne/allOther", 2),
- ] + _test_mutation_scoped_metrics
-
- _expected_mutation_operation_attributes = {
- "graphql.operation.type": "mutation",
- "graphql.operation.name": "",
- }
- _expected_mutation_resolver_attributes = {
- "graphql.field.name": "storage_add",
- "graphql.field.parentType": "Mutation",
- "graphql.field.path": "storage_add",
- "graphql.field.returnType": "StorageAdd",
- }
- _expected_query_operation_attributes = {
- "graphql.operation.type": "query",
- "graphql.operation.name": "",
- }
- _expected_query_resolver_attributes = {
- "graphql.field.name": "storage",
- "graphql.field.parentType": "Query",
- "graphql.field.path": "storage",
- "graphql.field.returnType": "[String]",
- }
-
- @validate_transaction_metrics(
- "query//storage",
- "GraphQL",
- scoped_metrics=_test_mutation_scoped_metrics,
- rollup_metrics=_test_mutation_unscoped_metrics + FRAMEWORK_METRICS,
- background_task=True,
- )
- @validate_span_events(exact_agents=_expected_mutation_operation_attributes)
- @validate_span_events(exact_agents=_expected_mutation_resolver_attributes)
- @validate_span_events(exact_agents=_expected_query_operation_attributes)
- @validate_span_events(exact_agents=_expected_query_resolver_attributes)
- @background_task()
- def _test():
- ok, response = graphql_run(app, 'mutation { storage_add(string: "abc") { string } }')
- assert ok and not response.get("errors")
- ok, response = graphql_run(app, "query { storage }")
- assert ok and not response.get("errors")
-
- # These are separate assertions because pypy stores 'abc' as a unicode string while other Python versions do not
- assert "storage" in str(response["data"])
- assert "abc" in str(response["data"])
-
- _test()
-
-
-@dt_enabled
-def test_middleware(app, graphql_run, is_graphql_2):
- _test_middleware_metrics = [
- ("GraphQL/operation/Ariadne/query//hello", 1),
- ("GraphQL/resolve/Ariadne/hello", 1),
- ("Function/test_application:example_middleware", 1),
- ]
-
- @validate_transaction_metrics(
- "query//hello",
- "GraphQL",
- scoped_metrics=_test_middleware_metrics,
- rollup_metrics=_test_middleware_metrics + _graphql_base_rollup_metrics,
- background_task=True,
- )
- # Span count 5: Transaction, Operation, Middleware, and 1 Resolver and Resolver function
- @validate_span_events(count=5)
- @background_task()
- def _test():
- from graphql import MiddlewareManager
-
- middleware = (
- [example_middleware]
- if get_package_version_tuple("ariadne") >= (0, 18)
- else MiddlewareManager(example_middleware)
- )
+from framework_graphql.test_application import *
- ok, response = graphql_run(app, "{ hello }", middleware=middleware)
- assert ok and not response.get("errors")
- assert "Hello!" in str(response["data"])
+from newrelic.common.package_version_utils import get_package_version
- _test()
+ARIADNE_VERSION = get_package_version("ariadne")
+ariadne_version_tuple = tuple(map(int, ARIADNE_VERSION.split(".")))
-@dt_enabled
-def test_exception_in_middleware(app, graphql_run):
- query = "query MyQuery { hello }"
- field = "hello"
-
- # Metrics
- _test_exception_scoped_metrics = [
- ("GraphQL/operation/Ariadne/query/MyQuery/%s" % field, 1),
- ("GraphQL/resolve/Ariadne/%s" % field, 1),
- ]
- _test_exception_rollup_metrics = [
- ("Errors/all", 1),
- ("Errors/allOther", 1),
- ("Errors/OtherTransaction/GraphQL/test_application:error_middleware", 1),
- ] + _test_exception_scoped_metrics
-
- # Attributes
- _expected_exception_resolver_attributes = {
- "graphql.field.name": field,
- "graphql.field.parentType": "Query",
- "graphql.field.path": field,
- "graphql.field.returnType": "String",
- }
- _expected_exception_operation_attributes = {
- "graphql.operation.type": "query",
- "graphql.operation.name": "MyQuery",
- "graphql.operation.query": query,
- }
-
- @validate_transaction_metrics(
- "test_application:error_middleware",
- "GraphQL",
- scoped_metrics=_test_exception_scoped_metrics,
- rollup_metrics=_test_exception_rollup_metrics + _graphql_base_rollup_metrics,
- background_task=True,
- )
- @validate_span_events(exact_agents=_expected_exception_operation_attributes)
- @validate_span_events(exact_agents=_expected_exception_resolver_attributes)
- @validate_transaction_errors(errors=_test_runtime_error)
- @background_task()
- def _test():
- from graphql import MiddlewareManager
-
- middleware = (
- [error_middleware]
- if get_package_version_tuple("ariadne") >= (0, 18)
- else MiddlewareManager(error_middleware)
- )
-
- _, response = graphql_run(app, query, middleware=middleware)
- assert response["errors"]
-
- _test()
-
-
-@pytest.mark.parametrize("field", ("error", "error_non_null"))
-@dt_enabled
-def test_exception_in_resolver(app, graphql_run, field):
- query = "query MyQuery { %s }" % field
- txn_name = "_target_application:resolve_error"
-
- # Metrics
- _test_exception_scoped_metrics = [
- ("GraphQL/operation/Ariadne/query/MyQuery/%s" % field, 1),
- ("GraphQL/resolve/Ariadne/%s" % field, 1),
- ]
- _test_exception_rollup_metrics = [
- ("Errors/all", 1),
- ("Errors/allOther", 1),
- ("Errors/OtherTransaction/GraphQL/%s" % txn_name, 1),
- ] + _test_exception_scoped_metrics
-
- # Attributes
- _expected_exception_resolver_attributes = {
- "graphql.field.name": field,
- "graphql.field.parentType": "Query",
- "graphql.field.path": field,
- "graphql.field.returnType": "String!" if "non_null" in field else "String",
- }
- _expected_exception_operation_attributes = {
- "graphql.operation.type": "query",
- "graphql.operation.name": "MyQuery",
- "graphql.operation.query": query,
- }
-
- @validate_transaction_metrics(
- txn_name,
- "GraphQL",
- scoped_metrics=_test_exception_scoped_metrics,
- rollup_metrics=_test_exception_rollup_metrics + _graphql_base_rollup_metrics,
- background_task=True,
- )
- @validate_span_events(exact_agents=_expected_exception_operation_attributes)
- @validate_span_events(exact_agents=_expected_exception_resolver_attributes)
- @validate_transaction_errors(errors=_test_runtime_error)
- @background_task()
- def _test():
- _, response = graphql_run(app, query)
- assert response["errors"]
-
- _test()
-
-
-@dt_enabled
-@pytest.mark.parametrize(
- "query,exc_class",
- [
- ("query MyQuery { missing_field }", "GraphQLError"),
- ("{ syntax_error ", "graphql.error.syntax_error:GraphQLSyntaxError"),
- ],
+@pytest.fixture(
+ scope="session", params=["sync-sync", "async-sync", "async-async", "wsgi-sync", "asgi-sync", "asgi-async"]
)
-def test_exception_in_validation(app, graphql_run, is_graphql_2, query, exc_class):
- if "syntax" in query:
- txn_name = "graphql.language.parser:parse"
- else:
- if is_graphql_2:
- txn_name = "graphql.validation.validation:validate"
- else:
- txn_name = "graphql.validation.validate:validate"
-
- # Import path differs between versions
- if exc_class == "GraphQLError":
- from graphql.error import GraphQLError
-
- exc_class = callable_name(GraphQLError)
-
- _test_exception_scoped_metrics = [
- ("GraphQL/operation/Ariadne///", 1),
- ]
- _test_exception_rollup_metrics = [
- ("Errors/all", 1),
- ("Errors/allOther", 1),
- ("Errors/OtherTransaction/GraphQL/%s" % txn_name, 1),
- ] + _test_exception_scoped_metrics
-
- # Attributes
- _expected_exception_operation_attributes = {
- "graphql.operation.type": "",
- "graphql.operation.name": "",
- "graphql.operation.query": query,
- }
-
- @validate_transaction_metrics(
- txn_name,
- "GraphQL",
- scoped_metrics=_test_exception_scoped_metrics,
- rollup_metrics=_test_exception_rollup_metrics + _graphql_base_rollup_metrics,
- background_task=True,
- )
- @validate_span_events(exact_agents=_expected_exception_operation_attributes)
- @validate_transaction_errors(errors=[exc_class])
- @background_task()
- def _test():
- _, response = graphql_run(app, query)
- assert response["errors"]
-
- _test()
-
-
-@dt_enabled
-def test_operation_metrics_and_attrs(app, graphql_run):
- operation_metrics = [("GraphQL/operation/Ariadne/query/MyQuery/library", 1)]
- operation_attrs = {
- "graphql.operation.type": "query",
- "graphql.operation.name": "MyQuery",
- }
-
- @validate_transaction_metrics(
- "query/MyQuery/library",
- "GraphQL",
- scoped_metrics=operation_metrics,
- rollup_metrics=operation_metrics + _graphql_base_rollup_metrics,
- background_task=True,
- )
- # Span count 16: Transaction, Operation, and 7 Resolvers and Resolver functions
- # library, library.name, library.book
- # library.book.name and library.book.id for each book resolved (in this case 2)
- @validate_span_events(count=16)
- @validate_span_events(exact_agents=operation_attrs)
- @background_task()
- def _test():
- ok, response = graphql_run(app, "query MyQuery { library(index: 0) { branch, book { id, name } } }")
- assert ok and not response.get("errors")
-
- _test()
-
-
-@dt_enabled
-def test_field_resolver_metrics_and_attrs(app, graphql_run):
- field_resolver_metrics = [("GraphQL/resolve/Ariadne/hello", 1)]
- graphql_attrs = {
- "graphql.field.name": "hello",
- "graphql.field.parentType": "Query",
- "graphql.field.path": "hello",
- "graphql.field.returnType": "String",
- }
-
- @validate_transaction_metrics(
- "query//hello",
- "GraphQL",
- scoped_metrics=field_resolver_metrics,
- rollup_metrics=field_resolver_metrics + _graphql_base_rollup_metrics,
- background_task=True,
- )
- # Span count 4: Transaction, Operation, and 1 Resolver and Resolver function
- @validate_span_events(count=4)
- @validate_span_events(exact_agents=graphql_attrs)
- @background_task()
- def _test():
- ok, response = graphql_run(app, "{ hello }")
- assert ok and not response.get("errors")
- assert "Hello!" in str(response["data"])
-
- _test()
-
-
-_test_queries = [
- ("{ hello }", "{ hello }"), # Basic query extraction
- ("{ error }", "{ error }"), # Extract query on field error
- ("{ library(index: 0) { branch } }", "{ library(index: ?) { branch } }"), # Integers
- ('{ echo(echo: "123") }', "{ echo(echo: ?) }"), # Strings with numerics
- ('{ echo(echo: "test") }', "{ echo(echo: ?) }"), # Strings
- ('{ TestEcho: echo(echo: "test") }', "{ TestEcho: echo(echo: ?) }"), # Aliases
- ('{ TestEcho: echo(echo: "test") }', "{ TestEcho: echo(echo: ?) }"), # Variables
- ( # Fragments
- '{ ...MyFragment } fragment MyFragment on Query { echo(echo: "test") }',
- "{ ...MyFragment } fragment MyFragment on Query { echo(echo: ?) }",
- ),
-]
-
-
-@dt_enabled
-@pytest.mark.parametrize("query,obfuscated", _test_queries)
-def test_query_obfuscation(app, graphql_run, query, obfuscated):
- graphql_attrs = {"graphql.operation.query": obfuscated}
-
- @validate_span_events(exact_agents=graphql_attrs)
- @background_task()
- def _test():
- ok, response = graphql_run(app, query)
- if not isinstance(query, str) or "error" not in query:
- assert ok and not response.get("errors")
-
- _test()
-
-
-_test_queries = [
- ("{ hello }", "/hello"), # Basic query
- ("{ error }", "/error"), # Extract deepest path on field error
- ('{ echo(echo: "test") }', "/echo"), # Fields with arguments
- (
- "{ library(index: 0) { branch, book { isbn branch } } }",
- "/library",
- ), # Complex Example, 1 level
- (
- "{ library(index: 0) { book { author { first_name }} } }",
- "/library.book.author.first_name",
- ), # Complex Example, 2 levels
- ("{ library(index: 0) { id, book { name } } }", "/library.book.name"), # Filtering
- ('{ TestEcho: echo(echo: "test") }', "/echo"), # Aliases
- (
- '{ search(contains: "A") { __typename ... on Book { name } } }',
- "/search.name",
- ), # InlineFragment
- (
- '{ hello echo(echo: "test") }',
- "",
- ), # Multiple root selections. (need to decide on final behavior)
- # FragmentSpread
- (
- "{ library(index: 0) { book { ...MyFragment } } } fragment MyFragment on Book { name id }", # Fragment filtering
- "/library.book.name",
- ),
- (
- "{ library(index: 0) { book { ...MyFragment } } } fragment MyFragment on Book { author { first_name } }",
- "/library.book.author.first_name",
- ),
- (
- "{ library(index: 0) { book { ...MyFragment } magazine { ...MagFragment } } } fragment MyFragment on Book { author { first_name } } fragment MagFragment on Magazine { name }",
- "/library",
- ),
-]
-
-
-@dt_enabled
-@pytest.mark.parametrize("query,expected_path", _test_queries)
-def test_deepest_unique_path(app, graphql_run, query, expected_path):
- if expected_path == "/error":
- txn_name = "_target_application:resolve_error"
- else:
- txn_name = "query/%s" % expected_path
-
- @validate_transaction_metrics(
- txn_name,
- "GraphQL",
- background_task=True,
- )
- @background_task()
- def _test():
- ok, response = graphql_run(app, query)
- if "error" not in query:
- assert ok and not response.get("errors")
-
- _test()
-
+def target_application(request):
+ from ._target_application import target_application
-@pytest.mark.parametrize("capture_introspection_setting", (True, False))
-def test_introspection_transactions(app, graphql_run, capture_introspection_setting):
- txn_ct = 1 if capture_introspection_setting else 0
+ target_application = target_application[request.param]
- @override_application_settings(
- {"instrumentation.graphql.capture_introspection_queries": capture_introspection_setting}
- )
- @validate_transaction_count(txn_ct)
- @background_task()
- def _test():
- ok, response = graphql_run(app, "{ __schema { types { name } } }")
- assert ok and not response.get("errors")
+ param = request.param.split("-")
+ is_background = param[0] not in {"wsgi", "asgi"}
+ schema_type = param[1]
+ extra_spans = 4 if param[0] == "wsgi" else 0
- _test()
+ assert ARIADNE_VERSION is not None
+ return "Ariadne", ARIADNE_VERSION, target_application, is_background, schema_type, extra_spans
diff --git a/tests/framework_ariadne/test_application_async.py b/tests/framework_ariadne/test_application_async.py
deleted file mode 100644
index ada34ffade..0000000000
--- a/tests/framework_ariadne/test_application_async.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# Copyright 2010 New Relic, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import asyncio
-
-import pytest
-from testing_support.fixtures import dt_enabled
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_span_events import validate_span_events
-
-from newrelic.api.background_task import background_task
-
-
-@pytest.fixture(scope="session")
-def graphql_run_async():
- """Wrapper function to simulate framework_graphql test behavior."""
-
- def execute(schema, query, *args, **kwargs):
- from ariadne import graphql
-
- return graphql(schema, {"query": query}, *args, **kwargs)
-
- return execute
-
-
-@dt_enabled
-def test_query_and_mutation_async(app, graphql_run_async):
- from graphql import __version__ as version
-
- FRAMEWORK_METRICS = [
- ("Python/Framework/Ariadne/None", 1),
- ("Python/Framework/GraphQL/%s" % version, 1),
- ]
- _test_mutation_scoped_metrics = [
- ("GraphQL/resolve/Ariadne/storage", 1),
- ("GraphQL/resolve/Ariadne/storage_add", 1),
- ("GraphQL/operation/Ariadne/query//storage", 1),
- ("GraphQL/operation/Ariadne/mutation//storage_add.string", 1),
- ]
- _test_mutation_unscoped_metrics = [
- ("OtherTransaction/all", 1),
- ("GraphQL/all", 2),
- ("GraphQL/Ariadne/all", 2),
- ("GraphQL/allOther", 2),
- ("GraphQL/Ariadne/allOther", 2),
- ] + _test_mutation_scoped_metrics
-
- _expected_mutation_operation_attributes = {
- "graphql.operation.type": "mutation",
- "graphql.operation.name": "",
- }
- _expected_mutation_resolver_attributes = {
- "graphql.field.name": "storage_add",
- "graphql.field.parentType": "Mutation",
- "graphql.field.path": "storage_add",
- "graphql.field.returnType": "StorageAdd",
- }
- _expected_query_operation_attributes = {
- "graphql.operation.type": "query",
- "graphql.operation.name": "",
- }
- _expected_query_resolver_attributes = {
- "graphql.field.name": "storage",
- "graphql.field.parentType": "Query",
- "graphql.field.path": "storage",
- "graphql.field.returnType": "[String]",
- }
-
- @validate_transaction_metrics(
- "query//storage",
- "GraphQL",
- scoped_metrics=_test_mutation_scoped_metrics,
- rollup_metrics=_test_mutation_unscoped_metrics + FRAMEWORK_METRICS,
- background_task=True,
- )
- @validate_span_events(exact_agents=_expected_mutation_operation_attributes)
- @validate_span_events(exact_agents=_expected_mutation_resolver_attributes)
- @validate_span_events(exact_agents=_expected_query_operation_attributes)
- @validate_span_events(exact_agents=_expected_query_resolver_attributes)
- @background_task()
- def _test():
- async def coro():
- ok, response = await graphql_run_async(app, 'mutation { storage_add(string: "abc") { string } }')
- assert ok and not response.get("errors")
- ok, response = await graphql_run_async(app, "query { storage }")
- assert ok and not response.get("errors")
-
- # These are separate assertions because pypy stores 'abc' as a unicode string while other Python versions do not
- assert "storage" in str(response.get("data"))
- assert "abc" in str(response.get("data"))
-
- loop = asyncio.new_event_loop()
- loop.run_until_complete(coro())
-
- _test()
diff --git a/tests/framework_ariadne/test_asgi.py b/tests/framework_ariadne/test_asgi.py
deleted file mode 100644
index 861f2aa932..0000000000
--- a/tests/framework_ariadne/test_asgi.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# Copyright 2010 New Relic, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-
-import pytest
-from testing_support.asgi_testing import AsgiTest
-from testing_support.fixtures import dt_enabled
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_span_events import validate_span_events
-
-
-@pytest.fixture(scope="session")
-def graphql_asgi_run():
- """Wrapper function to simulate framework_graphql test behavior."""
- from _target_application import _target_asgi_application
-
- app = AsgiTest(_target_asgi_application)
-
- def execute(query):
- return app.make_request(
- "POST", "/", headers={"Content-Type": "application/json"}, body=json.dumps({"query": query})
- )
-
- return execute
-
-
-@dt_enabled
-def test_query_and_mutation_asgi(graphql_asgi_run):
- from graphql import __version__ as version
-
- FRAMEWORK_METRICS = [
- ("Python/Framework/Ariadne/None", 1),
- ("Python/Framework/GraphQL/%s" % version, 1),
- ]
- _test_mutation_scoped_metrics = [
- ("GraphQL/resolve/Ariadne/storage_add", 1),
- ("GraphQL/operation/Ariadne/mutation//storage_add.string", 1),
- ]
- _test_query_scoped_metrics = [
- ("GraphQL/resolve/Ariadne/storage", 1),
- ("GraphQL/operation/Ariadne/query//storage", 1),
- ]
- _test_unscoped_metrics = [
- ("WebTransaction", 1),
- ("GraphQL/all", 1),
- ("GraphQL/Ariadne/all", 1),
- ("GraphQL/allWeb", 1),
- ("GraphQL/Ariadne/allWeb", 1),
- ]
- _test_mutation_unscoped_metrics = _test_unscoped_metrics + _test_mutation_scoped_metrics
- _test_query_unscoped_metrics = _test_unscoped_metrics + _test_query_scoped_metrics
-
- _expected_mutation_operation_attributes = {
- "graphql.operation.type": "mutation",
- "graphql.operation.name": "",
- }
- _expected_mutation_resolver_attributes = {
- "graphql.field.name": "storage_add",
- "graphql.field.parentType": "Mutation",
- "graphql.field.path": "storage_add",
- "graphql.field.returnType": "StorageAdd",
- }
- _expected_query_operation_attributes = {
- "graphql.operation.type": "query",
- "graphql.operation.name": "",
- }
- _expected_query_resolver_attributes = {
- "graphql.field.name": "storage",
- "graphql.field.parentType": "Query",
- "graphql.field.path": "storage",
- "graphql.field.returnType": "[String]",
- }
-
- @validate_transaction_metrics(
- "query//storage",
- "GraphQL",
- scoped_metrics=_test_query_scoped_metrics,
- rollup_metrics=_test_query_unscoped_metrics + FRAMEWORK_METRICS,
- )
- @validate_transaction_metrics(
- "mutation//storage_add.string",
- "GraphQL",
- scoped_metrics=_test_mutation_scoped_metrics,
- rollup_metrics=_test_mutation_unscoped_metrics + FRAMEWORK_METRICS,
- index=-2,
- )
- @validate_span_events(exact_agents=_expected_mutation_operation_attributes, index=-2)
- @validate_span_events(exact_agents=_expected_mutation_resolver_attributes, index=-2)
- @validate_span_events(exact_agents=_expected_query_operation_attributes)
- @validate_span_events(exact_agents=_expected_query_resolver_attributes)
- def _test():
- response = graphql_asgi_run('mutation { storage_add(string: "abc") { string } }')
- assert response.status == 200
- response = json.loads(response.body.decode("utf-8"))
- assert not response.get("errors")
-
- response = graphql_asgi_run("query { storage }")
- assert response.status == 200
- response = json.loads(response.body.decode("utf-8"))
- assert not response.get("errors")
-
- # These are separate assertions because pypy stores 'abc' as a unicode string while other Python versions do not
- assert "storage" in str(response.get("data"))
- assert "abc" in str(response.get("data"))
-
- _test()
diff --git a/tests/framework_ariadne/test_wsgi.py b/tests/framework_ariadne/test_wsgi.py
deleted file mode 100644
index 9ce2373d47..0000000000
--- a/tests/framework_ariadne/test_wsgi.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright 2010 New Relic, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import pytest
-import webtest
-from testing_support.fixtures import dt_enabled
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from testing_support.validators.validate_span_events import validate_span_events
-
-
-@pytest.fixture(scope="session")
-def graphql_wsgi_run():
- """Wrapper function to simulate framework_graphql test behavior."""
- from _target_application import _target_wsgi_application
-
- app = webtest.TestApp(_target_wsgi_application)
-
- def execute(query):
- return app.post_json("/", {"query": query})
-
- return execute
-
-
-@dt_enabled
-def test_query_and_mutation_wsgi(graphql_wsgi_run):
- from graphql import __version__ as version
-
- FRAMEWORK_METRICS = [
- ("Python/Framework/Ariadne/None", 1),
- ("Python/Framework/GraphQL/%s" % version, 1),
- ]
- _test_mutation_scoped_metrics = [
- ("GraphQL/resolve/Ariadne/storage_add", 1),
- ("GraphQL/operation/Ariadne/mutation//storage_add.string", 1),
- ]
- _test_query_scoped_metrics = [
- ("GraphQL/resolve/Ariadne/storage", 1),
- ("GraphQL/operation/Ariadne/query//storage", 1),
- ]
- _test_unscoped_metrics = [
- ("WebTransaction", 1),
- ("Python/WSGI/Response", 1),
- ("GraphQL/all", 1),
- ("GraphQL/Ariadne/all", 1),
- ("GraphQL/allWeb", 1),
- ("GraphQL/Ariadne/allWeb", 1),
- ]
- _test_mutation_unscoped_metrics = _test_unscoped_metrics + _test_mutation_scoped_metrics
- _test_query_unscoped_metrics = _test_unscoped_metrics + _test_query_scoped_metrics
-
- _expected_mutation_operation_attributes = {
- "graphql.operation.type": "mutation",
- "graphql.operation.name": "",
- }
- _expected_mutation_resolver_attributes = {
- "graphql.field.name": "storage_add",
- "graphql.field.parentType": "Mutation",
- "graphql.field.path": "storage_add",
- "graphql.field.returnType": "StorageAdd",
- }
- _expected_query_operation_attributes = {
- "graphql.operation.type": "query",
- "graphql.operation.name": "",
- }
- _expected_query_resolver_attributes = {
- "graphql.field.name": "storage",
- "graphql.field.parentType": "Query",
- "graphql.field.path": "storage",
- "graphql.field.returnType": "[String]",
- }
-
- @validate_transaction_metrics(
- "query//storage",
- "GraphQL",
- scoped_metrics=_test_query_scoped_metrics,
- rollup_metrics=_test_query_unscoped_metrics + FRAMEWORK_METRICS,
- )
- @validate_transaction_metrics(
- "mutation//storage_add.string",
- "GraphQL",
- scoped_metrics=_test_mutation_scoped_metrics,
- rollup_metrics=_test_mutation_unscoped_metrics + FRAMEWORK_METRICS,
- index=-2,
- )
- @validate_span_events(exact_agents=_expected_mutation_operation_attributes, index=-2)
- @validate_span_events(exact_agents=_expected_mutation_resolver_attributes, index=-2)
- @validate_span_events(exact_agents=_expected_query_operation_attributes)
- @validate_span_events(exact_agents=_expected_query_resolver_attributes)
- def _test():
- response = graphql_wsgi_run('mutation { storage_add(string: "abc") { string } }')
- assert response.status_code == 200
- response = response.json_body
- assert not response.get("errors")
-
- response = graphql_wsgi_run("query { storage }")
- assert response.status_code == 200
- response = response.json_body
- assert not response.get("errors")
-
- # These are separate assertions because pypy stores 'abc' as a unicode string while other Python versions do not
- assert "storage" in str(response.get("data"))
- assert "abc" in str(response.get("data"))
-
- _test()
diff --git a/tests/framework_bottle/conftest.py b/tests/framework_bottle/conftest.py
index 095a3331f3..1e20551a52 100644
--- a/tests/framework_bottle/conftest.py
+++ b/tests/framework_bottle/conftest.py
@@ -13,23 +13,31 @@
# limitations under the License.
import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
-
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "security.agent.enabled": True,
+ "security.enabled": True,
+ "security.mode": "IAST",
+ "security.validator_service_url": "wss://csec-staging.nr-data.net"
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (framework_bottle)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (framework_bottle)", default_settings=_default_settings
+)
-@pytest.fixture(scope='function')
+
+@pytest.fixture(scope="function")
def target_application():
import _target_application
+
return _target_application.target_application
diff --git a/tests/framework_bottle/test_application.py b/tests/framework_bottle/test_application.py
index 28619d5eb5..32db9a64a5 100644
--- a/tests/framework_bottle/test_application.py
+++ b/tests/framework_bottle/test_application.py
@@ -12,218 +12,233 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
import base64
+import pytest
+import webtest
+from bottle import __version__ as version
from testing_support.fixtures import (
+ override_application_settings,
override_ignore_status_codes,
- override_application_settings)
-from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics
-from newrelic.packages import six
-from testing_support.validators.validate_code_level_metrics import validate_code_level_metrics
-from testing_support.validators.validate_transaction_errors import validate_transaction_errors
-
-import webtest
+)
+from testing_support.validators.validate_code_level_metrics import (
+ validate_code_level_metrics,
+)
+from testing_support.validators.validate_transaction_errors import (
+ validate_transaction_errors,
+)
+from testing_support.validators.validate_transaction_metrics import (
+ validate_transaction_metrics,
+)
-from bottle import __version__ as version
+from newrelic.common.package_version_utils import get_package_version_tuple
-version = [int(x) for x in version.split('-')[0].split('.')]
+version = list(get_package_version_tuple("bottle"))
if len(version) == 2:
version.append(0)
version = tuple(version)
+assert version > (0, 1), "version information not found"
+
+version_metrics = [(f"Python/Framework/Bottle/{'.'.join(str(v) for v in version)}", 1)]
-requires_auth_basic = pytest.mark.skipif(version < (0, 9, 0),
- reason="Bottle only added auth_basic in 0.9.0.")
-requires_plugins = pytest.mark.skipif(version < (0, 9, 0),
- reason="Bottle only added auth_basic in 0.9.0.")
+requires_auth_basic = pytest.mark.skipif(version < (0, 9, 0), reason="Bottle only added auth_basic in 0.9.0.")
+requires_plugins = pytest.mark.skipif(version < (0, 9, 0), reason="Bottle only added auth_basic in 0.9.0.")
_test_application_index_scoped_metrics = [
- ('Python/WSGI/Application', 1),
- ('Python/WSGI/Response', 1),
- ('Python/WSGI/Finalize', 1),
- ('Function/_target_application:index_page', 1)]
+ ("Python/WSGI/Application", 1),
+ ("Python/WSGI/Response", 1),
+ ("Python/WSGI/Finalize", 1),
+ ("Function/_target_application:index_page", 1),
+]
if version >= (0, 9, 0):
- _test_application_index_scoped_metrics.extend([
- ('Function/bottle:Bottle.wsgi', 1)])
+ _test_application_index_scoped_metrics.extend([("Function/bottle:Bottle.wsgi", 1)])
else:
- _test_application_index_scoped_metrics.extend([
- ('Function/bottle:Bottle.__call__', 1)])
+ _test_application_index_scoped_metrics.extend([("Function/bottle:Bottle.__call__", 1)])
+
+_test_application_index_custom_metrics = version_metrics.copy()
-_test_application_index_custom_metrics = [
- ('Python/Framework/Bottle/%s.%s.%s' % version, 1)]
@validate_code_level_metrics("_target_application", "index_page")
@validate_transaction_errors(errors=[])
-@validate_transaction_metrics('_target_application:index_page',
- scoped_metrics=_test_application_index_scoped_metrics,
- custom_metrics=_test_application_index_custom_metrics)
+@validate_transaction_metrics(
+ "_target_application:index_page",
+ scoped_metrics=_test_application_index_scoped_metrics,
+ custom_metrics=_test_application_index_custom_metrics,
+)
def test_application_index(target_application):
- response = target_application.get('/index')
- response.mustcontain('INDEX RESPONSE')
+ response = target_application.get("/index")
+ response.mustcontain("INDEX RESPONSE")
+
_test_application_error_scoped_metrics = [
- ('Python/WSGI/Application', 1),
- ('Python/WSGI/Response', 1),
- ('Python/WSGI/Finalize', 1),
- ('Function/_target_application:error_page', 1)]
+ ("Python/WSGI/Application", 1),
+ ("Python/WSGI/Response", 1),
+ ("Python/WSGI/Finalize", 1),
+ ("Function/_target_application:error_page", 1),
+]
if version >= (0, 9, 0):
- _test_application_error_scoped_metrics.extend([
- ('Function/bottle:Bottle.wsgi', 1)])
+ _test_application_error_scoped_metrics.extend([("Function/bottle:Bottle.wsgi", 1)])
else:
- _test_application_error_scoped_metrics.extend([
- ('Function/bottle:Bottle.__call__', 1)])
+ _test_application_error_scoped_metrics.extend([("Function/bottle:Bottle.__call__", 1)])
-_test_application_error_custom_metrics = [
- ('Python/Framework/Bottle/%s.%s.%s' % version, 1)]
+_test_application_error_custom_metrics = version_metrics.copy()
+_test_application_error_errors = ["builtins:RuntimeError"]
-if six.PY3:
- _test_application_error_errors = ['builtins:RuntimeError']
-else:
- _test_application_error_errors = ['exceptions:RuntimeError']
@validate_code_level_metrics("_target_application", "error_page")
@validate_transaction_errors(errors=_test_application_error_errors)
-@validate_transaction_metrics('_target_application:error_page',
- scoped_metrics=_test_application_error_scoped_metrics,
- custom_metrics=_test_application_error_custom_metrics)
+@validate_transaction_metrics(
+ "_target_application:error_page",
+ scoped_metrics=_test_application_error_scoped_metrics,
+ custom_metrics=_test_application_error_custom_metrics,
+)
def test_application_error(target_application):
- response = target_application.get('/error', status=500, expect_errors=True)
+ response = target_application.get("/error", status=500, expect_errors=True)
+
_test_application_not_found_scoped_metrics = [
- ('Python/WSGI/Application', 1),
- ('Python/WSGI/Response', 1),
- ('Python/WSGI/Finalize', 1),
- ('Function/_target_application:error404_page', 1)]
+ ("Python/WSGI/Application", 1),
+ ("Python/WSGI/Response", 1),
+ ("Python/WSGI/Finalize", 1),
+ ("Function/_target_application:error404_page", 1),
+]
if version >= (0, 9, 0):
- _test_application_not_found_scoped_metrics.extend([
- ('Function/bottle:Bottle.wsgi', 1)])
+ _test_application_not_found_scoped_metrics.extend([("Function/bottle:Bottle.wsgi", 1)])
else:
- _test_application_not_found_scoped_metrics.extend([
- ('Function/bottle:Bottle.__call__', 1)])
+ _test_application_not_found_scoped_metrics.extend([("Function/bottle:Bottle.__call__", 1)])
+
+_test_application_not_found_custom_metrics = version_metrics.copy()
-_test_application_not_found_custom_metrics = [
- ('Python/Framework/Bottle/%s.%s.%s' % version, 1)]
@validate_code_level_metrics("_target_application", "error404_page")
@validate_transaction_errors(errors=[])
-@validate_transaction_metrics('_target_application:error404_page',
- scoped_metrics=_test_application_not_found_scoped_metrics,
- custom_metrics=_test_application_not_found_custom_metrics)
+@validate_transaction_metrics(
+ "_target_application:error404_page",
+ scoped_metrics=_test_application_not_found_scoped_metrics,
+ custom_metrics=_test_application_not_found_custom_metrics,
+)
def test_application_not_found(target_application):
- response = target_application.get('/missing', status=404)
- response.mustcontain('NOT FOUND')
+ response = target_application.get("/missing", status=404)
+ response.mustcontain("NOT FOUND")
+
_test_application_auth_basic_fail_scoped_metrics = [
- ('Python/WSGI/Application', 1),
- ('Python/WSGI/Response', 1),
- ('Python/WSGI/Finalize', 1),
- ('Function/_target_application:auth_basic_page', 1)]
+ ("Python/WSGI/Application", 1),
+ ("Python/WSGI/Response", 1),
+ ("Python/WSGI/Finalize", 1),
+ ("Function/_target_application:auth_basic_page", 1),
+]
if version >= (0, 9, 0):
- _test_application_auth_basic_fail_scoped_metrics.extend([
- ('Function/bottle:Bottle.wsgi', 1)])
+ _test_application_auth_basic_fail_scoped_metrics.extend([("Function/bottle:Bottle.wsgi", 1)])
else:
- _test_application_auth_basic_fail_scoped_metrics.extend([
- ('Function/bottle:Bottle.__call__', 1)])
+ _test_application_auth_basic_fail_scoped_metrics.extend([("Function/bottle:Bottle.__call__", 1)])
+
+_test_application_auth_basic_fail_custom_metrics = version_metrics.copy()
-_test_application_auth_basic_fail_custom_metrics = [
- ('Python/Framework/Bottle/%s.%s.%s' % version, 1)]
@requires_auth_basic
@validate_code_level_metrics("_target_application", "auth_basic_page")
@validate_transaction_errors(errors=[])
-@validate_transaction_metrics('_target_application:auth_basic_page',
- scoped_metrics=_test_application_auth_basic_fail_scoped_metrics,
- custom_metrics=_test_application_auth_basic_fail_custom_metrics)
+@validate_transaction_metrics(
+ "_target_application:auth_basic_page",
+ scoped_metrics=_test_application_auth_basic_fail_scoped_metrics,
+ custom_metrics=_test_application_auth_basic_fail_custom_metrics,
+)
def test_application_auth_basic_fail(target_application):
- response = target_application.get('/auth', status=401)
+ response = target_application.get("/auth", status=401)
+
_test_application_auth_basic_okay_scoped_metrics = [
- ('Python/WSGI/Application', 1),
- ('Python/WSGI/Response', 1),
- ('Python/WSGI/Finalize', 1),
- ('Function/_target_application:auth_basic_page', 1)]
+ ("Python/WSGI/Application", 1),
+ ("Python/WSGI/Response", 1),
+ ("Python/WSGI/Finalize", 1),
+ ("Function/_target_application:auth_basic_page", 1),
+]
if version >= (0, 9, 0):
- _test_application_auth_basic_okay_scoped_metrics.extend([
- ('Function/bottle:Bottle.wsgi', 1)])
+ _test_application_auth_basic_okay_scoped_metrics.extend([("Function/bottle:Bottle.wsgi", 1)])
else:
- _test_application_auth_basic_okay_scoped_metrics.extend([
- ('Function/bottle:Bottle.__call__', 1)])
+ _test_application_auth_basic_okay_scoped_metrics.extend([("Function/bottle:Bottle.__call__", 1)])
+
+_test_application_auth_basic_okay_custom_metrics = version_metrics.copy()
-_test_application_auth_basic_okay_custom_metrics = [
- ('Python/Framework/Bottle/%s.%s.%s' % version, 1)]
@requires_auth_basic
@validate_code_level_metrics("_target_application", "auth_basic_page")
@validate_transaction_errors(errors=[])
-@validate_transaction_metrics('_target_application:auth_basic_page',
- scoped_metrics=_test_application_auth_basic_okay_scoped_metrics,
- custom_metrics=_test_application_auth_basic_okay_custom_metrics)
+@validate_transaction_metrics(
+ "_target_application:auth_basic_page",
+ scoped_metrics=_test_application_auth_basic_okay_scoped_metrics,
+ custom_metrics=_test_application_auth_basic_okay_custom_metrics,
+)
def test_application_auth_basic_okay(target_application):
- authorization_value = base64.b64encode(b'user:password')
- if six.PY3:
- authorization_value = authorization_value.decode('Latin-1')
- environ = { 'HTTP_AUTHORIZATION': 'Basic ' + authorization_value }
- response = target_application.get('/auth', extra_environ=environ)
- response.mustcontain('AUTH OKAY')
+ authorization_value = base64.b64encode(b"user:password").decode("Latin-1")
+ environ = {"HTTP_AUTHORIZATION": f"Basic {authorization_value}"}
+ response = target_application.get("/auth", extra_environ=environ)
+ response.mustcontain("AUTH OKAY")
+
_test_application_plugin_error_scoped_metrics = [
- ('Python/WSGI/Application', 1),
- ('Python/WSGI/Response', 1),
- ('Python/WSGI/Finalize', 1),
- ('Function/_target_application:plugin_error_page', 1)]
+ ("Python/WSGI/Application", 1),
+ ("Python/WSGI/Response", 1),
+ ("Python/WSGI/Finalize", 1),
+ ("Function/_target_application:plugin_error_page", 1),
+]
if version >= (0, 9, 0):
- _test_application_plugin_error_scoped_metrics.extend([
- ('Function/bottle:Bottle.wsgi', 1)])
+ _test_application_plugin_error_scoped_metrics.extend([("Function/bottle:Bottle.wsgi", 1)])
else:
- _test_application_plugin_error_scoped_metrics.extend([
- ('Function/bottle:Bottle.__call__', 1)])
+ _test_application_plugin_error_scoped_metrics.extend([("Function/bottle:Bottle.__call__", 1)])
+
+_test_application_plugin_error_custom_metrics = version_metrics.copy()
-_test_application_plugin_error_custom_metrics = [
- ('Python/Framework/Bottle/%s.%s.%s' % version, 1)]
@requires_plugins
@validate_code_level_metrics("_target_application", "plugin_error_page")
@validate_transaction_errors(errors=[])
-@validate_transaction_metrics('_target_application:plugin_error_page',
- scoped_metrics=_test_application_plugin_error_scoped_metrics,
- custom_metrics=_test_application_plugin_error_custom_metrics)
+@validate_transaction_metrics(
+ "_target_application:plugin_error_page",
+ scoped_metrics=_test_application_plugin_error_scoped_metrics,
+ custom_metrics=_test_application_plugin_error_custom_metrics,
+)
@override_ignore_status_codes([403])
def test_application_plugin_error_ignore(target_application):
- response = target_application.get('/plugin_error', status=403,
- expect_errors=True)
+ response = target_application.get("/plugin_error", status=403, expect_errors=True)
+
@requires_plugins
@validate_code_level_metrics("_target_application", "plugin_error_page")
-@validate_transaction_errors(errors=['bottle:HTTPError'])
-@validate_transaction_metrics('_target_application:plugin_error_page',
- scoped_metrics=_test_application_plugin_error_scoped_metrics,
- custom_metrics=_test_application_plugin_error_custom_metrics)
+@validate_transaction_errors(errors=["bottle:HTTPError"])
+@validate_transaction_metrics(
+ "_target_application:plugin_error_page",
+ scoped_metrics=_test_application_plugin_error_scoped_metrics,
+ custom_metrics=_test_application_plugin_error_custom_metrics,
+)
def test_application_plugin_error_capture(target_application):
import newrelic.agent
- response = target_application.get('/plugin_error', status=403,
- expect_errors=True)
+
+ response = target_application.get("/plugin_error", status=403, expect_errors=True)
+
_test_html_insertion_settings = {
- 'browser_monitoring.enabled': True,
- 'browser_monitoring.auto_instrument': True,
- 'js_agent_loader': u'',
+ "browser_monitoring.enabled": True,
+ "browser_monitoring.auto_instrument": True,
+ "js_agent_loader": "",
}
+
@override_application_settings(_test_html_insertion_settings)
def test_html_insertion(target_application):
- response = target_application.get('/html_insertion')
+ response = target_application.get("/html_insertion")
# The 'NREUM HEADER' value comes from our override for the header.
# The 'NREUM.info' value comes from the programmatically generated
- # footer added by the agent.
-
- response.mustcontain('NREUM HEADER', 'NREUM.info')
+ # header added by the agent.
+ response.mustcontain("NREUM HEADER", "NREUM.info")
diff --git a/tests/framework_cherrypy/conftest.py b/tests/framework_cherrypy/conftest.py
index bc730bb1fa..181bbcb008 100644
--- a/tests/framework_cherrypy/conftest.py
+++ b/tests/framework_cherrypy/conftest.py
@@ -12,19 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (framework_cherrypy)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (framework_cherrypy)", default_settings=_default_settings
+)
diff --git a/tests/framework_cherrypy/test_application.py b/tests/framework_cherrypy/test_application.py
index 39f8b5c16d..53e20ad77d 100644
--- a/tests/framework_cherrypy/test_application.py
+++ b/tests/framework_cherrypy/test_application.py
@@ -12,31 +12,32 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import cherrypy
import pytest
import webtest
-
-from newrelic.packages import six
-
from testing_support.fixtures import (
- override_application_settings,
- override_ignore_status_codes)
-from testing_support.validators.validate_code_level_metrics import validate_code_level_metrics
-from testing_support.validators.validate_transaction_errors import validate_transaction_errors
-
-import cherrypy
+ override_application_settings,
+ override_ignore_status_codes,
+)
+from testing_support.validators.validate_code_level_metrics import (
+ validate_code_level_metrics,
+)
+from testing_support.validators.validate_transaction_errors import (
+ validate_transaction_errors,
+)
-CHERRYPY_VERSION = tuple(int(v) for v in cherrypy.__version__.split('.'))
+CHERRYPY_VERSION = tuple(int(v) for v in cherrypy.__version__.split("."))
-class Application(object):
+class Application():
@cherrypy.expose
def index(self):
- return 'INDEX RESPONSE'
+ return "INDEX RESPONSE"
@cherrypy.expose
def error(self):
- raise RuntimeError('error')
+ raise RuntimeError("error")
@cherrypy.expose
def not_found(self):
@@ -48,35 +49,37 @@ def not_found_as_http_error(self):
@cherrypy.expose
def not_found_as_str_http_error(self):
- raise cherrypy.HTTPError('404 Not Found')
+ raise cherrypy.HTTPError("404 Not Found")
@cherrypy.expose
def bad_http_error(self):
# this will raise HTTPError with status code 500 because 10 is not a
# valid status code
- raise cherrypy.HTTPError('10 Invalid status code')
+ raise cherrypy.HTTPError("10 Invalid status code")
@cherrypy.expose
def internal_redirect(self):
- raise cherrypy.InternalRedirect('/')
+ raise cherrypy.InternalRedirect("/")
@cherrypy.expose
def external_redirect(self):
- raise cherrypy.HTTPRedirect('/')
+ raise cherrypy.HTTPRedirect("/")
@cherrypy.expose
def upload_files(self, files):
- return 'UPLOAD FILES RESPONSE'
+ return "UPLOAD FILES RESPONSE"
@cherrypy.expose
def encode_multipart(self, field, files):
- return 'ENCODE MULTIPART RESPONSE'
+ return "ENCODE MULTIPART RESPONSE"
@cherrypy.expose
def html_insertion(self):
- return ('Some header'
- 'My First Heading
My first paragraph.
'
- '')
+ return (
+ "Some header"
+ "My First Heading
My first paragraph.
"
+ ""
+ )
application = cherrypy.Application(Application())
@@ -86,99 +89,91 @@ def html_insertion(self):
@validate_code_level_metrics("test_application.Application", "index")
@validate_transaction_errors(errors=[])
def test_application_index():
- response = test_application.get('')
- response.mustcontain('INDEX RESPONSE')
+ response = test_application.get("")
+ response.mustcontain("INDEX RESPONSE")
@validate_transaction_errors(errors=[])
def test_application_index_agent_disabled():
- environ = {'newrelic.enabled': False}
- response = test_application.get('', extra_environ=environ)
- response.mustcontain('INDEX RESPONSE')
+ environ = {"newrelic.enabled": False}
+ response = test_application.get("", extra_environ=environ)
+ response.mustcontain("INDEX RESPONSE")
@validate_transaction_errors(errors=[])
def test_application_missing():
- test_application.get('/missing', status=404)
-
+ test_application.get("/missing", status=404)
-if six.PY3:
- _test_application_unexpected_exception_errors = ['builtins:RuntimeError']
-else:
- _test_application_unexpected_exception_errors = ['exceptions:RuntimeError']
-
-@validate_transaction_errors(
- errors=_test_application_unexpected_exception_errors)
+@validate_transaction_errors(errors=["builtins:RuntimeError"])
def test_application_unexpected_exception():
- test_application.get('/error', status=500)
+ test_application.get("/error", status=500)
@validate_transaction_errors(errors=[])
def test_application_not_found():
- test_application.get('/not_found', status=404)
+ test_application.get("/not_found", status=404)
@validate_transaction_errors(errors=[])
def test_application_not_found_as_http_error():
- test_application.get('/not_found_as_http_error', status=404)
+ test_application.get("/not_found_as_http_error", status=404)
@validate_transaction_errors(errors=[])
def test_application_internal_redirect():
- response = test_application.get('/internal_redirect')
- response.mustcontain('INDEX RESPONSE')
+ response = test_application.get("/internal_redirect")
+ response.mustcontain("INDEX RESPONSE")
@validate_transaction_errors(errors=[])
def test_application_external_redirect():
- test_application.get('/external_redirect', status=302)
+ test_application.get("/external_redirect", status=302)
@validate_transaction_errors(errors=[])
def test_application_upload_files():
- test_application.post('/upload_files', upload_files=[('files', __file__)])
+ test_application.post("/upload_files", upload_files=[("files", __file__)])
@validate_transaction_errors(errors=[])
def test_application_encode_multipart():
- content_type, body = test_application.encode_multipart(
- params=[('field', 'value')], files=[('files', __file__)])
- test_application.request('/encode_multipart', method='POST',
- content_type=content_type, body=body)
+ content_type, body = test_application.encode_multipart(params=[("field", "value")], files=[("files", __file__)])
+ test_application.request("/encode_multipart", method="POST", content_type=content_type, body=body)
_test_html_insertion_settings = {
- 'browser_monitoring.enabled': True,
- 'browser_monitoring.auto_instrument': True,
- 'js_agent_loader': u'',
+ "browser_monitoring.enabled": True,
+ "browser_monitoring.auto_instrument": True,
+ "js_agent_loader": "",
}
@override_application_settings(_test_html_insertion_settings)
def test_html_insertion():
- response = test_application.get('/html_insertion')
+ response = test_application.get("/html_insertion")
# The 'NREUM HEADER' value comes from our override for the header.
# The 'NREUM.info' value comes from the programmatically generated
- # footer added by the agent.
+ # header added by the agent.
- response.mustcontain('NREUM HEADER', 'NREUM.info')
+ response.mustcontain("NREUM HEADER", "NREUM.info")
-_error_endpoints = ['/not_found_as_http_error']
+_error_endpoints = ["/not_found_as_http_error"]
if CHERRYPY_VERSION >= (3, 2):
- _error_endpoints.extend(['/not_found_as_str_http_error',
- '/bad_http_error'])
+ _error_endpoints.extend(["/not_found_as_str_http_error", "/bad_http_error"])
-@pytest.mark.parametrize('endpoint', _error_endpoints)
-@pytest.mark.parametrize('ignore_overrides,expected_errors', [
- ([], ['cherrypy._cperror:HTTPError']),
- ([404, 500], []),
-])
+@pytest.mark.parametrize("endpoint", _error_endpoints)
+@pytest.mark.parametrize(
+ "ignore_overrides,expected_errors",
+ [
+ ([], ["cherrypy._cperror:HTTPError"]),
+ ([404, 500], []),
+ ],
+)
def test_ignore_status_code(endpoint, ignore_overrides, expected_errors):
-
@validate_transaction_errors(errors=expected_errors)
@override_ignore_status_codes(ignore_overrides)
def _test():
@@ -189,5 +184,5 @@ def _test():
@validate_transaction_errors(errors=[])
def test_ignore_status_unexpected_param():
- response = test_application.get('/?arg=1', status=404)
- response.mustcontain(no=['INDEX RESPONSE'])
+ response = test_application.get("/?arg=1", status=404)
+ response.mustcontain(no=["INDEX RESPONSE"])
diff --git a/tests/framework_cherrypy/test_dispatch.py b/tests/framework_cherrypy/test_dispatch.py
index 64dccb2146..bc756fa2ec 100644
--- a/tests/framework_cherrypy/test_dispatch.py
+++ b/tests/framework_cherrypy/test_dispatch.py
@@ -15,7 +15,6 @@
import pytest
import webtest
-from newrelic.packages import six
from testing_support.validators.validate_transaction_errors import validate_transaction_errors
@@ -27,7 +26,7 @@
requires_cherrypy32 = pytest.mark.skipif(not is_ge_cherrypy32,
reason="The dispatch mechanism was only added in CherryPy 3.2.")
-class Resource(object):
+class Resource():
def _cp_dispatch(self, vpath):
raise RuntimeError('dispatch error')
@@ -40,12 +39,8 @@ def _cp_dispatch(self, vpath):
application = cherrypy.Application(Resource(), '/', conf)
test_application = webtest.TestApp(application)
-if six.PY3:
- _test_dispatch_exception_errors = ['builtins:RuntimeError']
-else:
- _test_dispatch_exception_errors = ['exceptions:RuntimeError']
@requires_cherrypy32
-@validate_transaction_errors(errors=_test_dispatch_exception_errors)
+@validate_transaction_errors(errors=['builtins:RuntimeError'])
def test_dispatch_exception():
response = test_application.get('/sub/a/b', status=500)
diff --git a/tests/framework_cherrypy/test_resource.py b/tests/framework_cherrypy/test_resource.py
index 385d28d91e..4d6f949fb9 100644
--- a/tests/framework_cherrypy/test_resource.py
+++ b/tests/framework_cherrypy/test_resource.py
@@ -19,7 +19,7 @@
import cherrypy
-class Resource(object):
+class Resource():
exposed = True
diff --git a/tests/framework_cherrypy/test_routes.py b/tests/framework_cherrypy/test_routes.py
index 9111a29ced..464fcc7c1e 100644
--- a/tests/framework_cherrypy/test_routes.py
+++ b/tests/framework_cherrypy/test_routes.py
@@ -21,7 +21,7 @@
import cherrypy
-class EndPoint(object):
+class EndPoint():
def index(self):
return 'INDEX RESPONSE'
diff --git a/tests/framework_django/conftest.py b/tests/framework_django/conftest.py
index 8a43ef5c90..bfc90d0e08 100644
--- a/tests/framework_django/conftest.py
+++ b/tests/framework_django/conftest.py
@@ -12,21 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import pytest
-
-from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611
+from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611
+ collector_agent_registration_fixture,
+ collector_available_fixture,
+)
_default_settings = {
- 'transaction_tracer.explain_threshold': 0.0,
- 'transaction_tracer.transaction_threshold': 0.0,
- 'transaction_tracer.stack_trace_threshold': 0.0,
- 'debug.log_data_collector_payloads': True,
- 'debug.record_transaction_failure': True,
- 'debug.log_autorum_middleware': True,
- 'feature_flag': set(['django.instrumentation.inclusion-tags.r1']),
+ "package_reporting.enabled": False, # Turn off package reporting for testing as it causes slow downs.
+ "transaction_tracer.explain_threshold": 0.0,
+ "transaction_tracer.transaction_threshold": 0.0,
+ "transaction_tracer.stack_trace_threshold": 0.0,
+ "debug.log_data_collector_payloads": True,
+ "debug.record_transaction_failure": True,
+ "debug.log_autorum_middleware": True,
+ "feature_flag": set(["django.instrumentation.inclusion-tags.r1"]),
+ "security.agent.enabled": True,
+ "security.enabled": True,
+ "security.mode": "IAST",
+ "security.validator_service_url": "wss://csec-staging.nr-data.net"
}
collector_agent_registration = collector_agent_registration_fixture(
- app_name='Python Agent Test (framework_django)',
- default_settings=_default_settings)
+ app_name="Python Agent Test (framework_django)", default_settings=_default_settings
+)
diff --git a/tests/framework_django/middleware.py b/tests/framework_django/middleware.py
index 2d9e794467..0e6669ae3f 100644
--- a/tests/framework_django/middleware.py
+++ b/tests/framework_django/middleware.py
@@ -19,7 +19,7 @@ class Custom410(Exception):
pass
-class ExceptionTo410Middleware(object):
+class ExceptionTo410Middleware():
def __init__(self, get_response=None):
self.get_response = get_response
diff --git a/tests/framework_django/templates/main.html b/tests/framework_django/templates/main.html
index bcf5afda39..5de5a534a3 100644
--- a/tests/framework_django/templates/main.html
+++ b/tests/framework_django/templates/main.html
@@ -26,6 +26,5 @@
My First Heading
My first paragraph.
{% show_results %}
- {% newrelic_browser_timing_footer %}