From b511687409287d68fd1628b715f98a49f542e25f Mon Sep 17 00:00:00 2001 From: Nirjan Chapagain Date: Fri, 13 Feb 2026 16:56:56 -0800 Subject: [PATCH 1/6] Adding new vc component --- .../CreateResponseFileTests.cs | 135 ++++++++++++++++++ .../CreateResourceFile.cs | 101 +++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs create mode 100644 src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs new file mode 100644 index 0000000000..2ff13fe232 --- /dev/null +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace VirtualClient.Dependencies.UnitTests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.IO.Abstractions; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Extensions.DependencyInjection; + using NUnit.Framework; + using VirtualClient.Common.Telemetry; + + [TestFixture] + [Category("Unit")] + public class CreateResponseFileTests + { + private string originalCurrentDirectory; + private string tempDirectory; + private MockFixture mockFixture; + + [SetUp] + public void SetUp() + { + this.originalCurrentDirectory = Environment.CurrentDirectory; + this.tempDirectory = Path.Combine(Path.GetTempPath(), "VirtualClient", "UnitTests", Guid.NewGuid().ToString("n")); + Directory.CreateDirectory(this.tempDirectory); + + Environment.CurrentDirectory = this.tempDirectory; + this.mockFixture = new MockFixture(); + } + + [TearDown] + public void TearDown() + { + Environment.CurrentDirectory = this.originalCurrentDirectory; + + try + { + if (Directory.Exists(this.tempDirectory)) + { + Directory.Delete(this.tempDirectory, recursive: true); + } + } + catch + { + // Best-effort cleanup for test runs. + } + } + + [Test] + public async Task ExecuteAsync_DoesNotCreateFile_WhenNoOptionsAreSupplied() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new FileSystem()); + + var parameters = new Dictionary + { + ["FileName"] = "resource_access.rsp" + }; + + TestCreateResponseFile executor = new TestCreateResponseFile(this.mockFixture); + await executor.ExecuteAsync().ConfigureAwait(false); + + string expectedPath = Path.Combine(this.tempDirectory, "resource_access.rsp"); + Assert.False(File.Exists(expectedPath), "The response file should not be created when no options are supplied."); + } + + [Test] + public async Task ExecuteAsync_CreatesResponseFile_WithExpectedContent() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new FileSystem()); + + var parameters = new Dictionary + { + ["FileName"] = "resource_access.rsp", + ["Option2"] = "--KeyVaultUri=\"https://crc-partner-vault.vault.azure.net\"", + ["Option1"] = "--System=\"Testing\"" + }; + + TestCreateResponseFile executor = new TestCreateResponseFile(this.mockFixture); + await executor.ExecuteAsync().ConfigureAwait(false); + + string expectedPath = Path.Combine(this.tempDirectory, "resource_access.rsp"); + Assert.True(File.Exists(expectedPath), "The response file should be created when options are supplied."); + + // Current implementation orders keys lexically (OrdinalIgnoreCase): Option1 then Option2. + string expectedContent = "--System=\"Testing\" --KeyVaultUri=\"https://crc-partner-vault.vault.azure.net\""; + string actualContent = await File.ReadAllTextAsync(expectedPath, Encoding.UTF8); + + Assert.AreEqual(expectedContent, actualContent); + } + + [Test] + public async Task ExecuteAsync_UsesAbsoluteFileName_WhenRootedPathProvided() + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(new FileSystem()); + + string absolutePath = Path.Combine(this.tempDirectory, "sub", "my.rsp"); + Directory.CreateDirectory(Path.GetDirectoryName(absolutePath)!); + + var parameters = new Dictionary + { + ["FileName"] = absolutePath, + ["Option1"] = "--System=\"Testing\"" + }; + + TestCreateResponseFile executor = new TestCreateResponseFile(this.mockFixture); + await executor.ExecuteAsync().ConfigureAwait(false); + + Assert.True(File.Exists(absolutePath), "The response file should be created at the rooted path."); + string actualContent = await File.ReadAllTextAsync(absolutePath, Encoding.UTF8); + Assert.AreEqual("--System=\"Testing\"", actualContent); + } + + private class TestCreateResponseFile : CreateResponseFile + { + public TestCreateResponseFile(MockFixture mockFixture) + : base(mockFixture.Dependencies, mockFixture.Parameters) + { + } + + public async Task ExecuteAsync() + { + await base.ExecuteAsync(EventContext.None, CancellationToken.None).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs new file mode 100644 index 0000000000..95e46d7528 --- /dev/null +++ b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace VirtualClient.Dependencies +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.IO.Abstractions; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Extensions.DependencyInjection; + using VirtualClient.Common.Extensions; + using VirtualClient.Common.Telemetry; + using VirtualClient.Contracts; + + /// + /// A Virtual Client component that creates a response file (e.g. a *.rsp file) containing a space-delimited + /// list of command-line options. + /// + /// + /// Virtual Client can automatically consume response files, allowing users to pass fewer arguments directly on the + /// command line (and keep long/complex option sets in a file instead). + /// + /// Options are provided via parameters whose keys start with Option (case-insensitive), for example: + /// Option1=--System="Testing" + /// Option2=--KeyVaultUri="https://crc-partner-vault.vault.azure.net" + /// + /// The file is written to unless is an absolute path. + /// + public class CreateResponseFile : VirtualClientComponent + { + private readonly IFileSystem fileSystem; + + /// + /// Initializes a new instance of the class. + /// + /// Dependency injection container. + /// Component parameters. + public CreateResponseFile(IServiceCollection dependencies, IDictionary parameters) + : base(dependencies, parameters) + { + this.fileSystem = dependencies.GetService(); + this.fileSystem.ThrowIfNull(nameof(this.fileSystem)); + } + + /// + /// Gets the name (or path) of the response file to create. + /// Defaults to `resource_access.rsp`. + /// + public string FileName + { + get + { + return this.Parameters.GetValue(nameof(this.FileName), "resource_access.rsp"); + } + } + + /// + /// Creates the response file when one or more `Option*` parameters are supplied. + /// + /// Context information provided to telemetry events. + /// Token that can be used to cancel the operation. + protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Preserve relative-path behavior, but allow callers to pass an absolute path too. + string filePath = Path.IsPathRooted(this.FileName) + ? this.FileName + : this.Combine(Environment.CurrentDirectory, this.FileName); + + // Ensure stable ordering of options (Option1, Option2, ...). Response file order can matter. + string[] optionKeys = this.Parameters.Keys + .Where(k => k.StartsWith("Option", StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + string content = string.Join(' ', optionKeys.Select(k => this.Parameters[k]).ToString().Trim()); + + // If all options were null/empty, do not create the file. + if (string.IsNullOrWhiteSpace(content)) + { + return; + } + + byte[] bytes = Encoding.UTF8.GetBytes(content); + + await using (FileSystemStream fileStream = this.fileSystem.FileStream.New( + filePath, + FileMode.Create, + FileAccess.Write, + FileShare.Read)) + { + await fileStream.WriteAsync(bytes, cancellationToken).ConfigureAwait(false); + await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false); + } + } + } +} \ No newline at end of file From ecaaeb1d5f915d8c77d441f842fb8708f04d37aa Mon Sep 17 00:00:00 2001 From: Nirjan Chapagain Date: Mon, 16 Feb 2026 12:40:29 -0800 Subject: [PATCH 2/6] Writing UT --- .../CreateResponseFileTests.cs | 125 ++++++++---------- .../CreateResourceFile.cs | 40 +++--- 2 files changed, 79 insertions(+), 86 deletions(-) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs index 2ff13fe232..3282ec5cd1 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs @@ -11,112 +11,99 @@ namespace VirtualClient.Dependencies.UnitTests using System.Text; using System.Threading; using System.Threading.Tasks; - using Microsoft.Extensions.DependencyInjection; + using Microsoft.CodeAnalysis.Options; + using Microsoft.Extensions.DependencyInjection.Extensions; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Moq; using NUnit.Framework; + using Org.BouncyCastle.Utilities; + using Polly.Retry; + using Renci.SshNet.Security; + using VirtualClient; + using VirtualClient.Common.Extensions; using VirtualClient.Common.Telemetry; + using static System.Net.WebRequestMethods; [TestFixture] [Category("Unit")] public class CreateResponseFileTests { - private string originalCurrentDirectory; private string tempDirectory; private MockFixture mockFixture; + private IFileSystem fileSystem; + private Mock fileStreamMock; [SetUp] public void SetUp() { - this.originalCurrentDirectory = Environment.CurrentDirectory; this.tempDirectory = Path.Combine(Path.GetTempPath(), "VirtualClient", "UnitTests", Guid.NewGuid().ToString("n")); Directory.CreateDirectory(this.tempDirectory); Environment.CurrentDirectory = this.tempDirectory; - this.mockFixture = new MockFixture(); - } - - [TearDown] - public void TearDown() - { - Environment.CurrentDirectory = this.originalCurrentDirectory; - try - { - if (Directory.Exists(this.tempDirectory)) - { - Directory.Delete(this.tempDirectory, recursive: true); - } - } - catch - { - // Best-effort cleanup for test runs. - } + this.mockFixture = new MockFixture(); + this.mockFixture.SetupMocks(true); + this.fileSystem = this.mockFixture.Dependencies.GetService(); + this.fileStreamMock = new Mock(); } [Test] - public async Task ExecuteAsync_DoesNotCreateFile_WhenNoOptionsAreSupplied() + public async Task ExecuteAsyncDoesNotCreateFileWhenNoOptionsAreSupplied() { - IServiceCollection services = new ServiceCollection(); - services.AddSingleton(new FileSystem()); - - var parameters = new Dictionary + this.mockFixture.Parameters = new Dictionary { ["FileName"] = "resource_access.rsp" }; - TestCreateResponseFile executor = new TestCreateResponseFile(this.mockFixture); - await executor.ExecuteAsync().ConfigureAwait(false); - - string expectedPath = Path.Combine(this.tempDirectory, "resource_access.rsp"); - Assert.False(File.Exists(expectedPath), "The response file should not be created when no options are supplied."); - } - - [Test] - public async Task ExecuteAsync_CreatesResponseFile_WithExpectedContent() - { - IServiceCollection services = new ServiceCollection(); - services.AddSingleton(new FileSystem()); - - var parameters = new Dictionary - { - ["FileName"] = "resource_access.rsp", - ["Option2"] = "--KeyVaultUri=\"https://crc-partner-vault.vault.azure.net\"", - ["Option1"] = "--System=\"Testing\"" - }; + var executor = new TestCreateResponseFile(this.mockFixture); - TestCreateResponseFile executor = new TestCreateResponseFile(this.mockFixture); await executor.ExecuteAsync().ConfigureAwait(false); string expectedPath = Path.Combine(this.tempDirectory, "resource_access.rsp"); - Assert.True(File.Exists(expectedPath), "The response file should be created when options are supplied."); + Assert.False(this.fileSystem.File.Exists(expectedPath), "The response file should not be created when no options are supplied."); - // Current implementation orders keys lexically (OrdinalIgnoreCase): Option1 then Option2. - string expectedContent = "--System=\"Testing\" --KeyVaultUri=\"https://crc-partner-vault.vault.azure.net\""; - string actualContent = await File.ReadAllTextAsync(expectedPath, Encoding.UTF8); - - Assert.AreEqual(expectedContent, actualContent); + this.mockFixture.FileSystem.Verify(x => x.File.Delete(It.IsAny()), Times.Never); } [Test] - public async Task ExecuteAsync_UsesAbsoluteFileName_WhenRootedPathProvided() + [TestCase("")] + [TestCase(" ")] + [TestCase(null)] + [TestCase("C:\\repos\\VirtualClient\\out\\bin\\Debug\\x64\\VirtualClient.Main\\net9.0\\test.rsp")] + [TestCase("/home/vmadmin/VirtualClient/out/bin/debug/x64/VirtualClient.Main/net9.0/test.rsp")] + [TestCase("test.rsp")] + [TestCase("test.txt")] + public async Task ExecuteAsyncCreatesResponseFileAsExpected(string inputFilePath) { - IServiceCollection services = new ServiceCollection(); - services.AddSingleton(new FileSystem()); + string expectedFilePath = string.IsNullOrWhiteSpace(inputFilePath) + ? this.mockFixture.Combine(Environment.CurrentDirectory, "resource_access.rsp") + : inputFilePath; - string absolutePath = Path.Combine(this.tempDirectory, "sub", "my.rsp"); - Directory.CreateDirectory(Path.GetDirectoryName(absolutePath)!); + this.mockFixture.Parameters["FileName"] = inputFilePath; + this.mockFixture.Parameters["Option2"] = "--KeyVaultUri=\"https://crc-partner-vault.vault.azure.net\""; + this.mockFixture.Parameters["Option1"] = "--System=\"Testing\""; - var parameters = new Dictionary - { - ["FileName"] = absolutePath, - ["Option1"] = "--System=\"Testing\"" - }; + string expectedContent = string.Join(' ', this.mockFixture.Parameters + .Where(p => p.Key.StartsWith("Option", StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value.ToString().Trim()) + .ToArray()); - TestCreateResponseFile executor = new TestCreateResponseFile(this.mockFixture); - await executor.ExecuteAsync().ConfigureAwait(false); + var executor = new TestCreateResponseFile(this.mockFixture); - Assert.True(File.Exists(absolutePath), "The response file should be created at the rooted path."); - string actualContent = await File.ReadAllTextAsync(absolutePath, Encoding.UTF8); - Assert.AreEqual("--System=\"Testing\"", actualContent); + Mock mockFileStream = new Mock(); + this.mockFixture.FileStream.Setup(f => f.New(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(mockFileStream.Object) + .Callback((string path, FileMode mode, FileAccess access, FileShare share) => + { + Assert.AreEqual(expectedFilePath, path); + Assert.IsTrue(mode == FileMode.Create); + Assert.IsTrue(access == FileAccess.ReadWrite); + Assert.IsTrue(share == FileShare.ReadWrite); + }); + + await executor.ExecuteAsync().ConfigureAwait(false); + byte[] bytes = Encoding.UTF8.GetBytes(expectedContent); + mockFileStream.Verify(x => x.WriteAsync(It.Is>(x => (x.Length == bytes.Length)), It.IsAny()), Times.Exactly(1)); } private class TestCreateResponseFile : CreateResponseFile @@ -126,9 +113,9 @@ public TestCreateResponseFile(MockFixture mockFixture) { } - public async Task ExecuteAsync() + public Task ExecuteAsync() { - await base.ExecuteAsync(EventContext.None, CancellationToken.None).ConfigureAwait(false); + return this.ExecuteAsync(EventContext.None, CancellationToken.None); } } } diff --git a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs index 95e46d7528..bb3a6e6443 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs @@ -72,29 +72,35 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel ? this.FileName : this.Combine(Environment.CurrentDirectory, this.FileName); - // Ensure stable ordering of options (Option1, Option2, ...). Response file order can matter. - string[] optionKeys = this.Parameters.Keys - .Where(k => k.StartsWith("Option", StringComparison.OrdinalIgnoreCase)) + string[] optionValues = this.Parameters + .Where(p => p.Key.StartsWith("Option", StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Value.ToString().Trim()) .ToArray(); - string content = string.Join(' ', optionKeys.Select(k => this.Parameters[k]).ToString().Trim()); + telemetryContext.AddContext(nameof(filePath), filePath); + telemetryContext.AddContext(nameof(optionValues), optionValues); - // If all options were null/empty, do not create the file. - if (string.IsNullOrWhiteSpace(content)) + if (optionValues.Length > 0) { - return; - } + if (this.fileSystem.File.Exists(filePath)) + { + this.fileSystem.File.Delete(filePath); + } - byte[] bytes = Encoding.UTF8.GetBytes(content); + string content = string.Join(' ', optionValues); + telemetryContext.AddContext(nameof(content), content); - await using (FileSystemStream fileStream = this.fileSystem.FileStream.New( - filePath, - FileMode.Create, - FileAccess.Write, - FileShare.Read)) - { - await fileStream.WriteAsync(bytes, cancellationToken).ConfigureAwait(false); - await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false); + byte[] bytes = Encoding.UTF8.GetBytes(content); + + await using (FileSystemStream fileStream = this.fileSystem.FileStream.New( + filePath, + FileMode.Create, + FileAccess.ReadWrite, + FileShare.ReadWrite)) + { + await fileStream.WriteAsync(bytes, cancellationToken).ConfigureAwait(false); + await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false); + } } } } From e7a0df64210d0c153b5ed876090775e6572d15c7 Mon Sep 17 00:00:00 2001 From: Nirjan Chapagain Date: Mon, 16 Feb 2026 12:58:07 -0800 Subject: [PATCH 3/6] Fixing ut --- .../CreateResponseFileTests.cs | 2 +- .../CreateResourceFile.cs | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs index 3282ec5cd1..6eb4ee839e 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs @@ -76,7 +76,7 @@ public async Task ExecuteAsyncDoesNotCreateFileWhenNoOptionsAreSupplied() public async Task ExecuteAsyncCreatesResponseFileAsExpected(string inputFilePath) { string expectedFilePath = string.IsNullOrWhiteSpace(inputFilePath) - ? this.mockFixture.Combine(Environment.CurrentDirectory, "resource_access.rsp") + ? "resource_access.rsp" : inputFilePath; this.mockFixture.Parameters["FileName"] = inputFilePath; diff --git a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs index bb3a6e6443..85e388db4f 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs @@ -54,7 +54,8 @@ public string FileName { get { - return this.Parameters.GetValue(nameof(this.FileName), "resource_access.rsp"); + string value = this.Parameters.GetValue(nameof(this.FileName), string.Empty); + return string.IsNullOrWhiteSpace(value) ? "resource_access.rsp" : value; } } @@ -65,26 +66,22 @@ public string FileName /// Token that can be used to cancel the operation. protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); + string a = this.FileName; - // Preserve relative-path behavior, but allow callers to pass an absolute path too. - string filePath = Path.IsPathRooted(this.FileName) - ? this.FileName - : this.Combine(Environment.CurrentDirectory, this.FileName); + cancellationToken.ThrowIfCancellationRequested(); string[] optionValues = this.Parameters .Where(p => p.Key.StartsWith("Option", StringComparison.OrdinalIgnoreCase)) .Select(x => x.Value.ToString().Trim()) .ToArray(); - telemetryContext.AddContext(nameof(filePath), filePath); telemetryContext.AddContext(nameof(optionValues), optionValues); if (optionValues.Length > 0) { - if (this.fileSystem.File.Exists(filePath)) + if (this.fileSystem.File.Exists(this.FileName)) { - this.fileSystem.File.Delete(filePath); + this.fileSystem.File.Delete(this.FileName); } string content = string.Join(' ', optionValues); @@ -93,7 +90,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel byte[] bytes = Encoding.UTF8.GetBytes(content); await using (FileSystemStream fileStream = this.fileSystem.FileStream.New( - filePath, + this.FileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) From b5d9f0ca0c98d43699f9f8877ab877e77e68d166 Mon Sep 17 00:00:00 2001 From: Nirjan Chapagain Date: Mon, 16 Feb 2026 15:16:08 -0800 Subject: [PATCH 4/6] minor fix --- .../CreateResponseFileTests.cs | 4 ++-- .../VirtualClient.Dependencies/CreateResourceFile.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs index 6eb4ee839e..ccf946e4d2 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs @@ -80,10 +80,10 @@ public async Task ExecuteAsyncCreatesResponseFileAsExpected(string inputFilePath : inputFilePath; this.mockFixture.Parameters["FileName"] = inputFilePath; - this.mockFixture.Parameters["Option2"] = "--KeyVaultUri=\"https://crc-partner-vault.vault.azure.net\""; + this.mockFixture.Parameters["Option2"] = "--KeyVaultUri=\"https://testing-ut-vault.vault.azure.net\""; this.mockFixture.Parameters["Option1"] = "--System=\"Testing\""; - string expectedContent = string.Join(' ', this.mockFixture.Parameters + string expectedContent = string.Join(Environment.NewLine, this.mockFixture.Parameters .Where(p => p.Key.StartsWith("Option", StringComparison.OrdinalIgnoreCase)) .Select(x => x.Value.ToString().Trim()) .ToArray()); diff --git a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs index 85e388db4f..18d0098795 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs @@ -84,7 +84,7 @@ protected override async Task ExecuteAsync(EventContext telemetryContext, Cancel this.fileSystem.File.Delete(this.FileName); } - string content = string.Join(' ', optionValues); + string content = string.Join(Environment.NewLine, optionValues); telemetryContext.AddContext(nameof(content), content); byte[] bytes = Encoding.UTF8.GetBytes(content); From 1818d403b8558ee4f39edbdde9329742234339f4 Mon Sep 17 00:00:00 2001 From: Nirjan Chapagain Date: Mon, 16 Feb 2026 15:22:10 -0800 Subject: [PATCH 5/6] Adding doc --- .../CreateResponseFileTests.cs | 2 +- .../VirtualClient.Dependencies/CreateResourceFile.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs index ccf946e4d2..4a3c2d9175 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs @@ -80,7 +80,7 @@ public async Task ExecuteAsyncCreatesResponseFileAsExpected(string inputFilePath : inputFilePath; this.mockFixture.Parameters["FileName"] = inputFilePath; - this.mockFixture.Parameters["Option2"] = "--KeyVaultUri=\"https://testing-ut-vault.vault.azure.net\""; + this.mockFixture.Parameters["Option2"] = "--KeyVaultUri=\"https://testing123-vault.vault.azure.net\""; this.mockFixture.Parameters["Option1"] = "--System=\"Testing\""; string expectedContent = string.Join(Environment.NewLine, this.mockFixture.Parameters diff --git a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs index 18d0098795..979b61d5c9 100644 --- a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs +++ b/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs @@ -26,9 +26,10 @@ namespace VirtualClient.Dependencies /// /// Options are provided via parameters whose keys start with Option (case-insensitive), for example: /// Option1=--System="Testing" - /// Option2=--KeyVaultUri="https://crc-partner-vault.vault.azure.net" + /// Option2=--KeyVaultUri="https://testing123-vault.vault.azure.net" /// /// The file is written to unless is an absolute path. + /// Doc: https://natemcmaster.github.io/CommandLineUtils/docs/response-file-parsing.html?tabs=using-attributes /// public class CreateResponseFile : VirtualClientComponent { From d0881849a1cb4beff57f52e0034c30c50cb421e3 Mon Sep 17 00:00:00 2001 From: Nirjan Chapagain Date: Mon, 16 Feb 2026 15:49:54 -0800 Subject: [PATCH 6/6] fixing naming issue --- .../CreateResponseFileTests.cs | 6 ------ .../{CreateResourceFile.cs => CreateResponseFile.cs} | 0 2 files changed, 6 deletions(-) rename src/VirtualClient/VirtualClient.Dependencies/{CreateResourceFile.cs => CreateResponseFile.cs} (100%) diff --git a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs index 4a3c2d9175..b8fe0df190 100644 --- a/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs +++ b/src/VirtualClient/VirtualClient.Dependencies.UnitTests/CreateResponseFileTests.cs @@ -11,18 +11,12 @@ namespace VirtualClient.Dependencies.UnitTests using System.Text; using System.Threading; using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Options; - using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Moq; using NUnit.Framework; - using Org.BouncyCastle.Utilities; - using Polly.Retry; - using Renci.SshNet.Security; using VirtualClient; using VirtualClient.Common.Extensions; using VirtualClient.Common.Telemetry; - using static System.Net.WebRequestMethods; [TestFixture] [Category("Unit")] diff --git a/src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs b/src/VirtualClient/VirtualClient.Dependencies/CreateResponseFile.cs similarity index 100% rename from src/VirtualClient/VirtualClient.Dependencies/CreateResourceFile.cs rename to src/VirtualClient/VirtualClient.Dependencies/CreateResponseFile.cs