From e727fc0dec3079422819de1163fa75875fbff968 Mon Sep 17 00:00:00 2001 From: Vlada Shubina Date: Tue, 27 Dec 2022 18:48:03 +0100 Subject: [PATCH] logging validation messages --- .../Settings/Scanner.cs | 102 +++++++++- .../DirectoryBasedTemplate.Interfaces.cs | 2 +- .../DirectoryBasedTemplate.cs | 4 +- .../RunnableProjectGenerator.cs | 15 -- .../TemplateValidationException.cs | 12 -- .../ScannerTests.cs | 145 +++++++++++++++ .../SchemaTests/JSONSchemaTests.cs | 4 + .../TemplateConfigTests/TemplateRootTests.cs | 3 +- .../.template.config/template.json | 176 +++++++++--------- .../.template.config}/template.json | 0 .../.template.config/template.json | 3 + 11 files changed, 341 insertions(+), 125 deletions(-) delete mode 100644 src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/TemplateValidationException.cs create mode 100644 test/Microsoft.TemplateEngine.Edge.UnitTests/ScannerTests.cs rename test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/{MissingMandatoryConfig => MissingIdentity/.template.config}/template.json (100%) create mode 100644 test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingMandatoryConfig/.template.config/template.json diff --git a/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs b/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs index d9894192899..7ed34a10623 100644 --- a/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs +++ b/src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Text; using System.Threading; using System.Threading.Tasks; #if !NETFULL @@ -214,16 +215,15 @@ private async Task ScanMountPointForTemplatesAsync(MountPointScanSou foreach (IGenerator generator in _environmentSettings.Components.OfType()) { IReadOnlyList templateList = await generator.GetTemplatesFromMountPointAsync(source.MountPoint, cancellationToken).ConfigureAwait(false); - foreach (IScanTemplateInfo template in templateList) - { - templates.Add(template); - } + LogScanningResults(source, templateList, generator); - source.FoundTemplates |= templateList.Count > 0; + IEnumerable validTemplates = templateList.Where(t => t.IsValid); + templates.AddRange(validTemplates); + source.FoundTemplates |= validTemplates.Any(); } //backward compatibility - var localizationLocators = templates.SelectMany(t => t.Localizations.Values).ToList(); + var localizationLocators = templates.SelectMany(t => t.Localizations.Values.Where(li => li.IsValid)).ToList(); return new ScanResult(source.MountPoint, templates, localizationLocators, Array.Empty<(string, Type, IIdentifiedComponent)>()); } @@ -281,6 +281,96 @@ private IEnumerable> LoadAllFromPath( return loaded; } + private void LogScanningResults(MountPointScanSource source, IReadOnlyList foundTemplates, IGenerator generator) + { + ILogger logger = _environmentSettings.Host.Logger; + logger.LogDebug("Scanning mount point '{0}' by generator '{1}': found {2} templates", source.MountPoint.MountPointUri, generator.Id, foundTemplates.Count); + foreach (IScanTemplateInfo template in foundTemplates) + { + string templateDisplayName = GetTemplateDisplayName(template); + logger.LogDebug("Found template {0}", templateDisplayName); + + LogValidationEntries( + logger, + string.Format("The template {0} has the following validation errors:", templateDisplayName), + template.ValidationErrors, + IValidationEntry.SeverityLevel.Error); + LogValidationEntries( + logger, + string.Format("The template {0} has the following validation warnings:", templateDisplayName), + template.ValidationErrors, + IValidationEntry.SeverityLevel.Warning); + LogValidationEntries( + logger, + string.Format("The template {0} has the following validation messages:", templateDisplayName), + template.ValidationErrors, + IValidationEntry.SeverityLevel.Info); + + foreach (KeyValuePair locator in template.Localizations) + { + ILocalizationLocator localizationInfo = locator.Value; + + LogValidationEntries( + logger, + string.Format("The template {0} has the following validation errors in '{1}' localization:", templateDisplayName, localizationInfo.Locale), + localizationInfo.ValidationErrors, + IValidationEntry.SeverityLevel.Error); + LogValidationEntries( + logger, + string.Format("The template {0} has the following validation warnings in '{1}' localization:", templateDisplayName, localizationInfo.Locale), + localizationInfo.ValidationErrors, + IValidationEntry.SeverityLevel.Warning); + LogValidationEntries( + logger, + string.Format("The template {0} has the following validation messages in '{1}' localization:", templateDisplayName, localizationInfo.Locale), + localizationInfo.ValidationErrors, + IValidationEntry.SeverityLevel.Info); + } + + if (!template.IsValid) + { + logger.LogError("Failed to install the template {0}: the template is not valid.", templateDisplayName); + } + foreach (ILocalizationLocator invalidLoc in template.Localizations.Values.Where(li => !li.IsValid)) + { + logger.LogWarning("Failed to install the '{0}' localization the template {1}: the localization file is not valid. The localization will be skipped.", invalidLoc.Locale, templateDisplayName); + } + } + + static string GetTemplateDisplayName(IScanTemplateInfo template) + { + string templateName = string.IsNullOrEmpty(template.Name) ? "" : template.Name; + return $"'{templateName}' ({template.Identity})"; + } + + static string PrintError(IValidationEntry error) => $" [{error.Severity}][{error.Code}] {error.ErrorMessage}"; + + static void LogValidationEntries(ILogger logger, string header, IReadOnlyList errors, IValidationEntry.SeverityLevel severity) + { + Action log = severity switch + { + IValidationEntry.SeverityLevel.None => (string s) => throw new NotSupportedException($"{IValidationEntry.SeverityLevel.None} severity is not supported."), + IValidationEntry.SeverityLevel.Info => (string s) => logger.LogDebug(s), + IValidationEntry.SeverityLevel.Warning => (string s) => logger.LogWarning(s), + IValidationEntry.SeverityLevel.Error => (string s) => logger.LogError(s), + _ => throw new InvalidOperationException($"{severity} is not expected value for {nameof(IValidationEntry.SeverityLevel)}."), + }; + + if (!errors.Any(e => e.Severity == severity)) + { + return; + } + + StringBuilder sb = new(); + sb.AppendLine(header); + foreach (IValidationEntry error in errors.Where(e => e.Severity == severity)) + { + sb.AppendLine(PrintError(error)); + } + log(sb.ToString()); + } + } + private class MountPointScanSource { public MountPointScanSource(string location, IMountPoint mountPoint, bool shouldStayInOriginalLocation, bool foundComponents, bool foundTemplates) diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.Interfaces.cs b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.Interfaces.cs index 2bd21dce971..e27ffbd3bee 100644 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.Interfaces.cs +++ b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.Interfaces.cs @@ -33,7 +33,7 @@ internal abstract partial class DirectoryBasedTemplate : ITemplateMetadata, ITem int ITemplateMetadata.Precedence => ConfigurationModel.Precedence; - string ITemplateMetadata.Name => ConfigurationModel.Name ?? throw new TemplateValidationException("Template configuration should have name defined"); + string ITemplateMetadata.Name => ConfigurationModel.Name ?? string.Empty; IReadOnlyList ITemplateMetadata.ShortNameList => ConfigurationModel.ShortNameList ?? new List(); diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.cs b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.cs index 27d1c036edb..19f5c3c7ed1 100644 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.cs +++ b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/DirectoryBasedTemplate.cs @@ -36,7 +36,7 @@ internal abstract partial class DirectoryBasedTemplate /// /// Creates the instance of the class based on configuration from . /// - /// when template configuration is invalid. + /// when template configuration is invalid. /// when template identity is null. /// when the template is not supported by current generator version. protected DirectoryBasedTemplate(IEngineEnvironmentSettings settings, IGenerator generator, IFile templateFile, string? baselineName = null) @@ -50,7 +50,7 @@ protected DirectoryBasedTemplate(IEngineEnvironmentSettings settings, IGenerator if (ConfigFile.Parent?.Parent is null) { - throw new TemplateValidationException(LocalizableStrings.Authoring_TemplateRootOutsideInstallSource); + throw new TemplateAuthoringException(LocalizableStrings.Authoring_TemplateRootOutsideInstallSource); } ConfigDirectory = templateFile.Parent; TemplateSourceRoot = ConfigFile.Parent.Parent; diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/RunnableProjectGenerator.cs b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/RunnableProjectGenerator.cs index b2bdf5e086c..e972a9853fe 100644 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/RunnableProjectGenerator.cs +++ b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/RunnableProjectGenerator.cs @@ -78,11 +78,6 @@ async Task> IGenerator.GetTemplatesFromMountPoi { throw; } - catch (TemplateValidationException) - { - //do nothing - //template validation prints all required information - } catch (NotSupportedException ex) { //do not print stack trace for this type. @@ -280,11 +275,6 @@ bool IGenerator.TryGetTemplateFromConfigInfo(IFileSystemInfo templateFileConfig, template = loadedTemplate; return true; } - catch (TemplateValidationException) - { - //do nothing - //template validation prints all required information - } catch (NotSupportedException ex) { //do not print stack trace for this type. @@ -360,11 +350,6 @@ internal async Task> GetTemplatesFromMountPoi cancellationToken.ThrowIfCancellationRequested(); templateList.Add(discoveredTemplate); } - catch (TemplateValidationException) - { - //do nothing - //template validation prints all required information - } catch (NotSupportedException ex) { //do not print stack trace for this type. diff --git a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/TemplateValidationException.cs b/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/TemplateValidationException.cs deleted file mode 100644 index 741f4a8915c..00000000000 --- a/src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/TemplateValidationException.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects -{ - internal class TemplateValidationException : Exception - { - internal TemplateValidationException(string message) : base(message) { } - } -} diff --git a/test/Microsoft.TemplateEngine.Edge.UnitTests/ScannerTests.cs b/test/Microsoft.TemplateEngine.Edge.UnitTests/ScannerTests.cs new file mode 100644 index 00000000000..9e5c3a1ee75 --- /dev/null +++ b/test/Microsoft.TemplateEngine.Edge.UnitTests/ScannerTests.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging; +using Microsoft.TemplateEngine.Abstractions; +using Microsoft.TemplateEngine.Edge.Settings; +using Microsoft.TemplateEngine.TestHelper; +using Microsoft.TemplateEngine.Tests; +using Xunit; + +namespace Microsoft.TemplateEngine.Edge.UnitTests +{ + public class ScannerTests : TestBase, IClassFixture + { + private readonly EnvironmentSettingsHelper _settingsHelper; + + public ScannerTests(EnvironmentSettingsHelper environmentSettingsHelper) + { + _settingsHelper = environmentSettingsHelper; + } + + [Fact] + public async Task CanLogValidationMessagesOnInstall_MissingIdentity() + { + string templatesLocation = Path.Combine(TestTemplatesLocation, "Invalid", "MissingIdentity"); + + List<(LogLevel Level, string Message)> loggedMessages = new(); + InMemoryLoggerProvider loggerProvider = new(loggedMessages); + + IEngineEnvironmentSettings settings = _settingsHelper.CreateEnvironment(virtualize: true, addLoggerProviders: new[] { loggerProvider }); + + Scanner scanner = new(settings); + + ScanResult result = await scanner.ScanAsync(templatesLocation, default).ConfigureAwait(false); + + Assert.Equal(0, result.Templates.Count); +#pragma warning disable CS0618 // Type or member is obsolete + Assert.Equal(0, result.Localizations.Count); +#pragma warning restore CS0618 // Type or member is obsolete + + string errorMessage = Assert.Single(loggedMessages, l => l.Level is LogLevel.Error).Message; + Assert.Equal($"Failed to load template from {Path.GetFullPath(templatesLocation) + Path.DirectorySeparatorChar}.template.config/template.json.{Environment.NewLine}Details: 'identity' is missing or is an empty string.", errorMessage); + } + + [Fact] + public async Task CanLogValidationMessagesOnInstall_ErrorsInTemplateConfig() + { + string templatesLocation = Path.Combine(TestTemplatesLocation, "Invalid", "MissingMandatoryConfig"); + + List<(LogLevel Level, string Message)> loggedMessages = new(); + InMemoryLoggerProvider loggerProvider = new(loggedMessages); + + IEngineEnvironmentSettings settings = _settingsHelper.CreateEnvironment(virtualize: true, addLoggerProviders: new[] { loggerProvider }); + + Scanner scanner = new(settings); + + ScanResult result = await scanner.ScanAsync(templatesLocation, default).ConfigureAwait(false); + + Assert.Equal(0, result.Templates.Count); +#pragma warning disable CS0618 // Type or member is obsolete + Assert.Equal(0, result.Localizations.Count); +#pragma warning restore CS0618 // Type or member is obsolete + + List errorMessages = loggedMessages.Where(lm => lm.Level == LogLevel.Error).Select(e => e.Message).ToList(); + Assert.Equal(2, errorMessages.Count); + + List warningMessages = loggedMessages.Where(lm => lm.Level == LogLevel.Warning).Select(e => e.Message).ToList(); + Assert.Empty(warningMessages); + + List debugMessages = loggedMessages.Where(lm => lm.Level == LogLevel.Debug).Select(e => e.Message).ToList(); + Assert.Equal(4, debugMessages.Count); + + Assert.Equal( + """ + The template '' (MissingConfigTest) has the following validation errors: + [Error][MV002] Missing 'name'. + [Error][MV003] Missing 'shortName'. + + """, + errorMessages[0]); + Assert.Equal("Failed to install the template '' (MissingConfigTest): the template is not valid.", errorMessages[1]); + + Assert.Contains( + """ + The template '' (MissingConfigTest) has the following validation messages: + [Info][MV005] Missing 'sourceName'. + [Info][MV006] Missing 'author'. + [Info][MV007] Missing 'groupIdentity'. + [Info][MV008] Missing 'generatorVersions'. + [Info][MV009] Missing 'precedence'. + [Info][MV010] Missing 'classifications'. + + """, + debugMessages); + } + + [Fact] + public async Task CanLogValidationMessagesOnInstall_Localization() + { + string templatesLocation = Path.Combine(TestTemplatesLocation, "Invalid", "Localization", "ValidationFailure"); + + List<(LogLevel Level, string Message)> loggedMessages = new(); + InMemoryLoggerProvider loggerProvider = new(loggedMessages); + + IEngineEnvironmentSettings settings = _settingsHelper.CreateEnvironment(virtualize: true, addLoggerProviders: new[] { loggerProvider }); + + Scanner scanner = new(settings); + + ScanResult result = await scanner.ScanAsync(templatesLocation, default).ConfigureAwait(false); + + Assert.Equal(1, result.Templates.Count); +#pragma warning disable CS0618 // Type or member is obsolete + Assert.Equal(0, result.Localizations.Count); +#pragma warning restore CS0618 // Type or member is obsolete + + List errorMessages = loggedMessages.Where(lm => lm.Level == LogLevel.Error).Select(e => e.Message).ToList(); + + Assert.Equal(2, errorMessages.Count); + + List warningMessages = loggedMessages.Where(lm => lm.Level == LogLevel.Warning).Select(e => e.Message).ToList(); + + Assert.Equal(3, warningMessages.Count); + + Assert.Equal( + """ + The template 'name' (TestAssets.Invalid.Localization.ValidationFailure) has the following validation errors in 'de-DE' localization: + [Error][LOC001] In localization file under the post action with id 'pa1', there are localized strings for manual instruction(s) with ids 'do-not-exist'. These manual instructions do not exist in the template.json file and should be removed from localization file. + [Error][LOC002] Post action(s) with id(s) 'pa0' specified in the localization file do not exist in the template.json file. Remove the localized strings from the localization file. + + """, + errorMessages[0]); + Assert.Equal( + """ + The template 'name' (TestAssets.Invalid.Localization.ValidationFailure) has the following validation errors in 'tr' localization: + [Error][LOC002] Post action(s) with id(s) 'pa6' specified in the localization file do not exist in the template.json file. Remove the localized strings from the localization file. + + """, + errorMessages[1]); + + Assert.Equal($"[{Path.GetFullPath(templatesLocation) + Path.DirectorySeparatorChar}.template.config/template.json]: id of the post action 'pa2' at index '3' is not unique. Only the first post action that uses this id will be localized.", warningMessages[0]); + Assert.Equal("Failed to install the 'de-DE' localization the template 'name' (TestAssets.Invalid.Localization.ValidationFailure): the localization file is not valid. The localization will be skipped.", warningMessages[1]); + Assert.Equal("Failed to install the 'tr' localization the template 'name' (TestAssets.Invalid.Localization.ValidationFailure): the localization file is not valid. The localization will be skipped.", warningMessages[2]); + } + } +} diff --git a/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/JSONSchemaTests.cs b/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/JSONSchemaTests.cs index 1540892663c..3db4653e48b 100644 --- a/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/JSONSchemaTests.cs +++ b/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/JSONSchemaTests.cs @@ -44,8 +44,12 @@ public void IsJSONSchemaValid(string testFile) public static IEnumerable GetAllTemplates() { + //those templates are intentionally wrong + string[] exceptions = new[] { "MissingIdentity", "MissingMandatoryConfig" }; + return Directory.EnumerateFiles(TestTemplatesLocation, "template.json", SearchOption.AllDirectories) .Where(s => s.Contains(".template.config")) + .Where(s => !exceptions.Any(e => s.Contains(e))) .Select(s => s.Remove(s.Length - JsonLocation.Length).Remove(0, TestTemplatesLocation.Length).Trim(Path.DirectorySeparatorChar)) .Select(s => new object?[] { s }); } diff --git a/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/TemplateConfigTests/TemplateRootTests.cs b/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/TemplateConfigTests/TemplateRootTests.cs index 29fb3a2f881..bc5f88fc2eb 100644 --- a/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/TemplateConfigTests/TemplateRootTests.cs +++ b/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/TemplateConfigTests/TemplateRootTests.cs @@ -10,6 +10,7 @@ using Microsoft.TemplateEngine.Abstractions.Mount; using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Validation; using Microsoft.TemplateEngine.TestHelper; +using Microsoft.TemplateEngine.Utils; using Xunit; namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests.TemplateConfigTests @@ -90,7 +91,7 @@ public void TemplateJsonCannotBeInMountPointRoot() IFile? templateConfigFile = mountPoint.FileInfo(pathToTemplateJson); Assert.NotNull(templateConfigFile); - TemplateValidationException e = Assert.Throws(() => new RunnableProjectConfig(environmentSettings, generator, templateConfigFile)); + TemplateAuthoringException e = Assert.Throws(() => new RunnableProjectConfig(environmentSettings, generator, templateConfigFile)); Assert.Equal(expectedErrorMessage, e.Message); } diff --git a/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/Localization/ValidationFailure/.template.config/template.json b/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/Localization/ValidationFailure/.template.config/template.json index 9aa27c28ed0..a94663532f9 100644 --- a/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/Localization/ValidationFailure/.template.config/template.json +++ b/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/Localization/ValidationFailure/.template.config/template.json @@ -1,102 +1,102 @@ { "$schema": "https://json.schemastore.org/template.json", - "author": "Test Asset", - "classifications": [ "Test Asset" ], - "name": "name", - "generatorVersions": "[1.0.0.0-*)", - "groupIdentity": "TestAssets.Invalid.Localiation.ValidationFailure", - "precedence": "100", - "identity": "TestAssets.Invalid.Localiation.ValidationFailure", - "shortName": "TestAssets.Invalid.Localiation.ValidationFailure", - "tags": { - "language": "C#", - "type": "project" + "author": "Test Asset", + "classifications": [ "Test Asset" ], + "name": "name", + "generatorVersions": "[1.0.0.0-*)", + "groupIdentity": "TestAssets.Invalid.Localization.ValidationFailure", + "precedence": "100", + "identity": "TestAssets.Invalid.Localization.ValidationFailure", + "shortName": "TestAssets.Invalid.Localization.ValidationFailure", + "tags": { + "language": "C#", + "type": "project" + }, + "sourceName": "bar", + "description": "desc", + "symbols": { + "someSymbol": { + "displayName": "sym0_displayName", + "description": "sym0_desc", + "type": "parameter", + "datatype": "string", + "defaultValue": "RegularDefaultString", + "replaces": "OriginalString", + "DefaultIfOptionWithoutValue": "NoValueDefaultString" }, - "sourceName": "bar", - "description": "desc", - "symbols": { - "someSymbol": { - "displayName": "sym0_displayName", - "description": "sym0_desc", - "type": "parameter", - "datatype": "string", - "defaultValue": "RegularDefaultString", - "replaces": "OriginalString", - "DefaultIfOptionWithoutValue": "NoValueDefaultString" + "someChoice": { + "type": "parameter", + "displayName": "sym1_displayName", + "description": "sym1_desc", + "datatype": "choice", + "choices": [ + { + "choice": "choice0", + "displayName": "sym1_choice0_displayName", + "description": "sym1_choice0" + }, + { + "choice": "choice1", + "displayName": "sym1_choice1_displayName", + "description": "sym1_choice1" }, - "someChoice": { - "type": "parameter", - "displayName": "sym1_displayName", - "description": "sym1_desc", - "datatype": "choice", - "choices": [ - { - "choice": "choice0", - "displayName": "sym1_choice0_displayName", - "description": "sym1_choice0" - }, - { - "choice": "choice1", - "displayName": "sym1_choice1_displayName", - "description": "sym1_choice1" - }, - { - "choice": "choice2", - "displayName": "sym1_choice2_displayName", - "description": "sym1_choice2" - } - ], - "replaces": "choice1", - "defaultValue": "choice1" + { + "choice": "choice2", + "displayName": "sym1_choice2_displayName", + "description": "sym1_choice2" + } + ], + "replaces": "choice1", + "defaultValue": "choice1" + } + }, + "postActions": [ + { + "description": "pa0_desc", + "manualInstructions": [ + { + "id": "first_instruction", + "text": "pa0_manualInstructions" } + ], + "actionId": "58E1433C-303B-4713-8A47-26E7B03BD8A5" }, - "postActions": [ + { + "id": "pa1", + "description": "pa1_desc", + "manualInstructions": [ { - "description": "pa0_desc", - "manualInstructions": [ - { - "id": "first_instruction", - "text": "pa0_manualInstructions" - } - ], - "actionId": "58E1433C-303B-4713-8A47-26E7B03BD8A5" + "id": "first_instruction", + "text": "pa1_manualInstructions0" }, { - "id": "pa1", - "description": "pa1_desc", - "manualInstructions": [ - { - "id": "first_instruction", - "text": "pa1_manualInstructions0" - }, - { - "id": "second_instruction", - "text": "pa1_manualInstructions1" - }, - { - "id": "third_instruction", - "text": "pa1_manualInstructions2" - } - ], - "actionId": "2B5CDBB7-1FAC-41EC-ADCC-08A73021BC40" + "id": "second_instruction", + "text": "pa1_manualInstructions1" }, { - "id": "pa2", - "manualInstructions": [ - { - "text": "pa2_manualInstructions" - } - ], - "actionId": "58E1433C-303B-4713-8A47-26E7B03BD8A5" - }, + "id": "third_instruction", + "text": "pa1_manualInstructions2" + } + ], + "actionId": "2B5CDBB7-1FAC-41EC-ADCC-08A73021BC40" + }, + { + "id": "pa2", + "manualInstructions": [ + { + "text": "pa2_manualInstructions" + } + ], + "actionId": "58E1433C-303B-4713-8A47-26E7B03BD8A5" + }, + { + "id": "pa2", + "manualInstructions": [ { - "id": "pa2", - "manualInstructions": [ - { - "text": "pa2_manualInstructions" - } - ], - "actionId": "58E1433C-303B-4713-8A47-26E7B03BD8A5" + "text": "pa2_manualInstructions" } - ] + ], + "actionId": "58E1433C-303B-4713-8A47-26E7B03BD8A5" + } + ] } diff --git a/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingMandatoryConfig/template.json b/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingIdentity/.template.config/template.json similarity index 100% rename from test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingMandatoryConfig/template.json rename to test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingIdentity/.template.config/template.json diff --git a/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingMandatoryConfig/.template.config/template.json b/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingMandatoryConfig/.template.config/template.json new file mode 100644 index 00000000000..03c33c9312d --- /dev/null +++ b/test/Microsoft.TemplateEngine.TestTemplates/test_templates/Invalid/MissingMandatoryConfig/.template.config/template.json @@ -0,0 +1,3 @@ +{ + "identity": "MissingConfigTest", +}