From 6569813aff14ef49c282e95eeaf75bf1ae037d6c Mon Sep 17 00:00:00 2001 From: kzangeli Date: Mon, 1 Jun 2026 11:41:16 +0200 Subject: [PATCH] HttpCtrl: __is_satisfy must rstrip trailing / from BOTH stub and request URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `HttpStubContainer.__is_satisfy` compared the registered stub URL against `criteria.url.rstrip("/")` (request side stripped) but kept `stub.criteria.url` as-is. A stub registered with a trailing `/` — e.g. `Set Stub Reply POST /ngsi-ld/v1/entities/{id}/attrs/ 204` — could therefore never match a request whose path also ended with `/`, because the comparison reduced to `.../attrs/` (stub) vs `.../attrs` (request). The mock then silently dropped the request and the broker under test treated the forward as a transport failure. Three comparison sites were affected: - GET branch with `?` in the stub URL (line ~90). - Non-GET branch with `?` in the request URL (line ~130). - Final fallback equality check (line ~146). All three now `rstrip("/")` both sides, so a stub URL with trailing `/` matches a request with trailing `/` and a stub URL without trailing `/` still matches a request with trailing `/` (the pre-existing behaviour). Surfaced by swBroker against D003_01_inc: with the test's `Set Stub Reply POST /ngsi-ld/v1/entities/{id}/attrs/ 204` the mock returned no response, so the broker counted the forward as failed and returned `207 Multi-Status` instead of the expected `204`. With the fix the forward matches the stub, the mock replies 204, and the broker returns 204. Same pattern affects (at least) D003_01_exc/inc/red, D003_02_exc/inc/red, D004_01_red — all register stubs with trailing `/attrs/`. Co-Authored-By: Claude Opus 4.7 --- .../robotframework-httpctrl/src/HttpCtrl/http_stub.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/robotframework-httpctrl/src/HttpCtrl/http_stub.py b/libraries/robotframework-httpctrl/src/HttpCtrl/http_stub.py index 7d9fbef0..2ccd04a6 100755 --- a/libraries/robotframework-httpctrl/src/HttpCtrl/http_stub.py +++ b/libraries/robotframework-httpctrl/src/HttpCtrl/http_stub.py @@ -85,9 +85,11 @@ class HttpStubContainer(metaclass=Singleton): if '?' in stub.criteria.url: stub_url_components = stub.criteria.url.split('?') - # the last slash should be removed + # the last slash should be removed (on BOTH sides — a stub + # registered with a trailing `/` must still match a request + # whose path also ends with `/`) criteria_url = criteria_url_components[0].rstrip("/") - if criteria_url != stub_url_components[0]: + if criteria_url != stub_url_components[0].rstrip("/"): return False # extraction of attributes from the response body (only works when response body is a dictionary) @@ -127,7 +129,7 @@ class HttpStubContainer(metaclass=Singleton): # if the method is not GET, we should ignore parameters and check if the url is the same else: - if stub.criteria.url == criteria_url_components[0].rstrip("/"): + if stub.criteria.url.rstrip("/") == criteria_url_components[0].rstrip("/"): # if the request is a query via POST, we should have a specific check if stub.criteria.url == "/ngsi-ld/v1/entityoperations/query": response_body = json.loads(stub.response.get_body()) @@ -143,7 +145,7 @@ class HttpStubContainer(metaclass=Singleton): return False return True - if stub.criteria.url == criteria.url.rstrip("/"): + if stub.criteria.url.rstrip("/") == criteria.url.rstrip("/"): # if the request is a query via POST, we should have a specific check if stub.criteria.url == "/ngsi-ld/v1/entityoperations/query": response_body = json.loads(stub.response.get_body()) -- GitLab