From 43450edecb9091d5b9f1b2367def708a3f584fc5 Mon Sep 17 00:00:00 2001
From: Elian Kraja <e.kraja@nextworks.it>
Date: Mon, 8 Oct 2018 12:56:14 +0200
Subject: [PATCH] Added functional tests for VNF Performance Management

---
 .../IndividualPmJob.robot                     |   4 +-
 .../IndividualSubscription.robot              |  81 ++++++++
 .../IndividualThreshold.robot                 |  88 +++++++++
 .../PMJobs.robot                              |  16 +-
 .../Subscriptions.robot                       | 184 ++++++++++++++++++
 .../environment/generic.txt                   |   1 +
 .../environment/individualSubscription.txt    |   3 +
 .../environment/individualThresholds.txt      |   3 +
 .../environment/subscriptions.txt             |   3 +
 .../{subscriptios.json => subscriptions.json} |   2 +-
 .../schemas/PmSubscriptions.schema.json       |   1 +
 .../schemas/Subscriptions.schema.json         |   1 +
 12 files changed, 376 insertions(+), 11 deletions(-)
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/IndividualSubscription.robot
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/IndividualThreshold.robot
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/Subscriptions.robot
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/environment/individualSubscription.txt
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/environment/individualThresholds.txt
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/environment/subscriptions.txt
 rename SOL003/VNFPerformanceManagement-API_nxw/jsons/{subscriptios.json => subscriptions.json} (53%)
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/schemas/PmSubscriptions.schema.json
 create mode 100644 SOL003/VNFPerformanceManagement-API_nxw/schemas/Subscriptions.schema.json

diff --git a/SOL003/VNFPerformanceManagement-API_nxw/IndividualPmJob.robot b/SOL003/VNFPerformanceManagement-API_nxw/IndividualPmJob.robot
index ac04b79c..91f5227e 100644
--- a/SOL003/VNFPerformanceManagement-API_nxw/IndividualPmJob.robot
+++ b/SOL003/VNFPerformanceManagement-API_nxw/IndividualPmJob.robot
@@ -37,7 +37,7 @@ GET Individual PM Job - Negative (Not Found)
 
 GET Individual PM Job - Negative (Unauthorized: Wrong Token)
     Log    Trying to perform a negative get, using wrong authorization bearer
-    Pass Execution If    ${AUTH_USAGE} == 0    Skipping test as VNFM is not supporting authentication
+    Pass Execution If    ${VNFM_AUTH_USAGE} == 0    Skipping test as VNFM is not supporting authentication
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     Set Request Header    Authorization    ${NEG_AUTHORIZATION}
@@ -53,7 +53,7 @@ GET Individual PM Job - Negative (Unauthorized: Wrong Token)
 
 GET Individual PM Job - Negative (Unauthorized: No Token)
     Log    Trying to perform a negative get, without authentication token.
