Skip to content

Commit

Permalink
feat(impl): [#518] Cucumber test step definitions for Policy Store API
Browse files Browse the repository at this point in the history
including:
- Split up the E2E step definition classes into two classes (one for each API: job API and policy API).
- Moved some code to helper classes.
- Configured RESTAssured request / response logging centrally (for better debugging and in order to see progress when github action runs).
  • Loading branch information
dsmf committed May 2, 2024
1 parent e47c5e5 commit f5b39ea
Show file tree
Hide file tree
Showing 7 changed files with 815 additions and 80 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ _**For better traceability add the corresponding GitHub issue number in each cha
- Added Integration Tests for the entire IRS flow using stubbed responses of Discovery Service, Semantic Hub, EDC,
Digital Twin Registry and BPDM Pool. #344

### Changed
## Added

- Cucumber test step definitions for Policy Store API (Happy Path) including some test helper utilities. #518

## Changed

- Dataspace Discovery Service handles multiple EDC-Urls received for BPN now. #214
- Updated license header to "Copyright (c) 2021,2024 Contributors to the Eclipse Foundation" #349
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/********************************************************************************
* Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
package org.eclipse.tractusx.irs.cucumber;

import static io.restassured.RestAssured.given;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.function.Predicate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.restassured.RestAssured;
import io.restassured.filter.log.LogDetail;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
import io.restassured.specification.RequestSpecification;
import org.apache.commons.lang3.StringUtils;

/**
* Common helper class for E2E tests.
*/
public class E2ETestHelper {

public static final ObjectMapper objectMapper;

static {
objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}

private static final boolean LOG_EVERY_REQUEST_AND_RESPONSE = true;

public static File getExpectedFile(final String fileName) {
return getFile("expected-files/" + fileName);
}

public static String getTemplateFileContent(final String fileName) throws IOException {
final File file = getTemplateFile(fileName);
byte[] bytes = Files.readAllBytes(file.toPath());
return new String(bytes, StandardCharsets.UTF_8);
}

public static File getTemplateFile(final String fileName) {
return getFile("templates/" + fileName);
}

public static File getFile(final String path) {
final ClassLoader classLoader = E2ETestHelper.class.getClassLoader();
final URL ressource = classLoader.getResource(path);
return new File(ressource.getFile());
}

public static RequestSpecification givenAuthentication(
AuthenticationProperties.AuthenticationPropertiesBuilder authBuilder) {
final AuthenticationProperties authProperties = authBuilder.build();
return given().spec(authProperties.getNewAuthenticationRequestSpecification());
}

static Predicate<String> startingWith(final String prefix) {
return s -> StringUtils.startsWith(s, prefix);
}

public static void configureRequestAndResponseLogging() {
if (LOG_EVERY_REQUEST_AND_RESPONSE) {
final LogDetail logDetail = LogDetail.ALL;
final RequestLoggingFilter requestLoggingFilter = new RequestLoggingFilter(logDetail);
final ResponseLoggingFilter responseLoggingFilter = new ResponseLoggingFilter(logDetail);
RestAssured.filters(requestLoggingFilter, responseLoggingFilter);
} else {
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(LogDetail.ALL);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
/********************************************************************************
* Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
package org.eclipse.tractusx.irs.cucumber;

import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.tractusx.irs.cucumber.E2ETestHelper.givenAuthentication;
import static org.eclipse.tractusx.irs.cucumber.E2ETestHelper.objectMapper;

import java.io.IOException;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java.DataTableType;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.tractusx.irs.cucumber.AuthenticationProperties.AuthenticationPropertiesBuilder;
import org.eclipse.tractusx.irs.cucumber.E2ETestHelperForPolicyStoreApi.CreatePolicyRequest.CreatePolicyRequestBuilder;
import org.springframework.http.HttpStatus;

/**
* Helper class for Policy Store API tests.
*/
public class E2ETestHelperForPolicyStoreApi {

public static final String URL_IRS_POLICIES = "/irs/policies";

public static final String QUERYPARAM_BUSINESS_PARTNER_NUMBERS = "businessPartnerNumbers";

public static String getPolicyTemplate() throws IOException {
return E2ETestHelper.getTemplateFileContent("policy-for-e2e-tests.json");
}

@SuppressWarnings("unchecked")
public static Map<String, ArrayList<LinkedHashMap<String, ?>>> fetchPoliciesForBpn(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder, final String bpn) {

return givenAuthentication(authenticationPropertiesBuilder).queryParam(QUERYPARAM_BUSINESS_PARTNER_NUMBERS, bpn)
.when()
.get(URL_IRS_POLICIES)
.then()
.statusCode(HttpStatus.OK.value())
.extract()
.body()
.as(Map.class);
}

public static ValidatableResponse fetchAllPolicies(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder) {
return givenAuthentication(authenticationPropertiesBuilder).when().get(URL_IRS_POLICIES).then();
}

@Builder
public record CreatePolicyRequest(OffsetDateTime validUntil, String businessPartnerNumber, JsonNode payload) {
}

@Builder
public record UpdatePolicyRequest(OffsetDateTime validUntil, List<String> businessPartnerNumbers,
List<String> policyIds) {
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class BpnToPolicyId {
private String bpn;
private String policyId;
}

@DataTableType
public BpnToPolicyId bpnToPolicyEntryTransformer(final Map<String, String> row) {
return new BpnToPolicyId(row.get("BPN"), row.get("policyId"));
}

public static HashMap<String, HashSet<String>> getExpectedBpnToPolicyIdsMapping(
final List<BpnToPolicyId> bpnToPolicyIdTable) {

final HashMap<String, HashSet<String>> expectedBpnToPolicyIdsMapping = new HashMap<>();
bpnToPolicyIdTable.forEach(entry -> {

HashSet<String> policyIds = expectedBpnToPolicyIdsMapping.get(entry.getBpn());
if (policyIds == null) {
policyIds = new HashSet<>();
}

policyIds.add(entry.getPolicyId());

expectedBpnToPolicyIdsMapping.put(entry.getBpn(), policyIds);
});
return expectedBpnToPolicyIdsMapping;
}

public static Stream<String> extractPolicyIdsForBpn(
final Map<String, ArrayList<LinkedHashMap<String, ?>>> bpnToPoliciesMap, final String bpn) {
return extractPolicyIds(bpnToPoliciesMap.get(bpn).stream());
}

@SuppressWarnings("rawtypes")
public static Stream<LinkedHashMap> extractPoliciesForBpn(
final Map<String, ArrayList<LinkedHashMap<String, ?>>> bpnToPoliciesMap, final String bpn) {
return extractPolicy(bpnToPoliciesMap.get(bpn).stream());
}

public static Stream<String> extractPolicyIdsStartingWith(
final Map<String, ArrayList<LinkedHashMap<String, ?>>> bpnToPoliciesMap, final String policyIdPrefix) {
return extractPolicyIds(bpnToPoliciesMap).filter(policyId -> StringUtils.startsWith(policyId, policyIdPrefix));
}

public static Stream<String> extractPolicyIds(
final Map<String, ArrayList<LinkedHashMap<String, ?>>> bpnToPoliciesMap) {
return extractPolicyIds(bpnToPoliciesMap.values().stream().flatMap(Collection::stream));
}

private static Stream<String> extractPolicyIds(final Stream<LinkedHashMap<String, ?>> linkedHashMapStream) {
return extractPolicy(linkedHashMapStream).map(v -> (String) v.get("policyId"));
}

@SuppressWarnings("rawtypes")
private static Stream<LinkedHashMap> extractPolicy(final Stream<LinkedHashMap<String, ?>> linkedHashMapStream) {
return extractPolicyPayloads(linkedHashMapStream).map(v -> (LinkedHashMap) v.get("policy"));
}

@SuppressWarnings("rawtypes")
private static Stream<LinkedHashMap> extractPolicyPayloads(
final Stream<LinkedHashMap<String, ?>> linkedHashMapStream) {
return linkedHashMapStream.map(v -> (LinkedHashMap) v.get("payload"));
}

public static JsonNode jsonFromString(final ObjectMapper objectMapper, final String jsonObjectStr)
throws JsonProcessingException {
return objectMapper.readTree(jsonObjectStr);
}

public static ValidatableResponse updatePolicies(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder, final List<String> policyIds,
final List<String> businessPartnerNumbers, final String validUntil) {

final var updatePolicyRequest = UpdatePolicyRequest.builder()
.policyIds(policyIds)
.businessPartnerNumbers(businessPartnerNumbers)
.validUntil(OffsetDateTime.parse(validUntil))
.build();
return givenAuthentication(authenticationPropertiesBuilder).contentType(ContentType.JSON)
.body(updatePolicyRequest)
.when()
.put(URL_IRS_POLICIES)
.then();
}

public static ValidatableResponse registerPolicyForBpn(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder, final String policyJson,
final String bpn, final String validUntil) {

final CreatePolicyRequest createPolicyRequest;
try {
CreatePolicyRequestBuilder builder = CreatePolicyRequest.builder();
if (validUntil != null) {
builder = builder.validUntil(OffsetDateTime.parse(validUntil));
}
createPolicyRequest = builder.businessPartnerNumber(bpn)
.payload(E2ETestHelperForPolicyStoreApi.jsonFromString(objectMapper,
policyJson))
.build();
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}

return givenAuthentication(authenticationPropertiesBuilder).contentType(ContentType.JSON)
.body(createPolicyRequest)
.when()
.post(URL_IRS_POLICIES)
.then();

}

public static ValidatableResponse fetchPoliciesForBusinessPartnerNumbers(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder,
final List<String> businessPartnerNumbers) {

return givenAuthentication(authenticationPropertiesBuilder).queryParam(QUERYPARAM_BUSINESS_PARTNER_NUMBERS,
businessPartnerNumbers).when().get(URL_IRS_POLICIES).then();
}

public static void cleanupPolicyIdsByPrefix(final AuthenticationPropertiesBuilder authenticationPropertiesBuilder,
final String policyIdPrefix) {
fetchPolicyIdsByPrefixSuccessfully(authenticationPropertiesBuilder, policyIdPrefix).forEach(
policyId -> cleanupPolicy(authenticationPropertiesBuilder, policyId));
}

public static List<String> fetchPolicyIdsByPrefixSuccessfully(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder, final String policyIdPrefix) {
final Map<String, ArrayList<LinkedHashMap<String, ?>>> bpnToPoliciesMap = E2ETestHelperForPolicyStoreApi.fetchAllPoliciesSuccessfully(
authenticationPropertiesBuilder);
return E2ETestHelperForPolicyStoreApi.extractPolicyIdsStartingWith(bpnToPoliciesMap, policyIdPrefix).toList();
}

@SuppressWarnings("unchecked")
public static Map<String, ArrayList<LinkedHashMap<String, ?>>> fetchAllPoliciesSuccessfully(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder) {
final ValidatableResponse getAllPoliciesResponse = fetchAllPolicies(authenticationPropertiesBuilder);
return getAllPoliciesResponse.statusCode(HttpStatus.OK.value()).extract().body().as(Map.class);
}

public static void cleanupPolicy(final AuthenticationPropertiesBuilder authenticationPropertiesBuilder,
final String policyId) {

final ValidatableResponse deleteResponse = deletePolicy(authenticationPropertiesBuilder, policyId);
final int status = deleteResponse.extract().statusCode();

assertThat(List.of(HttpStatus.OK.value(), HttpStatus.NOT_FOUND.value())).describedAs(
"Should either return status 200 OK or 404 NOT_FOUND").contains(status);
}

public static ValidatableResponse deletePolicy(
final AuthenticationPropertiesBuilder authenticationPropertiesBuilder, final String policyId) {
return givenAuthentication(authenticationPropertiesBuilder).pathParam("policyId", policyId)
.when()
.delete(URL_IRS_POLICIES + "/{policyId}")
.then();
}

@Data
@NoArgsConstructor
public static final class PolicyAttributes {
private String policyId;
private List<String> bpnls;

Check notice

Code scanning / CodeQL

Exposing internal representation Note test

getBpnls exposes the internal representation stored in field bpnls. The value may be modified
after this call to getBpnls
.
private String validUntil;
}
}
Loading

0 comments on commit f5b39ea

Please sign in to comment.