diff --git a/pom.xml b/pom.xml
index 9a8d9a8..66c7954 100644
--- a/pom.xml
+++ b/pom.xml
@@ -108,6 +108,12 @@
kotlin-test-junit5
test
+
+ org.mockito.kotlin
+ mockito-kotlin
+ 5.4.0
+ test
+
org.postgresql
postgresql
diff --git a/src/main/kotlin/org/meliapp/backend/controller/MeliSearchController.kt b/src/main/kotlin/org/meliapp/backend/controller/MeliSearchController.kt
index 70831cf..25aba8b 100644
--- a/src/main/kotlin/org/meliapp/backend/controller/MeliSearchController.kt
+++ b/src/main/kotlin/org/meliapp/backend/controller/MeliSearchController.kt
@@ -2,7 +2,7 @@ package org.meliapp.backend.controller
import org.meliapp.backend.dto.ApiResponse
import org.meliapp.backend.dto.meli.MeliSearchResponse
-import org.meliapp.backend.dto.product.ProductResponse
+import org.meliapp.backend.dto.product.ProductDetailsResponse
import org.meliapp.backend.service.MeliSearchService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@@ -17,7 +17,7 @@ class MeliSearchController(private val meliSearchService: MeliSearchService) {
}
@GetMapping("/{id}")
- fun findById(@PathVariable id: String): ResponseEntity> {
+ fun findById(@PathVariable id: String): ResponseEntity> {
return ResponseEntity.ok(ApiResponse(meliSearchService.findById(id)))
}
diff --git a/src/main/kotlin/org/meliapp/backend/controller/PurchaseController.kt b/src/main/kotlin/org/meliapp/backend/controller/PurchaseController.kt
new file mode 100644
index 0000000..2e8270e
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/controller/PurchaseController.kt
@@ -0,0 +1,26 @@
+package org.meliapp.backend.controller
+
+import org.meliapp.backend.dto.ApiResponse
+import org.meliapp.backend.dto.purchase.PurchaseRequest
+import org.meliapp.backend.dto.purchase.PurchaseResponse
+import org.meliapp.backend.service.PurchaseService
+import org.springframework.http.ResponseEntity
+import org.springframework.web.bind.annotation.*
+
+@RestController
+@RequestMapping("/api/purchases")
+class PurchaseController(
+ private val purchaseService: PurchaseService
+) {
+
+ @PostMapping
+ fun buy(@RequestBody purchaseRequest: PurchaseRequest): ResponseEntity> =
+ ResponseEntity.ok(ApiResponse(purchaseService.buy(purchaseRequest)))
+
+
+ @GetMapping
+ fun purchases(): ResponseEntity>> =
+ ResponseEntity.ok(ApiResponse(purchaseService.purchases()))
+
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/org/meliapp/backend/dto/meli/MeliSearchResponse.kt b/src/main/kotlin/org/meliapp/backend/dto/meli/MeliSearchResponse.kt
index c9a4a67..0f636ea 100644
--- a/src/main/kotlin/org/meliapp/backend/dto/meli/MeliSearchResponse.kt
+++ b/src/main/kotlin/org/meliapp/backend/dto/meli/MeliSearchResponse.kt
@@ -2,10 +2,10 @@ package org.meliapp.backend.dto.meli
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
-import org.meliapp.backend.dto.product.ProductResponse
+import org.meliapp.backend.dto.product.ProductListResponse
data class MeliSearchResponse @JsonCreator constructor(
- val results: List = emptyList(),
+ val results: List = emptyList(),
val filters: List = emptyList(),
@JsonProperty(value = "available_filters")
val availableFilters: List = emptyList(),
diff --git a/src/main/kotlin/org/meliapp/backend/dto/product/ProductDetailsResponse.kt b/src/main/kotlin/org/meliapp/backend/dto/product/ProductDetailsResponse.kt
new file mode 100644
index 0000000..beddeaf
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/dto/product/ProductDetailsResponse.kt
@@ -0,0 +1,25 @@
+package org.meliapp.backend.dto.product
+
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.math.BigDecimal
+
+data class ProductDetailsResponse(
+ @JsonProperty("id")
+ val meliId: String,
+ val title: String,
+ val price: BigDecimal,
+ val thumbnail: String,
+ val pictures: List,
+ var description: String?,
+
+ )
+
+data class DescriptionResponse(
+ @JsonProperty("plain_text")
+ val description: String
+)
+
+data class PicturesListResponse @JsonCreator constructor(
+ val url: String,
+)
\ No newline at end of file
diff --git a/src/main/kotlin/org/meliapp/backend/dto/product/ProductResponse.kt b/src/main/kotlin/org/meliapp/backend/dto/product/ProductListResponse.kt
similarity index 89%
rename from src/main/kotlin/org/meliapp/backend/dto/product/ProductResponse.kt
rename to src/main/kotlin/org/meliapp/backend/dto/product/ProductListResponse.kt
index 8a1acd0..8771e03 100644
--- a/src/main/kotlin/org/meliapp/backend/dto/product/ProductResponse.kt
+++ b/src/main/kotlin/org/meliapp/backend/dto/product/ProductListResponse.kt
@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import java.math.BigDecimal
-data class ProductResponse @JsonCreator constructor(
+data class ProductListResponse @JsonCreator constructor(
@JsonProperty("id")
val id: String = "",
@JsonProperty("title")
diff --git a/src/main/kotlin/org/meliapp/backend/dto/product/ProductPurchaseResponse.kt b/src/main/kotlin/org/meliapp/backend/dto/product/ProductPurchaseResponse.kt
new file mode 100644
index 0000000..cad96e4
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/dto/product/ProductPurchaseResponse.kt
@@ -0,0 +1,12 @@
+package org.meliapp.backend.dto.product
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.math.BigDecimal
+
+data class ProductPurchaseResponse(
+ @JsonProperty("meli_id")
+ val meliId: String,
+ val title: String,
+ val thumbnail: String,
+ val price: BigDecimal,
+)
diff --git a/src/main/kotlin/org/meliapp/backend/dto/purchase/PurchaseRequest.kt b/src/main/kotlin/org/meliapp/backend/dto/purchase/PurchaseRequest.kt
new file mode 100644
index 0000000..bb55ea1
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/dto/purchase/PurchaseRequest.kt
@@ -0,0 +1,9 @@
+package org.meliapp.backend.dto.purchase
+
+import com.fasterxml.jackson.annotation.JsonProperty
+
+data class PurchaseRequest(
+ @JsonProperty(value = "meli_id")
+ val meliId: String,
+ val quantity: Int
+)
diff --git a/src/main/kotlin/org/meliapp/backend/dto/purchase/PurchaseResponse.kt b/src/main/kotlin/org/meliapp/backend/dto/purchase/PurchaseResponse.kt
new file mode 100644
index 0000000..5875764
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/dto/purchase/PurchaseResponse.kt
@@ -0,0 +1,15 @@
+package org.meliapp.backend.dto.purchase
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import org.meliapp.backend.dto.product.ProductPurchaseResponse
+import java.math.BigDecimal
+import java.time.LocalDateTime
+
+data class PurchaseResponse(
+ val id: Long,
+ val quantity: Int,
+ val product: ProductPurchaseResponse,
+ @JsonProperty("created_at")
+ val createdAt: LocalDateTime,
+ val total: BigDecimal,
+)
diff --git a/src/main/kotlin/org/meliapp/backend/model/Purchase.kt b/src/main/kotlin/org/meliapp/backend/model/Purchase.kt
new file mode 100644
index 0000000..f121ba6
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/model/Purchase.kt
@@ -0,0 +1,21 @@
+package org.meliapp.backend.model
+
+import jakarta.persistence.*
+import org.hibernate.annotations.CreationTimestamp
+import java.math.BigDecimal
+import java.time.LocalDateTime
+
+@Entity
+class Purchase {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ var id: Long = 0
+ @ManyToOne
+ lateinit var user: User
+ @ManyToOne
+ lateinit var product: Product
+ var quantity: Int = 0
+ @CreationTimestamp
+ var purchaseDate: LocalDateTime = LocalDateTime.now()
+ var totalPrice: BigDecimal = BigDecimal.ZERO
+}
\ No newline at end of file
diff --git a/src/main/kotlin/org/meliapp/backend/repository/PurchaseRepository.kt b/src/main/kotlin/org/meliapp/backend/repository/PurchaseRepository.kt
new file mode 100644
index 0000000..1fe2399
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/repository/PurchaseRepository.kt
@@ -0,0 +1,11 @@
+package org.meliapp.backend.repository
+
+import org.meliapp.backend.model.Purchase
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.data.jpa.repository.Query
+import org.springframework.data.repository.query.Param
+
+interface PurchaseRepository : JpaRepository {
+ @Query("SELECT p FROM Purchase p WHERE p.user.id = :id")
+ fun findByUserId(@Param("id") id: Long): List
+}
\ No newline at end of file
diff --git a/src/main/kotlin/org/meliapp/backend/service/BookmarkService.kt b/src/main/kotlin/org/meliapp/backend/service/BookmarkService.kt
index 149a15d..c0b5f81 100644
--- a/src/main/kotlin/org/meliapp/backend/service/BookmarkService.kt
+++ b/src/main/kotlin/org/meliapp/backend/service/BookmarkService.kt
@@ -6,17 +6,14 @@ import org.meliapp.backend.dto.bookmark.BookmarkRequestBody
import org.meliapp.backend.dto.bookmark.BookmarkSummary
import org.meliapp.backend.exception.apc.BookmarkNotFoundException
import org.meliapp.backend.model.Bookmark
-import org.meliapp.backend.model.Product
import org.meliapp.backend.repository.BookmarkRepository
-import org.meliapp.backend.repository.ProductRepository
import org.springframework.stereotype.Service
@Service
class BookmarkService(
- private val meliSearchService: MeliSearchService,
private val authService: AuthService,
private val bookmarkRepository: BookmarkRepository,
- private val productRepository: ProductRepository,
+ private val productService: ProductService,
) {
fun getUserBookmarks(): List {
@@ -47,18 +44,8 @@ class BookmarkService(
@Transactional
fun bookmarkProduct(request: BookmarkRequestBody): BookmarkDetails {
val currentUser = authService.getUserAuthenticated()
- val productResponse = meliSearchService.findById(request.meliId)
-
- val savedProduct = productRepository
- .findByMeliId(productResponse.id)
- .orElseGet {
- productRepository.save(Product().apply {
- meliId = productResponse.id
- title = productResponse.title
- thumbnail = productResponse.thumbnail
- price = productResponse.price
- })
- }
+
+ val savedProduct = productService.findByMeliId(request.meliId)
val bookmark = Bookmark().apply {
product = savedProduct
diff --git a/src/main/kotlin/org/meliapp/backend/service/MeliSearchService.kt b/src/main/kotlin/org/meliapp/backend/service/MeliSearchService.kt
index 27ed1eb..e84966f 100644
--- a/src/main/kotlin/org/meliapp/backend/service/MeliSearchService.kt
+++ b/src/main/kotlin/org/meliapp/backend/service/MeliSearchService.kt
@@ -1,7 +1,8 @@
package org.meliapp.backend.service
import org.meliapp.backend.dto.meli.MeliSearchResponse
-import org.meliapp.backend.dto.product.ProductResponse
+import org.meliapp.backend.dto.product.DescriptionResponse
+import org.meliapp.backend.dto.product.ProductDetailsResponse
import org.meliapp.backend.exception.apc.ProductNotFoundException
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.ParameterizedTypeReference
@@ -36,7 +37,7 @@ class MeliSearchService(
}
- fun findById(id: String): ProductResponse {
+ fun findById(id: String): ProductDetailsResponse {
val queryString = "/items/${id}"
return restClient
@@ -45,7 +46,16 @@ class MeliSearchService(
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus({ it.is4xxClientError }) { _, _ -> throw ProductNotFoundException(id) }
- .body(object : ParameterizedTypeReference() {})!!
+ .body(object : ParameterizedTypeReference() {})!!
+ .also {
+ it.description = restClient
+ .get()
+ .uri("$queryString/description")
+ .accept(MediaType.APPLICATION_JSON)
+ .retrieve()
+ .body(object : ParameterizedTypeReference() {})
+ ?.description
+ }
}
diff --git a/src/main/kotlin/org/meliapp/backend/service/ProductService.kt b/src/main/kotlin/org/meliapp/backend/service/ProductService.kt
new file mode 100644
index 0000000..e91c7f2
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/service/ProductService.kt
@@ -0,0 +1,32 @@
+package org.meliapp.backend.service
+
+import org.meliapp.backend.model.Product
+import org.meliapp.backend.repository.ProductRepository
+import org.springframework.stereotype.Service
+import java.util.*
+
+@Service
+class ProductService(
+ private val productRepository: ProductRepository,
+ private val meliSearchService: MeliSearchService
+) {
+
+ fun findByMeliId(id: String): Product {
+ return productRepository.findByMeliId(id)
+ .orElseGet {
+ meliSearchService.findById(id).let {
+ productRepository.save(Product().apply {
+ title = it.title
+ price = it.price
+ thumbnail = it.thumbnail
+ meliId = id
+ })
+ }
+ }
+ }
+
+ fun findByMeliIdOld(meliId: String): Optional {
+ return productRepository.findByMeliId(meliId)
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/org/meliapp/backend/service/PurchaseService.kt b/src/main/kotlin/org/meliapp/backend/service/PurchaseService.kt
new file mode 100644
index 0000000..4f31a21
--- /dev/null
+++ b/src/main/kotlin/org/meliapp/backend/service/PurchaseService.kt
@@ -0,0 +1,54 @@
+package org.meliapp.backend.service
+
+import org.meliapp.backend.dto.product.ProductPurchaseResponse
+import org.meliapp.backend.dto.purchase.PurchaseRequest
+import org.meliapp.backend.dto.purchase.PurchaseResponse
+import org.meliapp.backend.model.Product
+import org.meliapp.backend.model.Purchase
+import org.meliapp.backend.repository.PurchaseRepository
+import org.springframework.stereotype.Service
+import java.math.BigDecimal
+
+@Service
+class PurchaseService(
+ private val purchaseRepository: PurchaseRepository,
+ private val authService: AuthService,
+ private val productService: ProductService
+) {
+
+ fun buy(purchaseRequest: PurchaseRequest): PurchaseResponse =
+ toPurchaseResponse(
+ purchaseRepository.save(
+ Purchase().apply {
+ this.user = authService.getUserAuthenticated()
+ this.product = productService.findByMeliId(purchaseRequest.meliId)
+ quantity = purchaseRequest.quantity
+ totalPrice = product.price * BigDecimal(quantity)
+ }
+ )
+ )
+
+ fun purchases(): List =
+ purchaseRepository
+ .findByUserId(authService.getUserAuthenticated().id)
+ .map { toPurchaseResponse(it) }
+
+ private fun toPurchaseResponse(purchase: Purchase): PurchaseResponse =
+ PurchaseResponse(
+ purchase.id,
+ purchase.quantity,
+ toProductPurchaseResponse(purchase.product),
+ purchase.purchaseDate,
+ purchase.totalPrice
+ )
+
+ private fun toProductPurchaseResponse(product: Product): ProductPurchaseResponse =
+ ProductPurchaseResponse(
+ product.meliId,
+ product.title,
+ product.thumbnail,
+ product.price
+ )
+
+
+}
\ No newline at end of file
diff --git a/src/test/kotlin/org/meliapp/backend/service/BookmarkServiceUnitTest.kt b/src/test/kotlin/org/meliapp/backend/service/BookmarkServiceUnitTest.kt
index 3d4a8c2..19cc35a 100644
--- a/src/test/kotlin/org/meliapp/backend/service/BookmarkServiceUnitTest.kt
+++ b/src/test/kotlin/org/meliapp/backend/service/BookmarkServiceUnitTest.kt
@@ -6,30 +6,24 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.meliapp.backend.dto.bookmark.BookmarkRequestBody
-import org.meliapp.backend.dto.product.ProductResponse
import org.meliapp.backend.exception.apc.BookmarkNotFoundException
import org.meliapp.backend.exception.apc.ProductNotFoundException
import org.meliapp.backend.model.Bookmark
import org.meliapp.backend.model.Product
import org.meliapp.backend.model.User
import org.meliapp.backend.repository.BookmarkRepository
-import org.meliapp.backend.repository.ProductRepository
-import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.InjectMocks
import org.mockito.Mock
-import org.mockito.Mockito.*
+import org.mockito.kotlin.*
import org.springframework.test.context.junit.jupiter.SpringExtension
-import java.math.BigDecimal
import java.util.*
-import org.mockito.Mockito.`when` as whenever
@ExtendWith(SpringExtension::class)
class BookmarkServiceUnitTest {
- @Mock
- private lateinit var meliSearchService: MeliSearchService
-
@Mock
private lateinit var authService: AuthService
@@ -37,7 +31,7 @@ class BookmarkServiceUnitTest {
private lateinit var bookmarkRepository: BookmarkRepository
@Mock
- private lateinit var productRepository: ProductRepository
+ private lateinit var productService: ProductService
@InjectMocks
private lateinit var bookmarkService: BookmarkService
@@ -47,24 +41,15 @@ class BookmarkServiceUnitTest {
// Arrange
val bookmarkRequest = BookmarkRequestBody("meli_id", 5, "comment")
- val productResponse = ProductResponse(
- id = bookmarkRequest.meliId,
- price = BigDecimal(1),
- title = "product_title",
- availableQuantity = 1,
- thumbnail = "product_thumbnail",
- )
-
- val mockUser = mock(User::class.java)
- val mockProduct = mock(Product::class.java)
+ val mockUser: User = mock()
+ val mockProduct: Product = mock()
whenever(mockUser.id).thenReturn(1L)
whenever(mockProduct.meliId).thenReturn("meli_id")
whenever(mockProduct.title).thenReturn("product_title")
whenever(mockProduct.thumbnail).thenReturn("product_thumbnail")
- whenever(meliSearchService.findById(bookmarkRequest.meliId)).thenReturn(productResponse)
- whenever(productRepository.findByMeliId(bookmarkRequest.meliId)).thenReturn(Optional.of(mockProduct))
+ whenever(productService.findByMeliId(bookmarkRequest.meliId)).thenReturn(mockProduct)
whenever(authService.getUserAuthenticated()).thenReturn(mockUser)
// Act
@@ -77,52 +62,18 @@ class BookmarkServiceUnitTest {
// Verify
verify(bookmarkRepository, times(1)).save(any())
- verify(productRepository, times(0)).save(any())
-
- }
-
- @Test
- fun `should not save an already persisted product`() {
- // Arrange
- val bookmarkRequest = BookmarkRequestBody("meli_id", 5, "comment")
-
- val productResponse = ProductResponse(
- id = bookmarkRequest.meliId,
- price = BigDecimal(1),
- title = "product_title",
- availableQuantity = 1,
- thumbnail = "product_thumbnail",
- )
-
- val mockUser = mock(User::class.java)
- val mockProduct = mock(Product::class.java)
- whenever(mockUser.id).thenReturn(1L)
- whenever(mockProduct.meliId).thenReturn("meli_id")
- whenever(mockProduct.title).thenReturn("product_title")
- whenever(mockProduct.thumbnail).thenReturn("product_thumbnail")
-
- whenever(meliSearchService.findById(bookmarkRequest.meliId)).thenReturn(productResponse)
- whenever(productRepository.findByMeliId(bookmarkRequest.meliId)).thenReturn(Optional.empty())
- whenever(productRepository.save(any())).thenReturn(mockProduct)
- whenever(authService.getUserAuthenticated()).thenReturn(mockUser)
-
- // Act
- bookmarkService.bookmarkProduct(bookmarkRequest)
-
- // Verify
- verify(productRepository, times(1)).save(any())
}
@Test
fun `should throw exception when bookmarking with wrong meli id`() {
// Arrange
val bookmarkRequest = BookmarkRequestBody("meli_id", 5, "comment")
- val mockUser = mock(User::class.java)
+ val mockUser: User = mock()
whenever(mockUser.id).thenReturn(1L)
- whenever(meliSearchService.findById(bookmarkRequest.meliId)).thenThrow(ProductNotFoundException::class.java)
whenever(authService.getUserAuthenticated()).thenReturn(mockUser)
+ whenever(productService.findByMeliId(anyString())).thenThrow(ProductNotFoundException::class.java)
// Act and Assert
assertThrows { bookmarkService.bookmarkProduct(bookmarkRequest) }
@@ -131,7 +82,7 @@ class BookmarkServiceUnitTest {
@Test
fun `should return a list of bookmarks from user`() {
// Arrange
- val mockUser = mock(User::class.java)
+ val mockUser: User = mock()
whenever(mockUser.id).thenReturn(1L)
whenever(authService.getUserAuthenticated()).thenReturn(mockUser)
@@ -147,8 +98,8 @@ class BookmarkServiceUnitTest {
// Arrange
val bookmarkId = 1L
val userId = 1L
- val bookmark = mock(Bookmark::class.java)
- val user = mock(User::class.java)
+ val bookmark: Bookmark = mock()
+ val user: User = mock()
whenever(user.id).thenReturn(userId)
whenever(authService.getUserAuthenticated()).thenReturn(user)
@@ -167,7 +118,7 @@ class BookmarkServiceUnitTest {
// Arrange
val bookmarkId = 1L
val userId = 1L
- val user = mock(User::class.java)
+ val user: User = mock()
whenever(bookmarkRepository.findByIdAndUserId(userId, bookmarkId)).thenReturn(Optional.empty())
whenever(user.id).thenReturn(userId)
@@ -181,8 +132,8 @@ class BookmarkServiceUnitTest {
fun `should edit bookmark`() {
// Arrange
val request = BookmarkRequestBody("meli_id", 4, "updated comment")
- val mockUser = mock(User::class.java)
- val mockProduct = mock(Product::class.java)
+ val mockUser: User = mock()
+ val mockProduct: Product = mock()
val bookmark = Bookmark().apply {
id = 1L
diff --git a/src/test/kotlin/org/meliapp/backend/service/MeliSearchServiceUnitTest.kt b/src/test/kotlin/org/meliapp/backend/service/MeliSearchServiceUnitTest.kt
index 1426029..2f131be 100644
--- a/src/test/kotlin/org/meliapp/backend/service/MeliSearchServiceUnitTest.kt
+++ b/src/test/kotlin/org/meliapp/backend/service/MeliSearchServiceUnitTest.kt
@@ -5,7 +5,6 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.meliapp.backend.dto.meli.MeliSearchResponse
-import org.meliapp.backend.dto.product.ProductResponse
import org.meliapp.backend.exception.apc.ProductNotFoundException
import org.mockito.MockitoAnnotations
import org.springframework.beans.factory.annotation.Autowired
@@ -21,7 +20,6 @@ import org.springframework.test.web.client.match.MockRestRequestMatchers.request
import org.springframework.test.web.client.response.MockRestResponseCreators.withResourceNotFound
import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess
import org.springframework.web.client.RestClient
-import java.math.BigDecimal
import kotlin.test.assertEquals
@@ -75,21 +73,29 @@ class MeliSearchServiceUnitTest {
fun `find by id should return a product response`() {
val id = "MLA123"
val expectedUri = "/items/$id"
-
- val mockResponse = ProductResponse(id, "", BigDecimal.valueOf(0), "", 0)
+ val expectedDescriptionUri = "$expectedUri/description"
server.expect(requestTo(expectedUri))
.andExpect(method(HttpMethod.GET))
.andRespond(
withSuccess(
- "{ \"id\": \"$id\", \"title\": \"\", \"price\": 0, \"thumbnail\": \"\", \"available_quantity\": 0 }",
+ "{ \"id\": \"$id\", \"title\": \"\", \"price\": 0, \"thumbnail\": \"\", \"available_quantity\": 0, \"pictures\": [] }",
+ MediaType.APPLICATION_JSON
+ )
+ )
+
+ server.expect(requestTo(expectedDescriptionUri))
+ .andExpect(method(HttpMethod.GET))
+ .andRespond(
+ withSuccess(
+ "{ \"plain_text\": \"description\"}",
MediaType.APPLICATION_JSON
)
)
val response = meliSearchService.findById(id)
- assertEquals(mockResponse, response)
+ assertEquals(id, response.meliId)
}
@Test
diff --git a/src/test/kotlin/org/meliapp/backend/service/ProductServiceUnitTest.kt b/src/test/kotlin/org/meliapp/backend/service/ProductServiceUnitTest.kt
new file mode 100644
index 0000000..c0c504a
--- /dev/null
+++ b/src/test/kotlin/org/meliapp/backend/service/ProductServiceUnitTest.kt
@@ -0,0 +1,91 @@
+package org.meliapp.backend.service
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.api.extension.ExtendWith
+import org.meliapp.backend.dto.product.ProductDetailsResponse
+import org.meliapp.backend.exception.apc.ProductNotFoundException
+import org.meliapp.backend.model.Product
+import org.meliapp.backend.repository.ProductRepository
+import org.mockito.InjectMocks
+import org.mockito.Mock
+import org.mockito.kotlin.*
+import org.springframework.test.context.junit.jupiter.SpringExtension
+import java.math.BigDecimal
+import java.util.*
+
+@ExtendWith(SpringExtension::class)
+class ProductServiceUnitTest {
+
+ @Mock
+ private lateinit var meliSearchService: MeliSearchService
+
+ @Mock
+ private lateinit var productRepository: ProductRepository
+
+ @InjectMocks
+ private lateinit var productService: ProductService
+
+ @Test
+ fun `can find a product using it's meli id locally`() {
+ // Arrange
+ val product: Product = mock()
+ val meliId = "MELI_ID"
+
+ whenever(product.meliId).thenReturn(meliId)
+ whenever(productRepository.findByMeliId(meliId)).thenReturn(Optional.of(product))
+
+ // Act
+ val result = productService.findByMeliIdOld(meliId)
+
+ // Assert
+ assertTrue(result.isPresent)
+ assertEquals(meliId, result.get().meliId)
+
+ }
+
+ @Test
+ fun `should use search service when cannot find product locally`() {
+ val meliId = "MELI_ID"
+
+ val product = Product().apply {
+ title = "title"
+ thumbnail = "thumbnail"
+ price = BigDecimal(999)
+ this.meliId = meliId
+ }
+
+ val productResponse = ProductDetailsResponse(
+ meliId = meliId,
+ thumbnail = product.thumbnail,
+ price = product.price,
+ title = product.title,
+ pictures = listOf(),
+ description = ""
+
+ )
+
+ whenever(productRepository.findByMeliId(any())).thenReturn(Optional.empty())
+ whenever(meliSearchService.findById(any())).thenReturn(productResponse)
+ whenever(productRepository.save(any())).thenReturn(product)
+
+ val result = productService.findByMeliId(meliId)
+
+ assertEquals(product.id, result.id)
+ verify(productRepository, times(1)).save(any())
+ }
+
+ @Test
+ fun `should throw an exception when the cannot find the product with the given id`() {
+ val meliId = "MELI_ID"
+
+ whenever(productRepository.findByMeliId(meliId)).thenReturn(Optional.empty())
+ whenever(meliSearchService.findById(meliId)).thenThrow(ProductNotFoundException::class.java)
+
+ assertThrows { productService.findByMeliId(meliId) }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/test/kotlin/org/meliapp/backend/service/PurchaseServiceUnitTest.kt b/src/test/kotlin/org/meliapp/backend/service/PurchaseServiceUnitTest.kt
new file mode 100644
index 0000000..41e7a21
--- /dev/null
+++ b/src/test/kotlin/org/meliapp/backend/service/PurchaseServiceUnitTest.kt
@@ -0,0 +1,82 @@
+package org.meliapp.backend.service
+
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.meliapp.backend.dto.purchase.PurchaseRequest
+import org.meliapp.backend.model.Product
+import org.meliapp.backend.model.Purchase
+import org.meliapp.backend.model.User
+import org.meliapp.backend.repository.PurchaseRepository
+import org.mockito.InjectMocks
+import org.mockito.Mock
+import org.mockito.kotlin.*
+import org.springframework.test.context.junit.jupiter.SpringExtension
+import java.math.BigDecimal
+import kotlin.test.assertEquals
+
+@ExtendWith(SpringExtension::class)
+class PurchaseServiceUnitTest {
+
+ @Mock
+ lateinit var purchaseRepository: PurchaseRepository
+
+ @Mock
+ lateinit var productService: ProductService
+
+ @Mock
+ lateinit var authService: AuthService
+
+ @InjectMocks
+ lateinit var purchaseService: PurchaseService
+
+ @BeforeEach
+ fun setup() {
+ val user: User = mock()
+ whenever(user.id).thenReturn(1)
+ whenever(authService.getUserAuthenticated()).thenReturn(user)
+ }
+
+ @AfterEach
+ fun cleanup() {
+ reset(purchaseRepository, productService, authService)
+ }
+
+ @Test
+ fun `can buy an existing product`() {
+ val meliId = "MELI_ID"
+ val productTitle = "TITLE"
+ val thumbnail = "THUMBNAIL"
+ val product: Product = mock()
+
+ whenever(product.meliId).thenReturn(meliId)
+ whenever(product.price).thenReturn(BigDecimal.ZERO)
+ whenever(product.title).thenReturn(productTitle)
+ whenever(product.thumbnail).thenReturn(thumbnail)
+ whenever(productService.findByMeliId(meliId)).thenReturn(product)
+ whenever(purchaseRepository.save(any())).thenAnswer { it.getArgument(0) }
+
+ val result = purchaseService.buy(PurchaseRequest(meliId, 1))
+
+ assertEquals(meliId, result.product.meliId)
+ assertEquals(productTitle, result.product.title)
+ assertEquals(thumbnail, result.product.thumbnail)
+ assertEquals(BigDecimal.ZERO, result.product.price)
+ assertEquals(1, result.quantity)
+ assertEquals(BigDecimal.ZERO, result.total)
+
+ verify(purchaseRepository, times(1)).save(any())
+
+ }
+
+ @Test
+ fun `can get all the purchases from current user`() {
+ whenever(purchaseRepository.findByUserId(any())).thenReturn(listOf())
+
+ val result = purchaseService.purchases()
+
+ assertEquals(0, result.size)
+ }
+
+}
\ No newline at end of file