-    Pass Execution If    ${AUTH_USAGE} == 0    Skipping test as NFVO is not supporting authentication
+    Pass Execution If    ${VNFM_AUTH_USAGE} == 0    Skipping test as VNFM is not supporting authentication
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     GET    ${apiRoot}/${apiName}/${apiVersion}/pm_jobs/${pmJobId}
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/IndividualSubscription.robot b/SOL003/VNFPerformanceManagement-API_nxw/IndividualSubscription.robot
new file mode 100644
index 00000000..6bb132dd
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/IndividualSubscription.robot
@@ -0,0 +1,81 @@
+*** Settings ***
+Documentation     This resource represents subscriptions. The client can use this resource to subscribe to notifications related to VNF
+...               performance management and to query its subscriptions.
+Library           JSONSchemaLibrary    schemas/
+Resource          environment/generic.txt    # Generic Parameters
+Library           REST    ${VNFM_SCHEMA}://${VNFM_HOST}:${VNFM_PORT}
+Library           OperatingSystem
+Library           JSONLibrary
+Resource          environment/individualSubscription.txt
+
+*** Test Cases ***
+GET Individual Subscription
+    [Documentation]    The client can use this method for reading an individual subscription about Performance management notifications
+    ...    subscribed by the client.
+    ...    This method shall follow the provisions specified in the tables 6.4.8.3.2-1 and 6.4.8.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    GET    ${apiRoot}/${apiName}/${apiVersion}/subscriptions/${subscriptionId}
+    Integer    response status    200
+    Log    Received a 200 OK as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    PmSubscription.schema.json    ${json}
+    Log    Validated PmSubscription schema
+
+GET Individual Subscription - Negative (Not Found)
+    [Documentation]    The client can use this method for reading an individual subscription about Performance management notifications
+    ...    subscribed by the client.
+    ...    This method shall follow the provisions specified in the tables 6.4.8.3.2-1 and 6.4.8.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"
+    GET    ${apiRoot}/${apiName}/${apiVersion}/subscriptions/${erroneousSubscriptionId}
+    Integer    response status    404
+    Log    Received a 404 Not found as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    ProblemDetails.schema.json    ${json}
+    Log    Validated ProblemDetails schema
+
+POST Individual Subscription - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    POST    ${apiRoot}/${apiName}/${apiVersion}/subscriptions/${subscriptionId}
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
+
+PUT Individual Subscription - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    PUT    ${apiRoot}/${apiName}/${apiVersion}/subscriptions/${subscriptionId}
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
+
+PATCH Individual Subscription - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    PATCH    ${apiRoot}/${apiName}/${apiVersion}/subscriptions/${subscriptionId}
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
+
+DELETE Individual Subscription - (Method not implemented)
+    [Documentation]    This method terminates an individual subscription.
+    ...    This method shall follow the provisions specified in the tables 6.4.8.3.5-1 and 6.4.8.3.5-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"
+    DELETE    ${apiRoot}/${apiName}/${apiVersion}/subscriptions/${subscriptionId}
+    Integer    response status    204
+    Log    Received a 204 No Content as expected
+    ${body}=    Output    response body
+    Should Be Empty    ${body}
+    Log    Body of the response is empty
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/IndividualThreshold.robot b/SOL003/VNFPerformanceManagement-API_nxw/IndividualThreshold.robot
new file mode 100644
index 00000000..75240e97
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/IndividualThreshold.robot
@@ -0,0 +1,88 @@
+*** Settings ***
+Documentation     This resource represents an individual threshold.
+Library           JSONSchemaLibrary    schemas/
+Resource          environment/generic.txt    # Generic Parameters
+Library           JSONLibrary
+Library           REST    ${VNFM_SCHEMA}://${VNFM_HOST}:${VNFM_PORT}
+Library           OperatingSystem
+Resource          environment/individualThresholds.txt
+
+*** Test Cases ***
+GET Individual Threshold
+    [Documentation]    The client can use this method to query information about thresholds.
+    ...    This method shall follow the provisions specified in the tables 6.4.5.3.2-1 and 6.4.5.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set Headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    GET    ${apiRoot}/${apiName}/${apiVersion}/thresholds/${thresholdId}
+    Integer    response status    200
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Log    Trying to validate result with thresholds schema
+    Validate Json    Thresholds.schema.json    ${json}
+
+GET Individual Threshold - Negative (Not Found)
+    [Documentation]    The client can use this method to query information about thresholds.
+    ...    This method shall follow the provisions specified in the tables 6.4.5.3.2-1 and 6.4.5.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set Headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    GET    ${apiRoot}/${apiName}/${apiVersion}/thresholds/${erroneousThresholdId}
+    Integer    response status    404
+    Log    Received 404 Not Found as expected
+    ${problemDetails}=    Output    response body
+    ${json}=    evaluate    json.loads('''${problemDetails}''')    json
+    Log    Trying to validate ProblemDetails
+    Validate Json    ProblemDetails.schema.json    ${json}
+    Log    Validation OK
+
+DELETE Individual Threshold
+    [Documentation]    This method allows to delete a threshold.
+    ...    This method shall follow the provisions specified in the tables 6.4.6.3.5-1, and 6.4.6.3.5-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    DELETE    ${apiRoot}/${apiName}/${apiVersion}/thresholds/${thresholdId}
+    Integer    response status    204
+    Log    Received 204 No Content as expected
+    ${body}=    Output    response body
+    Should Be Empty    ${body}
+    Log    Body of the response is empty
+
+DELETE Individual Threshold - Negative (Not Found)
+    [Documentation]    This method allows to delete a threshold.
+    ...    This method shall follow the provisions specified in the tables 6.4.6.3.5-1, and 6.4.6.3.5-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    DELETE    ${apiRoot}/${apiName}/${apiVersion}/thresholds/${erroneousThresholdId}
+    Integer    response status    404
+    Log    Received 404 Not Found as expected
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Log    Trying to validate result with ProblemDetails schema
+    Validate Json    ProblemDetails.schema.json    ${json}
+
+POST Individual Threshold - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    POST    ${apiRoot}/${apiName}/${apiVersion}/thresholds
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
+
+PUT Individual Threshold - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    PUT    ${apiRoot}/${apiName}/${apiVersion}/thresholds
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
+
+PATCH Individual Threshold - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    PATCH    ${apiRoot}/${apiName}/${apiVersion}/thresholds
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/PMJobs.robot b/SOL003/VNFPerformanceManagement-API_nxw/PMJobs.robot
index ce221786..6c68a111 100644
--- a/SOL003/VNFPerformanceManagement-API_nxw/PMJobs.robot
+++ b/SOL003/VNFPerformanceManagement-API_nxw/PMJobs.robot
@@ -8,7 +8,7 @@ Resource          environment/pmJobs.txt
 
 *** Test Cases ***
 GET all Pm Jobs
