@@ -12,6 +12,7 @@ use nexus_test_utils::resource_helpers::create_local_user;
1212use nexus_test_utils:: resource_helpers:: grant_iam;
1313use nexus_test_utils:: resource_helpers:: link_ip_pool;
1414use nexus_test_utils:: resource_helpers:: object_create;
15+ use nexus_test_utils:: resource_helpers:: object_create_error;
1516use nexus_test_utils:: resource_helpers:: test_params;
1617use nexus_test_utils_macros:: nexus_test;
1718use nexus_types:: external_api:: params;
@@ -50,6 +51,27 @@ impl ResourceAllocator {
5051 . await
5152 }
5253
54+ async fn set_quotas_expect_error (
55+ & self ,
56+ client : & ClientTestContext ,
57+ quotas : params:: SiloQuotasUpdate ,
58+ code : http:: StatusCode ,
59+ ) -> HttpErrorResponseBody {
60+ NexusRequest :: expect_failure_with_body (
61+ client,
62+ code,
63+ http:: Method :: PUT ,
64+ "/v1/system/silos/quota-test-silo/quotas" ,
65+ & Some ( & quotas) ,
66+ )
67+ . authn_as ( self . auth . clone ( ) )
68+ . execute ( )
69+ . await
70+ . expect ( "Expected failure updating quotas" )
71+ . parsed_body :: < HttpErrorResponseBody > ( )
72+ . expect ( "Failed to read response after setting quotas" )
73+ }
74+
5375 async fn get_quotas ( & self , client : & ClientTestContext ) -> SiloQuotas {
5476 NexusRequest :: object_get (
5577 client,
@@ -386,3 +408,68 @@ async fn test_quota_limits(cptestctx: &ControlPlaneTestContext) {
386408 assert_eq ! ( quotas. limits. storage, quota_limit. storage. unwrap( ) ) ;
387409 }
388410}
411+
412+ #[ nexus_test]
413+ async fn test_negative_quota ( cptestctx : & ControlPlaneTestContext ) {
414+ let client = & cptestctx. external_client ;
415+
416+ // Can't make a silo with a negative quota
417+ let mut quotas = params:: SiloQuotasCreate :: empty ( ) ;
418+ quotas. cpus = -1 ;
419+ let response = object_create_error (
420+ client,
421+ "/v1/system/silos" ,
422+ & params:: SiloCreate {
423+ identity : IdentityMetadataCreateParams {
424+ name : "negative-cpus-not-allowed" . parse ( ) . unwrap ( ) ,
425+ description : "" . into ( ) ,
426+ } ,
427+ quotas,
428+ discoverable : true ,
429+ identity_mode : shared:: SiloIdentityMode :: LocalOnly ,
430+ admin_group_name : None ,
431+ tls_certificates : vec ! [ ] ,
432+ mapped_fleet_roles : Default :: default ( ) ,
433+ } ,
434+ http:: StatusCode :: BAD_REQUEST ,
435+ )
436+ . await ;
437+
438+ assert ! (
439+ response. message. contains(
440+ "Cannot create silo quota: CPU quota must not be negative"
441+ ) ,
442+ "Unexpected response: {}" ,
443+ response. message
444+ ) ;
445+
446+ // Make the silo with an empty quota
447+ let system = setup_silo_with_quota (
448+ & client,
449+ "quota-test-silo" ,
450+ params:: SiloQuotasCreate :: empty ( ) ,
451+ )
452+ . await ;
453+
454+ // Can't update a silo with a negative quota
455+ let quota_limit = params:: SiloQuotasUpdate {
456+ cpus : Some ( -1 ) ,
457+ memory : Some ( 0_u64 . try_into ( ) . unwrap ( ) ) ,
458+ storage : Some ( 0_u64 . try_into ( ) . unwrap ( ) ) ,
459+ } ;
460+ let response = system
461+ . set_quotas_expect_error (
462+ client,
463+ quota_limit. clone ( ) ,
464+ http:: StatusCode :: BAD_REQUEST ,
465+ )
466+ . await ;
467+
468+ assert ! (
469+ response. message. contains(
470+ "Cannot update silo quota: CPU quota must not be negative"
471+ ) ,
472+ "Unexpected response: {}" ,
473+ response. message
474+ ) ;
475+ }
0 commit comments