From f374fc9bbe7189428dff5e798a7f2562440afd5b Mon Sep 17 00:00:00 2001
From: roost-io <8110509+mgdevstack@users.noreply.github.com>
Date: Sun, 3 May 2026 21:26:10 +0530
Subject: [PATCH] Unit test generated by RoostGPT
Using AI Model gpt-5
---
pom.xml | 249 +++---
.../MarketControllerCreateProductTest.java | 748 ++++++++++++++++++
.../MarketControllerDeleteProductTest.java | 370 +++++++++
.../MarketControllerShowAllProductsTest.java | 159 ++++
4 files changed, 1430 insertions(+), 96 deletions(-)
create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerCreateProductTest.java
create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerDeleteProductTest.java
create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerShowAllProductsTest.java
diff --git a/pom.xml b/pom.xml
index d5e071f9..cf9d3292 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,96 +1,153 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.0.6
-
-
- com.medeiros
- SPRINGProject
- 0.0.1-SNAPSHOT
- SPRINGProject
- Demo project for Spring Boot
-
- 20
-
- 6.0.3
-
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
-
- org.springframework.boot
- spring-boot-devtools
- runtime
- true
-
-
- com.mysql
- mysql-connector-j
- runtime
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
- org.springframework.boot
- spring-boot-starter-thymeleaf
- 3.0.6
-
-
-
- org.springframework.boot
- spring-boot-starter-security
-
-
-
- io.jsonwebtoken
- jjwt-api
- 0.11.5
-
-
-
-
-
-
- org.springframework.security
- spring-security-core
- 6.0.3
-
-
-
-
-
-
- io.jsonwebtoken
- jjwt-impl
- 0.11.5
- runtime
-
-
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
-
-
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.0.6
+
+
+
+ com.medeiros
+ SPRINGProject
+ 0.0.1-SNAPSHOT
+ SPRINGProject
+ Demo project for Spring Boot
+
+ 20
+
+ 6.0.3
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+ 3.0.6
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+
+ org.springframework.security
+ spring-security-core
+ 6.0.3
+
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 2.23.4
+ test
+
+
+
+ io.spring.javaformat
+ spring-javaformat-formatter
+ 0.0.40
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.11
+
+
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+ coverageReport
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+ 3.2.5
+
+ testReport
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 2.1
+
+ testReport
+
+
+
+
+ io.spring.javaformat
+ spring-javaformat-maven-plugin
+ 0.0.40
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerCreateProductTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerCreateProductTest.java
new file mode 100644
index 00000000..ab96a2b0
--- /dev/null
+++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerCreateProductTest.java
@@ -0,0 +1,748 @@
+
+// ********RoostGPT********
+/*
+Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5
+
+ROOST_METHOD_HASH=createProduct_2b4ce50b8c
+ROOST_METHOD_SIG_HASH=createProduct_7aa14a7887
+
+Scenario 1: Successful creation returns the expected success message and performs saves
+
+Details:
+ TestName: createProductReturnsSuccessMessageOnValidInput
+ Description: Verifies that when valid parameters are provided, MarketController.createProduct constructs a ProductModel, saves it through ProductModelRepository, logs the operation through LogRepository, and returns the exact success message.
+
+Execution:
+ Arrange: Create a MarketController instance. Inject mock ProductModelRepository into controller.ProductRepository and mock LogRepository into controller.Log. Stub controller.Date.getTimeNow() to return a fixed timestamp value. Prepare typical input values: description="Guitar", price=199.99, musicId=10, userId=5, listProduct="Strings,Capo".
+ Act: Invoke createProduct("Guitar", 199.99, 10, 5, "Strings,Capo").
+ Assert: Assert that the returned String equals "Produto Criado". Verify ProductRepository.save was called exactly once with a ProductModel constructed from the same inputs. Verify Log.save was called exactly once with a LogModel representing route "product/create/{userid}", entity "Product", and the stubbed timestamp.
+
+Validation:
+ Confirms end-to-end controller behavior for the happy path: correct message, one product save, one log save, and correct use of the fixed log route string. Ensures basic business flow is intact.
+
+Scenario 2: ProductRepository.save is invoked once with a ProductModel reflecting all inputs
+
+Details:
+ TestName: createProductSavesProductWithAllProvidedFields
+ Description: Ensures that the ProductModel passed to ProductRepository.save contains description, price, musicId, userId, and listProduct exactly as provided to the controller method.
+
+Execution:
+ Arrange: Instantiate MarketController with mocks for ProductRepository and Log. Optionally stub Date.getTimeNow() to any value. Choose inputs: description="Piano", price=5000.0, musicId=22, userId=101, listProduct="Bench,Sheet Music".
+ Act: Call createProduct("Piano", 5000.0, 22, 101, "Bench,Sheet Music").
+ Assert: Capture the ProductModel argument passed to ProductRepository.save and assert its internal state matches the inputs (via reflection or argument matchers that compare constructor-initialized fields). Also verify save was called exactly once.
+
+Validation:
+ Verifies that data flows unmodified from method parameters into the ProductModel that is persisted, ensuring no reordering, transformation, or omission of fields.
+
+Scenario 3: LogRepository.save is invoked once with the expected route, entity, and timestamp
+
+Details:
+ TestName: createProductLogsWithExpectedRouteEntityAndTimestamp
+ Description: Validates that the controller constructs LogModel with route "product/create/{userid}" (literal), entity "Product", and timestamp from Date.getTimeNow().
+
+Execution:
+ Arrange: Create MarketController. Inject mocks for ProductRepository and Log. Stub Date.getTimeNow() to a known time value (e.g., "2026-05-03T10:00:00Z" or a fixed long). Use any valid product inputs.
+ Act: Invoke createProduct with valid inputs.
+ Assert: Capture the LogModel passed to Log.save and assert: route=="product/create/{userid}", entityName=="Product", timestamp==stubbed time. Verify Log.save was called exactly once.
+
+Validation:
+ Ensures logging is consistent and relies on the controller’s Date field, using the constant route string and expected entity tag.
+
+Scenario 4: Operations occur in correct order: product is saved before log is saved
+
+Details:
+ TestName: createProductSavesProductBeforeWritingLog
+ Description: Ensures that ProductRepository.save is called prior to Log.save, maintaining intended operation order.
+
+Execution:
+ Arrange: Instantiate MarketController with mocks for ProductRepository and Log; stub Date.getTimeNow(). Prepare any valid inputs.
+ Act: Call createProduct with valid inputs.
+ Assert: Verify the call order: ProductRepository.save is invoked before Log.save (using ordered verification).
+
+Validation:
+ Correct sequencing helps guarantee that a product is persisted before the action is logged, aligning with expected transactional semantics.
+
+Scenario 5: Zero price is accepted and still results in save and log
+
+Details:
+ TestName: createProductAcceptsZeroPrice
+ Description: Verifies behavior when price is 0.0—no validation occurs, and the product is still saved and logged.
+
+Execution:
+ Arrange: Set up controller and mocks, stub Date.getTimeNow(). Inputs: description="Harmonica", price=0.0, musicId=3, userId=7, listProduct="Case".
+ Act: Invoke createProduct("Harmonica", 0.0, 3, 7, "Case").
+ Assert: Assert returned value is "Produto Criado". Verify ProductRepository.save and Log.save each invoked once with appropriate objects.
+
+Validation:
+ Confirms lack of price validation, documenting that zero price is permitted by the controller layer.
+
+Scenario 6: Negative price is accepted and still results in save and log
+
+Details:
+ TestName: createProductAcceptsNegativePrice
+ Description: Ensures the controller does not reject negative prices, continuing to save and log.
+
+Execution:
+ Arrange: Prepare controller and mocks, stub Date.getTimeNow(). Inputs: description="Flute", price=-10.5, musicId=12, userId=9, listProduct="Cleaning Kit".
+ Act: Call createProduct("Flute", -10.5, 12, 9, "Cleaning Kit").
+ Assert: Assert returned value equals "Produto Criado". Verify ProductRepository.save and Log.save called once each.
+
+Validation:
+ Captures current behavior (no validation in controller) for negative price, important for defining future validation needs.
+
+Scenario 7: Extremely large price (Double.MAX_VALUE) is passed through
+
+Details:
+ TestName: createProductHandlesVeryLargePrice
+ Description: Tests that a very large price is forwarded to the ProductModel and saved, and the flow completes with logging and success message.
+
+Execution:
+ Arrange: Controller with mocked repositories; stub Date.getTimeNow(). Inputs: description="Grand Piano", price=Double.MAX_VALUE, musicId=77, userId=1, listProduct="Bench,Cover".
+ Act: Invoke createProduct with those arguments.
+ Assert: Assert return equals "Produto Criado". Verify ProductRepository.save received a ProductModel with the given price and Log.save called once.
+
+Validation:
+ Ensures numeric extremes are handled without controller-level overflow checks, highlighting potential persistence constraints externally.
+
+Scenario 8: NaN price is forwarded (controller-level) and still attempts save and log
+
+Details:
+ TestName: createProductForwardsNaNPriceToRepository
+ Description: Confirms the controller does not guard against Double.NaN and still attempts to persist and log.
+
+Execution:
+ Arrange: Controller with mocked repositories; stub Date.getTimeNow(). Inputs: description="Synth", price=Double.NaN, musicId=5, userId=2, listProduct="Stand".
+ Act: Call createProduct with these inputs.
+ Assert: Assert return is "Produto Criado". Verify ProductRepository.save and Log.save each called once (stubbing repository to accept NaN).
+
+Validation:
+ Documents that the controller layer is agnostic to special double values, which may inform validation or persistence strategy elsewhere.
+
+Scenario 9: Negative musicId is accepted and still saved
+
+Details:
+ TestName: createProductAcceptsNegativeMusicId
+ Description: Ensures musicId can be negative at controller level without blocking save/log.
+
+Execution:
+ Arrange: Controller with mocks; stub Date.getTimeNow(). Inputs: description="Drums", price=300.0, musicId=-42, userId=88, listProduct="Sticks,Seat".
+ Act: Invoke createProduct.
+ Assert: Return equals "Produto Criado"; verify one save on ProductRepository and one log on Log.
+
+Validation:
+ Captures absence of ID validation, which may be relevant for domain constraints later.
+
+Scenario 10: Zero userId is accepted and forwarded
+
+Details:
+ TestName: createProductAcceptsZeroUserId
+ Description: Ensures that userId=0 does not prevent saving or logging.
+
+Execution:
+ Arrange: Mocks and controller ready; stub Date.getTimeNow(). Inputs: description="Bass", price=450.0, musicId=15, userId=0, listProduct="Strap".
+ Act: Call createProduct.
+ Assert: Confirm "Produto Criado" is returned and both repositories receive a single call to save.
+
+Validation:
+ Documents current permissiveness regarding the path variable userId, useful for security or validation considerations.
+
+Scenario 11: Empty description string is preserved and does not block save
+
+Details:
+ TestName: createProductAllowsEmptyDescription
+ Description: Checks that an empty description "" is passed through into the ProductModel and save/log still occur.
+
+Execution:
+ Arrange: Controller with mocks; stub Date.getTimeNow(). Inputs: description="", price=99.0, musicId=9, userId=12, listProduct="Bag".
+ Act: Invoke createProduct.
+ Assert: Assert return equals "Produto Criado". Capture ProductModel to assert the description remains empty. Verify both save calls occur once.
+
+Validation:
+ Ensures inputs are not trimmed or validated by the controller, preserving the exact user-provided string.
+
+Scenario 12: Empty listProduct string is preserved and does not block save
+
+Details:
+ TestName: createProductAllowsEmptyListProduct
+ Description: Ensures listProduct="" is accepted and persisted without changes.
+
+Execution:
+ Arrange: Set controller and mocks; stub Date.getTimeNow(). Inputs: description="Violin", price=999.0, musicId=31, userId=4, listProduct="".
+ Act: Invoke createProduct.
+ Assert: Assert return equals "Produto Criado". Capture ProductModel and assert listProduct is empty. Verify both save calls occur once.
+
+Validation:
+ Demonstrates that optional-like string inputs are allowed empty at controller layer.
+
+Scenario 13: Special characters and Unicode are preserved
+
+Details:
+ TestName: createProductPreservesUnicodeAndSpecialCharacters
+ Description: Verifies that description and listProduct with Unicode, emojis, and special characters are passed through intact.
+
+Execution:
+ Arrange: Controller with mocks; stub Date.getTimeNow(). Inputs: description="Cello 🎻 – Série Édition", price=1200.5, musicId=55, userId=13, listProduct="Arco,Resina,Estojo™".
+ Act: Call createProduct.
+ Assert: Assert return equals "Produto Criado". Capture ProductModel and assert fields contain the exact Unicode/special characters. Verify save and log each once.
+
+Validation:
+ Ensures controller does not alter encoding or sanitize strings, which is important for internationalization.
+
+Scenario 14: Whitespace-only description is kept as-is
+
+Details:
+ TestName: createProductRetainsWhitespaceOnlyDescription
+ Description: Ensures a description of only spaces is not trimmed by the controller.
+
+Execution:
+ Arrange: Controller with mocks; stub Date.getTimeNow(). Inputs: description=" ", price=10.0, musicId=2, userId=3, listProduct="Pick".
+ Act: Invoke createProduct.
+ Assert: Assert return equals "Produto Criado". Capture ProductModel and assert description remains " ". Verify saves occur once each.
+
+Validation:
+ Demonstrates that the controller does not mutate whitespace, which may be significant for later validation rules.
+
+Scenario 15: If ProductRepository.save throws, the exception propagates and Log.save is not called
+
+Details:
+ TestName: createProductPropagatesExceptionWhenProductSaveFails
+ Description: Validates error handling when the product persistence step fails with a runtime exception.
+
+Execution:
+ Arrange: Controller with mocks; configure ProductRepository.save to throw a RuntimeException. Stub Date.getTimeNow(). Inputs: any valid parameters.
+ Act: Invoke createProduct and expect a RuntimeException to be thrown.
+ Assert: Assert that an exception is thrown and no return value is produced. Verify that Log.save was never called.
+
+Validation:
+ Confirms lack of try/catch in controller and that logging does not occur when product persistence fails, preventing misleading log entries.
+
+Scenario 16: If Log.save throws, the exception propagates after product save
+
+Details:
+ TestName: createProductPropagatesExceptionWhenLogSaveFails
+ Description: Ensures that a logging failure after a successful product save results in an exception being thrown by the controller.
+
+Execution:
+ Arrange: Controller with mocks; ProductRepository.save succeeds; configure Log.save to throw a RuntimeException. Inputs: any valid parameters.
+ Act: Invoke createProduct and expect a RuntimeException to be thrown.
+ Assert: Assert that an exception is thrown. Verify ProductRepository.save was called once. Verify Log.save was called once and then failed.
+
+Validation:
+ Confirms side-effect sequence: product may be persisted even if logging fails, and the controller does not mask logging errors.
+
+Scenario 17: The timestamp used in the log equals the value from Date.getTimeNow
+
+Details:
+ TestName: createProductUsesDateFieldForLogTimestamp
+ Description: Confirms that the controller uses its Date field (LogModel instance) to obtain the logging timestamp.
+
+Execution:
+ Arrange: Controller with mocks; replace controller.Date with a stubbed LogModel where getTimeNow() returns a known fixed value. Inputs: any valid parameters.
+ Act: Invoke createProduct.
+ Assert: Capture LogModel passed to Log.save and assert that its timestamp equals the stubbed fixed value.
+
+Validation:
+ Verifies the dependency usage on the Date field rather than any external or system time source.
+
+Scenario 18: Returned message is exactly "Produto Criado" with correct casing and spacing
+
+Details:
+ TestName: createProductReturnsExactPortugueseMessage
+ Description: Ensures the return value is the exact literal "Produto Criado".
+
+Execution:
+ Arrange: Controller with mocks; stub Date.getTimeNow(). Inputs: any valid parameters.
+ Act: Invoke createProduct.
+ Assert: Assert that the returned String equals exactly "Produto Criado" (case-sensitive, includes space).
+
+Validation:
+ Protects against inadvertent changes in user-facing response messages.
+
+Scenario 19: No unexpected repository interactions occur during creation
+
+Details:
+ TestName: createProductDoesNotPerformUnrelatedRepositoryOperations
+ Description: Ensures that only ProductRepository.save and Log.save are invoked, and methods like findAll or deleteById are not called.
+
+Execution:
+ Arrange: Controller with mocks; stub Date.getTimeNow(). Inputs: any valid parameters.
+ Act: Call createProduct.
+ Assert: Verify only one call to ProductRepository.save and one call to Log.save; verify no interactions with ProductRepository.findAll, ProductRepository.deleteById, or other methods.
+
+Validation:
+ Confirms that createProduct remains focused on creation and logging without side effects.
+
+Scenario 20: Log route string remains literal "product/create/{userid}" and does not interpolate userId
+
+Details:
+ TestName: createProductUsesLiteralRouteInLog
+ Description: Validates that the log path is the literal "product/create/{userid}" and does not substitute the actual userId.
+
+Execution:
+ Arrange: Controller with mocks; stub Date.getTimeNow(). Inputs: choose a distinctive userId (e.g., 9999).
+ Act: Invoke createProduct with that userId.
+ Assert: Capture the LogModel passed to Log.save and assert the route equals exactly "product/create/{userid}" (no interpolation).
+
+Validation:
+ Documents current logging design, which uses a literal placeholder rather than the concrete path value, important for log analysis expectations.
+
+*/
+
+// ********RoostGPT********
+package com.medeiros.SPRINGProject.Controllers;
+
+import com.medeiros.SPRINGProject.Models.LogModel;
+import com.medeiros.SPRINGProject.Models.LogRepository;
+import com.medeiros.SPRINGProject.Models.ProductModel;
+import com.medeiros.SPRINGProject.Models.ProductModelRepository;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import java.lang.reflect.Field;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+import org.junit.jupiter.api.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@ExtendWith(MockitoExtension.class)
+public class MarketControllerCreateProductTest {
+
+ @Mock
+ private ProductModelRepository ProductRepository;
+
+ @Mock
+ private LogRepository Log;
+
+ @Mock
+ private LogModel Date; // Injected into controller.Date
+
+ @InjectMocks
+ private MarketController controller;
+
+ private static final LocalDateTime FIXED_TIME = LocalDateTime.of(2026, 5, 3, 10, 0, 0);
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 1: Successful creation returns the expected success message and performs saves")
+ public void createProductReturnsSuccessMessageOnValidInput() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Guitar", 199.99, 10, 5, "Strings,Capo");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository, times(1)).save(productCaptor.capture());
+ ProductModel capturedProduct = productCaptor.getValue();
+ assertProductModelContainsValues(capturedProduct, "Guitar", 199.99, 10, 5, "Strings,Capo");
+ ArgumentCaptor logCaptor = ArgumentCaptor.forClass(LogModel.class);
+ verify(Log, times(1)).save(logCaptor.capture());
+ LogModel capturedLog = logCaptor.getValue();
+ assertLogModelContainsValues(capturedLog, "product/create/{userid}", "Product", FIXED_TIME);
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 2: ProductRepository.save is invoked once with a ProductModel reflecting all inputs")
+ public void createProductSavesProductWithAllProvidedFields() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Piano", 5000.0, 22, 101, "Bench,Sheet Music");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository, times(1)).save(productCaptor.capture());
+ ProductModel capturedProduct = productCaptor.getValue();
+ assertProductModelContainsValues(capturedProduct, "Piano", 5000.0, 22, 101, "Bench,Sheet Music");
+ verify(Log, times(1)).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 3: LogRepository.save is invoked once with the expected route, entity, and timestamp")
+ public void createProductLogsWithExpectedRouteEntityAndTimestamp() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Item", 1.0, 1, 1, "List");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor logCaptor = ArgumentCaptor.forClass(LogModel.class);
+ verify(Log, times(1)).save(logCaptor.capture());
+ LogModel capturedLog = logCaptor.getValue();
+ assertLogModelContainsValues(capturedLog, "product/create/{userid}", "Product", FIXED_TIME);
+ verify(ProductRepository, times(1)).save(any(ProductModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("integration")
+ @DisplayName("Scenario 4: Operations occur in correct order: product is saved before log is saved")
+ public void createProductSavesProductBeforeWritingLog() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Drum", 250.0, 3, 2, "Sticks");
+ assertEquals((String) "Produto Criado", (String) result);
+ InOrder inOrder = inOrder(ProductRepository, Log);
+ inOrder.verify(ProductRepository).save(any(ProductModel.class));
+ inOrder.verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 5: Zero price is accepted and still results in save and log")
+ public void createProductAcceptsZeroPrice() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Harmonica", 0.0, 3, 7, "Case");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(productCaptor.capture());
+ assertProductModelContainsValues(productCaptor.getValue(), "Harmonica", 0.0, 3, 7, "Case");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 6: Negative price is accepted and still results in save and log")
+ public void createProductAcceptsNegativePrice() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Flute", -10.5, 12, 9, "Cleaning Kit");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(productCaptor.capture());
+ assertProductModelContainsValues(productCaptor.getValue(), "Flute", -10.5, 12, 9, "Cleaning Kit");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 7: Extremely large price (Double.MAX_VALUE) is passed through")
+ public void createProductHandlesVeryLargePrice() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ double largePrice = Double.MAX_VALUE;
+ String result = controller.createProduct("Grand Piano", largePrice, 77, 1, "Bench,Cover");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(productCaptor.capture());
+ assertProductModelContainsValues(productCaptor.getValue(), "Grand Piano", largePrice, 77, 1, "Bench,Cover");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 8: NaN price is forwarded (controller-level) and still attempts save and log")
+ public void createProductForwardsNaNPriceToRepository() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ double nanPrice = Double.NaN;
+ String result = controller.createProduct("Synth", nanPrice, 5, 2, "Stand");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(productCaptor.capture());
+ assertProductModelContainsValues(productCaptor.getValue(), "Synth", nanPrice, 5, 2, "Stand");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 9: Negative musicId is accepted and still saved")
+ public void createProductAcceptsNegativeMusicId() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Drums", 300.0, -42, 88, "Sticks,Seat");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(productCaptor.capture());
+ assertProductModelContainsValues(productCaptor.getValue(), "Drums", 300.0, -42, 88, "Sticks,Seat");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 10: Zero userId is accepted and forwarded")
+ public void createProductAcceptsZeroUserId() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Bass", 450.0, 15, 0, "Strap");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor productCaptor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(productCaptor.capture());
+ assertProductModelContainsValues(productCaptor.getValue(), "Bass", 450.0, 15, 0, "Strap");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 11: Empty description string is preserved and does not block save")
+ public void createProductAllowsEmptyDescription() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("", 99.0, 9, 12, "Bag");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(captor.capture());
+ assertProductModelContainsValues(captor.getValue(), "", 99.0, 9, 12, "Bag");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 12: Empty listProduct string is preserved and does not block save")
+ public void createProductAllowsEmptyListProduct() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Violin", 999.0, 31, 4, "");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(captor.capture());
+ assertProductModelContainsValues(captor.getValue(), "Violin", 999.0, 31, 4, "");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 13: Special characters and Unicode are preserved")
+ public void createProductPreservesUnicodeAndSpecialCharacters() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String description = "Cello 🎻 – Série Édition";
+ String listProduct = "Arco,Resina,Estojo™";
+ String result = controller.createProduct(description, 1200.5, 55, 13, listProduct);
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(captor.capture());
+ assertProductModelContainsValues(captor.getValue(), description, 1200.5, 55, 13, listProduct);
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("boundary")
+ @DisplayName("Scenario 14: Whitespace-only description is kept as-is")
+ public void createProductRetainsWhitespaceOnlyDescription() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String description = " ";
+ String result = controller.createProduct(description, 10.0, 2, 3, "Pick");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ProductModel.class);
+ verify(ProductRepository).save(captor.capture());
+ assertProductModelContainsValues(captor.getValue(), description, 10.0, 2, 3, "Pick");
+ verify(Log).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("invalid")
+ @DisplayName("Scenario 15: If ProductRepository.save throws, the exception propagates and Log.save is not called")
+ public void createProductPropagatesExceptionWhenProductSaveFails() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ doThrow(new RuntimeException("persist fail")).when(ProductRepository).save(any(ProductModel.class));
+ RuntimeException ex = assertThrows(RuntimeException.class, () ->
+ controller.createProduct("Clarinet", 200.0, 11, 22, "Reeds"));
+ assertNotNull((Object) ex);
+ verify(ProductRepository, times(1)).save(any(ProductModel.class));
+ verify(Log, never()).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("invalid")
+ @DisplayName("Scenario 16: If Log.save throws, the exception propagates after product save")
+ public void createProductPropagatesExceptionWhenLogSaveFails() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ doNothing().when(ProductRepository).save(any(ProductModel.class));
+ doThrow(new RuntimeException("log fail")).when(Log).save(any(LogModel.class));
+ RuntimeException ex = assertThrows(RuntimeException.class, () ->
+ controller.createProduct("Trumpet", 350.0, 6, 8, "Mouthpiece"));
+ assertNotNull((Object) ex);
+ verify(ProductRepository, times(1)).save(any(ProductModel.class));
+ verify(Log, times(1)).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 17: The timestamp used in the log equals the value from Date.getTimeNow")
+ public void createProductUsesDateFieldForLogTimestamp() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Oboe", 700.0, 14, 21, "Reed,Case");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor logCaptor = ArgumentCaptor.forClass(LogModel.class);
+ verify(Log).save(logCaptor.capture());
+ assertLogModelContainsValues(logCaptor.getValue(), "product/create/{userid}", "Product", FIXED_TIME);
+ verify(ProductRepository).save(any(ProductModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 18: Returned message is exactly 'Produto Criado' with correct casing and spacing")
+ public void createProductReturnsExactPortugueseMessage() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Saxophone", 1200.0, 18, 30, "Reed,Strap");
+ assertEquals((String) "Produto Criado", (String) result);
+ verify(ProductRepository, times(1)).save(any(ProductModel.class));
+ verify(Log, times(1)).save(any(LogModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 19: No unexpected repository interactions occur during creation")
+ public void createProductDoesNotPerformUnrelatedRepositoryOperations() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ String result = controller.createProduct("Triangle", 15.0, 2, 44, "Stick");
+ assertEquals((String) "Produto Criado", (String) result);
+ verify(ProductRepository, times(1)).save(any(ProductModel.class));
+ verify(Log, times(1)).save(any(LogModel.class));
+ // Ensure no unrelated interactions
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ @Test
+ @Tag("valid")
+ @DisplayName("Scenario 20: Log route string remains literal 'product/create/{userid}' and does not interpolate userId")
+ public void createProductUsesLiteralRouteInLog() {
+ when(Date.getTimeNow()).thenReturn(FIXED_TIME);
+ int distinctiveUserId = 9999;
+ String result = controller.createProduct("Harp", 5000.0, 42, distinctiveUserId, "Strings");
+ assertEquals((String) "Produto Criado", (String) result);
+ ArgumentCaptor logCaptor = ArgumentCaptor.forClass(LogModel.class);
+ verify(Log).save(logCaptor.capture());
+ assertLogModelContainsValues(logCaptor.getValue(), "product/create/{userid}", "Product", FIXED_TIME);
+ verify(ProductRepository).save(any(ProductModel.class));
+ verifyNoMoreInteractions(ProductRepository, Log);
+ }
+
+ // Helper assertion methods
+ private void assertProductModelContainsValues(ProductModel model, String description, double price, int musicId,
+ int userId, String listProduct) {
+ assertNotNull((Object) model);
+ List stringValues = readAllFieldValuesOfType(model, String.class);
+ List doubleValues = readAllDoubleFieldValues(model);
+ List intValues = readAllIntFieldValues(model);
+ assertTrue(stringValues.contains(description), "Expected description not found in ProductModel fields");
+ // For listProduct empty/unicode cases
+ assertTrue(stringValues.contains(listProduct), "Expected listProduct not found in ProductModel fields");
+ if (Double.isNaN((double) price)) {
+ boolean anyNaN = doubleValues.stream().anyMatch(d -> Double.isNaN(d.doubleValue()));
+ assertTrue(anyNaN, "Expected NaN price not found in ProductModel fields");
+ }
+ else {
+ assertTrue(doubleValues.stream().anyMatch(d -> Double.compare(d, price) == 0),
+ "Expected price not found in ProductModel fields");
+ }
+ assertTrue(intValues.contains((Integer) musicId), "Expected musicId not found in ProductModel fields");
+ assertTrue(intValues.contains((Integer) userId), "Expected userId not found in ProductModel fields");
+ }
+
+ private void assertLogModelContainsValues(LogModel model, String expectedRoute, String expectedEntity,
+ LocalDateTime expectedTimestamp) {
+ assertNotNull((Object) model);
+ List stringValues = readAllFieldValuesOfType(model, String.class);
+ List dateValues = readAllLocalDateTimeFieldValues(model);
+ // Assert the literal route string, entity, and the exact stubbed timestamp are
+ // present
+ assertTrue(stringValues.contains(expectedRoute), "Expected route not found in LogModel fields");
+ assertTrue(stringValues.contains(expectedEntity), "Expected entity not found in LogModel fields");
+ assertTrue(dateValues.contains(expectedTimestamp), "Expected timestamp not found in LogModel fields");
+ }
+
+ private List readAllFieldValuesOfType(Object obj, Class type) {
+ List result = new ArrayList<>();
+ for (Field f : getAllFields(obj.getClass())) {
+ try {
+ f.setAccessible(true);
+ if (type.isAssignableFrom(f.getType())) {
+ Object value = f.get(obj);
+ if (value != null) {
+ result.add((String) value);
+ }
+ }
+ }
+ catch (IllegalAccessException ignored) {
+ }
+ }
+ return result;
+ }
+
+ private List readAllDoubleFieldValues(Object obj) {
+ List result = new ArrayList<>();
+ for (Field f : getAllFields(obj.getClass())) {
+ try {
+ f.setAccessible(true);
+ if (f.getType() == double.class) {
+ result.add((double) f.getDouble(obj));
+ }
+ else if (f.getType() == Double.class) {
+ Object val = f.get(obj);
+ if (val != null) {
+ result.add(((Double) val).doubleValue());
+ }
+ }
+ }
+ catch (IllegalAccessException ignored) {
+ }
+ }
+ return result;
+ }
+
+ private List readAllIntFieldValues(Object obj) {
+ List result = new ArrayList<>();
+ for (Field f : getAllFields(obj.getClass())) {
+ try {
+ f.setAccessible(true);
+ if (f.getType() == int.class) {
+ result.add((int) f.getInt(obj));
+ }
+ else if (f.getType() == Integer.class) {
+ Object val = f.get(obj);
+ if (val != null) {
+ result.add(((Integer) val).intValue());
+ }
+ }
+ }
+ catch (IllegalAccessException ignored) {
+ }
+ }
+ return result;
+ }
+
+ private List readAllLocalDateTimeFieldValues(Object obj) {
+ List result = new ArrayList<>();
+ for (Field f : getAllFields(obj.getClass())) {
+ try {
+ f.setAccessible(true);
+ if (f.getType() == LocalDateTime.class) {
+ Object val = f.get(obj);
+ if (val != null) {
+ result.add((LocalDateTime) val);
+ }
+ }
+ }
+ catch (IllegalAccessException ignored) {
+ }
+ }
+ return result;
+ }
+
+ private List getAllFields(Class> clazz) {
+ List fields = new ArrayList<>();
+ Class> current = clazz;
+ while (current != null && current != Object.class) {
+ Field[] declared = current.getDeclaredFields();
+ for (Field f : declared) {
+ fields.add(f);
+ }
+ current = current.getSuperclass();
+ }
+ return fields;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerDeleteProductTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerDeleteProductTest.java
new file mode 100644
index 00000000..964a1ea7
--- /dev/null
+++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerDeleteProductTest.java
@@ -0,0 +1,370 @@
+
+// ********RoostGPT********
+/*
+Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5
+
+ROOST_METHOD_HASH=deleteProduct_3207cdc0ca
+ROOST_METHOD_SIG_HASH=deleteProduct_2e765309a5
+
+Scenario 1: Successful deletion with a valid positive id
+
+Details:
+ TestName: deleteProductWithValidIdReturnsSuccessMessage
+ Description: Verifies that when a valid positive id is provided, the controller calls ProductModelRepository.deleteById with the same id and returns the fixed success message.
+
+Execution:
+ Arrange: Create a MarketController instance and inject a mocked ProductModelRepository. Ensure deleteById does not throw for the chosen id (e.g., 42). LogRepository can be mocked but is not used in this method.
+ Act: Invoke deleteProduct with id = 42.
+ Assert: Confirm the returned value is exactly "Produto Deletado". Additionally, check that ProductModelRepository.deleteById was invoked once with 42.
+
+Validation:
+ This assertion validates that the happy-path behavior works: the repository delete method is called and the user-facing message is returned exactly as defined. It confirms the wiring and control flow for a standard delete operation.
+
+
+Scenario 2: Repository throws when the id does not exist
+
+Details:
+ TestName: deleteProductWithNonExistingIdPropagatesException
+ Description: Ensures that if ProductModelRepository.deleteById throws a runtime exception due to a non-existent id, the controller does not swallow it and the exception propagates.
+
+Execution:
+ Arrange: Inject a mocked ProductModelRepository into MarketController. Configure deleteById to throw a RuntimeException when called with id = 9999.
+ Act: Invoke deleteProduct with id = 9999 and capture the thrown exception.
+ Assert: Verify that a RuntimeException is thrown and no success message is returned.
+
+Validation:
+ The test shows the method has no error handling for repository failures and allows exceptions to surface. This is important for understanding how higher layers (e.g., exception handlers) need to manage such errors.
+
+
+Scenario 3: Negative id input
+
+Details:
+ TestName: deleteProductWithNegativeIdPropagatesRepositoryError
+ Description: Checks behavior when a negative id is provided. The controller should pass the id to deleteById and propagate any exception thrown by the repository.
+
+Execution:
+ Arrange: Inject a mocked ProductModelRepository. Configure deleteById(-1) to throw a RuntimeException.
+ Act: Call deleteProduct with id = -1 and capture the thrown exception.
+ Assert: Verify that a RuntimeException is thrown and the method does not return "Produto Deletado".
+
+Validation:
+ Validates input boundary handling. Since the controller does not validate ids, it depends on the repository to handle invalid ids, and errors should propagate correctly.
+
+
+Scenario 4: Zero id input where repository accepts the value
+
+Details:
+ TestName: deleteProductWithZeroIdReturnsSuccessWhenRepositoryAllows
+ Description: Ensures that if the repository does not throw for id = 0, the controller returns the success message as usual.
+
+Execution:
+ Arrange: Mock ProductModelRepository such that deleteById(0) completes normally (no exception).
+ Act: Invoke deleteProduct with id = 0.
+ Assert: Confirm the returned value is "Produto Deletado" and deleteById was called with 0.
+
+Validation:
+ Demonstrates that the controller does not validate ids and will return success if the underlying repository accepts the id.
+
+
+Scenario 5: Very large id value
+
+Details:
+ TestName: deleteProductWithMaxIntIdPassesThroughAndReturnsSuccess
+ Description: Verifies that a very large id (e.g., Integer.MAX_VALUE) is passed through to the repository and results in success if no exception is thrown.
+
+Execution:
+ Arrange: Mock ProductModelRepository so that deleteById(Integer.MAX_VALUE) completes without exceptions.
+ Act: Call deleteProduct with id = Integer.MAX_VALUE.
+ Assert: Ensure the returned string is "Produto Deletado" and deleteById was invoked with Integer.MAX_VALUE.
+
+Validation:
+ Confirms robustness with boundary numeric values and ensures the controller does not alter the id.
+
+
+Scenario 6: Repository interaction happens exactly once
+
+Details:
+ TestName: deleteProductInvokesRepositoryExactlyOnce
+ Description: Ensures the controller does not perform multiple repository deletions for a single request.
+
+Execution:
+ Arrange: Provide a mocked ProductModelRepository. Allow deleteById to run without throwing.
+ Act: Invoke deleteProduct with a sample id (e.g., 7).
+ Assert: Verify that ProductModelRepository.deleteById was called exactly once with id = 7. Assert that the returned string is "Produto Deletado".
+
+Validation:
+ Confirms there are no duplicate side effects and the method’s interaction with the repository is minimal and precise.
+
+
+Scenario 7: No logging occurs during deletion
+
+Details:
+ TestName: deleteProductDoesNotUseLogRepositoryOrDateField
+ Description: Validates that the LogRepository and the Date (LogModel) field are not used by deleteProduct.
+
+Execution:
+ Arrange: Inject mocks for ProductModelRepository and LogRepository. Ensure deleteById completes normally.
+ Act: Call deleteProduct with a valid id (e.g., 11).
+ Assert: Check that there are no interactions with the LogRepository mock and that the return value is "Produto Deletado".
+
+Validation:
+ Ensures the method’s scope is limited to deletion and does not perform logging or time-stamping, matching the implementation.
+
+
+Scenario 8: HTTP DELETE mapping returns success and body when id is numeric
+
+Details:
+ TestName: deleteEndpointWithValidNumericIdReturns200AndMessage
+ Description: Through a controller-layer test (e.g., using a Spring MVC test harness), verify that DELETE /product/delete/{id} with a numeric id triggers deleteById and returns the expected response body.
+
+Execution:
+ Arrange: Configure a test slice with MarketController and a mocked ProductModelRepository. Stub deleteById to do nothing for id = 5.
+ Act: Perform an HTTP DELETE to /product/delete/5.
+ Assert: Expect HTTP 200 OK (or default success) and response body "Produto Deletado". Also verify deleteById(5) was called.
+
+Validation:
+ Confirms request mapping, path variable binding, and response serialization behave correctly for a standard delete request.
+
+
+Scenario 9: HTTP DELETE with non-numeric id results in client error
+
+Details:
+ TestName: deleteEndpointWithNonNumericIdReturnsClientError
+ Description: Verifies that invoking DELETE /product/delete/{id} with a non-numeric value (e.g., "abc") fails binding and returns a 400-series client error without calling the repository.
+
+Execution:
+ Arrange: Stand up the controller in a web-layer test with ProductModelRepository mocked. Do not stub deleteById since it should not be reached.
+ Act: Perform DELETE /product/delete/abc.
+ Assert: Expect a 400 Bad Request (or equivalent) due to path variable binding failure. Verify ProductModelRepository.deleteById was never called.
+
+Validation:
+ Ensures path variable type enforcement and protects the repository from invalid inputs at the web layer.
+
+
+Scenario 10: Repeated deletion attempts for the same id
+
+Details:
+ TestName: deleteProductRepeatedCallsFirstSucceedsSecondThrows
+ Description: Simulates two consecutive deletions on the same id where the first call succeeds and the second call leads to a repository exception (e.g., because the entity was already deleted).
+
+Execution:
+ Arrange: Mock ProductModelRepository so that the first deleteById(21) call completes normally and the second call throws a RuntimeException.
+ Act: Call deleteProduct(21) once and capture the result; then call deleteProduct(21) again and capture the exception.
+ Assert: First result equals "Produto Deletado"; second invocation throws a RuntimeException.
+
+Validation:
+ Demonstrates behavior in idempotency-like scenarios and confirms that the controller consistently returns success only when the repository does not error.
+
+
+Scenario 11: Missing repository injection leads to NullPointerException
+
+Details:
+ TestName: deleteProductWithoutInjectedRepositoryThrowsNullPointer
+ Description: Validates that the controller relies on dependency injection for ProductModelRepository and fails if it is not provided.
+
+Execution:
+ Arrange: Instantiate MarketController without setting ProductModelRepository (leave it null).
+ Act: Invoke deleteProduct with any id (e.g., 3) and observe behavior.
+ Assert: Expect a NullPointerException (or similar) due to calling deleteById on a null repository reference.
+
+Validation:
+ Highlights the necessity of proper DI configuration in the application context for this controller to function.
+
+
+Scenario 12: Exact success message verification
+
+Details:
+ TestName: deleteProductReturnsExactPortugueseMessage
+ Description: Ensures that the returned success message matches the exact expected Portuguese string, including capitalization and spacing.
+
+Execution:
+ Arrange: Mock ProductModelRepository so deleteById executes normally.
+ Act: Call deleteProduct with a valid id (e.g., 2).
+ Assert: Compare the returned string exactly to "Produto Deletado" (no extra spaces, correct capitalization).
+
+Validation:
+ Confirms the user-facing response is stable and localized as implemented, preventing regressions in message content.
+
+
+Scenario 13: No dependence on LogModel Date state
+
+Details:
+ TestName: deleteProductBehaviorIndependentOfDateField
+ Description: Ensures that the Date field (a LogModel instance) does not affect the behavior or return value of deleteProduct.
+
+Execution:
+ Arrange: Prepare MarketController with a mocked ProductModelRepository and a pre-initialized Date field. Ensure deleteById completes normally.
+ Act: Call deleteProduct with any valid id (e.g., 8).
+ Assert: Verify the return value is "Produto Deletado" and repository interaction occurs as expected, regardless of any state in the Date field.
+
+Validation:
+ Confirms the method’s logic is isolated to deletion and not influenced by unrelated fields in the controller.
+
+*/
+
+// ********RoostGPT********
+
+package com.medeiros.SPRINGProject.Controllers;
+
+import com.medeiros.SPRINGProject.Models.LogRepository;
+import com.medeiros.SPRINGProject.Models.ProductModelRepository;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Assertions;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.web.servlet.MockMvc;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import org.junit.jupiter.api.*;
+import com.medeiros.SPRINGProject.Models.LogModel;
+import com.medeiros.SPRINGProject.Models.ProductModel;
+import org.springframework.web.bind.annotation.*;
+
+@WebMvcTest(MarketController.class)
+class MarketControllerDeleteProductTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private MarketController marketController;
+
+ @MockBean
+ private ProductModelRepository ProductRepository;
+
+ @MockBean
+ private LogRepository Log;
+
+ @Test
+ @Tag("valid")
+ public void deleteProductWithValidIdReturnsSuccessMessage() {
+ doNothing().when(ProductRepository).deleteById(42);
+ String result = marketController.deleteProduct(42);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ verify(ProductRepository, times(1)).deleteById(42);
+ }
+
+ @Test
+ @Tag("invalid")
+ public void deleteProductWithNonExistingIdPropagatesException() {
+ doThrow(new RuntimeException()).when(ProductRepository).deleteById(9999);
+ assertThrows(RuntimeException.class, () -> marketController.deleteProduct(9999));
+ verify(ProductRepository, times(1)).deleteById(9999);
+ }
+
+ @Test
+ @Tag("invalid")
+ public void deleteProductWithNegativeIdPropagatesRepositoryError() {
+ doThrow(new RuntimeException()).when(ProductRepository).deleteById(-1);
+ assertThrows(RuntimeException.class, () -> marketController.deleteProduct(-1));
+ verify(ProductRepository, times(1)).deleteById(-1);
+ }
+
+ @Test
+ @Tag("boundary")
+ public void deleteProductWithZeroIdReturnsSuccessWhenRepositoryAllows() {
+ doNothing().when(ProductRepository).deleteById(0);
+ String result = marketController.deleteProduct(0);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ verify(ProductRepository, times(1)).deleteById(0);
+ }
+
+ @Test
+ @Tag("boundary")
+ public void deleteProductWithMaxIntIdPassesThroughAndReturnsSuccess() {
+ int id = Integer.MAX_VALUE;
+ doNothing().when(ProductRepository).deleteById(id);
+ String result = marketController.deleteProduct(id);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ verify(ProductRepository, times(1)).deleteById(id);
+ }
+
+ @Test
+ @Tag("valid")
+ public void deleteProductInvokesRepositoryExactlyOnce() {
+ int id = 7;
+ doNothing().when(ProductRepository).deleteById(id);
+ String result = marketController.deleteProduct(id);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ verify(ProductRepository, times(1)).deleteById(id);
+ }
+
+ @Test
+ @Tag("valid")
+ public void deleteProductDoesNotUseLogRepositoryOrDateField() {
+ int id = 11;
+ doNothing().when(ProductRepository).deleteById(id);
+ String result = marketController.deleteProduct(id);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ Mockito.verifyNoInteractions(Log);
+ verify(ProductRepository, times(1)).deleteById(id);
+ }
+
+ @Test
+ @Tag("integration")
+ public void deleteEndpointWithValidNumericIdReturns200AndMessage() throws Exception {
+ int id = 5;
+ doNothing().when(ProductRepository).deleteById(id);
+ mockMvc.perform(delete("/product/delete/{id}", id))
+ .andExpect(status().isOk())
+ .andExpect(content().string("Produto Deletado"));
+ verify(ProductRepository, times(1)).deleteById(id);
+ }
+
+ @Test
+ @Tag("integration")
+ public void deleteEndpointWithNonNumericIdReturnsClientError() throws Exception {
+ mockMvc.perform(delete("/product/delete/abc")).andExpect(status().is4xxClientError());
+ verify(ProductRepository, never()).deleteById(anyInt());
+ }
+
+ @Test
+ @Tag("boundary")
+ public void deleteProductRepeatedCallsFirstSucceedsSecondThrows() {
+ int id = 21;
+ doNothing().doThrow(new RuntimeException()).when(ProductRepository).deleteById(id);
+ String result = marketController.deleteProduct(id);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ assertThrows(RuntimeException.class, () -> marketController.deleteProduct(id));
+ verify(ProductRepository, times(2)).deleteById(id);
+ }
+
+ @Test
+ @Tag("invalid")
+ public void deleteProductWithoutInjectedRepositoryThrowsNullPointer() {
+ MarketController controllerWithoutRepo = new MarketController();
+ assertThrows(NullPointerException.class, () -> controllerWithoutRepo.deleteProduct(3));
+ }
+
+ @Test
+ @Tag("valid")
+ public void deleteProductReturnsExactPortugueseMessage() {
+ int id = 2;
+ doNothing().when(ProductRepository).deleteById(id);
+ String result = marketController.deleteProduct(id);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ verify(ProductRepository, times(1)).deleteById(id);
+ }
+
+ @Test
+ @Tag("valid")
+ public void deleteProductBehaviorIndependentOfDateField() {
+ int id = 8;
+ doNothing().when(ProductRepository).deleteById(id);
+ String result = marketController.deleteProduct(id);
+ Assertions.assertEquals((String) "Produto Deletado", (String) result);
+ verify(ProductRepository, times(1)).deleteById(id);
+ Mockito.verifyNoInteractions(Log);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerShowAllProductsTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerShowAllProductsTest.java
new file mode 100644
index 00000000..01b8a290
--- /dev/null
+++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/MarketControllerShowAllProductsTest.java
@@ -0,0 +1,159 @@
+
+// ********RoostGPT********
+/*
+Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5
+
+ROOST_METHOD_HASH=showAllProducts_e3f486dbbd
+ROOST_METHOD_SIG_HASH=showAllProducts_f2f96749da
+
+Scenario 1: Returns all products when the repository contains multiple items
+
+Details:
+ TestName: returnsAllProductsWhenRepositoryReturnsNonEmptyList
+ Description: Validates that showAllProducts delegates to ProductRepository.findAll and returns the full collection of ProductModel instances when the repository has multiple products.
+
+Execution:
+ Arrange: Mock ProductRepository to return an Iterable containing several ProductModel instances with varied fields. Ensure Log and Date are present but do not define any behavior for them, as they are not used by this method.
+ Act: Invoke showAllProducts on a MarketController instance configured with the mocked ProductRepository.
+ Assert: Use JUnit assertions to verify that the returned Iterable contains exactly the same elements the mock provided (by content and count).
+
+Validation:
+ Confirms that the controller method correctly forwards the call to the repository and yields the data without transformation. This test ensures the primary use case works as intended and that all products are exposed through the endpoint logic.
+
+*/
+
+// ********RoostGPT********
+
+package com.medeiros.SPRINGProject.Controllers;
+
+import com.medeiros.SPRINGProject.Models.LogRepository;
+import com.medeiros.SPRINGProject.Models.ProductModel;
+import com.medeiros.SPRINGProject.Models.ProductModelRepository;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.junit.jupiter.api.*;
+import com.medeiros.SPRINGProject.Models.LogModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@ExtendWith(MockitoExtension.class)
+public class MarketControllerShowAllProductsTest {
+
+ @Mock
+ private ProductModelRepository ProductRepository;
+
+ @Mock
+ private LogRepository Log;
+
+ @InjectMocks
+ private MarketController controller;
+
+ @Test
+ @Tag("valid")
+ public void testReturnsAllProductsWhenRepositoryReturnsNonEmptyList() {
+ ProductModel p1 = new ProductModel("description-1", 10.0d, 1, 100, "list-1"); // TODO:
+ // Adjust
+ // sample
+ // values
+ // if
+ // model
+ // constraints
+ // change
+ ProductModel p2 = new ProductModel("description-2", 20.5d, 2, 200, "list-2"); // TODO:
+ // Adjust
+ // sample
+ // values
+ // if
+ // model
+ // constraints
+ // change
+ ProductModel p3 = new ProductModel("description-3", 30.99d, 3, 300, "list-3"); // TODO:
+ // Adjust
+ // sample
+ // values
+ // if
+ // model
+ // constraints
+ // change
+ List expectedList = Arrays.asList(p1, p2, p3);
+ Iterable expectedIterable = expectedList;
+ when(ProductRepository.findAll()).thenReturn(expectedIterable);
+ Iterable result = controller.showAllProducts();
+ assertSame((Iterable) expectedIterable, (Iterable) result);
+ List resultList = new ArrayList<>();
+ Iterator iterator = result.iterator();
+ while (iterator.hasNext()) {
+ resultList.add(iterator.next());
+ }
+ assertEquals((int) expectedList.size(), (int) resultList.size());
+ for (int i = 0; i < (int) expectedList.size(); i++) {
+ assertSame((ProductModel) expectedList.get(i), (ProductModel) resultList.get(i));
+ }
+ verify(ProductRepository, times(1)).findAll();
+ }
+
+ @Test
+ @Tag("boundary")
+ public void testReturnsEmptyIterableWhenRepositoryIsEmpty() {
+ List expectedList = Collections.emptyList();
+ Iterable expectedIterable = expectedList;
+ when(ProductRepository.findAll()).thenReturn(expectedIterable);
+ Iterable result = controller.showAllProducts();
+ assertSame((Iterable) expectedIterable, (Iterable) result);
+ List resultList = new ArrayList<>();
+ for (ProductModel pm : result) {
+ resultList.add(pm);
+ }
+ assertEquals((int) 0, (int) resultList.size());
+ verify(ProductRepository, times(1)).findAll();
+ }
+
+ @Test
+ @Tag("boundary")
+ public void testReturnsSingleProductWhenRepositoryHasOneItem() {
+ ProductModel p1 = new ProductModel("description-single", 15.75d, 10, 1000, "list-single"); // TODO:
+ // Adjust
+ // sample
+ // values
+ // if
+ // model
+ // constraints
+ // change
+ List expectedList = Collections.singletonList(p1);
+ Iterable expectedIterable = expectedList;
+ when(ProductRepository.findAll()).thenReturn(expectedIterable);
+ Iterable result = controller.showAllProducts();
+ assertSame((Iterable) expectedIterable, (Iterable) result);
+ List resultList = new ArrayList<>();
+ for (ProductModel pm : result) {
+ resultList.add(pm);
+ }
+ assertEquals((int) 1, (int) resultList.size());
+ assertSame((ProductModel) p1, (ProductModel) resultList.get(0));
+ verify(ProductRepository, times(1)).findAll();
+ }
+
+ @Test
+ @Tag("invalid")
+ public void testThrowsExceptionWhenRepositoryThrows() {
+ when(ProductRepository.findAll()).thenThrow(new RuntimeException());
+ assertThrows(RuntimeException.class, () -> controller.showAllProducts());
+ verify(ProductRepository, times(1)).findAll();
+ }
+
+}
\ No newline at end of file