@@ -386,6 +386,74 @@ mod tests {
386386 } ) ;
387387 }
388388
389+ async fn test_duplicate_key_cross_index_impl (
390+ mut archive : impl Archive < Key = FixedBytes < 64 > , Value = i32 > ,
391+ ) {
392+ // Store the same key at two different indices; distinct values only so
393+ // the test can observe which entry wins a key lookup.
394+ let key = test_key ( "dupe-xindex" ) ;
395+ archive. put ( 2 , key. clone ( ) , 20 ) . await . expect ( "put(2)" ) ;
396+ archive. put ( 5 , key. clone ( ) , 50 ) . await . expect ( "put(5)" ) ;
397+
398+ // Both indices must resolve individually.
399+ assert_eq ! (
400+ archive. get( Identifier :: Index ( 2 ) ) . await . unwrap( ) ,
401+ Some ( 20 ) ,
402+ "Index(2) must resolve to the value stored at 2"
403+ ) ;
404+ assert_eq ! (
405+ archive. get( Identifier :: Index ( 5 ) ) . await . unwrap( ) ,
406+ Some ( 50 ) ,
407+ "Index(5) must resolve to the value stored at 5"
408+ ) ;
409+
410+ // Key lookup may return either value per the contract; just assert it
411+ // returns one of them and that `has` reports presence.
412+ let got = archive
413+ . get ( Identifier :: Key ( & key) )
414+ . await
415+ . unwrap ( )
416+ . expect ( "key lookup must find at least one entry" ) ;
417+ assert ! ( got == 20 || got == 50 , "unexpected value: {got}" ) ;
418+ assert ! ( archive. has( Identifier :: Key ( & key) ) . await . unwrap( ) ) ;
419+ }
420+
421+ #[ test_traced]
422+ fn test_duplicate_key_cross_index_prunable_no_compression ( ) {
423+ let executor = deterministic:: Runner :: default ( ) ;
424+ executor. start ( |context| async move {
425+ let archive = create_prunable ( context, None ) . await ;
426+ test_duplicate_key_cross_index_impl ( archive) . await ;
427+ } ) ;
428+ }
429+
430+ #[ test_traced]
431+ fn test_duplicate_key_cross_index_prunable_compression ( ) {
432+ let executor = deterministic:: Runner :: default ( ) ;
433+ executor. start ( |context| async move {
434+ let archive = create_prunable ( context, Some ( 3 ) ) . await ;
435+ test_duplicate_key_cross_index_impl ( archive) . await ;
436+ } ) ;
437+ }
438+
439+ #[ test_traced]
440+ fn test_duplicate_key_cross_index_immutable_no_compression ( ) {
441+ let executor = deterministic:: Runner :: default ( ) ;
442+ executor. start ( |context| async move {
443+ let archive = create_immutable ( context, None ) . await ;
444+ test_duplicate_key_cross_index_impl ( archive) . await ;
445+ } ) ;
446+ }
447+
448+ #[ test_traced]
449+ fn test_duplicate_key_cross_index_immutable_compression ( ) {
450+ let executor = deterministic:: Runner :: default ( ) ;
451+ executor. start ( |context| async move {
452+ let archive = create_immutable ( context, Some ( 3 ) ) . await ;
453+ test_duplicate_key_cross_index_impl ( archive) . await ;
454+ } ) ;
455+ }
456+
389457 #[ test_traced]
390458 fn test_duplicate_key_immutable_compression ( ) {
391459 let executor = deterministic:: Runner :: default ( ) ;
0 commit comments