From cc01798c14ad855b675f2054831862a3916064e8 Mon Sep 17 00:00:00 2001 From: K Siva Prasad Reddy Date: Fri, 3 Nov 2023 06:44:49 +0530 Subject: [PATCH] Update Steps docs --- .github/workflows/maven.yml | 2 +- .gitignore | 7 +- .sdkmanrc | 2 +- README.md | 7 +- .../catalog/ApplicationTests.java | 11 -- step-1-getting-started.md | 30 +++-- step-2-exploring-the-app.md | 20 ++-- step-3-local-development-environment.md | 107 ++++++++++-------- step-4-connect-to-services.md | 29 +++-- step-5-use-reusable-containers.md | 20 ++-- step-6-write-tests.md | 55 ++++++++- 11 files changed, 178 insertions(+), 112 deletions(-) delete mode 100644 src/test/java/com/testcontainers/catalog/ApplicationTests.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 88f838a..dcaca20 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -20,4 +20,4 @@ jobs: cache: 'maven' - name: Test - run: ./mvnw compile + run: ./mvnw test diff --git a/.gitignore b/.gitignore index 5eac309..7c4cba4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -HELP.md target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ @@ -30,4 +29,8 @@ build/ !**/src/test/**/build/ ### VS Code ### -.vscode/ \ No newline at end of file +.vscode/ + +/src/test +!/src/test/java/com/testcontainers/catalog/.keep +!/src/test/resources/logback-test.xml diff --git a/.sdkmanrc b/.sdkmanrc index 817e04b..2183da0 100644 --- a/.sdkmanrc +++ b/.sdkmanrc @@ -1,2 +1,2 @@ java=17.0.8-tem -maven=3.9.4 +maven=3.9.5 diff --git a/README.md b/README.md index aaa02e7..5fae673 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,13 @@ This workshop will explain how to use Testcontainers \([https://www.testcontainers.com](https://www.testcontainers.com)\) in your Java application development process. -We will work with a Spring Boot application and explore: -* Using Testcontainers for provisioning application dependent services like PostgreSQL, Kafka, LocalStack for local development. +We will work with a Spring Boot application and explore how to: +* Use Testcontainers for provisioning application dependent services like PostgreSQL, Kafka, LocalStack for local development +* Use [Testcontainers Desktop](https://testcontainers.com/desktop/) for local development and debugging * Write tests using Testcontainers -* Using [Testcontainers Desktop](https://testcontainers.com/desktop/) for local development and debugging ## Table of contents -* [Introduction](README.md) * [Step 1: Getting Started](step-1-getting-started.md) * [Step 2: Exploring the app](step-2-exploring-the-app.md) * [Step 3: Local Development Environment with Testcontainers](step-3-local-development-environment.md) diff --git a/src/test/java/com/testcontainers/catalog/ApplicationTests.java b/src/test/java/com/testcontainers/catalog/ApplicationTests.java deleted file mode 100644 index 239328d..0000000 --- a/src/test/java/com/testcontainers/catalog/ApplicationTests.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.testcontainers.catalog; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ApplicationTests { - - @Test - void contextLoads() {} -} diff --git a/step-1-getting-started.md b/step-1-getting-started.md index 2b57bd1..cbb9ad5 100644 --- a/step-1-getting-started.md +++ b/step-1-getting-started.md @@ -1,10 +1,15 @@ # Step 1: Getting Started +Before getting started, let's make sure you have everything you need for this workshop. -## Check Java +## Prerequisites + +### Install Java 17 or newer You'll need Java 17 or newer for this workshop. Testcontainers libraries are compatible with Java 8+, but this workshop uses a Spring Boot 3.x application which requires Java 17 or newer. -## Check Docker +I would recommend using [SDKMAN](https://sdkman.io/) to install Java on your machine. + +## Install Docker Make sure you have a Docker environment available on your machine. @@ -35,14 +40,23 @@ Server: Docker Desktop 4.24.2 (124339) ``` ## Install Testcontainers Desktop -Download the latest version of Testcontainers Desktop app from [https://testcontainers.com/desktop/](https://testcontainers.com/desktop/) and install it on your machine. -Once you start the Testcontainers Desktop application, it will automatically detect the container runtimes installed on your system (Docker Desktop, OrbStack, etc) +[Testcontainers Desktop](https://testcontainers.com/desktop/) is a companion app for the open source Testcontainers libraries +that makes local development and testing with real dependencies simple. + +Download the latest version of Testcontainers Desktop app from [https://testcontainers.com/desktop/](https://testcontainers.com/desktop/) +and install it on your machine. + +Once you start the Testcontainers Desktop application, it will automatically detect the container runtimes +installed on your system (Docker Desktop, OrbStack, etc) and allows you to choose which container runtime you want to use by Testcontainers. ## Download the project -Clone the following project from GitHub to your computer: -[https://github.com/testcontainers/java-local-development-workshop](https://github.com/testcontainers/java-local-development-workshop) +Clone the [java-local-development-workshop](https://github.com/testcontainers/java-local-development-workshop) repository from GitHub to your computer: + +```shell +git clone https://github.com/testcontainers/java-local-development-workshop.git +``` ## Compile the project to download the dependencies @@ -58,8 +72,10 @@ This might be helpful if the internet connection at the workshop venue is somewh ```shell docker pull postgres:16-alpine docker pull localstack/localstack:2.3 -docker pull confluentinc/cp-kafka:7.5.0 docker pull wiremock/wiremock:3.2.0-alpine +docker pull confluentinc/cp-kafka:7.5.0 +docker pull confluentinc/cp-schema-registry:7.5.0 +docker pull confluentinc/cp-enterprise-control-center:7.5.0 ``` ### diff --git a/step-2-exploring-the-app.md b/step-2-exploring-the-app.md index 7b7e854..f32946e 100644 --- a/step-2-exploring-the-app.md +++ b/step-2-exploring-the-app.md @@ -1,13 +1,11 @@ # Step 2: Exploring the app -The application we are going to work is a microservice based on Spring Boot for managing a catalog of products. +The application we are going to work on is a microservice based on Spring Boot for managing a catalog of products. It provides APIs to save and retrieve the product information. ![Sample App Architecture](assets/tcd-workshop-app.png) -## Storage - -### SQL database with the products +## SQL database with the products When a product is created, we will store the product information in our database. @@ -15,22 +13,26 @@ Our database of choice is PostgreSQL, accessed with Spring Data JPA. Check `com.testcontainers.catalog.domain.internal.ProductRepository`. -### LocalStack +## LocalStack We would like to store the product images in AWS S3 Object storage. -But we will use [LocalStack](https://localstack.cloud/) to emulate the AWS cloud environment locally during local development and testing with Spring Cloud AWS. +We will use [LocalStack](https://localstack.cloud/) to emulate the AWS cloud environment locally during local development and testing with [Spring Cloud AWS](https://awspring.io/). Check `com.testcontainers.catalog.domain.internal.S3FileStorageService`. -### Kafka +## Kafka -When a product image is uploaded to AWS S3, we should publish an event to Kafka. +When a product image is uploaded to AWS S3, an event will be published to Kafka. The kafka event listener will then consume the event and update the product information with the image URL. Check `com.testcontainers.catalog.domain.internal.ProductEventPublisher` and `com.testcontainers.catalog.events.ProductEventListener`. -## API +## External Service Integrations +Our application talks to `inventory-service` to fetch the product availability information. +We will use [WireMock](https://wiremock.org/) to mock the `inventory-service` during local development and testing. + +## API Endpoints The API is a Spring Web REST controller \(`com.testcontainers.catalog.api.ProductController`\) and exposes the following endpoints: diff --git a/step-3-local-development-environment.md b/step-3-local-development-environment.md index b6efe8e..183658b 100644 --- a/step-3-local-development-environment.md +++ b/step-3-local-development-environment.md @@ -1,5 +1,5 @@ # Step 3: Local Development environment with Testcontainers -Our application uses PostgreSQL, Kafka, LocalStack. +Our application uses PostgreSQL, Kafka, and LocalStack. Currently, if you run the `Application.java` from your IDE, you will see the following error: @@ -23,9 +23,10 @@ Consider the following: Process finished with exit code 0 ``` -To run the application locally, we need to provision these services. +To run the application locally, we need to have these services up and running. + Instead of installing these services on our local machine, or using Docker to run these services manually, -we will use [Spring Boot support for Testcontainers at Development Time](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.testcontainers.at-development-time) to provision these services. +we will use [Spring Boot support for Testcontainers at Development Time](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.testcontainers.at-development-time) to provision these services automatically. > **NOTE** > @@ -63,7 +64,8 @@ First, make sure you have the following Testcontainers dependencies in your `pom ``` We will also use **RestAssured** for API testing and **Awaitility** for testing asynchronous processes. -So, add the following dependencies as well + +So, add the following dependencies as well: ```xml @@ -79,6 +81,7 @@ So, add the following dependencies as well ``` ## Create ContainersConfig class under src/test/java +Let's create `ContainersConfig` class under `src/test/java` to configure the required containers. ```java package com.testcontainers.catalog; @@ -131,17 +134,17 @@ public class ContainersConfig { ``` Let's understand what this configuration class does: -* `@TestConfiguration` annotation indicates that this class should be used for Spring Boot test configuration. -* Spring Boot provides ServiceConnection support for JdbcConnectionDetails and KafkaConnectionDetails out-of-the-box. - So, we configured PostgreSQLContainer and KafkaContainer as beans with @ServiceConnection annotation. - This configuration will automatically start these containers and register the DataSource and Kafka connection properties automatically. -* However, Spring Cloud AWS doesn't provide ServiceConnection support out-of-the-box. +* `@TestConfiguration` annotation indicates that this configuration class defines the beans that can be used for Spring Boot tests. +* Spring Boot provides `ServiceConnection` support for `JdbcConnectionDetails` and `KafkaConnectionDetails` out-of-the-box. + So, we configured `PostgreSQLContainer` and `KafkaContainer` as beans with `@ServiceConnection` annotation. + This configuration will automatically start these containers and register the **DataSource** and **Kafka** connection properties automatically. +* Spring Cloud AWS doesn't provide ServiceConnection support out-of-the-box [yet](https://github.com/awspring/spring-cloud-aws/issues/793). But there is support for [Contributing Dynamic Properties at Development Time](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.testcontainers.at-development-time.dynamic-properties). - So, we configured LocalStackContainer as a bean and registered the Spring Cloud AWS configuration properties using DynamicPropertyRegistry. -* We also configured an ApplicationRunner bean to create the AWS resources like S3 bucket upon application startup. + So, we configured `LocalStackContainer` as a bean and registered the Spring Cloud AWS configuration properties using `DynamicPropertyRegistry`. +* We also configured an `ApplicationRunner` bean to create the AWS resources like S3 bucket upon application startup. ## Create TestApplication class under src/test/java -Next, let's create a TestApplication class to start the application with the Testcontainers configuration. +Next, let's create a `TestApplication` class under `src/test/java` to start the application with the Testcontainers configuration. ```java package com.testcontainers.catalog; @@ -151,20 +154,22 @@ import org.springframework.boot.SpringApplication; public class TestApplication { public static void main(String[] args) { - SpringApplication.from(Application::main) + SpringApplication + //note that we are using Application from src/main/java instead of TestApplication from src/test/java + .from(Application::main) .with(ContainersConfig.class) .run(args); } } ``` -Run the TestApplication from our IDE and verify that the application starts successfully. +Run the `TestApplication` from our IDE and verify that the application starts successfully. -Now, you can invoke the APIs using curl or Postman. +Now, you can invoke the APIs using CURL or Postman or any of your favourite HTTP Client tools. ### Create a product ```shell -curl -v --location 'http://localhost:8080/api/products' \ +curl -v -X "GET" 'http://localhost:8080/api/products' \ --header 'Content-Type: application/json' \ --data '{ "code": "P201", @@ -184,7 +189,7 @@ You should get a response similar to the following: ### Upload Product Image ```shell -curl --location 'http://localhost:8080/api/products/P101/image' \ +curl -X "POST" 'http://localhost:8080/api/products/P101/image' \ --form 'file=@"/Users/siva/work/product-p101.jpg"' ``` @@ -197,7 +202,7 @@ You should see a response similar to the following: ### Get a product by code ```shell -curl --location 'http://localhost:8080/api/products/P101' +curl -X "GET" 'http://localhost:8080/api/products/P101' ``` You should be able to see the response similar to the following: @@ -214,7 +219,7 @@ You should be able to see the response similar to the following: } ``` -If you check the application logs, you should see the following logs: +If you check the application logs, you should see the following error in logs: ```shell com.testcontainers.catalog.domain.internal.DefaultProductService - Error while calling inventory service @@ -230,7 +235,8 @@ org.springframework.web.client.ResourceAccessException: I/O error on GET request at java.base/java.util.Optional.map(Optional.java:260) ``` -When we invoke the GET /api/products/{code} API, the application tries to call the inventory service to get the inventory details. +When we invoke the `GET /api/products/{code}` API endpoint, +the application tried to call the inventory service to get the inventory details. As the inventory service is not running, we get the above error. Let's use WireMock to mock the inventory service APIs for our local development and testing. @@ -247,31 +253,6 @@ Add the following dependency to your `pom.xml`: ``` -Next, update the ContainersConfig class to start the WireMock container as well. - -```java -package com.testcontainers.catalog; - -... -... -import org.wiremock.integrations.testcontainers.WireMockContainer; - -@TestConfiguration(proxyBeanMethods = false) -public class ContainersConfig { - - ... - ... - - @Bean - WireMockContainer wiremockServer(DynamicPropertyRegistry registry) { - WireMockContainer wiremockServer = new WireMockContainer("wiremock/wiremock:3.2.0-alpine") - .withMappingFromResource("mocks-config.json"); - registry.add("application.inventory-service-url", wiremockServer::getBaseUrl); - return wiremockServer; - } -} -``` - Create `src/test/resources/mocks-config.json` to define Mock API behaviour. ```json @@ -325,13 +306,41 @@ Create `src/test/resources/mocks-config.json` to define Mock API behaviour. } ``` -Now restart the TestApplication and invoke the GET /api/products/P101 API again. +Next, update the `ContainersConfig` class to configure the `WireMockContainer` as follows: + +```java +package com.testcontainers.catalog; + +... +... +import org.wiremock.integrations.testcontainers.WireMockContainer; + +@TestConfiguration(proxyBeanMethods = false) +public class ContainersConfig { + + ... + ... + + @Bean + WireMockContainer wiremockServer(DynamicPropertyRegistry registry) { + WireMockContainer wiremockServer = new WireMockContainer("wiremock/wiremock:3.2.0-alpine") + .withMappingFromResource("mocks-config.json"); + registry.add("application.inventory-service-url", wiremockServer::getBaseUrl); + return wiremockServer; + } +} +``` + +Once the WireMock server is started, we are registering the WireMock server URL as `application.inventory-service-url`. +So, when we make a call to `inventory-service` from our application, it will call the WireMock server instead. + +Now restart the `TestApplication` and invoke the `GET /api/products/P101` API again. ```shell curl --location 'http://localhost:8080/api/products/P101' ``` -You should the response similar to the following: +You should see the response similar to the following: ```json { @@ -347,7 +356,7 @@ You should the response similar to the following: And there should be no error in the console logs. -Try `curl --location 'http://localhost:8080/api/products/P103'`. +Try `curl -X "GET" 'http://localhost:8080/api/products/P103'`. You should get the following response with `"available":false` because we mocked inventory-service such that the quantity for P103 to be 0. ```json @@ -362,5 +371,7 @@ You should get the following response with `"available":false` because we mocked } ``` +Now we have the working local development environment with PostgreSQL, Kafka, LocalStack, and WireMock. + ### [Next](step-4-connect-to-services.md) diff --git a/step-4-connect-to-services.md b/step-4-connect-to-services.md index 3f576c8..3a0af53 100644 --- a/step-4-connect-to-services.md +++ b/step-4-connect-to-services.md @@ -1,6 +1,6 @@ # Step 4: Connect to services -In the previous step, we managed to run the application and invoke our API endpoints. +In the previous step, we get our application running locally and invoked our API endpoints. What if you want to check the data in the database or the messages in Kafka? @@ -8,21 +8,20 @@ Testcontainers by default start the containers and map the exposed ports on a ra Each time you restart the application, the mapped ports will be different. This is good for testing, but for local development and debugging, it would be convenient to be able to connect on fixed ports. -This is where Testcontainers Desktop comes in handy. +This is where **Testcontainers Desktop** helps you. ## Testcontainers Desktop -https://testcontainers.com/guides/simple-local-development-with-testcontainers-desktop/ +Testcontainers Desktop application provides several features that helps you with local development and debugging. +To learn more about Testcontainers Desktop, check out the [Simple local development with Testcontainers Desktop](https://testcontainers.com/guides/simple-local-development-with-testcontainers-desktop/) guide. -The Testcontainers Desktop app makes it easy to use fixed ports for the container services -so that you can always connect to those services using the same configured fixed port. - -We can use Testcontainers Desktop fixed port support to connect to the PostgreSQL database running as a Docker container. +The Testcontainers Desktop app makes it easy to use fixed ports for your containers, +so that you can always connect to those services using the same fixed port. ## Connect to PostgreSQL database -Click on Testcontainers Desktop → select Services → Open config location. +Click on **Testcontainers Desktop → select Services → Open config location...**. -In the opened directory there would be a `postgres.toml.example` file. Rename it to `postgres.toml` file, -and it should contain the following configuration: +In the opened directory there would be a `postgres.toml.example` file. +Make a copy of it and rename it to `postgres.toml` file and update it with the following content: ```toml ports = [ @@ -43,7 +42,7 @@ psql -h localhost -p 5432 -U test -d test While you can use any of your existing tools to connect to these containerized services, you can also run a helper container that will allow you to connect to the services. -For example, you can start a Kafka Control Center container and connect to the Kafka instance via web browser. +For example, you can start a **Kafka Control Center** container and connect to the Kafka instance via web browser. Let's update `KafkaContainer` configuration in `ContainersConfig` as follows: @@ -146,7 +145,7 @@ We are using the `confluentinc/cp-enterprise-control-center:7.5.0` image to star The Control Center is a web-based tool for managing and monitoring Apache Kafka clusters. Let's map its port 9021 to the host's port 19021. -Create kafka-control-center.toml file in the Testcontainers Desktop config directory with the following content: +Create `kafka-control-center.toml` file in the Testcontainers Desktop config directory with the following content: ```toml ports = [ @@ -159,10 +158,10 @@ Now, if you restart the application, you should be able to connect to the Kafka * Invoke the Product Image Upload API * Go to the Kafka Control Center at http://localhost:19021/ -* Click on the Cluster tab -* Click on the Topics tab +* Click on the **Cluster** tab +* Click on the **Topics** tab * Click on the topic `product-image-updates` -* Click on the Messages tab +* Click on the **Messages** tab You should be able to see the messages sent to Kafka. diff --git a/step-5-use-reusable-containers.md b/step-5-use-reusable-containers.md index 3a1f585..d9f61ef 100644 --- a/step-5-use-reusable-containers.md +++ b/step-5-use-reusable-containers.md @@ -1,15 +1,17 @@ # Step 5: Use Reusable containers During the development, you will keep changing the code and verify the behavior either by running the tests -or running the application locally. Recreating the containers for every code change might slow down -your quick feedback cycle. One technique that you can apply to speed up testing and -local development is using the reusable containers experimental feature. +or running the application locally. Recreating the containers everytime you restart the application +might slow down your quick feedback cycle. + +One technique that you can apply to speed up testing and local development is using the **reusable containers** feature. Since you are using the Testcontainers Desktop, the `testcontainers.reuse.enable` flag is set automatically for your dev environment. -You can enable or disable it by clicking on **Enable reusable containers** option under Preferences. +You can enable or disable it by clicking on **Enable reusable containers** option under **Preferences**. -When the reuse feature is enabled, you only need to configure which containers should be reused using the Testcontainers API. While using Testcontainers for Java you can achieve this using .withReuse(true) as follows: +Once the `reuse` feature is enabled, you need to configure which containers should be reused using the Testcontainers API. +With Testcontainers for Java API, you can achieve this using `.withReuse(true)` as follows: ```java @TestConfiguration(proxyBeanMethods = false) @@ -26,11 +28,11 @@ public class ContainersConfig { } ``` -When you first start the application, the containers will be created. -When you stop the application, the containers will continue to run. -When you restart the application again, the containers will be reused. +* When you first start the application, the containers will be created. +* When you stop the application, the containers will continue to run. +* When you restart the application again, the containers will be reused. -If you no longer want to keep the containers running, then you can remove them by clicking on Testcontainers Desktop → Terminate containers. +If you no longer want to keep the containers running, then you can remove them by clicking on **Testcontainers Desktop → Terminate containers**. ### [Next](step-6-write-tests.md) diff --git a/step-6-write-tests.md b/step-6-write-tests.md index 4526bd4..eb2fc4f 100644 --- a/step-6-write-tests.md +++ b/step-6-write-tests.md @@ -24,8 +24,7 @@ import org.springframework.test.context.ActiveProfiles; @SpringBootTest( webEnvironment = RANDOM_PORT, properties = { - "spring.kafka.consumer.auto-offset-reset=earliest", - "spring.cloud.aws.region.static=us-east-1" + "spring.kafka.consumer.auto-offset-reset=earliest" }) @Import(ContainersConfig.class) public abstract class BaseIntegrationTest { @@ -40,12 +39,12 @@ public abstract class BaseIntegrationTest { } ``` -* We have reused the `ContainersConfig` class that we created in the previous steps. -* We have configured the `spring.kafka.consumer.auto-offset-reset` property to `earliest` to make sure that we can read all the messages from the beginning of the topic. +* We have reused the `ContainersConfig` class that we created in the previous steps to define all the required containers. +* We have configured the `spring.kafka.consumer.auto-offset-reset` property to `earliest` to make sure that we read all the messages from the beginning of the topic. * We have configured the `RestAssured.port` to the dynamic port of the application that is started by Spring Boot. ## First Test - Verify Application Context Starts Successfully -Let's create/update the test class `ApplicationTests` under `src/test/java` with the following test: +Let's create the test class `ApplicationTests` under `src/test/java` with the following test: ```java package com.testcontainers.catalog; @@ -59,6 +58,8 @@ class ApplicationTests extends BaseIntegrationTest { } ``` +If you run this test, it should pass and that means we have successfully configured the application to start with all the required containers. + ## Lets add tests for ProductController API endpoints Before writing the API tests, let's create `src/test/resources/test-data.sql` to insert some test data into the database as follows: @@ -115,6 +116,8 @@ class ProductControllerTest extends BaseIntegrationTest { Next, let's add a test for product image upload API endpoint. +Copy any sample image with name `P101.jpg` into `src/main/resources`. + ```java package com.testcontainers.catalog.api; @@ -170,3 +173,45 @@ class ProductControllerTest extends BaseIntegrationTest { } } ``` + +This test checks the following: +* Before uploading the image, the product image URL is null for the product with code P101. +* Invoke the Product Image Upload API endpoint with the sample image file. +* Assert that the response status is 200 and the response body contains the image file name. +* Assert that the product image URL is updated in the database after the image upload. + +Next, let's add a test for getting the product information by code. + +```java +@Sql("/test-data.sql") +class ProductControllerTest extends BaseIntegrationTest { + @Autowired + ProductService productService; + + @Test + void getProductByCodeSuccessfully() { + String code = "P101"; + + Product product = given().contentType(ContentType.JSON) + .when() + .get("/api/products/{code}", code) + .then() + .statusCode(200) + .extract() + .as(Product.class); + + assertThat(product.code()).isEqualTo(code); + assertThat(product.name()).isEqualTo("Product %s".formatted(code)); + assertThat(product.description()).isEqualTo("Product %s description".formatted(code)); + assertThat(product.price().compareTo(new BigDecimal("34.0"))).isEqualTo(0); + assertThat(product.available()).isTrue(); + } +} +``` + +## Assignment +* Write tests for create product API fails if the payload is invalid. +* Write tests for create product API fails if the product code already exists. +* Write tests for get product by code API fails if the product code does not exist. +* Write tests for get product by code API that returns `"available": false` when WireMock server return quantity=0. +* Write tests for get product by code API that returns `"available": true` from WireMock server throws Exception.