Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-spring -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>7.23.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-junit-platform-engine -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>7.23.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.23.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,22 @@ class ProductsService {
val total: Int
)


/**
* Retrieves a paginated list of products and the total count from the external DummyJSON API.
*
* @param start The starting index (number of products to skip).
* @param size The number of products to retrieve.
* @return [ProductsResult] containing the list of products and the total count.
*/
fun getProductsAndTotal(start: Int, size: Int): ProductsResult {
if (start < 0 || size < 1) return ProductsResult(emptyList(), 0)
val url = UriComponentsBuilder.fromUriString(baseUrl)
.queryParam("skip", start)
.queryParam("limit", size)
.toUriString()
val response = restTemplate.getForObject(url, DummyJsonResponse::class.java)

val products = response?.products?.map {

Copilot AI Jun 26, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If response is null, products becomes null and can cause an unexpected NPE when constructing ProductsResult. Consider using an Elvis operator to default to an empty list: val products = response?.products?.map { ... } ?: emptyList().

Copilot uses AI. Check for mistakes.
Product(
id = it.id.toLong(),
Expand All @@ -50,4 +59,48 @@ class ProductsService {
val total = response?.total ?: 0
return ProductsResult(products, total)
}

/**
* Fetches all products from the external API in batches of 20.
* @return ProductsResult containing all products and the total count.
*/
fun getAllProductsInBatches(): ProductsResult {
val batchSize = 20
val firstBatch = getProductsAndTotal(0, batchSize)
val total = firstBatch.total
val allProducts = mutableListOf<Product>()
allProducts.addAll(firstBatch.products)
var fetched = firstBatch.products.size
while (fetched < total) {
val batch = getProductsAndTotal(fetched, batchSize)
allProducts.addAll(batch.products)
fetched += batch.products.size
if (batch.products.isEmpty()) break // safety check
}
return ProductsResult(allProducts, total)
}



}





















Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.web.client.RestTemplate

@Suppress("SpringJavaInjectionPointsAutowiringInspection")
@SpringBootTest
@AutoConfigureMockMvc
class ProductsControllerTest {
Expand Down Expand Up @@ -56,6 +57,18 @@ class ProductsControllerTest {
.andReturn()
}

// write a test case to handle empty response from the service
@Test
fun `should return empty products when service returns no products`() {
val dummyResponse = ProductsService.DummyJsonResponse(products = emptyList(), total = 0)
Mockito.`when`(
restTemplate.getForObject(Mockito.anyString(), Mockito.eq(ProductsService.DummyJsonResponse::class.java))
).thenReturn(dummyResponse)

mockMvc.perform(get("/api/v1/products?start=0&size=10").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk)
.andExpect(jsonPath("$.products").isEmpty)
.andExpect(jsonPath("$.total").value(0))
}
}