-    Log    Trying to get all PM Jobs present in the NFVO Catalogue
+    Log    Trying to get all PM Jobs present in the VNFM
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Request Header    Authorization    ${VNFM_AUTHENTICATION}
@@ -22,7 +22,7 @@ GET all Pm Jobs
     Log    Validation OK
 
 GET all Pm Jobs - Filter
-    Log    Trying to get all PM Jobs present in the NFVO Catalogue, using filter params
+    Log    Trying to get all PM Jobs present in the VNFM, using filter params
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Request Header    Authorization    ${VNFM_AUTHENTICATION}
@@ -36,7 +36,7 @@ GET all Pm Jobs - Filter
     Log    Validation OK
 
 GET all Pm Jobs - Negative (wronge filter name)
-    Log    Trying to get all PM Jobs present in the NFVO Catalogue, using an erroneous filter param
+    Log    Trying to get all PM Jobs present in the VNFM, using an erroneous filter param
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Request Header    Authorization    ${VNFM_AUTHENTICATION}
@@ -52,7 +52,7 @@ GET all Pm Jobs - Negative (wronge filter name)
 
 GET all Pm Jobs - Negative (Unauthorized: Wrong Token)
     Log    Trying to perform a negative get, using wrong authorization bearer
-    Pass Execution If    ${AUTH_USAGE} == 0    Skipping test as NFVO is not supporting authentication
+    Pass Execution If    ${VNFM_AUTH_USAGE} == 0    Skipping test as VNFM is not supporting authentication
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     Set Request Header    Authorization    ${NEG_AUTHORIZATION}
@@ -68,7 +68,7 @@ GET all Pm Jobs - Negative (Unauthorized: Wrong Token)
 
 GET all Pm Jobs - Negative (Unauthorized: No Token)
     Log    Trying to perform a negative get, using wrong authorization bearer
-    Pass Execution If    ${AUTH_USAGE} == 0    Skipping test as NFVO is not supporting authentication
+    Pass Execution If    ${VNFM_AUTH_USAGE} == 0    Skipping test as VNFM is not supporting authentication
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     GET    ${apiRoot}/${apiName}/${apiVersion}/pm_jobs
@@ -82,7 +82,7 @@ GET all Pm Jobs - Negative (Unauthorized: No Token)
     Log    Validation OK
 
 GET all Pm Jobs - all_fields
-    Log    Trying to get all PM Jobs present in the NFVO Catalogue, using 'all_fields' filter
+    Log    Trying to get all PM Jobs present in the VNFM, using 'all_fields' filter
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Request Header    Authorization    ${VNFM_AUTHENTICATION}
@@ -108,8 +108,8 @@ GET all Pm Jobs - all_fields
     Log    Validation for _links schema OK
 
 GET all Pm Jobs - fields
