[PAVST] Add TC_PAVST_2_10.py for URL validation (#41362)

* Add TC_PAVST_2_10.py for URL validation.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Address Geminibot reiew comments and clean up.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Restyled by autopep8

* Restyled by isort

* Address LINT errors and review comments.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Move URL validation to SDK.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Address review comments.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Update TCs for URL validation.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Pass expected cluster status.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Restyled by autopep8

* Fix Lint Error

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Updating status code to valid code

Co-author: Sambhavi<sambhavi.1@samsung.com>
Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Fix build failure

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Fix build failure

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

* Fix CI failure.

Signed-off-by: Raveendra Karu <r.karu@samsung.com>

---------

Signed-off-by: Raveendra Karu <r.karu@samsung.com>
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h
index 227b8c8..3d118a0 100644
--- a/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h
+++ b/examples/all-clusters-app/all-clusters-common/include/push-av-stream-transport-delegate-impl.h
@@ -56,8 +56,6 @@
         const uint16_t connectionID, TriggerActivationReasonEnum activationReason,
         const Optional<Structs::TransportMotionTriggerTimeControlStruct::DecodableType> & timeControl) override;
 
-    bool ValidateUrl(const std::string & url) override;
-
     Protocols::InteractionModel::Status
     ValidateBandwidthLimit(StreamUsageEnum streamUsage, const Optional<DataModel::Nullable<uint16_t>> & videoStreamId,
                            const Optional<DataModel::Nullable<uint16_t>> & audioStreamId) override;
diff --git a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp
index 9f795d9..056449e 100644
--- a/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp
+++ b/examples/all-clusters-app/all-clusters-common/src/push-av-stream-transport-delegate-impl.cpp
@@ -121,11 +121,6 @@
     return Status::Success;
 }
 
-bool PushAvStreamTransportManager::ValidateUrl(const std::string & url)
-{
-    return true;
-}
-
 Protocols::InteractionModel::Status PushAvStreamTransportManager::SelectVideoStream(StreamUsageEnum streamUsage,
                                                                                     uint16_t & videoStreamId)
 {
diff --git a/examples/camera-app/linux/include/clusters/push-av-stream-transport/push-av-stream-manager.h b/examples/camera-app/linux/include/clusters/push-av-stream-transport/push-av-stream-manager.h
index 0c787d7..b8f3bc4 100644
--- a/examples/camera-app/linux/include/clusters/push-av-stream-transport/push-av-stream-manager.h
+++ b/examples/camera-app/linux/include/clusters/push-av-stream-transport/push-av-stream-manager.h
@@ -76,8 +76,6 @@
     void SetTLSCerts(Tls::CertificateTable::BufferedClientCert & clientCertEntry,
                      Tls::CertificateTable::BufferedRootCert & rootCertEntry) override;
 
-    bool ValidateUrl(const std::string & url) override;
-
     bool ValidateStreamUsage(StreamUsageEnum streamUsage) override;
 
     bool ValidateSegmentDuration(uint16_t segmentDuration, const Optional<DataModel::Nullable<uint16_t>> & videoStreamId) override;
diff --git a/examples/camera-app/linux/src/clusters/push-av-stream-transport/push-av-stream-manager.cpp b/examples/camera-app/linux/src/clusters/push-av-stream-transport/push-av-stream-manager.cpp
index 5f5feff..935efe7 100644
--- a/examples/camera-app/linux/src/clusters/push-av-stream-transport/push-av-stream-manager.cpp
+++ b/examples/camera-app/linux/src/clusters/push-av-stream-transport/push-av-stream-manager.cpp
@@ -361,24 +361,6 @@
     return false;
 }
 
