diff --git a/README.md b/README.md
index 91bc1cd..cb70863 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,33 @@
-# web-price
+Prices web service (web-price)
+===================================
+Web service for obtaining the price of a product given a date, the product id and the brand id.
+
+## Requirements
+
+- Java 21
+- Maven
+
+## Development
+
+1. Local installation of the project
+ ```
+ mvn clean install
+ ```
+2. Run the app from the project root with:
+ ```
+ mvn clean spring-boot:run -pl boot -Dspring-boot.run.profiles=local
+ ```
+ or navigating to the `boot` directory of the project:
+ ```
+ mvn clean spring-boot:run -Dspring-boot.run.profiles=local
+ ```
+3. Service should be ready to receive requests at http://localhost:8080/price with OpenAPI docs available at http://localhost:8080/swagger-ui/index.html
+
+## Running Tests
+
+To run all tests in the project:
+
+```bash
+mvn test
+```
diff --git a/application/src/test/java/com/techivw/webprice/application/services/PriceServiceTest.java b/application/src/test/java/com/techivw/webprice/application/services/PriceServiceTest.java
new file mode 100644
index 0000000..2a61a80
--- /dev/null
+++ b/application/src/test/java/com/techivw/webprice/application/services/PriceServiceTest.java
@@ -0,0 +1,77 @@
+package com.techivw.webprice.application.services;
+
+import com.techivw.webprice.application.exceptions.NotFoundException;
+import com.techivw.webprice.application.ports.in.PriceServicePort;
+import com.techivw.webprice.application.ports.out.PriceRepositoryPort;
+import com.techivw.webprice.domain.Price;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(SpringExtension.class)
+class PriceServiceTest {
+
+ @Mock
+ private PriceRepositoryPort priceRepositoryPort;
+
+ private PriceServicePort priceService;
+
+ private final LocalDateTime TEST_DATE = LocalDateTime.parse("2023-01-01T12:00:00");
+ private final Long TEST_PRODUCT_ID = 35455L;
+ private final Long TEST_BRAND_ID = 1L;
+
+ @BeforeEach
+ void setUp() {
+ priceService = new PriceService(priceRepositoryPort);
+ }
+
+ @Test
+ void shouldReturnPriceWhenPriceExists() {
+ Price expectedPrice = createPrice();
+ when(priceRepositoryPort.findHighestPriorityPriceByDateTimeAndProductIdAndBrandId(
+ eq(TEST_DATE), eq(TEST_PRODUCT_ID), eq(TEST_BRAND_ID)))
+ .thenReturn(Optional.of(expectedPrice));
+
+ Price result = priceService.getPriceWithHighestPriorityByDateTimeAndProductIdAndBrandId(
+ TEST_DATE, TEST_PRODUCT_ID, TEST_BRAND_ID);
+
+ assertNotNull(result);
+ assertEquals(expectedPrice.getProductId(), result.getProductId());
+ assertEquals(expectedPrice.getPrice(), result.getPrice());
+ }
+
+ @Test
+ void shouldThrowNotFoundExceptionWhenPriceDoesNotExist() {
+ when(priceRepositoryPort.findHighestPriorityPriceByDateTimeAndProductIdAndBrandId(
+ any(LocalDateTime.class), anyLong(), anyLong()))
+ .thenReturn(Optional.empty());
+
+ assertThrows(NotFoundException.class, () ->
+ priceService.getPriceWithHighestPriorityByDateTimeAndProductIdAndBrandId(
+ TEST_DATE, TEST_PRODUCT_ID, TEST_BRAND_ID));
+ }
+
+ private Price createPrice() {
+ return Price.builder()
+ .brandId(TEST_BRAND_ID)
+ .productId(TEST_PRODUCT_ID)
+ .startDate(TEST_DATE.minusDays(1))
+ .endDate(TEST_DATE.plusDays(1))
+ .priceList(1L)
+ .price(BigDecimal.valueOf(35.50))
+ .priority(1)
+ .currency("EUR")
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/infrastructure/in/rest-api/pom.xml b/infrastructure/in/rest-api/pom.xml
index 9ad6546..de49e08 100644
--- a/infrastructure/in/rest-api/pom.xml
+++ b/infrastructure/in/rest-api/pom.xml
@@ -16,12 +16,6 @@
rest-api
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
com.techivw
domain
@@ -34,16 +28,4 @@
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
- -javaagent:${settings.localRepository}/net/bytebuddy/byte-buddy-agent/1.15.11/byte-buddy-agent-1.15.11.jar
-
-
-
-
-
diff --git a/infrastructure/out/sql-repository/src/test/java/com/techivw/webprice/infrastructure/repositories/adapters/PriceRepositoryAdapterTest.java b/infrastructure/out/sql-repository/src/test/java/com/techivw/webprice/infrastructure/repositories/adapters/PriceRepositoryAdapterTest.java
new file mode 100644
index 0000000..a260ca5
--- /dev/null
+++ b/infrastructure/out/sql-repository/src/test/java/com/techivw/webprice/infrastructure/repositories/adapters/PriceRepositoryAdapterTest.java
@@ -0,0 +1,78 @@
+package com.techivw.webprice.infrastructure.repositories.adapters;
+
+import com.techivw.webprice.domain.Price;
+import com.techivw.webprice.infrastructure.repositories.PriceEntityJPARepository;
+import com.techivw.webprice.infrastructure.repositories.model.PriceEntity;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class PriceRepositoryAdapterTest {
+
+ @Mock
+ private PriceEntityJPARepository repository;
+
+ private PriceRepositoryAdapter adapter;
+
+ private final LocalDateTime TEST_DATE = LocalDateTime.parse("2023-01-01T12:00:00");
+ private final Long TEST_PRODUCT_ID = 35455L;
+ private final Long TEST_BRAND_ID = 1L;
+
+ @BeforeEach
+ void setUp() {
+ adapter = new PriceRepositoryAdapter(repository);
+ }
+
+ @Test
+ void shouldReturnPriceWhenPriceEntityExists() {
+ PriceEntity priceEntity = createSamplePriceEntity();
+ when(repository.findPriceByDateTimeAndProductIdAndBrandId(
+ eq(TEST_DATE), eq(TEST_PRODUCT_ID), eq(TEST_BRAND_ID)))
+ .thenReturn(Optional.of(priceEntity));
+
+ Optional result = adapter.findHighestPriorityPriceByDateTimeAndProductIdAndBrandId(
+ TEST_DATE, TEST_PRODUCT_ID, TEST_BRAND_ID);
+
+ assertTrue(result.isPresent());
+ Price price = result.get();
+ assertEquals(TEST_PRODUCT_ID, price.getProductId());
+ assertEquals(TEST_BRAND_ID, price.getBrandId());
+ assertEquals(BigDecimal.valueOf(35.50), price.getPrice());
+ }
+
+ @Test
+ void shouldReturnEmptyOptionalWhenPriceEntityDoesNotExist() {
+ when(repository.findPriceByDateTimeAndProductIdAndBrandId(
+ any(LocalDateTime.class), anyLong(), anyLong()))
+ .thenReturn(Optional.empty());
+
+ Optional result = adapter.findHighestPriorityPriceByDateTimeAndProductIdAndBrandId(
+ TEST_DATE, TEST_PRODUCT_ID, TEST_BRAND_ID);
+
+ assertFalse(result.isPresent());
+ }
+
+ private PriceEntity createSamplePriceEntity() {
+ PriceEntity entity = new PriceEntity();
+ entity.setBrandId(TEST_BRAND_ID);
+ entity.setProductId(TEST_PRODUCT_ID);
+ entity.setStartDate(TEST_DATE.minusDays(1));
+ entity.setEndDate(TEST_DATE.plusDays(1));
+ entity.setPriceList(1L);
+ entity.setPrice(BigDecimal.valueOf(35.50));
+ entity.setPriority(1);
+ entity.setCurrency("EUR");
+ return entity;
+ }
+}
diff --git a/pom.xml b/pom.xml
index 60e4ead..13ffae0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,5 +51,23 @@
springdoc-openapi-starter-webmvc-ui
2.8.6
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ -javaagent:${settings.localRepository}/net/bytebuddy/byte-buddy-agent/1.15.11/byte-buddy-agent-1.15.11.jar
+
+
+
+
+