22
33import com .codesungrape .hmcts .bookapi .dto .BookRequest ;
44import com .codesungrape .hmcts .bookapi .entity .Book ;
5+ import com .codesungrape .hmcts .bookapi .exception .ResourceNotFoundException ;
56import com .codesungrape .hmcts .bookapi .repository .BookRepository ;
67import com .codesungrape .hmcts .bookapi .service .BookService ;
78import org .junit .jupiter .api .BeforeEach ;
1415import org .mockito .Mock ;
1516import org .mockito .junit .jupiter .MockitoExtension ;
1617
18+ import java .util .Optional ;
1719import java .util .UUID ;
1820import java .util .stream .Stream ;
1921
2022import static org .junit .jupiter .api .Assertions .assertEquals ;
2123import static org .junit .jupiter .api .Assertions .assertNotNull ;
2224import static org .junit .jupiter .api .Assertions .assertThrows ;
25+ import static org .junit .jupiter .api .Assertions .assertTrue ;
2326import static org .mockito .ArgumentMatchers .any ;
2427import static org .mockito .Mockito .never ;
2528import static org .mockito .Mockito .when ;
@@ -53,6 +56,10 @@ class BookServiceTest {
5356 private Book persistedBook ;
5457 private UUID testId ;
5558
59+ // --------------------------------------
60+ // Parameter Sources
61+ // --------------------------------------
62+
5663 // Provide test data, static method: can be called without creating an object.
5764 private static Stream <Arguments > provideLongFieldTestCases () {
5865 UUID testId = UUID .randomUUID ();
@@ -84,8 +91,9 @@ private static Stream<Arguments> provideLongFieldTestCases() {
8491 );
8592 }
8693
87- // --------- TESTS ------------
88-
94+ // --------------------------------------
95+ // Tests
96+ // --------------------------------------
8997 @ BeforeEach
9098 void setUp () {
9199 testId = UUID .randomUUID ();
@@ -109,6 +117,9 @@ void setUp() {
109117 .build ();
110118 }
111119
120+ // --------------------------------------
121+ // Tests: createBook
122+ // --------------------------------------
112123 @ Test
113124 void testCreateBook_Success () {
114125
@@ -129,13 +140,6 @@ void testCreateBook_Success() {
129140 verify (testBookRepository , times (1 )).save (any (Book .class ));
130141 }
131142
132- // CoPilot feedback:
133- // This test will fail because BookRequest uses @value from Lombok with @notblank validation.
134- // The @notblank constraint on the title field means that creating a BookRequest with a null
135- // title should trigger validation failure at the DTO level, not allow the object to be
136- // created. Either the test expectations are incorrect, or the DTO validation is not being
137- // applied. The same issue affects tests on lines 105-116, 119-127, and 130-138.
138-
139143 @ Test
140144 void testCreateBook_NullRequest_ThrowsException () {
141145 // Act & Assert
@@ -192,7 +196,6 @@ void testCreateBook_BlankTitle_ThrowsException() {
192196 );
193197 }
194198
195- // --------- Repository failures
196199 @ Test
197200 void testCreateBook_RepositoryFailure_ThrowsException () {
198201 // Arrange
@@ -213,7 +216,10 @@ void testCreateBook_RepositoryFailure_ThrowsException() {
213216 @ ParameterizedTest (name = "{0}" ) // Display the test name
214217 @ MethodSource ("provideLongFieldTestCases" )
215218 void testCreateBook_VeryLongFields_Success (
216- String testName , BookRequest request , Book expectedBook ) {
219+ String testName ,
220+ BookRequest request ,
221+ Book expectedBook
222+ ) {
217223
218224 // Arrange
219225 when (testBookRepository .save (any (Book .class ))).thenReturn (expectedBook );
@@ -260,4 +266,66 @@ void testCreateBook_SpecialCharactersInTitle_Success() {
260266 // Did the service perform the correct action on its dependency?
261267 verify (testBookRepository , times (1 )).save (any (Book .class ));
262268 }
269+
270+ // --------------------------------------------------------------------------------------------
271+ // Tests: deleteBookById(UUID)
272+ // -------------------------------------------------------------------------------------------
273+
274+ @ Test
275+ void testDelete_Book_ShouldThrowException_WhenIdNotFound () {
276+
277+ // Arrange: As goal is to test what happens when the resource doesn't exist,
278+ // we intentionally simulate DB returning NO result
279+ when (testBookRepository .findById (testId )).thenReturn (Optional .empty ());
280+
281+ // ACT and ASSERT: throw ResourceNotFoundException when calling the delete method.
282+ assertThrows (
283+ // custom exception to reflect business rules vs technical problem
284+ ResourceNotFoundException .class ,
285+ () -> testBookService .deleteBookById (testId )
286+ );
287+
288+ // Assert: ensure the save method was NEVER called.
289+ // proves delete business logic halts immediately when the resource isn't found.
290+ verify (testBookRepository , never ()).save (any (Book .class ));
291+ }
292+
293+ @ Test
294+ void testDeleteBookById_Success () {
295+
296+ // Arrange:
297+ persistedBook .setDeleted (false ); // ensure starting state
298+
299+ when (testBookRepository .findById (testId ))
300+ .thenReturn (Optional .of (persistedBook ));
301+
302+ when (testBookRepository .save (any (Book .class )))
303+ .thenReturn (persistedBook );
304+
305+ // Act: call the service method we are testing
306+ testBookService .deleteBookById (testId );
307+
308+ // Assert: the entity was marked deleted
309+ assertTrue (persistedBook .isDeleted ());
310+
311+ // Assert: repository methods were called correctly
312+ verify (testBookRepository , times (1 )).findById (testId );
313+ verify (testBookRepository , times (1 )).save (persistedBook );
314+ }
315+
316+ @ Test
317+ void testDeleteBookById_ShouldDoNothing_WhenAlreadyDeleted () {
318+
319+ // Arrange:
320+ persistedBook .setDeleted (true ); // ensure starting state
321+ when (testBookRepository .findById (testId ))
322+ .thenReturn (Optional .of (persistedBook ));
323+
324+ // Act: call the service method we are testing
325+ testBookService .deleteBookById (testId );
326+
327+ // Assert
328+ // Verify save was NEVER called (the if condition was false, so the if block was skipped)
329+ verify (testBookRepository , never ()).save (any (Book .class ));
330+ }
263331}
0 commit comments