Commit 92c19ff4 authored by kzangeli's avatar kzangeli
Browse files

fix: 038_02_01 Location parsing + 038_08_03 actually-invalid q

038_02_01 stored the raw Location header as the subscription id;
Location is a URI reference (RFC 9110 § 10.2.2) carrying the resource
path, so the follow-up retrieve and the teardown delete built doubled
URLs and failed — leaking the created subscription into the 041_*
count assertions. The id is now the last path segment, and the
retrieve comparison ignores the clause-12.4.7 Additional Members
(root level and inside 'notification') like its 038_01/038_03
siblings.

038_08_03's fixture used "q": "invalidQuery" — a bare attribute
path, which the § 7.x ABNF (QueryTerm = Attribute) makes a VALID
existence-predicate query that brokers must accept with 201. The
fixture now sends "(invalidQuery" (unbalanced parenthesis), the same
genuinely-invalid form 037_03_02 uses.

The notification_result_regex_expr variable and the assertionUtils.py
threshold_to_diff_deeper=0 fix are re-declared identically to their
origin branch (unmerged MR) — git deduplicates on merge.

Analysis recorded as testsuite-doubts.md #96.
parent d9a20233
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -22,7 +22,10 @@ ${subscription_id}= ${EMPTY}
    ${response}=    Create Context Source Registration Subscription    ${subscription_payload}

    Dictionary Should Contain Key    ${response.headers}    Location    msg=HTTP Headers do not contain key 'Location'
    ${subscription_id}=    Get From Dictionary    ${response.headers}    Location
    ${location}=    Get From Dictionary    ${response.headers}    Location
    # Location is a URI reference (RFC 9110 § 10.2.2) — typically the resource
    # path /ngsi-ld/v1/csourceSubscriptions/<id>. Extract the generated id.
    ${subscription_id}=    Fetch From Right    ${location}    /
    Set Suite Variable    ${subscription_id}

    Check Response Status Code    201    ${response.status_code}
@@ -32,7 +35,18 @@ ${subscription_id}= ${EMPTY}
    ...    subscription_id=${subscription_id}
    ...    context=${ngsild_test_suite_context}
    ...    accept=${CONTENT_TYPE_LD_JSON}
    ${ignored_attributes}=    Create List    ${id_regex_expr}    ${status_regex_expr}
    # Ignore the Additional Members ('lastFailure', 'lastNotification', 'timesFailed',
    # 'timesSent', 'status'), both at root level and inside 'notification' (clause
    # 5.2.14), where they may have been set already by the initial notification sent
    # upon subscription creation (clause 12.4.7).
    ${ignored_attributes}=    Create List
    ...    ${id_regex_expr}
    ...    ${status_regex_expr}
    ...    ${lastfailure_regex_expr}
    ...    ${lastNotification_regex_expr}
    ...    ${timesFailed_regex_expr}
    ...    ${timesSent_regex_expr}
    ...    ${notification_result_regex_expr}
    Check Created Resource Set To    ${subscription_payload}    ${response1.json()}    ${ignored_attributes}