-bool PushAvStreamTransportManager::ValidateUrl(const std::string & url)
-{
-    const std::string https = "https://";
-
-    // Check minimum length and https prefix
-    if (url.size() <= https.size() || url.substr(0, https.size()) != https)
-    {
-        return false;
-    }
-
-    // Check for non-empty host
-    size_t hostStart = https.size();
-    size_t hostEnd   = url.find('/', hostStart);
-    std::string host = (hostEnd == std::string::npos) ? url.substr(hostStart) : url.substr(hostStart, hostEnd - hostStart);
-
-    return !host.empty();
-}
-
 Protocols::InteractionModel::Status PushAvStreamTransportManager::SelectVideoStream(StreamUsageEnum streamUsage,
                                                                                     uint16_t & videoStreamId)
 {
diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h
index a16c2a4..58cc890 100644
--- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h
+++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-delegate.h
@@ -130,14 +130,6 @@
         const Optional<PushAvStreamTransport::Structs::TransportMotionTriggerTimeControlStruct::Type> & timeControl) = 0;
 
     /**
-     * @brief Validates the provided URL.
-     *
-     * @param url The URL to validate
-     * @return true if URL is valid, false otherwise
-     */
-    virtual bool ValidateUrl(const std::string & url) = 0;
-
-    /**
      * @brief Validates the provided StreamUsage.
      *
      * @param streamUsage The StreamUsage to validate
diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp
index 8ebc5c7..b2a2a53 100644
--- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp
+++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.cpp
@@ -232,6 +232,86 @@
     }
 }
 
+bool PushAvStreamTransportServerLogic::ValidateUrl(const std::string & url)
+{
+    const std::string https = "https://";
+
+    // Check minimum length and https prefix
+    if (url.size() <= https.size() || url.substr(0, https.size()) != https)
+    {
+        return false;
+    }
+
+    // Check that URL does not contain fragment character '#'
+    if (url.find('#') != std::string::npos)
+    {
+        ChipLogError(Camera, "URL contains fragment character '#'");
+        return false;
+    }
+
+    // Check that URL does not contain query character '?'
+    if (url.find('?') != std::string::npos)
+    {
+        ChipLogError(Camera, "URL contains query character '?'");
+        return false;
+    }
+
+    // Check that URL ends with a forward slash '/'
+    if (url.back() != '/')
+    {
+        ChipLogError(Camera, "URL does not end with '/'");
+        return false;
+    }
+
+    // Extract host part
+    size_t hostStart = https.size();
+    size_t hostEnd   = url.find('/', hostStart);
+    // If no '/' is found after the scheme, the rest of the URL is the host.
+    // If a '/' is found, the host is the part between the scheme and the first '/'.
+    std::string host;
+    if (hostEnd == std::string::npos)
+    {
+        // This case handles URLs like "https://example.com" or "https://localhost"
+        // The host is the entire string after "https://"
+        host = url.substr(hostStart);
+    }
+    else
+    {
+        // This case handles URLs like "https://example.com/" or "https://example.com/path"
+        // The host is the part between "https://" and the first '/'
+        host = url.substr(hostStart, hostEnd - hostStart);
+    }
+    // Check for non-empty host. This check is now more robust.
+    if (host.empty())
+    {
+        ChipLogError(Camera, "URL does not contain a valid host.");
+        return false;
+    }
+    // Allow 'localhost' and 'localhost:<port>'
+    if (host == "localhost" || (host.length() > 10 && host.substr(0, 10) == "localhost:"))
+    {
+        // Additional check to ensure there's something after the colon for port
+        if (host == "localhost:" || (host.length() > 10 && host[10] == '\0'))
+        {
+            // This would be "localhost:" with nothing after, which is invalid
+            ChipLogError(Camera, "URL host '%s' is not valid. 'localhost:' must be followed by a port number.", host.c_str());
+            return false;
+        }
+        return true;
+    }
+    // Simplified host validation:
+    // A valid host should typically contain a dot (e.g., 'example.com').
+    // This is a basic check and not a comprehensive host/IP validation.
+    if (host.find('.') == std::string::npos)
+    {
+        ChipLogError(Camera, "URL host '%s' is not valid. Must be 'localhost', 'localhost:<port>', or contain a dot.",
+                     host.c_str());
+        return false;
+    }
+
+    return true;
+}
+
 CHIP_ERROR PushAvStreamTransportServerLogic::ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec)
 {
     uint32_t timeoutMs = timeoutSec * MILLISECOND_TICKS_PER_SECOND;
@@ -621,7 +701,7 @@
         return std::nullopt;
     }
 
-    bool isValidUrl = mDelegate->ValidateUrl(std::string(transportOptions.url.data(), transportOptions.url.size()));
+    bool isValidUrl = ValidateUrl(std::string(transportOptions.url.data(), transportOptions.url.size()));
 
     if (isValidUrl == false)
     {
diff --git a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h
index 7161433..e6b4170 100644
--- a/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h
+++ b/src/app/clusters/push-av-stream-transport-server/push-av-stream-transport-logic.h
@@ -163,6 +163,14 @@
      * @param timeoutSec    timeout in seconds
      */
     CHIP_ERROR ScheduleTransportDeallocate(uint16_t connectionID, uint32_t timeoutSec);
+
+    /**
+     * @brief Validates the provided URL.
+     *
+     * @param url The URL to validate
+     * @return true if URL is valid, false otherwise
+     */
+    bool ValidateUrl(const std::string & url);
 };
 
 } // namespace Clusters
diff --git a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp
index b31dcb2..ff7c9ae 100644
--- a/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp
+++ b/src/app/clusters/push-av-stream-transport-server/tests/TestPushAVStreamTransportCluster.cpp
@@ -299,8 +299,6 @@
         return Status::Success;
     }
 
