Commit 6569813a authored by kzangeli's avatar kzangeli
Browse files

HttpCtrl: __is_satisfy must rstrip trailing / from BOTH stub and request URLs



`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: default avatarClaude Opus 4.7 <noreply@anthropic.com>
parent 59eca008
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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())