Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Technical test cat amania sofiane yousfi #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
10 changes: 9 additions & 1 deletion technical-test-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand All @@ -39,6 +42,11 @@
<version>1.18.18</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package technical.test.api;

import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
@AllArgsConstructor
public class HomePageController {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package technical.test.api;

import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import technical.test.api.dto.ProductDTO;
import technical.test.api.entity.Product;
import technical.test.api.exception.ProductAlreadyExistsException;
import technical.test.api.exception.ProductNotFoundException;
import technical.test.api.service.ProductService;

import java.util.List;
import java.util.stream.Collectors;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;

@RestController
@RequestMapping("/api/v1/products")
@AllArgsConstructor
public class ProductsController {

private ProductService productService;

@ExceptionHandler(ProductAlreadyExistsException.class)
@ResponseStatus(HttpStatus.CONFLICT)
public String onProductAlreadyExistsError(Exception e){
return e.getMessage();
}

@ExceptionHandler(ProductNotFoundException.class)
@ResponseStatus(HttpStatus.NO_CONTENT)
public String onProductNotFoundError(Exception e){
return e.getMessage();
}

@PostMapping("/create")
@ResponseStatus(HttpStatus.CREATED)
public String CreateProduct() throws ProductAlreadyExistsException {
Product p = new Product("REF1", "Couette 160/200", "Auchan", 20);
productService.insertProduct(p);
return "Product with reference " + p.getReference() + " created successfully";
}

@PostMapping(value = "", consumes = APPLICATION_JSON_VALUE, produces = TEXT_PLAIN_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public String insertProduct(@RequestBody ProductDTO productDTO) throws ProductAlreadyExistsException {
Product p = new Product(
productDTO.getReference(),
productDTO.getName(),
productDTO.getBrand(),
productDTO.getPrice()
);

productService.insertProduct(p);
return "Product with reference " + productDTO.getReference() + " created successfully";
}

@CrossOrigin
@GetMapping(value = "", produces = APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public List<ProductDTO> getAllProducts() throws ProductNotFoundException {
List<Product> products = productService.findAllProducts();
if (products.isEmpty()){
throw new ProductNotFoundException("No product found");
}
List<ProductDTO> test = products
.stream()
.map(p -> new ProductDTO(
p.getReference(),
p.getName(),
p.getBrand(),
p.getPrice()
))
.collect(Collectors.toList());
return test;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

@EnableMongoRepositories
@EnableMongoRepositories(basePackages="technical.test.api.repository")
@Configuration
public class MongoDBConfig {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package technical.test.api.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductDTO {
private String reference;
private String name;
private String brand;
private Integer price;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package technical.test.api.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {

@Id
private String id;
@Indexed(unique = true)
private String reference;
private String name;
private String brand;
private Integer price;

public Product(String reference, String name, String brand, Integer price) {
this.reference = reference;
this.name = name;
this.brand = brand;
this.price = price;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package technical.test.api.exception;

public class ProductAlreadyExistsException extends Exception {
public ProductAlreadyExistsException(String s) {
super(s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package technical.test.api.exception;

public class ProductNotFoundException extends Exception {
public ProductNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package technical.test.api.repository;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import technical.test.api.entity.Product;

import java.util.List;

@Repository
public interface ProductRepository extends MongoRepository<Product, String> {

List<Product> findProductsByReference(String reference);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package technical.test.api.service;

import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import technical.test.api.entity.Product;
import technical.test.api.exception.ProductAlreadyExistsException;
import technical.test.api.repository.ProductRepository;

import java.util.List;

@Service
@AllArgsConstructor
public class ProductService {


private final ProductRepository productRepository;

public void insertProduct(Product product) throws ProductAlreadyExistsException {
if (!findProductsByReference(product.getReference()).isEmpty()){
throw new ProductAlreadyExistsException("Product with reference " + product.getReference() + " already exists");
}
productRepository.insert(product);
}

public List<Product> findProductsByReference(String reference){
return productRepository.findProductsByReference(reference);
}

public List<Product> findAllProducts() {
return productRepository.findAll();
}
}
5 changes: 4 additions & 1 deletion technical-test-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
server:
port: 8080
port: 8080

spring.data.mongodb.database: "auchan-test"
spring.data.mongodb.port: 27017
99 changes: 99 additions & 0 deletions technical-test-api/src/test/java/TestProductsController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import technical.test.api.ProductsController;
import technical.test.api.dto.ProductDTO;
import technical.test.api.entity.Product;
import technical.test.api.exception.ProductAlreadyExistsException;
import technical.test.api.service.ProductService;

import java.util.ArrayList;
import java.util.Arrays;

import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ProductsController.class})
@EnableWebMvc
@AutoConfigureMockMvc
public class TestProductsController {

@Autowired
private MockMvc mockMvc;

@MockBean
private ProductService productService;

@Test
public void createProduct_should_return_201() throws Exception {
when(productService.findProductsByReference(anyString())).thenReturn(new ArrayList<>());
doNothing().when(productService).insertProduct(any(Product.class));

mockMvc.perform(post("/api/v1/products/create"))
.andExpect(status().isCreated());
}

@Test
public void insertProduct_should_return_201() throws Exception {
when(productService.findProductsByReference(anyString())).thenReturn(new ArrayList<>());
doNothing().when(productService).insertProduct(any(Product.class));

ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(new ProductDTO("a", "b", "c", 10));

mockMvc.perform(post("/api/v1/products")
.contentType(APPLICATION_JSON_VALUE)
.content(json))
.andExpect(status().isCreated());
}

@Test
public void insertProduct_should_throw_ProductAlreadyExistsException() throws Exception {
doThrow(ProductAlreadyExistsException.class).when(productService).insertProduct(any(Product.class));

ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(new ProductDTO("a", "b", "c", 10));

mockMvc.perform(post("/api/v1/products")
.contentType(APPLICATION_JSON_VALUE)
.content(json))
.andExpect(status().isConflict());
}

@Test
public void getAllProducts_should_return_list_of_products_and_status_200() throws Exception {
when(productService.findAllProducts()).thenReturn(Arrays.asList(
new Product("p1", "n1", "b1", 1),
new Product("p2", "n2", "b2", 1)
));

mockMvc.perform(get("/api/v1/products").contentType(APPLICATION_JSON_VALUE)).andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(2)));
}

@Test
public void getAllProducts_should_return_204_when_ProductNotFoundException() throws Exception {
when(productService.findAllProducts()).thenReturn(new ArrayList<>());

mockMvc.perform(get("/api/v1/products").contentType(APPLICATION_JSON_VALUE)).andDo(print())
.andExpect(status().isNoContent());
}
}
25 changes: 25 additions & 0 deletions technical-test-api/src/test/java/repository/MockMongoConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package repository;

import org.mockito.Mockito;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

@TestConfiguration
public class MockMongoConfig {

@Bean
public MongoTemplate mongoTemplate() {
MongoMappingContext mappingContext = new MongoMappingContext();

MongoConverter mongoConverter = Mockito.mock(MongoConverter.class);
Mockito.when(mongoConverter.getMappingContext()).then(ignoredInvocation -> mappingContext);

MongoTemplate mongoTemplate = Mockito.mock(MongoTemplate.class);
Mockito.when(mongoTemplate.getConverter()).thenReturn(mongoConverter);

return mongoTemplate;
}
}
Loading