-    bool ValidateUrl(const std::string & url) override { return true; }
-
     Protocols::InteractionModel::Status SelectVideoStream(StreamUsageEnum streamUsage, uint16_t & videoStreamId) override
     {
         // TODO: Select and Assign videoStreamID from the allocated videoStreams
@@ -469,7 +467,7 @@
     std::vector<TransportZoneOptionsDecodableStruct> mTransportZoneOptions;
     TransportTriggerOptionsDecodableStruct triggerOptions;
 
-    std::string url = "rtsp://192.168.1.100:554/stream";
+    std::string url = "https://192.168.1.100:554/stream/";
     TransportOptionsDecodableStruct transportOptions;
 
     uint8_t tlvBuffer[512];
@@ -598,7 +596,7 @@
     std::vector<TransportZoneOptionsDecodableStruct> mTransportZoneOptions;
     TransportTriggerOptionsDecodableStruct triggerOptions;
 
-    std::string url = "rtsp://192.168.1.100:554/stream";
+    std::string url = "https://192.168.1.100:554/stream/";
     TransportOptionsDecodableStruct transportOptions;
 
     uint8_t tlvBuffer[512];
@@ -756,7 +754,7 @@
     EXPECT_EQ(respTransportOptions.audioStreamID, 2);
     EXPECT_EQ(respTransportOptions.TLSEndpointID, 1);
     std::string respUrlStr(respTransportOptions.url.data(), respTransportOptions.url.size());
-    EXPECT_EQ(respUrlStr, "rtsp://192.168.1.100:554/stream");
+    EXPECT_EQ(respUrlStr, "https://192.168.1.100:554/stream/");
 
     Structs::TransportTriggerOptionsStruct::DecodableType respTriggerOptions = respTransportOptions.triggerOptions;
     EXPECT_EQ(respTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion);
@@ -892,7 +890,7 @@
     EXPECT_EQ(readTransportOptions.TLSEndpointID, 1);
 
     std::string urlStr(readTransportOptions.url.data(), readTransportOptions.url.size());
-    EXPECT_EQ(urlStr, "rtsp://192.168.1.100:554/stream");
+    EXPECT_EQ(urlStr, "https://192.168.1.100:554/stream/");
 
     Structs::TransportTriggerOptionsStruct::DecodableType readTriggerOptions = readTransportOptions.triggerOptions;
     EXPECT_EQ(readTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion);
@@ -985,7 +983,7 @@
     std::vector<TransportZoneOptionsDecodableStruct> mTransportZoneOptions;
     TransportTriggerOptionsDecodableStruct triggerOptions;
 
-    std::string url = "rtsp://192.168.1.100:554/stream";
+    std::string url = "https://192.168.1.100:554/stream/";
     TransportOptionsDecodableStruct transportOptions;
 
     uint8_t tlvBuffer[512];
@@ -1162,7 +1160,7 @@
     transportOptions.videoStreamID.SetValue(11);
     transportOptions.audioStreamID.SetValue(22);
     transportOptions.TLSEndpointID    = 1;
-    url                               = "rtsp://192.168.1.100:554/modify-stream";
+    url                               = "https://192.168.1.100:554/modify-stream/";
     transportOptions.url              = Span(url.data(), url.size());
     transportOptions.triggerOptions   = triggerOptions;
     transportOptions.containerOptions = containerOptions;
@@ -1245,7 +1243,7 @@
     EXPECT_EQ(findTransportOptions.TLSEndpointID, 1);
 
     std::string findUrlStr(findTransportOptions.url.data(), findTransportOptions.url.size());
-    EXPECT_EQ(findUrlStr, "rtsp://192.168.1.100:554/modify-stream");
+    EXPECT_EQ(findUrlStr, "https://192.168.1.100:554/modify-stream/");
 
     Structs::TransportTriggerOptionsStruct::DecodableType findTriggerOptions = findTransportOptions.triggerOptions;
     EXPECT_EQ(findTriggerOptions.triggerType, TransportTriggerTypeEnum::kMotion);
@@ -1320,7 +1318,7 @@
     std::vector<TransportZoneOptionsDecodableStruct> mTransportZoneOptions;
     TransportTriggerOptionsDecodableStruct triggerOptions;
 
-    std::string url = "rtsp://192.168.1.100:554/stream";
+    std::string url = "https://192.168.1.100:554/stream/";
     TransportOptionsDecodableStruct transportOptions;
 
     uint8_t tlvBuffer[512];
diff --git a/src/python_testing/TC_PAVSTI_1_1.py b/src/python_testing/TC_PAVSTI_1_1.py
index 1dbefbd..9a5b1ee 100644
--- a/src/python_testing/TC_PAVSTI_1_1.py
+++ b/src/python_testing/TC_PAVSTI_1_1.py
@@ -286,7 +286,7 @@
             "videoStreamID": videoStreamId,
             "audioStreamID": audioStreamId,
             "TLSEndpointID": tlsEndpointId,
-            "url": f"https://{self.host_ip}:1234/streams/{uploadStreamId}",
+            "url": f"https://{self.host_ip}:1234/streams/{uploadStreamId}/",
             "triggerOptions": {"triggerType": pushavCluster.Enums.TransportTriggerTypeEnum.kCommand, "maxPreRollLen": 10},
             "ingestMethod": pushavCluster.Enums.IngestMethodsEnum.kCMAFIngest,
             "containerFormat": pushavCluster.Enums.ContainerFormatEnum.kCmaf,
diff --git a/src/python_testing/TC_PAVSTI_1_2.py b/src/python_testing/TC_PAVSTI_1_2.py
index 5cd591a..15042c8 100644
--- a/src/python_testing/TC_PAVSTI_1_2.py
+++ b/src/python_testing/TC_PAVSTI_1_2.py
@@ -243,7 +243,7 @@
                     "videoStreamID": videoStreamId,
                     "audioStreamID": audioStreamId,
                     "TLSEndpointID": tlsEndpointId,
-                    "url": f"https://{self.host_ip}:1234/streams/{uploadStreamId}",
+                    "url": f"https://{self.host_ip}:1234/streams/{uploadStreamId}/",
                     "triggerOptions": {"triggerType": pushavCluster.Enums.TransportTriggerTypeEnum.kContinuous},
                     "ingestMethod": pushavCluster.Enums.IngestMethodsEnum.kCMAFIngest,
                     "containerFormat": pushavCluster.Enums.ContainerFormatEnum.kCmaf,
diff --git a/src/python_testing/TC_PAVSTTestBase.py b/src/python_testing/TC_PAVSTTestBase.py
index dde9b93..24374c8 100644
--- a/src/python_testing/TC_PAVSTTestBase.py
+++ b/src/python_testing/TC_PAVSTTestBase.py
@@ -187,7 +187,7 @@
 
     async def allocate_one_pushav_transport(self, endpoint, triggerType=Clusters.PushAvStreamTransport.Enums.TransportTriggerTypeEnum.kContinuous,
                                             trigger_Options=None, ingestMethod=Clusters.PushAvStreamTransport.Enums.IngestMethodsEnum.kCMAFIngest,
-                                            url="https://localhost:1234/streams/1", stream_Usage=None, container_Options=None,
+                                            url="https://localhost:1234/streams/1/", stream_Usage=None, container_Options=None,
                                             videoStream_ID=None, audioStream_ID=None, expected_cluster_status=None, tlsEndPoint=1, expiryTime=10):
         endpoint = self.get_endpoint(default=1)
         cluster = Clusters.PushAvStreamTransport
@@ -233,7 +233,7 @@
         containerOptions = {
             "containerType": cluster.Enums.ContainerFormatEnum.kCmaf,
             "CMAFContainerOptions": {"CMAFInterface": cluster.Enums.CMAFInterfaceEnum.kInterface1, "chunkDuration": 4, "segmentDuration": 4000,
-                                     "sessionGroup": 3, "trackName": " "},
+                                     "sessionGroup": 3, "trackName": "media"},
         }
 
         if (container_Options is not None):
