2121
2222import java .util .Arrays ;
2323import java .util .Collections ;
24+ import java .util .Map ;
2425
2526import org .bson .Document ;
2627import org .bson .types .ObjectId ;
2728import org .junit .jupiter .api .BeforeEach ;
29+ import org .junit .jupiter .api .DisplayName ;
2830import org .junit .jupiter .api .Test ;
2931import org .junit .jupiter .api .extension .ExtendWith ;
3032import org .mockito .ArgumentCaptor ;
3537import org .mockito .quality .Strictness ;
3638
3739import org .springframework .dao .InvalidDataAccessApiUsageException ;
40+ import org .springframework .data .mapping .context .MappingContext ;
41+ import org .springframework .data .mapping .model .SpELContext ;
3842import org .springframework .data .mongodb .MongoDatabaseFactory ;
3943import org .springframework .data .mongodb .core .DocumentTestUtils ;
4044import org .springframework .data .mongodb .core .MongoExceptionTranslator ;
4347import com .mongodb .client .FindIterable ;
4448import com .mongodb .client .MongoCollection ;
4549import com .mongodb .client .MongoDatabase ;
50+ import org .springframework .data .mongodb .core .mapping .DocumentReference ;
51+ import org .springframework .data .mongodb .core .mapping .MongoPersistentEntity ;
52+ import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty ;
4653
4754/**
4855 * Unit tests for {@link DefaultDbRefResolver}.
@@ -58,6 +65,8 @@ class DefaultDbRefResolverUnitTests {
5865 @ Mock MongoDatabase dbMock ;
5966 @ Mock MongoCollection <Document > collectionMock ;
6067 @ Mock FindIterable <Document > cursorMock ;
68+ @ Mock MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > mappingContext ;
69+ @ Mock SpELContext spELContext ;
6170 private DefaultDbRefResolver resolver ;
6271
6372 @ BeforeEach
@@ -91,7 +100,7 @@ void bulkFetchShouldLoadDbRefsCorrectly() {
91100 }
92101
93102 @ Test // DATAMONGO-1194
94- void bulkFetchShouldThrowExceptionWhenUsingDifferntCollectionsWithinSetOfReferences () {
103+ void bulkFetchShouldThrowExceptionWhenUsingDifferentCollectionsWithinSetOfReferences () {
95104
96105 DBRef ref1 = new DBRef ("collection-1" , new ObjectId ());
97106 DBRef ref2 = new DBRef ("collection-2" , new ObjectId ());
@@ -134,4 +143,75 @@ void bulkFetchContainsDuplicates() {
134143
135144 assertThat (resolver .bulkFetch (Arrays .asList (ref1 , ref2 ))).containsExactly (document , document );
136145 }
146+
147+ @ Test // GH-5065
148+ @ DisplayName ("GH-5065: Empty Map with @DocumentReference annotation should deserialize to an empty map." )
149+ void resolveEmptyMapIsNotNull () {
150+ DocumentReference documentReference = mock (DocumentReference .class );
151+ when (documentReference .lookup ()).thenReturn ("{ '_id' : ?#{#target} }" );
152+ when (documentReference .sort ()).thenReturn ("" );
153+ when (documentReference .lazy ()).thenReturn (false );
154+ MongoPersistentProperty property = mock (MongoPersistentProperty .class );
155+ when (property .isCollectionLike ()).thenReturn (false );
156+ when (property .isMap ()).thenReturn (true );
157+ when (property .isDocumentReference ()).thenReturn (true );
158+ when (property .getDocumentReference ()).thenReturn (documentReference );
159+ DocumentReferenceSource source = mock (DocumentReferenceSource .class );
160+ when (source .getTargetSource ()).thenReturn (Document .parse ("{}" ));
161+ ReferenceLookupDelegate lookupDelegate = new ReferenceLookupDelegate (mappingContext , spELContext );
162+
163+ ReferenceResolver .MongoEntityReader entityReader = mock (ReferenceResolver .MongoEntityReader .class );
164+
165+ Object target = resolver .resolveReference (property , source , lookupDelegate , entityReader );
166+
167+ verify (property , times (3 )).isMap ();
168+ verify (property , times (2 )).isDocumentReference ();
169+ verify (property , times (2 )).getDocumentReference ();
170+ verify (property , times (3 )).isCollectionLike ();
171+ verify (documentReference , times (1 )).lookup ();
172+ verify (documentReference , times (1 )).sort ();
173+ verify (documentReference , times (1 )).lazy ();
174+ verify (source , times (3 )).getTargetSource ();
175+ verifyNoMoreInteractions (documentReference , property , source ); // Make sure we only call the properties we mocked.
176+ assertThat (target )
177+ .isNotNull ()
178+ .isInstanceOf (Map .class );
179+ }
180+
181+ @ Test // GH-5065
182+ @ DisplayName ("GH-5065: Lazy loaded empty Map with @DocumentReference annotation should deserialize to an empty map with a non-null values property." )
183+ void resolveLazyLoadedEmptyMapIsNotNull () {
184+ DocumentReference documentReference = mock (DocumentReference .class );
185+ when (documentReference .lookup ()).thenReturn ("{ '_id' : ?#{#target} }" );
186+ when (documentReference .sort ()).thenReturn ("" );
187+ when (documentReference .lazy ()).thenReturn (true );
188+ MongoPersistentProperty property = mock (MongoPersistentProperty .class );
189+ when (property .isCollectionLike ()).thenReturn (false );
190+ when (property .isMap ()).thenReturn (true );
191+ when (property .isDocumentReference ()).thenReturn (true );
192+ when (property .getDocumentReference ()).thenReturn (documentReference );
193+ //noinspection rawtypes,unchecked
194+ when (property .getType ()).thenReturn ((Class ) Map .class );
195+ DocumentReferenceSource source = mock (DocumentReferenceSource .class );
196+ when (source .getTargetSource ()).thenReturn (Document .parse ("{}" ));
197+ ReferenceLookupDelegate lookupDelegate = new ReferenceLookupDelegate (mappingContext , spELContext );
198+
199+ ReferenceResolver .MongoEntityReader entityReader = mock (ReferenceResolver .MongoEntityReader .class );
200+
201+ Object target = resolver .resolveReference (property , source , lookupDelegate , entityReader );
202+
203+ verify (property , times (1 )).isMap ();
204+ verify (property , times (1 )).isDocumentReference ();
205+ verify (property , times (1 )).getDocumentReference ();
206+ verify (property , times (1 )).isCollectionLike ();
207+ verify (property , times (1 )).getType ();
208+ verify (documentReference , times (1 )).lazy ();
209+ verify (source , times (1 )).getTargetSource ();
210+ verifyNoMoreInteractions (documentReference , property , source ); // Make sure we only call the properties we mocked.
211+
212+ assertThat (target )
213+ .isNotNull ()
214+ .isInstanceOf (Map .class )
215+ .asInstanceOf (MAP ).values ().isNotNull ();
216+ }
137217}
0 commit comments