import dns = require ( "dns" ) ; import { BlobServiceClient , newPipeline , StorageSharedKeyCredential , BlockBlobClient } from "@azure/storage-blob" ; import assert = require ( "assert" ) ; import { configLogger } from "../../src/common/Logger" ; import BlobTestServerFactory from "../BlobTestServerFactory" ; import { appendToURLPath , EMULATOR_ACCOUNT_KEY , EMULATOR_ACCOUNT_NAME , getUniqueName } from "../testutils" ; configLogger ( false ) ; describe ( "SpecialNaming" , ( ) => { const factory = new BlobTestServerFactory ( ) ; const server = factory . createServer ( ) ; const baseURL = `http://${ server . config . host }:${ server . config . port }/devstoreaccount1` ; const productionStyleHostName = "devstoreaccount1.localhost" ; const noAccountHostName = "host.docker.internal" ; const noAccountHostNameConnectionString = `DefaultEndpointsProtocol=http;AccountName=${ EMULATOR_ACCOUNT_NAME };AccountKey=${ EMULATOR_ACCOUNT_KEY };BlobEndpoint=http://${ noAccountHostName }:${ server . config . port }/${ EMULATOR_ACCOUNT_NAME };` ; const serviceClient = new BlobServiceClient ( baseURL , newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } , keepAliveOptions : { enable : false } } ) ) ; const containerName = getUniqueName ( "1container-with-dash" ) ; const containerClient = serviceClient . getContainerClient ( containerName ) ; before ( async ( ) => { await server . start ( ) ; await containerClient . create ( ) ; } ) ; after ( async ( ) => { await containerClient . delete ( ) ; await server . close ( ) ; await server . clean ( ) ; } ) ; it ( "Should work with special container and blob names with spaces @loki @sql" , async ( ) => { const blobName = getUniqueName ( "blob empty" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special container and blob names with unicode @loki @sql" , async ( ) => { const blobName = getUniqueName ( "unicod\u00e9" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; assert . deepStrictEqual ( response . segment . blobItems [ 0 ] . name , blobName ) ; } ) ; it ( "Should work with special container and blob names with spaces in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "blob empty" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special container and blob names with / @loki @sql" , async ( ) => { const blobName = getUniqueName ( "////blob/empty /another" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special container and blob names with / in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "////blob/empty /another" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special container and blob names uppercase @loki @sql" , async ( ) => { const blobName = getUniqueName ( "////Upper/blob/empty /another" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special container and blob names uppercase in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "////Upper/blob/empty /another" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob names Chinese characters @loki @sql" , async ( ) => { const blobName = getUniqueName ( "////Upper/blob/empty /another 汉字" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob names Chinese characters in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "////Upper/blob/empty /another 汉字" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name characters @loki @sql" , async ( ) => { const blobName = getUniqueName ( "汉字. special ~!@#$%^&*()_+`1234567890-={}|[]\\:\";'<>?,/'" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "b" , { prefix : blobName . replace ( /\\/g , "/" ) } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name characters in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "汉字. special ~!@#$%^&*()_+`1234567890-={}|[]\\:\";'<>?,/'" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName . replace ( /%/g , "%25" ) . replace ( /\?/g , "%3F" ) ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "b" , { prefix : blobName . replace ( /\\/g , "/" ) } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Russian URI encoded @loki @sql" , async ( ) => { const blobName = getUniqueName ( "ру́сский язы́к" ) ; const blobNameEncoded = encodeURIComponent ( blobName ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobNameEncoded ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobNameEncoded } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Russian @loki @sql" , async ( ) => { const blobName = getUniqueName ( "ру́сский язы́к" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Russian in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "ру́сский язы́к" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Arabic URI encoded @loki @sql" , async ( ) => { const blobName = getUniqueName ( "عربي/عربى" ) ; const blobNameEncoded = encodeURIComponent ( blobName ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobNameEncoded ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobNameEncoded } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Arabic @loki @sql" , async ( ) => { const blobName = getUniqueName ( "عربي/عربى" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Arabic in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "عربي/عربى" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Japanese URI encoded @loki @sql" , async ( ) => { const blobName = getUniqueName ( "にっぽんご/にほんご" ) ; const blobNameEncoded = encodeURIComponent ( blobName ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobNameEncoded ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobNameEncoded } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Japanese @loki @sql" , async ( ) => { const blobName = getUniqueName ( "にっぽんご/にほんご" ) ; const blockBlobClient = containerClient . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( "Should work with special blob name Japanese in URL string @loki @sql" , async ( ) => { const blobName = getUniqueName ( "にっぽんご/にほんご" ) ; const blockBlobClient = new BlockBlobClient ( appendToURLPath ( containerClient . url , blobName ) , ( containerClient as any ) . pipeline ) ; await blockBlobClient . upload ( "A" , 1 ) ; await blockBlobClient . getProperties ( ) ; const response = ( await containerClient . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } ) ; it ( `Should work with production style URL when ${ productionStyleHostName } is resolvable` , async ( ) => { await dns . promises . lookup ( productionStyleHostName ) . then ( async ( lookupAddress ) => { const baseURLProductionStyle = `http://${ productionStyleHostName }:${ server . config . port }` ; const serviceClientProductionStyle = new BlobServiceClient ( baseURLProductionStyle , newPipeline ( new StorageSharedKeyCredential ( EMULATOR_ACCOUNT_NAME , EMULATOR_ACCOUNT_KEY ) , { retryOptions : { maxTries : 1 } , keepAliveOptions : { enable : false } } ) ) ; const containerClientProductionStyle = serviceClientProductionStyle . getContainerClient ( containerName ) ; const blobName = getUniqueName ( "myblob" ) ; const blockBlobClient = containerClientProductionStyle . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "ABC" , 3 ) ; const response = ( await containerClientProductionStyle . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } , ( ) => { assert . ok ( `Skipping test case - it needs ${ productionStyleHostName } to be resolvable` ) ; } ) ; } ) ; it ( `Should work with no account host name URL when ${ noAccountHostName } is resolvable` , async ( ) => { await dns . promises . lookup ( noAccountHostName ) . then ( async ( lookupAddress ) => { const serviceClientNoHostName = BlobServiceClient . fromConnectionString ( noAccountHostNameConnectionString , { retryOptions : { maxTries : 1 } , keepAliveOptions : { enable : false } } ) ; const containerClientProductionStyle = serviceClientNoHostName . getContainerClient ( containerName ) ; const blobName = getUniqueName ( "myblob" ) ; const blockBlobClient = containerClientProductionStyle . getBlockBlobClient ( blobName ) ; await blockBlobClient . upload ( "ABC" , 3 ) ; const response = ( await containerClientProductionStyle . listBlobsByHierarchy ( "$" , { prefix : blobName } ) . byPage ( ) . next ( ) ) . value ; assert . notDeepEqual ( response . segment . blobItems . length , 0 ) ; } , ( ) => { assert . ok ( `Skipping test case - it needs ${ noAccountHostName } to be resolvable` ) ; } ) ; } ) ; } ) ;