1
+ using System ;
1
2
using System . Collections . Generic ;
3
+ using System . Diagnostics ;
4
+ using System . Linq ;
5
+ using System . Threading . Tasks ;
2
6
using Microsoft . VisualStudio . TestTools . UnitTesting ;
7
+ using Moq ;
8
+ using Parse . Abstractions . Infrastructure . Control ;
9
+ using Parse . Abstractions . Infrastructure ;
3
10
using Parse . Abstractions . Internal ;
11
+ using Parse . Abstractions . Platform . Objects ;
4
12
using Parse . Infrastructure ;
13
+ using Microsoft . VisualStudio . TestPlatform . ObjectModel ;
14
+ using Parse . Platform . Objects ;
15
+ using System . Threading ;
16
+ using Parse . Abstractions . Platform . Users ;
5
17
6
18
namespace Parse . Tests ;
7
19
8
20
[ TestClass ]
9
21
public class RelationTests
10
22
{
23
+ [ ParseClassName ( "TestObject" ) ]
24
+ private class TestObject : ParseObject { }
25
+
26
+ [ ParseClassName ( "Friend" ) ]
27
+ private class Friend : ParseObject { }
28
+
29
+ private ParseClient Client { get ; set ; }
30
+
31
+ [ TestInitialize ]
32
+ public void SetUp ( )
33
+ {
34
+ // Initialize the client and ensure the instance is set
35
+ Client = new ParseClient ( new ServerConnectionData { Test = true } ) ;
36
+ Client . Publicize ( ) ;
37
+
38
+ // Register the test classes
39
+ Client . RegisterSubclass ( typeof ( TestObject ) ) ;
40
+ Client . RegisterSubclass ( typeof ( Friend ) ) ;
41
+ Client . RegisterSubclass ( typeof ( ParseUser ) ) ;
42
+ Client . RegisterSubclass ( typeof ( ParseSession ) ) ;
43
+ Client . RegisterSubclass ( typeof ( ParseUser ) ) ;
44
+
45
+ // **--- Mocking Setup ---**
46
+ var hub = new MutableServiceHub ( ) ; // Use MutableServiceHub for mocking
47
+ var mockUserController = new Mock < IParseUserController > ( ) ;
48
+ var mockObjectController = new Mock < IParseObjectController > ( ) ;
49
+
50
+ // **Mock SignUpAsync for ParseUser:**
51
+ mockUserController
52
+ . Setup ( controller => controller . SignUpAsync (
53
+ It . IsAny < IObjectState > ( ) ,
54
+ It . IsAny < IDictionary < string , IParseFieldOperation > > ( ) ,
55
+ It . IsAny < IServiceHub > ( ) ,
56
+ It . IsAny < CancellationToken > ( ) ) )
57
+ . ReturnsAsync ( new MutableObjectState { ObjectId = "some0neTol4v4" } ) ; // Predefined ObjectId for User
58
+
59
+ // **Mock SaveAsync for ParseObject (Friend objects):**
60
+ int objectSaveCounter = 1 ; // Counter for Friend ObjectIds
61
+ mockObjectController
62
+ . Setup ( controller => controller . SaveAsync (
63
+ It . IsAny < IObjectState > ( ) ,
64
+ It . IsAny < IDictionary < string , IParseFieldOperation > > ( ) ,
65
+ It . IsAny < string > ( ) ,
66
+ It . IsAny < IServiceHub > ( ) ,
67
+ It . IsAny < CancellationToken > ( ) ) )
68
+ . ReturnsAsync ( ( ) => // Use a lambda to generate different ObjectIds for each Friend
69
+ {
70
+ return new MutableObjectState { ObjectId = $ "mockFriendObjectId{ objectSaveCounter ++ } " } ;
71
+ } ) ;
72
+
73
+ // **Inject Mocks into ServiceHub:**
74
+ hub . UserController = mockUserController . Object ;
75
+ hub . ObjectController = mockObjectController . Object ;
76
+
77
+ }
78
+
79
+ [ TestCleanup ]
80
+ public void TearDown ( ) => ( Client . Services as ServiceHub ) . Reset ( ) ;
81
+
11
82
[ TestMethod ]
12
83
public void TestRelationQuery ( )
13
84
{
@@ -24,4 +95,278 @@ public void TestRelationQuery()
24
95
25
96
Assert . AreEqual ( "child" , encoded [ "redirectClassNameForKey" ] ) ;
26
97
}
27
- }
98
+
99
+ [ TestMethod ]
100
+ [ Description ( "Tests AddRelationToUserAsync throws exception when user is null" ) ] // Mock difficulty: 1
101
+ public async Task AddRelationToUserAsync_ThrowsException_WhenUserIsNull ( )
102
+ {
103
+
104
+ var relatedObjects = new List < ParseObject >
105
+ {
106
+ new ParseObject ( "Friend" , Client . Services ) { [ "name" ] = "Friend1" }
107
+ } ;
108
+
109
+ await Assert . ThrowsExceptionAsync < ArgumentNullException > ( ( ) => UserManagement . AddRelationToUserAsync ( null , "friends" , relatedObjects ) ) ;
110
+
111
+ }
112
+ [ TestMethod ]
113
+ [ Description ( "Tests AddRelationToUserAsync throws exception when relationfield is null" ) ] // Mock difficulty: 1
114
+ public async Task AddRelationToUserAsync_ThrowsException_WhenRelationFieldIsNull ( )
115
+ {
116
+ var user = new ParseUser ( ) { Username = "TestUser" , Password = "TestPass" , Services = Client . Services } ;
117
+ await user . SignUpAsync ( ) ;
118
+ var relatedObjects = new List < ParseObject >
119
+ {
120
+ new ParseObject ( "Friend" , Client . Services ) { [ "name" ] = "Friend1" }
121
+ } ;
122
+ await Assert . ThrowsExceptionAsync < ArgumentException > ( ( ) => UserManagement . AddRelationToUserAsync ( user , null , relatedObjects ) ) ;
123
+ }
124
+
125
+ [ TestMethod ]
126
+ [ Description ( "Tests UpdateUserRelationAsync throws exception when user is null" ) ] // Mock difficulty: 1
127
+ public async Task UpdateUserRelationAsync_ThrowsException_WhenUserIsNull ( )
128
+ {
129
+ var relatedObjectsToAdd = new List < ParseObject >
130
+ {
131
+ new ParseObject ( "Friend" , Client . Services ) { [ "name" ] = "Friend1" }
132
+ } ;
133
+ var relatedObjectsToRemove = new List < ParseObject >
134
+ {
135
+ new ParseObject ( "Friend" , Client . Services ) { [ "name" ] = "Friend2" }
136
+ } ;
137
+
138
+
139
+ await Assert . ThrowsExceptionAsync < ArgumentNullException > ( ( ) => UserManagement . UpdateUserRelationAsync ( null , "friends" , relatedObjectsToAdd , relatedObjectsToRemove ) ) ;
140
+ }
141
+ [ TestMethod ]
142
+ [ Description ( "Tests UpdateUserRelationAsync throws exception when relationfield is null" ) ] // Mock difficulty: 1
143
+ public async Task UpdateUserRelationAsync_ThrowsException_WhenRelationFieldIsNull ( )
144
+ {
145
+ var user = new ParseUser ( ) { Username = "TestUser" , Password = "TestPass" , Services = Client . Services } ;
146
+ await user . SignUpAsync ( ) ;
147
+
148
+ var relatedObjectsToAdd = new List < ParseObject >
149
+ {
150
+ new ParseObject ( "Friend" , Client . Services ) { [ "name" ] = "Friend1" }
151
+ } ;
152
+ var relatedObjectsToRemove = new List < ParseObject >
153
+ {
154
+ new ParseObject ( "Friend" , Client . Services ) { [ "name" ] = "Friend2" }
155
+ } ;
156
+
157
+
158
+ await Assert . ThrowsExceptionAsync < ArgumentException > ( ( ) => UserManagement . UpdateUserRelationAsync ( user , null , relatedObjectsToAdd , relatedObjectsToRemove ) ) ;
159
+ }
160
+ [ TestMethod ]
161
+ [ Description ( "Tests DeleteUserRelationAsync throws exception when user is null" ) ] // Mock difficulty: 1
162
+ public async Task DeleteUserRelationAsync_ThrowsException_WhenUserIsNull ( )
163
+ {
164
+ await Assert . ThrowsExceptionAsync < ArgumentNullException > ( ( ) => UserManagement . DeleteUserRelationAsync ( null , "friends" ) ) ;
165
+ }
166
+ [ TestMethod ]
167
+ [ Description ( "Tests DeleteUserRelationAsync throws exception when relationfield is null" ) ] // Mock difficulty: 1
168
+ public async Task DeleteUserRelationAsync_ThrowsException_WhenRelationFieldIsNull ( )
169
+ {
170
+ var user = new ParseUser ( ) { Username = "TestUser" , Password = "TestPass" , Services = Client . Services } ;
171
+ await user . SignUpAsync ( ) ;
172
+
173
+ await Assert . ThrowsExceptionAsync < ArgumentException > ( ( ) => UserManagement . DeleteUserRelationAsync ( user , null ) ) ;
174
+ }
175
+ [ TestMethod ]
176
+ [ Description ( "Tests GetUserRelationsAsync throws exception when user is null" ) ] // Mock difficulty: 1
177
+ public async Task GetUserRelationsAsync_ThrowsException_WhenUserIsNull ( )
178
+ {
179
+ await Assert . ThrowsExceptionAsync < ArgumentNullException > ( ( ) => UserManagement . GetUserRelationsAsync ( null , "friends" ) ) ;
180
+ }
181
+ [ TestMethod ]
182
+ [ Description ( "Tests GetUserRelationsAsync throws exception when relationfield is null" ) ] // Mock difficulty: 1
183
+ public async Task GetUserRelationsAsync_ThrowsException_WhenRelationFieldIsNull ( )
184
+ {
185
+ var user = new ParseUser ( ) { Username = "TestUser" , Password = "TestPass" , Services = Client . Services } ;
186
+ await user . SignUpAsync ( ) ;
187
+
188
+ await Assert . ThrowsExceptionAsync < ArgumentException > ( ( ) => UserManagement . GetUserRelationsAsync ( user , null ) ) ;
189
+ }
190
+
191
+
192
+
193
+ [ TestMethod ]
194
+ [ Description ( "Tests that AddRelationToUserAsync throws when a related object is unsaved" ) ]
195
+ public async Task AddRelationToUserAsync_ThrowsException_WhenRelatedObjectIsUnsaved ( )
196
+ {
197
+ // Arrange: Create and sign up a test user.
198
+ var user = new ParseUser ( ) { Username = "TestUser" , Password = "TestPass" , Services = Client . Services } ;
199
+ await user . SignUpAsync ( ) ;
200
+
201
+ // Create an unsaved Friend object (do NOT call SaveAsync).
202
+ var unsavedFriend = new ParseObject ( "Friend" , Client . Services ) { [ "name" ] = "UnsavedFriend" } ;
203
+ var relatedObjects = new List < ParseObject > { unsavedFriend } ;
204
+
205
+ // Act & Assert: Expect an exception when trying to add an unsaved object.
206
+ await Assert . ThrowsExceptionAsync < ArgumentException > ( ( ) =>
207
+ UserManagement . AddRelationToUserAsync ( user , "friends" , relatedObjects ) ) ;
208
+ }
209
+
210
+
211
+
212
+ }
213
+
214
+ public static class UserManagement
215
+ {
216
+ public static async Task AddRelationToUserAsync ( ParseUser user , string relationField , IList < ParseObject > relatedObjects )
217
+ {
218
+ if ( user == null )
219
+ {
220
+ throw new ArgumentNullException ( nameof ( user ) , "User must not be null." ) ;
221
+ }
222
+
223
+ if ( string . IsNullOrEmpty ( relationField ) )
224
+ {
225
+ throw new ArgumentException ( "Relation field must not be null or empty." , nameof ( relationField ) ) ;
226
+ }
227
+
228
+ if ( relatedObjects == null || relatedObjects . Count == 0 )
229
+ {
230
+ Debug . WriteLine ( "No objects provided to add to the relation." ) ;
231
+ return ;
232
+ }
233
+
234
+ var relation = user . GetRelation < ParseObject > ( relationField ) ;
235
+
236
+ foreach ( var obj in relatedObjects )
237
+ {
238
+ relation . Add ( obj ) ;
239
+ }
240
+
241
+ await user . SaveAsync ( ) ;
242
+ Debug . WriteLine ( $ "Added { relatedObjects . Count } objects to the '{ relationField } ' relation for user '{ user . Username } '.") ;
243
+ }
244
+ public static async Task UpdateUserRelationAsync ( ParseUser user , string relationField , IList < ParseObject > toAdd , IList < ParseObject > toRemove )
245
+ {
246
+ if ( user == null )
247
+ {
248
+ throw new ArgumentNullException ( nameof ( user ) , "User must not be null." ) ;
249
+ }
250
+
251
+ if ( string . IsNullOrEmpty ( relationField ) )
252
+ {
253
+ throw new ArgumentException ( "Relation field must not be null or empty." , nameof ( relationField ) ) ;
254
+ }
255
+
256
+ var relation = user . GetRelation < ParseObject > ( relationField ) ;
257
+
258
+ // Add objects to the relation
259
+ if ( toAdd != null && toAdd . Count > 0 )
260
+ {
261
+ foreach ( var obj in toAdd )
262
+ {
263
+ relation . Add ( obj ) ;
264
+ }
265
+ Debug . WriteLine ( $ "Added { toAdd . Count } objects to the '{ relationField } ' relation.") ;
266
+ }
267
+
268
+ // Remove objects from the relation
269
+ if ( toRemove != null && toRemove . Count > 0 )
270
+ {
271
+
272
+ foreach ( var obj in toRemove )
273
+ {
274
+ relation . Remove ( obj ) ;
275
+ }
276
+ Debug . WriteLine ( $ "Removed { toRemove . Count } objects from the '{ relationField } ' relation.") ;
277
+ }
278
+
279
+ await user . SaveAsync ( ) ;
280
+ }
281
+ public static async Task DeleteUserRelationAsync ( ParseUser user , string relationField )
282
+ {
283
+ if ( user == null )
284
+ {
285
+ throw new ArgumentNullException ( nameof ( user ) , "User must not be null." ) ;
286
+ }
287
+
288
+ if ( string . IsNullOrEmpty ( relationField ) )
289
+ {
290
+ throw new ArgumentException ( "Relation field must not be null or empty." , nameof ( relationField ) ) ;
291
+ }
292
+
293
+ var relation = user . GetRelation < ParseObject > ( relationField ) ;
294
+ var relatedObjects = await relation . Query . FindAsync ( ) ;
295
+
296
+
297
+ foreach ( var obj in relatedObjects )
298
+ {
299
+ relation . Remove ( obj ) ;
300
+ }
301
+
302
+ await user . SaveAsync ( ) ;
303
+ Debug . WriteLine ( $ "Removed all objects from the '{ relationField } ' relation for user '{ user . Username } '.") ;
304
+ }
305
+ public static async Task ManageUserRelationsAsync ( ParseClient client )
306
+ {
307
+ // Get the current user
308
+ var user = await ParseClient . Instance . GetCurrentUser ( ) ;
309
+
310
+ if ( user == null )
311
+ {
312
+ Debug . WriteLine ( "No user is currently logged in." ) ;
313
+ return ;
314
+ }
315
+
316
+ const string relationField = "friends" ; // Example relation field name
317
+
318
+ // Create related objects to add
319
+ var relatedObjectsToAdd = new List < ParseObject >
320
+ {
321
+ new ParseObject ( "Friend" , client . Services ) { [ "name" ] = "Alice" } ,
322
+ new ParseObject ( "Friend" , client . Services ) { [ "name" ] = "Bob" }
323
+ } ;
324
+
325
+ // Save related objects to the server before adding to the relation
326
+ foreach ( var obj in relatedObjectsToAdd )
327
+ {
328
+ await obj . SaveAsync ( ) ;
329
+ }
330
+
331
+ // Add objects to the relation
332
+ await AddRelationToUserAsync ( user , relationField , relatedObjectsToAdd ) ;
333
+
334
+ // Query the relation
335
+ var relatedObjects = await GetUserRelationsAsync ( user , relationField ) ;
336
+
337
+ // Update the relation (add and remove objects)
338
+ var relatedObjectsToRemove = new List < ParseObject > { relatedObjects [ 0 ] } ; // Remove the first related object
339
+ var newObjectsToAdd = new List < ParseObject >
340
+ {
341
+ new ParseObject ( "Friend" , client . Services ) { [ "name" ] = "Charlie" }
342
+ } ;
343
+
344
+ foreach ( var obj in newObjectsToAdd )
345
+ {
346
+ await obj . SaveAsync ( ) ;
347
+ }
348
+
349
+ await UpdateUserRelationAsync ( user , relationField , newObjectsToAdd , relatedObjectsToRemove ) ;
350
+
351
+ }
352
+ public static async Task < IList < ParseObject > > GetUserRelationsAsync ( ParseUser user , string relationField )
353
+ {
354
+ if ( user == null )
355
+ {
356
+ throw new ArgumentNullException ( nameof ( user ) , "User must not be null." ) ;
357
+ }
358
+
359
+ if ( string . IsNullOrEmpty ( relationField ) )
360
+ {
361
+ throw new ArgumentException ( "Relation field must not be null or empty." , nameof ( relationField ) ) ;
362
+ }
363
+
364
+ var relation = user . GetRelation < ParseObject > ( relationField ) ;
365
+
366
+ var results = await relation . Query . FindAsync ( ) ;
367
+ Debug . WriteLine ( $ "Retrieved { results . Count ( ) } objects from the '{ relationField } ' relation for user '{ user . Username } '.") ;
368
+ return results . ToList ( ) ;
369
+ }
370
+
371
+ }
372
+
0 commit comments