@@ -160,4 +160,144 @@ impl CentroidCRDT {
160160 distances. truncate ( limit) ;
161161 distances
162162 }
163+ }
164+
165+ #[ cfg( test) ]
166+ mod tests {
167+ use super :: * ;
168+ use uuid:: Uuid ;
169+
170+ #[ test]
171+ fn test_create_centroid ( ) {
172+ let node_id = Uuid :: new_v4 ( ) ;
173+ let mut crdt = CentroidCRDT :: new ( node_id) ;
174+
175+ let vector = Vector :: new ( vec ! [ 1.0 , 2.0 , 3.0 ] ) ;
176+ let centroid_id = crdt. create_centroid ( vector. clone ( ) ) ;
177+
178+ assert_eq ! ( crdt. centroids. len( ) , 1 ) ;
179+ assert_eq ! ( crdt. operations. len( ) , 1 ) ;
180+
181+ let centroid = crdt. get_centroid ( & centroid_id) . unwrap ( ) ;
182+ assert_eq ! ( centroid. vector. values, vector. values) ;
183+ }
184+
185+ #[ test]
186+ fn test_update_centroid ( ) {
187+ let node_id = Uuid :: new_v4 ( ) ;
188+ let mut crdt = CentroidCRDT :: new ( node_id) ;
189+
190+ let vector1 = Vector :: new ( vec ! [ 1.0 , 2.0 , 3.0 ] ) ;
191+ let centroid_id = crdt. create_centroid ( vector1) ;
192+
193+ let vector2 = Vector :: new ( vec ! [ 4.0 , 5.0 , 6.0 ] ) ;
194+ crdt. update_centroid ( centroid_id, vector2) . unwrap ( ) ;
195+
196+ assert_eq ! ( crdt. operations. len( ) , 2 ) ;
197+
198+ let centroid = crdt. get_centroid ( & centroid_id) . unwrap ( ) ;
199+ // After updating, the vector should be a weighted average of the original and new vector
200+ // With 1 existing element and 1 new element, the weights are 1/2 each
201+ // So the expected value is (1.0*1 + 4.0*1)/2, (2.0*1 + 5.0*1)/2, (3.0*1 + 6.0*1)/2
202+ // Which equals 2.5, 3.5, 4.5
203+ assert ! ( centroid. vector. values[ 0 ] > 1.0 && centroid. vector. values[ 0 ] < 4.0 ) ;
204+ assert ! ( centroid. vector. values[ 1 ] > 2.0 && centroid. vector. values[ 1 ] < 5.0 ) ;
205+ assert ! ( centroid. vector. values[ 2 ] > 3.0 && centroid. vector. values[ 2 ] < 6.0 ) ;
206+ assert_eq ! ( centroid. count, 2 ) ;
207+ }
208+
209+ #[ test]
210+ fn test_delete_centroid ( ) {
211+ let node_id = Uuid :: new_v4 ( ) ;
212+ let mut crdt = CentroidCRDT :: new ( node_id) ;
213+
214+ let vector = Vector :: new ( vec ! [ 1.0 , 2.0 , 3.0 ] ) ;
215+ let centroid_id = crdt. create_centroid ( vector. clone ( ) ) ;
216+
217+ crdt. delete_centroid ( centroid_id) . unwrap ( ) ;
218+
219+ assert_eq ! ( crdt. centroids. len( ) , 0 ) ;
220+ assert_eq ! ( crdt. operations. len( ) , 2 ) ;
221+ }
222+
223+ #[ test]
224+ fn test_merge ( ) {
225+ // Create two CRDTs
226+ let node_id1 = Uuid :: new_v4 ( ) ;
227+ let node_id2 = Uuid :: new_v4 ( ) ;
228+ let mut crdt1 = CentroidCRDT :: new ( node_id1) ;
229+ let mut crdt2 = CentroidCRDT :: new ( node_id2) ;
230+
231+ // Add a centroid to the first CRDT
232+ let vector1 = Vector :: new ( vec ! [ 1.0 , 2.0 , 3.0 ] ) ;
233+ let centroid_id1 = crdt1. create_centroid ( vector1. clone ( ) ) ;
234+
235+ // Add a different centroid to the second CRDT
236+ let vector2 = Vector :: new ( vec ! [ 4.0 , 5.0 , 6.0 ] ) ;
237+ let centroid_id2 = crdt2. create_centroid ( vector2. clone ( ) ) ;
238+
239+ // Merge the second CRDT into the first
240+ crdt1. merge ( & crdt2) ;
241+
242+ // First CRDT should now have both centroids
243+ assert_eq ! ( crdt1. centroids. len( ) , 2 ) ;
244+ assert ! ( crdt1. centroids. contains_key( & centroid_id1) ) ;
245+ assert ! ( crdt1. centroids. contains_key( & centroid_id2) ) ;
246+
247+ // Verify the centroids have the correct vectors
248+ let merged_centroid1 = crdt1. get_centroid ( & centroid_id1) . unwrap ( ) ;
249+ let merged_centroid2 = crdt1. get_centroid ( & centroid_id2) . unwrap ( ) ;
250+
251+ assert_eq ! ( merged_centroid1. vector. values, vector1. values) ;
252+ assert_eq ! ( merged_centroid2. vector. values, vector2. values) ;
253+
254+ // Operations should be merged too
255+ assert_eq ! ( crdt1. operations. len( ) , 2 ) ;
256+ assert_eq ! ( crdt1. observed. len( ) , 2 ) ;
257+ }
258+
259+ #[ test]
260+ fn test_concurrent_operations ( ) {
261+ // Test handling of concurrent operations with different timestamps
262+ let node_id1 = Uuid :: new_v4 ( ) ;
263+ let node_id2 = Uuid :: new_v4 ( ) ;
264+ let mut crdt1 = CentroidCRDT :: new ( node_id1) ;
265+ let mut crdt2 = CentroidCRDT :: new ( node_id2) ;
266+
267+ // Both CRDTs create a centroid with the same ID but different vectors
268+ let centroid_id = Uuid :: new_v4 ( ) ;
269+ let vector1 = Vector :: new ( vec ! [ 1.0 , 2.0 , 3.0 ] ) ;
270+ let vector2 = Vector :: new ( vec ! [ 4.0 , 5.0 , 6.0 ] ) ;
271+
272+ // Create the same centroid in both CRDTs with different timestamps
273+ let now = chrono:: Utc :: now ( ) ;
274+ let later = now + chrono:: Duration :: seconds ( 10 ) ;
275+
276+ // Earlier operation in CRDT1
277+ let op1 = CentroidOperation {
278+ id : Uuid :: new_v4 ( ) ,
279+ centroid_id,
280+ timestamp : now,
281+ operation_type : OperationType :: Create ( vector1) ,
282+ } ;
283+
284+ // Later operation in CRDT2
285+ let op2 = CentroidOperation {
286+ id : Uuid :: new_v4 ( ) ,
287+ centroid_id,
288+ timestamp : later,
289+ operation_type : OperationType :: Create ( vector2. clone ( ) ) ,
290+ } ;
291+
292+ crdt1. apply_operation ( op1) ;
293+ crdt2. apply_operation ( op2) ;
294+
295+ // Merge CRDT2 into CRDT1
296+ crdt1. merge ( & crdt2) ;
297+
298+ // The centroid in CRDT1 should now have the vector from CRDT2
299+ // because it has a later timestamp
300+ let centroid = crdt1. get_centroid ( & centroid_id) . unwrap ( ) ;
301+ assert_eq ! ( centroid. vector. values, vector2. values) ;
302+ }
163303}
0 commit comments