From b8e40a516fae02deb2674914492a6aa3540e5722 Mon Sep 17 00:00:00 2001 From: Stephen Weatherford Date: Fri, 19 Jun 2020 12:01:58 -0700 Subject: [PATCH] Integrate new validation code that fixes linked/nested templates --- test/LinkedTemplates.test.ts | 130 ++++++++++++++++++++++++++++++++++- test/support/diagnostics.ts | 34 +++++---- 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/test/LinkedTemplates.test.ts b/test/LinkedTemplates.test.ts index e87f3bdd4..d1a7c1d7d 100644 --- a/test/LinkedTemplates.test.ts +++ b/test/LinkedTemplates.test.ts @@ -5,7 +5,7 @@ // tslint:disable:no-unused-expression max-func-body-length promise-function-async max-line-length no-unnecessary-class // tslint:disable:no-non-null-assertion object-literal-key-quotes variable-name no-constant-condition -import { testDiagnosticsFromFile } from "./support/diagnostics"; +import { testDiagnostics, testDiagnosticsFromFile } from "./support/diagnostics"; suite("Linked templates", () => { suite("variables and parameters inside templateLink object refer to the parent's scope", () => { @@ -18,5 +18,133 @@ suite("Linked templates", () => { ] ); }); + + suite('Regress #773: Regression from 0.10.0: top-level parameters not recognized in nested template properties', () => { + test("simple", async () => { + await testDiagnosticsFromFile( + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "name": "aadLinkedTemplate", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2019-10-01", + "properties": { + "mode": "Incremental", + "templateLink": { + "uri": "https://foo" + } + } + } + ] + }, + { + parameters: { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": {} + } + }, + [ + // Should be no errors + ]); + }); + test("linked-templates-scope.json", async () => { + await testDiagnosticsFromFile( + 'templates/linked-templates-scope.json', + { + parameters: { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": {} + } + }, + [ + // Should be no errors + ] + ); + }); + }); + }); + + suite("Error location inside linked and nested templates", async () => { + test("Nested template wrong param type", async () => { + await testDiagnostics( + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2019-10-01", + "name": "inner", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "parameter1": { + "value": 123 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "parameter1": { + "type": "string" + } + }, + "resources": [] + } + } + } + ] + }, + { + includeRange: true, + parameters: { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + } + } + }, + [ + "Error: Template validation failed: Template parameter JToken type is not valid. Expected 'String, Uri'. Actual 'Integer'. Please see https://aka.ms/arm-deploy/#parameter-file for usage details. (arm-template (validation) [8,20-8,20]) [The error occurred in a nested template near here [13,39-13,39]]", + "Warning: The parameter 'parameter1' is never used. (arm-template (expressions) [22,12-22,24])"] + ); + }); + + test("Nested template missing properties", async () => { + await testDiagnostics( + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2019-10-01", + "name": "inner" + } + ] + }, + { + includeRange: true, + parameters: { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + } + } + }, + [ + `Error: Template validation failed: The deployment must have Properties property set. Please see https://aka.ms/arm-deploy for usage details. (arm-template (validation) [4,4-4,4])`, + `Warning: Missing required property \"properties\" (arm-template (schema) [4,4-4,5])` + ] + ); + }); }); }); diff --git a/test/support/diagnostics.ts b/test/support/diagnostics.ts index 4e0efe6fc..6e5007cb8 100644 --- a/test/support/diagnostics.ts +++ b/test/support/diagnostics.ts @@ -16,7 +16,7 @@ const DEBUG_BREAK_AFTER_DIAGNOSTICS_COMPLETE = false; import * as assert from "assert"; import * as fs from 'fs'; import * as path from 'path'; -import { Diagnostic, DiagnosticSeverity, Disposable, languages, TextDocument } from "vscode"; +import { Diagnostic, DiagnosticSeverity, Disposable, languages, Range, TextDocument } from "vscode"; import { diagnosticsCompletePrefix, expressionsDiagnosticsSource, ExpressionType, ext, LanguageServerState, languageServerStateSource } from "../../extension.bundle"; import { DISABLE_LANGUAGE_SERVER } from "../testConstants"; import { parseParametersWithMarkers } from "./parseTemplate"; @@ -464,26 +464,34 @@ function diagnosticToString(diagnostic: Diagnostic, options: IGetDiagnosticsOpti default: assert.fail(`Expected severity ${diagnostic.severity}`); } - let s = `${severity}: ${diagnostic.message} (${diagnostic.source})`; - - // Do the expected messages include ranges? - if (includeRange) { - // tslint:disable-next-line: strict-boolean-expressions - if (!diagnostic.range) { - s += " []"; - } else { - s += ` [${diagnostic.range.start.line},${diagnostic.range.start.character}` - + `-${diagnostic.range.end.line},${diagnostic.range.end.character}]`; - } + let s = `${severity}: ${diagnostic.message} (${diagnostic.source}${rangeAsString(diagnostic.range)})`; + if (diagnostic.relatedInformation) { + const related = diagnostic.relatedInformation[0]; + s = `${s} [${related.message}${rangeAsString(related.location.range)}]`; } return s; + + function rangeAsString(range: Range): string { + // Do the expected messages include ranges? + if (includeRange) { + // tslint:disable-next-line: strict-boolean-expressions + if (!range) { + return "[]"; + } else { + return ` [${range.start.line},${range.start.character}` + + `-${range.end.line},${range.end.character}]`; + } + } + + return ""; + } } function compareDiagnostics(actual: Diagnostic[], expected: string[], options: ITestDiagnosticsOptions): void { // Do the expected messages include ranges? let expectedHasRanges = expected.length === 0 || !!expected[0].match(/[0-9]+,[0-9]+-[0-9]+,[0-9]+/); - let includeRanges = !!options.includeRange && expectedHasRanges; + let includeRanges = !!options.includeRange || expectedHasRanges; let actualAsStrings = actual.map(d => diagnosticToString(d, options, includeRanges));