From 931dceb9745ba226fab8d64ed02f8b37adee2223 Mon Sep 17 00:00:00 2001 From: gioelemella <128155546+gioelemella@users.noreply.github.com> Date: Sun, 21 Jan 2024 17:43:10 +0100 Subject: [PATCH] [PRDP-318] feat: Setup integration test for transaction APIs (#31) * [PRDP-318] setup integration test for transaction APIs * [PRDP-318] added secret for integration test in identity * [PRDP-318] added integration test for cart transactions * [PRDP-318] fix integration tests * [PRDP-318] fixed integration test parameters * [PRDP-318] format code --------- Co-authored-by: giomella --- .github/workflows/code_review.yml | 1 + .github/workflows/integration_test.yml | 1 + .gitignore | 1 + .identity/00_data.tf | 5 + .identity/03_github_environment.tf | 1 + .../src/features/organizationsreceipt.feature | 32 +++++ integration-test/src/package.json | 5 +- .../support/biz_events_cosmosdb_client.js | 40 ++++++ .../support/bizeventservice_client.js | 28 +++- .../src/step_definitions/support/common.js | 129 +++++++++++++++++- .../support/organizationsreceipt_steps.js | 85 +++++++++++- 11 files changed, 313 insertions(+), 15 deletions(-) create mode 100644 integration-test/src/step_definitions/support/biz_events_cosmosdb_client.js diff --git a/.github/workflows/code_review.yml b/.github/workflows/code_review.yml index 7cf0d7a3..ccdb065b 100644 --- a/.github/workflows/code_review.yml +++ b/.github/workflows/code_review.yml @@ -79,6 +79,7 @@ jobs: export CANARY=${{ inputs.canary }} export CUCUMBER_PUBLISH_TOKEN=${{ secrets.CUCUMBER_PUBLISH_TOKEN }} export COSMOS_DB_PRIMARY_KEY=${{ secrets.COSMOS_DB_PRIMARY_KEY }} + export COSMOS_DB_CONN_STRING=${{ secrets.COSMOS_DB_CONN_STRING }} cd ./integration-test chmod +x ./run_integration_test.sh diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index a000a1c0..7d4e986f 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -58,6 +58,7 @@ jobs: export CANARY=${{ inputs.canary }} export CUCUMBER_PUBLISH_TOKEN=${{ secrets.CUCUMBER_PUBLISH_TOKEN }} export COSMOS_DB_PRIMARY_KEY=${{ secrets.COSMOS_DB_PRIMARY_KEY }} + export COSMOS_DB_CONN_STRING=${{ secrets.COSMOS_DB_CONN_STRING }} cd ./integration-test chmod +x ./run_integration_test.sh diff --git a/.gitignore b/.gitignore index 928d6bc0..239fcba0 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ hs_err_pid* # Project files /target/ **/node_modules +yarn.lock # Terraform **/.terraform/ \ No newline at end of file diff --git a/.identity/00_data.tf b/.identity/00_data.tf index 962e655c..f4ba1300 100644 --- a/.identity/00_data.tf +++ b/.identity/00_data.tf @@ -60,3 +60,8 @@ data "azurerm_key_vault_secret" "key_vault_integration_cosmos_negative_biz_key" name = format("cosmos-%s-biz-key", var.env_short) key_vault_id = data.azurerm_key_vault.domain_key_vault.id } + +data "azurerm_cosmosdb_account" "bizevents_cosmos" { + name = "pagopa-${var.env_short}-${local.location_short}-bizevents-ds-cosmos-account" + resource_group_name = "pagopa-${var.env_short}-${local.location_short}-bizevents-rg" +} diff --git a/.identity/03_github_environment.tf b/.identity/03_github_environment.tf index ea92a141..aa44c24c 100644 --- a/.identity/03_github_environment.tf +++ b/.identity/03_github_environment.tf @@ -24,6 +24,7 @@ locals { "CLIENT_ID" : module.github_runner_app.application_id, "TENANT_ID" : data.azurerm_client_config.current.tenant_id, "SUBSCRIPTION_ID" : data.azurerm_subscription.current.subscription_id, + "COSMOS_DB_CONN_STRING" : "AccountEndpoint=https://pagopa-${var.env_short}-${local.location_short}-bizevents-ds-cosmos-account.documents.azure.com:443/;AccountKey=${data.azurerm_cosmosdb_account.bizevents_cosmos.primary_key};" } env_variables = { "CONTAINER_APP_ENVIRONMENT_NAME" : local.container_app_environment.name, diff --git a/integration-test/src/features/organizationsreceipt.feature b/integration-test/src/features/organizationsreceipt.feature index d44bc3f0..9d469e37 100644 --- a/integration-test/src/features/organizationsreceipt.feature +++ b/integration-test/src/features/organizationsreceipt.feature @@ -23,3 +23,35 @@ Feature: All about Organizations Receipt Then the operator gets the status code 200 And the details of the Biz-Event are returned to the operator with id "123456789" And the Biz-Event to test with id "123456789" is removed + + Scenario: An user asks for a its transactions + Given 3 Biz-Event with debtor fiscal code "INTTST00A00A000A" + And 3 Biz-Event with payer fiscal code "INTTST00A00A000A" + And Save all on Cosmos DB + When the user with fiscal code "INTTST00A00A000A" asks for its transactions + Then the user gets the status code 200 + And the user gets 6 transactions + + Scenario: An user asks for a its transactions with cart transaction + Given 3 Biz-Event with debtor fiscal code "INTTST00A00A000A" + And 3 Biz-Event with payer fiscal code "INTTST00A00A000A" + And 3 cart Biz-Event with transactionId "biz-event-service-int-test-transaction-1", debtor fiscal code "INTTST00A00A000A" and amount 1000 + And Save all on Cosmos DB + When the user with fiscal code "INTTST00A00A000A" asks for its transactions + Then the user gets the status code 200 + And the user gets 7 transactions + And one of the transactions is a cart with id "biz-event-service-int-test-transaction-1" and amount 30.00 + + Scenario: An user asks for a transaction + Given Biz-Event with debtor fiscal code "INTTST00A00A000A" and id "biz-event-service-int-test-transaction-2" + And Save all on Cosmos DB + When the user with fiscal code "INTTST00A00A000A" asks the transaction with id "biz-event-service-int-test-transaction-2" and isCart "false" + Then the user gets the status code 200 + And the user gets the transaction with id "biz-event-service-int-test-transaction-2" + + Scenario: An user asks for a cart transaction + Given 3 cart Biz-Event with transactionId "biz-event-service-int-test-transaction-3", debtor fiscal code "INTTST00A00A000A" and amount 1000 + And Save all on Cosmos DB + When the user with fiscal code "INTTST00A00A000A" asks the transaction with id "biz-event-service-int-test-transaction-3" and isCart "true" + Then the user gets the status code 200 + And the user gets the transaction with id "biz-event-service-int-test-transaction-3" diff --git a/integration-test/src/package.json b/integration-test/src/package.json index 40edd1fb..e0e9e205 100644 --- a/integration-test/src/package.json +++ b/integration-test/src/package.json @@ -9,11 +9,12 @@ "cucumber": "npx cucumber-js --publish -r step_definitions" }, "dependencies": { + "@azure/cosmos": "^4.0.0", "@cucumber/cucumber": "^8.4.0", "axios": "^0.27.2", + "crypto-js": "^4.0.0", "dotenv": "^16.0.2", "dotenv-cli": "^3.2.0", - "npx": "^10.2.2", - "crypto-js": "^4.0.0" + "npx": "^10.2.2" } } diff --git a/integration-test/src/step_definitions/support/biz_events_cosmosdb_client.js b/integration-test/src/step_definitions/support/biz_events_cosmosdb_client.js new file mode 100644 index 00000000..f50b3534 --- /dev/null +++ b/integration-test/src/step_definitions/support/biz_events_cosmosdb_client.js @@ -0,0 +1,40 @@ +const { CosmosClient } = require("@azure/cosmos"); +const { createEvent } = require("./common"); + +const cosmos_db_conn_string = process.env.COSMOS_DB_CONN_STRING; +const databaseId = process.env.COSMOS_DB_NAME; // es. db +const containerId = process.env.COSMOS_DB_CONTAINER_NAME; // es. biz-events + +const client = new CosmosClient(cosmos_db_conn_string); +const container = client.database(databaseId).container(containerId); + +async function getDocumentByIdFromBizEventsDatastore(id) { + return await container.items + .query({ + query: "SELECT * from c WHERE c.id=@id", + parameters: [{ name: "@id", value: id }] + }) + .fetchAll(); +} + +async function createDocumentInBizEventsDatastore(event) { + try { + return await container.items.create(event); + } catch (err) { + console.log(err); + } +} + +async function deleteDocumentFromBizEventsDatastore(id) { + try { + return await container.item(id, id).delete(); + } catch (error) { + if (error.code !== 404) { + console.log(error) + } + } +} + +module.exports = { + getDocumentByIdFromBizEventsDatastore, createDocumentInBizEventsDatastore, deleteDocumentFromBizEventsDatastore +} \ No newline at end of file diff --git a/integration-test/src/step_definitions/support/bizeventservice_client.js b/integration-test/src/step_definitions/support/bizeventservice_client.js index 75d9c818..ee5a824a 100644 --- a/integration-test/src/step_definitions/support/bizeventservice_client.js +++ b/integration-test/src/step_definitions/support/bizeventservice_client.js @@ -1,26 +1,40 @@ -const {get} = require("./common"); +const { get } = require("./common"); const bizevents_service_host = process.env.BIZ_EVENTS_SERVICE_HOST; function healthCheckInfo() { - return get(bizevents_service_host+`info`, {}) + return get(bizevents_service_host + `info`, {}) } function getOrganizationReceipt(organizationfiscalcode, iur, iuv) { - return get(bizevents_service_host+`organizations/${organizationfiscalcode}/receipts/${iur}/paymentoptions/${iuv}`, {}) + return get(bizevents_service_host + `organizations/${organizationfiscalcode}/receipts/${iur}/paymentoptions/${iuv}`, {}) } function getBizEventById(id) { - return get(bizevents_service_host+`events/${id}`, {}) + return get(bizevents_service_host + `events/${id}`, {}) } function getBizEventByOrgFiscalCodeAndIuv(organizationfiscalcode, iuv) { - return get(bizevents_service_host+`events/organizations/${organizationfiscalcode}/iuvs/${iuv}`, {}) + return get(bizevents_service_host + `events/organizations/${organizationfiscalcode}/iuvs/${iuv}`, {}) +} + +function getTransactionListForUserWithFiscalCode(fiscalcode) { + return get(bizevents_service_host + `transactions?start=0&size=10`, { + "x-fiscal-code": fiscalcode + }) +} + +function getTransactionWithIdForUserWithFiscalCode(id, fiscalcode, isCart) { + return get(bizevents_service_host + `transactions/${id}?isCart=${isCart}`, { + "x-fiscal-code": fiscalcode + }) } module.exports = { - healthCheckInfo, + healthCheckInfo, getOrganizationReceipt, getBizEventById, - getBizEventByOrgFiscalCodeAndIuv + getBizEventByOrgFiscalCodeAndIuv, + getTransactionListForUserWithFiscalCode, + getTransactionWithIdForUserWithFiscalCode } diff --git a/integration-test/src/step_definitions/support/common.js b/integration-test/src/step_definitions/support/common.js index bd76cf75..51d43772 100644 --- a/integration-test/src/step_definitions/support/common.js +++ b/integration-test/src/step_definitions/support/common.js @@ -68,6 +68,18 @@ function getCosmosDBAuthorizationToken(verb,autorizationType,autorizationVersion return encodeURIComponent("type=" + autorizationType + "&ver=" + autorizationVersion + "&sig=" + signature_base64); } +function makeId(length) { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + let counter = 0; + while (counter < length) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + counter += 1; + } + return result; +} + function getDocumentForTest(id) { return { "id": id, @@ -140,4 +152,119 @@ function getDocumentForTest(id) { } } -module.exports = {get, post, put, del, getCosmosDBAuthorizationToken, getDocumentForTest} +function createEvent(id, transactionId, totalNotice, debtorFiscalCode, payerFiscalCode, amount) { + return { + "id": id, + "version": "2", + "idPaymentManager": "54927408", + "complete": "false", + "receiptId": "9851395f09544a04b288202299193ca6", + "missingInfo": [ + "psp.pspPartitaIVA", + "paymentInfo.primaryCiIncurredFee", + "paymentInfo.idBundle", + "paymentInfo.idCiBundle" + ], + "debtorPosition": { + "modelType": "2", + "noticeNumber": "310391366991197059", + "iuv": "10391366991197059" + }, + "creditor": { + "idPA": "66666666666", + "idBrokerPA": "66666666666", + "idStation": "66666666666_08", + "companyName": "PA paolo", + "officeName": "office" + }, + "psp": { + "idPsp": "60000000001", + "idBrokerPsp": "60000000001", + "idChannel": "60000000001_08", + "psp": "PSP Paolo", + "pspFiscalCode": "CF60000000006", + "channelDescription": "app" + }, + "debtor": { + "fullName": "paGetPaymentName", + "entityUniqueIdentifierType": "G", + "entityUniqueIdentifierValue": debtorFiscalCode || "JHNDOE00A01F205N", + "streetName": "paGetPaymentStreet", + "civicNumber": "paGetPayment99", + "postalCode": "20155", + "city": "paGetPaymentCity", + "stateProvinceRegion": "paGetPaymentState", + "country": "IT", + "eMail": "paGetPayment@test.it" + }, + "payer": { + "fullName": "name", + "entityUniqueIdentifierType": "G", + "entityUniqueIdentifierValue": payerFiscalCode || "JHNDOE00A01F205S", + "streetName": "street", + "civicNumber": "civic", + "postalCode": "postal", + "city": "city", + "stateProvinceRegion": "state", + "country": "IT", + "eMail": "prova@test.it" + }, + "paymentInfo": { + "paymentDateTime": "2023-03-17T16:37:36.955813", + "applicationDate": "2021-12-12", + "transferDate": "2021-12-11", + "dueDate": "2021-12-12", + "paymentToken": "9851395f09544a04b288202299193ca6", + "amount": "10.0", + "fee": "2.0", + "totalNotice": totalNotice ? totalNotice : "1", + "paymentMethod": "creditCard", + "touchpoint": "app", + "remittanceInformation": "TARI 2021", + "description": "TARI 2021", + "metadata": [ + { + "key": "1", + "value": "22" + } + ] + }, + "transferList": [ + { + "idTransfer": "1", + "fiscalCodePA": "66666666666", + "companyName": "PA paolo", + "amount": "10.0", + "transferCategory": "paGetPaymentTest", + "remittanceInformation": "/RFB/00202200000217527/5.00/TXT/" + } + ], + "transactionDetails": { + "user": { + "fullName": "John Doe", + "type": "F", + "fiscalCode": payerFiscalCode || "JHNDOE00A01F205N", + "notificationEmail": "john.doe@mail.it", + "userId": "1234", + "userStatus": "11", + "userStatusDescription": "REGISTERED_SPID" + }, + "transaction": { + "idTransaction": "123456", + "transactionId": transactionId ? transactionId : "123456", + "grandTotal": amount || 0, + "amount": 0, + "fee": 0 + } + }, + "timestamp": 1679067463501, + "properties": { + "diagnostic-id": "00-f70ef3167cffad76c6657a67a33ee0d2-61d794a75df0b43b-01", + "serviceIdentifier": "NDP002SIT" + }, + "eventStatus": "DONE", + "eventRetryEnrichmentCount": 0 + }; +} + +module.exports = {get, post, put, del, getCosmosDBAuthorizationToken, getDocumentForTest, createEvent, makeId} diff --git a/integration-test/src/step_definitions/support/organizationsreceipt_steps.js b/integration-test/src/step_definitions/support/organizationsreceipt_steps.js index 867de861..56b8c023 100644 --- a/integration-test/src/step_definitions/support/organizationsreceipt_steps.js +++ b/integration-test/src/step_definitions/support/organizationsreceipt_steps.js @@ -1,20 +1,32 @@ const assert = require('assert') -const {Given, When, Then, setDefaultTimeout, After} = require('@cucumber/cucumber') -const {getOrganizationReceipt, getBizEventById, getBizEventByOrgFiscalCodeAndIuv} = require("./bizeventservice_client"); -const {createDocument, deleteDocument} = require("./cosmosdb_client"); +const { Given, When, Then, setDefaultTimeout, After } = require('@cucumber/cucumber') +const { getOrganizationReceipt, getBizEventById, getBizEventByOrgFiscalCodeAndIuv, getTransactionListForUserWithFiscalCode, getTransactionWithIdForUserWithFiscalCode } = require("./bizeventservice_client"); +const { createDocument, deleteDocument } = require("./cosmosdb_client"); +const { createEvent, makeId } = require("./common"); +const { createDocumentInBizEventsDatastore, deleteDocumentFromBizEventsDatastore } = require("./biz_events_cosmosdb_client"); + +const BIZ_ID = "biz-event-service-int-test-transaction-"; let responseToCheck; let receipt; let bizEvent; +let bizEventList = []; setDefaultTimeout(360 * 1000); // After each Scenario After(async function () { - // remove event - responseToCheck = null; + // remove event + if (bizEventList.length > 0) { + for (let bizEvent of bizEventList) { + await deleteDocumentFromBizEventsDatastore(bizEvent.id); + } + } + + responseToCheck = null; receipt = null; bizEvent = null; + bizEventList = []; }); @@ -71,3 +83,66 @@ Then('the operator gets the status code {int}', async function (status) { Then('the details of the Biz-Event are returned to the operator with id {string}', async function (id) { assert.strictEqual(bizEvent.id, id); }); + + +Given('{int} Biz-Event with debtor fiscal code {string}', (numberOfEvents, debtorFiscalCode) => { + for (let i = 0; i < numberOfEvents; i++) { + bizEventList.push(createEvent(BIZ_ID + i + makeId(4), undefined, undefined, debtorFiscalCode)) + } +}) + +Given('{int} Biz-Event with payer fiscal code {string}', (numberOfEvents, payerFiscalCode) => { + for (let i = 0; i < numberOfEvents; i++) { + bizEventList.push(createEvent(BIZ_ID + i + makeId(4), undefined, undefined, undefined, payerFiscalCode)) + } +}) + +Given('Save all on Cosmos DB', async () => { + for (let bizEvent of bizEventList) { + let response = await createDocumentInBizEventsDatastore(bizEvent); + assert.strictEqual(response.statusCode, 201); + response = null; + } +}) + +When('the user with fiscal code {string} asks for its transactions', async (fiscalCode) => { + responseToCheck = await getTransactionListForUserWithFiscalCode(fiscalCode); +}) + +Then('the user gets the status code {int}', (status) => { + assert.strictEqual(responseToCheck.status, status); +}) + +Then('the user gets {int} transactions', (totalTransactions) => { + assert.strictEqual(responseToCheck.data.length, totalTransactions); +}) + +Given('{int} cart Biz-Event with transactionId {string}, debtor fiscal code {string} and amount {int}', (numberOfEvents, transactionId, debtorFiscalCode, amount) => { + for (let i = 0; i < numberOfEvents; i++) { + bizEventList.push(createEvent(i + makeId(10), transactionId, numberOfEvents, debtorFiscalCode, undefined, amount)) + } +}) + +Then('one of the transactions is a cart with id {string} and amount {bigdecimal}', (transactionId, amount) => { + let found = false; + for (let transaction of responseToCheck.data) { + if (transaction.transactionId == transactionId) { + assert.strictEqual(transaction.amount, amount); + found = true; + } + } + assert.strictEqual(found, true); +}) + +Given('Biz-Event with debtor fiscal code {string} and id {string}', (debtorFiscalCode, id) => { + bizEventList.push(createEvent(id, id, undefined, debtorFiscalCode)) +}) + +When('the user with fiscal code {string} asks the transaction with id {string} and isCart {string}', async (fiscalCode, id, cart) => { + let isCart = (cart == "true"); + responseToCheck = await getTransactionWithIdForUserWithFiscalCode(id, fiscalCode, isCart); +}) + +Then('the user gets the transaction with id {string}', (id) => { + assert.strictEqual(responseToCheck.data.infoTransaction.transactionId, id); +})