import { HttpOperationResponse , ServiceClient } from '@azure/ms-rest-js' ; import * as assert from 'assert' ; import * as fse from 'fs-extra' ; import * as path from 'path' ; import * as vscode from 'vscode' ; import { createTestActionContext , runWithTestActionContext , TestInput } from 'vscode-azureextensiondev' ; import { copyFunctionUrl , createGenericClient , createNewProjectInternal , deployProductionSlot , FuncVersion , getRandomHexString , nonNullProp } from '../../extension.bundle' ; import { addParallelSuite , ParallelTest , runInSeries } from '../addParallelSuite' ; import { getTestWorkspaceFolder } from '../global.test' ; import { defaultTestFuncVersion , getCSharpValidateOptions , getJavaScriptValidateOptions , getPowerShellValidateOptions , getPythonValidateOptions , getTypeScriptValidateOptions , IValidateProjectOptions , validateProject } from '../project/validateProject' ; import { getRotatingAuthLevel , getRotatingLocation , getRotatingNodeVersion , getRotatingPythonVersion } from './getRotatingValue' ; import { resourceGroupsToDelete } from './global.nightly.test' ; interface CreateProjectAndDeployTestCase extends ICreateProjectAndDeployOptions { title : string ; buildMachineOsToSkip ? : NodeJS . Platform | NodeJS . Platform [ ] ; } const testCases = [ { title : 'JavaScript' , ... getJavaScriptValidateOptions ( true ) , deployInputs : [ getRotatingNodeVersion ( ) ] } , { title : 'TypeScript' , ... getTypeScriptValidateOptions ( ) , deployInputs : [ getRotatingNodeVersion ( ) ] } , { title : 'C# .NET Core 3.1' , buildMachineOsToSkip : 'darwin' , ... getCSharpValidateOptions ( 'netcoreapp3.1' ) , createProjectInputs : [ /net.*3/i ] , deployInputs : [ /net.*3/i ] , createFunctionInputs : [ 'Company.Function' ] } , { title : 'C# .NET 5' , buildMachineOsToSkip : 'darwin' , ... getCSharpValidateOptions ( 'net5.0' ) , createProjectInputs : [ /net.*5/i ] , deployInputs : [ /net.*5/i ] , createFunctionInputs : [ 'Company.Function' ] } , { title : 'C# .NET 6' , buildMachineOsToSkip : [ 'darwin' , 'win32' ] , ... getCSharpValidateOptions ( 'net6.0' , FuncVersion . v4 ) , createProjectInputs : [ /net.*6/i ] , deployInputs : [ /net.*6/i ] , createFunctionInputs : [ 'Company.Function' ] } , { title : 'PowerShell' , ... getPowerShellValidateOptions ( ) , deployInputs : [ /powershell.*7/i ] } , { title : 'Python' , buildMachineOsToSkip : 'win32' , ... getPythonValidateOptions ( '.venv' ) , createProjectInputs : [ /3\.7/ ] , deployInputs : [ getRotatingPythonVersion ( ) ] } ] ; const parallelTests = [ ] ; for ( const testCase of testCases ) { const osToSkip = Array . isArray ( testCase . buildMachineOsToSkip ) ? testCase . buildMachineOsToSkip : [ testCase . buildMachineOsToSkip ] ; if ( ! osToSkip . some ( o => o === process . platform ) ) { parallelTests . push ( { title : testCase . title , callback : async ( ) => { await testCreateProjectAndDeploy ( testCase ) ; } } ) ; } } addParallelSuite ( parallelTests , { title : 'Create Project and Deploy' , timeoutMS : 7 * 60 * 1000 , isLongRunning : true } ) ; interface ICreateProjectAndDeployOptions extends IValidateProjectOptions { createProjectInputs ? : ( string | RegExp | TestInput ) [ ] ; createFunctionInputs ? : ( string | RegExp | TestInput ) [ ] ; deployInputs ? : ( string | RegExp | TestInput ) [ ] ; } async function testCreateProjectAndDeploy ( options ) { const functionName = 'func' + getRandomHexString ( ) ; const testWorkspacePath = getTestWorkspaceFolder ( ) ; await runWithTestActionContext ( 'createNewProject' , async ( context ) => { options . createProjectInputs = options . createProjectInputs || [ ] ; options . createFunctionInputs = options . createFunctionInputs || [ ] ; await context . ui . runWithInputs ( [ testWorkspacePath , options . language , ... options . createProjectInputs , /http\s*trigger/i , functionName , ... options . createFunctionInputs , getRotatingAuthLevel ( ) ] , async ( ) => { const createProjectOptions = options . version !== defaultTestFuncVersion ? { version : options . version } : { } ; await createNewProjectInternal ( context , createProjectOptions ) ; } ) ; } ) ; options . excludedPaths = options . excludedPaths || [ ] ; options . excludedPaths . push ( '.git' ) ; await validateProject ( testWorkspacePath , options ) ; const routePrefix = getRandomHexString ( ) ; await addRoutePrefixToProject ( testWorkspacePath , routePrefix ) ; const appName = 'funcBasic' + getRandomHexString ( ) ; resourceGroupsToDelete . push ( appName ) ; await runWithTestActionContext ( 'deploy' , async ( context ) => { options . deployInputs = options . deployInputs || [ ] ; await context . ui . runWithInputs ( [ testWorkspacePath , /create new function app/i , appName , ... options . deployInputs , getRotatingLocation ( ) ] , async ( ) => { await deployProductionSlot ( context ) ; } ) ; } ) ; await validateFunctionUrl ( appName , functionName , routePrefix ) ; } async function addRoutePrefixToProject ( testWorkspacePath , routePrefix ) { const hostPath = path . join ( testWorkspacePath , 'host.json' ) ; const hostJson = await fse . readJSON ( hostPath ) ; hostJson . extensions = { http : { routePrefix } } ; await fse . writeJSON ( hostPath , hostJson ) ; } async function validateFunctionUrl ( appName , functionName , routePrefix ) { const inputs = [ /^((?!Local Project).)*$/i , appName , functionName ] ; let functionUrl ; await runWithTestActionContext ( 'copyFunctionUrl' , async ( context ) => { await runInSeries ( 'copyFunctionUrl' , async ( ) => { await vscode . env . clipboard . writeText ( '' ) ; await context . ui . runWithInputs ( inputs , async ( ) => { await copyFunctionUrl ( context ) ; } ) ; functionUrl = await vscode . env . clipboard . readText ( ) ; } ) ; } ) ; assert . ok ( functionUrl ?. includes ( routePrefix ) , `Function url "${ functionUrl }" did not include routePrefix "${ routePrefix }".` ) ; const client = await createGenericClient ( await createTestActionContext ( ) , undefined ) ; const response = await client . sendRequest ( { method : 'POST' , url : functionUrl , body : { name : "World" } } ) ; const body = nonNullProp ( response , 'bodyAsText' ) ; assert . ok ( ( body . includes ( 'Hello' ) && body . includes ( 'World' ) ) || body . includes ( 'Welcome' ) , 'Expected function response to include "Hello World" or "Welcome"' ) ; }