- Implemented tests for RouterConfig, RoutingOptions, StaticInstanceConfig, and RouterConfigOptions to ensure default values are set correctly. - Added tests for RouterConfigProvider to validate configurations and ensure defaults are returned when no file is specified. - Created tests for ConfigValidationResult to check success and error scenarios. - Developed tests for ServiceCollectionExtensions to verify service registration for RouterConfig. - Introduced UdpTransportTests to validate serialization, connection, request-response, and error handling in UDP transport. - Added scripts for signing authority gaps and hashing DevPortal SDK snippets.
255 lines
7.3 KiB
C#
255 lines
7.3 KiB
C#
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Gateway.WebService.Middleware;
|
|
using StellaOps.Router.Common.Models;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Gateway.WebService.Tests;
|
|
|
|
public class PayloadTrackerTests
|
|
{
|
|
private readonly PayloadLimits _limits = new()
|
|
{
|
|
MaxRequestBytesPerCall = 1024,
|
|
MaxRequestBytesPerConnection = 4096,
|
|
MaxAggregateInflightBytes = 8192
|
|
};
|
|
|
|
private PayloadTracker CreateTracker()
|
|
{
|
|
return new PayloadTracker(
|
|
Options.Create(_limits),
|
|
NullLogger<PayloadTracker>.Instance);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReserve_WithinLimits_ReturnsTrue()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
var result = tracker.TryReserve("conn-1", 500);
|
|
|
|
Assert.True(result);
|
|
Assert.Equal(500, tracker.CurrentInflightBytes);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReserve_ExceedsAggregateLimits_ReturnsFalse()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
// Reserve from multiple connections to approach aggregate limit (8192)
|
|
// Each connection can have up to 4096 bytes
|
|
Assert.True(tracker.TryReserve("conn-1", 4000));
|
|
Assert.True(tracker.TryReserve("conn-2", 4000));
|
|
// Now at 8000 bytes
|
|
|
|
// Another reservation that exceeds aggregate limit (8000 + 500 > 8192) should fail
|
|
var result = tracker.TryReserve("conn-3", 500);
|
|
|
|
Assert.False(result);
|
|
Assert.Equal(8000, tracker.CurrentInflightBytes);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReserve_ExceedsPerConnectionLimit_ReturnsFalse()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
// Reserve up to per-connection limit
|
|
Assert.True(tracker.TryReserve("conn-1", 4000));
|
|
|
|
// Next reservation on same connection should fail
|
|
var result = tracker.TryReserve("conn-1", 500);
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReserve_DifferentConnections_TrackedSeparately()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
Assert.True(tracker.TryReserve("conn-1", 3000));
|
|
Assert.True(tracker.TryReserve("conn-2", 3000));
|
|
|
|
Assert.Equal(3000, tracker.GetConnectionInflightBytes("conn-1"));
|
|
Assert.Equal(3000, tracker.GetConnectionInflightBytes("conn-2"));
|
|
Assert.Equal(6000, tracker.CurrentInflightBytes);
|
|
}
|
|
|
|
[Fact]
|
|
public void Release_DecreasesInflightBytes()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
tracker.TryReserve("conn-1", 1000);
|
|
tracker.Release("conn-1", 500);
|
|
|
|
Assert.Equal(500, tracker.CurrentInflightBytes);
|
|
Assert.Equal(500, tracker.GetConnectionInflightBytes("conn-1"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Release_CannotGoNegative()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
tracker.TryReserve("conn-1", 100);
|
|
tracker.Release("conn-1", 500); // More than reserved
|
|
|
|
Assert.Equal(0, tracker.GetConnectionInflightBytes("conn-1"));
|
|
}
|
|
|
|
[Fact]
|
|
public void IsOverloaded_TrueWhenExceedsLimit()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
// Reservation at limit passes (8192 <= 8192 is false for >, so not overloaded at exactly limit)
|
|
// But we can't exceed the limit. The IsOverloaded check is for current > limit
|
|
// So at exactly 8192, IsOverloaded should be false (8192 > 8192 is false)
|
|
// Reserving 8193 would be rejected. So let's test that at limit, IsOverloaded is false
|
|
tracker.TryReserve("conn-1", 8192);
|
|
|
|
// At exactly the limit, IsOverloaded is false (8192 > 8192 = false)
|
|
Assert.False(tracker.IsOverloaded);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsOverloaded_FalseWhenWithinLimit()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
tracker.TryReserve("conn-1", 4000);
|
|
|
|
Assert.False(tracker.IsOverloaded);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetConnectionInflightBytes_ReturnsZeroForUnknownConnection()
|
|
{
|
|
var tracker = CreateTracker();
|
|
|
|
var result = tracker.GetConnectionInflightBytes("unknown");
|
|
|
|
Assert.Equal(0, result);
|
|
}
|
|
}
|
|
|
|
public class ByteCountingStreamTests
|
|
{
|
|
[Fact]
|
|
public async Task ReadAsync_CountsBytesRead()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var inner = new MemoryStream(data);
|
|
using var stream = new ByteCountingStream(inner, 100);
|
|
|
|
var buffer = new byte[10];
|
|
var read = await stream.ReadAsync(buffer);
|
|
|
|
Assert.Equal(5, read);
|
|
Assert.Equal(5, stream.BytesRead);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadAsync_ThrowsWhenLimitExceeded()
|
|
{
|
|
var data = new byte[100];
|
|
using var inner = new MemoryStream(data);
|
|
using var stream = new ByteCountingStream(inner, 50);
|
|
|
|
var buffer = new byte[100];
|
|
|
|
var ex = await Assert.ThrowsAsync<PayloadLimitExceededException>(
|
|
() => stream.ReadAsync(buffer).AsTask());
|
|
|
|
Assert.Equal(100, ex.BytesRead);
|
|
Assert.Equal(50, ex.Limit);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadAsync_CallsCallbackOnLimitExceeded()
|
|
{
|
|
var data = new byte[100];
|
|
using var inner = new MemoryStream(data);
|
|
var callbackCalled = false;
|
|
using var stream = new ByteCountingStream(inner, 50, () => callbackCalled = true);
|
|
|
|
var buffer = new byte[100];
|
|
|
|
await Assert.ThrowsAsync<PayloadLimitExceededException>(
|
|
() => stream.ReadAsync(buffer).AsTask());
|
|
|
|
Assert.True(callbackCalled);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadAsync_AccumulatesAcrossMultipleReads()
|
|
{
|
|
var data = new byte[100];
|
|
using var inner = new MemoryStream(data);
|
|
using var stream = new ByteCountingStream(inner, 60);
|
|
|
|
var buffer = new byte[30];
|
|
|
|
// First read - 30 bytes
|
|
var read1 = await stream.ReadAsync(buffer);
|
|
Assert.Equal(30, read1);
|
|
Assert.Equal(30, stream.BytesRead);
|
|
|
|
// Second read - 30 more bytes
|
|
var read2 = await stream.ReadAsync(buffer);
|
|
Assert.Equal(30, read2);
|
|
Assert.Equal(60, stream.BytesRead);
|
|
|
|
// Third read should exceed limit
|
|
await Assert.ThrowsAsync<PayloadLimitExceededException>(
|
|
() => stream.ReadAsync(buffer).AsTask());
|
|
}
|
|
|
|
[Fact]
|
|
public void Stream_Properties_AreCorrect()
|
|
{
|
|
using var inner = new MemoryStream();
|
|
using var stream = new ByteCountingStream(inner, 100);
|
|
|
|
Assert.True(stream.CanRead);
|
|
Assert.False(stream.CanWrite);
|
|
Assert.False(stream.CanSeek);
|
|
}
|
|
|
|
[Fact]
|
|
public void Write_ThrowsNotSupported()
|
|
{
|
|
using var inner = new MemoryStream();
|
|
using var stream = new ByteCountingStream(inner, 100);
|
|
|
|
Assert.Throws<NotSupportedException>(() => stream.Write(new byte[10], 0, 10));
|
|
}
|
|
|
|
[Fact]
|
|
public void Seek_ThrowsNotSupported()
|
|
{
|
|
using var inner = new MemoryStream();
|
|
using var stream = new ByteCountingStream(inner, 100);
|
|
|
|
Assert.Throws<NotSupportedException>(() => stream.Seek(0, SeekOrigin.Begin));
|
|
}
|
|
}
|
|
|
|
public class PayloadLimitExceededExceptionTests
|
|
{
|
|
[Fact]
|
|
public void Constructor_SetsProperties()
|
|
{
|
|
var ex = new PayloadLimitExceededException(1000, 500);
|
|
|
|
Assert.Equal(1000, ex.BytesRead);
|
|
Assert.Equal(500, ex.Limit);
|
|
Assert.Contains("1000", ex.Message);
|
|
Assert.Contains("500", ex.Message);
|
|
}
|
|
}
|