@@ -19,6 +19,10 @@ import {
1919} from '../../models/app/app.test-data.js'
2020import { ExtensionInstance } from '../../models/extensions/extension-instance.js'
2121import { ListApps } from '../../api/graphql/app-management/generated/apps.js'
22+ import {
23+ ListAppDevStores ,
24+ ListAppDevStoresQuery ,
25+ } from '../../api/graphql/business-platform-organizations/generated/list_app_dev_stores.js'
2226import { PublicApiVersionsQuery } from '../../api/graphql/webhooks/generated/public-api-versions.js'
2327import { AvailableTopicsQuery } from '../../api/graphql/webhooks/generated/available-topics.js'
2428import { CliTesting , CliTestingMutation } from '../../api/graphql/webhooks/generated/cli-testing.js'
@@ -2052,3 +2056,117 @@ describe('uidStrategyFromTypename', () => {
20522056 expect ( uidStrategyFromTypename ( 'UnknownStrategy' ) ) . toBe ( 'uuid' )
20532057 } )
20542058} )
2059+
2060+ describe ( 'devStoresForOrg' , ( ) => {
2061+ test ( 'queries Business Platform with both STORE_TYPE and STORE_STATUS=ACTIVE filters' , async ( ) => {
2062+ // Given
2063+ const orgGid = 'gid://shopify/Organization/123'
2064+ const searchTerm = 'my-store'
2065+ const mockedResponse : ListAppDevStoresQuery = {
2066+ organization : {
2067+ id : orgGid ,
2068+ name : 'Org 123' ,
2069+ accessibleShops : {
2070+ edges : [
2071+ {
2072+ node : {
2073+ id : 'gid://BusinessPlatform/Shop/1' ,
2074+ externalId : encodedGidFromShopId ( '1' ) ,
2075+ name : 'My Active Store' ,
2076+ storeType : 'APP_DEVELOPMENT' ,
2077+ primaryDomain : 'my-active-store.myshopify.com' ,
2078+ shortName : 'my-active-store' ,
2079+ url : 'https://my-active-store.myshopify.com' ,
2080+ } ,
2081+ } ,
2082+ ] ,
2083+ pageInfo : { hasNextPage : false } ,
2084+ } ,
2085+ currentUser : { organizationPermissions : [ 'ondemand_access_to_stores' ] } ,
2086+ } ,
2087+ }
2088+ vi . mocked ( businessPlatformOrganizationsRequestDoc ) . mockResolvedValueOnce ( mockedResponse )
2089+
2090+ // When
2091+ const client = AppManagementClient . getInstance ( )
2092+ client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
2093+ const result = await client . devStoresForOrg ( orgGid , searchTerm )
2094+
2095+ // Then
2096+ expect ( vi . mocked ( businessPlatformOrganizationsRequestDoc ) ) . toHaveBeenCalledWith ( {
2097+ query : ListAppDevStores ,
2098+ token : 'business-platform-token' ,
2099+ organizationId : '123' ,
2100+ variables : { searchTerm} ,
2101+ unauthorizedHandler : {
2102+ type : 'token_refresh' ,
2103+ handler : expect . any ( Function ) ,
2104+ } ,
2105+ } )
2106+
2107+ // The deployed query AST must include both filters with the right shape so
2108+ // BP doesn't return inactive/cancelled/deleted stores. Mirrors the filter
2109+ // pattern already in production for the Dev Dashboard dev-stores list.
2110+ const accessibleShopsField = (
2111+ ListAppDevStores as unknown as {
2112+ definitions : { selectionSet : { selections : { selectionSet : { selections : any [ ] } } [ ] } } [ ]
2113+ }
2114+ ) . definitions [ 0 ] ! . selectionSet . selections [ 0 ] ! . selectionSet . selections . find (
2115+ ( selection : any ) => selection . name ?. value === 'accessibleShops' ,
2116+ )
2117+ const filtersArg = accessibleShopsField . arguments . find ( ( arg : any ) => arg . name . value === 'filters' )
2118+ expect ( filtersArg . value . kind ) . toBe ( 'ListValue' )
2119+ const filters = filtersArg . value . values . map ( ( value : any ) => {
2120+ const output : Record < string , string > = { }
2121+ for ( const field of value . fields ) output [ field . name . value ] = field . value . value
2122+ return output
2123+ } )
2124+ expect ( filters ) . toEqual ( [
2125+ { field : 'STORE_TYPE' , operator : 'EQUALS' , value : 'app_development' } ,
2126+ { field : 'STORE_STATUS' , operator : 'EQUALS' , value : 'ACTIVE' } ,
2127+ ] )
2128+
2129+ expect ( result . hasMorePages ) . toBe ( false )
2130+ expect ( result . stores ) . toHaveLength ( 1 )
2131+ expect ( result . stores [ 0 ] ) . toMatchObject ( {
2132+ shopName : 'My Active Store' ,
2133+ shopDomain : 'my-active-store.myshopify.com' ,
2134+ provisionable : true ,
2135+ storeType : 'APP_DEVELOPMENT' ,
2136+ } )
2137+ } )
2138+
2139+ test ( 'throws when the organization is not found' , async ( ) => {
2140+ // Given
2141+ vi . mocked ( businessPlatformOrganizationsRequestDoc ) . mockResolvedValueOnce ( { organization : null } )
2142+
2143+ // When
2144+ const client = AppManagementClient . getInstance ( )
2145+ client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
2146+
2147+ // Then
2148+ await expect ( client . devStoresForOrg ( 'gid://shopify/Organization/123' ) ) . rejects . toThrow ( 'No organization found' )
2149+ } )
2150+
2151+ test ( 'returns an empty list when no accessible stores match the filters' , async ( ) => {
2152+ // Given a deleted/inactive store would be filtered out by STORE_STATUS=ACTIVE,
2153+ // so BP returns an empty edges list to the CLI.
2154+ vi . mocked ( businessPlatformOrganizationsRequestDoc ) . mockResolvedValueOnce ( {
2155+ organization : {
2156+ id : 'gid://shopify/Organization/123' ,
2157+ name : 'Org 123' ,
2158+ accessibleShops : { edges : [ ] , pageInfo : { hasNextPage : false } } ,
2159+ currentUser : { organizationPermissions : [ ] } ,
2160+ } ,
2161+ } )
2162+
2163+ // When
2164+ const client = AppManagementClient . getInstance ( )
2165+ client . businessPlatformToken = ( ) => Promise . resolve ( 'business-platform-token' )
2166+ const result = await client . devStoresForOrg ( 'gid://shopify/Organization/123' )
2167+
2168+ // Then
2169+ expect ( result . stores ) . toEqual ( [ ] )
2170+ expect ( result . hasMorePages ) . toBe ( false )
2171+ } )
2172+ } )
0 commit comments