From ced70f63a0be48d4b506e55210d1c9cf92820690 Mon Sep 17 00:00:00 2001 From: ZLoo Date: Mon, 29 Jul 2024 08:03:27 +0300 Subject: [PATCH] Fix CA1062 (#5048) --- .../RedisBuilderExtensions.cs | 20 +- .../RedisCommanderConfigWriterHook.cs | 2 + .../RedisPublicApiTests.cs | 188 ++++++++++++++++++ 3 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 tests/Aspire.Hosting.Redis.Tests/RedisPublicApiTests.cs diff --git a/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs b/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs index c521cfc706..7e1d0bfb73 100644 --- a/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs +++ b/src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs @@ -26,6 +26,8 @@ public static class RedisBuilderExtensions /// A reference to the . public static IResourceBuilder AddRedis(this IDistributedApplicationBuilder builder, string name, int? port = null) { + ArgumentNullException.ThrowIfNull(builder); + var redis = new RedisResource(name); return builder.AddResource(redis) .WithEndpoint(port: port, targetPort: 6379, name: RedisResource.PrimaryEndpointName) @@ -42,6 +44,8 @@ public static IResourceBuilder AddRedis(this IDistributedApplicat /// public static IResourceBuilder WithRedisCommander(this IResourceBuilder builder, Action>? configureContainer = null, string? containerName = null) { + ArgumentNullException.ThrowIfNull(builder); + if (builder.ApplicationBuilder.Resources.OfType().SingleOrDefault() is { } existingRedisCommanderResource) { var builderForExistingResource = builder.ApplicationBuilder.CreateResourceBuilder(existingRedisCommanderResource); @@ -74,6 +78,8 @@ public static IResourceBuilder WithRedisCommander(this IResourceB /// The resource builder for PGAdmin. public static IResourceBuilder WithHostPort(this IResourceBuilder builder, int? port) { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithEndpoint("http", endpoint => { endpoint.Port = port; @@ -100,6 +106,8 @@ public static IResourceBuilder WithHostPort(this IResour /// The . public static IResourceBuilder WithDataVolume(this IResourceBuilder builder, string? name = null, bool isReadOnly = false) { + ArgumentNullException.ThrowIfNull(builder); + builder.WithVolume(name ?? VolumeNameGenerator.CreateVolumeName(builder, "data"), "/data", isReadOnly); if (!isReadOnly) { @@ -128,6 +136,9 @@ public static IResourceBuilder WithDataVolume(this IResourceBuild /// The . public static IResourceBuilder WithDataBindMount(this IResourceBuilder builder, string source, bool isReadOnly = false) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(source); + builder.WithBindMount(source, "/data", isReadOnly); if (!isReadOnly) { @@ -153,11 +164,16 @@ public static IResourceBuilder WithDataBindMount(this IResourceBu /// The number of key change operations required to trigger a snapshot at the interval. Defaults to 1. /// The . public static IResourceBuilder WithPersistence(this IResourceBuilder builder, TimeSpan? interval = null, long keysChangedThreshold = 1) - => builder.WithAnnotation(new CommandLineArgsCallbackAnnotation(context => + { + ArgumentNullException.ThrowIfNull(builder); + + return builder.WithAnnotation(new CommandLineArgsCallbackAnnotation(context => { context.Args.Add("--save"); - context.Args.Add((interval ?? TimeSpan.FromSeconds(60)).TotalSeconds.ToString(CultureInfo.InvariantCulture)); + context.Args.Add( + (interval ?? TimeSpan.FromSeconds(60)).TotalSeconds.ToString(CultureInfo.InvariantCulture)); context.Args.Add(keysChangedThreshold.ToString(CultureInfo.InvariantCulture)); return Task.CompletedTask; }), ResourceAnnotationMutationBehavior.Replace); + } } diff --git a/src/Aspire.Hosting.Redis/RedisCommanderConfigWriterHook.cs b/src/Aspire.Hosting.Redis/RedisCommanderConfigWriterHook.cs index 1e284ef655..4fe062a4f1 100644 --- a/src/Aspire.Hosting.Redis/RedisCommanderConfigWriterHook.cs +++ b/src/Aspire.Hosting.Redis/RedisCommanderConfigWriterHook.cs @@ -11,6 +11,8 @@ internal sealed class RedisCommanderConfigWriterHook : IDistributedApplicationLi { public Task AfterEndpointsAllocatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken) { + ArgumentNullException.ThrowIfNull(appModel); + if (appModel.Resources.OfType().SingleOrDefault() is not { } commanderResource) { // No-op if there is no commander resource (removed after hook added). diff --git a/tests/Aspire.Hosting.Redis.Tests/RedisPublicApiTests.cs b/tests/Aspire.Hosting.Redis.Tests/RedisPublicApiTests.cs new file mode 100644 index 0000000000..2fb00c695d --- /dev/null +++ b/tests/Aspire.Hosting.Redis.Tests/RedisPublicApiTests.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Aspire.Hosting.ApplicationModel; +using Xunit; + +namespace Aspire.Hosting.Redis.Tests; + +public class RedisPublicApiTests +{ + #region RedisBuilderExtensions + + [Fact] + public void AddRedisContainerShouldThrowsWhenBuilderIsNull() + { + IDistributedApplicationBuilder builder = null!; + const string name = "Redis"; + + var action = () => builder.AddRedis(name); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(builder), exception.ParamName); + }); + } + + [Fact] + public void AddRedisContainerShouldThrowsWhenNameIsNull() + { + IDistributedApplicationBuilder builder = new DistributedApplicationBuilder([]); + string name = null!; + + var action = () => builder.AddRedis(name); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(name), exception.ParamName); + }); + } + + [Fact] + public void WithRedisCommanderShouldThrowsWhenBuilderIsNull() + { + IResourceBuilder builder = null!; + + var action = () => builder.WithRedisCommander(); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(builder), exception.ParamName); + }); + } + + [Fact] + public void WithHostPortShouldThrowsWhenBuilderIsNull() + { + IResourceBuilder builder = null!; + const int port = 777; + + var action = () => builder.WithHostPort(port); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(builder), exception.ParamName); + }); + } + + [Fact] + public void WithDataVolumeShouldThrowsWhenBuilderIsNull() + { + IResourceBuilder builder = null!; + + var action = () => builder.WithDataVolume(); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(builder), exception.ParamName); + }); + } + + [Fact] + public void WithDataBindMountShouldThrowsWhenBuilderIsNull() + { + IResourceBuilder builder = null!; + const string source = "/data"; + + var action = () => builder.WithDataBindMount(source); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(builder), exception.ParamName); + }); + } + + [Fact] + public void WithDataBindMountShouldThrowsWhenNameIsNull() + { + var distributedApplicationBuilder = new DistributedApplicationBuilder([]); + const string name = "Redis"; + var resource = new RedisResource(name); + var builder = distributedApplicationBuilder.AddResource(resource); + string source = null!; + + var action = () => builder.WithDataBindMount(source); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(source), exception.ParamName); + }); + } + + [Fact] + public void WithPersistenceShouldThrowsWhenBuilderIsNull() + { + IResourceBuilder builder = null!; + + var action = () => builder.WithPersistence(); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(builder), exception.ParamName); + }); + } + + #endregion + + #region RedisCommanderConfigWriterHook + + [Fact] + public async Task AfterEndpointsAllocatedAsyncShouldThrowsWhenDistributedApplicationModelIsNull() + { + DistributedApplicationModel appModel = null!; + var cancellationToken = CancellationToken.None; + + var instance = (RedisCommanderConfigWriterHook)Activator.CreateInstance(typeof(RedisCommanderConfigWriterHook), true)!; + + async Task Action() => await instance.AfterEndpointsAllocatedAsync(appModel, cancellationToken); + + var exception = await Assert.ThrowsAsync(Action); + Assert.Equal(nameof(appModel), exception.ParamName); + } + + #endregion + + #region RedisCommanderResource + + [Fact] + public void CtorRedisCommanderResourceShouldThrowsWhenNameIsNull() + { + string name = null!; + + var action = () => new RedisCommanderResource(name); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(name), exception.ParamName); + }); + } + + #endregion + + #region RedisResource + + [Fact] + public void CtorRedisResourceShouldThrowsWhenNameIsNull() + { + string name = null!; + + var action = () => new RedisResource(name); + + Assert.Multiple(() => + { + var exception = Assert.Throws(action); + Assert.Equal(nameof(name), exception.ParamName); + }); + } + + #endregion +}