File: ValueSnapshotTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Tests\Aspire.Hosting.Tests.csproj (Aspire.Hosting.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Aspire.Hosting.Tests;
 
public class ValueSnapshotTests
{
    [Fact]
    public async Task GetValueAsync_WaitsForFirstValue()
    {
        var snapshot = new ValueSnapshot<string>();
 
        var getTask = snapshot.GetValueAsync();
        Assert.False(getTask.IsCompleted);
 
        snapshot.SetValue("test");
 
        var result = await getTask;
        Assert.Equal("test", result);
    }
 
    [Fact]
    public async Task GetValueAsync_ReturnsImmediatelyAfterValueSet()
    {
        var snapshot = new ValueSnapshot<string>();
        snapshot.SetValue("test");
 
        var getTask = snapshot.GetValueAsync();
 
        Assert.True(getTask.IsCompleted);
 
        var result = await getTask;
 
        Assert.Equal("test", result);
    }
 
    [Fact]
    public async Task SetValue_UpdatesValue()
    {
        var snapshot = new ValueSnapshot<string>();
        snapshot.SetValue("first");
 
        var firstResult = await snapshot.GetValueAsync();
        Assert.Equal("first", firstResult);
 
        snapshot.SetValue("second");
 
        var secondResult = await snapshot.GetValueAsync();
        Assert.Equal("second", secondResult);
    }
 
    [Fact]
    public async Task SetValue_CompletesAllWaiters()
    {
        var snapshot = new ValueSnapshot<string>();
 
        var task1 = snapshot.GetValueAsync();
        var task2 = snapshot.GetValueAsync();
        var task3 = snapshot.GetValueAsync();
 
        snapshot.SetValue("test");
 
        var result1 = await task1;
        var result2 = await task2;
        var result3 = await task3;
 
        Assert.Equal("test", result1);
        Assert.Equal("test", result2);
        Assert.Equal("test", result3);
    }
 
    [Fact]
    public async Task SetException_ThrowsException()
    {
        var snapshot = new ValueSnapshot<string>();
        var exception = new InvalidOperationException("Test error");
 
        snapshot.SetException(exception);
 
        var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => snapshot.GetValueAsync());
        Assert.Same(exception, ex);
    }
 
    [Fact]
    public async Task SetException_CompletesAllWaiters()
    {
        var snapshot = new ValueSnapshot<string>();
        var exception = new InvalidOperationException("Test error");
 
        var task1 = snapshot.GetValueAsync();
        var task2 = snapshot.GetValueAsync();
 
        snapshot.SetException(exception);
 
        var ex1 = await Assert.ThrowsAsync<InvalidOperationException>(() => task1);
        var ex2 = await Assert.ThrowsAsync<InvalidOperationException>(() => task2);
 
        Assert.Same(exception, ex1);
        Assert.Same(exception, ex2);
    }
 
    [Fact]
    public async Task SetException_CanBeReplacedWithValue()
    {
        var snapshot = new ValueSnapshot<string>();
        var exception = new InvalidOperationException("Test error");
 
        snapshot.SetException(exception);
        await Assert.ThrowsAsync<InvalidOperationException>(() => snapshot.GetValueAsync());
 
        snapshot.SetValue("recovered");
 
        var result = await snapshot.GetValueAsync();
        Assert.Equal("recovered", result);
    }
 
    [Fact]
    public async Task SetValue_CanBeReplacedWithException()
    {
        var snapshot = new ValueSnapshot<string>();
 
        snapshot.SetValue("test");
        Assert.Equal("test", await snapshot.GetValueAsync());
 
        var exception = new InvalidOperationException("Error occurred");
        snapshot.SetException(exception);
 
        var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => snapshot.GetValueAsync());
        Assert.Same(exception, ex);
    }
 
    [Fact]
    public void IsValueSet_FalseBeforeAnySet()
    {
        var snapshot = new ValueSnapshot<string>();
 
        Assert.False(snapshot.IsValueSet);
    }
 
    [Fact]
    public void IsValueSet_TrueAfterSetValue()
    {
        var snapshot = new ValueSnapshot<string>();
 
        snapshot.SetValue("test");
 
        Assert.True(snapshot.IsValueSet);
    }
 
    [Fact]
    public void IsValueSet_TrueAfterSetException()
    {
        var snapshot = new ValueSnapshot<string>();
 
        snapshot.SetException(new InvalidOperationException());
 
        Assert.True(snapshot.IsValueSet);
    }
 
    [Fact]
    public void IsValueSet_RemainsTrueAfterUpdate()
    {
        var snapshot = new ValueSnapshot<string>();
 
        snapshot.SetValue("first");
        Assert.True(snapshot.IsValueSet);
 
        snapshot.SetValue("second");
        Assert.True(snapshot.IsValueSet);
 
        snapshot.SetException(new InvalidOperationException());
        Assert.True(snapshot.IsValueSet);
    }
 
    [Fact]
    public async Task GetValueAsync_SupportsCancellation()
    {
        var snapshot = new ValueSnapshot<string>();
        using var cts = new CancellationTokenSource();
 
        var getTask = snapshot.GetValueAsync(cts.Token);
        cts.Cancel();
 
        await Assert.ThrowsAsync<TaskCanceledException>(() => getTask);
    }
 
    [Fact]
    public async Task GetValueAsync_CancellationDoesNotAffectOtherWaiters()
    {
        var snapshot = new ValueSnapshot<string>();
        using var cts = new CancellationTokenSource();
 
        var cancelledTask = snapshot.GetValueAsync(cts.Token);
        var normalTask = snapshot.GetValueAsync();
 
        cts.Cancel();
        await Assert.ThrowsAsync<TaskCanceledException>(() => cancelledTask);
 
        snapshot.SetValue("test");
        var result = await normalTask;
 
        Assert.Equal("test", result);
    }
 
    [Fact]
    public void SetException_ThrowsArgumentNullException_WhenExceptionIsNull()
    {
        var snapshot = new ValueSnapshot<string>();
 
        Assert.Throws<ArgumentNullException>(() => snapshot.SetException(null!));
    }
}