From d2a8286203ee036b1d1f126c1a5ccc9bf7ba3ff7 Mon Sep 17 00:00:00 2001 From: Dennis Laetsch Date: Tue, 7 Oct 2025 09:52:01 +0200 Subject: [PATCH 1/3] added non working path from ProviderState test --- .../contract/ProductConsumerPactTest.java | 9 ++-- .../PactProviderVerificationTest.java | 42 ++++++++++++------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java b/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java index 6148d7a..439e0a3 100644 --- a/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java +++ b/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java @@ -26,16 +26,17 @@ class ProductConsumerPactTest { @Pact(consumer = "order-backend", provider = "product-backend") public RequestResponsePact getProduct_ok(PactDslWithProvider builder) { + Integer id = 1; PactDslJsonBody body = new PactDslJsonBody() - .numberType("id", 1) + .numberType("id", id) .stringType("name", "TestProduct") .numberType("price", 19.99) .stringMatcher("currency", "EUR|USD"); return builder - .given("product 1 exists") - .uponReceiving("GET /products/1") - .path("/products/1") + .given("product exists", Map.of("id", id)) + .uponReceiving("GET ein Product das existiert") + .pathFromProviderState("/products/${id}", "/products/%d".formatted(id)) .method("GET") .willRespondWith() .status(200) diff --git a/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java b/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java index 22d1125..7aff28d 100644 --- a/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java +++ b/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java @@ -20,7 +20,7 @@ import produktkatalog.infrastructure.ProductService; import java.math.BigDecimal; -import java.math.BigInteger; +import java.util.Map; import java.util.Optional; import static org.mockito.Mockito.when; @@ -44,10 +44,32 @@ void setup(@NotNull PactVerificationContext ctx) { ctx.setTarget(new HttpTestTarget("localhost", port, "/product-api")); } - @State("product 1 exists") - void setupUpProductExists() { - when(productService.getProductById(1)) - .thenReturn(Optional.of(new Product(1, "TestProduct", new BigDecimal("19.99"), EUR))); + @State("product exists") + void setupUpProductExists(Map params) { + Integer id = Integer.valueOf(params.getOrDefault("id", 1).toString()); + + System.err.println("ID: "+ id); //ID: 1 found! + /** + * 1) Verifying a pact between order-backend and product-backend - GET ein Product das existiert: has status code 200 + * + * 1.1) status: expected status of 200 but was 404 + * + * 1.2) body: $ Actual map is missing the following keys: currency, id, name, price + * + * { + * - "currency": "USD", + * - "id": 1, + * - "name": "TestProduct", + * - "price": 19.99 + * + "error": "Not Found", + * + "path": "/product-api/products/", + * + "status": 404, + * + "timestamp": "2025-10-07T07:50:53.601+00:00" + * } + */ + + when(productService.getProductById(id)) + .thenReturn(Optional.of(new Product(id, "TestProduct", new BigDecimal("19.99"), EUR))); } @State("product 999 not found") @@ -61,14 +83,4 @@ void setUpProductNotFound() { void verify(PactVerificationContext ctx) { ctx.verifyInteraction(); } - - - private static Integer asInt(Object value) { - if (value instanceof Integer i) return i; - if (value instanceof Long l) return l.intValue(); - if (value instanceof BigInteger bi) return bi.intValue(); - if (value instanceof Number n) return n.intValue(); - return Integer.parseInt(String.valueOf(value)); - } - } From 781721c1fe1fc3b93d8175a12b14b0b713be78d7 Mon Sep 17 00:00:00 2001 From: Dennis Laetsch Date: Tue, 7 Oct 2025 09:56:51 +0200 Subject: [PATCH 2/3] add pact to vsc --- .../PactProviderVerificationTest.java | 3 +- .../pacts/order-backend-product-backend.json | 78 ++++++++++++++++++- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java b/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java index 7aff28d..d0c3c68 100644 --- a/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java +++ b/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java @@ -28,7 +28,8 @@ @Provider("product-backend") @Consumer("order-backend") -@PactFolder("../order-backend/target/pacts") +//@PactFolder("../order-backend/target/pacts") +@PactFolder("/pacts") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ProductApp.class) @ExtendWith(PactVerificationSpring6Provider.class) class PactProviderVerificationTest { diff --git a/product-backend/src/test/resources/pacts/order-backend-product-backend.json b/product-backend/src/test/resources/pacts/order-backend-product-backend.json index 1fe8d25..2984c6e 100644 --- a/product-backend/src/test/resources/pacts/order-backend-product-backend.json +++ b/product-backend/src/test/resources/pacts/order-backend-product-backend.json @@ -4,26 +4,96 @@ }, "interactions": [ { - "description": "GET /product-api/products/1", + "description": "GET /products/999", "providerStates": [ { - "name": "product with id 1 exists" + "name": "product 999 not found" } ], "request": { "method": "GET", - "path": "/product-api/products/1" + "path": "/products/999" + }, + "response": { + "status": 404 + } + }, + { + "description": "GET ein Product das existiert", + "providerStates": [ + { + "name": "product exists", + "params": { + "id": 1 + } + } + ], + "request": { + "generators": { + "path": { + "dataType": "STRING", + "expression": "/products/${id}", + "type": "ProviderState" + } + }, + "method": "GET", + "path": "/products/1" }, "response": { "body": { - "currency": "EUR", + "currency": "USD", "id": 1, "name": "TestProduct", "price": 19.99 }, + "generators": { + "body": { + "$.currency": { + "regex": "EUR|USD", + "type": "Regex" + } + } + }, "headers": { "Content-Type": "application/json" }, + "matchingRules": { + "body": { + "$.currency": { + "combine": "AND", + "matchers": [ + { + "match": "regex", + "regex": "EUR|USD" + } + ] + }, + "$.id": { + "combine": "AND", + "matchers": [ + { + "match": "number" + } + ] + }, + "$.name": { + "combine": "AND", + "matchers": [ + { + "match": "type" + } + ] + }, + "$.price": { + "combine": "AND", + "matchers": [ + { + "match": "number" + } + ] + } + } + }, "status": 200 } } From 6146a59153f06aedf83ed6daa267274bb517a99a Mon Sep 17 00:00:00 2001 From: Dennis Laetsch Date: Tue, 7 Oct 2025 11:12:05 +0200 Subject: [PATCH 3/3] fix: pathFromProvider state working with pact version 4.6.17 --- .../infrastructure/ProductServiceImpl.java | 2 +- .../contract/ProductConsumerPactTest.java | 17 ++++--- .../order-backend-product-backend.json | 45 +++++++++++-------- pom.xml | 2 +- .../PactProviderVerificationTest.java | 33 +++----------- 5 files changed, 44 insertions(+), 55 deletions(-) rename {product-backend/src/test/resources/pacts => pacts}/order-backend-product-backend.json (80%) diff --git a/order-backend/src/main/java/auftragsverwaltung/infrastructure/ProductServiceImpl.java b/order-backend/src/main/java/auftragsverwaltung/infrastructure/ProductServiceImpl.java index e989646..6e09194 100644 --- a/order-backend/src/main/java/auftragsverwaltung/infrastructure/ProductServiceImpl.java +++ b/order-backend/src/main/java/auftragsverwaltung/infrastructure/ProductServiceImpl.java @@ -19,7 +19,7 @@ public ProductServiceImpl(RestTemplate restTemplate, @Value("${product.api.base- @Override public ProductDTO getProductById(Integer id) { - String url = baseUrl + "/products/{id}"; + String url = baseUrl + "/product-api/products/{id}"; return restTemplate.getForObject(url, ProductDTO.class, id); } } diff --git a/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java b/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java index 439e0a3..eac371b 100644 --- a/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java +++ b/order-backend/src/test/java/auftragsverwaltung/contract/ProductConsumerPactTest.java @@ -8,6 +8,7 @@ import au.com.dius.pact.core.model.PactSpecVersion; import au.com.dius.pact.core.model.RequestResponsePact; import au.com.dius.pact.core.model.annotations.Pact; +import au.com.dius.pact.core.model.annotations.PactDirectory; import auftragsverwaltung.infrastructure.ProductDTO; import auftragsverwaltung.infrastructure.ProductServiceImpl; import org.junit.jupiter.api.Test; @@ -22,21 +23,22 @@ @ExtendWith(PactConsumerTestExt.class) @PactTestFor(providerName = "product-backend", pactVersion = PactSpecVersion.V3) +@PactDirectory("../pacts") class ProductConsumerPactTest { @Pact(consumer = "order-backend", provider = "product-backend") public RequestResponsePact getProduct_ok(PactDslWithProvider builder) { - Integer id = 1; + Integer anyId = 1; PactDslJsonBody body = new PactDslJsonBody() - .numberType("id", id) + .numberType("id", anyId) .stringType("name", "TestProduct") .numberType("price", 19.99) .stringMatcher("currency", "EUR|USD"); return builder - .given("product exists", Map.of("id", id)) + .given("product exists", Map.of("id", anyId)) .uponReceiving("GET ein Product das existiert") - .pathFromProviderState("/products/${id}", "/products/%d".formatted(id)) + .pathFromProviderState("/product-api/products/${id}", "/product-api/products/%d".formatted(anyId)) .method("GET") .willRespondWith() .status(200) @@ -47,10 +49,11 @@ public RequestResponsePact getProduct_ok(PactDslWithProvider builder) { @Pact(consumer = "order-backend", provider = "product-backend") public RequestResponsePact getProduct_notFound(PactDslWithProvider builder) { + Integer notFoundId = 999; return builder - .given("product 999 not found") - .uponReceiving("GET /products/999") - .path("/products/999") + .given("product not found") + .uponReceiving("GET eine Produktnummer die nicht existiert") + .pathFromProviderState("/product-api/products/${notFoundId}", "/product-api/products/%d".formatted(notFoundId)) .method("GET") .willRespondWith() .status(404) diff --git a/product-backend/src/test/resources/pacts/order-backend-product-backend.json b/pacts/order-backend-product-backend.json similarity index 80% rename from product-backend/src/test/resources/pacts/order-backend-product-backend.json rename to pacts/order-backend-product-backend.json index 2984c6e..6a794c6 100644 --- a/product-backend/src/test/resources/pacts/order-backend-product-backend.json +++ b/pacts/order-backend-product-backend.json @@ -3,21 +3,6 @@ "name": "order-backend" }, "interactions": [ - { - "description": "GET /products/999", - "providerStates": [ - { - "name": "product 999 not found" - } - ], - "request": { - "method": "GET", - "path": "/products/999" - }, - "response": { - "status": 404 - } - }, { "description": "GET ein Product das existiert", "providerStates": [ @@ -32,16 +17,16 @@ "generators": { "path": { "dataType": "STRING", - "expression": "/products/${id}", + "expression": "/product-api/products/${id}", "type": "ProviderState" } }, "method": "GET", - "path": "/products/1" + "path": "/product-api/products/1" }, "response": { "body": { - "currency": "USD", + "currency": "EUR", "id": 1, "name": "TestProduct", "price": 19.99 @@ -96,11 +81,33 @@ }, "status": 200 } + }, + { + "description": "GET eine Produktnummer die nicht existiert", + "providerStates": [ + { + "name": "product not found" + } + ], + "request": { + "generators": { + "path": { + "dataType": "STRING", + "expression": "/product-api/products/${notFoundId}", + "type": "ProviderState" + } + }, + "method": "GET", + "path": "/product-api/products/999" + }, + "response": { + "status": 404 + } } ], "metadata": { "pact-jvm": { - "version": "4.6.11" + "version": "4.6.17" }, "pactSpecification": { "version": "3.0.0" diff --git a/pom.xml b/pom.xml index 4122983..1b88660 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ 3.5.6 21 - 4.6.11 + 4.6.17 diff --git a/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java b/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java index d0c3c68..f52955a 100644 --- a/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java +++ b/product-backend/src/test/java/auftragsverwaltung/provider/PactProviderVerificationTest.java @@ -28,8 +28,7 @@ @Provider("product-backend") @Consumer("order-backend") -//@PactFolder("../order-backend/target/pacts") -@PactFolder("/pacts") +@PactFolder("../pacts") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ProductApp.class) @ExtendWith(PactVerificationSpring6Provider.class) class PactProviderVerificationTest { @@ -42,40 +41,20 @@ class PactProviderVerificationTest { @BeforeEach void setup(@NotNull PactVerificationContext ctx) { - ctx.setTarget(new HttpTestTarget("localhost", port, "/product-api")); + ctx.setTarget(new HttpTestTarget("localhost", port)); } @State("product exists") void setupUpProductExists(Map params) { Integer id = Integer.valueOf(params.getOrDefault("id", 1).toString()); - - System.err.println("ID: "+ id); //ID: 1 found! - /** - * 1) Verifying a pact between order-backend and product-backend - GET ein Product das existiert: has status code 200 - * - * 1.1) status: expected status of 200 but was 404 - * - * 1.2) body: $ Actual map is missing the following keys: currency, id, name, price - * - * { - * - "currency": "USD", - * - "id": 1, - * - "name": "TestProduct", - * - "price": 19.99 - * + "error": "Not Found", - * + "path": "/product-api/products/", - * + "status": 404, - * + "timestamp": "2025-10-07T07:50:53.601+00:00" - * } - */ - when(productService.getProductById(id)) .thenReturn(Optional.of(new Product(id, "TestProduct", new BigDecimal("19.99"), EUR))); } - @State("product 999 not found") - void setUpProductNotFound() { - when(productService.getProductById(999)) + @State("product not found") + void setUpProductNotFound(Map params) { + Integer id = Integer.valueOf(params.getOrDefault("id", 1).toString()); + when(productService.getProductById(id)) .thenReturn(Optional.empty()); }