using System.Net; using System.Text.Json; using JobTrackerApi.Services; using Microsoft.Extensions.Configuration; using Moq; using Xunit; namespace JobTrackerApi.Tests; public sealed class MaintenanceJobDispatcherTests { [Fact] public void Job_is_unavailable_without_a_dispatch_token() { var dispatcher = new MaintenanceJobDispatcher(new ConfigurationBuilder().Build(), Mock.Of()); var job = Assert.Single(dispatcher.GetJobs()); Assert.Equal(MaintenanceJobDispatcher.CvBenchmarkJobId, job.Id); Assert.False(job.Available); Assert.Null(job.RunsUrl); } [Fact] public async Task Dispatch_sends_only_the_registered_workflow_and_job_id() { HttpRequestMessage? captured = null; string? body = null; var handler = new StubHandler(async request => { captured = request; body = request.Content is null ? null : await request.Content.ReadAsStringAsync(); return new HttpResponseMessage(HttpStatusCode.NoContent); }); var factory = new Mock(); factory.Setup(x => x.CreateClient("maintenance-jobs")).Returns(new HttpClient(handler)); var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["MaintenanceJobs:GiteaBaseUrl"] = "https://git.example.test/", ["MaintenanceJobs:GiteaOwner"] = "owner", ["MaintenanceJobs:GiteaRepository"] = "repo", ["MaintenanceJobs:GiteaToken"] = "test-token", }).Build(); var dispatcher = new MaintenanceJobDispatcher(config, factory.Object); await dispatcher.DispatchAsync(MaintenanceJobDispatcher.CvBenchmarkJobId, CancellationToken.None); Assert.NotNull(captured); Assert.Equal(HttpMethod.Post, captured!.Method); Assert.Equal("https://git.example.test/api/v1/repos/owner/repo/actions/workflows/maintenance.yml/dispatches", captured.RequestUri!.ToString()); Assert.Equal("token", captured.Headers.Authorization!.Scheme); using var json = JsonDocument.Parse(body!); Assert.Equal("main", json.RootElement.GetProperty("ref").GetString()); Assert.Equal("cv-benchmark", json.RootElement.GetProperty("inputs").GetProperty("job").GetString()); } [Fact] public async Task Dispatch_rejects_any_unregistered_job_without_an_http_call() { var factory = new Mock(); var dispatcher = new MaintenanceJobDispatcher(new ConfigurationBuilder().Build(), factory.Object); await Assert.ThrowsAsync(() => dispatcher.DispatchAsync("shell", CancellationToken.None)); factory.Verify(x => x.CreateClient(It.IsAny()), Times.Never); } private sealed class StubHandler(Func> send) : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => send(request); } }