diff --git a/src/python_testing/TC_PAVST_2_10.py b/src/python_testing/TC_PAVST_2_10.py
new file mode 100644
index 0000000..29147bc
--- /dev/null
+++ b/src/python_testing/TC_PAVST_2_10.py
@@ -0,0 +1,169 @@
+#
+#    Copyright (c) 2025 Project CHIP Authors
+#    All rights reserved.
+#
+#    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.
+
+# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
+# for details about the block below.
+#
+# === BEGIN CI TEST ARGUMENTS ===
+# test-runner-runs:
+#   run1:
+#     app: ${CAMERA_APP}
+#     app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
+#     script-args: >
+#       --storage-path admin_storage.json
+#       --string-arg th_server_app_path:${PUSH_AV_SERVER}
+#       --string-arg host_ip:localhost
+#       --commissioning-method on-network
+#       --discriminator 1234
+#       --passcode 20202021
+#       --PICS src/app/tests/suites/certification/ci-pics-values
+#       --trace-to json:${TRACE_TEST_JSON}.json
+#       --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
+#       --endpoint 1
+#     factory-reset: true
+#     quiet: true
+# === END CI TEST ARGUMENTS ===
+
+import logging
+
+from mobly import asserts
+from TC_PAVSTI_Utils import PAVSTIUtils, PushAvServerProcess
+from TC_PAVSTTestBase import PAVSTTestBase
+
+import matter.clusters as Clusters
+from matter.interaction_model import InteractionModelError
+from matter.testing.matter_testing import (MatterBaseTest, TestStep, async_test_body, default_matter_test_main, has_cluster,
+                                           run_if_endpoint_matches)
+
+logger = logging.getLogger(__name__)
+
+
+class TC_PAVST_2_10(MatterBaseTest, PAVSTTestBase, PAVSTIUtils):
+    def desc_TC_PAVST_2_10(self) -> str:
+        return "[TC-PAVST-2.10] Validate URL validation in clip upload"
+
+    def pics_TC_PAVST_2_10(self):
+        return ["PAVST.S", "AVSM.S"]
+
+    @async_test_body
+    async def setup_class(self):
+        th_server_app = self.user_params.get("th_server_app_path", None)
+        self.server = PushAvServerProcess(server_path=th_server_app)
+        self.server.start(
+            expected_output="Running on https://0.0.0.0:1234",
+            timeout=30,
+        )
+        super().setup_class()
+
+    def teardown_class(self):
+        if self.server is not None:
+            self.server.terminate()
+        super().teardown_class()
+
+    def steps_TC_PAVST_2_10(self) -> list[TestStep]:
+        return [
+            TestStep("precondition", "Commissioning, already done", is_commissioning=True),
+            TestStep(1, "TH Reads CurrentConnections attribute from PushAV Stream Transport Cluster on DUT",
+                     "Verify the number of PushAV Connections is 0. If not 0, deallocate any existing connections."),
+            TestStep(2, "TH Reads SupportedFormats attribute from PushAV Stream Transport Cluster on DUT",
+                     "Store the SupportedFormats as aSupportedFormats."),
+            TestStep(3, "TH Reads AllocatedVideoStreams attribute from CameraAVStreamManagement Cluster on DUT",
+                     "Store as aAllocatedVideoStreams."),
+            TestStep(4, "TH Reads AllocatedAudioStreams attribute from CameraAVStreamManagement Cluster on DUT",
+                     "Store as aAllocatedAudioStreams."),
+            TestStep(5, "TH sends AllocatePushTransport command with URL using non‑https scheme",
+                     "DUT should respond with Status Code InvalidURL."),
+            TestStep(6, "TH sends AllocatePushTransport command with URL containing a fragment (#)",
+                     "DUT should respond with Status Code InvalidURL."),
+            TestStep(7, "TH sends AllocatePushTransport command with URL containing a query (?)",
+                     "DUT should respond with Status Code InvalidURL."),
+            TestStep(8, "TH sends AllocatePushTransport command with URL not ending with '/'",
+                     "DUT should respond with Status Code InvalidURL."),
+            TestStep(9, "TH sends AllocatePushTransport command with URL missing host",
+                     "DUT should respond with Status Code InvalidURL."),
+        ]
+
+    @run_if_endpoint_matches(has_cluster(Clusters.PushAvStreamTransport))
+    async def test_TC_PAVST_2_10(self):
+        endpoint = self.get_endpoint(default=1)
+        self.endpoint = endpoint
+        self.node_id = self.dut_node_id
+        pvcluster = Clusters.PushAvStreamTransport
+        pvattr = Clusters.PushAvStreamTransport.Attributes
+
+        # Precondition
+        self.step("precondition")
+        host_ip = self.user_params.get("host_ip", None)
+        tlsEndpointId, host_ip = await self.precondition_provision_tls_endpoint(
+            endpoint=endpoint, server=self.server, host_ip=host_ip)
+
+        # Reads CurrentConnections attribute (step 1)
+        self.step(1)
+        transport_configs = await self.read_single_attribute_check_success(
+            endpoint=endpoint, cluster=pvcluster, attribute=pvattr.CurrentConnections)
+        for cfg in transport_configs:
+            if cfg.ConnectionID != 0:
+                try:
+                    await self.send_single_cmd(
+                        cmd=pvcluster.Commands.DeallocatePushTransport(ConnectionID=cfg.ConnectionID),
+                        endpoint=endpoint)
+                except InteractionModelError as e:
+                    logging.warning(f"Failed to deallocate connection {cfg.ConnectionID} during cleanup: {e}")
+
+        # Read supported formats (step 2)
+        self.step(2)
+        aSupportedFormats = await self.read_single_attribute_check_success(
+            endpoint=endpoint, cluster=pvcluster, attribute=pvattr.SupportedFormats)
+        logger.info(f"aSupportedFormats={aSupportedFormats}")
+
+        # Read allocated video streams (step 3)
+        self.step(3)
+        aAllocatedVideoStreams = await self.allocate_one_video_stream()
+        asserts.assert_greater_equal(
+            len(aAllocatedVideoStreams),
+            1,
+            "AllocatedVideoStreams must not be empty",
+        )
+
+        # Read allocated audio streams (step 4)
+        self.step(4)
+        aAllocatedAudioStreams = await self.allocate_one_audio_stream()
+        asserts.assert_greater_equal(
+            len(aAllocatedAudioStreams),
+            1,
+            "AllocatedAudioStreams must not be empty",
+        )
+
+        # Define invalid URL cases
+        stream_id = self.server.create_stream()
+        invalid_cases = [
+            ("non‑https scheme", f"http://{host_ip}:1234/streams/{stream_id}/"),
+            ("fragment", f"https://{host_ip}:1234/streams/{stream_id}#/frag"),
+            ("query", f"https://{host_ip}:1234/streams/{stream_id}?bad=query"),
+            ("no trailing slash", f"https://{host_ip}:1234/streams/{stream_id}"),
+            ("missing host", f"https:///streams/{stream_id}/"),
+        ]
+
+        for idx, (desc, url) in enumerate(invalid_cases, start=5):
+            self.step(idx)
+            status = await self.allocate_one_pushav_transport(
+                endpoint, tlsEndPoint=tlsEndpointId, url=url, expected_cluster_status=pvcluster.Enums.StatusCodeEnum.kInvalidURL, expiryTime=30)
+            asserts.assert_equal(status, pvcluster.Enums.StatusCodeEnum.kInvalidURL,
+                                 f"Push AV Transport should return InvalidURL for {desc}")
+
+
+if __name__ == "__main__":
+    default_matter_test_main()
diff --git a/src/python_testing/TC_PAVST_2_2.py b/src/python_testing/TC_PAVST_2_2.py
index 4715bea..266aaac 100644
--- a/src/python_testing/TC_PAVST_2_2.py
+++ b/src/python_testing/TC_PAVST_2_2.py
@@ -139,7 +139,7 @@
         )
 
         self.step(5)
