/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Tools/dotnet-user-secrets/test/SecretManagerTests.cs
399 строк
16 KB
Damian Edwards
Address file-based app user secrets feedback (#67010)
04 июн 2026, 22:33
Не верифицирован
04 июн 2026, 22:33
6865ecc
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using Microsoft.AspNetCore.InternalTesting; using Microsoft.AspNetCore.Tools; using Microsoft.Extensions.Configuration.UserSecrets; using Microsoft.Extensions.Configuration.UserSecrets.Tests; using Microsoft.Extensions.Tools.Internal; using Xunit; using Xunit.Abstractions; namespace Microsoft.Extensions.SecretManager.Tools.Tests; public class SecretManagerTests : IClassFixture<UserSecretsTestFixture> { private readonly TestConsole _console; private readonly UserSecretsTestFixture _fixture; private readonly ITestOutputHelper _testOut; public SecretManagerTests(UserSecretsTestFixture fixture, ITestOutputHelper output) { _fixture = fixture; _testOut = output; _console = new TestConsole(output); } private Program CreateProgram() { return new Program(_console, Directory.GetCurrentDirectory()); } [Theory] [InlineData(null)] [InlineData("")] public void Error_MissingId(string id) { var project = Path.Combine(_fixture.CreateProject(id), "TestProject.csproj"); var secretManager = CreateProgram(); secretManager.RunInternal("list", "-p", project, "--verbose"); Assert.Contains(Resources.FormatError_ProjectMissingId(project), _console.GetOutput()); } [Fact] public void Error_InvalidProjectFormat() { var project = Path.Combine(_fixture.CreateProject("<"), "TestProject.csproj"); var secretManager = CreateProgram(); secretManager.RunInternal("list", "-p", project); Assert.Contains(Resources.FormatError_ProjectFailedToLoad(project), _console.GetOutput()); } [Fact] public void Error_Project_DoesNotExist() { var projectPath = Path.Combine(_fixture.GetTempSecretProject(), "does_not_exist", "TestProject.csproj"); var secretManager = CreateProgram(); secretManager.RunInternal("list", "--project", projectPath); Assert.Contains(SecretsHelpersResources.FormatError_File_NotFound(projectPath), _console.GetOutput()); } [Fact] public void Error_ProjectAndFileOptions() { var projectPath = Path.Combine(_fixture.GetTempSecretProject(), "does_not_exist", "TestProject.csproj"); var secretManager = CreateProgram(); secretManager.RunInternal("list", "--project", projectPath, "--file", projectPath); Assert.Contains(Resources.Error_ProjectAndFileOptions, _console.GetOutput()); } [Fact] public void Error_File_DoesNotExist() { var filePath = Path.Combine(_fixture.GetTempSecretProject(), "does_not_exist.cs"); var secretManager = CreateProgram(); secretManager.RunInternal("list", "--file", filePath); Assert.Contains(SecretsHelpersResources.FormatError_File_NotFound(filePath), _console.GetOutput()); } [Fact] public void Error_File_InvalidExtension() { var filePath = Path.Combine(_fixture.GetTempSecretProject(), "foo.txt"); File.WriteAllText(filePath, string.Empty); var secretManager = CreateProgram(); secretManager.RunInternal("list", "--file", filePath); var output = _console.GetOutput(); Assert.Contains(Resources.Error_FileInvalidExtension, output); Assert.DoesNotContain(SecretsHelpersResources.FormatError_ProjectFailedToLoad(filePath), output); } [Fact] public void Error_InitFile() { var dir = _fixture.CreateFileBasedApp(null); var secretManager = CreateProgram(); secretManager.RunInternal("init", "--file", Path.Combine(dir, "app.cs")); Assert.Contains(Resources.Error_InitNotSupportedForFileBasedApps, _console.GetOutput()); } [Fact] public void SupportsRelativePaths() { var projectPath = _fixture.GetTempSecretProject(); var cwd = Path.Combine(projectPath, "nested1"); Directory.CreateDirectory(cwd); var secretManager = new Program(_console, cwd); secretManager.RunInternal("list", "-p", ".." + Path.DirectorySeparatorChar, "--verbose"); Assert.Contains(Resources.FormatMessage_Project_File_Path(Path.Combine(cwd, "..", "TestProject.csproj")), _console.GetOutput()); } [Theory] [InlineData(false, true)] [InlineData(false, false)] [InlineData(true, false)] public void SetSecrets(bool fromCurrentDirectory, bool fileBasedApp) { var secrets = new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("key1", Guid.NewGuid().ToString()), new KeyValuePair<string, string>("Facebook:AppId", Guid.NewGuid().ToString()), new KeyValuePair<string, string>(@"key-@\/.~123!#$%^&*())-+==", @"key-@\/.~123!#$%^&*())-+=="), new KeyValuePair<string, string>("key2", string.Empty), new KeyValuePair<string, string>("-oneDashedKey", "-oneDashedValue"), new KeyValuePair<string, string>("--twoDashedKey", "--twoDashedValue") }; var projectPath = fileBasedApp ? _fixture.GetTempFileBasedApp(out _) : _fixture.GetTempSecretProject(); var dir = fromCurrentDirectory ? projectPath : Path.GetTempPath(); ReadOnlySpan<string> pathArgs = fromCurrentDirectory ? [] : (fileBasedApp ? ["-f", Path.Join(projectPath, "app.cs")] : ["-p", projectPath]); var secretManager = new Program(_console, dir); foreach (var secret in secrets) { secretManager.RunInternal(["set", secret.Key, secret.Value, .. pathArgs, "--verbose"]); } foreach (var keyValue in secrets) { Assert.Contains( string.Format(CultureInfo.InvariantCulture, "Successfully saved {0} to the secret store.", keyValue.Key), _console.GetOutput()); } _console.ClearOutput(); secretManager.RunInternal(["list", .. pathArgs, "--verbose"]); foreach (var keyValue in secrets) { Assert.Contains( string.Format(CultureInfo.InvariantCulture, "{0} = {1}", keyValue.Key, keyValue.Value), _console.GetOutput()); } // Remove secrets. _console.ClearOutput(); foreach (var secret in secrets) { secretManager.RunInternal(["remove", secret.Key, .. pathArgs, "--verbose"]); } // Verify secrets are removed. _console.ClearOutput(); secretManager.RunInternal(["list", .. pathArgs, "--verbose"]); Assert.Contains(Resources.Error_No_Secrets_Found, _console.GetOutput()); } [Fact] public void SetSecrets_FileBasedAppInCurrentDirectory() { var directoryPath = _fixture.GetTempFileBasedApp(out _); var secretManager = new Program(_console, directoryPath); secretManager.RunInternal("set", "key1", "value1", "--verbose"); Assert.Contains(SecretsHelpersResources.FormatError_NoProjectsFound(directoryPath), _console.GetOutput()); } [Fact] public void SetSecret_Update_Existing_Secret() { var projectPath = _fixture.GetTempSecretProject(); var secretManager = CreateProgram(); secretManager.RunInternal("set", "secret1", "value1", "-p", projectPath, "--verbose"); Assert.Contains("Successfully saved secret1 to the secret store.", _console.GetOutput()); secretManager.RunInternal("set", "secret1", "value2", "-p", projectPath, "--verbose"); Assert.Contains("Successfully saved secret1 to the secret store.", _console.GetOutput()); _console.ClearOutput(); secretManager.RunInternal("list", "-p", projectPath, "--verbose"); Assert.Contains("secret1 = value2", _console.GetOutput()); } [Fact] public void SetSecret_With_Verbose_Flag() { string secretId; var projectPath = _fixture.GetTempSecretProject(out secretId); var secretManager = CreateProgram(); secretManager.RunInternal("-v", "set", "secret1", "value1", "-p", projectPath); Assert.Contains(string.Format(CultureInfo.InvariantCulture, "Project file path {0}.", Path.Combine(projectPath, "TestProject.csproj")), _console.GetOutput()); Assert.Contains(string.Format(CultureInfo.InvariantCulture, "Secrets file path {0}.", PathHelper.GetSecretsPathFromSecretsId(secretId)), _console.GetOutput()); Assert.Contains("Successfully saved secret1 to the secret store.", _console.GetOutput()); _console.ClearOutput(); secretManager.RunInternal("-v", "list", "-p", projectPath); Assert.Contains(string.Format(CultureInfo.InvariantCulture, "Project file path {0}.", Path.Combine(projectPath, "TestProject.csproj")), _console.GetOutput()); Assert.Contains(string.Format(CultureInfo.InvariantCulture, "Secrets file path {0}.", PathHelper.GetSecretsPathFromSecretsId(secretId)), _console.GetOutput()); Assert.Contains("secret1 = value1", _console.GetOutput()); } [Fact] public void Remove_Non_Existing_Secret() { var projectPath = _fixture.GetTempSecretProject(); var secretManager = CreateProgram(); secretManager.RunInternal("remove", "secret1", "-p", projectPath, "--verbose"); Assert.Contains("Cannot find 'secret1' in the secret store.", _console.GetOutput()); } [Fact] public void Remove_Is_Case_Insensitive() { var projectPath = _fixture.GetTempSecretProject(); var secretManager = CreateProgram(); secretManager.RunInternal("set", "SeCreT1", "value", "-p", projectPath, "--verbose"); secretManager.RunInternal("list", "-p", projectPath, "--verbose"); Assert.Contains("SeCreT1 = value", _console.GetOutput()); secretManager.RunInternal("remove", "secret1", "-p", projectPath, "--verbose"); _console.ClearOutput(); secretManager.RunInternal("list", "-p", projectPath, "--verbose"); Assert.Contains(Resources.Error_No_Secrets_Found, _console.GetOutput()); } [Fact] public void List_Json_OptionIsWired() { string id; var projectPath = _fixture.GetTempSecretProject(out id); var secretManager = CreateProgram(); secretManager.RunInternal("set", "key1", "value1", "-p", projectPath); _console.ClearOutput(); secretManager.RunInternal("list", "--id", id, "--json"); var stdout = _console.GetOutput(); Assert.Contains("//BEGIN", stdout); Assert.Contains("\"key1\": \"value1\"", stdout); Assert.Contains("//END", stdout); } [Fact] public void List_Flattens_Nested_Objects() { string secretId; var projectPath = _fixture.GetTempSecretProject(out secretId); var secretsFile = PathHelper.GetSecretsPathFromSecretsId(secretId); Directory.CreateDirectory(Path.GetDirectoryName(secretsFile)); File.WriteAllText(secretsFile, @"{ ""AzureAd"": { ""ClientSecret"": ""abc"" } }", Encoding.UTF8); var secretManager = CreateProgram(); secretManager.RunInternal("list", "-p", projectPath); Assert.Contains("AzureAd:ClientSecret = abc", _console.GetOutput()); } [Fact] public void Set_Flattens_Nested_Objects() { string secretId; var projectPath = _fixture.GetTempSecretProject(out secretId); var secretsFile = PathHelper.GetSecretsPathFromSecretsId(secretId); Directory.CreateDirectory(Path.GetDirectoryName(secretsFile)); File.WriteAllText(secretsFile, @"{ ""AzureAd"": { ""ClientSecret"": ""abcd郩˙î""} }", Encoding.UTF8); var secretManager = CreateProgram(); secretManager.RunInternal("set", "AzureAd:ClientSecret", "¡™£¢∞", "-p", projectPath); secretManager.RunInternal("list", "-p", projectPath); Assert.Contains("AzureAd:ClientSecret = ¡™£¢∞", _console.GetOutput()); var fileContents = File.ReadAllText(secretsFile, Encoding.UTF8); Assert.Equal(@"{ ""AzureAd:ClientSecret"": ""\u00A1\u2122\u00A3\u00A2\u221E"" }", fileContents, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true); } [Theory] [InlineData(false, true)] [InlineData(false, false)] [InlineData(true, false)] public void Clear_Secrets(bool fromCurrentDirectory, bool fileBasedApp) { var projectPath = fileBasedApp ? _fixture.GetTempFileBasedApp(out _) : _fixture.GetTempSecretProject(); var dir = fromCurrentDirectory ? projectPath : Path.GetTempPath(); ReadOnlySpan<string> pathArgs = fromCurrentDirectory ? [] : (fileBasedApp ? ["-f", Path.Join(projectPath, "app.cs")] : ["-p", projectPath]); var secretManager = new Program(_console, dir); var secrets = new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("key1", Guid.NewGuid().ToString()), new KeyValuePair<string, string>("Facebook:AppId", Guid.NewGuid().ToString()), new KeyValuePair<string, string>(@"key-@\/.~123!#$%^&*())-+==", @"key-@\/.~123!#$%^&*())-+=="), new KeyValuePair<string, string>("key2", string.Empty) }; foreach (var secret in secrets) { secretManager.RunInternal(["set", secret.Key, secret.Value, .. pathArgs, "--verbose"]); } foreach (var keyValue in secrets) { Assert.Contains( string.Format(CultureInfo.InvariantCulture, "Successfully saved {0} to the secret store.", keyValue.Key), _console.GetOutput()); } // Verify secrets are persisted. _console.ClearOutput(); secretManager.RunInternal(["list", .. pathArgs, "--verbose"]); foreach (var keyValue in secrets) { Assert.Contains( string.Format(CultureInfo.InvariantCulture, "{0} = {1}", keyValue.Key, keyValue.Value), _console.GetOutput()); } // Clear secrets. _console.ClearOutput(); secretManager.RunInternal(["clear", .. pathArgs, "--verbose"]); secretManager.RunInternal(["list", .. pathArgs, "--verbose"]); Assert.Contains(Resources.Error_No_Secrets_Found, _console.GetOutput()); } [Fact] public void Init_When_Project_Has_No_Secrets_Id() { var projectPath = _fixture.CreateProject(null); var project = Path.Combine(projectPath, "TestProject.csproj"); var secretManager = new Program(_console, projectPath); secretManager.RunInternal("init", "-p", project); Assert.DoesNotContain(Resources.FormatError_ProjectMissingId(project), _console.GetOutput()); Assert.DoesNotContain("--help", _console.GetOutput()); } [ConditionalFact] [OSSkipCondition(OperatingSystems.Windows, SkipReason = "UnixFileMode is not supported on Windows.")] public void SetSecrets_CreatesFileWithUserOnlyUnixFileMode() { var projectPath = _fixture.GetTempSecretProject(); var secretManager = new Program(_console, projectPath); secretManager.RunInternal("set", "key1", Guid.NewGuid().ToString(), "--verbose"); Assert.NotNull(secretManager.SecretsFilePath); Assert.Equal(UnixFileMode.UserRead | UnixFileMode.UserWrite, File.GetUnixFileMode(secretManager.SecretsFilePath)); } }