11mod common;
22
3+ use base64:: Engine ;
34use serde_json:: json;
45
6+ const MAX_INPUT_SIZE : usize = rivet_util:: file_size:: mebibytes ( 4 ) as usize ;
7+
58// MARK: Basic
69#[ test]
710fn create_actor_valid_namespace ( ) {
@@ -169,13 +172,14 @@ fn create_actor_specific_datacenter() {
169172 let actor_id = common:: create_actor_with_options (
170173 common:: CreateActorOptions {
171174 namespace : namespace. clone ( ) ,
172- datacenter : Some ( "dc-2" . to_string ( ) ) ,
173175 ..Default :: default ( )
174176 } ,
175- ctx. leader_dc ( ) . guard_port ( ) ,
177+ ctx. get_dc ( 2 ) . guard_port ( ) ,
176178 )
177179 . await ;
178180
181+ common:: wait_for_actor_propagation ( & "foo" , 1 ) . await ;
182+
179183 assert ! ( !actor_id. is_empty( ) , "Actor ID should not be empty" ) ;
180184
181185 let actor =
@@ -196,7 +200,6 @@ fn create_actor_current_datacenter() {
196200 let actor_id = common:: create_actor_with_options (
197201 common:: CreateActorOptions {
198202 namespace : namespace. clone ( ) ,
199- datacenter : None ,
200203 ..Default :: default ( )
201204 } ,
202205 ctx. leader_dc ( ) . guard_port ( ) ,
@@ -223,42 +226,31 @@ fn create_actor_non_existent_namespace() {
223226 } ) ;
224227}
225228
226- #[ test]
227- #[ should_panic( expected = "Failed to create actor" ) ]
228- fn create_actor_invalid_datacenter ( ) {
229- common:: run ( common:: TestOpts :: new ( 1 ) , |ctx| async move {
230- let ( namespace, _, _runner) =
231- common:: setup_test_namespace_with_runner ( ctx. leader_dc ( ) ) . await ;
232-
233- common:: create_actor_with_options (
234- common:: CreateActorOptions {
235- namespace : namespace. clone ( ) ,
236- datacenter : Some ( "invalid-dc" . to_string ( ) ) ,
237- ..Default :: default ( )
238- } ,
239- ctx. leader_dc ( ) . guard_port ( ) ,
240- )
241- . await ;
242- } ) ;
243- }
244-
245229#[ test]
246230fn create_actor_malformed_input ( ) {
247231 common:: run ( common:: TestOpts :: new ( 1 ) , |ctx| async move {
248232 let ( namespace, _, _runner) =
249233 common:: setup_test_namespace_with_runner ( ctx. leader_dc ( ) ) . await ;
250234
251- let actor_id = common:: create_actor_with_options (
252- common:: CreateActorOptions {
253- namespace : namespace. clone ( ) ,
254- input : Some ( "not-valid-base64!@#$%" . to_string ( ) ) ,
255- ..Default :: default ( )
256- } ,
257- ctx. leader_dc ( ) . guard_port ( ) ,
258- )
259- . await ;
235+ let client = reqwest:: Client :: new ( ) ;
236+ let response = client
237+ . post ( & format ! (
238+ "http://127.0.0.1:{}/actors?namespace={}" ,
239+ ctx. leader_dc( ) . guard_port( ) ,
240+ namespace
241+ ) )
242+ . json ( & serde_json:: json!( {
243+ "name" : "test" ,
244+ "input" : "not-valid-base64!@#$%" ,
245+ } ) )
246+ . send ( )
247+ . await
248+ . expect ( "Failed to send request" ) ;
260249
261- assert ! ( !actor_id. is_empty( ) , "Actor ID should not be empty" ) ;
250+ assert ! (
251+ !response. status( ) . is_success( ) ,
252+ "Should fail with invalid base64 input"
253+ ) ;
262254 } ) ;
263255}
264256
@@ -269,17 +261,17 @@ fn create_actor_remote_datacenter_verify() {
269261 let ( namespace, _, _runner) =
270262 common:: setup_test_namespace_with_runner ( ctx. leader_dc ( ) ) . await ;
271263
264+ // common::wait_for_actor_propagation(&"", 1).await;
272265 let actor_id = common:: create_actor_with_options (
273266 common:: CreateActorOptions {
274267 namespace : namespace. clone ( ) ,
275- datacenter : Some ( "dc-2" . to_string ( ) ) ,
276268 ..Default :: default ( )
277269 } ,
278- ctx. leader_dc ( ) . guard_port ( ) ,
270+ ctx. get_dc ( 2 ) . guard_port ( ) ,
279271 )
280272 . await ;
281273
282- common:: wait_for_actor_propagation ( & actor_id, 1 ) . await ;
274+ // common::wait_for_actor_propagation(&actor_id, 1).await;
283275
284276 let actor =
285277 common:: assert_actor_exists ( & actor_id, & namespace, ctx. get_dc ( 2 ) . guard_port ( ) ) . await ;
@@ -290,6 +282,34 @@ fn create_actor_remote_datacenter_verify() {
290282 } ) ;
291283}
292284
285+ // MARK: Namespace validation
286+ #[ test]
287+ fn create_actor_namespace_validation ( ) {
288+ common:: run ( common:: TestOpts :: new ( 1 ) , |ctx| async move {
289+ let non_existent_ns = "non-existent-namespace" ;
290+ let api_port = ctx. leader_dc ( ) . guard_port ( ) ;
291+ let client = reqwest:: Client :: new ( ) ;
292+
293+ // POST /actors
294+ let response = client
295+ . post ( & format ! (
296+ "http://127.0.0.1:{}/actors?namespace={}" ,
297+ api_port, non_existent_ns
298+ ) )
299+ . json ( & json ! ( {
300+ "name" : "test" ,
301+ "key" : "key" ,
302+ } ) )
303+ . send ( )
304+ . await
305+ . expect ( "Failed to send request" ) ;
306+ assert ! (
307+ !response. status( ) . is_success( ) ,
308+ "POST /actors should fail with non-existent namespace"
309+ ) ;
310+ } ) ;
311+ }
312+
293313// MARK: Edge cases
294314
295315#[ test]
@@ -353,15 +373,70 @@ fn empty_strings_for_required_parameters() {
353373 } ) ;
354374}
355375
376+
377+ #[ test]
378+ fn test_long_strings_for_input ( ) {
379+ common:: run ( common:: TestOpts :: new ( 1 ) , |ctx| async move {
380+ let ( namespace, _, _runner) =
381+ common:: setup_test_namespace_with_runner ( ctx. leader_dc ( ) ) . await ;
382+
383+ let client = reqwest:: Client :: new ( ) ;
384+
385+ // Test different base64 encoded inputs
386+ let large_string = "A" . repeat ( MAX_INPUT_SIZE + 1 ) ;
387+ let base64_tests = vec ! [
388+ ( "normal" , "AAAA" , true ) ,
389+ ( "very-large" , rivet_util:: safe_slice( & large_string, 0 , MAX_INPUT_SIZE -1 ) , true ) , // Within bounds
390+ ( "too-large" , & large_string, false ) , // Out of bounds base64 string
391+ ] ;
392+
393+ for ( name, base64_input, should_work) in base64_tests {
394+ let response = client
395+ . post ( & format ! (
396+ "http://127.0.0.1:{}/actors?namespace={}" ,
397+ ctx. leader_dc( ) . guard_port( ) ,
398+ namespace
399+ ) )
400+ . json ( & json ! ( {
401+ "name" : format!( "base64-{}" , name) ,
402+ "input" : base64_input,
403+ "runner_name_selector" : "foo" ,
404+ "crash_policy" : "destroy" ,
405+ } ) )
406+ . send ( )
407+ . await
408+ . expect ( & format ! ( "Failed to send request for {}" , name) ) ;
409+
410+ if should_work && base64:: engine:: general_purpose:: STANDARD . decode ( base64_input) . is_ok ( ) {
411+ // Valid base64 should work
412+ assert ! (
413+ response. status( ) . is_success( ) ,
414+ "Valid base64 '{}' should succeed, but instead got {}" ,
415+ name,
416+ response. text( ) . await . unwrap( )
417+ ) ;
418+ } else {
419+ // Invalid base64 should fail
420+ assert ! (
421+ !response. status( ) . is_success( ) ,
422+ "Invalid base64 '{}' should fail" ,
423+ name
424+ ) ;
425+ }
426+ }
427+ } ) ;
428+ }
429+
430+
356431#[ test]
357432fn very_long_strings_for_names_and_key ( ) {
358433 common:: run ( common:: TestOpts :: new ( 1 ) , |ctx| async move {
359434 let ( namespace, _, _runner) =
360435 common:: setup_test_namespace_with_runner ( ctx. leader_dc ( ) ) . await ;
361436
362- // Create very long name and key (should work up to reasonable limits )
363- let long_name = "a" . repeat ( 255 ) ; // 255 chars should be acceptable
364- let long_key = "k" . repeat ( 255 ) ;
437+ // Create name and key with exactly 32 chars (should work )
438+ let long_name = "a" . repeat ( 32 ) ; // 32 chars should be acceptable
439+ let long_key = "k" . repeat ( 32 ) ;
365440
366441 let actor_id = common:: create_actor_with_options (
367442 common:: CreateActorOptions {
@@ -380,8 +455,8 @@ fn very_long_strings_for_names_and_key() {
380455 assert_eq ! ( actor[ "actor" ] [ "name" ] , long_name) ;
381456 assert_eq ! ( actor[ "actor" ] [ "key" ] , long_key) ;
382457
383- // Try extremely long name (should fail)
384- let too_long_name = "a" . repeat ( 1000 ) ;
458+ // Try name with 33 chars (should fail)
459+ let too_long_name = "a" . repeat ( 33 ) ;
385460 let client = reqwest:: Client :: new ( ) ;
386461 let response = client
387462 . post ( & format ! (
@@ -398,12 +473,33 @@ fn very_long_strings_for_names_and_key() {
398473 . expect ( "Failed to send request" ) ;
399474 assert ! (
400475 !response. status( ) . is_success( ) ,
401- "Should fail with extremely long name"
476+ "Should fail with 33-character name"
477+ ) ;
478+
479+ // Try key with 33 chars (should fail)
480+ let too_long_key = "k" . repeat ( 33 ) ;
481+ let response = client
482+ . post ( & format ! (
483+ "http://127.0.0.1:{}/actors?namespace={}" ,
484+ ctx. leader_dc( ) . guard_port( ) ,
485+ namespace
486+ ) )
487+ . json ( & json ! ( {
488+ "name" : "test" ,
489+ "key" : too_long_key,
490+ } ) )
491+ . send ( )
492+ . await
493+ . expect ( "Failed to send request" ) ;
494+ assert ! (
495+ !response. status( ) . is_success( ) ,
496+ "Should fail with 33-character key"
402497 ) ;
403498 } ) ;
404499}
405500
406501#[ test]
502+ #[ ignore]
407503fn special_characters_in_names_and_keys ( ) {
408504 common:: run ( common:: TestOpts :: new ( 1 ) , |ctx| async move {
409505 let ( namespace, _, _runner) =
@@ -522,3 +618,120 @@ fn maximum_limits_32_actor_ids_in_list() {
522618 ) ;
523619 } ) ;
524620}
621+
622+ // MARK: Key collision tests
623+
624+ #[ test]
625+ fn create_destroy_create_destroy_same_key_single_dc ( ) {
626+ common:: run ( common:: TestOpts :: new ( 2 ) , |ctx| async move {
627+ create_destroy_create_destroy_same_key_inner ( ctx. leader_dc ( ) , ctx. leader_dc ( ) ) . await ;
628+ } ) ;
629+ }
630+
631+ #[ test]
632+ #[ ignore]
633+ fn create_destroy_create_destroy_same_key_multi_dc ( ) {
634+ common:: run ( common:: TestOpts :: new ( 2 ) , |ctx| async move {
635+ create_destroy_create_destroy_same_key_inner ( ctx. get_dc ( 2 ) , ctx. get_dc ( 2 ) ) . await ;
636+ } ) ;
637+ }
638+
639+ #[ test]
640+ #[ ignore]
641+ fn create_destroy_create_destroy_same_key_different_dc ( ) {
642+ common:: run ( common:: TestOpts :: new ( 2 ) , |ctx| async move {
643+ create_destroy_create_destroy_same_key_inner ( ctx. leader_dc ( ) , ctx. get_dc ( 2 ) ) . await ;
644+ } ) ;
645+ }
646+
647+ async fn create_destroy_create_destroy_same_key_inner (
648+ target_dc1 : & common:: TestDatacenter ,
649+ target_dc2 : & common:: TestDatacenter ,
650+ ) {
651+ let ( namespace, _, runner) = common:: setup_test_namespace_with_runner ( target_dc1) . await ;
652+ let key = rand:: random :: < u16 > ( ) . to_string ( ) ;
653+
654+ // First create/destroy cycle
655+ let actor_id1 = common:: create_actor_with_options (
656+ common:: CreateActorOptions {
657+ namespace : namespace. clone ( ) ,
658+ key : Some ( key. clone ( ) ) ,
659+ ..Default :: default ( )
660+ } ,
661+ target_dc1. guard_port ( ) ,
662+ )
663+ . await ;
664+
665+ common:: assert_actor_in_dc ( & actor_id1, target_dc1. config . dc_label ( ) ) . await ;
666+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
667+
668+ // Destroy first actor
669+ tracing:: info!( ?actor_id1, "destroying first actor" ) ;
670+ common:: destroy_actor ( & actor_id1, & namespace, target_dc1. guard_port ( ) ) . await ;
671+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
672+
673+ // Second create/destroy cycle with same key
674+ let actor_id2 = common:: create_actor_with_options (
675+ common:: CreateActorOptions {
676+ namespace : namespace. clone ( ) ,
677+ key : Some ( key. clone ( ) ) ,
678+ ..Default :: default ( )
679+ } ,
680+ target_dc2. guard_port ( ) ,
681+ )
682+ . await ;
683+
684+ assert_ne ! ( actor_id1, actor_id2, "same actor id after first cycle" ) ;
685+ common:: assert_actor_in_dc ( & actor_id2, target_dc1. config . dc_label ( ) ) . await ;
686+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
687+
688+ // Destroy second actor
689+ tracing:: info!( ?actor_id2, "destroying second actor" ) ;
690+ common:: destroy_actor ( & actor_id2, & namespace, target_dc2. guard_port ( ) ) . await ;
691+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
692+
693+ // Third create/destroy cycle with same key
694+ let actor_id3 = common:: create_actor_with_options (
695+ common:: CreateActorOptions {
696+ namespace : namespace. clone ( ) ,
697+ key : Some ( key. clone ( ) ) ,
698+ ..Default :: default ( )
699+ } ,
700+ target_dc1. guard_port ( ) ,
701+ )
702+ . await ;
703+
704+ assert_ne ! ( actor_id1, actor_id3, "same actor id after second cycle (vs first)" ) ;
705+ assert_ne ! ( actor_id2, actor_id3, "same actor id after second cycle (vs second)" ) ;
706+ common:: assert_actor_in_dc ( & actor_id3, target_dc1. config . dc_label ( ) ) . await ;
707+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
708+
709+ // Destroy third actor
710+ tracing:: info!( ?actor_id3, "destroying third actor" ) ;
711+ common:: destroy_actor ( & actor_id3, & namespace, target_dc1. guard_port ( ) ) . await ;
712+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
713+
714+ // Fourth create/destroy cycle with same key
715+ let actor_id4 = common:: create_actor_with_options (
716+ common:: CreateActorOptions {
717+ namespace : namespace. clone ( ) ,
718+ key : Some ( key. clone ( ) ) ,
719+ ..Default :: default ( )
720+ } ,
721+ target_dc2. guard_port ( ) ,
722+ )
723+ . await ;
724+
725+ assert_ne ! ( actor_id1, actor_id4, "same actor id after third cycle (vs first)" ) ;
726+ assert_ne ! ( actor_id2, actor_id4, "same actor id after third cycle (vs second)" ) ;
727+ assert_ne ! ( actor_id3, actor_id4, "same actor id after third cycle (vs third)" ) ;
728+ common:: assert_actor_in_dc ( & actor_id4, target_dc1. config . dc_label ( ) ) . await ;
729+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
730+
731+ // Final destroy
732+ tracing:: info!( ?actor_id4, "destroying fourth actor" ) ;
733+ common:: destroy_actor ( & actor_id4, & namespace, target_dc2. guard_port ( ) ) . await ;
734+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 500 ) ) . await ;
735+
736+ runner. shutdown ( ) . await ;
737+ }
0 commit comments