getDocumentWithHttpInfo(
UUID eServiceTemplateId,
UUID eServiceTemplateVersionId,
UUID documentId
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/probing/config/ProbingClientConfigs.java b/interop-qa-tests/src/main/java/it/pagopa/interop/probing/config/ProbingClientConfigs.java
new file mode 100644
index 0000000000..535b33b894
--- /dev/null
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/probing/config/ProbingClientConfigs.java
@@ -0,0 +1,40 @@
+package it.pagopa.interop.probing.config;
+
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Getter
+@Setter
+@Component
+public class ProbingClientConfigs {
+
+ /**
+ * Token da usare per le probing-api, non possiamo staccarlo in autonomia,
+ * bisogna fornire una client_assertion al team dev il quale ci fornirà un token a lunga scadenza (3 settimane)
+ */
+ private String bearerTokenKms;
+
+ private String baseUrl;
+
+ /**
+ * Token da usare per le probing-statistics-api, non possiamo staccarlo in autonomia,
+ * il flow cognito è SRP(Secure Remote Password) e non abbiamo tutte le info, AWS QA non ha nessun sistema cognito dichiarato
+ * ed evidentemente punta altrove.
+ *
+ * Il token lo si prende da FE facendo accesso con le opportune credenziali, dura 1 giorno.
+ */
+ private String bearerTokenTelemetry;
+
+ public ProbingClientConfigs(
+ @Value("${pn.interop.probing.base-url}") String baseUrl,
+ @Value("${pn.interop.probing.bearer-token-kms}") String bearerTokenKms,
+ @Value("${pn.interop.probing.bearer-token-telemetry}") String bearerTokenTelemetry
+ ) {
+ this.baseUrl = baseUrl;
+ this.bearerTokenKms = bearerTokenKms;
+ this.bearerTokenTelemetry = bearerTokenTelemetry;
+ }
+}
\ No newline at end of file
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/probing/service/IProbingClient.java b/interop-qa-tests/src/main/java/it/pagopa/interop/probing/service/IProbingClient.java
new file mode 100644
index 0000000000..95914f052b
--- /dev/null
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/probing/service/IProbingClient.java
@@ -0,0 +1,74 @@
+package it.pagopa.interop.probing.service;
+
+import it.pagopa.interop.authorization.service.utils.SettableBearerToken;
+import it.pagopa.interop.generated.openapi.clients.probing.model.*;
+import it.pagopa.interop.generated.openapi.clients.probingStatistics.model.TelemetryDataEserviceResponse;
+
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.util.List;
+import java.util.UUID;
+
+public interface IProbingClient extends SettableBearerToken {
+
+ void getProbingApiHealthStatus();
+
+ MainDataEserviceResponse getEserviceMainData(Long eserviceRecordId);
+
+ ProbingDataEserviceResponse getEserviceProbingData(Long eserviceRecordId);
+
+ List getAllEservice();
+
+ List findEserviceByName(String name);
+
+ List findEserviceByProducer(String producer);
+
+ SearchEserviceResponse searchEservices(
+ Integer limit,
+ Integer offset,
+ String eserviceName,
+ String producerName,
+ Integer versionNumber,
+ List state
+ );
+
+ void updateEserviceFrequency(
+ UUID eserviceId,
+ UUID versionId,
+ Integer frequency,
+ OffsetTime startTime,
+ OffsetTime endTime
+ );
+
+ void updateEserviceProbingState(
+ UUID eserviceId,
+ UUID versionId,
+ ChangeProbingStateRequest request
+ );
+
+ void updateEserviceState(
+ UUID eserviceId,
+ UUID versionId,
+ ChangeEserviceStateRequest request
+ );
+
+ List getEservicesProducers(
+ Integer limit,
+ Integer offset,
+ String producerName
+ );
+
+ void getStatisticsHealthStatus();
+
+ TelemetryDataEserviceResponse statisticsEservices(
+ Long eserviceRecordId,
+ Integer pollingFrequency
+ );
+
+ TelemetryDataEserviceResponse filteredStatisticsEservices(
+ Long eserviceRecordId,
+ Integer pollingFrequency,
+ OffsetDateTime startDate,
+ OffsetDateTime endDate
+ );
+}
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/probing/service/impl/ProbingClient.java b/interop-qa-tests/src/main/java/it/pagopa/interop/probing/service/impl/ProbingClient.java
new file mode 100644
index 0000000000..91205ee448
--- /dev/null
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/probing/service/impl/ProbingClient.java
@@ -0,0 +1,284 @@
+package it.pagopa.interop.probing.service.impl;
+
+import it.pagopa.interop.common.client.AbstractClient;
+import it.pagopa.interop.generated.openapi.clients.probing.ApiClient;
+import it.pagopa.interop.generated.openapi.clients.probing.api.EServicesApi;
+import it.pagopa.interop.generated.openapi.clients.probing.api.HealthApi;
+import it.pagopa.interop.generated.openapi.clients.probing.model.*;
+import it.pagopa.interop.probing.config.ProbingClientConfigs;
+import it.pagopa.interop.probing.service.IProbingClient;
+import it.pagopa.interop.utils.HttpCallExecutor;
+import lombok.ToString;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.UUID;
+
+@ToString
+@Component
+@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
+public class ProbingClient extends AbstractClient implements IProbingClient {
+
+ private static final DateTimeFormatter UTC_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(java.time.ZoneOffset.UTC);
+
+
+ // --- probing (core) ---
+ private final HealthApi statusApi;
+ private final EServicesApi eServicesApi;
+
+ // --- probingStatistics ---
+ private final it.pagopa.interop.generated.openapi.clients.probingStatistics.api.HealthApi statisticsStatusApi;
+ private final it.pagopa.interop.generated.openapi.clients.probingStatistics.api.TelemetryApi telemetryApi;
+
+ private final RestTemplate restTemplate;
+ private final String basePath;
+
+ private final String probingBearerTokenKms;
+ private final String probingBearerTokenTelemetry;
+
+ public ProbingClient(RestTemplate restTemplate,
+ ProbingClientConfigs probingClientConfigs,
+ HttpCallExecutor httpCallExecutor) {
+ this.restTemplate = restTemplate;
+ this.basePath = probingClientConfigs.getBaseUrl();
+
+ this.probingBearerTokenKms = probingClientConfigs.getBearerTokenKms();
+ this.probingBearerTokenTelemetry = probingClientConfigs.getBearerTokenTelemetry();
+
+ super.httpCallExecutor = httpCallExecutor;
+
+ // --- probing core ---
+ ApiClient probingApiClient = createProbingApiClient(probingBearerTokenKms);
+ this.statusApi = new HealthApi(probingApiClient);
+ this.eServicesApi = new EServicesApi(probingApiClient);
+
+ // --- probingStatistics ---
+ it.pagopa.interop.generated.openapi.clients.probingStatistics.ApiClient statsApiClient =
+ createStatisticsApiClient(probingBearerTokenTelemetry);
+ this.statisticsStatusApi = new it.pagopa.interop.generated.openapi.clients.probingStatistics.api.HealthApi(statsApiClient);
+ this.telemetryApi = new it.pagopa.interop.generated.openapi.clients.probingStatistics.api.TelemetryApi(statsApiClient);
+ }
+
+ @Override
+ public void setBearerToken(String bearerToken) {
+ // --- probing core ---
+ ApiClient probingApiClient = createProbingApiClient(bearerToken);
+ this.statusApi.setApiClient(probingApiClient);
+ this.eServicesApi.setApiClient(probingApiClient);
+
+ // --- probingStatistics ---
+ it.pagopa.interop.generated.openapi.clients.probingStatistics.ApiClient statsApiClient =
+ createStatisticsApiClient(bearerToken);
+ this.statisticsStatusApi.setApiClient(statsApiClient);
+ this.telemetryApi.setApiClient(statsApiClient);
+ }
+
+ private ApiClient createProbingApiClient(String bearerToken) {
+ ApiClient apiClient = new ApiClient(restTemplate);
+ apiClient.setBasePath(basePath);
+ apiClient.setBearerToken(bearerToken);
+ return apiClient;
+ }
+
+ private it.pagopa.interop.generated.openapi.clients.probingStatistics.ApiClient createStatisticsApiClient(String bearerToken) {
+ it.pagopa.interop.generated.openapi.clients.probingStatistics.ApiClient apiClient =
+ new it.pagopa.interop.generated.openapi.clients.probingStatistics.ApiClient(restTemplate);
+ apiClient.setBasePath(basePath);
+ apiClient.setBearerToken(bearerToken);
+ return apiClient;
+ }
+
+ @Override
+ public void getProbingApiHealthStatus() {
+ performOperation(statusApi::getStatusWithHttpInfo);
+ }
+
+ @Override
+ public MainDataEserviceResponse getEserviceMainData(Long eserviceRecordId) {
+ return performOperation(() -> eServicesApi.getEserviceMainDataWithHttpInfo(eserviceRecordId))
+ .orElseThrow(() -> new IllegalStateException(
+ "Errore nel recupero main data e-service (response non 2xx o body nullo)"
+ ));
+ }
+
+ @Override
+ public ProbingDataEserviceResponse getEserviceProbingData(Long eserviceRecordId) {
+ return performOperation(() -> eServicesApi.getEserviceProbingDataWithHttpInfo(eserviceRecordId))
+ .orElseThrow(() -> new IllegalStateException(
+ "Errore nel recupero probing data e-service (response non 2xx o body nullo)"
+ ));
+ }
+
+ @Override
+ public SearchEserviceResponse searchEservices(
+ Integer limit,
+ Integer offset,
+ String eserviceName,
+ String producerName,
+ Integer versionNumber,
+ List state
+ ) {
+ return performOperation(() -> eServicesApi.searchEservicesWithHttpInfo(
+ limit, offset, eserviceName, producerName, versionNumber, state
+ ))
+ .orElseThrow(() -> new IllegalStateException(
+ "Errore nella ricerca e-services (response non 2xx o body nullo)"
+ ));
+ }
+
+ @Override
+ public List getAllEservice() {
+ return searchAll(null, null, null, null);
+ }
+
+ @Override
+ public List findEserviceByName(String name) {
+ return searchAll(name, null, null, null);
+ }
+
+ @Override
+ public List findEserviceByProducer(String producer) {
+ return searchAll(null, producer, null, null);
+ }
+
+ private List searchAll(
+ String eserviceName,
+ String producerName,
+ Integer versionNumber,
+ List state
+ ) {
+ final int limit = 30;
+ int offset = 0;
+
+ List all = new java.util.ArrayList<>();
+ Long totalElements = null;
+
+ while (true) {
+ SearchEserviceResponse page = searchEservices(
+ limit,
+ offset,
+ eserviceName,
+ producerName,
+ versionNumber,
+ state
+ );
+
+ if (page == null) {
+ break;
+ }
+
+ if (totalElements == null) {
+ totalElements = page.getTotalElements();
+ }
+
+ List content = page.getContent();
+ if (content == null || content.isEmpty()) {
+ break;
+ }
+
+ all.addAll(content);
+
+ int previousOffset = offset;
+ offset += content.size();
+
+ // Protezione anti-loop: se non avanza interrompi
+ if (offset == previousOffset) {
+ break;
+ }
+
+ // Stop “preciso” se totalElements è presente
+ if (totalElements != null && offset >= totalElements) {
+ break;
+ }
+
+ // Fallback: se pagina più corta del limit, siamo all'ultima
+ if (content.size() < limit) {
+ break;
+ }
+ }
+
+ return all;
+ }
+
+
+ @Override
+ public void updateEserviceFrequency(UUID eserviceId, UUID versionId, Integer frequency, OffsetTime startTime, OffsetTime endTime) {
+ ChangeProbingFrequencyRequest req = new ChangeProbingFrequencyRequest();
+ req.setFrequency(frequency);
+ req.setStartTime(startTime != null
+ ? startTime.withOffsetSameInstant(ZoneOffset.UTC).toLocalTime().format(DateTimeFormatter.ISO_LOCAL_TIME)
+ : null
+ );
+ req.setEndTime(endTime != null
+ ? endTime.withOffsetSameInstant(ZoneOffset.UTC).toLocalTime().format(DateTimeFormatter.ISO_LOCAL_TIME)
+ : null
+ );
+
+ performOperation(() -> eServicesApi.updateEserviceFrequencyWithHttpInfo(eserviceId, versionId, req));
+
+ if (!super.httpCallExecutor.getResponseStatus().is2xxSuccessful()) {
+ throw new IllegalStateException("Errore durante l'update della frequency dell'e-ervice con id: " + eserviceId);
+ }
+ }
+
+ @Override
+ public void updateEserviceProbingState(UUID eserviceId, UUID versionId, ChangeProbingStateRequest request) {
+ performOperation(() -> eServicesApi.updateEserviceProbingStateWithHttpInfo(eserviceId, versionId, request));
+
+ if (!super.httpCallExecutor.getResponseStatus().is2xxSuccessful()) {
+ throw new IllegalStateException("Errore durante l'update del probing state dell'e-ervice con id: " + eserviceId);
+ }
+ }
+
+ @Override
+ public void updateEserviceState(UUID eserviceId, UUID versionId, ChangeEserviceStateRequest request) {
+ performOperation(() -> eServicesApi.updateEserviceStateWithHttpInfo(eserviceId, versionId, request));
+
+ if (!super.httpCallExecutor.getResponseStatus().is2xxSuccessful()) {
+ throw new IllegalStateException("Errore durante l'update dell'eservice state con e-service id: " + eserviceId);
+ }
+ }
+
+ @Override
+ public List getEservicesProducers(Integer limit, Integer offset, String producerName) {
+ return performOperation(() -> eServicesApi.getEservicesProducersWithHttpInfo(limit, offset, producerName))
+ .orElseThrow(() -> new IllegalStateException(
+ "Errore nel recupero producers (response non 2xx o body nullo)"
+ ));
+ }
+
+ @Override
+ public void getStatisticsHealthStatus() {
+ performOperation(statisticsStatusApi::getStatusWithHttpInfo);
+ }
+
+ @Override
+ public it.pagopa.interop.generated.openapi.clients.probingStatistics.model.TelemetryDataEserviceResponse statisticsEservices(Long eserviceRecordId, Integer pollingFrequency) {
+ return performOperation(() -> telemetryApi.statisticsEservicesWithHttpInfo(eserviceRecordId, pollingFrequency))
+ .orElseThrow(() -> new IllegalStateException(
+ "Errore nel recupero statistiche e-service (response non 2xx o body nullo)"
+ ));
+ }
+
+ @Override
+ public it.pagopa.interop.generated.openapi.clients.probingStatistics.model.TelemetryDataEserviceResponse filteredStatisticsEservices(Long eserviceRecordId, Integer pollingFrequency, OffsetDateTime startDate, OffsetDateTime endDate) {
+ return performOperation(() -> telemetryApi.filteredStatisticsEservicesWithHttpInfo(
+ eserviceRecordId, pollingFrequency, toUtcDateTimeFormat(startDate), toUtcDateTimeFormat(endDate)
+ ))
+ .orElseThrow(() -> new IllegalStateException(
+ "Errore nel recupero statistiche filtrate e-service (response non 2xx o body nullo)"
+ ));
+ }
+
+ public static String toUtcDateTimeFormat(OffsetDateTime dt) {
+ if (dt == null) return null;
+ return UTC_FMT.format(dt.toInstant());
+ }
+}
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/IPurposeTemplateClient.java b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/IPurposeTemplateClient.java
index e2c3d0fe30..8c5bee94c1 100644
--- a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/IPurposeTemplateClient.java
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/IPurposeTemplateClient.java
@@ -22,10 +22,12 @@ public interface IPurposeTemplateClient extends SettableBearerToken {
PurposeTemplateWithCompactCreator getPurposeTemplate(UUID purposeTemplateId) throws RestClientException;
EServiceDescriptorsPurposeTemplate getPurposeTemplateEServices(UUID purposeTemplateId, Integer offset, Integer limit, List producerIds, String eserviceName) throws RestClientException;
File getRiskAnalysisTemplateAnswerAnnotationDocument(UUID purposeTemplateId, UUID answerId, UUID documentId) throws RestClientException;
- EServiceDescriptorPurposeTemplate linkEServiceToPurposeTemplate(UUID purposeTemplateId, InlineObject2 inlineObject2) throws RestClientException;
+
+ EServiceDescriptorPurposeTemplate linkEServiceToPurposeTemplate(UUID purposeTemplateId, LinkEServiceToPurposeTemplateRequest request) throws RestClientException;
void publishPurposeTemplate(UUID purposeTemplateId) throws RestClientException;
void suspendPurposeTemplate(UUID purposeTemplateId) throws RestClientException;
- void unlinkEServiceToPurposeTemplate(UUID purposeTemplateId, InlineObject3 inlineObject3) throws RestClientException;
+
+ void unlinkEServiceToPurposeTemplate(UUID purposeTemplateId, LinkEServiceToPurposeTemplateRequest request) throws RestClientException;
void unsuspendPurposeTemplate(UUID purposeTemplateId) throws RestClientException;
PurposeTemplate updatePurposeTemplate(UUID purposeTemplateId, PurposeTemplateSeed purposeTemplateSeed) throws RestClientException;
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/M2MPurposeClientImpl.java b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/M2MPurposeClientImpl.java
index 731c71bbbb..a64aa902d4 100644
--- a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/M2MPurposeClientImpl.java
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/M2MPurposeClientImpl.java
@@ -3,23 +3,15 @@
import it.pagopa.interop.conf.InteropClientConfigs;
import it.pagopa.interop.generated.openapi.clients.m2mGateway.ApiClient;
import it.pagopa.interop.generated.openapi.clients.m2mGateway.api.PurposesApi;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.Agreement;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.DelegationRef;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.FileDownloadMultipart;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.Purpose;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.PurposeDraftUpdateSeed;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.PurposeVersion;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.PurposeVersionSeed;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.PurposeVersions;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.Purposes;
-import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.ReversePurposeDraftUpdateSeed;
+import it.pagopa.interop.generated.openapi.clients.m2mGateway.model.*;
import it.pagopa.interop.purpose.service.IM2MPurposeClient;
-import java.util.UUID;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
+import java.util.UUID;
+
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class M2MPurposeClientImpl implements IM2MPurposeClient {
@@ -131,7 +123,7 @@ public FileDownloadMultipart downloadPurposeVersionDocument(UUID purposeId, UUID
public Purpose patchPurpose(UUID purposeId, PurposePatchRequest body) {
return purposesApi.updateDraftPurpose(
purposeId,
- new PurposeDraftUpdateSeed()
+ new UpdateDraftPurposeRequest()
.title(body.getTitle())
.description(body.getDescription())
.riskAnalysisForm(body.getRiskAnalysisForm())
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeApiClientImpl.java b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeApiClientImpl.java
index 0a83838dc1..9c883a06af 100644
--- a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeApiClientImpl.java
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeApiClientImpl.java
@@ -15,6 +15,7 @@
import org.springframework.web.client.RestTemplate;
import java.io.File;
+import java.io.IOException;
import java.util.List;
import java.util.UUID;
@@ -127,7 +128,11 @@ public Purposes getProducerPurposes(Integer offset, Integer limit, String q, Lis
@Override
public File getRiskAnalysisDocument(UUID purposeId, UUID versionId, UUID documentId) {
- return purposesApi.getRiskAnalysisDocument(purposeId, versionId, documentId);
+ try {
+ return purposesApi.getRiskAnalysisDocument(purposeId, versionId, documentId).getFile();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeTemplateClientImpl.java b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeTemplateClientImpl.java
index 7a66cab7ee..49e6075e6b 100644
--- a/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeTemplateClientImpl.java
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/purpose/service/impl/PurposeTemplateClientImpl.java
@@ -16,6 +16,7 @@
import org.springframework.web.client.RestTemplate;
import java.io.File;
+import java.io.IOException;
import java.util.List;
import java.util.UUID;
@@ -111,12 +112,16 @@ public EServiceDescriptorsPurposeTemplate getPurposeTemplateEServices(UUID purpo
@Override
public File getRiskAnalysisTemplateAnswerAnnotationDocument(UUID purposeTemplateId, UUID answerId, UUID documentId) throws RestClientException {
- return purposesTemplateApi.getRiskAnalysisTemplateAnswerAnnotationDocument(purposeTemplateId, answerId, documentId);
+ try {
+ return purposesTemplateApi.getRiskAnalysisTemplateAnswerAnnotationDocument(purposeTemplateId, answerId, documentId).getFile();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
- public EServiceDescriptorPurposeTemplate linkEServiceToPurposeTemplate(UUID purposeTemplateId, InlineObject2 inlineObject2) throws RestClientException {
- return purposesTemplateApi.linkEServiceToPurposeTemplate(purposeTemplateId, inlineObject2);
+ public EServiceDescriptorPurposeTemplate linkEServiceToPurposeTemplate(UUID purposeTemplateId, LinkEServiceToPurposeTemplateRequest request) throws RestClientException {
+ return purposesTemplateApi.linkEServiceToPurposeTemplate(purposeTemplateId, request);
}
@Override
@@ -130,8 +135,8 @@ public void suspendPurposeTemplate(UUID purposeTemplateId) throws RestClientExce
}
@Override
- public void unlinkEServiceToPurposeTemplate(UUID purposeTemplateId, InlineObject3 inlineObject3) throws RestClientException {
- purposesTemplateApi.unlinkEServiceToPurposeTemplate(purposeTemplateId, inlineObject3);
+ public void unlinkEServiceToPurposeTemplate(UUID purposeTemplateId, LinkEServiceToPurposeTemplateRequest req) throws RestClientException {
+ purposesTemplateApi.unlinkEServiceToPurposeTemplate(purposeTemplateId, req);
}
@Override
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/tenant/service/impl/TenantsApiClientImpl.java b/interop-qa-tests/src/main/java/it/pagopa/interop/tenant/service/impl/TenantsApiClientImpl.java
index a14e7518d6..7008aa7d28 100644
--- a/interop-qa-tests/src/main/java/it/pagopa/interop/tenant/service/impl/TenantsApiClientImpl.java
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/tenant/service/impl/TenantsApiClientImpl.java
@@ -3,24 +3,8 @@
import it.pagopa.interop.conf.InteropClientConfigs;
import it.pagopa.interop.generated.openapi.clients.bff.ApiClient;
import it.pagopa.interop.generated.openapi.clients.bff.api.TenantsApi;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CertifiedAttributesResponse;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CertifiedTenantAttributeSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CompactOrganizations;
-import it.pagopa.interop.generated.openapi.clients.bff.model.DeclaredAttributesResponse;
-import it.pagopa.interop.generated.openapi.clients.bff.model.DeclaredTenantAttributeSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.InlineObject5;
-import it.pagopa.interop.generated.openapi.clients.bff.model.MailSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RequesterCertifiedAttributes;
-import it.pagopa.interop.generated.openapi.clients.bff.model.Tenant;
-import it.pagopa.interop.generated.openapi.clients.bff.model.TenantDelegatedFeaturesFlagsUpdateSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.TenantFeatureType;
-import it.pagopa.interop.generated.openapi.clients.bff.model.Tenants;
-import it.pagopa.interop.generated.openapi.clients.bff.model.UpdateVerifiedTenantAttributeSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.VerifiedAttributesResponse;
-import it.pagopa.interop.generated.openapi.clients.bff.model.VerifiedTenantAttributeSeed;
+import it.pagopa.interop.generated.openapi.clients.bff.model.*;
import it.pagopa.interop.tenant.service.ITenantsApi;
-import java.util.List;
-import java.util.UUID;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.retry.annotation.Backoff;
@@ -29,6 +13,9 @@
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;
+import java.util.List;
+import java.util.UUID;
+
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Retryable(
@@ -114,7 +101,7 @@ public void revokeCertifiedAttribute(UUID tenantId, UUID attributeId) {
@Override
public void revokeVerifiedAttribute(UUID tenantId, UUID attributeId, UUID agreementId) {
- tenantsApi.revokeVerifiedAttribute(tenantId, attributeId, new InlineObject5().agreementId(agreementId));
+ tenantsApi.revokeVerifiedAttribute(tenantId, attributeId, new RevokeVerifiedAttributeRequest().agreementId(agreementId));
}
@Override
diff --git a/interop-qa-tests/src/main/java/it/pagopa/interop/utils/HttpCallExecutor.java b/interop-qa-tests/src/main/java/it/pagopa/interop/utils/HttpCallExecutor.java
index a1bf03bad7..a2d6c59f0d 100644
--- a/interop-qa-tests/src/main/java/it/pagopa/interop/utils/HttpCallExecutor.java
+++ b/interop-qa-tests/src/main/java/it/pagopa/interop/utils/HttpCallExecutor.java
@@ -1,7 +1,5 @@
package it.pagopa.interop.utils;
-import static java.util.Objects.isNull;
-
import it.pagopa.interop.common.IHttpExecutor;
import lombok.Data;
import lombok.Getter;
@@ -16,6 +14,8 @@
import java.util.function.Function;
import java.util.function.Supplier;
+import static java.util.Objects.isNull;
+
@Slf4j
@Getter
@Data
@@ -26,6 +26,10 @@ public class HttpCallExecutor implements IHttpExecutor {
private String errorMessage;
private Object response;
+ private HttpStatus snapResponseStatus;
+ private Object snapResponse;
+ private String snapErrorMessage;
+
@Override
public HttpStatus performCall(Supplier promise) {
try {
@@ -83,4 +87,18 @@ public void setRawResponse(int statusCode, Object rawBody) {
this.response = rawBody;
}
+ @Override
+ public void snapshot() {
+ this.snapResponseStatus = this.responseStatus;
+ this.snapResponse = this.response;
+ this.snapErrorMessage = this.errorMessage;
+ }
+
+ @Override
+ public void resetFormSnapshot() {
+ this.responseStatus = this.snapResponseStatus;
+ this.response = this.snapResponse;
+ this.errorMessage = this.snapErrorMessage;
+ }
+
}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/agreement/AgreementCommonSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/agreement/AgreementCommonSteps.java
index d5592952db..9a20705fa1 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/agreement/AgreementCommonSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/agreement/AgreementCommonSteps.java
@@ -158,6 +158,8 @@ public void tenantHasAlreadyCreatedAndPublishedEService(String tenantType, int t
clientTokenConfigurator.setBearerToken(identityService.getToken(tenantType, null));
// Create e-services and publish descriptors
List eServiceDescriptorList = new ArrayList<>();
+ List eServiceNames = new ArrayList<>();
+
for (int i = 0; i < totalEservices; i++) {
// Create e-service and descriptor
int randomInt = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE);
@@ -176,10 +178,12 @@ public void tenantHasAlreadyCreatedAndPublishedEService(String tenantType, int t
sharedStepsContext.getEServicesCommonContext().setPublicationTimestamp(OffsetDateTime.now());
// Add the e-service to the list of published ones
eServiceDescriptorList.add(eServiceDescriptor);
+ eServiceNames.add(eserviceName);
}
// Set the first e-service and descriptor
if (!eServiceDescriptorList.isEmpty()) {
EServicesCommonContext eServicesCommonContext = sharedStepsContext.getEServicesCommonContext();
+ eServicesCommonContext.setName(eServiceNames.get(0));
eServicesCommonContext.setPublishedEservicesIds(eServiceDescriptorList);
EServiceDescriptor firstDescriptor = eServiceDescriptorList.get(0);
eServicesCommonContext.setEserviceId(firstDescriptor.getEServiceId());
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/CertifiedAttributeCreationSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/CertifiedAttributeCreationSteps.java
index fbc30538fa..76cc933ae3 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/CertifiedAttributeCreationSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/CertifiedAttributeCreationSteps.java
@@ -8,7 +8,8 @@
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
-import org.apache.commons.lang.math.RandomUtils;
+
+import java.util.concurrent.ThreadLocalRandom;
public class CertifiedAttributeCreationSteps {
private final SharedStepsContext sharedStepsContext;
@@ -32,7 +33,7 @@ public void createCertifiedAttribute() {
clientTokenConfigurator.setBearerToken(sharedStepsContext.getUserToken());
httpCallExecutor.performCall(() -> clientTokenConfigurator.getAttributeApiClient().createCertifiedAttributeRE(
new CertifiedAttributeSeed()
- .name("new certified attribute %d".formatted(RandomUtils.nextInt()))
+ .name("new certified attribute %d".formatted(ThreadLocalRandom.current().nextInt()))
.description("description test")));
}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/DeclaredAttributeCreationSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/DeclaredAttributeCreationSteps.java
index 924892433c..3f5cd9da85 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/DeclaredAttributeCreationSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/DeclaredAttributeCreationSteps.java
@@ -9,12 +9,12 @@
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
import it.pagopa.pn.interop.cucumber.steps.common.AttributeCommonContext;
-import java.time.OffsetDateTime;
-
import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
-import org.apache.commons.lang.math.RandomUtils;
import org.springframework.http.ResponseEntity;
+import java.time.OffsetDateTime;
+import java.util.concurrent.ThreadLocalRandom;
+
public class DeclaredAttributeCreationSteps {
private final SharedStepsContext sharedStepsContext;
private final ClientTokenConfigurator clientTokenConfigurator;
@@ -34,7 +34,7 @@ public DeclaredAttributeCreationSteps(
@When("l'utente crea un attributo dichiarato")
public void createDeclaredAttribute() {
- String attributeName = "new declared attribute %d".formatted(RandomUtils.nextInt());
+ String attributeName = "new declared attribute %d".formatted(ThreadLocalRandom.current().nextInt());
String attributeDescription = "description test";
clientTokenConfigurator.setBearerToken(sharedStepsContext.getUserToken());
httpCallExecutor.performCall(() -> clientTokenConfigurator.getAttributeApiClient().createDeclaredAttributeRE(
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/VerifiedAttributeCreationSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/VerifiedAttributeCreationSteps.java
index 20c3059509..2b03c17fcd 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/VerifiedAttributeCreationSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/attribute/VerifiedAttributeCreationSteps.java
@@ -9,12 +9,12 @@
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
import it.pagopa.pn.interop.cucumber.steps.common.AttributeCommonContext;
-import java.time.OffsetDateTime;
-
import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
-import org.apache.commons.lang.math.RandomUtils;
import org.springframework.http.ResponseEntity;
+import java.time.OffsetDateTime;
+import java.util.concurrent.ThreadLocalRandom;
+
public class VerifiedAttributeCreationSteps {
private final SharedStepsContext sharedStepsContext;
private final ClientTokenConfigurator clientTokenConfigurator;
@@ -34,7 +34,7 @@ public VerifiedAttributeCreationSteps(
@When("l'utente crea un attributo verificato")
public void createVerifiedAttribute() {
- String attributeName = "new verified attribute %d".formatted(RandomUtils.nextInt());
+ String attributeName = "new verified attribute %d".formatted(ThreadLocalRandom.current().nextInt());
String attributeDescription = "description test";
clientTokenConfigurator.setBearerToken(sharedStepsContext.getUserToken());
httpCallExecutor.performCall(() -> clientTokenConfigurator.getAttributeApiClient().createVerifiedAttributeRE(
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/catalog/EServiceConsumersDownloadSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/catalog/EServiceConsumersDownloadSteps.java
index f2e5fb1a27..0c1c0e64a2 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/catalog/EServiceConsumersDownloadSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/catalog/EServiceConsumersDownloadSteps.java
@@ -5,9 +5,9 @@
import it.pagopa.interop.authorization.service.identity.IdentityService;
import it.pagopa.interop.generated.openapi.clients.bff.model.AgreementState;
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
-import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
import it.pagopa.pn.interop.cucumber.steps.common.EServicesCommonContext;
+import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
import java.util.UUID;
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/config/InteropCucumberSpringIntegration.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/config/InteropCucumberSpringIntegration.java
index 5f62492074..4f992b4337 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/config/InteropCucumberSpringIntegration.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/config/InteropCucumberSpringIntegration.java
@@ -3,11 +3,7 @@
import io.cucumber.spring.CucumberContextConfiguration;
import it.pagopa.interop.agreement.service.IAgreementClient;
import it.pagopa.interop.agreement.service.IEServiceClient;
-import it.pagopa.interop.agreement.service.impl.AgreementClientImpl;
-import it.pagopa.interop.agreement.service.impl.EServiceApiClientImpl;
-import it.pagopa.interop.agreement.service.impl.M2MAgreementClientImpl;
-import it.pagopa.interop.agreement.service.impl.M2MClientsClientImpl;
-import it.pagopa.interop.agreement.service.impl.M2MTenantClientImpl;
+import it.pagopa.interop.agreement.service.impl.*;
import it.pagopa.interop.attribute.service.IAttributeApiClient;
import it.pagopa.interop.attribute.service.impl.AttributeApiClientImpl;
import it.pagopa.interop.attribute.service.impl.M2MCertifiedAttributeClientImpl;
@@ -38,6 +34,8 @@
import it.pagopa.interop.eservice.service.mapper.EServiceAttributeMapperImpl;
import it.pagopa.interop.event.mapper.M2MEventMapperImpl;
import it.pagopa.interop.event.service.M2MEventClientImpl;
+import it.pagopa.interop.probing.config.ProbingClientConfigs;
+import it.pagopa.interop.probing.service.impl.ProbingClient;
import it.pagopa.interop.purpose.RiskAnalysisDataInitializer;
import it.pagopa.interop.purpose.service.IPurposeTemplateClient;
import it.pagopa.interop.purpose.service.impl.M2MPurposeClientImpl;
@@ -56,21 +54,13 @@
import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.M2MDataPreparationService;
import it.pagopa.pn.interop.cucumber.steps.e_service_template.shared.EServiceTemplateStepContext;
import it.pagopa.pn.interop.cucumber.steps.e_service_template.shared.EServiceTemplateTestAssistant;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.assistant.EServiceDelegationPatchOperationsAssistant;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.assistant.EServiceDescriptionPatchOperationsAssistant;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.assistant.EServiceNamePatchOperationsAssistant;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.assistant.EServicePatchContext;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.assistant.EServicePatchOperationsAssistant;
+import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.assistant.*;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.descriptor.assistant.EServiceDescriptorPatchContext;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.descriptor.assistant.EServiceDescriptorPatchOperationsAssistant;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.descriptor.assistant.EServiceDescriptorQuotasPatchOperationsAssistant;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.descriptor.mapper.EServiceDescriptorMapperImpl;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.descriptor.mapper.EServiceDescriptorQuotasMapperImpl;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.mapper.DocumentMapperImpl;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.mapper.EServiceDelegationMapperImpl;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.mapper.EServiceDescriptionMapperImpl;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.mapper.EServiceMapperImpl;
-import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.mapper.EServiceNameMapperImpl;
+import it.pagopa.pn.interop.cucumber.steps.m2m.eservice.mapper.*;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice_template.assistant.EServiceTemplatePatchContext;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice_template.assistant.EServiceTemplatePatchOperationsAssistant;
import it.pagopa.pn.interop.cucumber.steps.m2m.eservice_template.mapper.EServiceTemplateMapperImpl;
@@ -121,6 +111,7 @@
TracingFileUtils.class,
BlobFileCreator.class,
TracingClientConfigs.class,
+ ProbingClientConfigs.class,
DevAbstractInteropTracingClient.class,
QAAbstractInteropTracingClient.class,
CommonUtils.class,
@@ -183,7 +174,8 @@
ISelfcareClient.class,
SelfcareClientImpl.class,
IPurposeTemplateClient.class,
- PurposeTemplateClientImpl.class
+ PurposeTemplateClientImpl.class,
+ ProbingClient.class
})
@EnableScheduling
@EnableConfigurationProperties
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/delegate/DelegationCommonStep.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/delegate/DelegationCommonStep.java
index 1d7e578ce6..c3531cf324 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/delegate/DelegationCommonStep.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/delegate/DelegationCommonStep.java
@@ -168,6 +168,12 @@ public void thenStatusCodeIs(int statusCode) {
else Assertions.assertEquals(statusCode, actualStatusCode);
}
+ @Then("la response riporta lo status code {int}")
+ public void statusCodeIs(int statusCode) {
+ int actualStatusCode = httpCallExecutor.getResponseStatus().value();
+ Assertions.assertEquals(statusCode, actualStatusCode);
+ }
+
boolean isSuccessful(int statusCode) {
return statusCode >= 200 && statusCode < 300;
}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/quota/EServiceTemplateQuotaUpdateSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/quota/EServiceTemplateQuotaUpdateSteps.java
index 83ab31dc90..cba065c234 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/quota/EServiceTemplateQuotaUpdateSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/quota/EServiceTemplateQuotaUpdateSteps.java
@@ -1,9 +1,5 @@
package it.pagopa.pn.interop.cucumber.steps.e_service_template.quota;
-import static java.lang.Math.abs;
-import static java.util.Objects.nonNull;
-import static org.assertj.core.api.Assertions.fail;
-
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import it.pagopa.interop.authorization.service.utils.PollingPredicateException;
@@ -14,11 +10,16 @@
import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceTemplateVersionQuotasUpdateSeed;
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
-import java.util.UUID;
import lombok.Data;
-import org.apache.commons.lang.math.RandomUtils;
import org.springframework.http.ResponseEntity;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static java.lang.Math.abs;
+import static java.util.Objects.nonNull;
+import static org.assertj.core.api.Assertions.fail;
+
/** Cucumber steps involving quotas of E-service templates */
@Data
public class EServiceTemplateQuotaUpdateSteps {
@@ -50,7 +51,7 @@ public void editEServiceTemplateVersionQuotas() {
}
private EServiceTemplateVersionQuotasUpdateSeed nextQuotasUpdateSeed() {
- int dailyCallsPerConsumer = RandomUtils.nextInt(1_000_000_000); // numero massimo supportato
+ int dailyCallsPerConsumer = ThreadLocalRandom.current().nextInt(1_000_000_000); // numero massimo supportato
return new EServiceTemplateVersionQuotasUpdateSeed()
.voucherLifespan(86400)
.dailyCallsTotal(dailyCallsPerConsumer + 1)
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/shared/EServiceTemplateTestAssistant.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/shared/EServiceTemplateTestAssistant.java
index cf76343bad..786321ce67 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/shared/EServiceTemplateTestAssistant.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/shared/EServiceTemplateTestAssistant.java
@@ -1,11 +1,5 @@
package it.pagopa.pn.interop.cucumber.steps.e_service_template.shared;
-import static java.util.Objects.nonNull;
-import static java.util.Objects.requireNonNull;
-import static org.apache.commons.lang3.StringUtils.isNotEmpty;
-import static org.assertj.core.api.Assertions.fail;
-
-import com.google.common.io.Files;
import it.pagopa.interop.authorization.service.identity.IdentityService;
import it.pagopa.interop.authorization.service.utils.PollingPredicateException;
import it.pagopa.interop.authorization.service.utils.PollingService;
@@ -14,31 +8,14 @@
import it.pagopa.interop.e_service_template.IEServiceTemplateClient.EServiceTemplateDocumentKind;
import it.pagopa.interop.e_service_template.mapper.DescriptorAttributesMapper;
import it.pagopa.interop.e_service_template.mapper.RiskAnalysisMapper;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CreatedResource;
-import it.pagopa.interop.generated.openapi.clients.bff.model.DescriptorAttributes;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceDoc;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceTemplateAttributesSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceTemplateRiskAnalysis;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceTemplateRiskAnalysisSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceTemplateVersionAttributeSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceTemplateVersionDetails;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceTemplateVersionState;
-import it.pagopa.interop.generated.openapi.clients.bff.model.TenantKind;
-import it.pagopa.interop.generated.openapi.clients.bff.model.UpdateEServiceTemplateVersionSeed;
+import it.pagopa.interop.generated.openapi.clients.bff.model.*;
import it.pagopa.interop.purpose.domain.RiskAnalysis;
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
import it.pagopa.pn.interop.cucumber.steps.common.EServiceTemplateDocumentInfo;
import it.pagopa.pn.interop.cucumber.steps.common.EServiceTemplateInfo;
import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.UUID;
-import java.util.function.BiConsumer;
-import java.util.function.Predicate;
import lombok.Data;
-import org.apache.commons.lang.math.RandomUtils;
import org.jeasy.random.EasyRandom;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
@@ -47,6 +24,19 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+
+import static java.util.Objects.nonNull;
+import static java.util.Objects.requireNonNull;
+import static org.apache.commons.lang3.StringUtils.isNotEmpty;
+import static org.assertj.core.api.Assertions.fail;
+
/** It contains general utility functions used across all other classes. */
@Data
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@@ -286,7 +276,7 @@ private void checkDocumentAddedToEServiceTemplateVersion(UUID eServiceTemplateId
* sia inviando che ricevendo, anziché restituire void o un
* oggetto File restituiscano un'oggetto hash di qualche tipo; a quel
* punto qui basterà confrontare i valori hash. */
- return res.getStatusCode().is2xxSuccessful() && nonNull(res.getBody()) && Arrays.equals(Files.toByteArray(res.getBody()), lastAddedDocument.body());
+ return res.getStatusCode().is2xxSuccessful() && nonNull(res.getBody()) && Arrays.equals(java.nio.file.Files.readAllBytes(res.getBody().getFile().toPath()), lastAddedDocument.body());
} catch (IOException e) {
throw new RuntimeException("Errore nella lettura del body binario della risposta HTTP: %s".formatted(res), e);
}
@@ -301,7 +291,7 @@ private void checkDocumentAddedToEServiceTemplateVersion(UUID eServiceTemplateId
public String buildPrettyName(EServiceTemplateDocumentKind kind) {
return "e-service-template-%s-%d-%d".formatted(kind.toString(), sharedStepsContext.getTestSeed(),
- RandomUtils.nextInt(1_000_000));
+ ThreadLocalRandom.current().nextInt(1_000_000));
}
public void addRiskAnalysisToEServiceTemplateSuccessfully() {
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/version/crud/EServiceTemplateVersionCreateSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/version/crud/EServiceTemplateVersionCreateSteps.java
index 5c81776752..77dd7e3079 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/version/crud/EServiceTemplateVersionCreateSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/e_service_template/version/crud/EServiceTemplateVersionCreateSteps.java
@@ -1,6 +1,5 @@
package it.pagopa.pn.interop.cucumber.steps.e_service_template.version.crud;
-import com.google.common.base.Predicates;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
@@ -13,10 +12,11 @@
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
import it.pagopa.pn.interop.cucumber.steps.common.EServiceTemplateInfo;
import it.pagopa.pn.interop.cucumber.steps.e_service_template.shared.EServiceTemplateTestAssistant;
-import java.util.UUID;
import lombok.Data;
import org.springframework.http.ResponseEntity;
+import java.util.UUID;
+
/** Cucumber steps involving creation, editing, viewing or deletion
* of E-service template versions */
// TODO perché @Data? Considerarne rimozione da questa e dalle altre classi
@@ -70,7 +70,10 @@ public void addEServiceTemplateVersion(EServiceTemplateVersionState state) {
@Then("la creazione di una ulteriore versione nell'e-service template è stata effettuata correttamente")
public void checkEServiceTemplateVersionCreated() {
- testAssistant.checkEServiceTemplateVersion(Predicates.alwaysTrue(), "La versione dell'e-service template non è stata creata correttamente");
+ testAssistant.checkEServiceTemplateVersion(
+ x -> true,
+ "La versione dell'e-service template non è stata creata correttamente"
+ );
}
@Then("la creazione di una ulteriore versione in stato {eServiceTemplateVersionState} nell'e-service template è stata effettuata correttamente")
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/m2m/common/ScenarioHttpCallExecutor.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/m2m/common/ScenarioHttpCallExecutor.java
index ee305524c6..e2d990b0b4 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/m2m/common/ScenarioHttpCallExecutor.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/m2m/common/ScenarioHttpCallExecutor.java
@@ -2,13 +2,13 @@
import io.cucumber.spring.ScenarioScope;
import it.pagopa.interop.common.IHttpExecutor;
-import java.util.function.Function;
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpStatusCodeException;
+import java.util.function.Function;
import java.util.function.Supplier;
@Component
@@ -20,6 +20,10 @@ public class ScenarioHttpCallExecutor implements IHttpExecutor {
private Object response;
private String errorMessage;
+ private HttpStatus snapResponseStatus;
+ private Object snapResponse;
+ private String snapErrorMessage;
+
@Override
public T performCallSavingBodyResponse(Supplier> promise) {
T body = performCall(promise, ResponseEntity::getStatusCode).getBody();
@@ -73,5 +77,19 @@ public void setRawResponse(int statusCode, Object rawBody) {
this.response = rawBody;
}
+ @Override
+ public void snapshot() {
+ this.snapResponseStatus = this.responseStatus;
+ this.snapResponse = this.response;
+ this.snapErrorMessage = this.errorMessage;
+ }
+
+ @Override
+ public void resetFormSnapshot() {
+ this.responseStatus = this.snapResponseStatus;
+ this.response = this.snapResponse;
+ this.errorMessage = this.snapErrorMessage;
+ }
+
}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/m2m/common/utils/AbstractResolver.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/m2m/common/utils/AbstractResolver.java
new file mode 100644
index 0000000000..441b8d2e89
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/m2m/common/utils/AbstractResolver.java
@@ -0,0 +1,44 @@
+package it.pagopa.pn.interop.cucumber.steps.m2m.common.utils;
+
+import it.pagopa.pn.interop.cucumber.utility.enums.ResolvableToken;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public abstract class AbstractResolver {
+
+ protected T resolveOrParse(
+ String raw,
+ Function nonTokenParser,
+ Supplier actualSupplier,
+ Supplier expectedSupplier,
+ Supplier randomSupplier,
+ Supplier blankSupplier
+ ) {
+ if (raw == null) return nonTokenParser.apply(null);
+
+ ResolvableToken token = ResolvableToken.from(raw);
+ if (token == null) {
+ return nonTokenParser.apply(raw);
+ }
+
+ return switch (token) {
+ case ACTUAL -> actualSupplier != null ? actualSupplier.get() : null;
+ case EXPECTED -> expectedSupplier != null ? expectedSupplier.get() : null;
+ case RANDOM -> randomSupplier != null ? randomSupplier.get() : null;
+ case BLANK -> blankSupplier != null ? blankSupplier.get() : null;
+ case NULL -> null;
+ default -> throw new IllegalArgumentException("Unknown token: " + token);
+ };
+ }
+
+ // overload comodo quando BLANK/RANDOM non servono
+ protected T resolveOrParse(
+ String raw,
+ Function nonTokenParser,
+ Supplier actualSupplier,
+ Supplier expectedSupplier
+ ) {
+ return resolveOrParse(raw, nonTokenParser, actualSupplier, expectedSupplier, null, null);
+ }
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/ProbingLoadSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/ProbingLoadSteps.java
new file mode 100644
index 0000000000..bdc60ad9ed
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/ProbingLoadSteps.java
@@ -0,0 +1,256 @@
+package it.pagopa.pn.interop.cucumber.steps.probing;
+
+import io.cucumber.datatable.DataTable;
+import io.cucumber.java.en.And;
+import io.cucumber.java.en.Given;
+import io.cucumber.java.en.Then;
+import io.cucumber.java.en.When;
+import it.pagopa.interop.generated.openapi.clients.probing.model.ChangeProbingStateRequest;
+import it.pagopa.interop.generated.openapi.clients.probing.model.ProbingDataEserviceResponse;
+import it.pagopa.interop.probing.service.impl.ProbingClient;
+import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
+import it.pagopa.pn.interop.cucumber.steps.probing.model.EserviceRow;
+import lombok.extern.slf4j.Slf4j;
+import org.assertj.core.api.Assertions;
+
+import java.time.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.LongAdder;
+
+import static it.pagopa.pn.interop.cucumber.utility.StepParser.dateTimeOrNull;
+import static it.pagopa.pn.interop.cucumber.utility.StepParser.durationOrNull;
+
+@Slf4j
+public class ProbingLoadSteps {
+ private final ProbingClient probingClient;
+
+ private int totalEservices;
+ private int workers;
+
+ private int frequencyMinutes;
+ private String startDateExpr;
+ private String endDateExpr;
+
+ private int waitPeriods; // quanti periodi aspettare
+ private Duration extraWait; // buffer oltre ai periodi
+ private Duration recentTolerance; // tolleranza
+
+ // calcolati
+ private Duration waitDuration;
+
+ // Timestamp riferimento: quando abilito
+ private Instant enableTimeUtc;
+
+ public ProbingLoadSteps(ProbingClient probingClient, SharedStepsContext sharedStepsContext) {
+ this.probingClient = probingClient;
+ this.probingClient.setHttpCallExecutor(sharedStepsContext.getHttpCallExecutor());
+ }
+
+ @Given("preparo il load test probing con:")
+ public void setup(DataTable table) {
+ Map row = table.asMaps().get(0);
+
+ totalEservices = Integer.parseInt(row.get("totalEservices"));
+ workers = Integer.parseInt(row.get("workers"));
+
+ frequencyMinutes = Integer.parseInt(row.get("frequency"));
+ startDateExpr = row.get("startDate");
+ endDateExpr = row.get("endDate");
+
+ waitPeriods = Integer.parseInt(row.get("waitPeriods"));
+ extraWait = durationOrNull(row.get("extraWait"));
+ recentTolerance = durationOrNull(row.get("recentTolerance"));
+
+ Assertions.assertThat(totalEservices).as("totalEservices").isGreaterThan(0);
+ Assertions.assertThat(workers).as("workers").isGreaterThan(0);
+
+ Assertions.assertThat(frequencyMinutes).as("frequency").isGreaterThanOrEqualTo(1);
+ Assertions.assertThat(waitPeriods).as("waitPeriods").isGreaterThanOrEqualTo(1);
+ Assertions.assertThat(extraWait).as("extraWait").isNotNull();
+ Assertions.assertThat(recentTolerance).as("recentTolerance").isNotNull();
+
+ // Attesa = N * frequency + buffer
+ waitDuration = Duration.ofMinutes((long) frequencyMinutes * (long) waitPeriods).plus(extraWait);
+
+ log.info("LOAD setup: total={}, workers={}, frequency={}m, window=[{}, {}], waitPeriods={}, extraWait={}, waitDuration={}, recentTolerance={}",
+ totalEservices, workers, frequencyMinutes, startDateExpr, endDateExpr, waitPeriods, extraWait, waitDuration, recentTolerance
+ );
+ }
+
+ @When("aggiorno scheduling in parallelo per tutti gli eservice")
+ public void updateSchedulingParallel() {
+ OffsetDateTime startUtc = dateTimeOrNull(startDateExpr);
+ OffsetDateTime endUtc = dateTimeOrNull(endDateExpr);
+
+ Assertions.assertThat(endUtc).as("endDate deve essere dopo startDate").isAfter(startUtc);
+
+ runParallelRange(totalEservices, idx -> {
+ EserviceRow row = EserviceRow.atIndex(idx);
+
+ probingClient.updateEserviceFrequency(row.getEserviceId(), row.getVersionId(), frequencyMinutes, startUtc.toOffsetTime(), endUtc.toOffsetTime());
+ }, "updateScheduling");
+ }
+
+ @And("abilito probing in parallelo per tutti gli eservice")
+ public void enableParallel() {
+ // momento a partire dal quale mi aspetto aggiornamenti
+ enableTimeUtc = Instant.now();
+
+ runParallelRange(totalEservices, idx -> {
+ EserviceRow row = EserviceRow.atIndex(idx);
+
+ ChangeProbingStateRequest req = new ChangeProbingStateRequest().probingEnabled(true);
+ probingClient.updateEserviceProbingState(row.getEserviceId(), row.getVersionId(), req);
+ }, "enableProbing");
+
+ log.info("Enable completato. enableTimeUtc={}", enableTimeUtc);
+ }
+
+ @And("attendo N periodi di frequency più extraWait")
+ public void waitNPeriodsPlusExtra() {
+ log.info("Attendo {}", waitDuration);
+ sleep(waitDuration);
+ }
+
+ @Then("verifico in parallelo che responseReceived sia valorizzata e aggiornata dopo l'enable per tutti gli eservice")
+ public void verifyAllUpdatedAfterEnable() {
+ Assertions.assertThat(enableTimeUtc).as("enableTimeUtc").isNotNull();
+
+ Instant minAllowed = enableTimeUtc.minus(recentTolerance);
+ log.info("Verifica: responseReceived deve essere >= {} (enableTimeUtc={} minus tolerance={})",
+ minAllowed, enableTimeUtc, recentTolerance
+ );
+
+ ConcurrentLinkedQueue notOk = new ConcurrentLinkedQueue<>();
+
+ // Una sola GET per e-service
+ runParallelRange(totalEservices, idx -> {
+ Instant last = readLastResponseInstant(idx);
+
+ if (last == null) {
+ notOk.add("idx=" + idx + " responseReceived=null");
+ return;
+ }
+
+ if (last.isBefore(minAllowed)) {
+ notOk.add("idx=" + idx + " responseReceived=" + last + " < minAllowed=" + minAllowed);
+ }
+ }, "verifyAllOnce");
+
+ if (!notOk.isEmpty()) {
+ List top = notOk.stream().limit(30).toList();
+ throw new AssertionError(
+ "Verifica fallita: " + notOk.size() + "/" + totalEservices + " eservice non aggiornati dopo l'enable.\n" +
+ "Parametri: frequency=" + frequencyMinutes + "m, waitPeriods=" + waitPeriods + ", extraWait=" + extraWait +
+ ", waitDuration=" + waitDuration + ", tolerance=" + recentTolerance + "\n" +
+ "Esempi (max 30):\n" + String.join("\n", top)
+ );
+ }
+
+ log.info("OK: tutti gli eservice risultano aggiornati dopo l'enable.");
+ }
+
+ @And("disabilito probing in parallelo per tutti gli eservice")
+ public void disableParallel() {
+ runParallelRange(totalEservices, idx -> {
+ EserviceRow row = EserviceRow.atIndex(idx);
+
+ UUID eserviceId = row.getEserviceId();
+ UUID versionId = row.getVersionId();
+
+ ChangeProbingStateRequest req = new ChangeProbingStateRequest().probingEnabled(false);
+ probingClient.updateEserviceProbingState(eserviceId, versionId, req);
+ }, "disableProbing");
+ }
+
+ private Instant readLastResponseInstant(int idx) {
+ EserviceRow row = EserviceRow.atIndex(idx);
+
+ Long recordId = row.getId();
+ ProbingDataEserviceResponse resp = probingClient.getEserviceProbingData(recordId);
+ Assertions.assertThat(resp).as("ProbingDataEserviceResponse").isNotNull();
+
+ String rr = resp.getResponseReceived();
+ if (rr == null || rr.isBlank()) return null;
+
+ // 1) UTC standard
+ try {
+ return Instant.parse(rr);
+ } catch (Exception ignored) {
+ }
+ try {
+ return OffsetDateTime.parse(rr).toInstant();
+ } catch (Exception ignored) {
+ }
+
+ // 2) fallback legacy (solo orario)
+ try {
+ OffsetTime ot = OffsetTime.parse(rr);
+ return ot.atDate(LocalDate.now()).toInstant();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("responseReceived non parsabile: '" + rr + "'", e);
+ }
+ }
+
+ private void runParallelRange(int total, ThrowingIntConsumer fn, String phase) {
+ ExecutorService pool = Executors.newFixedThreadPool(workers);
+ try {
+ LongAdder ok = new LongAdder();
+ ConcurrentLinkedQueue errors = new ConcurrentLinkedQueue<>();
+ List> futures = new ArrayList<>(total);
+
+ long start = System.nanoTime();
+
+ for (int i = 0; i < total; i++) {
+ final int idx = i;
+ futures.add(pool.submit(() -> {
+ try {
+ fn.accept(idx);
+ ok.increment();
+ } catch (Exception e) {
+ errors.add("idx=" + idx + " -> " + e.getMessage());
+ }
+ }));
+ }
+
+ for (Future> f : futures) {
+ try {
+ f.get();
+ } catch (Exception e) {
+ errors.add("future -> " + e.getMessage());
+ }
+ }
+
+ long ms = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
+ log.info("Phase '{}' completata: {} ok / {} tot in {}ms (workers={})",
+ phase, ok.sum(), total, ms, workers
+ );
+
+ if (!errors.isEmpty()) {
+ List top = errors.stream().limit(30).toList();
+ throw new AssertionError(
+ "Phase '" + phase + "' fallita: errors=" + errors.size() + ", ok=" + ok.sum() + "/" + total + "\n" +
+ "Esempi (max 30):\n" + String.join("\n", top)
+ );
+ }
+ } finally {
+ pool.shutdownNow();
+ }
+ }
+
+ @FunctionalInterface
+ private interface ThrowingIntConsumer {
+ void accept(int value) throws Exception;
+ }
+
+ private static void sleep(Duration d) {
+ try {
+ Thread.sleep(d.toMillis());
+ } catch (InterruptedException ignored) {
+ }
+ }
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/ProbingSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/ProbingSteps.java
new file mode 100644
index 0000000000..4453fe2a3d
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/ProbingSteps.java
@@ -0,0 +1,620 @@
+package it.pagopa.pn.interop.cucumber.steps.probing;
+
+import io.cucumber.java.en.And;
+import io.cucumber.java.en.Given;
+import io.cucumber.java.en.Then;
+import io.cucumber.java.en.When;
+import it.pagopa.interop.authorization.service.utils.PollingService;
+import it.pagopa.interop.common.IHttpExecutor;
+import it.pagopa.interop.generated.openapi.clients.probing.model.*;
+import it.pagopa.interop.generated.openapi.clients.probingStatistics.model.TelemetryDataEserviceResponse;
+import it.pagopa.interop.probing.service.impl.ProbingClient;
+import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
+import it.pagopa.pn.interop.cucumber.steps.probing.model.EserviceRow;
+import it.pagopa.pn.interop.cucumber.steps.probing.model.ProbingContext;
+import it.pagopa.pn.interop.cucumber.steps.probing.utils.ProbingResolver;
+import it.pagopa.pn.interop.cucumber.steps.probing.utils.ProbingUtils;
+import it.pagopa.pn.interop.cucumber.utility.StepParser;
+import lombok.extern.slf4j.Slf4j;
+import org.assertj.core.api.Assertions;
+import org.springframework.http.HttpStatus;
+
+import java.time.*;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import static it.pagopa.pn.interop.cucumber.steps.probing.utils.ProbingUtils.matchesAllFilters;
+import static it.pagopa.pn.interop.cucumber.utility.StepParser.*;
+
+@Slf4j
+public class ProbingSteps {
+
+ private final IHttpExecutor httpCallExecutor;
+ private final ProbingClient probingClient;
+ private final ProbingContext probingContext;
+ private final ProbingResolver resolver;
+
+ public ProbingSteps(ProbingClient probingClient, SharedStepsContext sharedStepsContext) {
+ this.httpCallExecutor = sharedStepsContext.getHttpCallExecutor();
+ this.probingClient = probingClient;
+ this.probingClient.setHttpCallExecutor(httpCallExecutor);
+ this.probingContext = new ProbingContext();
+ this.resolver = new ProbingResolver(probingContext);
+ }
+
+ @And("il microservizio {string} risulta attivo")
+ public void getStatus(String ms) {
+ final int maxTry = 30; // ~30 secondi
+ final long sleepMs = 1_000L; // 1 secondo
+
+ switch (ms) {
+ case "probing-api" -> PollingService.makePolling(
+ () -> {
+ probingClient.getProbingApiHealthStatus();
+ return httpCallExecutor.getResponseStatus();
+ },
+ HttpStatus::is2xxSuccessful,
+ "Il ms " + ms + " dovrebbe risultare attivo",
+ maxTry,
+ sleepMs
+ );
+
+ case "probing-statistics-api" -> PollingService.makePolling(
+ () -> {
+ probingClient.getStatisticsHealthStatus();
+ return httpCallExecutor.getResponseStatus();
+ },
+ HttpStatus::is2xxSuccessful,
+ "Il ms " + ms + " dovrebbe risultare attivo",
+ maxTry,
+ sleepMs
+ );
+
+ default -> throw new IllegalArgumentException("Il microservizio " + ms + " non esiste");
+ }
+ }
+
+ @When("recupero la lista dei producers con limit {string} e offset {string} e producerName {string}")
+ public void getProducersWithProducerName(String limit, String offset, String producerName) {
+ Integer limitValue = nullableInteger(limit);
+ Integer offsetValue = nullableInteger(offset);
+ String producerTarget = StepParser.nullOrValue(producerName);
+
+ try {
+ List producers =
+ probingClient.getEservicesProducers(limitValue, offsetValue, producerTarget);
+
+ Assertions.assertThat(producers)
+ .as("La lista dei producer non deve essere null")
+ .isNotNull();
+
+ if (httpCallExecutor.getResponseStatus().is2xxSuccessful() && producerTarget != null && !producers.isEmpty()) {
+
+ Assertions.assertThat(producers)
+ .as("Tutti i risultati devono avere producerName='%s'", producerTarget)
+ .allSatisfy(p ->
+ Assertions.assertThat(p.getValue())
+ .as("producerName del singolo elemento non deve essere null")
+ .isNotNull()
+ );
+
+ Assertions.assertThat(producers)
+ .as("Tutti i risultati devono matchare producerName='%s'", producerTarget)
+ .allMatch(p -> p.getValue().equals(producerTarget));
+ }
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("vengono recuperati dal catalogo gli e-service con limit {string} e offset {string} e filtri eserviceName {string}, producerName {string}, versionNumber {string}, state {string}")
+ public void getEServiceCatalogWithPaginationAndFilters(String limit, String offset, String eserviceName, String producerName, String versionNumber, String state) {
+ Integer limitValue = nullableInteger(limit);
+ Integer offsetValue = nullableInteger(offset);
+
+ String nameFilter = resolver.resolveEserviceName(eserviceName);
+ String producerFilter = resolver.resolveProducer(producerName);
+ Integer versionFilter = StepParser.nullableInteger(versionNumber);
+ List stateFilter = StepParser.singletonListNullable(StepParser.nullOrValue(state), EserviceStateFE::fromValue);
+
+ SearchEserviceResponse response;
+
+ try {
+ response = probingClient.searchEservices(
+ limitValue,
+ offsetValue,
+ nameFilter,
+ producerFilter,
+ versionFilter,
+ stateFilter
+ );
+
+ Assertions.assertThat(response).as("La response non deve essere null").isNotNull();
+ ProbingUtils.EserviceFilters appliedFilters = new ProbingUtils.EserviceFilters(nameFilter, producerFilter, versionFilter, stateFilter);
+ assertResultsMatchFilters(response, appliedFilters);
+
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("viene modificato lo stato di probing dell'e-service con id {string} e id versione {string} in {string}")
+ @When("viene modificato lo stato di probing dell'e-service con id {string} e id versione {string} in {string} e si verifica che coincida con quanto atteso")
+ public void setProbingState(String eserviceId, String versionId, String probingEnabled) {
+ UUID eserviceUuid = resolver.resolveEserviceId(eserviceId);
+ UUID versionUuid = resolver.resolveVersionId(versionId);
+ Long eserviceRecordId = resolver.getEserviceRecordId();
+
+ ChangeProbingStateRequest probingState = new ChangeProbingStateRequest()
+ .probingEnabled(nullableBoolean(probingEnabled));
+
+ try {
+ probingClient.updateEserviceProbingState(eserviceUuid, versionUuid, probingState);
+ httpCallExecutor.snapshot();
+
+ EserviceRow expected = probingContext.getExpectedEserviceRow();
+ expected.setProbingEnabled(Boolean.parseBoolean(probingEnabled));
+
+ PollingService.makePolling(
+ () -> probingClient.getEserviceProbingData(eserviceRecordId),
+ resp -> resp.getProbingEnabled().equals(Boolean.valueOf(probingEnabled)),
+ "Errore durante il setting di probingEnabled per l'eservice con eserviceRecordId '" + eserviceRecordId + "'",
+ 30,
+ 1_000L
+ );
+
+ httpCallExecutor.resetFormSnapshot();
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("viene modificato lo stato operativo dell'e-service con id {string} e id versione {string} in {string}")
+ @When("viene modificato lo stato operativo dell'e-service con id {string} e id versione {string} in {string} e si verifica che coincida con quanto atteso")
+ public void setOperationalState(String eserviceId, String versionId, String eserviceState) {
+ UUID eserviceUuid = resolver.resolveEserviceId(eserviceId);
+ UUID versionUuid = resolver.resolveVersionId(versionId);
+ Long eserviceRecordId = resolver.getEserviceRecordId();
+ EserviceStateBE stateBE = resolver.resolveEserviceStateBE(eserviceState);
+
+ ChangeEserviceStateRequest operationalState = new ChangeEserviceStateRequest()
+ .eServiceState(parseNullableSafe(eserviceState, EserviceStateBE::fromValue));
+
+ try {
+ probingClient.updateEserviceState(eserviceUuid, versionUuid, operationalState);
+ EserviceRow expected = probingContext.getExpectedEserviceRow();
+ expected.setState(stateBE.getValue());
+
+ httpCallExecutor.snapshot();
+
+ PollingService.makePolling(
+ () -> probingClient.getEserviceProbingData(eserviceRecordId),
+ resp -> resp.getEserviceActive().equals(stateBE.equals(EserviceStateBE.ACTIVE)),
+ "Errore durante il setting dell'eserviceState per l'eservice con eserviceRecordId '" + eserviceRecordId + "'",
+ 30,
+ 1_000L
+ );
+
+ httpCallExecutor.resetFormSnapshot();
+
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("aggiorno i parametri di probing dell'e-service con eserviceId {string} e versionId {string} impostando frequency {string}, startDate {string}, endDate {string}")
+ @When("vengono aggiornati i parametri di probing dell'e-service con eserviceId {string} e versionId {string} impostando frequency {string}, startDate {string}, endDate {string} e si verifica che coincidano con quanto atteso")
+ public void setEserviceFrequency(String eserviceId, String versionId, String frequency, String startDate, String endDate) {
+ UUID eserviceUuid = resolver.resolveEserviceId(eserviceId);
+ UUID versionUuid = resolver.resolveVersionId(versionId);
+ Integer frequencyValue = resolver.resolveFrequency(frequency);
+ OffsetTime startValue = resolver.resolvePollingStartTime(startDate);
+ OffsetTime endValue = resolver.resolvePollingEndTime(endDate);
+
+ try {
+ probingClient.updateEserviceFrequency(eserviceUuid, versionUuid, frequencyValue, startValue, endValue);
+
+ Long eserviceRecordId = resolver.getEserviceRecordId();
+ EserviceRow expected = probingContext.getExpectedEserviceRow();
+ expected.setPollingFrequency(frequencyValue);
+ httpCallExecutor.snapshot();
+
+ PollingService.makePolling(
+ () -> probingClient.getEserviceMainData(eserviceRecordId),
+ resp -> resp.getPollingFrequency() == expected.getPollingFrequency(),
+ "Errore durante il setting di probingEnabled per l'eservice con eserviceRecordId '" + eserviceRecordId + "'",
+ 30,
+ 1_000L
+ );
+
+ httpCallExecutor.resetFormSnapshot();
+
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("vengono recuperati i main data dell'e-service con eserviceRecordId {string}")
+ public void getEserviceMainData(String eserviceRecordId) {
+ Long recordId = resolver.resolveEserviceRecordId(eserviceRecordId);
+
+ try {
+ MainDataEserviceResponse response = probingClient.getEserviceMainData(recordId);
+ Assertions.assertThat(response).as("La response contenente i metadati anagrafici dell'e-service non deve essere null").isNotNull();
+
+ EserviceRow actual = probingContext.getActualEserviceRow();
+ actual.setPollingFrequency(response.getPollingFrequency());
+
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("vengono recuperati i dati di probing dell'e-service con eserviceRecordId {string}")
+ public void getEserviceProbingData(String eserviceRecordId) {
+ Long recordId = resolver.resolveEserviceRecordId(eserviceRecordId);
+
+ try {
+ ProbingDataEserviceResponse response = probingClient.getEserviceProbingData(recordId);
+ Assertions.assertThat(response).as("La response contenente i dati di probing dell'e-service non deve essere null").isNotNull();
+
+ EserviceRow actual = probingContext.getActualEserviceRow();
+ actual.setProbingEnabled(response.getProbingEnabled());
+ actual.setState(response.getState().getValue());
+
+ probingContext.setLastResponseTime(response.getResponseReceived() != null ? OffsetDateTime.parse(response.getResponseReceived()) : null);
+
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("viene recuperata la telemetria pubblica dell'e-service con eserviceRecordId {string} e pollingFrequency {string}")
+ public void getEservicePublicTelemetry(String eserviceRecordId, String pollingFrequency) {
+ Long recordIdValue = resolver.resolveEserviceRecordId(eserviceRecordId);
+ Integer poolingFrequencyValue = resolver.resolveFrequency(pollingFrequency);
+
+ try {
+ TelemetryDataEserviceResponse response = probingClient.statisticsEservices(recordIdValue, poolingFrequencyValue);
+ Assertions.assertThat(response).as("La response contenente la telemetria pubblica dell'e-service non deve essere null").isNotNull();
+
+ if (httpCallExecutor.getResponseStatus().is2xxSuccessful()) {
+ probingContext.getActualTelemetry().add(response);
+ }
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @When("viene recuperata la telemetria dell'e-service con eserviceRecordId {string} e impostando pollingFrequency {string} , startDate {string} , endDate {string}")
+ public void getEserviceTelemetry(String eserviceRecordId, String pollingFrequency, String startDate, String endDate) {
+ Long recordIdValue = resolver.resolveEserviceRecordId(eserviceRecordId);
+ Integer poolingFrequencyValue = resolver.resolveFrequency(pollingFrequency);
+ OffsetDateTime startDateValue = dateTimeOrNull(startDate);
+ OffsetDateTime endDateValue = dateTimeOrNull(endDate);
+
+ try {
+ TelemetryDataEserviceResponse response = probingClient.filteredStatisticsEservices(recordIdValue, poolingFrequencyValue, startDateValue, endDateValue);
+ Assertions.assertThat(response).as("La response contenente la telemetria dell'e-service non deve essere null").isNotNull();
+
+ probingContext.getActualTelemetry().add(response);
+ } catch (IllegalStateException e) {
+ log.warn(e.getMessage());
+ }
+ }
+
+ @Given("vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo")
+ public void getEserviceRow() {
+ EserviceRow eserviceRow = EserviceRow.atIndex(probingContext.getThreadNumber());
+ probingContext.setActualEserviceRow(eserviceRow);
+ probingContext.setExpectedEserviceRow(eserviceRow);
+ }
+
+ @Given("vengono calcolate le informazioni di probing relative ad un e-service con health check {word} presente a catalogo")
+ public void getEserviceRowWithOutcome(String outcomeStr) {
+ EserviceRow.Outcome outcome = EserviceRow.Outcome.valueOf(outcomeStr.toUpperCase());
+
+ int okCount = ProbingContext.ESERVICE_OK_COUNT;
+ int errorCount = ProbingContext.ESERVICE_KO_COUNT;
+ int randomCount = ProbingContext.ESERVICE_RANDOM_COUNT;
+
+ EserviceRow row = EserviceRow.pickByOutcome(outcome, okCount, errorCount, randomCount);
+
+ probingContext.setActualEserviceRow(row);
+ probingContext.setExpectedEserviceRow(row);
+ }
+
+ @Then("verifica che la responseReceived sia aggiornata coerentemente rispetto la frequency {string}, clockScheduler {string}, startDate {string}, endDate {string}")
+ public void assertScheduler(String pollingFrequency, String clockScheduler, String startDate, String endDate) throws Exception {
+
+ // --- Inputs
+ Duration tick = resolver.resolveSchedulerInterval(clockScheduler);
+ Assertions.assertThat(tick)
+ .as("clockScheduler deve essere valorizzato e > 0")
+ .isNotNull()
+ .isGreaterThan(Duration.ZERO);
+
+ int freqMinutes = resolver.resolveFrequency(pollingFrequency);
+ Assertions.assertThat(freqMinutes)
+ .as("pollingFrequency (minutes) deve essere >= 1")
+ .isGreaterThanOrEqualTo(1);
+
+ OffsetTime start = resolver.resolvePollingStartTime(startDate);
+ OffsetTime end = resolver.resolvePollingEndTime(endDate);
+
+ Assertions.assertThat(start).as("startDate non deve essere null").isNotNull();
+ Assertions.assertThat(end).as("endDate non deve essere null").isNotNull();
+ Assertions.assertThat(end).as("endDate deve essere dopo startDate").isAfter(start);
+
+ // --- Instants
+ LocalDate today = LocalDate.now();
+ Instant startI = start.atDate(today).toInstant();
+ Instant endI = end.atDate(today).toInstant();
+
+ // --- Policy (solo tick + jitter)
+ Duration jitter = Duration.ofSeconds(20);
+ Duration unit = tick.plus(jitter);
+ Duration notAdvancingTolerance = Duration.ofSeconds(2);
+ Duration boundaryBuffer = Duration.ofSeconds(1);
+
+ Instant now = Instant.now();
+
+ // --- AFTER: fuori finestra
+ if (!now.isBefore(endI)) {
+ assertNotAdvancing(unit, notAdvancingTolerance,
+ "Il probing sta avanzando DOPO la finestra attesa");
+ return;
+ }
+
+ // --- BEFORE: fuori finestra (non deve avanzare fino allo start)
+ if (now.isBefore(startI)) {
+ Duration untilStart = Duration.between(now, startI);
+ Duration observe = min(unit, untilStart.minus(boundaryBuffer));
+
+ if (!observe.isNegative() && !observe.isZero()) {
+ assertNotAdvancing(observe, notAdvancingTolerance,
+ "Il probing sta avanzando PRIMA della finestra attesa");
+ }
+
+ // best effort: prova a entrare in finestra al massimo entro 1 unit
+ waitUntil(startI, unit);
+
+ now = Instant.now();
+ if (!now.isBefore(endI)) {
+ // finestra scaduta durante l'attesa
+ return;
+ }
+ if (now.isBefore(startI)) {
+ // non siamo entrati (attesa cappata): stop qui
+ return;
+ }
+ }
+
+ // --- INSIDE: siamo in finestra
+ now = Instant.now();
+ if (now.isBefore(endI)) {
+ verifyInsideWindow(endI, now, unit, notAdvancingTolerance, tick, jitter);
+
+ // --- STOP: aspetta end (max 1 unit) e poi non deve avanzare per 1 unit
+ waitUntil(endI, unit);
+ // assorbe eventuale update tardivo, poi verifica stabilità
+ assertStopsAfterEnd(unit, notAdvancingTolerance,
+ "Il probing non si è fermato stabilmente dopo endDate");
+ }
+ }
+
+ private void verifyInsideWindow(
+ Instant endI,
+ Instant now,
+ Duration unit,
+ Duration notAdvancingTolerance,
+ Duration tick,
+ Duration jitter
+ ) throws Exception {
+
+ Duration untilEnd = Duration.between(now, endI);
+ if (untilEnd.isNegative() || untilEnd.isZero()) return;
+
+ // Se manca meno di 1 unit alla fine finestra, non ha senso pretendere advancing: best effort
+ if (untilEnd.compareTo(unit) < 0) {
+ observeAndValidateInside(endI, untilEnd.plusSeconds(2), notAdvancingTolerance,
+ "Dentro finestra ma troppo vicini alla endDate per pretendere un update (unit=" + unit + ")");
+ return;
+ }
+
+ // Altrimenti: mi aspetto almeno 1 update entro 1 unit (tick + jitter)
+ assertAdvancingWithin(unit,
+ "Il probing non sta avanzando durante la finestra (atteso >=1 update entro 1 unit=" + unit +
+ ", tick=" + tick + ", jitter=" + jitter + ")");
+ }
+
+
+ @Then("verifica che la responseReceived NON sia aggiornata quando probing è disabilitato")
+ public void assertSchedulerWhenProbingDisabled() throws Exception {
+ Duration observe = Duration.ofSeconds(30);
+ Duration tolerance = Duration.ofSeconds(2);
+
+ assertNotAdvancing(observe, tolerance, "Il probing sta aggiornando anche se probingEnabled=false");
+ }
+
+ @And("la telemetria dell'e-service risulta aggiornata con successo")
+ public void assertTelemetry() {
+ EserviceRow row = probingContext.getActualEserviceRow();
+ List actualTelemetry = probingContext.getActualTelemetry();
+
+ Assertions.assertThat(actualTelemetry)
+ .as("La telemetria dell'e-service non deve essere vuota")
+ .isNotEmpty()
+ .allSatisfy(t -> Assertions.assertThat(t).isNotNull());
+
+ for (TelemetryDataEserviceResponse t : actualTelemetry) {
+
+ Assertions.assertThat(t.getPerformances()).as("performances non deve essere null").isNotNull();
+ Assertions.assertThat(t.getFailures()).as("failures non deve essere null").isNotNull();
+ Assertions.assertThat(t.getPercentages()).as("percentages non deve essere null").isNotNull();
+
+ boolean hasKoFailure = t.getFailures().stream()
+ .anyMatch(f -> f != null && "KO".equalsIgnoreCase(String.valueOf(f.getStatus())));
+
+ boolean hasOkPercentage = t.getPercentages().stream()
+ .anyMatch(p -> p != null && "OK".equalsIgnoreCase(String.valueOf(p.getStatus())));
+
+ // opzionale ma utile: performance “sensate”
+ boolean hasAnyPerformance = !t.getPerformances().isEmpty();
+ boolean hasNonNegativeResponseTime = t.getPerformances().stream()
+ .filter(Objects::nonNull)
+ .allMatch(p -> p.getResponseTime() >= 0);
+
+ Assertions.assertThat(hasNonNegativeResponseTime)
+ .as("responseTime deve essere >= 0 quando presente")
+ .isTrue();
+
+ // Pattern "tutto OK"
+ boolean looksLikeOk = !hasKoFailure && hasOkPercentage && hasAnyPerformance;
+
+ // Pattern "tutto KO" (in questo caso non richiedo per forza percentages KO, perché non hai mostrato payload KO completo)
+ boolean looksLikeKo = hasKoFailure && !hasOkPercentage;
+
+ switch (row.getOutcome()) {
+ case OK -> {
+ Assertions.assertThat(looksLikeOk)
+ .as("Outcome OK: mi aspetto un pattern coerente di successo (no KO failures + OK percentages + performances)")
+ .isTrue();
+ }
+ case ERROR -> {
+ Assertions.assertThat(looksLikeKo)
+ .as("Outcome ERROR: mi aspetto un pattern coerente di KO (KO failures + no OK percentages)")
+ .isTrue();
+ }
+ case RANDOM -> {
+ boolean mixed = hasKoFailure && hasOkPercentage;
+
+ Assertions.assertThat(looksLikeOk || looksLikeKo || mixed)
+ .as("Outcome RANDOM: accetto pattern OK, KO oppure misto (potrei essere 'sfortunato' e beccare tutto OK o tutto KO)")
+ .isTrue();
+
+ Assertions.assertThat(hasAnyPerformance || !t.getFailures().isEmpty() || !t.getPercentages().isEmpty())
+ .as("Outcome RANDOM: mi aspetto almeno un contenuto tra performances/failures/percentages")
+ .isTrue();
+ }
+ default -> throw new IllegalStateException("Outcome non gestito: " + row.getOutcome());
+ }
+ }
+ }
+
+ @And("lo stato di probing dell'e-service viene aggiornato con valore {string}")
+ public void assertEserviceState(String eserviceState) {
+ EserviceStateBE stateBE = resolver.resolveEserviceStateBE(eserviceState);
+ EserviceRow actual = probingContext.getActualEserviceRow();
+
+ Assertions.assertThat(actual.getState())
+ .as("Lo stato operativo dell'e-service non coincide con quello atteso")
+ .isEqualTo(stateBE.getValue());
+ }
+
+ private void assertNotAdvancing(Duration observe, Duration tolerance, String message) throws Exception {
+ Instant t1 = readLastResponseTime();
+ TimeUnit.MILLISECONDS.sleep(observe.toMillis());
+ Instant t2 = readLastResponseTime();
+
+ long deltaMillis = t2.toEpochMilli() - t1.toEpochMilli(); // niente abs
+
+ Assertions.assertThat(deltaMillis)
+ .as(message + " (delta=" + deltaMillis + "ms, tolerance=" + tolerance.toMillis() + "ms)")
+ .isBetween(0L, tolerance.toMillis()); // non deve diminuire, né avanzare troppo
+ }
+
+ private void assertAdvancingWithin(Duration observe, String message) throws Exception {
+ Instant t1 = readLastResponseTime();
+
+ long deadlineNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(observe.toMillis());
+ long stepMillis = 1000L; // 1s step: frequency è in minuti, non serve più fitto
+
+ while (System.nanoTime() < deadlineNanos) {
+ TimeUnit.MILLISECONDS.sleep(stepMillis);
+ Instant t2 = readLastResponseTime();
+ if (t2.isAfter(t1)) {
+ return;
+ }
+ }
+
+ Instant tFinal = readLastResponseTime();
+ long deltaMillis = tFinal.toEpochMilli() - t1.toEpochMilli();
+ Assertions.fail(message + " (delta=" + deltaMillis + "ms, observe=" + observe.toMillis() + "ms)");
+ }
+
+ private void observeAndValidateInside(Instant endI, Duration observe, Duration tolerance, String message) throws Exception {
+ Instant baseline = readLastResponseTime();
+
+ long deadlineNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(observe.toMillis());
+ long stepMillis = 1000L;
+
+ while (System.nanoTime() < deadlineNanos) {
+ TimeUnit.MILLISECONDS.sleep(stepMillis);
+ Instant current = readLastResponseTime();
+
+ if (current.isAfter(baseline)) {
+ // Se è avanzato, deve comunque non superare end (+ tolleranza)
+ Instant endPlusTol = endI.plusMillis(tolerance.toMillis());
+ Assertions.assertThat(current)
+ .as(message + " (lastResponseTime avanzato ma oltre endDate)")
+ .isBeforeOrEqualTo(endPlusTol);
+
+ baseline = current; // aggiorno baseline e continuo a osservare
+ }
+ }
+ }
+
+ private void assertStopsAfterEnd(Duration unit, Duration tolerance, String message) throws Exception {
+ // Fase 1: assorbi un eventuale update tardivo (1 unit)
+ TimeUnit.MILLISECONDS.sleep(unit.toMillis());
+
+ // Fase 2: ora deve essere stabile per 1 unit
+ assertNotAdvancing(unit, tolerance, message);
+ }
+
+
+ private void waitUntil(Instant target, Duration maxWait) throws Exception {
+ Instant now = Instant.now();
+ if (!now.isBefore(target)) return;
+
+ long msToWait = target.toEpochMilli() - now.toEpochMilli();
+ long capped = Math.min(msToWait, maxWait.toMillis());
+ if (capped > 0) TimeUnit.MILLISECONDS.sleep(capped);
+ }
+
+ private Duration min(Duration a, Duration b) {
+ return a.compareTo(b) <= 0 ? a : b;
+ }
+
+ private Instant readLastResponseTime() {
+ Long eserviceRecordId = resolver.getEserviceRecordId();
+ this.getEserviceMainData(String.valueOf(eserviceRecordId));
+ this.getEserviceProbingData(String.valueOf(eserviceRecordId));
+
+ return probingContext.getLastResponseTime().toInstant();
+ }
+
+ private void assertResultsMatchFilters(SearchEserviceResponse response, ProbingUtils.EserviceFilters filters) {
+ List items = response.getContent();
+ if (items == null || items.isEmpty()) return; // niente da validare
+
+ // Se tutti i filtri sono null, non serve validare
+ if (filters.eserviceName() == null && isNullOrBlank(filters.producerName())
+ && filters.versionNumber() == null && filters.states() == null) {
+ return;
+ }
+
+ for (SearchEserviceContent item : items) {
+ Assertions.assertThat(matchesAllFilters(item, filters))
+ .as("Risultato non coerente con filtri: item=%s, filters=%s", item, filters)
+ .isTrue();
+ }
+ }
+
+ private boolean isNullOrBlank(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/EserviceRow.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/EserviceRow.java
new file mode 100644
index 0000000000..7407c28c66
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/EserviceRow.java
@@ -0,0 +1,249 @@
+package it.pagopa.pn.interop.cucumber.steps.probing.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.time.LocalTime;
+import java.time.OffsetTime;
+import java.util.List;
+import java.util.UUID;
+
+import static it.pagopa.pn.interop.cucumber.steps.probing.utils.ProbingUtils.italyToday;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class EserviceRow {
+ private Long id;
+ private UUID eserviceId;
+ private UUID versionId;
+ private String eserviceName;
+ private String producerName;
+ private String eserviceTechnology; // "REST" | "SOAP"
+ private List basePath; // nello script: array con 1 stringa
+ private List audience;
+ private String state;
+ private int versionNumber;
+ private int lockVersion;
+ private boolean probingEnabled;
+ private OffsetTime pollingStartTime;
+ private OffsetTime pollingEndTime;
+ private int pollingFrequency;
+ private Outcome outcome;
+
+ public enum Outcome {OK, ERROR, RANDOM}
+
+ // ------------------------
+ // Costanti default (come nello script)
+ // ------------------------
+ private static final OffsetTime POLLING_START_DEFAULT = italyToday(LocalTime.of(8, 0, 0));
+ private static final OffsetTime POLLING_END_DEFAULT = italyToday(LocalTime.of(17, 0, 0));
+ private static final int POLLING_FREQUENCY_DEFAULT = 15;
+ private static final boolean PROBING_ENABLED_DEFAULT = false;
+
+ private static final String STATE_DEFAULT = "ACTIVE";
+ private static final int VERSION_NUMBER_DEFAULT = 1;
+ private static final int LOCK_VERSION_DEFAULT = 0;
+
+ private static final String REST_PREFIX = "/rest/interop/probing/";
+ private static final String SOAP_PREFIX = "/soap/interop/probing/";
+
+ private static final String DEFAULT_BASE_HOST = "http://probing-be-eservice-mock.qa:8080";
+
+ // ------------------------
+ // Convenience: stato endpoint
+ // ------------------------
+ public boolean isOk() {
+ return outcome == Outcome.OK;
+ }
+
+ public boolean isRandom() {
+ return outcome == Outcome.RANDOM;
+ }
+
+ public boolean isKo() {
+ return outcome == Outcome.ERROR;
+ } // KO = ERROR
+
+ // ------------------------
+ // Factory methods
+ // ------------------------
+
+ /**
+ * Come nello script: outcome deterministico in base a i (gs) e ai count.
+ */
+ public static EserviceRow atIndex(long i, int okCount, int errorCount, int randomCount, String baseHost) {
+ if (i <= 0) throw new IllegalArgumentException("Index i must be >= 1");
+ int total = okCount + errorCount + randomCount;
+ if (total <= 0) throw new IllegalArgumentException("Total eservices count must be > 0");
+ if (i > total) throw new IllegalArgumentException("Index i must be <= total (" + total + ")");
+
+ String resolvedHost = (baseHost == null || baseHost.isBlank()) ? DEFAULT_BASE_HOST : baseHost;
+
+ UUID eserviceId = uuidFromLongSeed(i);
+ UUID versionId = uuidFromLongSeed(1_000_000_000L + i);
+
+ String eserviceName = nameFromIndex(i);
+ String producerName = "Producer " + (((i - 1) % 50) + 1);
+
+ // Script: CASE WHEN (gs % 2) = 0 THEN 'SOAP' ELSE 'REST'
+ boolean isEven = (i % 2) == 0;
+ String technology = isEven ? "SOAP" : "REST";
+
+ Outcome outcome = outcomeForIndex(i, okCount, errorCount);
+
+ // Script: v_base_host + (/soap|/rest) + (ok|error|random) + '/status'
+ String endpointPrefix = isEven ? SOAP_PREFIX : REST_PREFIX;
+ String outcomePath = switch (outcome) {
+ case OK -> "ok";
+ case ERROR -> "error";
+ case RANDOM -> "random";
+ };
+
+ List basePath = List.of(
+ resolvedHost + endpointPrefix + outcomePath + "/status"
+ );
+
+ List audience = List.of(
+ "AUD_" + (((i - 1) % 20) + 1)
+ );
+
+ return new EserviceRow(
+ i,
+ eserviceId,
+ versionId,
+ eserviceName,
+ producerName,
+ technology,
+ basePath,
+ audience,
+ STATE_DEFAULT,
+ VERSION_NUMBER_DEFAULT,
+ LOCK_VERSION_DEFAULT,
+ PROBING_ENABLED_DEFAULT,
+ POLLING_START_DEFAULT,
+ POLLING_END_DEFAULT,
+ POLLING_FREQUENCY_DEFAULT,
+ outcome
+ );
+ }
+
+ /**
+ * Restituisce un EserviceRow coerente con lo script dato l'Outcome richiesto.
+ * Usa il primo indice valido per quell'outcome.
+ */
+ public static EserviceRow pickByOutcome(
+ Outcome outcome,
+ int okCount,
+ int errorCount,
+ int randomCount
+ ) {
+ int total = okCount + errorCount + randomCount;
+ if (total <= 0) {
+ throw new IllegalArgumentException("Total eservices count must be > 0");
+ }
+
+ long index;
+
+ switch (outcome) {
+ case OK -> {
+ if (okCount <= 0) {
+ throw new IllegalArgumentException("No OK eservices available");
+ }
+ index = 1L;
+ }
+ case ERROR -> {
+ if (errorCount <= 0) {
+ throw new IllegalArgumentException("No ERROR eservices available");
+ }
+ index = 1L + okCount;
+ }
+ case RANDOM -> {
+ if (randomCount <= 0) {
+ throw new IllegalArgumentException("No RANDOM eservices available");
+ }
+ index = 1L + okCount + errorCount;
+ }
+ default -> throw new IllegalStateException("Unsupported outcome: " + outcome);
+ }
+
+ return atIndex(index, okCount, errorCount, randomCount, DEFAULT_BASE_HOST);
+ }
+
+
+ /**
+ * Overload "comodo": usa i default dello script per host e count.
+ * (Se vuoi, puoi anche rimuoverlo e forzare sempre i parametri.)
+ */
+ public static EserviceRow atIndex(long i) {
+ // default come tuo script di esempio: ok=1, error=0, random=1
+ return atIndex(i, 1, 0, 1, DEFAULT_BASE_HOST);
+ }
+
+ public static EserviceRow fromName(String eserviceName, int okCount, int errorCount, int randomCount, String baseHost) {
+ return atIndex(indexFromName(eserviceName), okCount, errorCount, randomCount, baseHost);
+ }
+
+ public static EserviceRow fromName(String eserviceName) {
+ return atIndex(indexFromName(eserviceName));
+ }
+
+ // ------------------------
+ // Utilities pubbliche
+ // ------------------------
+
+ public static String nameFromIndex(long i) {
+ if (i <= 0) throw new IllegalArgumentException("Index i must be >= 1");
+ return "ESVC-" + String.format("%08d", i);
+ }
+
+ public static int indexFromName(String eserviceName) {
+ if (eserviceName == null) throw new IllegalArgumentException("Name is null");
+ if (!eserviceName.matches("^ESVC-\\d{8}$")) {
+ throw new IllegalArgumentException("Invalid name format. Expected ESVC-XXXXXXXX (8 digits).");
+ }
+ return Integer.parseInt(eserviceName.substring(5));
+ }
+
+ /**
+ * Replica della CASE dello script:
+ * WHEN gs <= okCount THEN ok
+ * WHEN gs <= okCount + errorCount THEN error
+ * ELSE random
+ */
+ public static Outcome outcomeForIndex(long i, int okCount, int errorCount) {
+ if (i <= okCount) return Outcome.OK;
+ if (i <= (long) okCount + errorCount) return Outcome.ERROR;
+ return Outcome.RANDOM;
+ }
+
+ // ------------------------
+ // Replica di uuid_from_int(seed)
+ // ------------------------
+
+ private static UUID uuidFromLongSeed(long seed) {
+ String hex32 = md5Hex(Long.toString(seed)); // md5(seed::text) su Postgres
+ String uuidStr =
+ hex32.substring(0, 8) + "-" +
+ hex32.substring(8, 12) + "-" +
+ hex32.substring(12, 16) + "-" +
+ hex32.substring(16, 20) + "-" +
+ hex32.substring(20, 32);
+ return UUID.fromString(uuidStr);
+ }
+
+ private static String md5Hex(String s) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ byte[] digest = md.digest(s.getBytes(StandardCharsets.UTF_8));
+ StringBuilder sb = new StringBuilder(32);
+ for (byte b : digest) sb.append(String.format("%02x", b));
+ return sb.toString();
+ } catch (Exception e) {
+ throw new RuntimeException("MD5 not available", e);
+ }
+ }
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/ProbingContext.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/ProbingContext.java
new file mode 100644
index 0000000000..c9fc7f8bef
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/ProbingContext.java
@@ -0,0 +1,42 @@
+package it.pagopa.pn.interop.cucumber.steps.probing.model;
+
+import it.pagopa.interop.generated.openapi.clients.probing.model.SearchEserviceContent;
+import it.pagopa.interop.generated.openapi.clients.probingStatistics.model.TelemetryDataEserviceResponse;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Getter
+@Setter
+public class ProbingContext{
+ public static int ESERVICE_OK_COUNT = 10_000;
+ public static int ESERVICE_KO_COUNT = 5_000;
+ public static int ESERVICE_RANDOM_COUNT = 5_000;
+ public static int ESERVICE_SIZE = ESERVICE_OK_COUNT + ESERVICE_KO_COUNT + ESERVICE_RANDOM_COUNT;
+
+ public static int SCHEDULER_INTERVAL = 3;
+
+ final Integer threadNumber;
+ private static final AtomicInteger COUNTER = new AtomicInteger(0);
+
+ private List actualResults;
+ private EserviceRow actualEserviceRow;
+ private EserviceRow expectedEserviceRow;
+ private OffsetDateTime lastResponseTime;
+ private List actualTelemetry = new ArrayList<>();
+ private List expectedTelemetry = new ArrayList<>();
+
+ public ProbingContext() {
+ this.threadNumber = nextEserviceIndex();
+ }
+
+ private static int nextEserviceIndex() {
+ return COUNTER.updateAndGet(current ->
+ current >= ESERVICE_SIZE ? 1 : current + 1
+ );
+ }
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/ProbingResponse.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/ProbingResponse.java
new file mode 100644
index 0000000000..280a5ccd0b
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/model/ProbingResponse.java
@@ -0,0 +1,15 @@
+package it.pagopa.pn.interop.cucumber.steps.probing.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class ProbingResponse {
+ private Integer probingFrequency;
+ private LocalDateTime lastResponseTime;
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/utils/ProbingResolver.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/utils/ProbingResolver.java
new file mode 100644
index 0000000000..71bc1b4c0a
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/utils/ProbingResolver.java
@@ -0,0 +1,171 @@
+package it.pagopa.pn.interop.cucumber.steps.probing.utils;
+
+import it.pagopa.interop.generated.openapi.clients.probing.model.EserviceStateBE;
+import it.pagopa.pn.interop.cucumber.steps.m2m.common.utils.AbstractResolver;
+import it.pagopa.pn.interop.cucumber.steps.probing.model.ProbingContext;
+import it.pagopa.pn.interop.cucumber.utility.StepParser;
+import lombok.RequiredArgsConstructor;
+
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.util.UUID;
+
+@RequiredArgsConstructor
+public class ProbingResolver extends AbstractResolver {
+
+ private final ProbingContext probingContext;
+
+ public UUID resolveEserviceId(String raw) {
+ return resolveOrParse(
+ raw,
+ StepParser::uuidOrRandomOrNull,
+ () -> probingContext.getActualEserviceRow().getEserviceId(),
+ () -> probingContext.getExpectedEserviceRow().getEserviceId(),
+ UUID::randomUUID,
+ null
+ );
+ }
+
+ public UUID resolveVersionId(String raw) {
+ return resolveOrParse(
+ raw,
+ StepParser::uuidOrRandomOrNull,
+ () -> probingContext.getActualEserviceRow().getVersionId(),
+ () -> probingContext.getExpectedEserviceRow().getVersionId(),
+ UUID::randomUUID,
+ null
+ );
+ }
+
+ public Long resolveEserviceRecordId(String raw) {
+ return resolveOrParse(
+ raw,
+ StepParser::longOrRandomOrNull,
+ this::getEserviceRecordId,
+ this::getEserviceRecordId,
+ () -> 1L + (long) (Math.random() * Long.MAX_VALUE),
+ null
+ );
+ }
+
+ public String resolveEserviceName(String raw) {
+ return resolveOrParse(
+ raw,
+ v -> v, // non token: ritorna raw
+ () -> probingContext.getActualEserviceRow().getEserviceName(),
+ () -> probingContext.getExpectedEserviceRow().getEserviceName(),
+ null,
+ () -> ""
+ );
+ }
+
+ public String resolveProducer(String raw) {
+ return resolveOrParse(
+ raw,
+ v -> v,
+ () -> probingContext.getActualEserviceRow().getProducerName(),
+ () -> probingContext.getExpectedEserviceRow().getProducerName(),
+ null,
+ () -> ""
+ );
+ }
+
+ public EserviceStateBE resolveEserviceStateBE(String raw) {
+ return resolveOrParse(
+ raw,
+ v -> v == null ? null : EserviceStateBE.fromValue(v),
+ () -> EserviceStateBE.fromValue(probingContext.getActualEserviceRow().getState()),
+ () -> EserviceStateBE.fromValue(probingContext.getExpectedEserviceRow().getState()),
+ null,
+ null
+ );
+ }
+
+ public Long getEserviceRecordId() {
+ return probingContext.getActualEserviceRow().getId();
+ }
+
+ public Integer resolveFrequency(String raw) {
+ if (raw == null) return null;
+
+ // 1) delta (+N / -N)
+ int delta = resolveIntegerDelta(raw);
+
+ // 2) parte base (prima di + / -) -> se non c'è operatore resta tutta la stringa
+ String basePart = raw;
+ int plusIdx = raw.indexOf('+');
+ int minusIdx = raw.indexOf('-', 1); // evita il "-" iniziale tipo "-1"
+ int opIdx = plusIdx >= 0 ? plusIdx : minusIdx;
+ if (opIdx >= 0) {
+ basePart = raw.substring(0, opIdx).trim();
+ }
+
+ // 3) risolvi base: token -> actual/expected/random/null, altrimenti parse int
+ Integer baseValue = resolveOrParse(
+ basePart,
+ StepParser::intOrRandomOrNull, // NON token
+ () -> probingContext.getActualEserviceRow().getPollingFrequency(),
+ () -> probingContext.getExpectedEserviceRow().getPollingFrequency(),
+ ProbingResolver::randomPositiveInt,
+ null
+ );
+
+ // 4) applica delta
+ if (baseValue == null) return null;
+ return baseValue + delta;
+ }
+
+ public Duration resolveSchedulerInterval(String row) {
+ String normalizedRow = StepParser.normalize(row);
+ return Duration.ofMinutes(normalizedRow == null ? ProbingContext.SCHEDULER_INTERVAL : Integer.parseInt(row));
+ }
+
+ private static int randomPositiveInt() {
+ return 1 + (int) (Math.random() * Integer.MAX_VALUE);
+ }
+
+ public OffsetTime resolvePollingStartTime(String raw) {
+ return resolveOrParse(
+ raw,
+ v -> {
+ OffsetDateTime dt = StepParser.dateTimeOrNull(v);
+ return dt == null ? null : ProbingUtils.italyToday(dt.toLocalTime());
+ },
+ () -> probingContext.getActualEserviceRow().getPollingStartTime(),
+ () -> probingContext.getExpectedEserviceRow().getPollingStartTime()
+ );
+ }
+
+ public OffsetTime resolvePollingEndTime(String raw) {
+ return resolveOrParse(
+ raw,
+ v -> {
+ OffsetDateTime dt = StepParser.dateTimeOrNull(v);
+ return dt == null ? null : ProbingUtils.italyToday(dt.toLocalTime());
+ },
+ () -> probingContext.getActualEserviceRow().getPollingEndTime(),
+ () -> probingContext.getExpectedEserviceRow().getPollingEndTime()
+ );
+ }
+
+
+ private Integer resolveIntegerDelta(String raw) {
+ if (raw == null) return 0;
+
+ String token = raw.trim();
+
+ int plusIdx = token.indexOf('+');
+ int minusIdx = token.indexOf('-', 1);
+
+ if (plusIdx > -1) {
+ return Integer.parseInt(token.substring(plusIdx + 1).trim());
+ }
+
+ if (minusIdx > -1) {
+ return -Integer.parseInt(token.substring(minusIdx + 1).trim());
+ }
+
+ return 0;
+ }
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/utils/ProbingUtils.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/utils/ProbingUtils.java
new file mode 100644
index 0000000000..ccb3d0f6ed
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/probing/utils/ProbingUtils.java
@@ -0,0 +1,68 @@
+package it.pagopa.pn.interop.cucumber.steps.probing.utils;
+
+import it.pagopa.interop.generated.openapi.clients.probing.model.EserviceStateFE;
+import it.pagopa.interop.generated.openapi.clients.probing.model.SearchEserviceContent;
+
+import java.time.*;
+import java.util.List;
+
+public class ProbingUtils {
+
+ private static final ZoneId ROME = ZoneId.of("Europe/Rome");
+
+ public record EserviceFilters(String eserviceName, String producerName, Integer versionNumber,
+ List states) {
+ }
+
+ public static OffsetTime italyToday(LocalTime time) {
+ // “oggi” secondo l’Italia
+ LocalDate todayRome = LocalDate.now(ROME);
+
+ // offset corretto per oggi in Italia (CET/CEST)
+ ZoneOffset offset = ZonedDateTime.of(todayRome, time, ROME).getOffset();
+
+ return OffsetTime.of(time.withNano(0), offset);
+ }
+
+ public static boolean matchesAllFilters(SearchEserviceContent item, EserviceFilters f) {
+ if (item == null) return false;
+
+ if (f.eserviceName() != null) {
+ String actual = safeTrim(item.getEserviceName());
+ String expected = safeTrim(f.eserviceName());
+ if (!containsIgnoreCase(actual, expected)) return false;
+ }
+
+ if (f.producerName() != null) {
+ String actual = safeTrim(item.getProducerName());
+ String expected = safeTrim(f.producerName());
+ if (!equalsIgnoreCase(actual, expected)) return false;
+ }
+
+ if (f.versionNumber() != null) {
+ Integer actual = item.getVersionNumber();
+ if (actual == null || !actual.equals(f.versionNumber())) return false;
+ }
+
+ if (f.states() != null && !f.states().isEmpty()) {
+ EserviceStateFE actual = item.getState();
+ return actual != null && f.states().contains(actual);
+ }
+
+ return true;
+ }
+
+ private static String safeTrim(String s) {
+ return s == null ? null : s.trim();
+ }
+
+ private static boolean equalsIgnoreCase(String a, String b) {
+ if (a == null || b == null) return false;
+ return a.equalsIgnoreCase(b);
+ }
+
+ private static boolean containsIgnoreCase(String a, String b) {
+ if (a == null || b == null) return false;
+ return a.toLowerCase().contains(b.toLowerCase());
+ }
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purpose/PurposeRiskAnalysisDocumentDownloadSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purpose/PurposeRiskAnalysisDocumentDownloadSteps.java
index 20bac0c594..2b6667673b 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purpose/PurposeRiskAnalysisDocumentDownloadSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purpose/PurposeRiskAnalysisDocumentDownloadSteps.java
@@ -10,10 +10,9 @@
import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeVersionDocument;
import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeVersionSeed;
import it.pagopa.interop.purpose.domain.CreatedEserviceVersion;
-import it.pagopa.interop.utils.HttpCallExecutor;
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
-import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
+import it.pagopa.pn.interop.cucumber.steps.datapreparationservice.BFFDataPreparationService;
import it.pagopa.pn.interop.cucumber.utility.CommonUtils;
import org.junit.jupiter.api.Assertions;
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purposetemplate/PurposeTemplateSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purposetemplate/PurposeTemplateSteps.java
index 30f738e5e2..be8453d5de 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purposetemplate/PurposeTemplateSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/purposetemplate/PurposeTemplateSteps.java
@@ -1,53 +1,16 @@
package it.pagopa.pn.interop.cucumber.steps.purposetemplate;
-import static org.assertj.core.api.Assertions.assertThat;
-
import io.cucumber.java.en.And;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import it.pagopa.interop.authorization.service.utils.PollingService;
import it.pagopa.interop.common.IHttpExecutor;
-import it.pagopa.interop.generated.openapi.clients.bff.model.Agreement;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CatalogPurposeTemplates;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CompactPurposeTemplateEService;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CreatedResource;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CreatorPurposeTemplate;
-import it.pagopa.interop.generated.openapi.clients.bff.model.CreatorPurposeTemplates;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceDescriptorPurposeTemplateWithCompactEServiceAndDescriptor;
-import it.pagopa.interop.generated.openapi.clients.bff.model.EServiceDescriptorsPurposeTemplate;
-import it.pagopa.interop.generated.openapi.clients.bff.model.InlineObject2;
-import it.pagopa.interop.generated.openapi.clients.bff.model.InlineObject3;
-import it.pagopa.interop.generated.openapi.clients.bff.model.PatchPurposeUpdateFromTemplateContent;
-import it.pagopa.interop.generated.openapi.clients.bff.model.Purpose;
-import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeFromTemplateSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeTemplate;
-import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeTemplateSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeTemplateState;
-import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeTemplateWithCompactCreator;
-import it.pagopa.interop.generated.openapi.clients.bff.model.PurposeVersionState;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisFormSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisFormTemplate;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisFormTemplateSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisTemplateAnswer;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisTemplateAnswerAnnotation;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisTemplateAnswerAnnotationDocument;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisTemplateAnswerAnnotationSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisTemplateAnswerRequest;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisTemplateAnswerResponse;
-import it.pagopa.interop.generated.openapi.clients.bff.model.RiskAnalysisTemplateAnswerSeed;
-import it.pagopa.interop.generated.openapi.clients.bff.model.TargetTenantKind;
+import it.pagopa.interop.generated.openapi.clients.bff.model.*;
import it.pagopa.interop.purpose.service.IPurposeApiClient;
import it.pagopa.interop.purpose.service.IPurposeTemplateClient;
import it.pagopa.pn.interop.cucumber.steps.ClientTokenConfigurator;
import it.pagopa.pn.interop.cucumber.steps.SharedStepsContext;
import it.pagopa.pn.interop.cucumber.utility.BlobFileCreator;
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.stream.Stream;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@@ -55,6 +18,12 @@
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
+import java.io.File;
+import java.util.*;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
@Slf4j
public class PurposeTemplateSteps {
@@ -360,10 +329,10 @@ public void linkPurposeTemplateToEservice(boolean exists) {
UUID eServiceId = sharedStepsContext.getEServicesCommonContext().getEserviceId();
UUID ptId = exists ? createdPurposeTemplate.getId() : UUID.randomUUID();
- InlineObject2 inlineObject = new InlineObject2();
- inlineObject.setEserviceId(eServiceId);
+ LinkEServiceToPurposeTemplateRequest request = new LinkEServiceToPurposeTemplateRequest();
+ request.setEserviceId(eServiceId);
- httpCallExecutor.performCall(() -> purposeTemplateClient.linkEServiceToPurposeTemplate(ptId, inlineObject));
+ httpCallExecutor.performCall(() -> purposeTemplateClient.linkEServiceToPurposeTemplate(ptId, request));
}
@Then("si effettua la get degli e-service associati al purpose template {exists}")
@@ -412,10 +381,10 @@ public void unlinkPurposeTemplateToEservice(boolean exists) {
UUID ptId = exists ? createdPurposeTemplate.getId() : UUID.randomUUID();
- InlineObject3 o3 = new InlineObject3();
- o3.setEserviceId(eServiceId);
+ LinkEServiceToPurposeTemplateRequest request = new LinkEServiceToPurposeTemplateRequest();
+ request.setEserviceId(eServiceId);
- httpCallExecutor.performCall(() -> purposeTemplateClient.unlinkEServiceToPurposeTemplate(ptId, o3));
+ httpCallExecutor.performCall(() -> purposeTemplateClient.unlinkEServiceToPurposeTemplate(ptId, request));
if (exists) {
if (httpCallExecutor.getResponseStatus().is2xxSuccessful()) {
pollingService.makePolling(
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/tracing/TracingSteps.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/tracing/TracingSteps.java
index 9d29b65c49..a8fb644cba 100644
--- a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/tracing/TracingSteps.java
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/steps/tracing/TracingSteps.java
@@ -6,12 +6,7 @@
import io.cucumber.java.en.When;
import it.pagopa.interop.authorization.service.utils.PollingService;
import it.pagopa.interop.authorization.service.utils.SettableBearerToken;
-import it.pagopa.interop.client.b2b.generated.openapi.clients.interop.tracing.model.GetTracingErrorsResponse;
-import it.pagopa.interop.client.b2b.generated.openapi.clients.interop.tracing.model.GetTracingErrorsResponseResults;
-import it.pagopa.interop.client.b2b.generated.openapi.clients.interop.tracing.model.GetTracingsResponse;
-import it.pagopa.interop.client.b2b.generated.openapi.clients.interop.tracing.model.GetTracingsResponseResults;
-import it.pagopa.interop.client.b2b.generated.openapi.clients.interop.tracing.model.SubmitTracingResponse;
-import it.pagopa.interop.client.b2b.generated.openapi.clients.interop.tracing.model.TracingState;
+import it.pagopa.interop.client.b2b.generated.openapi.clients.interop.tracing.model.*;
import it.pagopa.interop.tracing.service.IInteropTracingClient;
import it.pagopa.pn.interop.cucumber.utility.TracingFileUtils;
import org.junit.jupiter.api.Assertions;
@@ -39,7 +34,6 @@ public class TracingSteps {
/**
* Dependency injection
- * @param pnPollingFactory {@link PnPollingFactory}
* @param interopTracingClient {@link IInteropTracingClient}
* @param tracingFileUtils {@link TracingFileUtils}
* @param pollingService {@link PollingService}
@@ -65,7 +59,7 @@ public void updateCsv() {
selectOperator("tenant1");
GetTracingsResponse tracingsResponse = interopTracingClient.getTracings(OFFSET_VALUE, LIMIT_VALUE, null);
submissionDate = tracingsResponse.getResults().stream()
- .map(GetTracingsResponseResults::getDate)
+ .map(GetTracingsResponseResultsInner::getDate)
.min(LocalDate::compareTo)
.map(date -> date.minusDays(1))
.orElseGet(() -> LocalDate.now().minusDays(1));
@@ -146,7 +140,7 @@ private void getTracingErrors(UUID tracingId) {
public void verifyGetTracingErrorResponse() {
Assertions.assertNotNull(getTracingErrorsResponse, "There was an error while retrieving the tracing error!");
Assertions.assertNotNull(getTracingErrorsResponse.getResults());
- List expectedResult = List.of(
+ List expectedResult = List.of(
createExpectedResponse("INVALID_STATUS_CODE", "status: Invalid HTTP status code", "0e1e4c98-6f2e-4f55-90e3-45f7d3f1dbf8", 1),
createExpectedResponse("INVALID_DATE", String.format("date: Date field (2024-08-25) in csv is different from tracing date (%s).", submissionDate.toString()), "0e1e4c98-6f2e-4f55-90e3-45f7d3f1dbf8", 1),
createExpectedResponse("PURPOSE_NOT_FOUND", "purpose_id: Invalid purpose id 0e1e4c98-6f2e-4f55-90e3-45f7d3f1dbf8.", "0e1e4c98-6f2e-4f55-90e3-45f7d3f1dbf8", 1),
@@ -182,7 +176,7 @@ private void recoverError(String tracingId, Resource resource) {
public void checkReturnedTracingId() {
Assertions.assertTrue(getTracingsResponse.getResults()
.stream()
- .map(GetTracingsResponseResults::getTracingId)
+ .map(GetTracingsResponseResultsInner::getTracingId)
.anyMatch(tracingId -> tracingId.equals(submitTracingResponse.getTracingId().toString())));
}
@@ -215,7 +209,7 @@ public void getHealthStatus() {
public void recoverMissingCsvForDate(String fileType) {
Assertions.assertNotNull(getTracingsResponse, "There was an error while retrieving the tracing with MISSING status!");
Assertions.assertFalse(getTracingsResponse.getResults().isEmpty(), "No tracing with MISSING status found!");
- GetTracingsResponseResults tracingsResponseResults = getTracingsResponse.getResults().get(0);
+ GetTracingsResponseResultsInner tracingsResponseResults = getTracingsResponse.getResults().get(0);
tracingFileUtils.updateCsv(tracingsResponseResults.getDate());
submissionDate = tracingsResponseResults.getDate();
uploadCsv(fileType);
@@ -227,7 +221,7 @@ public void waitForStatus(String state) {
() -> interopTracingClient.getTracings(0, 50, List.of(TracingState.fromValue(state))),
res -> res.getResults().stream()
.filter(x -> x.getTracingId().equals(submitTracingResponse.getTracingId().toString()))
- .map(GetTracingsResponseResults::getState)
+ .map(GetTracingsResponseResultsInner::getState)
.anyMatch(tracingState -> tracingState.equals(state)),
String.format("The TracingId: %s did not reach the desired status: %s", submitTracingResponse.getTracingId().toString(), state)
);
@@ -235,7 +229,7 @@ public void waitForStatus(String state) {
@Then("viene recuperato il file di tracing appena caricato e si verifica che lo stato sia {string}")
public void retrieveTracingAndVerifyStatus(String state) {
- GetTracingsResponseResults result;
+ GetTracingsResponseResultsInner result;
int attempt = 0;
int totalPages;
try {
@@ -267,8 +261,8 @@ public void retrieveTracingAndVerifyStatus(String state) {
}
- private GetTracingErrorsResponseResults createExpectedResponse(String errorCode, String message, String purposeId, Integer rowNumber) {
- GetTracingErrorsResponseResults tracingErrorsResponse = new GetTracingErrorsResponseResults();
+ private GetTracingErrorsResponseResultsInner createExpectedResponse(String errorCode, String message, String purposeId, Integer rowNumber) {
+ GetTracingErrorsResponseResultsInner tracingErrorsResponse = new GetTracingErrorsResponseResultsInner();
tracingErrorsResponse.setErrorCode(errorCode);
tracingErrorsResponse.setMessage(message);
tracingErrorsResponse.setPurposeId(purposeId);
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/utility/StepParser.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/utility/StepParser.java
new file mode 100644
index 0000000000..a9887cc3b8
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/utility/StepParser.java
@@ -0,0 +1,204 @@
+package it.pagopa.pn.interop.cucumber.utility;
+
+import it.pagopa.pn.interop.cucumber.utility.enums.ResolvableToken;
+
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public final class StepParser {
+ private StepParser() {
+ }
+
+ public static String normalize(String value) {
+ if (value == null) return null;
+
+ String v = value.trim();
+ ResolvableToken token = ResolvableToken.from(v);
+
+ if (token == ResolvableToken.NULL) return null;
+ if (token == ResolvableToken.BLANK) return "";
+ return v;
+ }
+
+ private static T parseCore(String value, boolean safe, Supplier randomSupplier, Function parser) {
+ String v = normalize(value);
+ if (v == null) return null;
+
+ ResolvableToken token = ResolvableToken.from(v);
+
+ try {
+ if (token == ResolvableToken.RANDOM && randomSupplier != null) {
+ return randomSupplier.get();
+ }
+ return parser.apply(v);
+ } catch (RuntimeException ex) {
+ if (safe) return null;
+ throw ex;
+ }
+ }
+
+ private static List parseSingletonListCore(String raw, boolean safe, Supplier randomSupplier, Function parser) {
+ T v = parseCore(raw, safe, randomSupplier, parser);
+ if (v == null) return null;
+
+ ResolvableToken token = ResolvableToken.from(raw);
+ if (token == null) return List.of(v);
+
+ List result = new ArrayList<>();
+ result.add(null);
+
+ return switch (token) {
+ case EMPTY_LIST -> List.of();
+ case NULL_ELEMENT_LIST -> result;
+ default -> throw new IllegalArgumentException("Unknown token: " + token);
+ };
+ }
+
+ public static String nullOrBlankOrValue(String value) {
+ return normalize(value);
+ }
+
+ public static String nullOrValue(String value) {
+ String v = normalize(value);
+ return (v == null || v.isBlank()) ? null : v;
+ }
+
+ public static T parseNullable(String value, Function parser) {
+ return parseCore(value, false, null, parser);
+ }
+
+ public static T parseNullableSafe(String value, Function parser) {
+ return parseCore(value, true, null, parser);
+ }
+
+ public static Integer nullableInteger(String value) {
+ return parseCore(value, true, null, Integer::valueOf);
+ }
+
+ public static Boolean nullableBoolean(String value) {
+ return parseCore(value, false, null, Boolean::parseBoolean);
+ }
+
+ public static UUID uuidOrRandomOrNull(String value) {
+ return parseCore(value, true, UUID::randomUUID, UUID::fromString);
+ }
+
+ public static Integer intOrRandomOrNull(String value) {
+ return parseCore(value, true, () -> ThreadLocalRandom.current().nextInt(), Integer::parseInt);
+ }
+
+ public static Long longOrRandomOrNull(String value) {
+ return parseCore(value, true, () -> ThreadLocalRandom.current().nextLong(), Long::parseLong);
+ }
+
+ public static List singletonListNullable(String value, Function mapper) {
+ return parseCore(
+ value,
+ true,
+ null,
+ raw -> {
+ ResolvableToken token = ResolvableToken.from(raw);
+
+ if (token != null) {
+ List nullElementList = new ArrayList<>();
+ nullElementList.add(null);
+
+ if (token == ResolvableToken.EMPTY_LIST) return List.of();
+ if (token == ResolvableToken.NULL_ELEMENT_LIST) return nullElementList;
+ throw new IllegalStateException("Token non gestito: " + token);
+ }
+
+ return List.of(mapper.apply(raw));
+ });
+ }
+
+ public static List singletonFilterList(
+ String value,
+ Supplier expectedSupplier,
+ Supplier actualSupplier,
+ Function mapper
+ ) {
+ return parseCore(
+ value,
+ true,
+ null,
+ raw -> {
+ ResolvableToken token = ResolvableToken.from(raw);
+
+ // non-token: mappa il valore e mettilo in lista
+ if (token == null) {
+ return List.of(mapper.apply(raw));
+ }
+
+ // token generali da filtro
+ if (token == ResolvableToken.NULL) return null;
+ if (token == ResolvableToken.BLANK) return null;
+ if (token == ResolvableToken.EXPECTED) {
+ return expectedSupplier != null ? List.of(expectedSupplier.get()) : null;
+ }
+ if (token == ResolvableToken.ACTUAL) {
+ return actualSupplier != null ? List.of(actualSupplier.get()) : null;
+ }
+
+ // token specifici liste
+ if (token == ResolvableToken.EMPTY_LIST) return List.of();
+
+ if (token == ResolvableToken.NULL_ELEMENT_LIST) {
+ return java.util.Collections.singletonList(null);
+ }
+
+ throw new IllegalStateException("Token non gestito: " + token);
+ }
+ );
+ }
+
+ public static Duration durationOrNull(String s) {
+ String normalized = normalize(s);
+ if (normalized == null) return null;
+
+ s = normalized.trim().toLowerCase(Locale.ROOT);
+ if (s.endsWith("ms")) return Duration.ofMillis(Long.parseLong(s.substring(0, s.length() - 2)));
+ if (s.endsWith("s")) return Duration.ofSeconds(Long.parseLong(s.substring(0, s.length() - 1)));
+ if (s.endsWith("m")) return Duration.ofMinutes(Long.parseLong(s.substring(0, s.length() - 1)));
+ if (s.endsWith("h")) return Duration.ofHours(Long.parseLong(s.substring(0, s.length() - 1)));
+ return Duration.parse(s);
+ }
+
+ public static OffsetDateTime dateTimeOrNull(String raw) {
+ raw = normalize(raw);
+ if (raw == null) return null;
+
+ String token = raw.trim();
+ String lower = token.toLowerCase();
+
+ OffsetDateTime now = OffsetDateTime.now().truncatedTo(ChronoUnit.SECONDS);
+ if (lower.equals("now")) return now;
+
+ // now+ / now- con suffisso h/m/s
+ if (lower.startsWith("now+") || lower.startsWith("now-")) {
+ boolean plus = lower.charAt(3) == '+';
+ String amountPart = lower.substring(4); // es: "15s", "2h", "10m"
+
+ char unit = amountPart.charAt(amountPart.length() - 1);
+ long value = Long.parseLong(amountPart.substring(0, amountPart.length() - 1));
+
+ return switch (unit) {
+ case 'h' -> plus ? now.plusHours(value) : now.minusHours(value);
+ case 'm' -> plus ? now.plusMinutes(value) : now.minusMinutes(value);
+ case 's' -> plus ? now.plusSeconds(value) : now.minusSeconds(value);
+ default -> throw new IllegalArgumentException("Unità non supportata nel token: " + token);
+ };
+ }
+
+ return OffsetDateTime.parse(token);
+ }
+
+}
diff --git a/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/utility/enums/ResolvableToken.java b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/utility/enums/ResolvableToken.java
new file mode 100644
index 0000000000..4b8afaf05e
--- /dev/null
+++ b/interop-qa-tests/src/test/java/it/pagopa/pn/interop/cucumber/utility/enums/ResolvableToken.java
@@ -0,0 +1,35 @@
+package it.pagopa.pn.interop.cucumber.utility.enums;
+
+public enum ResolvableToken {
+ ACTUAL("%actual"),
+ NULL("%null"),
+ EXPECTED("%expected"),
+ RANDOM("%random"),
+ BLANK("%blank"),
+
+ EMPTY_LIST("%empty_list"),
+ NULL_ELEMENT_LIST("%null_element_list");
+
+ private final String value;
+
+ ResolvableToken(String value) {
+ this.value = value;
+ }
+
+ public String value() {
+ return value;
+ }
+
+ public static ResolvableToken from(String raw) {
+ if (raw == null) return null;
+
+ String normalized = raw.trim().toLowerCase();
+ for (ResolvableToken token : values()) {
+ if (token.value.equals(normalized)) {
+ return token;
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/interop-qa-tests/src/test/resources/it/pagopa/pn/cucumber/probing/probing.feature b/interop-qa-tests/src/test/resources/it/pagopa/pn/cucumber/probing/probing.feature
new file mode 100644
index 0000000000..a0843f6b0b
--- /dev/null
+++ b/interop-qa-tests/src/test/resources/it/pagopa/pn/cucumber/probing/probing.feature
@@ -0,0 +1,296 @@
+Feature: Probing
+
+ Scenario Outline: [GET_STATUS] - Health probing-ms check
+ Given il microservizio risulta attivo
+ Then la response riporta lo status code 200
+
+ Examples:
+ | ms |
+ | "probing-api" |
+ | "probing-statistics-api" |
+
+ Scenario Outline: [GET_ESERVICES_CATALOG] - Consultazione e-service presenti nel catalogo probing (multi-filtro)
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When vengono recuperati dal catalogo gli e-service con limit "" e offset "" e filtri eserviceName "", producerName "", versionNumber "", state ""
+ Then la response riporta lo status code
+
+ Examples:
+ # --- Baseline / boundaries (no filters) ---
+ | limit | offset | eserviceName | producerName | versionNumber | state | statusCode |
+ | 10 | 0 | %null | %null | %null | %null | 200 |
+ | 1 | 0 | %null | %null | %null | %null | 200 |
+ | 100 | 0 | %null | %null | %null | %null | 200 |
+
+ # --- Single filter ---
+ | 10 | 0 | %expected | %null | %null | %null | 200 |
+ | 10 | 0 | %null | %expected | %null | %null | 200 |
+ | 10 | 0 | %null | %null | 1 | %null | 200 |
+ | 10 | 0 | %null | %null | %null | ACTIVE | 200 |
+
+ # --- Multi-filter (AND) ---
+ | 10 | 0 | %expected | %expected | %null | %null | 200 |
+ | 10 | 0 | %null | %expected | 1 | %null | 200 |
+ | 10 | 0 | %expected | %expected | 1 | ACTIVE | 200 |
+
+ # --- Required params missing ---
+ | %null | 0 | %null | %null | %null | %null | 400 |
+ | 10 | %null | %null | %null | %null | %null | 400 |
+
+ # --- Pagination invalid values ---
+ | 0 | 0 | %null | %null | %null | %null | 400 |
+ | 101 | 0 | %null | %null | %null | %null | 400 |
+ | 10 | -1 | %null | %null | %null | %null | 400 |
+
+ # --- Filter values edge cases (%blank treated as no filter) ---
+ | 10 | 0 | %blank | %null | %null | %null | 200 |
+ | 10 | 0 | %null | %blank | %null | %null | 200 |
+
+ # --- versionNumber invalid ---
+ | 10 | 0 | %null | %null | 0 | %null | 400 |
+ | 10 | 0 | %null | %null | -1 | %null | 400 |
+
+ Scenario Outline: [UPDATE_FREQUENCY] - Aggiornamento frequency e finestra temporale per e-service
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When vengono aggiornati i parametri di probing dell'e-service con eserviceId "" e versionId "" impostando frequency "", startDate "", endDate "" e si verifica che coincidano con quanto atteso
+ Then la response riporta lo status code
+
+ Examples:
+ # Happy paths
+ | eserviceId | versionId | frequency | startDate | endDate | statusCode |
+ | %actual | %actual | %actual | %actual | %actual | 204 |
+ | %actual | %actual | %actual+10 | %actual | %actual | 204 |
+ | %actual | %actual | %actual | now+1h | now+2h | 204 |
+
+ # Frequency invalid
+ | %actual | %actual | -1 | %actual | %actual | 400 |
+ | %actual | %actual | 0 | %actual | %actual | 400 |
+
+ # Window invalid
+ | %actual | %actual | %actual | now+2h | now+1h | 400 |
+ | %actual | %actual | %actual | %null | now+2h | 400 |
+ | %actual | %actual | %actual | now+1h | %null | 400 |
+
+ # Not found (wrong ids)
+ | %null | %actual | %actual | %actual | %actual | 400 |
+ | %actual | %null | %actual | %actual | %actual | 400 |
+ | %random | %actual | %actual | %actual | %actual | 404 |
+ | %actual | %random | %actual | %actual | %actual | 404 |
+
+ Scenario Outline: [UPDATE_PROBING_STATE] - Modifica stato di probing con combinazioni id/versione
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When viene modificato lo stato di probing dell'e-service con id "" e id versione "" in "" e si verifica che coincida con quanto atteso
+ Then la response riporta lo status code
+
+ Examples:
+ # Happy paths
+ | eserviceId | versionId | probingEnabled | statusCode |
+ | %actual | %actual | true | 204 |
+ | %actual | %actual | false | 204 |
+
+ # ProbingEnabled invalid
+ | %actual | %actual | %null | 400 |
+
+ # eserviceId/versionId invalid
+ | %actual | %null | true | 400 |
+ | %null | %actual | true | 400 |
+
+ # Not found (wrong ids)
+ | %actual | %random | true | 404 |
+ | %random | %actual | true | 404 |
+
+ Scenario Outline: [UPDATE_OPERATIONAL_STATE] - Modifica stato operativo con combinazioni id/versione
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When viene modificato lo stato operativo dell'e-service con id "" e id versione "" in "" e si verifica che coincida con quanto atteso
+ Then la response riporta lo status code
+
+ Examples:
+ # Happy paths
+ | eserviceId | versionId | eserviceState | statusCode |
+ | %actual | %actual | ACTIVE | 204 |
+ | %actual | %actual | INACTIVE | 204 |
+
+ # EserviceState invalid
+ | %actual | %actual | %null | 400 |
+
+ # eserviceId/versionId invalid
+ | %actual | %null | ACTIVE | 400 |
+ | %null | %actual | ACTIVE | 400 |
+
+ # Not found (wrong ids)
+ | %actual | %random | ACTIVE | 404 |
+ | %random | %actual | ACTIVE | 404 |
+
+ Scenario Outline: [GET_PRODUCERS] - Recupero lista producers con paginazione
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When recupero la lista dei producers con limit "" e offset "" e producerName ""
+ Then la response riporta lo status code
+
+ Examples:
+ # Happy paths
+ | limit | offset | producer | statusCode |
+ | 30 | 0 | %expected | 200 |
+ | 1 | 0 | %expected | 200 |
+ | 100 | 0 | %expected | 200 |
+ | 30 | 10 | %expected | 200 |
+ | 30 | 0 | %null | 200 |
+ | 30 | 0 | %blank | 200 |
+
+ # Required params missing
+ | %null | 0 | %expected | 400 |
+ | 10 | %null | %expected | 400 |
+
+ # Pagination invalid values
+ | 0 | 0 | %expected | 400 |
+ | -1 | 0 | %expected | 400 |
+ | 101 | 0 | %expected | 400 |
+ | 10 | -1 | %expected | 400 |
+ | 0 | -1 | %expected | 400 |
+
+ # producerName edge cases (should not 400)
+ | 30 | 0 | NOT_EXISTING_PRODUCER | 200 |
+
+ Scenario Outline: [GET_ESERVICE_MAIN_DATA] - Recupera i metadati anagrafici di un e-service tramite il suo eserviceRecordId
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When vengono recuperati i main data dell'e-service con eserviceRecordId ""
+ Then la response riporta lo status code
+
+ Examples:
+ | eserviceRecordId | statusCode |
+ | %actual | 200 |
+ | %null | 400 |
+ | -1 | 400 |
+ | %random | 404 |
+
+ Scenario Outline: [GET_ESERVICE_PROBING_DATA] - Recupera i dati di probing di un e-service tramite il suo eserviceRecordId
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When vengono recuperati i dati di probing dell'e-service con eserviceRecordId ""
+ Then la response riporta lo status code
+
+ Examples:
+ | eserviceRecordId | statusCode |
+ | %actual | 200 |
+ | %null | 400 |
+ | -1 | 400 |
+ | %random | 404 |
+
+ # NOTA: Lo status code 200 è previsto anche per il caso di un eserviceRecordId random per i motivi descritti nel ticket
+ # https://pagopa.atlassian.net/browse/PIN-9090
+ Scenario Outline: [GET_ESERVICE_PUBLIC_TELEMETRY] - Recupera la telemetria pubblica di un e-service tramite il suo eserviceRecordId
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When viene recuperata la telemetria pubblica dell'e-service con eserviceRecordId "" e pollingFrequency ""
+ Then la response riporta lo status code
+
+ Examples:
+ # Happy paths
+ | eserviceRecordId | frequency | statusCode |
+ | %actual | %actual | 200 |
+ | %random | %actual | 200 |
+
+ # Frequency invalid values
+ | %actual | %null | 400 |
+ | %actual | -1 | 400 |
+
+ # eserviceRecordId invalid values
+ | %null | %actual | 400 |
+
+ # NOTA: Lo status code 200 è previsto anche per il caso di un eserviceRecordId random per i motivi descritti nel ticket
+ # https://pagopa.atlassian.net/browse/PIN-9090
+ Scenario Outline: [GET_ESERVICE_TELEMETRY] - Recupera la telemetria di un e-service tramite il suo eserviceRecordId e filtro temporale
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When viene recuperata la telemetria dell'e-service con eserviceRecordId "" e impostando pollingFrequency "" , startDate "" , endDate ""
+ Then la response riporta lo status code
+
+ Examples:
+ # Happy paths
+ | eserviceRecordId | frequency | startDate | endDate | statusCode |
+ | %actual | %actual | now-20h | now-10h | 200 |
+ | %random | %actual | now-20h | now-10h | 200 |
+
+ # eserviceRecordId invalid values
+ | %null | %actual | now-20h | now-10h | 400 |
+ | -1 | %actual | now-20h | now-10h | 400 |
+
+ # frequency invalid values
+ | %actual | %null | now-20h | now-10h | 400 |
+ | %actual | -1 | now-20h | now-10h | 400 |
+
+ # startDate invalid values
+ | %actual | %actual | %null | now-10h | 400 |
+
+ # endDate invalid values
+ | %actual | %actual | now-20h | %null | 400 |
+
+ Scenario Outline: [SCHEDULING] - Update frequency aggiorna lo scheduling
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ When vengono aggiornati i parametri di probing dell'e-service con eserviceId "%expected" e versionId "%expected" impostando frequency "", startDate "", endDate "" e si verifica che coincidano con quanto atteso
+ And viene modificato lo stato di probing dell'e-service con id "%expected" e id versione "%expected" in "true" e si verifica che coincida con quanto atteso
+ Then verifica che la responseReceived sia aggiornata coerentemente rispetto la frequency "", clockScheduler "", startDate "", endDate ""
+ And viene modificato lo stato di probing dell'e-service con id "%expected" e id versione "%expected" in "false" e si verifica che coincida con quanto atteso
+
+ Examples:
+ # BEFORE window (inizia tra poco): deve NON avanzare prima dello start,
+ # e poi (se la finestra è abbastanza lunga) deve avanzare almeno una volta dentro.
+ | frequency | clockScheduler | startDate | endDate |
+ | 1 | 3 | now+1m | now+5m |
+
+ # IN window (già dentro): finestra lunga -> atteso almeno 1 update
+ | 1 | 3 | now-5m | now+5m |
+
+ # AFTER window (finita da poco): deve NON avanzare dopo end
+ | 1 | 3 | now-10m | now-1m |
+
+ # Boundary start (start = now): finestra lunga -> atteso almeno 1 update
+ | 1 | 3 | now | now+5m |
+
+ # Boundary end (end = now): deve NON avanzare (siamo fuori finestra)
+ | 1 | 3 | now-10m | now |
+
+ # Start & stop nello stesso test (finestra corta): NON pretendere update,
+ # ma se accade deve rispettare end e poi deve fermarsi.
+ | 1 | 3 | now | now+2m |
+
+ # Window shorter than period: NON pretendere update
+ | 5 | 3 | now | now+2m |
+
+ Scenario Outline: [SCHEDULING_2] - Probing disabled non aggiorna mai
+ Given vengono calcolate le informazioni di probing relative ad un e-service presente a catalogo
+ And viene modificato lo stato di probing dell'e-service con id "%expected" e id versione "%expected" in "false" e si verifica che coincida con quanto atteso
+ When vengono aggiornati i parametri di probing dell'e-service con eserviceId "%expected" e versionId "%expected" impostando frequency "", startDate "", endDate "" e si verifica che coincidano con quanto atteso
+ Then verifica che la responseReceived NON sia aggiornata quando probing è disabilitato
+
+ Examples:
+ | frequency | startDate | endDate |
+ | 1 | now-5m | now+4m |
+ | 1 | now+1m | now+4m |
+ | 1 | now-10m | now-1m |
+ | 5 | now | now+2m |
+
+ Scenario Outline: [PROBING_COMPLETE_PROCESS] - Processo completo di probing con aggiornamento stato e telemetria
+ Given vengono calcolate le informazioni di probing relative ad un e-service con health check presente a catalogo
+ And vengono aggiornati i parametri di probing dell'e-service con eserviceId "%expected" e versionId "%expected" impostando frequency "", startDate "", endDate "" e si verifica che coincidano con quanto atteso
+ And viene modificato lo stato di probing dell'e-service con id "%expected" e id versione "%expected" in "true" e si verifica che coincida con quanto atteso
+ When verifica che la responseReceived sia aggiornata coerentemente rispetto la frequency "", clockScheduler "3", startDate "", endDate ""
+ And viene recuperata la telemetria pubblica dell'e-service con eserviceRecordId "%expected" e pollingFrequency ""
+ And vengono recuperati i dati di probing dell'e-service con eserviceRecordId "%expected"
+ Then la telemetria dell'e-service risulta aggiornata con successo
+ And lo stato di probing dell'e-service viene aggiornato con valore "ACTIVE"
+ And viene modificato lo stato di probing dell'e-service con id "%expected" e id versione "%expected" in "false" e si verifica che coincida con quanto atteso
+
+ Examples:
+ # BEFORE window (inizia tra poco)
+ | frequency | startDate | endDate | mockResponse |
+ | 1 | now+1m | now+10m | OK |
+ | 1 | now+1m | now+10m | ERROR |
+ | 1 | now+1m | now+10m | RANDOM |
+
+ Scenario: [LOAD] 20k enable e verifica update dopo N periodi
+ Given preparo il load test probing con:
+ | totalEservices | workers | frequency | startDate | endDate | waitPeriods | extraWait | recentTolerance |
+ | 20000 | 100 | 1 | now-1m | now+10m | 1 | 45s | 45s |
+ When aggiorno scheduling in parallelo per tutti gli eservice
+ And abilito probing in parallelo per tutti gli eservice
+ And attendo N periodi di frequency più extraWait
+ Then verifico in parallelo che responseReceived sia valorizzata e aggiornata dopo l'enable per tutti gli eservice
+ And disabilito probing in parallelo per tutti gli eservice
+
+
diff --git a/interop-qa-tests/src/test/resources/utils/probing/qa/populate_eservice.sql b/interop-qa-tests/src/test/resources/utils/probing/qa/populate_eservice.sql
new file mode 100644
index 0000000000..a0fd595564
--- /dev/null
+++ b/interop-qa-tests/src/test/resources/utils/probing/qa/populate_eservice.sql
@@ -0,0 +1,114 @@
+TRUNCATE TABLE qa_probing.eservices CASCADE;
+
+DO
+$$
+DECLARE
+ -- =====================
+ -- CONFIG (MODIFICABILE)
+ -- =====================
+v_ok_count int := 1;
+ v_error_count
+int := 1;
+ v_random_count
+int := 1;
+
+ v_base_host
+text := 'http://probing-be-eservice-mock.qa:8080';
+
+ -- polling (UTC)
+ v_polling_start_time
+timetz := '08:00:00+00';
+ v_polling_end_time
+timetz := '17:00:00+00';
+ v_polling_frequency
+int := 1;
+
+ v_probing_enabled_default
+boolean := false;
+
+ v_eservices_count
+int := 0;
+
+BEGIN
+ v_eservices_count
+:= v_ok_count + v_error_count + v_random_count;
+
+ IF
+v_eservices_count <= 0 THEN
+ RAISE EXCEPTION
+ 'Total eservices count must be > 0 (ok=% , error=% , random=%).',
+ v_ok_count, v_error_count, v_random_count;
+END IF;
+
+INSERT INTO qa_probing.eservices (id,
+ eservice_id,
+ version_id,
+ eservice_name,
+ producer_name,
+ eservice_technology,
+ base_path,
+ audience,
+ state,
+ version_number,
+ lock_version,
+ probing_enabled,
+ polling_start_time,
+ polling_end_time,
+ polling_frequency)
+ OVERRIDING SYSTEM VALUE
+SELECT gs::bigint AS id,
+
+ -- UUID deterministico da gs (
+ substr(md5(gs::bigint::text), 1, 8) || '-' ||
+ substr(md5(gs::bigint::text), 9, 4) || '-' ||
+ substr(md5(gs::bigint::text), 13, 4) || '-' ||
+ substr(md5(gs::bigint::text), 17, 4) || '-' ||
+ substr(md5(gs::bigint::text), 21, 12)
+ )::uuid AS eservice_id,
+
+ -- UUID deterministico da (1000000000 + gs) (
+ substr(md5((1000000000 + gs::bigint)::text), 1, 8) || '-' ||
+ substr(md5((1000000000 + gs::bigint)::text), 9, 4) || '-' ||
+ substr(md5((1000000000 + gs::bigint)::text), 13, 4) || '-' ||
+ substr(md5((1000000000 + gs::bigint)::text), 17, 4) || '-' ||
+ substr(md5((1000000000 + gs::bigint)::text), 21, 12)
+ )::uuid AS version_id, ('ESVC-' || lpad(gs::text, 8, '0')) AS eservice_name,
+ ('Producer ' || ((gs - 1) % 50 + 1)) AS producer_name,
+
+ -- Tecnologia: deterministica e NON NULL (pari/dispari)
+ CASE WHEN (gs % 2) = 0 THEN 'SOAP' ELSE 'REST'
+END AS eservice_technology,
+
+ -- base_path: host fisso + endpoint coerente con tecnologia + outcome configurabile
+ ARRAY[
+ v_base_host ||
+ CASE WHEN (gs % 2) = 0
+ THEN '/soap/interop/probing/'
+ ELSE '/rest/interop/probing/'
+END
+||
+ CASE
+ WHEN gs <= v_ok_count THEN 'ok'
+ WHEN gs <= v_ok_count + v_error_count THEN 'error'
+ ELSE 'random'
+END
+||
+ '/status'
+ ]::varchar[] AS base_path,
+
+ ARRAY['AUD_' || (1 + ((gs - 1) % 20))]::varchar[] AS audience,
+ 'ACTIVE' AS state,
+ 1 AS version_number,
+ 0 AS lock_version,
+ v_probing_enabled_default AS probing_enabled,
+ v_polling_start_time AS polling_start_time,
+ v_polling_end_time AS polling_end_time,
+ v_polling_frequency AS polling_frequency
+ FROM generate_series(1, v_eservices_count) gs;
+
+ RAISE
+NOTICE 'Inserted % rows (ok=% , error=% , random=%).',
+ v_eservices_count, v_ok_count, v_error_count, v_random_count;
+
+END
+$$;