import { diagnosticSources , IDeploymentParameterDefinition , IDeploymentTemplate , testDiagnostics } from "../support/diagnostics" ; import { testWithLanguageServer } from "../support/testWithLanguageServer" ; suite ( "Expressions functional tests" , ( ) => { function testExpression ( testName , expression , expected ) { testWithLanguageServer ( testName || expression , async ( ) => { let template = { $schema : "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#" , contentVersion : "1.0.0.0" , resources : [ { name : "resource1" , type : "some type" , apiVersion : "xxx" , location : "westus" , properties : { } } ] , variables : { } , parameters : { } } ; function addVarIfUsed ( varName , value ) { if ( expression . match ( new RegExp ( `variables\\s*\\(\\s*'${ varName }'\\s*\\)` , "i" ) ) ) { template . variables ! [ varName ] = value ; } } function addParamIfUsed ( paramName , definition ) { if ( expression . match ( new RegExp ( `parameters\\s*\\(\\s*'${ paramName }'\\s*\\)` , "i" ) ) ) { template . parameters ! [ paramName ] = definition ; } } addVarIfUsed ( "stringVar" , "hello" ) ; addVarIfUsed ( "intVar" , "321" ) ; addVarIfUsed ( "arrayVar" , [ 1 , 2 , [ 3 , 4 , 5 ] ] ) ; addParamIfUsed ( "intParam" , { type : "int" } ) ; addParamIfUsed ( "arrayParam" , { type : "array" , defaultValue : [ 1 , 2 , 3 ] } ) ; addParamIfUsed ( "objParam" , { type : "object" , defaultValue : { abc : 1 , def : "def" , ghi : [ 1 , 2 , 3 ] , jkl : { a : "hello" } } } ) ; template . resources [ 0 ] ! . properties ! [ `test` ] = expression ; await testDiagnostics ( template , { includeSources : [ diagnosticSources . expressions ] , includeRange : false } , expected ) ; } ) ; } function testLiteralExpression ( literalExpression , expected ) { testExpression ( `testLiteralExpression("${ literalExpression }")` , `[concat(${ literalExpression })]` , expected ) ; } suite ( "general issues" , ( ) => { testExpression ( "Empty expression" , "[]" , [ "Error: Expected a function or property expression. (arm-template (expressions))" ] ) ; testExpression ( "Missing right bracket" , "[" , [ "Error: Expected a right square bracket (']'). (arm-template (expressions))" , "Error: Expected a function or property expression. (arm-template (expressions))" ] ) ; testExpression ( "" , "[concat('abc')" , [ "Error: Expected a right square bracket (']'). (arm-template (expressions))" ] ) ; testExpression ( "string after function" , "[deployment()'world']" , [ "Error: Expected the end of the string. (arm-template (expressions)) [11,31]" ] ) ; testExpression ( "with several invalid literals" , ".[]82348923asdglih   asl .,'" , [ ] ) ; testExpression ( "https://github.com/Microsoft/vscode-azurearmtools/issues/34" , "[concat(reference(parameters('publicIpName')).dnsSettings.fqdn, ';  sudo docker volume rm ''dockercompose_cert-volume''; sudo docker-compose up')]" , [ "Error: Undefined parameter reference: 'publicIpName' (arm-template (expressions)) [11,47-11,61]" ] ) ; } ) ; suite ( "Plain strings vs expressions" , ( ) => { suite ( "1: Starts with [ but doesn't end with ] -> consider a string, don't change" , ( ) => { testExpression ( "" , "[[one]two" , [ ] ) ; } ) ; suite ( "2: Starts with [[ -> replace first [[ with [, consider a string" , ( ) => { testExpression ( "" , "[[one]" , [ ] ) ; testExpression ( "" , "[[one]two]" , [ ] ) ; testExpression ( "" , "[[[one]two]" , [ ] ) ; testExpression ( "" , "[[abc" , [ ] ) ; testExpression ( "" , "[[[abc" , [ ] ) ; } ) ; suite ( "3: Starts with [ (and ends with ]) -> expression" , ( ) => { testExpression ( "" , "[concat('')]" , [ ] ) ; } ) ; suite ( "4: Anything else is a string" , ( ) => { } ) ; testExpression ( "" , "]" , [ ] ) ; } ) ; suite ( "string literals" , ( ) => { testLiteralExpression ( "''" , [ ] ) ; testLiteralExpression ( "'hello'" , [ ] ) ; testLiteralExpression ( " '123' " , [ ] ) ; suite ( "Escaped apostrophes" , ( ) => { testLiteralExpression ( "'That''s it!'" , [ ] ) ; testLiteralExpression ( "''''" , [ ] ) ; testLiteralExpression ( "  ' '' '  " , [ ] ) ; testLiteralExpression ( "'''That''s all, \"folks\"!'''" , [ ] ) ; } ) ; testLiteralExpression ( "'Bad apostrophe's'" , [ "Error: Expected a comma (','). (arm-template (expressions))" , "Error: Expected a comma (','). (arm-template (expressions))" , "Error: Expected a right square bracket (']'). (arm-template (expressions))" , ] ) ; suite ( "Escaped apostrophes" , ( ) => { testLiteralExpression ( "''" , [ ] ) ; testLiteralExpression ( "'That''s it!'" , [ ] ) ; } ) ; testLiteralExpression ( "'Bad apostrophe's'" , [ "Error: Expected a comma (','). (arm-template (expressions))" , "Error: Expected a comma (','). (arm-template (expressions))" , "Error: Expected a right square bracket (']'). (arm-template (expressions))" , ] ) ; } ) ; suite ( "numeric literals" , ( ) => { testLiteralExpression ( "123" , [ ] ) ; testLiteralExpression ( "0" , [ ] ) ; testLiteralExpression ( "-0" , [ ] ) ; testLiteralExpression ( "1" , [ ] ) ; testLiteralExpression ( "-1" , [ ] ) ; testLiteralExpression ( "-1234" , [ ] ) ; testLiteralExpression ( "1234" , [ ] ) ; testLiteralExpression ( "7.8" , [ ] ) ; testLiteralExpression ( "-3.14159265" , [ ] ) ; } ) ; suite ( "Function calls" , ( ) => { suite ( "Missing left paren" , ( ) => { testExpression ( "" , "[concat'abc')]" , [ "Error: Expected the end of the string. (arm-template (expressions)) [11,25]" , "Error: Expected the end of the string. (arm-template (expressions)) [11,30]" , "Error: Missing function argument list. (arm-template (expressions)) [11,19]" ] ) ; testExpression ( "" , "[concat 1,2)]" , [ 'Error: Expected the end of the string. (arm-template (expressions)) [11,26]' , 'Error: Expected the end of the string. (arm-template (expressions)) [11,27]' , 'Error: Expected the end of the string. (arm-template (expressions)) [11,28]' , 'Error: Expected the end of the string. (arm-template (expressions)) [11,29]' , 'Error: Missing function argument list. (arm-template (expressions)) [11,19]' ] ) ; } ) ; suite ( "Missing right paren" , ( ) => { testExpression ( "" , "[concat('abc']" , [ "Error: Expected a right parenthesis (')'). (arm-template (expressions)) [11,31-11,32]" ] ) ; } ) ; testExpression ( "" , "[deployment()]" , [ ] ) ; testExpression ( "" , "[string(1)]" , [ ] ) ; testExpression ( "" , "[equals('abc','bc')]" , [ ] ) ; testExpression ( "" , "[ equals ( 'abc', 'bc' ) ]" , [ ] ) ; testExpression ( "" , "[equals('abc' 'bc')]" , [ "Error: Expected a comma (','). (arm-template (expressions)) [11,32-11,36]" , "Error: The function 'equals' takes 2 arguments. (arm-template (expressions)) [11,19-11,37]" ] ) ; testExpression ( "" , "[equals()]" , [ "Error: The function 'equals' takes 2 arguments. (arm-template (expressions)) [11,19-11,27]" ] ) ; testExpression ( "" , "[equals('a')]" , [ "Error: The function 'equals' takes 2 arguments. (arm-template (expressions)) [11,19]" ] ) ; testExpression ( "" , "[equals('a', 'b', 'c')]" , [ "Error: The function 'equals' takes 2 arguments. (arm-template (expressions)) [11,19]" ] ) ; testExpression ( "" , "[parameter('arrayParam')]" , [ "Error: Unrecognized function name 'parameter'. (arm-template (expressions)) [11,19]" ] ) ; } ) ; suite ( "Variables/Parameters" , ( ) => { testExpression ( "" , "[variables('arrayVar')]" , [ ] ) ; testExpression ( "" , "[parameters('arrayParam')]" , [ ] ) ; suite ( "Undefined parameters/variables" , ( ) => { testExpression ( "" , "[parameters('undefined')]" , [ 'Error: Undefined parameter reference: \'undefined\' (arm-template (expressions)) [11,30-11,41]' ] ) ; testExpression ( "" , "[variables('undefined')]" , [ "Error: Undefined variable reference: 'undefined' (arm-template (expressions)) [11,29-11,40]" ] ) ; testExpression ( "" , "[parameters('')]" , [ "Error: Undefined parameter reference: '' (arm-template (expressions)) [11,30-11,32]" ] ) ; testExpression ( "with child property access from undefined variable reference" , "[variables('undefVar').apples]" , [ "Error: Undefined variable reference: 'undefVar' (arm-template (expressions)) [11,29-11,39]" ] ) ; testExpression ( "with grandchild property access from undefined variable reference" , "[variables('undefVar').apples.bananas]" , [ "Error: Undefined variable reference: 'undefVar' (arm-template (expressions)) [11,29]" ] ) ; testExpression ( "with child property access from variable reference to non-object variable" , "[variables('intVar').apples]" , [ `Error: Property "apples" is not a defined property of "variables('intVar')". (arm-template (expressions))` ] ) ; testExpression ( "with grandchild property access from variable reference to non-object variable" , "[variables('stringVar').apples.bananas]" , [ `Error: Property "apples" is not a defined property of "variables('stringVar')". (arm-template (expressions))` ] ) ; } ) ; } ) ; suite ( "Array/Object access" , ( ) => { testExpression ( "" , "[parameters('arrayParam')[0]]" , [ ] ) ; testExpression ( "" , "[variables('arrayVar')[parameters('intParam')]]" , [ ] ) ; testExpression ( "" , "[variables('arrayVar')[1][1]]" , [ ] ) ; testExpression ( "" , "[variables('arrayVar')[add(12,3)]]" , [ ] ) ; testExpression ( "" , "[parameters('objParam').abc]" , [ ] ) ; testExpression ( "" , "[ parameters ( 'objParam' ) . abc ]" , [ ] ) ; testExpression ( "" , "[parameters('objParam').abc[2]]" , [ ] ) ; testExpression ( "" , "[string(variables('arrayVar')[0])]" , [ ] ) ; testExpression ( "" , "[variables('arrayVar')[1][1]" , [ "Error: Expected a right square bracket (']'). (arm-template (expressions)) [11,46]" ] ) ; } ) ; suite ( "Property access" , ( ) => { testExpression ( "" , "[resourceGroup().name]" , [ ] ) ; testExpression ( "" , "[resourceGroup()name]" , [ "Error: Expected the end of the string. (arm-template (expressions)) [11,34]" ] ) ; testExpression ( "" , "[resourceGroup().'name']" , [ "Error: Expected a literal value. (arm-template (expressions)) [11,35]" ] ) ; testExpression ( "" , "[resourceGroup().1]" , [ "Error: Expected a literal value. (arm-template (expressions)) [11,35]" ] ) ; testExpression ( "" , "[resourceGroup().]" , [ "Error: Expected a literal value. (arm-template (expressions)) [11,35]" ] ) ; testExpression ( "" , "[resourceGroup().name.length]" , [ ] ) ; } ) ; suite ( "Miscellaneous and real scenarios" , ( ) => { testExpression ( "" , "[concat(parameters('_artifactsLocation'), '/', '/scripts/azuremysql.sh', parameters('_artifactsLocationSasToken'))], )]" , [ 'Error: Nothing should exist after the closing \']\' except for whitespace. (arm-template (expressions)) [11,133-11,134]' , 'Error: Nothing should exist after the closing \']\' except for whitespace. (arm-template (expressions)) [11,135-11,136]' , 'Error: Nothing should exist after the closing \']\' except for whitespace. (arm-template (expressions)) [11,136-11,137]' , 'Error: Undefined parameter reference: \'_artifactsLocation\' (arm-template (expressions)) [11,37-11,57]' , 'Error: Undefined parameter reference: \'_artifactsLocationSasToken\' (arm-template (expressions)) [11,102-11,130]' ] ) ; } ) ; } ) ;