@@ -554,4 +554,185 @@ void setBookMetadata_withReplaceMissingMode_shouldNotReplaceExistingTitle() {
554554 assertEquals ("Existing Title" , bookEntity .getMetadata ().getTitle (),
555555 "Title should NOT be replaced when using REPLACE_MISSING mode and title exists" );
556556 }
557+
558+ @ Test
559+ void setBookMetadata_withReplaceWhenProvided_shouldPreserveExistingAgeRatingWhenIncomingIsNull () {
560+ BookEntity bookEntity = createBookEntity ();
561+ bookEntity .getMetadata ().setAgeRating (16 );
562+ bookEntity .getMetadata ().setAgeRatingLocked (false );
563+
564+ BookMetadata newMetadata = new BookMetadata ();
565+ newMetadata .setTitle ("Some Title" );
566+
567+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_WHEN_PROVIDED );
568+
569+ bookMetadataUpdater .setBookMetadata (context );
570+
571+ assertEquals (16 , bookEntity .getMetadata ().getAgeRating (),
572+ "ageRating should be preserved when incoming value is null and mode is REPLACE_WHEN_PROVIDED" );
573+ }
574+
575+ @ Test
576+ void setBookMetadata_withReplaceWhenProvided_shouldPreserveExistingContentRatingWhenIncomingIsNull () {
577+ BookEntity bookEntity = createBookEntity ();
578+ bookEntity .getMetadata ().setContentRating ("Mature" );
579+ bookEntity .getMetadata ().setContentRatingLocked (false );
580+
581+ BookMetadata newMetadata = new BookMetadata ();
582+ newMetadata .setTitle ("Some Title" );
583+
584+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_WHEN_PROVIDED );
585+
586+ bookMetadataUpdater .setBookMetadata (context );
587+
588+ assertEquals ("Mature" , bookEntity .getMetadata ().getContentRating (),
589+ "contentRating should be preserved when incoming value is null and mode is REPLACE_WHEN_PROVIDED" );
590+ }
591+
592+ @ Test
593+ void setBookMetadata_withReplaceAll_shouldClearAgeRatingWhenIncomingIsNull () {
594+ BookEntity bookEntity = createBookEntity ();
595+ bookEntity .getMetadata ().setAgeRating (16 );
596+ bookEntity .getMetadata ().setAgeRatingLocked (false );
597+
598+ BookMetadata newMetadata = new BookMetadata ();
599+
600+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_ALL );
601+
602+ bookMetadataUpdater .setBookMetadata (context );
603+
604+ assertNull (bookEntity .getMetadata ().getAgeRating (),
605+ "ageRating should be cleared when incoming value is null and mode is REPLACE_ALL" );
606+ }
607+
608+ @ Test
609+ void setBookMetadata_withReplaceAll_shouldClearContentRatingWhenIncomingIsNull () {
610+ BookEntity bookEntity = createBookEntity ();
611+ bookEntity .getMetadata ().setContentRating ("Mature" );
612+ bookEntity .getMetadata ().setContentRatingLocked (false );
613+
614+ BookMetadata newMetadata = new BookMetadata ();
615+
616+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_ALL );
617+
618+ bookMetadataUpdater .setBookMetadata (context );
619+
620+ assertNull (bookEntity .getMetadata ().getContentRating (),
621+ "contentRating should be cleared when incoming value is null and mode is REPLACE_ALL" );
622+ }
623+
624+ @ Test
625+ void setBookMetadata_withReplaceWhenProvided_shouldUpdateAgeRatingWhenProvided () {
626+ BookEntity bookEntity = createBookEntity ();
627+ bookEntity .getMetadata ().setAgeRating (12 );
628+ bookEntity .getMetadata ().setAgeRatingLocked (false );
629+
630+ BookMetadata newMetadata = new BookMetadata ();
631+ newMetadata .setAgeRating (18 );
632+
633+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_WHEN_PROVIDED );
634+
635+ bookMetadataUpdater .setBookMetadata (context );
636+
637+ assertEquals (18 , bookEntity .getMetadata ().getAgeRating (),
638+ "ageRating should be updated when a new value is provided" );
639+ }
640+
641+ @ Test
642+ void setBookMetadata_withReplaceWhenProvided_shouldUpdateContentRatingWhenProvided () {
643+ BookEntity bookEntity = createBookEntity ();
644+ bookEntity .getMetadata ().setContentRating ("Teen" );
645+ bookEntity .getMetadata ().setContentRatingLocked (false );
646+
647+ BookMetadata newMetadata = new BookMetadata ();
648+ newMetadata .setContentRating ("Mature" );
649+
650+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_WHEN_PROVIDED );
651+
652+ bookMetadataUpdater .setBookMetadata (context );
653+
654+ assertEquals ("Mature" , bookEntity .getMetadata ().getContentRating (),
655+ "contentRating should be updated when a new value is provided" );
656+ }
657+
658+ @ Test
659+ void setBookMetadata_withReplaceWhenProvided_shouldPreserveExistingTitleWhenIncomingIsNull () {
660+ BookEntity bookEntity = createBookEntity ();
661+ bookEntity .getMetadata ().setTitle ("Existing Title" );
662+ bookEntity .getMetadata ().setTitleLocked (false );
663+
664+ BookMetadata newMetadata = new BookMetadata ();
665+
666+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_WHEN_PROVIDED );
667+
668+ bookMetadataUpdater .setBookMetadata (context );
669+
670+ assertEquals ("Existing Title" , bookEntity .getMetadata ().getTitle (),
671+ "Title should be preserved when incoming value is null and mode is REPLACE_WHEN_PROVIDED" );
672+ }
673+
674+ @ Test
675+ void setBookMetadata_withReplaceWhenProvided_shouldPreserveAuthorsWhenIncomingIsEmpty () {
676+ BookEntity bookEntity = createBookEntity ();
677+ Set <AuthorEntity > existingAuthors = new HashSet <>();
678+ existingAuthors .add (AuthorEntity .builder ().name ("Author1" ).build ());
679+ bookEntity .getMetadata ().setAuthors (existingAuthors );
680+ bookEntity .getMetadata ().setAuthorsLocked (false );
681+
682+ BookMetadata newMetadata = new BookMetadata ();
683+
684+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_WHEN_PROVIDED );
685+
686+ bookMetadataUpdater .setBookMetadata (context );
687+
688+ assertEquals (1 , bookEntity .getMetadata ().getAuthors ().size (),
689+ "Authors should be preserved when incoming is null/empty and mode is REPLACE_WHEN_PROVIDED" );
690+ }
691+
692+ @ Test
693+ void setBookMetadata_withReplaceWhenProvided_lockedFieldShouldNotBeUpdated () {
694+ BookEntity bookEntity = createBookEntity ();
695+ bookEntity .getMetadata ().setAgeRating (12 );
696+ bookEntity .getMetadata ().setAgeRatingLocked (true );
697+
698+ BookMetadata newMetadata = new BookMetadata ();
699+ newMetadata .setAgeRating (18 );
700+
701+ MetadataUpdateContext context = buildContext (bookEntity , newMetadata , MetadataReplaceMode .REPLACE_WHEN_PROVIDED );
702+
703+ bookMetadataUpdater .setBookMetadata (context );
704+
705+ assertEquals (12 , bookEntity .getMetadata ().getAgeRating (),
706+ "Locked ageRating should not be updated even when a new value is provided" );
707+ }
708+
709+ private BookEntity createBookEntity () {
710+ BookEntity bookEntity = new BookEntity ();
711+ bookEntity .setId (1L );
712+ BookMetadataEntity metadataEntity = new BookMetadataEntity ();
713+ metadataEntity .setBook (bookEntity );
714+ bookEntity .setMetadata (metadataEntity );
715+
716+ BookFileEntity primaryFile = new BookFileEntity ();
717+ primaryFile .setBook (bookEntity );
718+ primaryFile .setBookType (BookFileType .EPUB );
719+ primaryFile .setBookFormat (true );
720+ primaryFile .setFileSubPath ("sub" );
721+ primaryFile .setFileName ("file.epub" );
722+ bookEntity .setBookFiles (List .of (primaryFile ));
723+
724+ return bookEntity ;
725+ }
726+
727+ private MetadataUpdateContext buildContext (BookEntity bookEntity , BookMetadata newMetadata , MetadataReplaceMode replaceMode ) {
728+ MetadataUpdateWrapper wrapper = MetadataUpdateWrapper .builder ()
729+ .metadata (newMetadata )
730+ .build ();
731+
732+ return MetadataUpdateContext .builder ()
733+ .bookEntity (bookEntity )
734+ .metadataUpdateWrapper (wrapper )
735+ .replaceMode (replaceMode )
736+ .build ();
737+ }
557738}
0 commit comments