-        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
diff --git a/src/python_testing/TC_PAVST_2_3.py b/src/python_testing/TC_PAVST_2_3.py
index 7d0bac4..4646bad 100644
--- a/src/python_testing/TC_PAVST_2_3.py
+++ b/src/python_testing/TC_PAVST_2_3.py
@@ -187,7 +187,7 @@
                     "videoStreamID": NullValue,
                     "audioStreamID": NullValue,
                     "TLSEndpointID": tlsEndpointId,
-                    "url": f"https://{host_ip}:1234/streams/1",
+                    "url": f"https://{host_ip}:1234/streams/1/",
                     "triggerOptions": {"triggerType": 2},
                     "ingestMethod": 0,
                     "containerOptions": {
@@ -219,7 +219,7 @@
         )
 
         self.step(6)
-        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
@@ -261,7 +261,7 @@
                         "videoStreamID": 1,
                         "audioStreamID": 1,
                         "TLSEndpointID": 5,
-                        "url": f"https://{host_ip}:1234/streams/1",
+                        "url": f"https://{host_ip}:1234/streams/1/",
                         "triggerOptions": {"triggerType": 2},
                         "ingestMethod": 0,
                         "containerOptions": {
@@ -278,19 +278,19 @@
 
         self.step(12)
         status = await self.allocate_one_pushav_transport(endpoint, ingestMethod=pvcluster.Enums.IngestMethodsEnum.kUnknownEnumValue,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.ConstraintError,
                              "DUT must respond with Status Code ConstraintError.")
 
         self.step(13)
         status = await self.allocate_one_pushav_transport(endpoint, expected_cluster_status=pvcluster.Enums.StatusCodeEnum.kInvalidURL,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https:/{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https:/{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, pvcluster.Enums.StatusCodeEnum.kInvalidURL,
                              "DUT must respond with Status Code InvalidURL.")
 
         self.step(14)
         status = await self.allocate_one_pushav_transport(endpoint, triggerType=pvcluster.Enums.TransportTriggerTypeEnum.kUnknownEnumValue,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.ConstraintError,
                              "DUT must respond with Status Code ConstraintError.")
 
@@ -302,7 +302,7 @@
                               "maxPreRollLen": 4000,
                               "motionZones": zoneList,
                               "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
-            status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+            status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
             asserts.assert_equal(status, Status.Success, "DUT should respond with Status Code Success with a Null Zone.")
         except InteractionModelError as e:
             asserts.assert_fail(f"Unexpected error when setting a Zone that is Null (meaning all Zones). Error received {e.status}")
@@ -315,7 +315,7 @@
                           "maxPreRollLen": 4000,
                           "motionZones": zoneList,
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.AlreadyExists,
                              "DUT should respond with Status Code AlreadyExists with a Duplicate Zone.")
 
@@ -327,7 +327,7 @@
                           "maxPreRollLen": 4000,
                           "motionZones": zoneList,
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.AlreadyExists,
                              "DUT should respond with Status Code AlreadyExists with Duplicate Null Zones.")
 
@@ -339,7 +339,7 @@
                               "motionZones": zoneList,
                               "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
             status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, expected_cluster_status=pvcluster.Enums.StatusCodeEnum.kInvalidZone,
-                                                              tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                              tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
             asserts.assert_equal(status, pvcluster.Enums.StatusCodeEnum.kInvalidZone,
                                  "DUT must responds with Status Code InvalidZone.")
         except InteractionModelError as e:
@@ -349,14 +349,14 @@
         self.step(19)
         status = await self.allocate_one_pushav_transport(endpoint, videoStream_ID=-1,
                                                           expected_cluster_status=pvcluster.Enums.StatusCodeEnum.kInvalidStream,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, pvcluster.Enums.StatusCodeEnum.kInvalidStream,
                              "DUT must  responds with Status Code InvalidStream.")
 
         self.step(20)
         status = await self.allocate_one_pushav_transport(endpoint, audioStream_ID=-1,
                                                           expected_cluster_status=pvcluster.Enums.StatusCodeEnum.kInvalidStream,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, pvcluster.Enums.StatusCodeEnum.kInvalidStream,
                              "DUT must  responds with Status Code InvalidStream.")
 
@@ -390,7 +390,7 @@
 
         self.step(22)
         status = await self.allocate_one_pushav_transport(endpoint, videoStream_ID=Nullable(), audioStream_ID=Nullable(),
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.Success,
                              "DUT must  responds with Status Code Success.")
 
@@ -405,7 +405,7 @@
                           "motionZones": zoneList,
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
         status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.DynamicConstraintError,
                              "DUT must respond with Status code DynamicConstraintError")
 
