mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 11:11:14 +00:00
8b0ddae075
Phase B of architecture coupling refactor (epic AZ-309). Replaces the monolithic SatelliteProvider.Services with three per-component csprojs to add a compiler-enforced module boundary (resolves F4): - SatelliteProvider.Services.TileDownloader - SatelliteProvider.Services.RegionProcessing - SatelliteProvider.Services.RouteManagement DI registrations relocated into per-component AddTileDownloader / AddRegionProcessing / AddRouteManagement extension methods called from Program.cs. RateLimitException moved to Common/Exceptions/ to keep the three new csprojs as siblings (no Region->TileDownloader ProjectReference). Dockerfiles and consumer csprojs (Api, Tests) rewired to the new project paths. No DI lifetime or hosted-service order changes. Build: 0 warn, 0 err. Unit tests: 40/40. Smoke integration: green. Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
2.6 KiB
C#
86 lines
2.6 KiB
C#
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using SatelliteProvider.Common.DTO;
|
|
using SatelliteProvider.Services.RegionProcessing;
|
|
|
|
namespace SatelliteProvider.Tests;
|
|
|
|
public class RegionRequestQueueTests
|
|
{
|
|
private static RegionRequest BuildRequest() => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Latitude = 47.461747,
|
|
Longitude = 37.647063,
|
|
SizeMeters = 200,
|
|
ZoomLevel = 18,
|
|
StitchTiles = false
|
|
};
|
|
|
|
[Fact]
|
|
public async Task EnqueueAsync_RespectsCapacity_WritesUpToCapacityWithoutBlocking_RS04()
|
|
{
|
|
const int capacity = 10;
|
|
var queue = new RegionRequestQueue(capacity, NullLogger<RegionRequestQueue>.Instance);
|
|
|
|
for (var i = 0; i < capacity; i++)
|
|
{
|
|
await queue.EnqueueAsync(BuildRequest());
|
|
}
|
|
|
|
queue.Count.Should().Be(capacity);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnqueueAsync_BlocksWhenAtCapacity_UntilDequeue_RL02()
|
|
{
|
|
const int capacity = 2;
|
|
var queue = new RegionRequestQueue(capacity, NullLogger<RegionRequestQueue>.Instance);
|
|
|
|
await queue.EnqueueAsync(BuildRequest());
|
|
await queue.EnqueueAsync(BuildRequest());
|
|
|
|
var overflow = queue.EnqueueAsync(BuildRequest()).AsTask();
|
|
var completedFirst = await Task.WhenAny(overflow, Task.Delay(150));
|
|
|
|
completedFirst.Should().NotBeSameAs(overflow, "overflow enqueue must wait while queue is full");
|
|
|
|
var dequeued = await queue.DequeueAsync();
|
|
dequeued.Should().NotBeNull();
|
|
|
|
await overflow.WaitAsync(TimeSpan.FromSeconds(1));
|
|
queue.Count.Should().Be(capacity);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnqueueAsync_HonorsCancellation_WhenFull()
|
|
{
|
|
var queue = new RegionRequestQueue(1, NullLogger<RegionRequestQueue>.Instance);
|
|
await queue.EnqueueAsync(BuildRequest());
|
|
|
|
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(150));
|
|
|
|
Func<Task> act = async () => await queue.EnqueueAsync(BuildRequest(), cts.Token);
|
|
|
|
await act.Should().ThrowAsync<OperationCanceledException>();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DequeueAsync_ReturnsItemsInFifoOrder()
|
|
{
|
|
var queue = new RegionRequestQueue(8, NullLogger<RegionRequestQueue>.Instance);
|
|
var first = BuildRequest();
|
|
var second = BuildRequest();
|
|
|
|
await queue.EnqueueAsync(first);
|
|
await queue.EnqueueAsync(second);
|
|
|
|
var dequeued1 = await queue.DequeueAsync();
|
|
var dequeued2 = await queue.DequeueAsync();
|
|
|
|
dequeued1!.Id.Should().Be(first.Id);
|
|
dequeued2!.Id.Should().Be(second.Id);
|
|
queue.Count.Should().Be(0);
|
|
}
|
|
}
|