diff --git a/Documentation/Diagrams/Entity Relationship Diagram.md b/Documentation/Diagrams/Entity Relationship Diagram.md index 1507a78..0c4fc68 100644 --- a/Documentation/Diagrams/Entity Relationship Diagram.md +++ b/Documentation/Diagrams/Entity Relationship Diagram.md @@ -9,7 +9,6 @@ erDiagram int postal_code int mobile_number string email - int review_id FK } Review { @@ -17,8 +16,8 @@ erDiagram string category string review_content int rating - int customer_id FK - int product_id FK + long customer_id FK + long product_id FK } Product { @@ -28,7 +27,6 @@ erDiagram string description double price int stock_quantity - int review_id FK } Customer ||--O{ Review : "Has" diff --git a/src/main/java/sg/com/smartinventory/SmartInventoryApplication.java b/src/main/java/sg/com/smartinventory/SmartInventoryApplication.java index cca0124..69169ad 100644 --- a/src/main/java/sg/com/smartinventory/SmartInventoryApplication.java +++ b/src/main/java/sg/com/smartinventory/SmartInventoryApplication.java @@ -9,13 +9,16 @@ @SpringBootApplication public class SmartInventoryApplication { // Name this according to your class name. + // The Logback library defines 5 log levels in order of priority: TRACE, DEBUG, + // INFO, WARN, ERROR, with each of these having a corresponding logging method: + // trace(), debug(), info(), warn(), error(). private static final Logger app_logger = LoggerFactory.getLogger(SmartInventoryApplication.class); public static void main(String[] args) { - app_logger.info("Starting SmartInventoryApplication. "); + app_logger.info("Starting Smart Inventory Application initialization... "); SpringApplication.run(SmartInventoryApplication.class, args); - app_logger.info("Exiting SmartInventoryApplication. "); + app_logger.info("Smart Inventory Application initialization complete! "); } } \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/entities/Customer.java b/src/main/java/sg/com/smartinventory/entities/Customer.java index 3eea3eb..f550f0f 100644 --- a/src/main/java/sg/com/smartinventory/entities/Customer.java +++ b/src/main/java/sg/com/smartinventory/entities/Customer.java @@ -26,7 +26,7 @@ public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") - private Long id; // PK. + private long id; // PK. PostgreSQL bigserial data type. @NotBlank(message = "First name is mandatory. ") @Column(name = "first_name") @@ -53,15 +53,12 @@ public class Customer { @Column(name = "email") private String email; - @Column(name = "review_id") - private int reviewId; // FK. - public Customer() { } // Define Constructor for DataLoader. public Customer(String firstName, String lastName, String country, String address, int postalCode, int mobileNumber, - String email, int reviewId) { + String email) { this(); this.firstName = firstName; @@ -71,6 +68,5 @@ public Customer(String firstName, String lastName, String country, String addres this.postalCode = postalCode; this.mobileNumber = mobileNumber; this.email = email; - this.reviewId = reviewId; } } \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/entities/Product.java b/src/main/java/sg/com/smartinventory/entities/Product.java index ba16780..66187ca 100644 --- a/src/main/java/sg/com/smartinventory/entities/Product.java +++ b/src/main/java/sg/com/smartinventory/entities/Product.java @@ -22,7 +22,7 @@ public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") - private Long id; + private long id; // PK. PostgreSQL bigserial data type. @Column(name = "category") private String category; @@ -39,9 +39,6 @@ public class Product { @Column(name = "stock_quantity") private int stockQuantity; - @Column(name = "review_id") - private int reviewId; // FK. - public Product() { } @@ -54,6 +51,5 @@ public Product(String category, String name, String description, double price, i this.description = description; this.price = price; this.stockQuantity = stockQuantity; - this.reviewId = reviewId; } } \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/entities/Review.java b/src/main/java/sg/com/smartinventory/entities/Review.java index f65ae12..7619674 100644 --- a/src/main/java/sg/com/smartinventory/entities/Review.java +++ b/src/main/java/sg/com/smartinventory/entities/Review.java @@ -6,7 +6,8 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; - +import jakarta.validation.constraints.Digits; +import jakarta.validation.constraints.Min; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -20,9 +21,9 @@ @Table(name = "review") public class Review { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.IDENTITY) // Bigserial. @Column(name = "id") - private Long id; // PK. + private long id; // PK. PostgreSQL bigserial data type. @Column(name = "category") private String category; @@ -33,17 +34,19 @@ public class Review { @Column(name = "rating") private int rating; + @Min(value = 1, message = "Customer ID should start from 1. ") @Column(name = "customer_id") - private int customerId; // FK. + private long customerId; // FK. PostgreSQL bigserial data type. + @Min(value = 1, message = "Product ID should start from 1. ") @Column(name = "product_id") - private int productId; // FK. + private long productId; // FK. PostgreSQL bigserial data type. public Review() { } // Define Constructor for DataLoader. - public Review(String category, String reviewContent, int rating, int customerId, int productId) { + public Review(String category, String reviewContent, int rating, long customerId, long productId) { this(); this.category = category; diff --git a/src/main/java/sg/com/smartinventory/utility/DataLoader.java b/src/main/java/sg/com/smartinventory/utility/DataLoader.java index 3962362..1822c6b 100644 --- a/src/main/java/sg/com/smartinventory/utility/DataLoader.java +++ b/src/main/java/sg/com/smartinventory/utility/DataLoader.java @@ -36,16 +36,16 @@ public void loadData() { // Create fake data. customerRepository.save(new Customer("John", "Doe", "USA", "123 Main St", 123456, 12345678, - "john.doe@example.com", 101)); + "john.doe@example.com")); customerRepository.save(new Customer("Alice", "Smith", "Canada", "456 Maple Ave", 543210, 98765432, - "alice.smith@example.com", 102)); + "alice.smith@example.com")); customerRepository.save(new Customer("Michael", "Johnson", "UK", "789 Oak Rd", 567890, 98761234, - "michael.johnson@example.com", 103)); + "michael.johnson@example.com")); customerRepository.save(new Customer("Emily", "Brown", "Australia", "321 Elm St", 135790, 45678912, - "emily.brown@example.com", 104)); + "emily.brown@example.com")); customerRepository.save(new Customer("David", "Wilson", "Germany", "654 Pine Rd", 987655, 36985214, - "david.wilson@example.com", 105)); + "david.wilson@example.com")); productRepository.save(new Product("Electronics", "Smartphone", "High-end smartphone with advanced features. ", 999.99, 100, 101)); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f2c90e9..b14bab1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,18 +1,22 @@ spring.application.name=smart-inventory -# Server Configuration. +# Server Port Configuration. # The default server port is 8080. server.port=9090 # Database Configuration. -# PostgreSQL +# PostgreSQL. spring.datasource.url=jdbc:postgresql://localhost:5432/smart_inventory # For WSL, use postgres. # For Mac, use your Mac username. spring.datasource.username=postgres -# Password can be blank if we set it to trust in pg_hba.conf +# Password can be blank if we set it to trust in pg_hba.conf. spring.datasource.password= + +# Database platform to use. spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect + +# Hibernate Database Initialization Method. # This will drop and create tables again. spring.jpa.hibernate.ddl-auto=create # This can be used to update tables. @@ -22,15 +26,18 @@ spring.jpa.hibernate.ddl-auto=create # Application logging configuration. # logging.level.root=INFO # logging.file.name=logs/application.log + # Database logging configuration. # The Spring/Hibernate classes, which generate SQL statements and set the parameters, already contain the code for logging them. # However, the level of those log statements is set to DEBUG and TRACE respectively, which is lower than the default level in Spring Boot — INFO. # By adding these properties, we are just setting those loggers to the required level. # For logging statements. # logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG + # For logging parameters of prepared statements. # logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE -# Set up a profile-specific configuration for tests. + +# Set up a profile-specific configuration for tests. For example a profile. # logging.config=classpath:logback-testloglevel.xml # SpringDoc Configuration. @@ -38,12 +45,26 @@ spring.jpa.hibernate.ddl-auto=create # For a custom path of the OpenAPI documentation in Json format, # add the custom springdoc property /api-docs endpoint custom path to get the path: http://localhost:8080/api-docs. springdoc.api-docs.path=/api-docs + # The OpenAPI definitions are in JSON format by default. For yaml format, we can obtain the definitions at: http://localhost:8080/api-docs.yaml. # To disable api-docs springdoc-openapi endpoints. # springdoc.api-docs.enabled=false + # The springdoc-openapi dependency already includes Swagger UI, to integrate springdoc-openapi with Swagger UI to interact with our API specification and exercise the endpoints.# We can access the API documentation at: http://localhost:8080/swagger-ui/index.html. # Using swagger-ui properties, to customize the path of the swagger-ui API documentation swagger-ui-custom.html to get the path: http://localhost:8080/swagger-ui-custom.html. # springdoc.swagger-ui.path=/swagger-ui-custom.html springdoc.swagger-ui.path=/swagger-ui.html + # To sort the API paths according to their HTTP methods with the springdoc.swagger-ui.operationsSorter property. -# springdoc.swagger-ui.operationsSorter=method \ No newline at end of file +# springdoc.swagger-ui.operationsSorter=method + +# Profile management. +# Spring Boot will always read the application.properties file. +# Other's profile files, such as application-development.properties only will complement and replace the properties defined before. +# The application.properties, and just this file, must have this line. +# spring.profiles.active=@spring.profiles.active@ +# Set the default active profile. +# Use the command to activate: mvn spring-boot:run -Dspring.profiles.active=development. +spring.profiles.active=development +# Set the default default profile. This is the profile that is used on any bean if you don't specify the @Profile annotation to be a certain profile. +# spring.profiles.default=development \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 7d101d1..1ea133c 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -1,4 +1,10 @@ + @@ -48,21 +54,23 @@ - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/sg/com/smartinventory/controllers/CustomerControllerTest.java b/src/test/java/sg/com/smartinventory/controllers/CustomerControllerTest.java index fb9fc46..0fc7eb1 100644 --- a/src/test/java/sg/com/smartinventory/controllers/CustomerControllerTest.java +++ b/src/test/java/sg/com/smartinventory/controllers/CustomerControllerTest.java @@ -4,8 +4,10 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -16,7 +18,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; -import sg.com.smartinventory.SmartInventoryApplication; import sg.com.smartinventory.entities.Customer; @SpringBootTest @@ -28,7 +29,10 @@ public class CustomerControllerTest { @Autowired private ObjectMapper objectMapper; - // Name this according to your class name. + /// Name this according to your class name. + // The Logback library defines 5 log levels in order of priority: TRACE, DEBUG, + // INFO, WARN, ERROR, with each of these having a corresponding logging method: + // trace(), debug(), info(), warn(), error(). private static final Logger test_logger = LoggerFactory.getLogger(CustomerControllerTest.class); @DisplayName("Create customer") @@ -38,9 +42,8 @@ public void createCustomerTest() throws Exception { // Step 1: Create a Customer object Customer newCustomer = Customer.builder().firstName("Jackie").lastName("Chan").country("Hong Kong") - .address("123 HK St") - .postalCode(654321).mobileNumber(87654321).email("jackie.chan@example.com") - .reviewId(110).build(); + .address("123 HK St").postalCode(654321).mobileNumber(87654321) + .email("jackie.chan@example.com").build(); // Step 2: Convert the Java object to JSON using ObjectMapper. String newCustomerAsJSON = objectMapper.writeValueAsString(newCustomer); @@ -51,7 +54,8 @@ public void createCustomerTest() throws Exception { .content(newCustomerAsJSON); // Step 4: Perform the request and get the response and assert. - mockMvc.perform(request).andExpect(status().isCreated()) + mockMvc.perform(request) + .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.firstName").value("Jackie")) .andExpect(jsonPath("$.lastName").value("Chan")); diff --git a/src/test/java/sg/com/smartinventory/controllers/ProductControllerTest.java b/src/test/java/sg/com/smartinventory/controllers/ProductControllerTest.java index 81ce9da..b2955f4 100644 --- a/src/test/java/sg/com/smartinventory/controllers/ProductControllerTest.java +++ b/src/test/java/sg/com/smartinventory/controllers/ProductControllerTest.java @@ -5,6 +5,9 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -26,13 +29,21 @@ public class ProductControllerTest { @Autowired private ObjectMapper objectMapper; + /// Name this according to your class name. + // The Logback library defines 5 log levels in order of priority: TRACE, DEBUG, + // INFO, WARN, ERROR, with each of these having a corresponding logging method: + // trace(), debug(), info(), warn(), error(). + private static final Logger test_logger = LoggerFactory.getLogger(CustomerControllerTest.class); + @DisplayName("Create product") @Test public void createProductTest() throws Exception { + test_logger.info("Starting test: createProductTest. "); + // Step 1: Create a Product object Product newProduct = Product.builder().category("Electronics").name("Smartphone") .description("High-end smartphone with advanced features. ") - .price(999.99).stockQuantity(100).reviewId(101).build(); + .price(999.99).stockQuantity(100).build(); // Step 2: Convert the Java object to JSON using ObjectMapper. String newProductAsJSON = objectMapper.writeValueAsString(newProduct); @@ -43,9 +54,12 @@ public void createProductTest() throws Exception { .content(newProductAsJSON); // Step 4: Perform the request and get the response and assert. - mockMvc.perform(request).andExpect(status().isCreated()) + mockMvc.perform(request) + .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.category").value("Electronics")) .andExpect(jsonPath("$.name").value("Smartphone")); + + test_logger.info("Ending test: createProductTest. "); } } \ No newline at end of file diff --git a/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java b/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java index b73ebf5..d2b2e6c 100644 --- a/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java +++ b/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java @@ -5,11 +5,15 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + 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.http.MediaType; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -26,9 +30,40 @@ public class ReviewControllerTest { @Autowired private ObjectMapper objectMapper; + /** + * Convert JSON Result to object. + * + * @param result The contents. + * @param tClass The expected object class. + * @return The result as a class object. + * @throws Exception + */ + public T fromJsonResult(MvcResult result, Class tClass) throws Exception { + return this.objectMapper.readValue(result.getResponse().getContentAsString(), tClass); + } + + /** + * Convert object to JSON bytes. + * + * @param object The object to JSON-ify. + * @return Byte array with JSON representation. + * @throws Exception + */ + public byte[] toJson(Object object) throws Exception { + return this.objectMapper.writeValueAsString(object).getBytes(); + } + + /// Name this according to your class name. + // The Logback library defines 5 log levels in order of priority: TRACE, DEBUG, + // INFO, WARN, ERROR, with each of these having a corresponding logging method: + // trace(), debug(), info(), warn(), error(). + private static final Logger test_logger = LoggerFactory.getLogger(CustomerControllerTest.class); + @DisplayName("Create review") @Test public void createReviewTest() throws Exception { + test_logger.info("Starting test: createReviewTest. "); + // Step 1: Create a Review object Review newReview = Review.builder().category("Electronics") .reviewContent("Great smartphone with excellent features. ").rating(5).customerId(101) @@ -43,9 +78,21 @@ public void createReviewTest() throws Exception { .content(newReviewAsJSON); // Step 4: Perform the request and get the response and assert. - mockMvc.perform(request).andExpect(status().isCreated()) + mockMvc.perform(request) + // MvcResult Result = mockMvc.perform(request) + // .andDo(print -> test_logger.error("Starting Request: createReviewTest. ")) + .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.category").value("Electronics")) .andExpect(jsonPath("$.rating").value(5)); + // .andDo(print -> test_logger.error("Customer's id: {}", + // print.getResponse().getContentAsString())) + // .andDo(print -> test_logger.error("Starting Request: createReviewTest. ")) + // .andReturn(); + + // Review ReviewObject = fromJsonResult(Result, Review.class); + + // test_logger.error("Ending test: createReviewTest. "); + test_logger.info("Ending test: createReviewTest. "); } } \ No newline at end of file diff --git a/src/test/java/sg/com/smartinventory/integration/IntegrationTest.java b/src/test/java/sg/com/smartinventory/integration/IntegrationTest.java index 41966c3..938208d 100644 --- a/src/test/java/sg/com/smartinventory/integration/IntegrationTest.java +++ b/src/test/java/sg/com/smartinventory/integration/IntegrationTest.java @@ -48,9 +48,8 @@ public class IntegrationTest { public void createCustomerTest() { // 1. Setup. Customer testObject1 = Customer.builder().firstName("Jackie").lastName("Chan").country("Hong Kong") - .address("123 HK St") - .postalCode(654321).mobileNumber(87654321).email("jackie.chan@example.com") - .reviewId(110).build(); + .address("123 HK St").postalCode(654321).mobileNumber(87654321) + .email("jackie.chan@example.com").build(); // mock the save method of the customer repository when((customerRepository.save(testObject1))).thenReturn(testObject1); @@ -70,13 +69,11 @@ public void createCustomerTest() { public void runIntegratedTest() throws Exception { // Step 1: Create the test objects. Customer testObject1 = Customer.builder().firstName("Jackie").lastName("Chan").country("Hong Kong") - .address("123 HK St") - .postalCode(654321).mobileNumber(87654321).email("jackie.chan@example.com") - .reviewId(110).build(); + .address("123 HK St").postalCode(654321).mobileNumber(87654321) + .email("jackie.chan@example.com").build(); Customer testObject2 = Customer.builder().firstName("Jackie").lastName("Chang").country("Hong Kong") - .address("123 HK St") - .postalCode(654321).mobileNumber(87654321).email("jackie.chang@example.com") - .reviewId(110).build(); + .address("123 HK St").postalCode(654321).mobileNumber(87654321) + .email("jackie.chang@example.com").build(); // Step 2: Convert the Java objects to JSON using ObjectMapper. String testObject1AsJSON = objectMapper.writeValueAsString(testObject1); @@ -91,11 +88,13 @@ public void runIntegratedTest() throws Exception { .content(testObject2AsJSON); // Step 4: Perform the request and get the response and assert. - mockMvc.perform(request).andExpect(status().isCreated()) + mockMvc.perform(request) + .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.firstName").value("Jackie")) .andExpect(jsonPath("$.lastName").value("Chan")); - mockMvc.perform(request2).andExpect(status().isCreated()) + mockMvc.perform(request2) + .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.firstName").value("Jackie")) .andExpect(jsonPath("$.lastName").value("Chang")); diff --git a/src/test/java/sg/com/smartinventory/services/CustomerServiceImplTest.java b/src/test/java/sg/com/smartinventory/services/CustomerServiceImplTest.java index 1bdcfed..3dcb4de 100644 --- a/src/test/java/sg/com/smartinventory/services/CustomerServiceImplTest.java +++ b/src/test/java/sg/com/smartinventory/services/CustomerServiceImplTest.java @@ -31,7 +31,7 @@ public void createCustomerTest() { // 1. SETUP // Create a new customer. Customer customer = Customer.builder().firstName("John").lastName("Wick").country("USA").address("123 Main St") - .postalCode(123456).mobileNumber(12345678).email("john.wick@example.com").reviewId(111).build(); + .postalCode(123456).mobileNumber(12345678).email("john.wick@example.com").build(); // Mock the save method of the customer repository. when((customerRepository.save(customer))).thenReturn(customer); diff --git a/src/test/java/sg/com/smartinventory/services/ProductServiceImplTest.java b/src/test/java/sg/com/smartinventory/services/ProductServiceImplTest.java new file mode 100644 index 0000000..516e6c1 --- /dev/null +++ b/src/test/java/sg/com/smartinventory/services/ProductServiceImplTest.java @@ -0,0 +1,49 @@ +package sg.com.smartinventory.services; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; + +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import org.springframework.boot.test.context.SpringBootTest; + +import sg.com.smartinventory.entities.Product; +import sg.com.smartinventory.repositories.ProductRepository; +import sg.com.smartinventory.serviceImpls.ProductServiceImpl; + +@SpringBootTest +public class ProductServiceImplTest { + @Mock + private ProductRepository productRepository; + + @InjectMocks + ProductServiceImpl productService; + + @Test + public void createProductTest() { + + // 1. SETUP + // Create a new product. + Product product = Product.builder().category("Electronics").name("Smartphone") + .description("High-end smartphone with advanced features. ") + .price(999.99).stockQuantity(100).build(); + + // Mock the save method of the product repository. + when((productRepository.save(product))).thenReturn(product); + + // 2. EXECUTE. + Product savedProduct = productService.createProduct(product); + + // 3. ASSERT. + assertEquals(product, savedProduct, "The saved product should be the same as the new product created. "); + + // Verify that the save method of the product repository is called once only. + verify(productRepository, times(1)).save(product); + } +} \ No newline at end of file diff --git a/src/test/java/sg/com/smartinventory/services/ReviewServiceImplTest.java b/src/test/java/sg/com/smartinventory/services/ReviewServiceImplTest.java new file mode 100644 index 0000000..6ce6a41 --- /dev/null +++ b/src/test/java/sg/com/smartinventory/services/ReviewServiceImplTest.java @@ -0,0 +1,49 @@ +package sg.com.smartinventory.services; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; + +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import org.springframework.boot.test.context.SpringBootTest; + +import sg.com.smartinventory.entities.Review; +import sg.com.smartinventory.repositories.ReviewRepository; +import sg.com.smartinventory.serviceImpls.ReviewServiceImpl; + +@SpringBootTest +public class ReviewServiceImplTest { + @Mock + private ReviewRepository reviewRepository; + + @InjectMocks + ReviewServiceImpl reviewService; + + @Test + public void createReviewTest() { + + // 1. SETUP + // Create a new review. + Review review = Review.builder().category("Electronics") + .reviewContent("Great smartphone with excellent features. ").rating(5).customerId(101) + .productId(201).build(); + + // Mock the save method of the review repository. + when((reviewRepository.save(review))).thenReturn(review); + + // 2. EXECUTE. + Review savedReview = reviewService.createReview(review); + + // 3. ASSERT. + assertEquals(review, savedReview, "The saved review should be the same as the new review created. "); + + // Verify that the save method of the review repository is called once only. + verify(reviewRepository, times(1)).save(review); + } +} \ No newline at end of file diff --git a/src/test/java/sg/com/smartinventory/useCases/UseCaseTest.java b/src/test/java/sg/com/smartinventory/useCases/UseCaseTest.java index 4592a44..2694b8b 100644 --- a/src/test/java/sg/com/smartinventory/useCases/UseCaseTest.java +++ b/src/test/java/sg/com/smartinventory/useCases/UseCaseTest.java @@ -48,9 +48,8 @@ public class UseCaseTest { public void createCustomerTest() { // 1. Setup. Customer testObject1 = Customer.builder().firstName("Jackie").lastName("Chan").country("Hong Kong") - .address("123 HK St") - .postalCode(654321).mobileNumber(87654321).email("jackie.chan@example.com") - .reviewId(110).build(); + .address("123 HK St").postalCode(654321).mobileNumber(87654321) + .email("jackie.chan@example.com").build(); // mock the save method of the customer repository when((customerRepository.save(testObject1))).thenReturn(testObject1); @@ -70,13 +69,11 @@ public void createCustomerTest() { public void runIntegratedTest() throws Exception { // Step 1: Create the test objects. Customer testObject1 = Customer.builder().firstName("Jackie").lastName("Chan").country("Hong Kong") - .address("123 HK St") - .postalCode(654321).mobileNumber(87654321).email("jackie.chan@example.com") - .reviewId(110).build(); + .address("123 HK St").postalCode(654321).mobileNumber(87654321) + .email("jackie.chan@example.com").build(); Customer testObject2 = Customer.builder().firstName("Jackie").lastName("Chang").country("Hong Kong") - .address("123 HK St") - .postalCode(654321).mobileNumber(87654321).email("jackie.chang@example.com") - .reviewId(110).build(); + .address("123 HK St").postalCode(654321).mobileNumber(87654321) + .email("jackie.chang@example.com").build(); // Step 2: Convert the Java objects to JSON using ObjectMapper. String testObject1AsJSON = objectMapper.writeValueAsString(testObject1); @@ -91,11 +88,13 @@ public void runIntegratedTest() throws Exception { .content(testObject2AsJSON); // Step 4: Perform the request and get the response and assert. - mockMvc.perform(request).andExpect(status().isCreated()) + mockMvc.perform(request) + .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.firstName").value("Jackie")) .andExpect(jsonPath("$.lastName").value("Chan")); - mockMvc.perform(request2).andExpect(status().isCreated()) + mockMvc.perform(request2) + .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.firstName").value("Jackie")) .andExpect(jsonPath("$.lastName").value("Chang")); diff --git a/src/test/resources/application-logging-test.properties b/src/test/resources/application-logging-test.properties index 25c2f1c..79cf0ed 100644 --- a/src/test/resources/application-logging-test.properties +++ b/src/test/resources/application-logging-test.properties @@ -1,6 +1,13 @@ # Test logging configuration. -# logging.config=classpath:logback-testloglevel.xml +# logging.config=classpath:logback-controllers.xml # Add a Spring profile to our test by using the ActiveProfiles annotation, for example @ActiveProfiles("logging-test"). -# logging.level.sg.com.smartinventory.testloglevel=TRACE -# logging.level.root=ERROR \ No newline at end of file +# logging.level.sg.com.smartinventory.controllers=TRACE +# logging.level.root=ERROR + +# Profile management. +# Set the default active profile. +# Use the command to activate: mvn test -Dspring.profiles.active=development. +spring.profiles.active=development +# Set the default default profile. This is the profile that is used on any bean if you don't specify the @Profile annotation to be a certain profile. +# spring.profiles.default=development \ No newline at end of file diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 9ebaa28..7ce75c2 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -45,23 +45,27 @@ - - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file