@@ -415,7 +415,7 @@
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1},
                           "motionSensitivity": 3,
                           "motionZones": zoneList}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.InvalidCommand,
                              "DUT must  responds with Status Code InvalidCommand.")
 
@@ -425,7 +425,7 @@
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1},
                           "motionSensitivity": 11,
                           "motionZones": zoneList}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.InvalidCommand,
                              "DUT must  responds with Status Code InvalidCommand.")
 
@@ -434,7 +434,7 @@
         triggerOptions = {"triggerType": pvcluster.Enums.TransportTriggerTypeEnum.kMotion,
                           "motionZones": zoneList,
                           "motionSensitivity": 3}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.InvalidCommand,
                              "DUT must  responds with Status Code InvalidCommand.")
 
@@ -444,7 +444,7 @@
                           "motionZones": zoneList,
                           "motionSensitivity": 3,
                           "motionTimeControl": {"initialDuration": 0, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.ConstraintError,
                              "DUT must  responds with Status code ConstraintError")
 
@@ -454,7 +454,7 @@
                           "motionZones": zoneList,
                           "motionSensitivity": 3,
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 0, "blindDuration": 1}}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.ConstraintError,
                              "DUT must  responds with Status code ConstraintError")
 
@@ -471,7 +471,7 @@
                           "motionZones": zoneList,
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
 
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.Success,
                              "DUT must  responds with Status Code Success.")
         current_connections = await self.read_single_attribute_check_success(
@@ -491,19 +491,19 @@
                           "maxPreRollLen": 4000,
                           "motionZones": zoneList,
                           "motionTimeControl": {"initialDuration": 1, "augmentationDuration": 1, "maxDuration": 1, "blindDuration": 1}}
