121 lines
4.7 KiB
C#
121 lines
4.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using StellaOps.Scheduler.Models;
|
|
using StellaOps.Scheduler.Queue;
|
|
using StellaOps.Scheduler.Queue.Nats;
|
|
using Xunit;
|
|
|
|
|
|
using StellaOps.TestKit;
|
|
namespace StellaOps.Scheduler.Queue.Tests;
|
|
|
|
public sealed class SchedulerQueueServiceCollectionExtensionsTests
|
|
{
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AddSchedulerQueues_RegistersNatsTransport()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddSingleton<ILoggerFactory>(_ => NullLoggerFactory.Instance);
|
|
services.AddSchedulerQueues(new ConfigurationBuilder().Build());
|
|
|
|
var optionsDescriptor = services.First(descriptor => descriptor.ServiceType == typeof(SchedulerQueueOptions));
|
|
var options = (SchedulerQueueOptions)optionsDescriptor.ImplementationInstance!;
|
|
options.Kind = SchedulerQueueTransportKind.Nats;
|
|
options.Nats.Url = "nats://localhost:4222";
|
|
|
|
await using var provider = services.BuildServiceProvider();
|
|
|
|
var plannerQueue = provider.GetRequiredService<ISchedulerPlannerQueue>();
|
|
var runnerQueue = provider.GetRequiredService<ISchedulerRunnerQueue>();
|
|
|
|
plannerQueue.Should().BeOfType<NatsSchedulerPlannerQueue>();
|
|
runnerQueue.Should().BeOfType<NatsSchedulerRunnerQueue>();
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task SchedulerQueueHealthCheck_ReturnsHealthy_WhenTransportsReachable()
|
|
{
|
|
var healthCheck = new SchedulerQueueHealthCheck(
|
|
new FakePlannerQueue(failPing: false),
|
|
new FakeRunnerQueue(failPing: false),
|
|
NullLogger<SchedulerQueueHealthCheck>.Instance);
|
|
|
|
var context = new HealthCheckContext
|
|
{
|
|
Registration = new HealthCheckRegistration("scheduler-queue", healthCheck, HealthStatus.Unhealthy, Array.Empty<string>())
|
|
};
|
|
|
|
var result = await healthCheck.CheckHealthAsync(context);
|
|
|
|
result.Status.Should().Be(HealthStatus.Healthy);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task SchedulerQueueHealthCheck_ReturnsUnhealthy_WhenRunnerPingFails()
|
|
{
|
|
var healthCheck = new SchedulerQueueHealthCheck(
|
|
new FakePlannerQueue(failPing: false),
|
|
new FakeRunnerQueue(failPing: true),
|
|
NullLogger<SchedulerQueueHealthCheck>.Instance);
|
|
|
|
var context = new HealthCheckContext
|
|
{
|
|
Registration = new HealthCheckRegistration("scheduler-queue", healthCheck, HealthStatus.Unhealthy, Array.Empty<string>())
|
|
};
|
|
|
|
var result = await healthCheck.CheckHealthAsync(context);
|
|
|
|
result.Status.Should().Be(HealthStatus.Unhealthy);
|
|
result.Description.Should().Contain("runner transport unreachable");
|
|
}
|
|
private abstract class FakeQueue<TMessage> : ISchedulerQueue<TMessage>, ISchedulerQueueTransportDiagnostics
|
|
{
|
|
private readonly bool _failPing;
|
|
|
|
protected FakeQueue(bool failPing)
|
|
{
|
|
_failPing = failPing;
|
|
}
|
|
|
|
public ValueTask<SchedulerQueueEnqueueResult> EnqueueAsync(TMessage message, CancellationToken cancellationToken = default)
|
|
=> ValueTask.FromResult(new SchedulerQueueEnqueueResult("stub", false));
|
|
|
|
public ValueTask<IReadOnlyList<ISchedulerQueueLease<TMessage>>> LeaseAsync(SchedulerQueueLeaseRequest request, CancellationToken cancellationToken = default)
|
|
=> ValueTask.FromResult<IReadOnlyList<ISchedulerQueueLease<TMessage>>>(Array.Empty<ISchedulerQueueLease<TMessage>>());
|
|
|
|
public ValueTask<IReadOnlyList<ISchedulerQueueLease<TMessage>>> ClaimExpiredAsync(SchedulerQueueClaimOptions options, CancellationToken cancellationToken = default)
|
|
=> ValueTask.FromResult<IReadOnlyList<ISchedulerQueueLease<TMessage>>>(Array.Empty<ISchedulerQueueLease<TMessage>>());
|
|
|
|
public ValueTask PingAsync(CancellationToken cancellationToken)
|
|
=> _failPing
|
|
? ValueTask.FromException(new InvalidOperationException("ping failed"))
|
|
: ValueTask.CompletedTask;
|
|
}
|
|
|
|
private sealed class FakePlannerQueue : FakeQueue<PlannerQueueMessage>, ISchedulerPlannerQueue
|
|
{
|
|
public FakePlannerQueue(bool failPing) : base(failPing)
|
|
{
|
|
}
|
|
}
|
|
|
|
private sealed class FakeRunnerQueue : FakeQueue<RunnerSegmentQueueMessage>, ISchedulerRunnerQueue
|
|
{
|
|
public FakeRunnerQueue(bool failPing) : base(failPing)
|
|
{
|
|
}
|
|
}
|
|
}
|