-    Log    Trying to get all VNF Packages present in the NFVO Catalogue, using filter params
-    Pass Execution If    ${AUTH_USAGE} == 0    Skipping test as NFVO is not supporting 'fields'
+    Log    Trying to get all VNF Packages present in the VNFM, using filter params
+    Pass Execution If    ${VNFM_AUTH_USAGE} == 0    Skipping test as VNFM is not supporting 'fields'
     Create HTTP Context    ${VNFM_HOST}:${VNFM_PORT}    ${VNFM_SCHEMA}
     Set Request Header    Accept    ${ACCEPT_JSON}
     Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Request Header    Authorization    ${VNFM_AUTHENTICATION}
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/Subscriptions.robot b/SOL003/VNFPerformanceManagement-API_nxw/Subscriptions.robot
new file mode 100644
index 00000000..5b883135
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/Subscriptions.robot
@@ -0,0 +1,184 @@
+*** Settings ***
+Documentation     This resource represents subscriptions. The client can use this resource to subscribe to notifications related to VNF
+...               performance management and to query its subscriptions.
+Library           JSONSchemaLibrary    schemas/
+Resource          environment/generic.txt    # Generic Parameters
+Library           REST    ${VNFM_SCHEMA}://${VNFM_HOST}:${VNFM_PORT}
+Library           OperatingSystem
+Library           JSONLibrary
+Resource          environment/subscriptions.txt
+
+*** Test Cases ***
+GET Subscription
+    [Documentation]    The client can use this method to query the list of active subscriptions to Performance management notifications
+    ...    subscribed by the client.
+    ...    This method shall follow the provisions specified in the tables 6.4.7.3.2-1 and 6.4.7.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    GET    ${apiRoot}/${apiName}/${apiVersion}/subscriptions
+    Integer    response status    200
+    Log    Received a 200 OK as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    PmSubscription.schema.json    ${json}
+    Log    Validated PmSubscription schema
+
+GET Subscription - Filter
+    [Documentation]    The client can use this method to query the list of active subscriptions to Performance management notifications
+    ...    subscribed by the client.
+    ...    This method shall follow the provisions specified in the tables 6.4.7.3.2-1 and 6.4.7.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    GET    ${apiRoot}/${apiName}/${apiVersion}/subscriptions?${filter_ok}
+    Integer    response status    200
+    Log    Received a 200 OK as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    PmSubscription.schema.json    ${json}
+    Log    Validated PmSubscription schema
+
+GET Subscription - Negative Filter (Erroneous filter)
+    [Documentation]    The client can use this method to query the list of active subscriptions to Performance management notifications
+    ...    subscribed by the client.
+    ...    This method shall follow the provisions specified in the tables 6.4.7.3.2-1 and 6.4.7.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    GET    ${apiRoot}/${apiName}/${apiVersion}/subscriptions?${filter_ko}
+    Integer    response status    400
+    Log    Received a 400 Bad Request as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    ProblemDetails.schema.json    ${json}
+    Log    Validated ProblemDetails schema
+
+GET Subscription - Negative (Not Found)
+    [Documentation]    The client can use this method to query the list of active subscriptions to Performance management notifications
+    ...    subscribed by the client.
+    ...    This method shall follow the provisions specified in the tables 6.4.7.3.2-1 and 6.4.7.3.2-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    GET    ${apiRoot}/${apiName}/${apiVersion}/subscription
+    Integer    response status    404
+    Log    Received a 404 Not found as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    ProblemDetails.schema.json    ${json}
+    Log    Validated ProblemDetails schema
+
+POST Subscription
+    [Documentation]    The POST method creates a new subscription.
+    ...    This method shall follow the provisions specified in the tables 6.4.7.3.1-1 and 6.4.7.3.1-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    ...    Creation of two subscription resources with the same callbackURI and the same filter can result in performance
+    ...    degradation and will provide duplicates of notifications to the NFVO, and might make sense only in very rare use cases.
+    ...    Consequently, the VNFM may either allow creating a subscription resource if another subscription resource with the
+    ...    same filter and callbackUri already exists (in which case it shall return the "201 Created" response code), or may decide
+    ...    to not create a duplicate subscription resource (in which case it shall return a "303 See Other" response code referencing
+    ...    the existing subscription resource with the same filter and callbackUri).
+    Set headers    {"Content-Type": "${CONTENT_TYPE_JSON}"}
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    ${body_request}=    Get File    jsons/subscriptions.json
+    POST    ${apiRoot}/${apiName}/${apiVersion}/subscriptions    ${body_request}
+    Integer    response status    201
+    Log    Received a 201 Created as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    PmSubscription.schema.json    ${json}
+    Log    Validated PmSubscription schema
+    Log    Trying to validate the Location header
+    ${headers}=    Output    response headers
+    Should Contain    ${headers}    Location
+
+POST Subscription - DUPLICATION
+    [Documentation]    The POST method creates a new subscription.
+    ...    This method shall follow the provisions specified in the tables 6.4.7.3.1-1 and 6.4.7.3.1-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    ...    Creation of two subscription resources with the same callbackURI and the same filter can result in performance
+    ...    degradation and will provide duplicates of notifications to the NFVO, and might make sense only in very rare use cases.
+    ...    Consequently, the VNFM may either allow creating a subscription resource if another subscription resource with the
+    ...    same filter and callbackUri already exists (in which case it shall return the "201 Created" response code), or may decide
+    ...    to not create a duplicate subscription resource (in which case it shall return a "303 See Other" response code referencing
+    ...    the existing subscription resource with the same filter and callbackUri).
+    Pass Execution If    ${VNFM_DUPLICATION} == 1    VNFM is permitting duplication. Skipping the test
+    Set headers    {"Content-Type": "${CONTENT_TYPE_JSON}"}
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    ${body_request}=    Get File    jsons/subscriptions.json
+    POST    ${apiRoot}/${apiName}/${apiVersion}/subscriptions    ${body_request}
+    Integer    response status    303
+    Log    Received a 303 See other as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    Should Be Empty    ${result}
+    Log    Body is empty
+    Log    Trying to validate the Location header
+    ${headers}=    Output    response headers
+    Should Contain    ${headers}    Location
+
+POST Subscription - NO DUPLICATION
+    [Documentation]    The POST method creates a new subscription.
+    ...    This method shall follow the provisions specified in the tables 6.4.7.3.1-1 and 6.4.7.3.1-2 for URI query parameters,
+    ...    request and response data structures, and response codes.
+    ...    Creation of two subscription resources with the same callbackURI and the same filter can result in performance
+    ...    degradation and will provide duplicates of notifications to the NFVO, and might make sense only in very rare use cases.
+    ...    Consequently, the VNFM may either allow creating a subscription resource if another subscription resource with the
+    ...    same filter and callbackUri already exists (in which case it shall return the "201 Created" response code), or may decide
+    ...    to not create a duplicate subscription resource (in which case it shall return a "303 See Other" response code referencing
+    ...    the existing subscription resource with the same filter and callbackUri).
+    Pass Execution If    ${VNFM_DUPLICATION} == 0    VNFM is not permitting duplication. Skipping the test
+    Set headers    {"Content-Type": "${CONTENT_TYPE_JSON}"}
+    Set headers    {"Accept": "${ACCEPT_JSON}"}
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": ${VNFM_AUTHENTICATION}"}
+    ${body_request}=    Get File    jsons/subscriptions.json
+    POST    ${apiRoot}/${apiName}/${apiVersion}/subscriptions    ${body_request}
+    Integer    response status    201
+    Log    Received a 201 Created as expected
+    ${contentType}=    Output    response headers Content-Type
+    Should Contain    ${contentType}    application/json
+    ${result}=    Output    response body
+    ${json}=    evaluate    json.loads('''${result}''')    json
+    Validate Json    PmSubscription.schema.json    ${json}
+    Log    Validated PmSubscription schema
+    Log    Trying to validate the Location header
+    ${headers}=    Output    response headers
+    Should Contain    ${headers}    Location
+
+PUT Subscription - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    PUT    ${apiRoot}/${apiName}/${apiVersion}/subscriptions
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
+
+PATCH Subscription - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    PATCH    ${apiRoot}/${apiName}/${apiVersion}/subscriptions
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
+
+DELETE Subscription - (Method not implemented)
+    [Documentation]    This method is not supported. When this method is requested on this resource, the VNFM shall return a "405 Method
+    ...    Not Allowed" response as defined in clause 4.3.5.4.
+    Run Keyword If    ${VNFM_AUTH_USAGE} == 1    Set Headers    {"Authorization": "${VNFM_AUTHENTICATION}"}
+    DELETE    ${apiRoot}/${apiName}/${apiVersion}/subscriptions
+    Integer    response status    405
+    Log    Received 405 Method not implemented as expected
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/environment/generic.txt b/SOL003/VNFPerformanceManagement-API_nxw/environment/generic.txt
index c34927aa..4f4b0194 100644
--- a/SOL003/VNFPerformanceManagement-API_nxw/environment/generic.txt
+++ b/SOL003/VNFPerformanceManagement-API_nxw/environment/generic.txt
@@ -16,3 +16,4 @@ ${apiName}        vnfpm
 ${FIELD_USAGE}    1
 ${VNFM_AUTHENTICATION}    Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
 ${VNFM_AUTH_USAGE}    1
