From 04b3eefdf2375460f903c33b08786eaf3b6cae9b Mon Sep 17 00:00:00 2001 From: roost-io <8110509+mgdevstack@users.noreply.github.com> Date: Sun, 3 May 2026 20:07:50 +0530 Subject: [PATCH] Unit test generated by RoostGPT Using AI Model gpt-5 --- pom.xml | 249 +++++--- .../AppControllerCommentMusicByIdTest.java | 584 ++++++++++++++++++ .../AppControllerCreateCommentTest.java | 127 ++++ ...AppControllerGetCommentsByMusicIdTest.java | 100 +++ .../AppControllerLikeMusicByIdTest.java | 108 ++++ .../AppControllerShowMusicsTest.java | 145 +++++ 6 files changed, 1217 insertions(+), 96 deletions(-) create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerCommentMusicByIdTest.java create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerCreateCommentTest.java create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerGetCommentsByMusicIdTest.java create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerLikeMusicByIdTest.java create mode 100644 src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerShowMusicsTest.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/AppControllerCommentMusicByIdTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerCommentMusicByIdTest.java new file mode 100644 index 00000000..cc75e753 --- /dev/null +++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerCommentMusicByIdTest.java @@ -0,0 +1,584 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5 + +ROOST_METHOD_HASH=CommentMusicById_73ccb95717 +ROOST_METHOD_SIG_HASH=CommentMusicById_70a5563f90 + +Scenario 1: Successfully increments comment count and saves the updated music + +Details: + TestName: incrementsCommentCountAndPersistsMusic + Description: Validates that when a music record is found, the method increases its number of comments by exactly one and persists the updated entity using MusicRepository.save. + +Execution: + Arrange: + - Create a mock MusicRepository that returns a mock MusicModel when findById(id) is invoked. + - Stub MusicModel.getNumberOfComents() to return a known baseline value (e.g., 5). + - Prepare a mock LogRepository and a mock CommentsRepository. Do not stub any methods on CommentsRepository. + - Construct an instance of AppController wired with the mocked repositories. + Act: + - Call CommentMusicById with the chosen id. + Assert: + - Verify via Mockito that MusicModel.setNumberOfComents(6) is called exactly once (increment by 1). + - Verify that musicRepo.save is invoked exactly once with the same MusicModel instance returned by findById. + Validation: + - Confirms the core business logic: the comment counter is incremented atomically in-memory and persisted. + - Ensures correct repository interaction for updating the domain model. + +Scenario 2: Returns the exact success message in Portuguese + +Details: + TestName: returnsExactPortugueseSuccessMessage + Description: Ensures the method returns the literal success message "Música Alterada!" upon a successful update. + +Execution: + Arrange: + - Mock MusicRepository.findById(id) to return a mock MusicModel with getNumberOfComents() returning any integer. + - Mock LogRepository to accept saves. + - Build AppController with mocks. + Act: + - Capture the returned String from CommentMusicById(id). + Assert: + - Use assertEquals("Música Alterada!", returnedString). + Validation: + - Verifies user-facing feedback is precise and unchanged, which is important for clients relying on exact response text. + +Scenario 3: Logs the action once after saving the music + +Details: + TestName: logsOnceAfterMusicSaved + Description: Confirms that a single log entry is created via LogRepository.save after the music update is persisted. + +Execution: + Arrange: + - Mock MusicRepository to return a MusicModel and allow save. + - Mock LogRepository; no special stubbing needed. + - Build AppController with mocks. + Act: + - Invoke CommentMusicById(id). + Assert: + - Verify that musicRepo.save is called before Log.save (use Mockito InOrder). + - Verify Log.save is called exactly once with any LogModel instance (use ArgumentCaptor or argument matcher). + Validation: + - Ensures that logging is performed as a side-effect of a successful update and that only one log record is persisted. + +Scenario 4: Null music record leads to NullPointerException and prevents save/log + +Details: + TestName: throwsWhenMusicNotFoundById + Description: Ensures that if MusicRepository.findById(id) returns null, the method throws a NullPointerException and does not attempt to save the music or write a log. + +Execution: + Arrange: + - Mock MusicRepository.findById(id) to return null. + - Mock LogRepository. + - Build AppController with mocks. + Act: + - Call CommentMusicById(id) expecting an exception. + Assert: + - Use assertThrows(NullPointerException, () -> controller.CommentMusicById(id)). + - Verify that musicRepo.save is never called. + - Verify that Log.save is never called. + Validation: + - Ensures clear failure behavior when the target music entity is absent and avoids unintended side-effects. + +Scenario 5: Propagates exception when MusicRepository.findById throws + +Details: + TestName: propagatesExceptionFromFindById + Description: Verifies that if MusicRepository.findById throws a runtime exception, the method propagates that exception without saving or logging. + +Execution: + Arrange: + - Stub musicRepo.findById(id) to throw a RuntimeException. + - Mock LogRepository. + - Build AppController with mocks. + Act: + - Invoke CommentMusicById(id) and expect a RuntimeException. + Assert: + - Use assertThrows(RuntimeException, ...). + - Verify no calls to musicRepo.save. + - Verify no calls to Log.save. + Validation: + - Confirms fail-fast behavior and that partial side-effects do not occur when preconditions fail. + +Scenario 6: Propagates exception when MusicRepository.save throws and avoids logging + +Details: + TestName: propagatesWhenMusicSaveFails + Description: Ensures that if persisting the updated music throws an exception, the method propagates the exception and does not attempt to log. + +Execution: + Arrange: + - Mock findById to return a MusicModel with baseline comments. + - Stub musicRepo.save to throw a RuntimeException. + - Mock LogRepository. + - Build AppController. + Act: + - Invoke CommentMusicById(id) expecting a RuntimeException. + Assert: + - assertThrows(RuntimeException, ...). + - Verify that Log.save is never called. + Validation: + - Confirms that logging only happens after a successful persistence and that failures during save halt further processing. + +Scenario 7: Propagates exception when LogRepository.save throws, after music is already saved + +Details: + TestName: propagatesWhenLogSaveFailsAfterMusicSaved + Description: Validates that if logging fails after the music is saved, the exception is propagated and the successful music update remains attempted. + +Execution: + Arrange: + - Mock musicRepo.findById returning a valid MusicModel. + - Allow musicRepo.save to succeed. + - Stub Log.save to throw a RuntimeException. + - Build AppController. + Act: + - Call CommentMusicById(id) expecting a RuntimeException. + Assert: + - assertThrows(RuntimeException, ...). + - Verify musicRepo.save was called exactly once before Log.save. + Validation: + - Ensures correct call ordering and demonstrates partial success (music persisted attempt) with failure during logging being surfaced to the caller. + +Scenario 8: Increments from zero to one + +Details: + TestName: incrementsFromZeroToOne + Description: Checks that starting from zero comments, the updated value passed to setNumberOfComents is exactly one. + +Execution: + Arrange: + - Mock findById to return a MusicModel whose getNumberOfComents() returns 0. + - Mock repositories for save and log. + Act: + - Invoke CommentMusicById(id). + Assert: + - Verify setNumberOfComents(1) is called once on the MusicModel. + Validation: + - Confirms correct handling of the baseline count edge case. + +Scenario 9: Increments a negative comment count by one + +Details: + TestName: incrementsNegativeCommentCount + Description: Ensures that if the current comment count is negative (e.g., -3), the method still increments by one to -2 and persists it. + +Execution: + Arrange: + - Mock findById to return a MusicModel with getNumberOfComents() returning -3. + - Mock repositories for save and log. + Act: + - Invoke CommentMusicById(id). + Assert: + - Verify setNumberOfComents(-2) is called once. + - Verify musicRepo.save is called once with the same MusicModel. + Validation: + - Demonstrates robustness against unexpected negative state by applying a consistent increment rule. + +Scenario 10: Handles integer overflow when starting at Integer.MAX_VALUE + +Details: + TestName: handlesOverflowWhenMaxInt + Description: Validates Java integer overflow behavior: if starting at Integer.MAX_VALUE, incrementing results in Integer.MIN_VALUE being passed to setNumberOfComents. + +Execution: + Arrange: + - Mock findById to return a MusicModel whose getNumberOfComents() returns Integer.MAX_VALUE. + - Mock repositories for save and log. + Act: + - Call CommentMusicById(id). + Assert: + - Verify setNumberOfComents(Integer.MIN_VALUE) is called once. + - Verify musicRepo.save called once with the MusicModel. + Validation: + - Captures edge-of-range behavior, important for data integrity and potential overflow handling strategies. + +Scenario 11: Accepts and processes a negative id when a record exists + +Details: + TestName: processesNegativeIdWhenRecordExists + Description: Ensures that a negative id parameter is accepted and processed if MusicRepository.findById returns a valid MusicModel. + +Execution: + Arrange: + - Choose a negative id (e.g., -42). + - Mock findById(-42) to return a MusicModel with getNumberOfComents() returning a known value. + - Mock repositories for save and log. + Act: + - Invoke CommentMusicById(-42). + Assert: + - Verify the increment and save as in the happy path. + - Assert the returned string equals "Música Alterada!". + Validation: + - Confirms the method does not validate id sign and relies on repository behavior, which can be intentional in some systems. + +Scenario 12: Accepts and processes id zero when a record exists + +Details: + TestName: processesZeroIdWhenRecordExists + Description: Ensures that id = 0 is accepted and processed if the repository finds a corresponding MusicModel. + +Execution: + Arrange: + - Mock findById(0) to return a MusicModel with a known comment count. + - Mock repositories for save and log. + Act: + - Invoke CommentMusicById(0). + Assert: + - Verify setNumberOfComents is called with count + 1 and that save and log each occur once. + Validation: + - Verifies the method’s neutrality to specific id values where repository policy allows such ids. + +Scenario 13: No interactions with CommentsRepository or algorithmBlender in this method + +Details: + TestName: noUnrelatedDependenciesInvoked + Description: Confirms that CommentMusicById only uses MusicRepository and LogRepository, leaving CommentsRepository and algorithmBlender unused. + +Execution: + Arrange: + - Prepare mocks for MusicRepository, LogRepository, and CommentsRepository. + - Build AppController with these mocks and a default algorithmBlender instance. + Act: + - Invoke CommentMusicById(id). + Assert: + - Verify no interactions with CommentsRepository. + - (Optionally) Ensure no external algorithmBlender behavior is invoked by confirming only the expected interactions occur on repositories. + Validation: + - Guards against unintended side-effects or tight coupling with unrelated components. + +Scenario 14: Two sequential invocations increment comments twice and log twice + +Details: + TestName: twoSequentialCallsIncrementTwiceAndLogTwice + Description: Ensures idempotent per-call behavior: two direct calls result in two increments and two log entries. + +Execution: + Arrange: + - Mock MusicRepository.findById(id) to return the same MusicModel mock each time with getNumberOfComents() initially returning 10, then 11 on subsequent read if you model state; or simply verify the set calls. + - Mock repositories for save and log. + Act: + - Call CommentMusicById(id) twice sequentially. + Assert: + - Verify setNumberOfComents(11) and then setNumberOfComents(12) are called in sequence (use InOrder). + - Verify musicRepo.save is called twice. + - Verify Log.save is called twice. + Validation: + - Confirms that the method can be called repeatedly and that each invocation performs a distinct increment and log. + +Scenario 15: Verifies call order within a single invocation + +Details: + TestName: verifiesFindIncrementSaveThenLogOrder + Description: Checks the internal order of interactions: find, read current count, set incremented count, persist, then log. + +Execution: + Arrange: + - Mock MusicRepository and LogRepository. + - Configure MusicModel as in the happy path. + Act: + - Invoke CommentMusicById(id). + Assert: + - Use Mockito InOrder to assert the following relative order: + 1) musicRepo.findById(id) + 2) music.getNumberOfComents() + 3) music.setNumberOfComents(current + 1) + 4) musicRepo.save(music) + 5) Log.save(any LogModel) + Validation: + - Ensures a logical sequence that first updates domain state, persists it, and only then logs the action. + +Scenario 16: Demonstrates potential lost update under concurrent-like setup + +Details: + TestName: demonstratesLostUpdateRiskWithSameBaseline + Description: Simulates two invocations that both read the same baseline count to illustrate a potential lost update (both set to the same incremented value). + +Execution: + Arrange: + - Mock MusicRepository.findById(id) to always return a MusicModel whose getNumberOfComents() returns the same fixed value (e.g., 5) each time. + - Mock repositories for save and log. + Act: + - Call CommentMusicById(id) twice sequentially without altering the mock’s returned count between calls. + Assert: + - Verify that setNumberOfComents(6) is invoked twice (indicating both increments used the same baseline). + - Verify that musicRepo.save is called twice and Log.save is called twice. + Validation: + - Highlights the read-modify-write pattern’s susceptibility to lost updates without additional concurrency control, important for understanding system behavior under race conditions. + +*/ + +// ********RoostGPT******** + +package com.medeiros.SPRINGProject.Controllers; + +import com.medeiros.SPRINGProject.Models.*; +import com.medeiros.SPRINGProject.algorithm.algorithmBlender; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.Map; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.*; + +@ExtendWith(MockitoExtension.class) +public class AppControllerCommentMusicByIdTest { + + @Mock + private MusicRepository musicRepo; + + @Mock + private LogRepository Log; + + @Mock + private CommentsRepository CommentsRepo; + + @InjectMocks + private AppController controller; + + @Test + @Tag("valid") + public void testIncrementsCommentCountAndPersistsMusic() { + int id = 1; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(5); + String result = controller.CommentMusicById(id); + verify(music, times(1)).setNumberOfComents(6); + verify(musicRepo, times(1)).save(music); + assertEquals((String) "Música Alterada!", (String) result); + } + + @Test + @Tag("valid") + public void testReturnsExactPortugueseSuccessMessage() { + int id = 2; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(3); + String returned = controller.CommentMusicById(id); + assertEquals((String) "Música Alterada!", (String) returned); + } + + @Test + @Tag("valid") + public void testLogsOnceAfterMusicSaved() { + int id = 3; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(7); + String returned = controller.CommentMusicById(id); + InOrder inOrder = inOrder(musicRepo, Log, music); + inOrder.verify(musicRepo).findById(id); + inOrder.verify(music).getNumberOfComents(); + inOrder.verify(music).setNumberOfComents(8); + inOrder.verify(musicRepo).save(music); + ArgumentCaptor logCaptor = ArgumentCaptor.forClass(LogModel.class); + inOrder.verify(Log).save(logCaptor.capture()); + assertNotNull((Object) logCaptor.getValue()); + verify(Log, times(1)).save(any(LogModel.class)); + assertEquals((String) "Música Alterada!", (String) returned); + } + + @Test + @Tag("invalid") + public void testThrowsWhenMusicNotFoundById() { + int id = 4; // TODO adjust id if necessary + when(musicRepo.findById(id)).thenReturn(null); + assertThrows(NullPointerException.class, () -> controller.CommentMusicById(id)); + verify(musicRepo, never()).save(any(MusicModel.class)); + verify(Log, never()).save(any(LogModel.class)); + } + + @Test + @Tag("invalid") + public void testPropagatesExceptionFromFindById() { + int id = 5; // TODO adjust id if necessary + when(musicRepo.findById(id)).thenThrow(new RuntimeException("find failure")); + assertThrows(RuntimeException.class, () -> controller.CommentMusicById(id)); + verify(musicRepo, never()).save(any(MusicModel.class)); + verify(Log, never()).save(any(LogModel.class)); + } + + @Test + @Tag("invalid") + public void testPropagatesWhenMusicSaveFails() { + int id = 6; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(3); + when(musicRepo.save(music)).thenThrow(new RuntimeException("save failure")); + assertThrows(RuntimeException.class, () -> controller.CommentMusicById(id)); + verify(Log, never()).save(any(LogModel.class)); + } + + @Test + @Tag("invalid") + public void testPropagatesWhenLogSaveFailsAfterMusicSaved() { + int id = 7; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(8); + doNothing().when(musicRepo).save(music); + doThrow(new RuntimeException("log failure")).when(Log).save(any(LogModel.class)); + assertThrows(RuntimeException.class, () -> controller.CommentMusicById(id)); + InOrder inOrder = inOrder(musicRepo, Log, music); + inOrder.verify(musicRepo).findById(id); + inOrder.verify(music).getNumberOfComents(); + inOrder.verify(music).setNumberOfComents(9); + inOrder.verify(musicRepo).save(music); + inOrder.verify(Log).save(any(LogModel.class)); + } + + @Test + @Tag("boundary") + public void testIncrementsFromZeroToOne() { + int id = 8; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(0); + String res = controller.CommentMusicById(id); + verify(music, times(1)).setNumberOfComents(1); + verify(musicRepo, times(1)).save(music); + assertEquals((String) "Música Alterada!", (String) res); + } + + @Test + @Tag("boundary") + public void testIncrementsNegativeCommentCount() { + int id = 9; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(-3); + String res = controller.CommentMusicById(id); + verify(music, times(1)).setNumberOfComents(-2); + verify(musicRepo, times(1)).save(music); + assertEquals((String) "Música Alterada!", (String) res); + } + + @Test + @Tag("boundary") + public void testHandlesOverflowWhenMaxInt() { + int id = 10; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(Integer.MAX_VALUE); + String res = controller.CommentMusicById(id); + verify(music, times(1)).setNumberOfComents(Integer.MIN_VALUE); + verify(musicRepo, times(1)).save(music); + assertEquals((String) "Música Alterada!", (String) res); + } + + @Test + @Tag("boundary") + public void testProcessesNegativeIdWhenRecordExists() { + int id = -42; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(2); + String res = controller.CommentMusicById(id); + verify(music, times(1)).setNumberOfComents(3); + verify(musicRepo, times(1)).save(music); + assertEquals((String) "Música Alterada!", (String) res); + } + + @Test + @Tag("boundary") + public void testProcessesZeroIdWhenRecordExists() { + int id = 0; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(1); + String res = controller.CommentMusicById(id); + verify(music, times(1)).setNumberOfComents(2); + verify(musicRepo, times(1)).save(music); + verify(Log, times(1)).save(any(LogModel.class)); + assertEquals((String) "Música Alterada!", (String) res); + } + + @Test + @Tag("valid") + public void testNoUnrelatedDependenciesInvoked() { + int id = 11; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(4); + String res = controller.CommentMusicById(id); + verifyNoInteractions(CommentsRepo); + verify(musicRepo, times(1)).save(music); + verify(Log, times(1)).save(any(LogModel.class)); + assertEquals((String) "Música Alterada!", (String) res); + } + + @Test + @Tag("integration") + public void testTwoSequentialCallsIncrementTwiceAndLogTwice() { + int id = 12; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(10, 11); + String res1 = controller.CommentMusicById(id); + String res2 = controller.CommentMusicById(id); + InOrder inOrder = inOrder(musicRepo, Log, music); + inOrder.verify(musicRepo).findById(id); + inOrder.verify(music).getNumberOfComents(); + inOrder.verify(music).setNumberOfComents(11); + inOrder.verify(musicRepo).save(music); + inOrder.verify(Log).save(any(LogModel.class)); + inOrder.verify(musicRepo).findById(id); + inOrder.verify(music).getNumberOfComents(); + inOrder.verify(music).setNumberOfComents(12); + inOrder.verify(musicRepo).save(music); + inOrder.verify(Log).save(any(LogModel.class)); + verify(musicRepo, times(2)).save(music); + verify(Log, times(2)).save(any(LogModel.class)); + assertEquals((String) "Música Alterada!", (String) res1); + assertEquals((String) "Música Alterada!", (String) res2); + } + + @Test + @Tag("valid") + public void testVerifiesFindIncrementSaveThenLogOrder() { + int id = 13; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(15); + String res = controller.CommentMusicById(id); + InOrder inOrder = inOrder(musicRepo, music, Log); + inOrder.verify(musicRepo).findById(id); + inOrder.verify(music).getNumberOfComents(); + inOrder.verify(music).setNumberOfComents(16); + inOrder.verify(musicRepo).save(music); + inOrder.verify(Log).save(any(LogModel.class)); + assertEquals((String) "Música Alterada!", (String) res); + } + + @Test + @Tag("integration") + public void testDemonstratesLostUpdateRiskWithSameBaseline() { + int id = 14; // TODO adjust id if necessary + MusicModel music = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(music); + when(music.getNumberOfComents()).thenReturn(5); + String res1 = controller.CommentMusicById(id); + String res2 = controller.CommentMusicById(id); + verify(music, times(2)).setNumberOfComents(6); + verify(musicRepo, times(2)).save(music); + verify(Log, times(2)).save(any(LogModel.class)); + assertEquals((String) "Música Alterada!", (String) res1); + assertEquals((String) "Música Alterada!", (String) res2); + } + +} \ No newline at end of file diff --git a/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerCreateCommentTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerCreateCommentTest.java new file mode 100644 index 00000000..8bbd7e5d --- /dev/null +++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerCreateCommentTest.java @@ -0,0 +1,127 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5 + +ROOST_METHOD_HASH=createComment_e2e99943cb +ROOST_METHOD_SIG_HASH=createComment_4104bef389 + +Scenario 1: Successfully creates and logs a comment with a valid id and non-empty text + +Details: + TestName: createsAndLogsCommentWithValidInputs + Description: Verifies that when provided with a valid music id and a non-empty comment string, the method creates a CommentsModel, saves it via CommentsRepository, logs the operation via LogRepository, and returns the original comment string. + +Execution: + Arrange: Set up an AppController instance. Inject a mock CommentsRepository into the controller’s CommentsRepo field and a mock LogRepository into the controller’s Log field. Optionally spy or stub the Date field (LogModel) to return a fixed time from getTimeNow(), if feasible. Prepare a valid id (e.g., 42) and a comment string (e.g., "Great track!"). + Act: Invoke createComment with the prepared id and comment string. + Assert: + - Assert that the returned value equals the same "Great track!" string. + - Verify CommentsRepo.save is called exactly once with any CommentsModel instance. + - Verify Log.save is called exactly once with any LogModel instance. + +Validation: + Confirms the core flow is executed: a comment is persisted, an audit log entry is written, and the original input comment is returned unmodified. This demonstrates the happy-path behavior and the integration with both persistence and logging components. + +*/ + +// ********RoostGPT******** +package com.medeiros.SPRINGProject.Controllers; + +import com.medeiros.SPRINGProject.Models.CommentsModel; +import com.medeiros.SPRINGProject.Models.CommentsRepository; +import com.medeiros.SPRINGProject.Models.LogModel; +import com.medeiros.SPRINGProject.Models.LogRepository; +import org.junit.jupiter.api.Assertions; +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.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import static org.mockito.Mockito.*; +import org.junit.jupiter.api.*; +import com.medeiros.SPRINGProject.Models.*; +import com.medeiros.SPRINGProject.algorithm.algorithmBlender; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.Map; + +@ExtendWith(MockitoExtension.class) +public class AppControllerCreateCommentTest { + + @Mock + private CommentsRepository CommentsRepo; + + @Mock + private LogRepository Log; + + @Spy + private LogModel Date = new LogModel(); + + @InjectMocks + private AppController appController; + + @Test + @Tag("valid") + @Tag("integration") + public void testCreatesAndLogsCommentWithValidInputs() { + int id = 42; + String inputComment = "Great track!"; + doReturn("2024-01-01T00:00:00Z").when(Date).getTimeNow(); + String result = appController.createComment(id, inputComment); + Assertions.assertEquals((String) inputComment, (String) result); + verify(CommentsRepo, times(1)).save(any(CommentsModel.class)); + verify(Log, times(1)).save(any(LogModel.class)); + } + + @Test + @Tag("boundary") + public void testCreatesCommentWithEmptyText() { + int id = 10; + String inputComment = ""; + String result = appController.createComment(id, inputComment); + Assertions.assertEquals((String) inputComment, (String) result); + verify(CommentsRepo, times(1)).save(any(CommentsModel.class)); + verify(Log, times(1)).save(any(LogModel.class)); + } + + @Test + @Tag("invalid") + public void testCreatesCommentWithNegativeId() { + int id = -1; // boundary/invalid id + String inputComment = "Comment with negative id"; + String result = appController.createComment(id, inputComment); + Assertions.assertEquals((String) inputComment, (String) result); + verify(CommentsRepo, times(1)).save(any(CommentsModel.class)); + verify(Log, times(1)).save(any(LogModel.class)); + } + + @Test + @Tag("invalid") + public void testThrowsWhenCommentsRepoSaveFails() { + int id = 5; + String inputComment = "Fails on save"; + doThrow(new RuntimeException("save failed")).when(CommentsRepo).save(any(CommentsModel.class)); + Assertions.assertThrows(RuntimeException.class, () -> { + appController.createComment(id, inputComment); + }); + verify(CommentsRepo, times(1)).save(any(CommentsModel.class)); + verify(Log, times(0)).save(any(LogModel.class)); + } + + @Test + @Tag("invalid") + public void testThrowsWhenLogSaveFailsAfterCommentSaved() { + int id = 7; + String inputComment = "Log will fail"; + doThrow(new RuntimeException("log failed")).when(Log).save(any(LogModel.class)); + Assertions.assertThrows(RuntimeException.class, () -> { + appController.createComment(id, inputComment); + }); + verify(CommentsRepo, times(1)).save(any(CommentsModel.class)); + verify(Log, times(1)).save(any(LogModel.class)); + } + +} \ No newline at end of file diff --git a/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerGetCommentsByMusicIdTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerGetCommentsByMusicIdTest.java new file mode 100644 index 00000000..ddec1221 --- /dev/null +++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerGetCommentsByMusicIdTest.java @@ -0,0 +1,100 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5 + +ROOST_METHOD_HASH=getCommentsByMusicId_9981fa8dac +ROOST_METHOD_SIG_HASH=getCommentsByMusicId_b64d712e58 + +Scenario 1: Returns constant "a" for a typical positive MusicId + +Details: + TestName: returnsConstantAForValidMusicId + Description: Verify that calling AppController.getCommentsByMusicId with a normal positive integer returns the constant string "a", as implemented. + +Execution: + Arrange: Instantiate AppController. Do not inject any dependencies since the method does not use them. + Act: Invoke getCommentsByMusicId with a MusicId such as 42. + Assert: Use JUnit assertions to confirm the result equals "a". + +Validation: + Confirming the exact constant output ensures the current implementation is honored and helps detect unintended changes to the method’s return value in future refactoring. + +*/ + +// ********RoostGPT******** + +package com.medeiros.SPRINGProject.Controllers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +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.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.*; +import com.medeiros.SPRINGProject.Models.*; +import com.medeiros.SPRINGProject.algorithm.algorithmBlender; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.Map; + +@ExtendWith(MockitoExtension.class) +public class AppControllerGetCommentsByMusicIdTest { + + @Test + @Tag("valid") + @DisplayName("Returns constant 'a' for a typical positive MusicId") + public void returnsConstantAForValidMusicId() { + final AppController controller = new AppController(); + final int musicId = 42; // TODO: Adjust MusicId as needed + final String expected = "a"; + final String actual = controller.getCommentsByMusicId(musicId); + assertEquals((String) expected, (String) actual); + } + + @Test + @Tag("boundary") + @DisplayName("Returns constant 'a' for MusicId = 0 (boundary case)") + public void returnsConstantAForZeroMusicId() { + final AppController controller = new AppController(); + final int musicId = 0; + final String expected = "a"; + final String actual = controller.getCommentsByMusicId(musicId); + assertEquals((String) expected, (String) actual); + } + + @Test + @Tag("invalid") + @DisplayName("Returns constant 'a' for a negative MusicId") + public void returnsConstantAForNegativeMusicId() { + final AppController controller = new AppController(); + final int musicId = -1; + final String expected = "a"; + final String actual = controller.getCommentsByMusicId(musicId); + assertEquals((String) expected, (String) actual); + } + + @Test + @Tag("boundary") + @DisplayName("Returns constant 'a' for Integer.MAX_VALUE MusicId") + public void returnsConstantAForMaxIntMusicId() { + final AppController controller = new AppController(); + final int musicId = Integer.MAX_VALUE; + final String expected = "a"; + final String actual = controller.getCommentsByMusicId(musicId); + assertEquals((String) expected, (String) actual); + } + + @Test + @Tag("boundary") + @DisplayName("Returns constant 'a' for Integer.MIN_VALUE MusicId") + public void returnsConstantAForMinIntMusicId() { + final AppController controller = new AppController(); + final int musicId = Integer.MIN_VALUE; + final String expected = "a"; + final String actual = controller.getCommentsByMusicId(musicId); + assertEquals((String) expected, (String) actual); + } + +} \ No newline at end of file diff --git a/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerLikeMusicByIdTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerLikeMusicByIdTest.java new file mode 100644 index 00000000..c8ea19db --- /dev/null +++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerLikeMusicByIdTest.java @@ -0,0 +1,108 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5 + +ROOST_METHOD_HASH=LikeMusicById_9bce296f63 +ROOST_METHOD_SIG_HASH=LikeMusicById_86ac50a5c5 + +Scenario 1: Increments likes and returns success message for a valid music id + +Details: + TestName: incrementsLikesAndReturnsSuccessMessage + Description: Verifies the standard success path where a music record is found by id, its like count is incremented by one, the updated entity is saved, a log entry is created, and the method returns the expected success String. + +Execution: + Arrange: Configure MusicRepository to return a mock MusicModel for a given id. Stub getNumberOfLikes() to return a known value (e.g., 10). Prepare LogRepository as a mock to accept a LogModel. + Act: Invoke LikeMusicById with the configured id. + Assert: + - Assert that the returned value equals "Música Alterada!". + - Verify that setNumberOfLikes(11) is called once on the MusicModel (original 10 + 1). + - Verify that musicRepo.save is invoked once with the same MusicModel instance. + - Verify that Log.save is invoked once with a non-null LogModel instance. + +Validation: + This confirms that the method performs the core business logic: increments likes by exactly one, persists the change, logs the action, and returns the expected confirmation message. It ensures the happy path works end-to-end. + +*/ + +// ********RoostGPT******** + +package com.medeiros.SPRINGProject.Controllers; + +import com.medeiros.SPRINGProject.Models.*; +import com.medeiros.SPRINGProject.algorithm.algorithmBlender; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.Map; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import static org.mockito.Mockito.*; +import org.junit.jupiter.api.*; + +@ExtendWith(MockitoExtension.class) +public class AppControllerLikeMusicByIdTest { + + @InjectMocks + private AppController appController; + + @Mock + private MusicRepository musicRepo; + + @Mock + private LogRepository Log; + + @Tag("valid") + @Test + public void testIncrementsLikesAndReturnsSuccessMessage() { + int id = 1; // TODO: adjust ID if needed for different cases + MusicModel mockMusic = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(mockMusic); + when(mockMusic.getNumberOfLikes()).thenReturn(10); + String result = appController.LikeMusicById(id); + assertEquals((String) "Música Alterada!", (String) result); + verify(musicRepo, times(1)).findById(id); + verify(mockMusic, times(1)).getNumberOfLikes(); + verify(mockMusic, times(1)).setNumberOfLikes(11); + verify(musicRepo, times(1)).save(mockMusic); + verify(Log, times(1)).save(argThat(log -> log != null)); + verifyNoMoreInteractions(musicRepo, Log, mockMusic); + } + + @Tag("invalid") + @Test + public void testThrowsExceptionWhenMusicNotFound() { + int id = 999; // TODO: adjust ID to simulate a non-existing music + when(musicRepo.findById(id)).thenReturn(null); + NullPointerException ex = assertThrows(NullPointerException.class, () -> { + appController.LikeMusicById(id); + }); + assertNotNull((NullPointerException) ex); + verify(musicRepo, times(1)).findById(id); + verifyNoMoreInteractions(musicRepo); + verifyNoInteractions(Log); + } + + @Tag("boundary") + @Test + public void testIncrementsLikesAtIntegerMaxValueBoundary() { + int id = 2; // TODO: adjust ID if needed for boundary testing + MusicModel mockMusic = mock(MusicModel.class); + when(musicRepo.findById(id)).thenReturn(mockMusic); + when(mockMusic.getNumberOfLikes()).thenReturn(Integer.MAX_VALUE); + String result = appController.LikeMusicById(id); + assertEquals((String) "Música Alterada!", (String) result); + verify(mockMusic, times(1)).setNumberOfLikes((int) Integer.MIN_VALUE); + verify(musicRepo, times(1)).save(mockMusic); + verify(Log, times(1)).save(argThat(log -> log != null)); + verifyNoMoreInteractions(musicRepo, Log, mockMusic); + } + +} \ No newline at end of file diff --git a/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerShowMusicsTest.java b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerShowMusicsTest.java new file mode 100644 index 00000000..192deb18 --- /dev/null +++ b/src/test/java/com/medeiros/SPRINGProject/Controllers/AppControllerShowMusicsTest.java @@ -0,0 +1,145 @@ + +// ********RoostGPT******** +/* +Test generated by RoostGPT for test maven-music-github using AI Type Azure Open AI and AI Model gpt-5 + +ROOST_METHOD_HASH=showMusics_52ef65b78a +ROOST_METHOD_SIG_HASH=showMusics_caba7c787a + +Scenario 1: Returns the same Map produced by algorithmBlender when repository provides typical data + +Details: + TestName: returnsAlgorithmMapWhenRepoHasData + Description: Verifies that showMusics delegates to MusicRepository.findAll(), forwards the resulting Iterable to algorithmBlender.algorithmCalc, and returns exactly the Map provided by algorithmBlender without alteration. + +Execution: + Arrange: Set up a mock MusicRepository that returns a non-empty Iterable. Prepare a mock algorithmBlender configured to return a predetermined Map. Replace the ab field in an AppController instance with this mock (e.g., via reflection), and inject the mocked MusicRepository into the controller instance. + Act: Invoke showMusics() on the controller. + Assert: Use assertions to confirm that the returned Map is the exact same instance as the Map produced by the mock algorithmBlender. Verify the repository’s findAll() was called once and algorithmBlender.algorithmCalc(...) was invoked exactly once with the Iterable returned by findAll(). + +Validation: + This validates correct orchestration: the method must call repository.findAll(), pass the result to algorithmBlender, and return the algorithm’s output unchanged. It ensures no unintended transformations occur in AppController, which is essential for correct separation of concerns. + +*/ + +// ********RoostGPT******** + +package com.medeiros.SPRINGProject.Controllers; + +import com.medeiros.SPRINGProject.Models.*; +import com.medeiros.SPRINGProject.algorithm.algorithmBlender; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.Map; +import com.medeiros.SPRINGProject.Controllers.AppController; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.*; + +@ExtendWith(MockitoExtension.class) +public class AppControllerShowMusicsTest { + + @Mock + private MusicRepository musicRepo; + + @Mock + private algorithmBlender ab; + + @InjectMocks + private AppController controller; + + @Test + @Tag("valid") + public void testReturnsAlgorithmMapWhenRepoHasData() { + List iterableFromRepo = Arrays.asList(mock(MusicModel.class), mock(MusicModel.class)); + Map expectedMap = new HashMap<>(); + expectedMap.put("rock", 2); + expectedMap.put("pop", 1); + when(musicRepo.findAll()).thenReturn(iterableFromRepo); + when(ab.algorithmCalc(iterableFromRepo)).thenReturn(expectedMap); + Map actual = controller.showMusics(); + assertSame((Map) expectedMap, (Map) actual); + verify(musicRepo, times(1)).findAll(); + verify(ab, times(1)).algorithmCalc(iterableFromRepo); + verifyNoMoreInteractions(musicRepo, ab); + } + + @Test + @Tag("boundary") + public void testReturnsMapWhenRepoReturnsEmptyIterable() { + Iterable emptyIterable = Collections.emptyList(); + Map expectedMap = Collections.emptyMap(); + when(musicRepo.findAll()).thenReturn(emptyIterable); + when(ab.algorithmCalc(emptyIterable)).thenReturn(expectedMap); + Map actual = controller.showMusics(); + assertSame((Map) expectedMap, (Map) actual); + verify(musicRepo, times(1)).findAll(); + verify(ab, times(1)).algorithmCalc(emptyIterable); + verifyNoMoreInteractions(musicRepo, ab); + } + + @Test + @Tag("boundary") + public void testReturnsNullWhenAlgorithmReturnsNull() { + Iterable iterableFromRepo = Collections.singletonList(mock(MusicModel.class)); + when(musicRepo.findAll()).thenReturn(iterableFromRepo); + when(ab.algorithmCalc(iterableFromRepo)).thenReturn(null); + Map actual = controller.showMusics(); + assertNull((Map) actual); + verify(musicRepo, times(1)).findAll(); + verify(ab, times(1)).algorithmCalc(iterableFromRepo); + verifyNoMoreInteractions(musicRepo, ab); + } + + @Test + @Tag("invalid") + public void testPassesNullToAlgorithmWhenRepoReturnsNull() { + Map expectedMap = new HashMap<>(); + expectedMap.put("jazz", 3); + when(musicRepo.findAll()).thenReturn(null); + when(ab.algorithmCalc(null)).thenReturn(expectedMap); + Map actual = controller.showMusics(); + assertSame((Map) expectedMap, (Map) actual); + verify(musicRepo, times(1)).findAll(); + verify(ab, times(1)).algorithmCalc(null); + verifyNoMoreInteractions(musicRepo, ab); + } + + @Test + @Tag("invalid") + public void testPropagatesExceptionWhenRepoThrows() { + when(musicRepo.findAll()).thenThrow(new RuntimeException("repo failure")); // TODO adjust message if needed + RuntimeException ex = assertThrows(RuntimeException.class, () -> controller.showMusics()); + assertTrue((boolean) (ex.getMessage() != null && !ex.getMessage().isEmpty())); + verify(musicRepo, times(1)).findAll(); + verifyNoInteractions(ab); + } + + @Test + @Tag("invalid") + public void testPropagatesExceptionWhenAlgorithmThrows() { + Iterable iterableFromRepo = Collections.singletonList(mock(MusicModel.class)); + when(musicRepo.findAll()).thenReturn(iterableFromRepo); + when(ab.algorithmCalc(iterableFromRepo)).thenThrow(new IllegalStateException("algorithm failure")); // TODO + // adjust + // message + // if + // needed + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> controller.showMusics()); + assertTrue((boolean) (ex.getMessage() != null && !ex.getMessage().isEmpty())); + verify(musicRepo, times(1)).findAll(); + verify(ab, times(1)).algorithmCalc(iterableFromRepo); + verifyNoMoreInteractions(musicRepo, ab); + } + +} \ No newline at end of file