From 20d7c56ab45d3962f12127c8176abceac56c8e93 Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Wed, 30 Sep 2026 13:45:33 +0200 Subject: [PATCH 1/3] Align Commonalities r4.4 to Sim Swap and Subscriptions --- .../sim-swap-subscriptions.yaml | 105 ++++++++---------- code/API_definitions/sim-swap.yaml | 105 ++++-------------- 2 files changed, 67 insertions(+), 143 deletions(-) diff --git a/code/API_definitions/sim-swap-subscriptions.yaml b/code/API_definitions/sim-swap-subscriptions.yaml index 612af37..30a6756 100644 --- a/code/API_definitions/sim-swap-subscriptions.yaml +++ b/code/API_definitions/sim-swap-subscriptions.yaml @@ -69,26 +69,28 @@ info: In cases where personal data is processed by the API and users can exercise their rights through mechanisms such as opt-in and/or opt-out, the use of three-legged access tokens is mandatory. This ensures that the API remains in compliance with privacy regulations, upholding the principles of transparency and user-centric privacy-by-design. - # Identifying the device from the access token + + # Identifying the phone number from the access token - This API requires the API consumer to identify a device as the subject of the API as follows: - - When the API is invoked using a two-legged access token, the subject will be identified from the optional `phoneNumber` identifier, which therefore MUST be provided. - - When a three-legged access token is used however, this optional `phoneNumber` identifier MUST NOT be provided, as the subject will be uniquely identified from the access token. + This API requires the API consumer to identify a phone number as the subject of the API as follows: + - When the API is invoked using a two-legged access token, the subject will be identified from the optional `phoneNumber` field, which therefore MUST be provided. + - When a three-legged access token is used however, this optional identifier MUST NOT be provided, as the subject will be uniquely identified from the access token. This approach simplifies API usage for API consumers using a three-legged access token to invoke the API by relying on the information that is associated with the access token and was identified during the authentication process. ## Error handling: - - If the subject cannot be identified from the access token and the optional `phoneNumber` identifier is not included in the request, then the server will return an error with the `422 MISSING_IDENTIFIER` error code. + - If the subject cannot be identified from the access token and the optional `phoneNumber` field is not included in the request, then the server will return an error with the `422 MISSING_IDENTIFIER` error code. - - If the subject can be identified from the access token and the optional `phoneNumber` identifier is also included in the request, then the server will return an error with the `422 UNNECESSARY_IDENTIFIER` error code. This will be the case even if the same device is identified by these two methods, as the server is unable to make this comparison. + - If the subject can be identified from the access token and the optional `phoneNumber` field is also included in the request, then the server will return an error with the `422 UNNECESSARY_IDENTIFIER` error code. This will be the case even if the same phone number is identified by these two methods, as the server is unable to make this comparison. + # Additional CAMARA error responses The list of error codes in this API specification is not exhaustive. Therefore the API specification MAY not document some non-mandatory error statuses as indicated in `CAMARA API Design Guide`. - Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified in the `API Readiness Checklist` document associated to this API version. + Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified from the `x-camara-commonalities` field, the changelog and the metadata of the released API version. As a specific rule, error `501 - NOT_IMPLEMENTED` can be only a possible error response if it is explicitly documented in the API. @@ -107,7 +109,7 @@ info: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html version: wip - x-camara-commonalities: 0.6 + x-camara-commonalities: 0.9.0 externalDocs: description: Product documentation at CAMARA @@ -176,15 +178,15 @@ paths: x-correlator: $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "410": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic410" + $ref: "../common/CAMARA_event_common.yaml#/components/responses/SinkGone410" "429": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + $ref: "../common/CAMARA_common.yaml#/components/responses/TooManyRequests429" security: - {} - notificationsBearerAuth: [] @@ -216,15 +218,15 @@ paths: "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionBadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionPermissionDenied403" "409": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic409" + $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionConflict409" "422": - $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionUnprocessableEntity422" + $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionPhoneNumber422" "429": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + $ref: "../common/CAMARA_common.yaml#/components/responses/TooManyRequests429" get: tags: - Sim Swap Subscription @@ -260,11 +262,11 @@ paths: SUBSCRIPTIONS_3LEGS: $ref: "#/components/examples/SUBSCRIPTIONS_3LEGS" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" /subscriptions/{subscriptionId}: get: tags: @@ -296,11 +298,11 @@ paths: "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionIdRequired400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + $ref: "../common/CAMARA_common.yaml#/components/responses/NotFound404" delete: tags: - Sim Swap Subscription @@ -331,11 +333,11 @@ paths: "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionIdRequired400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + $ref: "../common/CAMARA_common.yaml#/components/responses/NotFound404" components: securitySchemes: openId: @@ -360,28 +362,17 @@ components: Config: description: | - Implementation-specific configuration parameters needed by the subscription manager for acquiring events. - In CAMARA we have predefined attributes like `subscriptionExpireTime` or `subscriptionMaxEvents` to limit subscription lifetime. - Event type attributes must be defined in `subscriptionDetail` - type: object - required: - - subscriptionDetail - properties: - subscriptionDetail: - $ref: "#/components/schemas/CreateSubscriptionDetail" - subscriptionExpireTime: - type: string - format: date-time - maxLength: 64 - example: 2023-01-17T13:18:23.682Z - description: The subscription expiration time (in date-time format) requested by the API consumer. It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone. - subscriptionMaxEvents: - type: integer - format: int32 - description: Identifies the maximum number of event reports to be generated (>=1) requested by the API consumer - Once this number is reached, the subscription ends. - minimum: 1 - maximum: 1000000 - example: 5 + Implementation-specific configuration parameters needed by the subscription manager + for acquiring events. Extends `ConfigBase` from `CAMARA_event_common.yaml` with the + required, API-specific `subscriptionDetail` property. + allOf: + - $ref: "../common/CAMARA_event_common.yaml#/components/schemas/ConfigBase" + - type: object + required: + - subscriptionDetail + properties: + subscriptionDetail: + $ref: "#/components/schemas/CreateSubscriptionDetail" ApiEventType: type: string @@ -432,7 +423,9 @@ components: protocol: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Protocol" sink: - $ref: "#/components/schemas/Sink" + description: The address to which events shall be delivered using the selected protocol. + allOf: + - $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Sink" types: description: | Camara Event types eligible to be delivered by this subscription. @@ -502,13 +495,15 @@ components: protocol: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Protocol" sink: - $ref: "#/components/schemas/Sink" + description: The address to which events shall be delivered using the selected protocol. + allOf: + - $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Sink" sinkCredential: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/SinkCredential" types: description: | Camara Event types eligible to be delivered by this subscription. - Note: As of now we enforce to have only event type per subscription. + Note: For the current Commonalities API design guidelines, only one event type per subscription is allowed type: array minItems: 1 maxItems: 1 @@ -633,14 +628,6 @@ components: data: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/SubscriptionEnded" - Sink: - description: The address to which events shall be delivered using the selected protocol. - type: string - format: uri - maxLength: 2048 - pattern: ^https:\/\/.+$ - example: "https://endpoint.example.com/sink" - HTTPSubscriptionRequest: description: Subscription request for HTTP-based event delivery. allOf: diff --git a/code/API_definitions/sim-swap.yaml b/code/API_definitions/sim-swap.yaml index 77195e0..2ef5f0b 100644 --- a/code/API_definitions/sim-swap.yaml +++ b/code/API_definitions/sim-swap.yaml @@ -48,26 +48,28 @@ info: In cases where personal data is processed by the API and users can exercise their rights through mechanisms such as opt-in and/or opt-out, the use of three-legged access tokens is mandatory. This ensures that the API remains in compliance with privacy regulations, upholding the principles of transparency and user-centric privacy-by-design. + # Identifying the phone number from the access token - This API requires the API consumer to identify a phone number as the subject of the API. There is 2 ways to retrieve it depending on the authorization flow used: - - When the API is invoked using a two-legged access token, the phone number will be identified from the optional `phoneNumber` identifier, which therefore MUST be provided. - - When a three-legged access token is used however, this optional `phoneNumber` identifier MUST NOT be provided, as the phone number will be uniquely identified from the access token. + This API requires the API consumer to identify a phone number as the subject of the API as follows: + - When the API is invoked using a two-legged access token, the subject will be identified from the optional `phoneNumber` field, which therefore MUST be provided. + - When a three-legged access token is used however, this optional identifier MUST NOT be provided, as the subject will be uniquely identified from the access token. This approach simplifies API usage for API consumers using a three-legged access token to invoke the API by relying on the information that is associated with the access token and was identified during the authentication process. ## Error handling: - - If the phoneNumber cannot be identified from the access token and the optional `phoneNumber` identifier is not included in the request, then the server will return an error with the `422 MISSING_IDENTIFIER` error code. + - If the subject cannot be identified from the access token and the optional `phoneNumber` field is not included in the request, then the server will return an error with the `422 MISSING_IDENTIFIER` error code. - - If the phoneNumber can be identified from the access token and the optional `phoneNumber` identifier is also included in the request, then the server will return an error with the `422 UNNECESSARY_IDENTIFIER` error code. This will be the case even if the same device is identified by these two methods, as the server is unable to make this comparison. + - If the subject can be identified from the access token and the optional `phoneNumber` field is also included in the request, then the server will return an error with the `422 UNNECESSARY_IDENTIFIER` error code. This will be the case even if the same phone number is identified by these two methods, as the server is unable to make this comparison. + # Additional CAMARA error responses The list of error codes in this API specification is not exhaustive. Therefore the API specification MAY not document some non-mandatory error statuses as indicated in `CAMARA API Design Guide`. - Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified in the `API Readiness Checklist` document associated to this API version. + Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified from the `x-camara-commonalities` field, the changelog and the metadata of the released API version. As a specific rule, error `501 - NOT_IMPLEMENTED` can be only a possible error response if it is explicitly documented in the API. @@ -88,7 +90,7 @@ info: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html version: wip - x-camara-commonalities: 0.6 + x-camara-commonalities: 0.9.0 externalDocs: description: Product documentation at CAMARA url: https://github.com/camaraproject/SimSwap @@ -150,17 +152,17 @@ paths: RETRIEVE_MONITORED_NULL: $ref: "#/components/examples/RETRIEVE_MONITORED_NULL" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + $ref: "../common/CAMARA_common.yaml#/components/responses/IdentifierNotFound404" "422": - $ref: "#/components/responses/Generic422" + $ref: "../common/CAMARA_common.yaml#/components/responses/PhoneNumberIdentifier422" "429": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + $ref: "../common/CAMARA_common.yaml#/components/responses/TooManyRequests429" /check: post: security: @@ -199,86 +201,21 @@ paths: schema: $ref: "#/components/schemas/CheckSimSwapInfo" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequestWithRange400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + $ref: "../common/CAMARA_common.yaml#/components/responses/IdentifierNotFound404" "422": - $ref: "#/components/responses/Generic422" + $ref: "../common/CAMARA_common.yaml#/components/responses/PhoneNumberIdentifier422" "429": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + $ref: "../common/CAMARA_common.yaml#/components/responses/TooManyRequests429" components: securitySchemes: openId: $ref: "../common/CAMARA_common.yaml#/components/securitySchemes/openId" - responses: - Generic403: - description: Forbidden - headers: - x-correlator: - $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" - content: - application/json: - schema: - allOf: - - $ref: "../common/CAMARA_common.yaml#/components/schemas/ErrorInfo" - - type: object - properties: - status: - enum: - - 403 - code: - enum: - - PERMISSION_DENIED - examples: - GENERIC_403_PERMISSION_DENIED: - description: Permission denied. OAuth2 token access does not have the required scope or when the user fails operational security - value: - status: 403 - code: PERMISSION_DENIED - message: Client does not have sufficient permissions to perform this action. - Generic422: - description: Unprocessable Content - headers: - x-correlator: - $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" - content: - application/json: - schema: - allOf: - - $ref: "../common/CAMARA_common.yaml#/components/schemas/ErrorInfo" - - type: object - properties: - status: - enum: - - 422 - code: - enum: - - SERVICE_NOT_APPLICABLE - - MISSING_IDENTIFIER - - UNNECESSARY_IDENTIFIER - examples: - GENERIC_422_SERVICE_NOT_APPLICABLE: - description: Service not applicable for the provided identifier - value: - status: 422 - code: SERVICE_NOT_APPLICABLE - message: The service is not available for the provided identifier. - GENERIC_422_MISSING_IDENTIFIER: - description: phone number is not included in the request (in case of 2-legged) or the phone number identification cannot be derived from access token (in 3-legged) - value: - status: 422 - code: MISSING_IDENTIFIER - message: The device cannot be identified. - GENERIC_422_UNNECESSARY_IDENTIFIER: - description: An explicit identifier is provided when a device or phone number has already been identified from the access token - value: - status: 422 - code: UNNECESSARY_IDENTIFIER - message: The device is already identified by the access token. schemas: SimSwapInfo: type: object From b02f930e0eca7f96b8dae19488b53ad1a4faa766 Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Tue, 6 Oct 2026 11:52:26 +0200 Subject: [PATCH 2/3] test: align sim-swap-subscriptions.feature to Commonalities r4.4 event-subscription template --- .../sim-swap-subscriptions.feature | 941 +++++++++++------- 1 file changed, 596 insertions(+), 345 deletions(-) diff --git a/code/Test_definitions/sim-swap-subscriptions.feature b/code/Test_definitions/sim-swap-subscriptions.feature index 468fdd0..7728f1e 100644 --- a/code/Test_definitions/sim-swap-subscriptions.feature +++ b/code/Test_definitions/sim-swap-subscriptions.feature @@ -1,345 +1,596 @@ -Feature: CAMARA sim swap subscriptions API, vwip - # Input to be provided by the implementation to the tester - # - # Testing assets: - # * A mobile line identified by its phone number "phoneNumber" associated with a sim card 1 - # * Be able to perform a sim swap for this mobile line shifting from sim card 1 to sim card 2 - # * a callback url identified as "callbackUrl" allows to receive notification - # - # References to OAS spec schemas refer to schemas specified in sim-swap-subscriptions.yaml - - Background: Common subscriptions setup - Given the resource "/sim-swap-subscriptions/vwip/subscriptions" as BaseURL - And the header "Content-Type" is set to "application/json" - And the header "Authorization" is set to a valid access token - And the header "x-correlator" complies with the schema at "#/components/schemas/XCorrelator" - And the request body is set by default to a request body compliant with the schema - -########################## -# Happy path scenarios -########################## - - # These first scenarios serves as a minimum - - @sim_swap_subscription_creation_01_sync_creation - Scenario: Check sync subscription creation - This scenario could be bypass if async creation is provided (following scenario) - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And a valid phone number identified by the token or provided in the request body - And "$.sink" is set to provided callbackUrl - Then the response property "$.status" is 201 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has same value as the request header "x-correlator" - And the response body complies with the OAS schema at "#/components/schemas/Subscription" - And types, protocol, sink and config.subscriptionDetail.phoneNumber are present with provided value - And startsAt is valued with a datetime corresponding to the date time of the response - - @sim_swap_subscription_creation_02_async_creation - Scenario: Check async subscription creation - This scenario could be bypass if previous scenario is provided - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And a valid phone number identified by the token or provided in the request body - And "$.sink" is set to provided callbackUrl - Then the response property "$.status" is 202 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has same value as the request header "x-correlator" - And the response body complies with the OAS schema at "#/components/schemas/SubscriptionAsync" - - @sim_swap_subscription_retrieve_03_retrieve_by_id - Scenario: Check existing subscription is retrieved by id - Given a subscription is existing and identified by an "id" - And use BaseURL - When the HTTP "GET" request is sent with subscriptionId="id" - Then the response property "$.status" is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has same value as the request header "x-correlator" - And the response body complies with the OAS schema at "#/components/schemas/Subscription" - - @sim_swap_subscription_retrieve_04_retrieve_list_2legs - Scenario: Check existing subscription(s) is/are retreived in list - Given at least one subscription is existing for the API client making this request - And use BaseURL - When the HTTP "GET" request is sent - Then the response property "$.status" is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has same value as the request header "x-correlator" - And the response body complies with an array of OAS schema defined at "#/components/schemas/Subscription" - And subscription(s) is/are listed - - @sim_swap_subscription_retrieve_07_retrieve_list_3legs - Scenario: Check existing subscription(s) is/are retrieved in list - Given a subscription is existing for a phoneNumber - And this phone number is identified by the token - And use BaseURL - When the HTTP "GET" request is sent - Then the response property "$.status" is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has same value as the request header "x-correlator" - And the response body complies with an array of OAS schema defined at "#/components/schemas/Subscription" - And the subscriptions for this phoneNumber are listed - - @sim_swap_subscription_retrieve_08_retrieve_empty_list_3legs - Scenario: Check no existing subscription is retrieved in list - Given no subscription is existing for a phoneNumber - And this phone number is identified by the token - And use BaseURL - When the HTTP "GET" request is sent - Then the response property "$.status" is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has same value as the request header "x-correlator" - And the response body is an empty list - - @sim_swap_subscription_delete_05_delete_subscription - Scenario: Check deletion of existing subscription & triggering of subscription-ended event - Given a subscription is existing and identified by an "id" - And use BaseURL - When the HTTP "DELETE" request is sent with subscriptionId="id" - Then the response property "$.status" is 204 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has same value as the request header "x-correlator" - And the response body complies with the OAS schema at "#/components/schemas/Subscription" - And The callback notification application receives subscription-ended event at provided callbackUrl - And notification body complies with the OAS schema at "#/components/schemas/Subscription/CloudEvent" - And type="org.camaraproject.sim-swap-subscriptions.v0.subscription-ended" - And data.phoneNumber="$.phoneNumber" - And data.subscriptionId is valued with the subcriptionId - And time is valued by the date time of subscription termination - - @sim_swap_subscription_creation_06_swapped - Scenario: Check swapped event is triggered when a sim swap is performed on the device - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set with with provided phoneNumber - And "$.sink" is set to provided callbackUrl - Then the response property "$.status" is 201 - And subcriptionId is provided - And sims swap is performed on this mobile line - And The callback notification application receives swapped event at provided callbackUrl - And notification body complies with the OAS schema at "#/components/schemas/Subscription/CloudEvent" - And type="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And data.phoneNumber="$.phoneNumber" - And data.subscriptionId is valued with the subcriptionId - And time is valued by the date time of the sim swap - - @sim_swap_subscription_creation_07_subscription_ends_on_max_events - Scenario: Receive notification for subscription-ended event on max events reached - Given a valid subscription request body - And the request body property "$config.subscriptionMaxEvents" is set to 1 - When the request "createSimSwapSubscription" is sent - Then the response code is 201 - And the sim of the device was swapped - And event notification "swapped" is received on callback-url - And event notification "subscription-ended" is received on callback-url - And notification body complies with the OAS schema at "##/components/schemas/EventSubscriptionEnded" - And type="org.camaraproject.sim-swap-subscriptions.v0.subscription-ended" - And the response property "$.terminationReason" is "MAX_EVENTS_REACHED" - -######################### -# Rainy Day scenario -######################### - -# No test definition for 429 # - -################## -# Error code 400 -################## - - @sim_swap_subscription_creation_20_invalid_protocol - Scenario: subscription creation with invalid protocol - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"<>"HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set with with provided phoneNumber - And "$.sink" is set to provided callbackUrl - Then the response property "$.status" is 400 - And the response property "$.code" is "INVALID_PROTOCOL" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_21_invalid_credential - Scenario: subscription creation with invalid credential - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set with with provided phoneNumber - And "$.sink" is set to provided callbackUrl - And "$.sinkCredential.credentialType" <> "ACCESSTOKEN" - And "$.sinkCredential.accessTokenType" = "bearer" - And "$.sinkCredential.accessToken" is valued with a valid value - And "$.sinkCredential.accessTokenExpiresUtc" is valued with a valid value - Then the response property "$.status" is 400 - And the response property "$.code" is "INVALID_CREDENTIAL" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_22_invalid_token - Scenario: subscription creation with invalid token - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set with with provided phoneNumber - And "$.sink" is set to provided callbackUrl - And "$.sinkCredential.credentialType" = "ACCESSTOKEN" - And "$.sinkCredential.accessTokenType" <> "bearer" - And "$.sinkCredential.accessToken" is valued with a valid value - And "$.sinkCredential.accessTokenExpiresUtc" is valued with a valid value - Then the response property "$.status" is 400 - And the response property "$.code" is "INVALID_TOKEN" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_23_invalid_eventType - Scenario: subscription creation with invalid event type - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"<>"org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set with with provided phoneNumber - And "$.sink" is set to provided callbackUrl - Then the response property "$.status" is 400 - And the response property "$.code" is "INVALID_ARGUMENT" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_24_invalid_subscription_expire_time - Scenario: subscription creation with invalid expire time - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set with with provided phoneNumber - And "$.sink" is set to provided callbackUrl - And "$.config.subscriptionExpireTime" is set in the past - Then the response property "$.status" is 400 - And the response property "$.code" is "INVALID_ARGUMENT" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_25_require_input_properties_missing - Scenario Outline: subscription creation with required properties missing - Given use BaseURL - When the HTTP "POST" request is sent - And the request body property "" is not included - Then the response property "$.status" is 400 - And the response property "$.code" is "INVALID_ARGUMENT" - And the response property "$.message" contains a user friendly text - - Examples: - | input_property | - | $.protocol | - | $.sink | - | $.types | - | $.config.subscriptionDetail.phoneNumber | - - @sim_swap_subscription_creation_26_invalid_sink - Scenario: subscription creation with invalid sink - Given use BaseURL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set with with provided phoneNumber - And "$.sink" is not set to an url - And "$.config.subscriptionExpireTime" is set in the past - Then the response property "$.status" is 400 - And the response property "$.code" is "INVALID_ARGUMENT" - And the response property "$.message" contains a user friendly text - -################## -# Error Code 401 -################## - - @sim_swap_subscription_creation_40_no_authorization_header - Scenario: No Authorization header - Given the header "Authorization" is removed - And use BaseUrL - And the request body is set to a valid request body - When the HTTP "POST" request is sent - Then the response property "$.status" is 401 - And the response property "$.code" is "UNAUTHENTICATED" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_41_expired_access_token - Scenario: Expired access token - Given the header "Authorization" is set to an expired access token - And use BaseUrL - And the request body is set to a valid request body - When the HTTP "POST" request is sent - Then the response property "$.status" is 401 - And the response property "$.code" is "UNAUTHENTICATED" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_42_invalid_access_token - Scenario: Invalid access token - Given the header "Authorization" is set to an invalid access token - And use BaseUrL - And the request body is set to a valid request body - When the HTTP "POST" request is sent - Then the response header "Content-Type" is "application/json" - And the response property "$.status" is 401 - And the response property "$.code" is "UNAUTHENTICATED" - And the response property "$.message" contains a user friendly text - -################## -# Error Code 404 -################## - - @sim_swap_subscription_retrieve_80_not_found_retrieve_by_id - Scenario: Request to retrieve a non-existing subscription - Given use BaseURL - When the HTTP "GET" request is sent with subscriptionId set to non-existing subscription id - Then the response property "$.status" is 404 - And the response property "$.code" is "NOT_FOUND" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_delete_81_not_found_delete_by_id - Scenario: Request to delete a non-existing subscription - Given use BaseURL - When the HTTP "DELETE" request is sent subscriptionId set to non-existing subscription id - Then the response property "$.status" is 404 - And the response property "$.code" is "NOT_FOUND" - And the response property "$.message" contains a user friendly text - -################## -# Error Code 422 -################## - - @sim_swap_subscription_creation_101_phone_number_token_mismatch - Scenario: Inconsistent access token context for the phone number - # To test this, a token have to be obtained for a different phone number - Given the request body property "$.config.subscriptionDetail.phoneNumber" is set to a valid testing phone number - And the header "Authorization" is set to a valid access token identifying a phone number - And use BaseUrL - When the HTTP "POST" request is sent - Then the response property "$.status" is 422 - And the response property "$.code" is "UNNECESSARY_IDENTIFIER" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_100_not_applicable - Scenario: request for an unapplicable phone number for sim swap subscription - # To test this it is required to have a phone number not compatible with sim swap subscription - Given use BaseUrL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is set to a valid testing device that does not allow sim swap subscription - And "$.sink" is set to provided callbackUrl - Then the response property "$.status" is 422 - And the response property "$.code" is "UNSUPPORTED_IDENTIFIER" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscription_creation_102_missing_identifier - Scenario: request without any device identifier for sim swap subscription - Given use BaseUrL - When the HTTP "POST" request is sent - And "$.types"="org.camaraproject.sim-swap-subscriptions.v0.swapped" - And "$.protocol"="HTTP" - And "$.config.subscriptionDetail.phoneNumber" is not valued - And the valid access token does no identified a device - And "$.sink" is set to provided callbackUrl - Then the response property "$.status" is 422 - And the response property "$.code" is "MISSING_IDENTIFIER" - And the response property "$.message" contains a user friendly text +Feature: CAMARA Sim Swap Subscriptions API, vwip - Operations on subscriptions + + # Input to be provided by the implementation to the tester + # + # Testing assets: + # A sink-url identified as "callbackUrl", which receives notifications + # A mobile line identified by its phone number associated with a sim card 1 + # Be able to perform a sim swap for this mobile line shifting from sim card 1 to sim card 2 + # + # References to OAS spec schemas refer to schemas specified in sim-swap-subscriptions.yaml + + Background: Common sim-swap-subscriptions setup + Given the resource "/sim-swap-subscriptions/vwip/subscriptions" as BaseURL + And the header "Content-Type" is set to "application/json" + And the header "Authorization" is set to a valid access token + And the header "x-correlator" complies with the schema at "#/components/schemas/XCorrelator" + And the request body is set by default to a request body compliant with the schema + +############################ Happy Path Scenarios ############################################# + +# Note: Depending on the API managed personal data specific scenario update may be required to specify use of 2-legs or 3-legs access token. + + @sim_swap_subscriptions_01_Create_sim_swap_subscriptions_subscription_sync + Scenario: Create sim-swap-subscriptions subscription (sync creation) + # Some implementations may only support asynchronous subscription creation + Given that subscriptions are created synchronously + And a valid subscription request body + When the request "createSimSwapSubscription" is sent + Then the response code is 201 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + + @sim_swap_subscriptions_02_Create_sim_swap_subscriptions_subscription_async + Scenario: Create sim-swap-subscriptions subscription (async creation) + # Some implementations may only support synchronous subscription creation + Given that subscriptions are created asynchronously + And a valid subscription request body + When the request "createSimSwapSubscription" is sent + Then the response code is 202 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/SubscriptionAsync" + + @sim_swap_subscriptions_03_subscription_creation_event_validation + Scenario: Receive notification for subscription-started event on creation + Given a valid subscription request body + When the request "createSimSwapSubscription" is sent + Then the response code is 201 or 202 + And event notification "subscription-started" is received on callback-url + And notification body complies with the OAS schema at "#/components/schemas/EventSubscriptionStarted" + And type="org.camaraproject.sim-swap-subscriptions.v0.subscription-started" + And the response property "$.initiationReason" is "SUBSCRIPTION_CREATED" + + @sim_swap_subscriptions_04_Operation_to_retrieve_list_of_subscriptions_when_no_records + Scenario: Get a list of sim-swap-subscriptions subscriptions when no subscriptions available + Given a client without sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" + And the response body property "$.subscriptions" is an empty array + And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" + + @sim_swap_subscriptions_05_Operation_to_retrieve_list_of_subscriptions + Scenario: Get a list of subscriptions + Given a client with sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" + And each item in the response body property "$.subscriptions" complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" + + @sim_swap_subscriptions_06_Operation_to_retrieve_subscription_based_on_an_existing_subscription-id + Scenario: Get a subscription based on existing subscription-id. + Given the path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "retrieveSimSwapSubscription" is sent + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + + @sim_swap_subscriptions_07_Operation_to_delete_subscription_based_on_an_existing_subscription-id + Scenario: Delete a subscription based on existing subscription-id. + Given the path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "deleteSimSwapSubscription" is sent + Then the response code is 202 or 204 + And the response header "x-correlator" has the same value as the request header "x-correlator" + And if the response property "$.status" is 204 then the response body is not available + And if the response property "$.status" is 202 then the response body complies with the OAS schema at "#/components/schemas/SubscriptionAsync" + + @sim_swap_subscriptions_08_subscription_ends_on_expiry + Scenario: Receive notification for subscription-ended event on expiry + Given an existing sim-swap-subscriptions subscription with some value for the property "expiresAt" in the near future + When the subscription is expired + Then the event notification "subscription-ended" is received on callback-url + And notification body complies with the OAS schema at "#/components/schemas/EventSubscriptionEnded" + And type="org.camaraproject.sim-swap-subscriptions.v0.subscription-ended" + And the response property "$.terminationReason" is "SUBSCRIPTION_EXPIRED" + + @sim_swap_subscriptions_09_subscription_ends_on_max_events + Scenario: Receive notification for subscription-ended event on max events reached + Given an existing sim-swap-subscriptions subscription with the property "config.subscriptionMaxEvents" set to 1 + When the event subscribed occurs + Then event notification "swapped" is received on callback-url + And event notification "subscription-ended" is received on callback-url + And notification body complies with the OAS schema at "#/components/schemas/EventSubscriptionEnded" + And type="org.camaraproject.sim-swap-subscriptions.v0.subscription-ended" + And the response property "$.terminationReason" is "MAX_EVENTS_REACHED" + + @sim_swap_subscriptions_10_subscription_delete_event_validation + Scenario: Receive notification for subscription-ended event on deletion + Given the path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "deleteSimSwapSubscription" is sent + Then the response code is 202 or 204 + And event notification "subscription-ended" is received on callback-url + And notification body complies with the OAS schema at "#/components/schemas/EventSubscriptionEnded" + And type="org.camaraproject.sim-swap-subscriptions.v0.subscription-ended" + And the response property "$.terminationReason" is "SUBSCRIPTION_DELETED" + +######################### Scenario in case initialEvent is managed ############################## + + @sim_swap_subscriptions_11_subscription_creation_initial_event + Scenario: Receive initial event notification on creation + Given the API supports initial events to be sent + And a valid subscription request body with property "$.config.initialEvent" set to true + When the request "createSimSwapSubscription" is sent + Then the response code is 201 or 202 + And an event notification of the subscribed type is received on callback-url + And notification body complies with the OAS schema at "#/components/schemas/CloudEvent" + +######################### SIM Swap specific happy path scenario ############################## + + @sim_swap_subscriptions_swapped_event_validation + Scenario: Receive notification for swapped event when a SIM swap is performed + Given a valid subscription request body + And the request body property "$.types" is set to "org.camaraproject.sim-swap-subscriptions.v0.swapped" + When the request "createSimSwapSubscription" is sent + Then the response code is 201 + And a SIM swap is performed on the subscribed mobile line + And event notification "swapped" is received on callback-url + And notification body complies with the OAS schema at "#/components/schemas/CloudEvent" + And type="org.camaraproject.sim-swap-subscriptions.v0.swapped" + And data.subscriptionId is valued with the subscriptionId + +######################### Additional Happy Path Scenarios ############################## + + @sim_swap_subscriptions_12_Create_sim_swap_subscriptions_subscription_sync_with_accesstoken_sink_credential + Scenario: Create sim-swap-subscriptions subscription (sync creation) with ACCESSTOKEN sinkCredential + # Some implementations may only support asynchronous subscription creation + # Some implementations may decide to not return the sinkCredential in the response (data minimization principle) + Given that subscriptions are created synchronously + And a valid subscription request body + And the request property "$.sinkCredential.credentialType" is set to "ACCESSTOKEN" + And the request property "$.sinkCredential.accessTokenType" is set to "bearer" + And the request property "$.sinkCredential.accessToken" is set to a valid access token + And the request property "$.sinkCredential.accessTokenExpiresUtc" is set to a valid expiry date in the future + When the request "createSimSwapSubscription" is sent + Then the response code is 201 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType", if present, is set to value "ACCESSTOKEN" + And the response body property "$.sinkCredential.accessTokenExpiresUtc", if present, is set to the same value of the request property "$.sinkCredential.accessTokenExpiresUtc" + + @sim_swap_subscriptions_13_Create_sim_swap_subscriptions_subscription_sync_with_private_jwt_key_sink_credential_out_of_band_provisioning + Scenario: Create sim-swap-subscriptions subscription (sync creation) with PRIVATE_JWT_KEY sinkCredential, out-of-band provisioning + # Some implementations may only support asynchronous subscription creation + # Some implementations may only support out_of_band provisioning + Given that subscriptions are created synchronously + And a valid subscription request body + And the request property "$.sinkCredential.credentialType" is set to "PRIVATE_JWT_KEY" + When the request "createSimSwapSubscription" is sent + Then the response code is 201 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + + @sim_swap_subscriptions_14_Create_sim_swap_subscriptions_subscription_sync_with_private_jwt_key_sink_credential_in_band_provisioning + Scenario: Create sim-swap-subscriptions subscription (sync creation) with PRIVATE_JWT_KEY sinkCredential, in-band provisioning + # Some implementations may only support asynchronous subscription creation + # Some implementations may additionally support in_band provisioning + Given that subscriptions are created synchronously + And a valid subscription request body + And the request property "$.sinkCredential.credentialType" is set to "PRIVATE_JWT_KEY" + And the request property "$.sinkCredential.clientId" is set to a valid value + And the request property "$.sinkCredential.tokenUri" is set to a valid value + When the request "createSimSwapSubscription" is sent + Then the response code is 201 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType" is set to value "PRIVATE_JWT_KEY" + And the response body property "$.sinkCredential.jwksUri" is set to a valid value + + @sim_swap_subscriptions_15_Operation_to_retrieve_subscription_based_on_an_existing_subscription-id_access_token_sink_credential_returned + # Some implementations may decide to not return the sinkCredential in the response (data minimization principle) + Scenario: Get a subscription based on existing subscription-id, with ACCESSTOKEN sinkCredential returned. + Given the path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "retrieveSimSwapSubscription" is sent + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType", if present, is set to value "ACCESSTOKEN" + And the response body property "$.sinkCredential.accessTokenExpiresUtc", if present, is set to the same value of the request property "$.sinkCredential.accessTokenExpiresUtc" + + @sim_swap_subscriptions_16_Operation_to_retrieve_subscription_based_on_an_existing_subscription-id_private_jwt_key_sink_credential_returned + # Some implementations may decide to not return the sinkCredential in the response (data minimization principle) + # Mainly applicable for in-band provisioning of PRIVATE_JWT_KEY mode for a given subscription + Scenario: Get a subscription based on existing subscription-id, with PRIVATE_JWT_KEY sinkCredential returned. + Given the path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "retrieveSimSwapSubscription" is sent + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType" is set to value "PRIVATE_JWT_KEY" + And the response body property "$.sinkCredential.jwksUri" is set to a valid value + +########################### Error response scenarios ############################################ +########################### Subscription creation scenarios ##################################### + + @sim_swap_subscriptions_20_creation_sim_swap_subscriptions_subscription_with_invalid_parameter + Scenario: Create sim-swap-subscriptions subscription with invalid parameter + Given the request body is not compliant with the schema "#/components/schemas/SubscriptionRequest" + When the request "createSimSwapSubscription" is sent + Then the response code is 400 + And the response property "$.status" is 400 + And the response property "$.code" is "INVALID_ARGUMENT" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_21_creation_of_subscription_with_expiry_time_in_past + Scenario: Expiry time in past + Given a valid sim-swap-subscriptions subscription request body + And request body property "$.config.subscriptionExpireTime" in the past + When the request "createSimSwapSubscription" is sent + Then the response code is 400 + And the response property "$.status" is 400 + And the response property "$.code" is "INVALID_ARGUMENT" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_subscription_22_creation_with_invalid_eventType + Scenario: Subscription creation with invalid event type + Given a valid sim-swap-subscriptions subscription request body + And the request body property "$.types" is set to invalid value + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 400 + And the response property "$.code" is "INVALID_ARGUMENT" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_subscription_23_invalid_protocol + Scenario: Subscription creation with invalid protocol + Given a valid sim-swap-subscriptions subscription request body + And the request property "$.protocol" is not set to "HTTP" + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 400 + And the response property "$.code" is "INVALID_PROTOCOL" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_subscription_24_invalid_credential + Scenario: Subscription creation with invalid credential + Given a valid sim-swap-subscriptions subscription request body + And the request property "$.protocol" is set to "HTTP" + And the request property "$.sinkCredential.credentialType" is not set to "ACCESSTOKEN" and is not set to "PRIVATE_KEY_JWT" + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 400 + And the response property "$.code" is "INVALID_CREDENTIAL" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_subscription_25_invalid_token + Scenario: Subscription creation with invalid token + Given a valid sim-swap-subscriptions subscription request body + And the request property "$.protocol" is set to "HTTP" + And the request property "$.sinkCredential.credentialType" is set to "ACCESSTOKEN" + And the request property "$.sinkCredential.accessTokenType" is not set to "bearer" + And the request property "$.sinkCredential.accessToken" is valued with a valid value + And the request property "$.sinkCredential.accessTokenExpiresUtc" is valued with a valid value + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 400 + And the response property "$.code" is "INVALID_TOKEN" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_subscription_26_invalid_url + Scenario: Subscription creation with invalid url + Given a valid sim-swap-subscriptions subscription request body + And the request property "$.protocol" is set to "HTTP" + And the request property "$.sink" is set to "invalid-url" + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 400 + And the response property "$.code" is "INVALID_SINK" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_27_no_authorization_header_for_create_subscription + Scenario: No Authorization header for create subscription + Given a valid sim-swap-subscriptions subscription request body + And the request does not include the "Authorization" header + When the request "createSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_28_expired_access_token_for_create_subscription + Scenario: Expired access token for create subscription + Given a valid sim-swap-subscriptions subscription request body and header "Authorization" is expired + When the request "createSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_29_invalid_access_token_for_create_subscription + Scenario: Invalid access token for create subscription + Given a valid sim-swap-subscriptions subscription request body + And header "Authorization" set to an invalid access token + When the request "createSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + +########################### Subscription retrieval scenarios ##################################### + + @sim_swap_subscriptions_30_no_authorization_header_for_get_subscription + Scenario: No Authorization header for get subscription + Given header "Authorization" is not present + And path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "retrieveSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_31_expired_access_token_for_get_subscription + Scenario: Expired access token for get subscription + Given the header "Authorization" is set to expired token + And path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "retrieveSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_32_invalid_access_token_for_get_subscription + Scenario: Invalid access token for get subscription + Given the header "Authorization" set to an invalid access token + And path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "retrieveSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_33_get_unknown_sim_swap_subscriptions_subscription_for_a_device + Scenario: Get method for sim-swap-subscriptions subscription with subscription-id unknown to the system + Given the path parameter "subscriptionId" is set to a value not corresponding to any existing subscription + When the request "retrieveSimSwapSubscription" is sent + Then the response code is 404 + And the response property "$.status" is 404 + And the response property "$.code" is "NOT_FOUND" + And the response property "$.message" contains a user friendly text + +########################### Subscription list retrieval scenarios ##################################### + + @sim_swap_subscriptions_40_no_authorization_header_for_list_subscription + Scenario: No Authorization header for list subscription + Given header "Authorization" is not present + When the request "retrieveSimSwapSubscriptionList" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_41_expired_access_token_for_list_subscription + Scenario: Expired access token for list subscription + Given the header "Authorization" is set to expired token + When the request "retrieveSimSwapSubscriptionList" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_42_invalid_access_token_for_list_subscription + Scenario: Invalid access token for list subscription + Given the header "Authorization" set to an invalid access token + When the request "retrieveSimSwapSubscriptionList" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + +########################### Subscription deletion scenarios ##################################### + + @sim_swap_subscriptions_50_no_authorization_header_for_delete_subscription + Scenario: No Authorization header for delete subscription + Given header "Authorization" is set without a token + And path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "deleteSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_51_expired_access_token_for_delete_subscription + Scenario: Expired access token for delete subscription + Given header "Authorization" is set with an expired token + And path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "deleteSimSwapSubscription" is sent + Then the response status code is 401 + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_52_invalid_access_token_for_delete_subscription + Scenario: Invalid access token for delete subscription + Given header "Authorization" set to an invalid access token + And path parameter "subscriptionId" is set to the identifier of an existing sim-swap-subscriptions subscription + When the request "deleteSimSwapSubscription" is sent + Then the response status code is 401 + And the response header "Content-Type" is "application/json" + And the response property "$.status" is 401 + And the response property "$.code" is "UNAUTHENTICATED" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_53_delete_invalid_sim_swap_subscriptions_subscription + Scenario: Delete sim-swap-subscriptions subscription with subscription-id unknown to the system + Given the path parameter "subscriptionId" is set to a value not corresponding to any existing subscription + When the request "deleteSimSwapSubscription" is sent + Then the response code is 404 + And the response property "$.status" is 404 + And the response property "$.code" is "NOT_FOUND" + And the response property "$.message" contains a user friendly text + +######## Specific Subscription error scenario if multi-event is not permitted ################ + + @sim_swap_subscriptions_60_creation_with_unsupported_multiple_event_type + Scenario: Multi event subscription not supported + Given the API provider only allows one event to be subscribed per subscription request + And a valid subscription request body + And the request body property "$.types" is set to an array with 2 valid items + When the request "createSimSwapSubscription" is sent + Then the response code is 422 + And the response property "$.status" is 422 + And the response property "$.code" is "MULTIEVENT_SUBSCRIPTION_NOT_SUPPORTED" + And the response property "$.message" contains a user friendly text + +######## Specific Subscription error scenario if Private JWT Key is not pre-configured ################ + + @sim_swap_subscriptions_61_creation_with_private_jwt_key_not_configured + Scenario: Private JWT Key not configured for subscription creation + Given the API provider requires the use of a Private JWT key mechanism for subscription creation authentication + And the Private JWT key mechanism is not pre-configured in the environment + And a valid subscription request body with the property "$.sinkCredential.credentialType" set to "PRIVATE_KEY_JWT" + When the request "createSimSwapSubscription" is sent + Then the response code is 422 + And the response property "$.status" is 422 + And the response property "$.code" is "PRIVATE_KEY_JWT_NOT_CONFIGURED" + And the response property "$.message" contains a user friendly text + +######## SIM Swap specific 422 error scenarios ################ + + @sim_swap_subscriptions_C02.01_phone_number_not_schema_compliant + Scenario: Phone number value does not comply with the schema + Given the request body property "$.config.subscriptionDetail.phoneNumber" does not comply with the OAS schema at "#/components/schemas/PhoneNumber" + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 400 + And the response property "$.code" is "INVALID_ARGUMENT" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_C02.02_phone_number_not_found + Scenario: Phone number not found + Given the request body property "$.config.subscriptionDetail.phoneNumber" is set to a valid but non-existing phone number + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 404 + And the response property "$.code" is "IDENTIFIER_NOT_FOUND" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_C02.03_unnecessary_phone_number + Scenario: Phone number not to be included when it can be deduced from the access token + Given the header "Authorization" is set to a valid access token identifying a phone number + And the request body property "$.config.subscriptionDetail.phoneNumber" is set to a valid phone number + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 422 + And the response property "$.code" is "UNNECESSARY_IDENTIFIER" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_C02.04_missing_phone_number + Scenario: Phone number not included and cannot be deducted from the access token + Given the header "Authorization" is set to a valid access token not identifying a phone number + And the request body property "$.config.subscriptionDetail.phoneNumber" is not included + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 422 + And the response property "$.code" is "MISSING_IDENTIFIER" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_C02.05_phone_number_not_supported + Scenario: Phone number not supported by the service + Given the request body property "$.config.subscriptionDetail.phoneNumber" is set to a valid phone number not supported by the service + When the request "createSimSwapSubscription" is sent + Then the response property "$.status" is 422 + And the response property "$.code" is "UNSUPPORTED_IDENTIFIER" + And the response property "$.message" contains a user friendly text + +######## Subscription Pagination Scenarios (Optional depending on API initiative) ################ + +# Check applicability of below tests according to the nature of the API initiative Use Cases + + @sim_swap_subscriptions_70_pagination_default_values + Scenario: Subscription list pagination with default values for page and perPage + Given an API client with more than 20 sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent without setting query parameters "page" and "perPage" + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response header "Link" contains a link to the next page with rel="next" + And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" + And the response body property "$.subscriptions" has 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" + And the response body property "$.pagination.page" is 1 + And the response body property "$.pagination.perPage" is 20 + + @sim_swap_subscriptions_71_pagination_custom_values + Scenario: Subscription list pagination with custom values for page and perPage + Given an API client with more than 40 sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent with query parameters "page" set to 1 and "perPage" set to 20 + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response header "Link" contains a link to the next page with rel="next" + And the response header "X-Total-Pages" is equal to the value of the response body property "$.pagination.totalPages" + And the response header "X-Total-Count" is equal to the value of the response body property "$.pagination.totalCount" + And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" + And the response body property "$.subscriptions" has 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" + And the response body property "$.pagination.page" is 1 + And the response body property "$.pagination.perPage" is 20 + And the response body property "$.pagination.totalPages", if present, is greater than 2 + And the response body property "$.pagination.totalCount", if present, is greater than 40 + + @sim_swap_subscriptions_72_pagination_middle_page + Scenario: Subscription list pagination fetching a middle page of the list + Given an API client with more than 40 sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent with query parameters "page" set to 2 and "perPage" set to 20 + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response header "Link" contains a link to the previous page with rel="prev" and a link to the next page with rel="next" + And the response header "X-Total-Pages" is equal to the value of the response body property "$.pagination.totalPages" + And the response header "X-Total-Count" is equal to the value of the response body property "$.pagination.totalCount" + And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" + And the response body property "$.subscriptions" has 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" + And the response body property "$.pagination.page" is 2 + And the response body property "$.pagination.perPage" is 20 + And the response body property "$.pagination.totalPages", if present, is greater than 2 + And the response body property "$.pagination.totalCount", if present, is greater than 40 + + @sim_swap_subscriptions_73_pagination_last_page + Scenario: Subscription list pagination fetching the last page of the list + Given an API client with more than 40 and less than 60 sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent with query parameters "page" set to 3 and "perPage" set to 20 + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response header "Link" contains a link to the previous page with rel="prev" + And the response header "X-Total-Pages" is equal to the value of the response body property "$.pagination.totalPages" + And the response header "X-Total-Count" is equal to the value of the response body property "$.pagination.totalCount" + And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" + And the response body property "$.subscriptions" has between 1 and 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" + And the response body property "$.pagination.page" is 3 + And the response body property "$.pagination.perPage" is 20 + And the response body property "$.pagination.totalPages", if present, is 3 + And the response body property "$.pagination.totalCount", if present, is greater than 40 and less than 60 + + @sim_swap_subscriptions_74_pagination_invalid_page_parameter + Scenario: Subscription list pagination with invalid value for page parameter + Given an API client with more than 20 sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent with query parameter "page" set to any value less than 1 and "perPage" set to 20 + Then the response code is 400 + And the response property "$.status" is 400 + And the response property "$.code" is "INVALID_ARGUMENT" + And the response property "$.message" contains a user friendly text + + @sim_swap_subscriptions_75_pagination_invalid_perPage_parameter + Scenario: Subscription list pagination with invalid value for perPage parameter + Given an API client with more than 20 sim-swap-subscriptions subscriptions created + When the request "retrieveSimSwapSubscriptionList" is sent with query parameter "page" set to 1 and "perPage" set to any value outside the range [1-100] + Then the response code is 400 + And the response property "$.status" is 400 + And the response property "$.code" is "INVALID_ARGUMENT" + And the response property "$.message" contains a user friendly text From dcd65d8521e0a7cb135669acaa405b7180e57b9a Mon Sep 17 00:00:00 2001 From: Alberto Ramos Monagas Date: Tue, 6 Oct 2026 11:58:23 +0200 Subject: [PATCH 3/3] =?UTF-8?q?test:=20align=20sim-swap-subscriptions.feat?= =?UTF-8?q?ure=20to=20Commonalities=20r4.4=20event-subscription=20template?= =?UTF-8?q?=20(24=E2=86=9246=20scenarios)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sim-swap-subscriptions.feature | 89 ------------------- 1 file changed, 89 deletions(-) diff --git a/code/Test_definitions/sim-swap-subscriptions.feature b/code/Test_definitions/sim-swap-subscriptions.feature index 7728f1e..a256ff7 100644 --- a/code/Test_definitions/sim-swap-subscriptions.feature +++ b/code/Test_definitions/sim-swap-subscriptions.feature @@ -505,92 +505,3 @@ Feature: CAMARA Sim Swap Subscriptions API, vwip - Operations on subscriptions And the response property "$.code" is "UNSUPPORTED_IDENTIFIER" And the response property "$.message" contains a user friendly text -######## Subscription Pagination Scenarios (Optional depending on API initiative) ################ - -# Check applicability of below tests according to the nature of the API initiative Use Cases - - @sim_swap_subscriptions_70_pagination_default_values - Scenario: Subscription list pagination with default values for page and perPage - Given an API client with more than 20 sim-swap-subscriptions subscriptions created - When the request "retrieveSimSwapSubscriptionList" is sent without setting query parameters "page" and "perPage" - Then the response code is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has the same value as the request header "x-correlator" - And the response header "Link" contains a link to the next page with rel="next" - And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" - And the response body property "$.subscriptions" has 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" - And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" - And the response body property "$.pagination.page" is 1 - And the response body property "$.pagination.perPage" is 20 - - @sim_swap_subscriptions_71_pagination_custom_values - Scenario: Subscription list pagination with custom values for page and perPage - Given an API client with more than 40 sim-swap-subscriptions subscriptions created - When the request "retrieveSimSwapSubscriptionList" is sent with query parameters "page" set to 1 and "perPage" set to 20 - Then the response code is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has the same value as the request header "x-correlator" - And the response header "Link" contains a link to the next page with rel="next" - And the response header "X-Total-Pages" is equal to the value of the response body property "$.pagination.totalPages" - And the response header "X-Total-Count" is equal to the value of the response body property "$.pagination.totalCount" - And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" - And the response body property "$.subscriptions" has 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" - And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" - And the response body property "$.pagination.page" is 1 - And the response body property "$.pagination.perPage" is 20 - And the response body property "$.pagination.totalPages", if present, is greater than 2 - And the response body property "$.pagination.totalCount", if present, is greater than 40 - - @sim_swap_subscriptions_72_pagination_middle_page - Scenario: Subscription list pagination fetching a middle page of the list - Given an API client with more than 40 sim-swap-subscriptions subscriptions created - When the request "retrieveSimSwapSubscriptionList" is sent with query parameters "page" set to 2 and "perPage" set to 20 - Then the response code is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has the same value as the request header "x-correlator" - And the response header "Link" contains a link to the previous page with rel="prev" and a link to the next page with rel="next" - And the response header "X-Total-Pages" is equal to the value of the response body property "$.pagination.totalPages" - And the response header "X-Total-Count" is equal to the value of the response body property "$.pagination.totalCount" - And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" - And the response body property "$.subscriptions" has 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" - And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" - And the response body property "$.pagination.page" is 2 - And the response body property "$.pagination.perPage" is 20 - And the response body property "$.pagination.totalPages", if present, is greater than 2 - And the response body property "$.pagination.totalCount", if present, is greater than 40 - - @sim_swap_subscriptions_73_pagination_last_page - Scenario: Subscription list pagination fetching the last page of the list - Given an API client with more than 40 and less than 60 sim-swap-subscriptions subscriptions created - When the request "retrieveSimSwapSubscriptionList" is sent with query parameters "page" set to 3 and "perPage" set to 20 - Then the response code is 200 - And the response header "Content-Type" is "application/json" - And the response header "x-correlator" has the same value as the request header "x-correlator" - And the response header "Link" contains a link to the previous page with rel="prev" - And the response header "X-Total-Pages" is equal to the value of the response body property "$.pagination.totalPages" - And the response header "X-Total-Count" is equal to the value of the response body property "$.pagination.totalCount" - And the response body complies with the OAS schema at "#/components/schemas/SubscriptionList" - And the response body property "$.subscriptions" has between 1 and 20 items and each item complies with the OAS schema at "#/components/schemas/Subscription" - And the response body property "$.pagination" complies with the OAS schema at "#/components/schemas/Pagination" - And the response body property "$.pagination.page" is 3 - And the response body property "$.pagination.perPage" is 20 - And the response body property "$.pagination.totalPages", if present, is 3 - And the response body property "$.pagination.totalCount", if present, is greater than 40 and less than 60 - - @sim_swap_subscriptions_74_pagination_invalid_page_parameter - Scenario: Subscription list pagination with invalid value for page parameter - Given an API client with more than 20 sim-swap-subscriptions subscriptions created - When the request "retrieveSimSwapSubscriptionList" is sent with query parameter "page" set to any value less than 1 and "perPage" set to 20 - Then the response code is 400 - And the response property "$.status" is 400 - And the response property "$.code" is "INVALID_ARGUMENT" - And the response property "$.message" contains a user friendly text - - @sim_swap_subscriptions_75_pagination_invalid_perPage_parameter - Scenario: Subscription list pagination with invalid value for perPage parameter - Given an API client with more than 20 sim-swap-subscriptions subscriptions created - When the request "retrieveSimSwapSubscriptionList" is sent with query parameter "page" set to 1 and "perPage" set to any value outside the range [1-100] - Then the response code is 400 - And the response property "$.status" is 400 - And the response property "$.code" is "INVALID_ARGUMENT" - And the response property "$.message" contains a user friendly text