| File: NativeRedisAppHostTests.cs | Web Access |
| Project: src\tests\Aspire.Hosting.Native.Core.Tests\Aspire.Hosting.Native.Core.Tests.csproj (Aspire.Hosting.Native.Core.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.Diagnostics; using Aspire.Hosting.Native.Core.Tests.TestServices; namespace Aspire.Hosting.Native.Core.Tests; public class NativeRedisAppHostTests { [Theory] [InlineData("CSHARP", false)] [InlineData("TYPESCRIPT", false)] [InlineData("CSHARP", true)] [InlineData("TYPESCRIPT", true)] public async Task RealRedisIntegrationUsesDeclaredWorkloadsAuthenticatedHealthAndRetainedVolume(string language, bool revisions) { var server = Environment.GetEnvironmentVariable("ASPIRE_NATIVE_SYMBOLIC_TEST_SERVER"); var dcp = Environment.GetEnvironmentVariable("ASPIRE_NATIVE_SYMBOLIC_TEST_DCP"); var apphost = Environment.GetEnvironmentVariable($"ASPIRE_NATIVE_REDIS_{language}_{(revisions ? "VALIDATION" : "APPHOST")}"); var provider = Environment.GetEnvironmentVariable($"ASPIRE_NATIVE_REDIS_{language}_PROVIDER"); var node = Environment.GetEnvironmentVariable("ASPIRE_NATIVE_SYMBOLIC_TEST_NODE"); if (server is null || dcp is null || apphost is null || provider is null || language == "TYPESCRIPT" && node is null) { Assert.Skip("The dedicated native lane supplies actual Redis integration AppHosts/providers, AOT, DCP and Node."); } var prefix = $"native-redis-authoring-{Guid.NewGuid():N}"; var volume = $"{prefix}-cache-data"; try { await using var host = await NativeClientTestHost.StartWithWorkloadsAsync(server, dcp); var executable = language == "CSHARP" ? apphost : node!; var configuration = new[] { $"--native-endpoint={host.Endpoint}", $"--native-provider={(language == "CSHARP" ? "example.csharp.redis" : "example.ts.redis")}={(language == "CSHARP" ? provider : $"{node}|{provider}")}", $"--native-volume-prefix={prefix}", "--native-exit-on-completion=consumer", $"--consumer-path={provider}", $"--data-volume={volume}" }; var arguments = language == "CSHARP" ? configuration : new[] { apphost }.Concat(configuration).ToArray(); var output = await NativeIntegrationAppHostRunner.RunAsync(executable, arguments, host.Token, TimeProvider.System); Assert.Equal(revisions ? "redis-persistence-ok" : "", output.Trim()); // The provider's retiring ATS callback requests authenticated SAVE // through its native phase scope. AOF alone never creates dump.rdb; // this proves the authored callback ran after public handle revocation. var verify = new ProcessStartInfo("docker") { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, ArgumentList = { "run", "--rm", "--volume", $"{volume}:/data:ro", "--entrypoint", "/bin/sh", "redis:7.4-alpine", "-c", "test -s /data/dump.rdb" } }; using var snapshot = Process.Start(verify)!; var snapshotOutput = snapshot.StandardOutput.ReadToEndAsync(); var snapshotError = snapshot.StandardError.ReadToEndAsync(); await snapshot.WaitForExitAsync().WaitAsync(TimeSpan.FromSeconds(30), TimeProvider.System); await Task.WhenAll(snapshotOutput, snapshotError); Assert.Equal(0, snapshot.ExitCode); } finally { // Only this scenario's retained named volume is removed. Integration // retirement intentionally preserves it across application generations. var start = new ProcessStartInfo("docker") { RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false }; start.ArgumentList.Add("volume"); start.ArgumentList.Add("rm"); start.ArgumentList.Add(volume); using var cleanup = Process.Start(start)!; var output = cleanup.StandardOutput.ReadToEndAsync(); var error = cleanup.StandardError.ReadToEndAsync(); await cleanup.WaitForExitAsync().WaitAsync(TimeSpan.FromSeconds(15), TimeProvider.System); await Task.WhenAll(output, error); Assert.Equal(0, cleanup.ExitCode); } } }