@@ -383,6 +383,111 @@ function makeVeeamRequest(params, callback) {
383383 return done ( ) ;
384384 } ) ) ;
385385 } ) ;
386+
387+ it ( 'GET capacity.xml should return 200 when scubaclient returns 404 (post-install scenario)' , done => {
388+ // This test simulates the post-install scenario where scubaclient returns 404
389+ // because no metrics are available yet. By not calling scuba.incrementBytesForBucket,
390+ // the mock scuba server will return 404 for this bucket.
391+
392+ async . waterfall ( [
393+ next => makeVeeamRequest ( {
394+ method : 'PUT' ,
395+ bucket : TEST_BUCKET ,
396+ objectKey : '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/capacity.xml' ,
397+ headers : {
398+ 'content-length' : testCapacity . length ,
399+ 'content-md5' : testCapacityMd5 ,
400+ 'x-scal-canonical-id' : testArn ,
401+ } ,
402+ authCredentials : veeamAuthCredentials ,
403+ requestBody : testCapacity ,
404+ } , ( err , response ) => {
405+ if ( err ) {
406+ return done ( err ) ;
407+ }
408+ assert . strictEqual ( response . statusCode , 200 ) ;
409+ return next ( ) ;
410+ } ) ,
411+ next => makeVeeamRequest ( {
412+ method : 'GET' ,
413+ bucket : TEST_BUCKET ,
414+ objectKey : '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/capacity.xml' ,
415+ headers : {
416+ 'x-scal-canonical-id' : testArn ,
417+ } ,
418+ authCredentials : veeamAuthCredentials ,
419+ } , ( err , response ) => {
420+ if ( err ) {
421+ return done ( err ) ;
422+ }
423+ // Critical assertion: for 404 from scubaclient (no metrics yet),
424+ // should return 200 with static capacity data (Used=0)
425+ assert . strictEqual ( response . statusCode , 200 ,
426+ 'should return 200 when scubaclient returns 404 (no metrics available)' ) ;
427+ // Should return capacity.xml with static data
428+ assert ( response . body . includes ( '<CapacityInfo>' ) ,
429+ 'should return capacity.xml content' ) ;
430+ assert ( response . body . includes ( '<Used>0</Used>' ) ,
431+ 'Used should be 0 from static bucket metadata' ) ;
432+ return next ( ) ;
433+ } ) ,
434+ ] , err => {
435+ assert . ifError ( err ) ;
436+ return done ( ) ;
437+ } ) ;
438+ } ) ;
439+
440+ it ( 'GET system.xml should return 200 even when scubaclient is down' , done => {
441+ // system.xml doesn't use scubaclient, so it should always work
442+ // This test stops scuba to verify system.xml is independent of utilization metrics
443+ async . waterfall ( [
444+ next => makeVeeamRequest ( {
445+ method : 'PUT' ,
446+ bucket : TEST_BUCKET ,
447+ objectKey : '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/system.xml' ,
448+ headers : {
449+ 'content-length' : testSystem . length ,
450+ 'content-md5' : testSystemMd5 ,
451+ 'x-scal-canonical-id' : testArn ,
452+ } ,
453+ authCredentials : veeamAuthCredentials ,
454+ requestBody : testSystem ,
455+ } , ( err , response ) => {
456+ if ( err ) {
457+ return done ( err ) ;
458+ }
459+ assert . strictEqual ( response . statusCode , 200 ) ;
460+ return next ( ) ;
461+ } ) ,
462+ next => {
463+ // Stop scuba - system.xml should still work
464+ scuba . stop ( ) ;
465+ return next ( ) ;
466+ } ,
467+ next => makeVeeamRequest ( {
468+ method : 'GET' ,
469+ bucket : TEST_BUCKET ,
470+ objectKey : '.system-d26a9498-cb7c-4a87-a44a-8ae204f5ba6c/system.xml' ,
471+ headers : {
472+ 'x-scal-canonical-id' : testArn ,
473+ } ,
474+ authCredentials : veeamAuthCredentials ,
475+ } , ( err , response ) => {
476+ if ( err ) {
477+ return done ( err ) ;
478+ }
479+ assert . strictEqual ( response . statusCode , 200 ,
480+ 'system.xml should always return 200 even when scuba is down' ) ;
481+ assert . strictEqual ( response . body . replaceAll ( ' ' , '' ) , testSystem . replaceAll ( ' ' , '' ) ) ;
482+ return next ( ) ;
483+ } ) ,
484+ ] , err => {
485+ // Restart scuba for subsequent tests
486+ scuba . start ( ) ;
487+ assert . ifError ( err ) ;
488+ return done ( ) ;
489+ } ) ;
490+ } ) ;
386491 } ) ;
387492
388493 describe ( 'veeam DELETE routes:' , ( ) => {
0 commit comments