| File: Certificates\CertificateServiceTests.cs | Web Access |
| Project: src\tests\Aspire.Cli.Tests\Aspire.Cli.Tests.csproj (Aspire.Cli.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.InteropServices; using Aspire.Cli.Certificates; using Aspire.Cli.Tests.Utils; using Microsoft.AspNetCore.Certificates.Generation; using Microsoft.AspNetCore.InternalTesting; using Microsoft.Extensions.DependencyInjection; namespace Aspire.Cli.Tests.Certificates; public class CertificateServiceTests(ITestOutputHelper outputHelper) { [Fact] public async Task EnsureCertificatesTrustedAsync_WithFullyTrustedCert_ReturnsEmptyEnvVars() { using var workspace = TemporaryWorkspace.Create(outputHelper); var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => { options.CertificateToolRunnerFactory = sp => { return new TestCertificateToolRunner { CheckHttpCertificateCallback = () => { return new CertificateTrustResult { HasCertificates = true, TrustLevel = CertificateManager.TrustLevel.Full, Certificates = [new DevCertInfo { Version = 5, TrustLevel = CertificateManager.TrustLevel.Full, IsHttpsDevelopmentCertificate = true, ValidityNotBefore = DateTimeOffset.Now.AddDays(-1), ValidityNotAfter = DateTimeOffset.Now.AddDays(365) }] }; } }; }; }); var sp = services.BuildServiceProvider(); var cs = sp.GetRequiredService<ICertificateService>(); var result = await cs.EnsureCertificatesTrustedAsync(TestContext.Current.CancellationToken).DefaultTimeout(); Assert.NotNull(result); Assert.Empty(result.EnvironmentVariables); } [Fact] public async Task EnsureCertificatesTrustedAsync_WithNotTrustedCert_RunsTrustOperation() { using var workspace = TemporaryWorkspace.Create(outputHelper); var trustCalled = false; var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => { options.CertificateToolRunnerFactory = sp => { var callCount = 0; return new TestCertificateToolRunner { CheckHttpCertificateCallback = () => { callCount++; // First call returns not trusted, second call (after trust) returns fully trusted if (callCount == 1) { return new CertificateTrustResult { HasCertificates = true, TrustLevel = CertificateManager.TrustLevel.None, Certificates = [new DevCertInfo { Version = 5, TrustLevel = CertificateManager.TrustLevel.None, IsHttpsDevelopmentCertificate = true, ValidityNotBefore = DateTimeOffset.Now.AddDays(-1), ValidityNotAfter = DateTimeOffset.Now.AddDays(365) }] }; } return new CertificateTrustResult { HasCertificates = true, TrustLevel = CertificateManager.TrustLevel.Full, Certificates = [new DevCertInfo { Version = 5, TrustLevel = CertificateManager.TrustLevel.Full, IsHttpsDevelopmentCertificate = true, ValidityNotBefore = DateTimeOffset.Now.AddDays(-1), ValidityNotAfter = DateTimeOffset.Now.AddDays(365) }] }; }, TrustHttpCertificateCallback = () => { trustCalled = true; return EnsureCertificateResult.ExistingHttpsCertificateTrusted; } }; }; }); var sp = services.BuildServiceProvider(); var cs = sp.GetRequiredService<ICertificateService>(); var result = await cs.EnsureCertificatesTrustedAsync(TestContext.Current.CancellationToken).DefaultTimeout(); Assert.True(trustCalled); Assert.NotNull(result); } [Fact] public async Task EnsureCertificatesTrustedAsync_WithPartiallyTrustedCert_SetsSslCertDirOnLinux() { // Skip this test on non-Linux platforms if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { return; } using var workspace = TemporaryWorkspace.Create(outputHelper); var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => { options.CertificateToolRunnerFactory = sp => { return new TestCertificateToolRunner { CheckHttpCertificateCallback = () => { return new CertificateTrustResult { HasCertificates = true, TrustLevel = CertificateManager.TrustLevel.Partial, Certificates = [new DevCertInfo { Version = 5, TrustLevel = CertificateManager.TrustLevel.Partial, IsHttpsDevelopmentCertificate = true, ValidityNotBefore = DateTimeOffset.Now.AddDays(-1), ValidityNotAfter = DateTimeOffset.Now.AddDays(365) }] }; } }; }; }); var sp = services.BuildServiceProvider(); var cs = sp.GetRequiredService<ICertificateService>(); var result = await cs.EnsureCertificatesTrustedAsync(TestContext.Current.CancellationToken).DefaultTimeout(); Assert.NotNull(result); Assert.True(result.EnvironmentVariables.ContainsKey("SSL_CERT_DIR")); Assert.Contains(".aspnet/dev-certs/trust", result.EnvironmentVariables["SSL_CERT_DIR"]); } [Fact] public async Task EnsureCertificatesTrustedAsync_WithNoCertificates_RunsTrustOperation() { using var workspace = TemporaryWorkspace.Create(outputHelper); var trustCalled = false; var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => { options.CertificateToolRunnerFactory = sp => { var callCount = 0; return new TestCertificateToolRunner { CheckHttpCertificateCallback = () => { callCount++; // First call returns no certificates, second call (after trust) returns fully trusted if (callCount == 1) { return new CertificateTrustResult { HasCertificates = false, TrustLevel = null, Certificates = [] }; } return new CertificateTrustResult { HasCertificates = true, TrustLevel = CertificateManager.TrustLevel.Full, Certificates = [new DevCertInfo { Version = 5, TrustLevel = CertificateManager.TrustLevel.Full, IsHttpsDevelopmentCertificate = true, ValidityNotBefore = DateTimeOffset.Now.AddDays(-1), ValidityNotAfter = DateTimeOffset.Now.AddDays(365) }] }; }, TrustHttpCertificateCallback = () => { trustCalled = true; return EnsureCertificateResult.NewHttpsCertificateTrusted; } }; }; }); var sp = services.BuildServiceProvider(); var cs = sp.GetRequiredService<ICertificateService>(); var result = await cs.EnsureCertificatesTrustedAsync(TestContext.Current.CancellationToken).DefaultTimeout(); Assert.True(trustCalled); Assert.NotNull(result); } [Fact] public async Task EnsureCertificatesTrustedAsync_TrustOperationFails_DisplaysWarning() { using var workspace = TemporaryWorkspace.Create(outputHelper); var services = CliTestHelper.CreateServiceCollection(workspace, outputHelper, options => { options.CertificateToolRunnerFactory = sp => { return new TestCertificateToolRunner { CheckHttpCertificateCallback = () => { return new CertificateTrustResult { HasCertificates = true, TrustLevel = CertificateManager.TrustLevel.None, Certificates = [new DevCertInfo { Version = 5, TrustLevel = CertificateManager.TrustLevel.None, IsHttpsDevelopmentCertificate = true, ValidityNotBefore = DateTimeOffset.Now.AddDays(-1), ValidityNotAfter = DateTimeOffset.Now.AddDays(365) }] }; }, TrustHttpCertificateCallback = () => { return EnsureCertificateResult.FailedToTrustTheCertificate; } }; }; }); var sp = services.BuildServiceProvider(); var cs = sp.GetRequiredService<ICertificateService>(); // If this does not throw then the code is behaving correctly. var result = await cs.EnsureCertificatesTrustedAsync(TestContext.Current.CancellationToken).DefaultTimeout(); Assert.NotNull(result); } private sealed class TestCertificateToolRunner : ICertificateToolRunner { public Func<CertificateTrustResult>? CheckHttpCertificateCallback { get; set; } public Func<EnsureCertificateResult>? TrustHttpCertificateCallback { get; set; } public CertificateTrustResult CheckHttpCertificate() { if (CheckHttpCertificateCallback is not null) { return CheckHttpCertificateCallback(); } // Default: Return a fully trusted certificate result return new CertificateTrustResult { HasCertificates = true, TrustLevel = CertificateManager.TrustLevel.Full, Certificates = [] }; } public EnsureCertificateResult TrustHttpCertificate() { return TrustHttpCertificateCallback is not null ? TrustHttpCertificateCallback() : EnsureCertificateResult.ExistingHttpsCertificateTrusted; } public CertificateCleanResult CleanHttpCertificate() => new CertificateCleanResult { Success = true }; } }