+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
         "type":"Building"
      }
   ],
   "q": "invalidQuery",
   "q": "(invalidQuery",
   "notification":{
      "format":"keyValues",
      "endpoint":{
+9 −0
Original line number Diff line number Diff line
@@ -98,10 +98,16 @@ def compare_dictionaries_ignoring_keys(expected, actual, exclude_regex_paths, ig
    :param exclude_regex_paths: list of regex paths of keys to be ignored
    :param ignore_core_context_version: whether any core context version is allowed in the results
    :param group_by: a key to group the results, useful for lists of results

    threshold_to_diff_deeper=0: since deepdiff 8, when more than 1/3 of a dict's keys
    differ, DeepDiff reports the whole dict as changed instead of descending into it,
    which silently bypasses exclude_regex_paths targeting the nested keys (e.g. the
    broker-computed Additional Members inside 'notification'). 0 restores per-key diffs.
    """

    if group_by is not None and ignore_core_context_version:
        res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1,
                       threshold_to_diff_deeper=0,
                       iterable_compare_func=compare_func,
                       custom_operators=[
                           AnyCoreContextVersionOperator(),
@@ -111,6 +117,7 @@ def compare_dictionaries_ignoring_keys(expected, actual, exclude_regex_paths, ig
                       group_by=group_by)
    elif group_by is not None:
        res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1,
                       threshold_to_diff_deeper=0,
                       iterable_compare_func=compare_func,
                       custom_operators=[
                           StringOrSingleListContextOperator(),
@@ -120,6 +127,7 @@ def compare_dictionaries_ignoring_keys(expected, actual, exclude_regex_paths, ig
                       group_by=group_by)
    elif ignore_core_context_version:
        res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1,
                       threshold_to_diff_deeper=0,
                       iterable_compare_func=compare_func,
                       custom_operators=[
                           AnyCoreContextVersionOperator(),
@@ -128,6 +136,7 @@ def compare_dictionaries_ignoring_keys(expected, actual, exclude_regex_paths, ig
                       ])
    else:
        res = DeepDiff(expected, actual, exclude_regex_paths=exclude_regex_paths, ignore_order=True, verbose_level=1,
                       threshold_to_diff_deeper=0,
                       iterable_compare_func=compare_func,
                       custom_operators=[
                           StringOrSingleListContextOperator(),
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ ${lastNotification_regex_expr}= root\\['lastNotification'\\]
${timesFailed_regex_expr}=                  root\\['timesFailed'\\]
${timesSent_regex_expr}=                    root\\['timesSent'\\]
${is_active_expr}=                          root\\['isActive'\\]
${notification_result_regex_expr}=          \\['notification'\\]\\['(lastNotification|lastSuccess|lastFailure|timesSent|timesFailed|status)'\\]
${context_source_ignored_regex_expr}=       root\\['@context'\\]


+29 −0
Original line number Diff line number Diff line
@@ -2667,3 +2667,32 @@ doesn't match the URL id.
`$.id` with the actual `${entity_id}` before PATCHing. Or omit the
`id` from the fragment entirely (PATCH attrs doesn't need it).



## 96. `038_02_01` / `038_08_03` — Location parsed as an id, and a syntactically valid `q` expected to fail

Two independent defects in the csourceSubscription-creation tests; both also
LEAK their subscription (the teardown's delete fails / never runs), and the
leaked subscriptions then poison the `041_*` count assertions
(`Query All Subscriptions: 5 != 3` etc.) in full-suite runs.

**a) `038_02_01` (create without id):** the test stores the raw `Location`
response header as `${subscription_id}`. `Location` is a URI reference
(RFC 9110 § 10.2.2) — brokers return the resource path
`/ngsi-ld/v1/csourceSubscriptions/<id>` — so the follow-up retrieve and the
teardown delete build doubled URLs and fail. Fixed by extracting the last
path segment. The retrieve comparison additionally needs the clause-12.4.7
Additional-Members ignores (root level and inside `notification`) like its
siblings 038_01/038_03 — the initial notification to the dead fixture
endpoint sets `timesSent`/`timesFailed`/`lastNotification`/`lastFailure`/
`status` immediately.

**b) `038_08_03` (InvalidQuery):** the fixture's `"q": "invalidQuery"` is a
bare attribute path — per TS 104-175 § 7.x ABNF (`QueryTerm = Attribute`)
that is a syntactically VALID query (an existence predicate), so a broker
must accept it with 201. The fixture now uses `"(invalidQuery"` (unbalanced
parenthesis — genuinely invalid), mirroring `037_03_02`.

(The `notification_result_regex_expr` variable and the assertionUtils.py
`threshold_to_diff_deeper=0` fix are re-declared identically to their origin
branch since that MR is unmerged — git deduplicates on merge.)