-        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.Success,
                              "DUT must  responds with Status Code Success.")
 
         self.step(31)
         status = await self.allocate_one_pushav_transport(endpoint, stream_Usage=Clusters.Globals.Enums.StreamUsageEnum.kUnknownEnumValue,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.ConstraintError,
                              "DUT must  responds with Status code ConstraintError")
 
         self.step(32)
         containerOptions = {"containerType": pvcluster.Enums.ContainerFormatEnum.kCmaf}
-        status = await self.allocate_one_pushav_transport(endpoint, container_Options=containerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, container_Options=containerOptions, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, Status.InvalidCommand,
                              "DUT must  responds with Status code InvalidCommand")
 
@@ -511,18 +511,18 @@
         containerOptions = {
             "containerType": pvcluster.Enums.ContainerFormatEnum.kCmaf,
             "CMAFContainerOptions": {"CMAFInterface": pvcluster.Enums.CMAFInterfaceEnum.kInterface1, "chunkDuration": 4, "segmentDuration": 6000,
-                                     "sessionGroup": 3, "trackName": " "},
+                                     "sessionGroup": 3, "trackName": "media"},
         }
         status = await self.allocate_one_pushav_transport(endpoint, expected_cluster_status=pvcluster.Enums.StatusCodeEnum.kInvalidOptions,
                                                           stream_Usage=Clusters.Globals.Enums.StreamUsageEnum.kRecording, tlsEndPoint=tlsEndpointId,
-                                                          container_Options=containerOptions, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          container_Options=containerOptions, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, pvcluster.Enums.StatusCodeEnum.kInvalidOptions,
                              "DUT must responds with Status Code InvalidOptions.")
 
         self.step(34)
         status = await self.allocate_one_pushav_transport(endpoint, expected_cluster_status=pvcluster.Enums.StatusCodeEnum.kInvalidStreamUsage,
                                                           stream_Usage=Clusters.Globals.Enums.StreamUsageEnum.kInternal,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(status, pvcluster.Enums.StatusCodeEnum.kInvalidStreamUsage,
                              "DUT must  responds with Status code InvalidStreamUsage")
 
diff --git a/src/python_testing/TC_PAVST_2_4.py b/src/python_testing/TC_PAVST_2_4.py
index a88a77a..1c80e7e 100644
--- a/src/python_testing/TC_PAVST_2_4.py
+++ b/src/python_testing/TC_PAVST_2_4.py
@@ -147,7 +147,7 @@
             "AllocatedAudioStreams must not be empty",
         )
 