+${VNFM_DUPLICATION}    1
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/environment/individualSubscription.txt b/SOL003/VNFPerformanceManagement-API_nxw/environment/individualSubscription.txt
new file mode 100644
index 00000000..f5aa4ba9
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/environment/individualSubscription.txt
@@ -0,0 +1,3 @@
+*** Variables ***
+${subscriptionId}    17563e75-0e14-4bd7-94b4-6bbb869c79aa
+${erroneousSubscriptionId}    erroneousSubscriptionId
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/environment/individualThresholds.txt b/SOL003/VNFPerformanceManagement-API_nxw/environment/individualThresholds.txt
new file mode 100644
index 00000000..59056904
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/environment/individualThresholds.txt
@@ -0,0 +1,3 @@
+*** Variables ***
+${thresholdId}    1f50d68b-82e8-4deb-bd40-c934d4d1ac0a
+${erroneousThresholdId}    erroneousThresholdId
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/environment/subscriptions.txt b/SOL003/VNFPerformanceManagement-API_nxw/environment/subscriptions.txt
new file mode 100644
index 00000000..fec8c84b
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/environment/subscriptions.txt
@@ -0,0 +1,3 @@
+*** Variables ***
+${filter_ok}      callbackUri=http://localhost/subscriptions
+${filter_ko}      erroneousFilter=erroneous
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/jsons/subscriptios.json b/SOL003/VNFPerformanceManagement-API_nxw/jsons/subscriptions.json
similarity index 53%
rename from SOL003/VNFPerformanceManagement-API_nxw/jsons/subscriptios.json
rename to SOL003/VNFPerformanceManagement-API_nxw/jsons/subscriptions.json
index d0dd3eef..3fcc4e59 100644
--- a/SOL003/VNFPerformanceManagement-API_nxw/jsons/subscriptios.json
+++ b/SOL003/VNFPerformanceManagement-API_nxw/jsons/subscriptions.json
@@ -1,6 +1,6 @@
 {
 	"callbackUri": "http://127.0.0.1/subscribe",
 	"filter": {
-		"vnfPkgId": "788106a2-d692-44f3-a86d-384f0ce35e42"
+		"notificationTypes": ["ThresholdCrossedNotification"]
 	}
 }
