File: TestTypes\TestExtensions.cs
Web Access
Project: src\tests\Aspire.Hosting.CodeGeneration.TypeScript.Tests\Aspire.Hosting.CodeGeneration.TypeScript.Tests.csproj (Aspire.Hosting.CodeGeneration.TypeScript.Tests)
// 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;
 
namespace Aspire.Hosting.CodeGeneration.TypeScript.Tests.TestTypes;
 
/// <summary>
/// Extension methods for testing code generation.
/// </summary>
public static class TestExtensions
{
    /// <summary>
    /// Adds a test Redis resource from XML documentation.
    /// </summary>
    /// <ats-summary>Adds a test Redis resource from ATS documentation.</ats-summary>
    /// <param name="builder">The distributed application builder.</param>
    /// <param name="name">The resource name.</param>
    /// <ats-param name="name">The ATS resource name.</ats-param>
    /// <param name="port">The optional Redis port.</param>
    /// <ats-param name="port"></ats-param>
    /// <returns>The test Redis resource builder.</returns>
    /// <ats-returns>The ATS test Redis resource builder.</ats-returns>
    /// <remarks>
    /// Uses XML documentation instead of the attribute description when both are present.
    /// </remarks>
    /// <ats-remarks></ats-remarks>
    /// <ats-summary>Adds a test Redis resource</ats-summary>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> AddTestRedis(
        this IDistributedApplicationBuilder builder,
        string name,
        int? port = null)
    {
        var resource = new TestRedisResource(name);
        return builder.AddResource(resource)
            .WithEndpoint(port: port, name: "tcp");
    }
 
    /// <summary>
    /// Adds a test database resource.
    /// </summary>
    public static IResourceBuilder<TestDatabaseResource> AddTestDatabase(
        this IDistributedApplicationBuilder builder,
        string name,
        string? databaseName = null)
    {
        var resource = new TestDatabaseResource(name)
        {
            DatabaseName = databaseName
        };
        return builder.AddResource(resource);
    }
 
#if ASPIRE_TYPESCRIPT_CODEGEN_TESTS
    /// <summary>
    /// Adds a resource exposed only through a bare marker interface.
    /// </summary>
    // Deliberately returns IResourceBuilder<ITestMarkerResource>: the marker interface contributes
    // no capabilities of its own, so this export is the reference site that forces
    // TestMarkerResourcePromise to be emitted in the generated SDK. Kept as a plain comment rather
    // than <remarks> so this rationale does not land in the generated SDK doc comments. See
    // GenerateDistributedApplication_EmitsPromiseWrapperForBareMarkerResourceBuilder and
    // https://github.com/microsoft/aspire/issues/19507.
    [AspireExport]
    public static IResourceBuilder<ITestMarkerResource> AddTestMarker(
        this IDistributedApplicationBuilder builder,
        string name)
    {
        return builder.AddResource(new TestMarkerResource(name));
    }
