@@ -467,6 +467,116 @@ describe('WebApi Units', function () {
467467 assert . equal ( myWebApi . isNoProxyHost ( 'https://my-tfs-instance.host/myproject' ) , true ) ;
468468 assert . equal ( myWebApi . isNoProxyHost ( 'https://my-other-tfs-instance.host/myproject' ) , false ) ;
469469 } ) ;
470+
471+ describe ( 'getServiceEndpointApi' , function ( ) {
472+ const baseUrl : string = 'https://dev.azure.com/' ;
473+ const serviceEndpointResourceAreaId : string = '1814ab31-2f4f-4a9f-8761-f4d77dc5a5d7' ;
474+ const resourceAreasLocationId : string = 'e81700f7-3be2-46de-8624-2eb35882fcaa' ;
475+
476+ afterEach ( ( ) => {
477+ nock . cleanAll ( ) ;
478+ } ) ;
479+
480+ // Mocks the Location-area OPTIONS discovery call so the resourceAreas GET routes to `_apis/resourceAreas`.
481+ function mockLocationOptions ( server : string ) : void {
482+ nock ( server + '_apis/Location' )
483+ . options ( '' )
484+ . reply ( 200 , {
485+ value : [ {
486+ id : resourceAreasLocationId ,
487+ maxVersion : '7.2' ,
488+ releasedVersion : '7.2' ,
489+ routeTemplate : '_apis/resourceAreas' ,
490+ area : 'Location' ,
491+ resourceName : 'ResourceAreas' ,
492+ resourceVersion : '1'
493+ } ]
494+ } ) ;
495+ }
496+
497+ it ( 'returns a ServiceEndpointApi using the resolved resource area URL' , async ( ) => {
498+ // Arrange
499+ const resolvedUrl : string = 'https://serviceendpoint.dev.azure.com/' ;
500+ mockLocationOptions ( baseUrl ) ;
501+ nock ( baseUrl )
502+ . get ( '/_apis/resourceAreas' )
503+ . reply ( 200 , {
504+ value : [ {
505+ id : serviceEndpointResourceAreaId ,
506+ name : 'serviceendpoint' ,
507+ locationUrl : resolvedUrl
508+ } ]
509+ } ) ;
510+ const myWebApi : WebApi . WebApi = new WebApi . WebApi ( baseUrl , WebApi . getBasicHandler ( 'user' , 'password' ) ) ;
511+
512+ // Act
513+ const serviceEndpointApi = await myWebApi . getServiceEndpointApi ( ) ;
514+
515+ // Assert
516+ assert ( serviceEndpointApi , 'ServiceEndpointApi should be created' ) ;
517+ assert . equal ( serviceEndpointApi . baseUrl , resolvedUrl , 'baseUrl should match the resolved resource area locationUrl' ) ;
518+ } ) ;
519+
520+ it ( 'falls back to the server URL when resource areas are empty (on-prem)' , async ( ) => {
521+ // Arrange
522+ const onPremUrl : string = 'https://my-tfs-instance.host/' ;
523+ mockLocationOptions ( onPremUrl ) ;
524+ nock ( onPremUrl )
525+ . get ( '/_apis/resourceAreas' )
526+ . reply ( 200 , { count : 0 , value : null } ) ;
527+ const myWebApi : WebApi . WebApi = new WebApi . WebApi ( onPremUrl , WebApi . getBasicHandler ( 'user' , 'password' ) ) ;
528+
529+ // Act
530+ const serviceEndpointApi = await myWebApi . getServiceEndpointApi ( ) ;
531+
532+ // Assert
533+ assert ( serviceEndpointApi , 'ServiceEndpointApi should be created' ) ;
534+ assert . equal ( serviceEndpointApi . baseUrl , onPremUrl , 'baseUrl should fall back to the server URL on-prem' ) ;
535+ } ) ;
536+
537+ it ( 'uses provided handlers instead of the default auth handler' , async ( ) => {
538+ // Arrange
539+ const resolvedUrl : string = 'https://serviceendpoint.dev.azure.com/' ;
540+ mockLocationOptions ( baseUrl ) ;
541+ nock ( baseUrl )
542+ . get ( '/_apis/resourceAreas' )
543+ . reply ( 200 , {
544+ value : [ {
545+ id : serviceEndpointResourceAreaId ,
546+ name : 'serviceendpoint' ,
547+ locationUrl : resolvedUrl
548+ } ]
549+ } ) ;
550+ const customHandler = WebApi . getBearerHandler ( 'custom-token' ) ;
551+ const myWebApi : WebApi . WebApi = new WebApi . WebApi ( baseUrl , WebApi . getBasicHandler ( 'user' , 'password' ) ) ;
552+
553+ // Act
554+ const serviceEndpointApi = await myWebApi . getServiceEndpointApi ( undefined , [ customHandler ] ) ;
555+
556+ // Assert
557+ assert ( serviceEndpointApi , 'ServiceEndpointApi should be created when custom handlers are provided' ) ;
558+ assert . equal ( serviceEndpointApi . baseUrl , resolvedUrl , 'baseUrl should still resolve from resource areas' ) ;
559+ } ) ;
560+
561+ it ( 'uses the provided serverUrl as the fallback baseUrl when resource areas are empty' , async ( ) => {
562+ // Arrange
563+ const defaultUrl : string = baseUrl ;
564+ const customUrl : string = 'https://custom-tfs.contoso.com/' ;
565+ // Resource area discovery happens against the WebApi's default serverUrl (via its own LocationsApi),
566+ // but the custom serverUrl is used as the fallback when no resource areas are returned.
567+ mockLocationOptions ( defaultUrl ) ;
568+ nock ( defaultUrl )
569+ . get ( '/_apis/resourceAreas' )
570+ . reply ( 200 , { count : 0 , value : null } ) ;
571+ const myWebApi : WebApi . WebApi = new WebApi . WebApi ( defaultUrl , WebApi . getBasicHandler ( 'user' , 'password' ) ) ;
572+
573+ // Act
574+ const serviceEndpointApi = await myWebApi . getServiceEndpointApi ( customUrl ) ;
575+
576+ // Assert
577+ assert . equal ( serviceEndpointApi . baseUrl , customUrl , 'baseUrl should use the custom serverUrl fallback, not the WebApi default' ) ;
578+ } ) ;
579+ } ) ;
470580} ) ;
471581
472582describe ( 'Auth Handlers Units' , function ( ) {
0 commit comments