-        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
diff --git a/src/python_testing/TC_PAVST_2_5.py b/src/python_testing/TC_PAVST_2_5.py
index 18a46d8..150ecf7 100644
--- a/src/python_testing/TC_PAVST_2_5.py
+++ b/src/python_testing/TC_PAVST_2_5.py
@@ -146,7 +146,7 @@
             "AllocatedAudioStreams must not be empty",
         )
 
-        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
diff --git a/src/python_testing/TC_PAVST_2_6.py b/src/python_testing/TC_PAVST_2_6.py
index fd627f9..51a5e22 100644
--- a/src/python_testing/TC_PAVST_2_6.py
+++ b/src/python_testing/TC_PAVST_2_6.py
@@ -181,7 +181,7 @@
             "AllocatedAudioStreams must not be empty",
         )
 
-        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
diff --git a/src/python_testing/TC_PAVST_2_7.py b/src/python_testing/TC_PAVST_2_7.py
index 8cf120a..d21751a 100644
--- a/src/python_testing/TC_PAVST_2_7.py
+++ b/src/python_testing/TC_PAVST_2_7.py
@@ -188,7 +188,7 @@
         )
 
         status = await self.allocate_one_pushav_transport(endpoint, triggerType=pvcluster.Enums.TransportTriggerTypeEnum.kContinuous,
-                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
@@ -303,7 +303,7 @@
                           "maxPreRollLen": 4000}
 
         status = await self.allocate_one_pushav_transport(endpoint, trigger_Options=triggerOptions, tlsEndPoint=tlsEndpointId,
-                                                          url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+                                                          url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
diff --git a/src/python_testing/TC_PAVST_2_8.py b/src/python_testing/TC_PAVST_2_8.py
index 19358cd..ca4f465 100644
--- a/src/python_testing/TC_PAVST_2_8.py
+++ b/src/python_testing/TC_PAVST_2_8.py
@@ -147,7 +147,7 @@
             "AllocatedAudioStreams must not be empty",
         )
 
-        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}")
+        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/")
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )
diff --git a/src/python_testing/TC_PAVST_2_9.py b/src/python_testing/TC_PAVST_2_9.py
index 8bcbfd9..a3c8f18 100644
--- a/src/python_testing/TC_PAVST_2_9.py
+++ b/src/python_testing/TC_PAVST_2_9.py
@@ -147,7 +147,7 @@
         )
 
         self.step(5)
-        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}", expiryTime=5)
+        status = await self.allocate_one_pushav_transport(endpoint, tlsEndPoint=tlsEndpointId, url=f"https://{host_ip}:1234/streams/{uploadStreamId}/", expiryTime=5)
         asserts.assert_equal(
             status, Status.Success, "Push AV Transport should be allocated successfully"
         )