\ No newline at end of file
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/schemas/PmSubscriptions.schema.json b/SOL003/VNFPerformanceManagement-API_nxw/schemas/PmSubscriptions.schema.json
new file mode 100644
index 00000000..fefb142d
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/schemas/PmSubscriptions.schema.json
@@ -0,0 +1 @@
+{  "type": "array",  "items": {    "description": "This type represents a subscription.\n",    "type": "object",    "required": [      "id",      "callbackUri",      "_links"    ],    "properties": {      "id": {        "description": "An identifier with the intention of being globally unique.\n",        "type": "string"      },      "filter": {        "description": "This type represents a filter that can be used to subscribe for notifications related to performance management events.\n",        "type": "object",        "properties": {          "vnfInstanceSubscriptionFilter": {            "description": "This type represents subscription filter criteria to match VNF instances.\n",            "type": "object",            "properties": {              "vnfdIds": {                "description": "If present, match VNF instances that were created based on a VNFD identified by one of the vnfdId values listed in this attribute. The attributes \"vnfdIds\" and \"vnfProductsFromProviders\" are alternatives to reference to VNF instances that are based on certain VNFDs in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "description": "An identifier with the intention of being globally unique.\n",                  "type": "string"                }              },              "vnfProductsFromProviders": {                "description": "If present, match VNF instances that belong to VNF products from certain providers. The attributes \"vnfdIds\" and \"vnfProductsFromProviders\" are alternatives to reference to VNF instances that are based on certain VNFDs in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "type": "object",                  "required": [                    "vnfProvider"                  ],                  "properties": {                    "vnfProvider": {                      "description": "Name of the VNF provider to match.\n",                      "type": "string"                    },                    "vnfProducts": {                      "description": "If present, match VNF instances that belong to VNF products with certain product names, from one particular provider.\n",                      "type": "array",                      "items": {                        "type": "object",                        "required": [                          "vnfProductName"                        ],                        "properties": {                          "vnfProductName": {                            "description": "Name of the VNF product to match.\n",                            "type": "string"                          },                          "versions": {                            "description": "If present, match VNF instances that belong to VNF products with certain versions and a certain product name, from one particular provider.\n",                            "type": "array",                            "items": {                              "type": "object",                              "required": [                                "vnfSoftwareVersion"                              ],                              "properties": {                                "vnfSoftwareVersion": {                                  "description": "A version.\n",                                  "type": "string"                                },                                "vnfdVersions": {                                  "description": "If present, match VNF instances that belong to VNF products with certain VNFD versions, a certain software version and a certain product name, from one particular provider.\n",                                  "type": "array",                                  "items": {                                    "description": "A version.\n",                                    "type": "string"                                  }                                }                              }                            }                          }                        }                      }                    }                  }                }              },              "vnfInstanceIds": {                "description": "If present, match VNF instances with an instance identifier listed in this attribute. The attributes \"vnfInstanceIds\" and \"vnfInstanceNames\" are alternatives to reference to particular VNF Instances in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "description": "An identifier with the intention of being globally unique.\n",                  "type": "string"                }              },              "vnfInstanceNames": {                "description": "If present, match VNF instances with a VNF Instance Name listed in this attribute. The attributes \"vnfInstanceIds\" and \"vnfInstanceNames\" are alternatives to reference to particular VNF Instances in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "type": "string"                }              }            }          },          "notificationTypes": {            "description": "Match particular notification types. Permitted values: * ThresholdCrossedNotification * PerformanceInformationAvailableNotification The permitted values of the \"notificationTypes\" attribute are spelled exactly as the names of the notification types to facilitate automated code generation systems.\n",            "type": "string",            "enum": [              "ThresholdCrossedNotification",              "PerformanceInformationAvailableNotification"            ]          }        }      },      "callbackUri": {        "description": "The URI of the endpoint to send the notification to.\n",        "type": "string",        "format": "url"      },      "_links": {        "description": "Links to resources related to this resource.\n",        "type": "object",        "required": [          "self"        ],        "properties": {          "self": {            "description": "This type represents a link to a resource.\n",            "type": "object",            "required": [              "href"            ],            "properties": {              "href": {                "description": "URI of the referenced resource.\n",                "type": "string",                "format": "url"              }            }          }        }      }    }  }}
\ No newline at end of file
diff --git a/SOL003/VNFPerformanceManagement-API_nxw/schemas/Subscriptions.schema.json b/SOL003/VNFPerformanceManagement-API_nxw/schemas/Subscriptions.schema.json
new file mode 100644
index 00000000..fefb142d
--- /dev/null
+++ b/SOL003/VNFPerformanceManagement-API_nxw/schemas/Subscriptions.schema.json
@@ -0,0 +1 @@
+{  "type": "array",  "items": {    "description": "This type represents a subscription.\n",    "type": "object",    "required": [      "id",      "callbackUri",      "_links"    ],    "properties": {      "id": {        "description": "An identifier with the intention of being globally unique.\n",        "type": "string"      },      "filter": {        "description": "This type represents a filter that can be used to subscribe for notifications related to performance management events.\n",        "type": "object",        "properties": {          "vnfInstanceSubscriptionFilter": {            "description": "This type represents subscription filter criteria to match VNF instances.\n",            "type": "object",            "properties": {              "vnfdIds": {                "description": "If present, match VNF instances that were created based on a VNFD identified by one of the vnfdId values listed in this attribute. The attributes \"vnfdIds\" and \"vnfProductsFromProviders\" are alternatives to reference to VNF instances that are based on certain VNFDs in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "description": "An identifier with the intention of being globally unique.\n",                  "type": "string"                }              },              "vnfProductsFromProviders": {                "description": "If present, match VNF instances that belong to VNF products from certain providers. The attributes \"vnfdIds\" and \"vnfProductsFromProviders\" are alternatives to reference to VNF instances that are based on certain VNFDs in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "type": "object",                  "required": [                    "vnfProvider"                  ],                  "properties": {                    "vnfProvider": {                      "description": "Name of the VNF provider to match.\n",                      "type": "string"                    },                    "vnfProducts": {                      "description": "If present, match VNF instances that belong to VNF products with certain product names, from one particular provider.\n",                      "type": "array",                      "items": {                        "type": "object",                        "required": [                          "vnfProductName"                        ],                        "properties": {                          "vnfProductName": {                            "description": "Name of the VNF product to match.\n",                            "type": "string"                          },                          "versions": {                            "description": "If present, match VNF instances that belong to VNF products with certain versions and a certain product name, from one particular provider.\n",                            "type": "array",                            "items": {                              "type": "object",                              "required": [                                "vnfSoftwareVersion"                              ],                              "properties": {                                "vnfSoftwareVersion": {                                  "description": "A version.\n",                                  "type": "string"                                },                                "vnfdVersions": {                                  "description": "If present, match VNF instances that belong to VNF products with certain VNFD versions, a certain software version and a certain product name, from one particular provider.\n",                                  "type": "array",                                  "items": {                                    "description": "A version.\n",                                    "type": "string"                                  }                                }                              }                            }                          }                        }                      }                    }                  }                }              },              "vnfInstanceIds": {                "description": "If present, match VNF instances with an instance identifier listed in this attribute. The attributes \"vnfInstanceIds\" and \"vnfInstanceNames\" are alternatives to reference to particular VNF Instances in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "description": "An identifier with the intention of being globally unique.\n",                  "type": "string"                }              },              "vnfInstanceNames": {                "description": "If present, match VNF instances with a VNF Instance Name listed in this attribute. The attributes \"vnfInstanceIds\" and \"vnfInstanceNames\" are alternatives to reference to particular VNF Instances in a filter. They should not be used both in the same filter instance, but one alternative should be chosen.\n",                "type": "array",                "items": {                  "type": "string"                }              }            }          },          "notificationTypes": {            "description": "Match particular notification types. Permitted values: * ThresholdCrossedNotification * PerformanceInformationAvailableNotification The permitted values of the \"notificationTypes\" attribute are spelled exactly as the names of the notification types to facilitate automated code generation systems.\n",            "type": "string",            "enum": [              "ThresholdCrossedNotification",              "PerformanceInformationAvailableNotification"            ]          }        }      },      "callbackUri": {        "description": "The URI of the endpoint to send the notification to.\n",        "type": "string",        "format": "url"      },      "_links": {        "description": "Links to resources related to this resource.\n",        "type": "object",        "required": [          "self"        ],        "properties": {          "self": {            "description": "This type represents a link to a resource.\n",            "type": "object",            "required": [              "href"            ],            "properties": {              "href": {                "description": "URI of the referenced resource.\n",                "type": "string",                "format": "url"              }            }          }        }      }    }  }}
\ No newline at end of file
-- 
GitLab