#endif
 
    /// <summary>
    /// Configures a Redis resource with parameter-only resources whose generated names collide.
    /// </summary>
    /// <param name="builder">The Redis resource builder.</param>
    /// <param name="resource">The resource whose unused Promise wrapper would collide.</param>
    /// <param name="resourcePromise">The resource whose generated name matches that Promise wrapper.</param>
    /// <returns>The Redis resource builder.</returns>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> WithPromiseCollisionResources(
        this IResourceBuilder<TestRedisResource> builder,
        IResourceBuilder<ITestPromiseCollisionResource> resource,
        IResourceBuilder<ITestPromiseCollisionResourcePromise> resourcePromise)
    {
        return builder;
    }
 
    /// <summary>
    /// Configures a Redis resource with mutable-property and parameter-only resources whose generated names collide.
    /// </summary>
    /// <param name="builder">The Redis resource builder.</param>
    /// <param name="resource">The mutable-property-only resource whose unused Promise wrapper would collide.</param>
    /// <param name="resourcePromise">The parameter-only resource whose generated name matches that Promise wrapper.</param>
    /// <returns>The Redis resource builder.</returns>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> WithMutablePromiseCollisionResources(
        this IResourceBuilder<TestRedisResource> builder,
        IResourceBuilder<ITestMutablePromiseCollisionResource> resource,
        IResourceBuilder<ITestMutablePromiseCollisionResourcePromise> resourcePromise)
    {
        return builder;
    }
 
    /// <summary>
    /// Adds a child database to a Redis server resource (factory method pattern).
    /// </summary>
    /// <remarks>
    /// This method tests the factory method codegen pattern where a method on builder type A
    /// returns builder type B (e.g., SqlServerServerResource.AddDatabase returning SqlServerDatabaseResource).
    /// </remarks>
    /// <ats-summary>Adds a child database to a test Redis resource</ats-summary>
    [AspireExport]
    public static IResourceBuilder<TestDatabaseResource> AddTestChildDatabase(
        this IResourceBuilder<TestRedisResource> builder,
        string name,
        string? databaseName = null)
    {
        var resource = new TestDatabaseResource(name)
        {
            DatabaseName = databaseName
        };
        return builder.ApplicationBuilder.AddResource(resource);
    }
 
    /// <summary>
    /// Configures the Redis resource with persistence.
    /// </summary>
    /// <ats-summary>Configures the Redis resource with persistence</ats-summary>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> WithPersistence(
        this IResourceBuilder<TestRedisResource> builder,
        TestPersistenceMode mode = TestPersistenceMode.Volume)
    {
        return builder.WithAnnotation(new TestPersistenceAnnotation(mode));
    }
 
    /// <summary>
    /// Configures the resource with a custom callback.
    /// </summary>
    public static IResourceBuilder<T> WithCustomCallback<T>(
        this IResourceBuilder<T> builder,
        Action<TestCallbackContext> callback) where T : IResourceWithEnvironment
    {
        callback(new TestCallbackContext());
        return builder;
    }
 
    /// <summary>
    /// Adds an optional string parameter.
    /// </summary>
    /// <ats-summary>Adds an optional string parameter</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithOptionalString<T>(
        this IResourceBuilder<T> builder,
        string? value = null,
        bool enabled = true) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Adds multiple parameters with defaults.
    /// </summary>
    public static IResourceBuilder<T> WithMultipleDefaults<T>(
        this IResourceBuilder<T> builder,
        int count = 10,
        string prefix = "item",
        bool useUpperCase = false,
        double multiplier = 1.5) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Configures the resource with a builder callback.
    /// This tests the Action&lt;IResourceBuilder&lt;T&gt;&gt; pattern.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithBuilderCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Action<IResourceBuilder<TestRedisResource>>? configure = null)
    {
        configure?.Invoke(builder);
        return builder;
    }
 
    /// <summary>
    /// Returns the resource as IResourceWithConnectionString builder.
    /// This tests that interface types are discovered and get builder classes generated.
    /// </summary>
    public static IResourceBuilder<IResourceWithConnectionString> AsConnectionString(
        this IResourceBuilder<TestRedisResource> builder)
    {
        // Cast to interface builder
        return builder;
    }
 
    /// <summary>
    /// Tests circular type reference: Action takes IResourceBuilder which has methods that take Actions.
    /// This ensures the queue-based discovery handles cycles correctly.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithCircularCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Action<IResourceBuilder<TestRedisResource>> configure)
    {
        configure?.Invoke(builder);
        return builder;
    }
 
    /// <summary>
    /// Tests nested circular references: Action&lt;Action&lt;IResourceBuilder&gt;&gt;.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithNestedCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Action<Action<IResourceBuilder<TestRedisResource>>> outerConfigure)
    {
        outerConfigure?.Invoke(_ => { });
        return builder;
    }
 
    // ===== Edge Case Tests =====
 
    /// <summary>
    /// Tests async delegate callback: Func&lt;T, Task&gt;.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithAsyncCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Func<TestCallbackContext, Task> asyncCallback)
    {
        asyncCallback?.Invoke(new TestCallbackContext());
        return builder;
    }
 
    /// <summary>
    /// Tests async delegate with return value: Func&lt;T, Task&lt;TResult&gt;&gt;.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithAsyncCallbackWithResult(
        this IResourceBuilder<TestRedisResource> builder,
        Func<TestCallbackContext, Task<bool>> asyncCallback)
    {
        asyncCallback?.Invoke(new TestCallbackContext());
        return builder;
    }
 
    /// <summary>
    /// Tests async builder callback: Func&lt;IResourceBuilder&lt;T&gt;, Task&gt;.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithAsyncBuilderCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Func<IResourceBuilder<TestRedisResource>, Task> asyncConfigure)
    {
        asyncConfigure?.Invoke(builder);
        return builder;
    }
 
    /// <summary>
    /// Tests array parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithTags(
        this IResourceBuilder<TestRedisResource> builder,
        string[] tags)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests List&lt;T&gt; parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithLabels(
        this IResourceBuilder<TestRedisResource> builder,
        List<string> labels)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests Dictionary parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithMetadata(
        this IResourceBuilder<TestRedisResource> builder,
        Dictionary<string, string> metadata)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests IEnumerable&lt;T&gt; parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithItems(
        this IResourceBuilder<TestRedisResource> builder,
        IEnumerable<string> items)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests nullable value type parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithTimeout(
        this IResourceBuilder<TestRedisResource> builder,
        int? timeoutSeconds = null)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests multiple nullable value types.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithLimits(
        this IResourceBuilder<TestRedisResource> builder,
        int? maxConnections = null,
        double? memoryLimitMb = null,
        bool? enableLogging = null)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests TimeSpan parameter (common .NET type).
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithExpiry(
        this IResourceBuilder<TestRedisResource> builder,
        TimeSpan expiry)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests nullable TimeSpan parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithOptionalExpiry(
        this IResourceBuilder<TestRedisResource> builder,
        TimeSpan? expiry = null)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests multi-type-parameter callback: Func&lt;T1, T2, TResult&gt;.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithTransform(
        this IResourceBuilder<TestRedisResource> builder,
        Func<string, int, string> transform)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests Action with multiple parameters.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithMultiParamCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Action<string, int, bool> callback)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests KeyValuePair parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithSetting(
        this IResourceBuilder<TestRedisResource> builder,
        KeyValuePair<string, string> setting)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests Tuple parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithEndpointMapping(
        this IResourceBuilder<TestRedisResource> builder,
        (string name, int port) endpoint)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests Uri parameter.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithProxyUrl(
        this IResourceBuilder<TestRedisResource> builder,
        Uri proxyUrl)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests array of complex types.
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithCallbackContexts(
        this IResourceBuilder<TestRedisResource> builder,
        TestCallbackContext[] contexts)
    {
        return builder;
    }
 
    // ===== Additional Delegate Edge Cases =====
 
    /// <summary>
    /// Tests non-generic Action (no parameters).
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithSimpleCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Action callback)
    {
        callback?.Invoke();
        return builder;
    }
 
    /// <summary>
    /// Tests Func with no parameters (just return type).
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithValueProvider(
        this IResourceBuilder<TestRedisResource> builder,
        Func<string> valueProvider)
    {
        valueProvider?.Invoke();
        return builder;
    }
 
    /// <summary>
    /// Tests Func with no parameters returning Task (async factory).
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithAsyncValueProvider(
        this IResourceBuilder<TestRedisResource> builder,
        Func<Task<string>> asyncValueProvider)
    {
        asyncValueProvider?.Invoke();
        return builder;
    }
 
    /// <summary>
    /// Tests Action with 4 parameters (higher arity).
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithQuadCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Action<string, int, bool, double> callback)
    {
        return builder;
    }
 
    /// <summary>
    /// Tests Func with 4 parameters plus return (higher arity).
    /// </summary>
    public static IResourceBuilder<TestRedisResource> WithQuadTransform(
        this IResourceBuilder<TestRedisResource> builder,
        Func<string, int, bool, double, string> transform)
    {
        return builder;
    }
 
    // ===== Additional Test Cases for Full ATS Type Coverage =====
 
    /// <summary>
    /// Tests DTO parameter - verifies [AspireDto] generates TypeScript interface.
    /// </summary>
    /// <ats-summary>Configures the resource with a DTO</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithConfig<T>(
        this IResourceBuilder<T> builder,
        TestConfigDto config) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests mutable List return type - verifies AspireList wrapper generation.
    /// </summary>
    /// <ats-summary>Gets the tags for the resource</ats-summary>
    [AspireExport]
    public static List<string> GetTags(this IResourceBuilder<TestRedisResource> builder)
    {
        return [];
    }
 
    /// <summary>
    /// Tests mutable Dictionary return type - verifies AspireDict wrapper generation.
    /// </summary>
    /// <ats-summary>Gets the metadata for the resource</ats-summary>
    [AspireExport]
    public static Dictionary<string, string> GetMetadata(this IResourceBuilder<TestRedisResource> builder)
    {
        return [];
    }
 
    /// <summary>
    /// Tests ReferenceExpression parameter - verifies special handling (pass directly via toJSON).
    /// </summary>
    /// <ats-summary>Sets the connection string using a reference expression</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithConnectionString<T>(
        this IResourceBuilder<T> builder,
        ReferenceExpression connectionString) where T : IResourceWithConnectionString
    {
        return builder;
    }
 
    /// <summary>
    /// Tests callback receiving context wrapper.
    /// Verifies callback auto-wraps handle into context class with property-like objects.
    /// </summary>
    /// <ats-summary>Configures environment with callback (test version)</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> TestWithEnvironmentCallback<T>(
        this IResourceBuilder<T> builder,
        Func<TestEnvironmentContext, Task> callback) where T : IResourceWithEnvironment
    {
        return builder;
    }
 
    /// <summary>
    /// Tests DateTime parameter - verifies mapping to ISO 8601 string.
    /// </summary>
    /// <ats-summary>Sets the created timestamp</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithCreatedAt<T>(
        this IResourceBuilder<T> builder,
        DateTime createdAt) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests DateTimeOffset parameter - verifies mapping to ISO 8601 string.
    /// </summary>
    /// <ats-summary>Sets the modified timestamp</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithModifiedAt<T>(
        this IResourceBuilder<T> builder,
        DateTimeOffset modifiedAt) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests Guid parameter - verifies mapping to string.
    /// </summary>
    /// <ats-summary>Sets the correlation ID</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithCorrelationId<T>(
        this IResourceBuilder<T> builder,
        Guid correlationId) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests optional callback parameter - verifies conditional callback registration.
    /// </summary>
    /// <ats-summary>Configures with optional callback</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithOptionalCallback<T>(
        this IResourceBuilder<T> builder,
        Func<TestCallbackContext, Task>? callback = null) where T : IResource
    {
        callback?.Invoke(new TestCallbackContext());
        return builder;
    }
 
    /// <summary>
    /// Tests enum parameter - verifies string literal union generation.
    /// </summary>
    /// <ats-summary>Sets the resource status</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithStatus<T>(
        this IResourceBuilder<T> builder,
        TestResourceStatus status) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests nested DTO parameter.
    /// </summary>
    /// <ats-summary>Configures with nested DTO</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithNestedConfig<T>(
        this IResourceBuilder<T> builder,
        TestNestedDto config) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests async callback with context that returns a value.
    /// </summary>
    /// <ats-summary>Adds validation callback</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithValidator<T>(
        this IResourceBuilder<T> builder,
        Func<TestResourceContext, Task<bool>> validator) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests builder passed as parameter to another capability.
    /// Verifies wrapper class acceptance with internal handle extraction.
    /// </summary>
    /// <ats-summary>Waits for another resource (test version)</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> TestWaitFor<T>(
        this IResourceBuilder<T> builder,
        IResourceBuilder<IResource> dependency) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests readonly array return type - verifies copy/pass directly.
    /// </summary>
    /// <ats-summary>Gets the endpoints</ats-summary>
    [AspireExport]
    public static string[] GetEndpoints(this IResourceBuilder<TestRedisResource> builder)
    {
        return [];
    }
 
    // ===== Polymorphism Pattern Tests =====
 
    /// <summary>
    /// Pattern 2: Tests interface type directly as target (NOT generic constraint).
    /// This targets IResourceWithConnectionString directly, not via generic parameter.
    /// Should expand to all types implementing IResourceWithConnectionString.
    /// </summary>
    /// <ats-summary>Sets connection string using direct interface target</ats-summary>
    [AspireExport]
    public static IResourceBuilder<IResourceWithConnectionString> WithConnectionStringDirect(
        IResourceBuilder<IResourceWithConnectionString> builder,
        string connectionString)
    {
        return builder;
    }
 
    /// <summary>
    /// Pattern 3: Tests concrete type with inheritance.
    /// This targets TestRedisResource directly (extends ContainerResource).
    /// Should expand to TestRedisResource AND any types that inherit from it.
    /// </summary>
    /// <ats-summary>Redis-specific configuration</ats-summary>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> WithRedisSpecific(
        IResourceBuilder<TestRedisResource> builder,
        string option)
    {
        return builder;
    }
 
    /// <summary>
    /// Pattern 4/5: Tests interface/concrete type as parameter (not target).
    /// The dependency parameter should generate a union type: Handle | ResourceBuilderBase.
    /// </summary>
    /// <ats-summary>Adds a dependency on another resource</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithDependency<T>(
        this IResourceBuilder<T> builder,
        IResourceBuilder<IResourceWithConnectionString> dependency) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests an Aspire union parameter that includes an interface handle.
    /// The dependency parameter should generate a union type: string | ResourceBuilderBase.
    /// </summary>
    /// <ats-summary>Adds a dependency from a string or another resource</ats-summary>
    [AspireExport("withUnionDependency")]
    public static IResourceBuilder<T> WithUnionDependency<T>(
        this IResourceBuilder<T> builder,
        [AspireUnion(typeof(string), typeof(IResourceBuilder<IResourceWithConnectionString>))] object dependency) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests IReadOnlyList parameter - verifies readonly array handling.
    /// </summary>
    /// <ats-summary>Sets the endpoints</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithEndpoints<T>(
        this IResourceBuilder<T> builder,
        IReadOnlyList<string> endpoints) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests IReadOnlyDictionary parameter - verifies readonly dict handling.
    /// </summary>
    /// <ats-summary>Sets environment variables</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithEnvironmentVariables<T>(
        this IResourceBuilder<T> builder,
        IReadOnlyDictionary<string, string> variables) where T : IResourceWithEnvironment
    {
        return builder;
    }
 
    // ===== CancellationToken Tests =====
 
    /// <summary>
    /// Tests CancellationToken parameter - generated TypeScript should accept AbortSignal or CancellationToken for inputs.
    /// </summary>
    /// <ats-summary>Gets the status of the resource asynchronously</ats-summary>
    [AspireExport]
    public static Task<string> GetStatusAsync(
        this IResourceBuilder<TestRedisResource> builder,
        CancellationToken cancellationToken = default)
    {
        return Task.FromResult("running");
    }
 
    /// <summary>
    /// Tests CancellationToken in callback parameter - generated TypeScript should materialize host values as CancellationToken.
    /// </summary>
    /// <ats-summary>Performs a cancellable operation</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithCancellableOperation<T>(
        this IResourceBuilder<T> builder,
        Func<CancellationToken, Task> operation) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Tests CancellationToken mixed with other parameters.
    /// </summary>
    /// <ats-summary>Waits for the resource to be ready</ats-summary>
    [AspireExport]
    public static Task<bool> WaitForReadyAsync(
        this IResourceBuilder<TestRedisResource> builder,
        TimeSpan timeout,
        CancellationToken cancellationToken = default)
    {
        return Task.FromResult(true);
    }
 
    // ===== Multi-Parameter Callback Tests =====
 
    /// <summary>
    /// Tests multi-parameter callback with handle types for destructuring codegen.
    /// </summary>
    /// <ats-summary>Tests multi-param callback destructuring</ats-summary>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> WithMultiParamHandleCallback(
        this IResourceBuilder<TestRedisResource> builder,
        Func<TestCallbackContext, TestEnvironmentContext, Task> callback)
    {
        return builder;
    }
 
    // ===== Options Interface Merging Tests =====
 
    /// <summary>
    /// WithDataVolume on TestRedisResource — has both name and isReadOnly parameters.
    /// Tests that options interfaces merge parameters across overloads targeting different types.
    /// </summary>
    /// <ats-summary>Adds a data volume with persistence</ats-summary>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> WithDataVolume(
        this IResourceBuilder<TestRedisResource> builder,
        string? name = null,
        bool isReadOnly = false)
    {
        return builder;
    }
 
    /// <summary>
    /// WithDataVolume on TestDatabaseResource — has only name parameter.
    /// When combined with the TestRedisResource overload, the generated WithDataVolumeOptions
    /// interface must include both name and isReadOnly (the union of all parameters).
    /// </summary>
    /// <ats-summary>Adds a data volume</ats-summary>
    [AspireExport]
    public static IResourceBuilder<TestDatabaseResource> WithDataVolume(
        this IResourceBuilder<TestDatabaseResource> builder,
        string? name = null)
    {
        return builder;
    }
 
    // ===== Duplicate Class Name Tests =====
 
    /// <summary>
    /// Adds a vault through its interface while the concrete type is referenced separately.
    /// </summary>
    /// <param name="builder">The distributed application builder.</param>
    /// <param name="name">The resource name.</param>
    /// <returns>The interface vault resource builder.</returns>
    /// <ats-summary>Adds a test vault resource</ats-summary>
    [AspireExport]
    public static IResourceBuilder<ITestVaultResource> AddTestVault(
        this IDistributedApplicationBuilder builder,
        string name)
    {
        return builder.AddResource(new TestVaultResource(name));
    }
 
    /// <summary>
    /// Configures a Redis resource with the concrete vault resource as a parameter.
    /// </summary>
    /// <param name="builder">The Redis resource builder.</param>
    /// <param name="resource">The parameter-only concrete vault resource.</param>
    /// <returns>The Redis resource builder.</returns>
    [AspireExport]
    public static IResourceBuilder<TestRedisResource> WithConcreteVaultResource(
        this IResourceBuilder<TestRedisResource> builder,
        IResourceBuilder<TestVaultResource> resource)
    {
        return builder;
    }
 
    /// <summary>
    /// Directly targets the ITestVaultResource interface.
    /// DeriveClassName strips the 'I' prefix producing "TestVaultResource" — the same name
    /// as the concrete builder. The codegen must deduplicate to avoid emitting two classes.
    /// </summary>
    /// <ats-summary>Configures vault using direct interface target</ats-summary>
    [AspireExport]
    public static IResourceBuilder<ITestVaultResource> WithVaultDirect(
        IResourceBuilder<ITestVaultResource> builder,
        string option)
    {
        return builder;
    }
 
    // ===== SourceLocation Merge Tests =====
    // These test pairs share the same C# method name (SourceLocation) but differ by
    // a single required parameter. The codegen should merge them into one method with
    // that parameter made optional, and dispatch to the correct capability ID.
 
    /// <summary>
    /// Scenario 1: Single-param merge — one overload has name only, the other adds tag.
    /// Tests merge where the option variation is: single type vs tuple(type, type).
    /// </summary>
    /// <ats-summary>Adds a label to the resource</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithMergeLabel<T>(
        this IResourceBuilder<T> builder,
        string label) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Scenario 1b: Overload adds a category param — merge should make category optional.
    /// </summary>
    /// <ats-summary>Adds a categorized label to the resource</ats-summary>
    [AspireExport("withMergeLabelCategorized")]
    public static IResourceBuilder<T> WithMergeLabel<T>(
        this IResourceBuilder<T> builder,
        string label,
        string category) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Scenario 2: Tuple-param merge — both have multiple required params, differ by one.
    /// Tests merge where the option variation is: tuple(type, type) vs tuple(type, type, type).
    /// </summary>
    /// <ats-summary>Configures a named endpoint</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithMergeEndpoint<T>(
        this IResourceBuilder<T> builder,
        string endpointName,
        int port) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Scenario 2b: Overload adds a scheme param — merge should make scheme optional.
    /// </summary>
    /// <ats-summary>Configures a named endpoint with scheme</ats-summary>
    [AspireExport("withMergeEndpointScheme")]
    public static IResourceBuilder<T> WithMergeEndpoint<T>(
        this IResourceBuilder<T> builder,
        string endpointName,
        int port,
        string scheme) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Scenario 3: Dict/Parameters merge — both have required + optional params, differ by one.
    /// Tests merge where the option variation involves Parameters dict.
    /// </summary>
    /// <ats-summary>Configures resource logging</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithMergeLogging<T>(
        this IResourceBuilder<T> builder,
        string logLevel,
        bool enableConsole = true,
        int? maxFiles = null) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Scenario 3b: Overload adds a logPath param — merge should make logPath optional.
    /// </summary>
    /// <ats-summary>Configures resource logging with file path</ats-summary>
    [AspireExport("withMergeLoggingPath")]
    public static IResourceBuilder<T> WithMergeLogging<T>(
        this IResourceBuilder<T> builder,
        string logLevel,
        string logPath,
        bool enableConsole = true,
        int? maxFiles = null) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Scenario 4: Both-dict merge — both overloads have 4+ required params so both use Parameters dicts.
    /// The shorter overload has 4 required params, the longer has 5.
    /// </summary>
    /// <ats-summary>Configures a route</ats-summary>
    [AspireExport]
    public static IResourceBuilder<T> WithMergeRoute<T>(
        this IResourceBuilder<T> builder,
        string path,
        string method,
        string handler,
        int priority) where T : IResource
    {
        return builder;
    }
 
    /// <summary>
    /// Scenario 4b: Overload adds a middleware param — merge should make middleware optional.
    /// Both variations will use Parameters dicts since they have 4+ required params.
    /// </summary>
    /// <ats-summary>Configures a route with middleware</ats-summary>
    [AspireExport("withMergeRouteMiddleware")]
    public static IResourceBuilder<T> WithMergeRoute<T>(
        this IResourceBuilder<T> builder,
        string path,
        string method,
        string handler,
        int priority,
        string middleware) where T : IResource
    {
        return builder;
    }
}
 
/// <summary>
/// Test persistence mode enum.
/// </summary>
public enum TestPersistenceMode
{
    None,
    Volume,
    Bind
}
 
/// <summary>
/// Test persistence annotation.
/// </summary>
public class TestPersistenceAnnotation : IResourceAnnotation
{
    public TestPersistenceAnnotation(TestPersistenceMode mode)
    {
        Mode = mode;
    }
 
    public TestPersistenceMode Mode { get; }
}
 
/// <summary>
/// Test callback context for WithCustomCallback.
/// Also used to verify [AspireExport(ExposeProperties = true)] scanning.
/// </summary>
[AspireExport(ExposeProperties = true)]
public class TestCallbackContext
{
    public string? Name { get; set; }
    public int Value { get; set; }
 
    /// <summary>
    /// CancellationToken is supported by ATS.
    /// </summary>
    public CancellationToken CancellationToken { get; set; }
}