@@ -2832,3 +2832,79 @@ async fn snapshot_hook_fires_with_store() {
28322832 . expect ( "hook present when a store is set" ) ;
28332833 hook ( ) . await ;
28342834}
2835+
2836+ #[ tokio:: test]
2837+ async fn repro_issue_2107_corrupt_state_when_runtime_absent ( ) {
2838+ // No runtime -> require_runtime() fails. Reproduce issue #2107:
2839+ // a failed create must NOT leak the in-progress reservation.
2840+ let svc = make_service ( ) ;
2841+ let create = request (
2842+ "CreateDBInstance" ,
2843+ & [
2844+ ( "DBInstanceIdentifier" , "corrupt-db" ) ,
2845+ ( "Engine" , "mysql" ) ,
2846+ ( "EngineVersion" , "8.0" ) ,
2847+ ( "DBInstanceClass" , "db.t3.micro" ) ,
2848+ ( "MasterUsername" , "admin" ) ,
2849+ ( "MasterUserPassword" , "secretpass" ) ,
2850+ ] ,
2851+ ) ;
2852+
2853+ // First create fails because there is no container runtime.
2854+ let e1 = match svc. create_db_instance ( & create) . await {
2855+ Ok ( _) => panic ! ( "expected failure with no runtime" ) ,
2856+ Err ( e) => e,
2857+ } ;
2858+ assert_eq ! ( e1. code( ) , "InsufficientDBInstanceCapacity" ) ;
2859+
2860+ // Second create must ALSO be InsufficientDBInstanceCapacity, NOT
2861+ // DBInstanceAlreadyExists. A leaked reservation shows up here.
2862+ let e2 = match svc. create_db_instance ( & create) . await {
2863+ Ok ( _) => panic ! ( "expected failure with no runtime" ) ,
2864+ Err ( e) => e,
2865+ } ;
2866+ assert_ne ! (
2867+ e2. code( ) ,
2868+ "DBInstanceAlreadyExists" ,
2869+ "leaked in-progress reservation corrupts state"
2870+ ) ;
2871+
2872+ let state = svc. state . read ( ) ;
2873+ assert ! (
2874+ state. default_ref( ) . in_progress_instance_ids. is_empty( ) ,
2875+ "failed create left a leaked reservation"
2876+ ) ;
2877+ }
2878+
2879+ #[ tokio:: test]
2880+ async fn create_without_engine_version_uses_engine_default ( ) {
2881+ // Issue #2107: a version-less create must default EngineVersion to a
2882+ // version in the requested engine's supported list -- not a fixed
2883+ // postgres value like "16.3", which would make every version-less
2884+ // mysql/mariadb/oracle/... create fail validation with
2885+ // "EngineVersion '16.3' is not available" before it ever reaches the
2886+ // runtime. With no runtime the create still fails, but it must fail
2887+ // at require_runtime (InsufficientDBInstanceCapacity), proving
2888+ // validation passed with the engine-appropriate default.
2889+ let svc = make_service ( ) ;
2890+ let create = request (
2891+ "CreateDBInstance" ,
2892+ & [
2893+ ( "DBInstanceIdentifier" , "mysql-nover" ) ,
2894+ ( "Engine" , "mysql" ) ,
2895+ ( "DBInstanceClass" , "db.t3.micro" ) ,
2896+ ( "MasterUsername" , "admin" ) ,
2897+ ( "MasterUserPassword" , "secretpass" ) ,
2898+ ] ,
2899+ ) ;
2900+ let err = match svc. create_db_instance ( & create) . await {
2901+ Ok ( _) => panic ! ( "expected failure with no runtime" ) ,
2902+ Err ( e) => e,
2903+ } ;
2904+ assert_eq ! (
2905+ err. code( ) ,
2906+ "InsufficientDBInstanceCapacity" ,
2907+ "version-less mysql create must pass validation via the engine default, \
2908+ not fail on a hardcoded postgres version"
2909+ ) ;
2910+ }
0 commit comments