Rename Vexer to Excititor

This commit is contained in:
2025-10-18 20:00:46 +03:00
parent fbd1826ef3
commit 7e1b10d3b2
263 changed files with 848 additions and 848 deletions

View File

@@ -0,0 +1,172 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using FluentAssertions;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Excititor.Connectors.Ubuntu.CSAF.Configuration;
using StellaOps.Excititor.Connectors.Ubuntu.CSAF.Metadata;
using System.IO.Abstractions.TestingHelpers;
using Xunit;
using System.Threading;
namespace StellaOps.Excititor.Connectors.Ubuntu.CSAF.Tests.Metadata;
public sealed class UbuntuCatalogLoaderTests
{
private const string SampleIndex = """
{
"generated": "2025-10-10T00:00:00Z",
"channels": [
{
"name": "stable",
"catalogUrl": "https://ubuntu.com/security/csaf/stable/catalog.json",
"sha256": "abc",
"lastUpdated": "2025-10-09T10:00:00Z"
},
{
"name": "esm",
"catalogUrl": "https://ubuntu.com/security/csaf/esm/catalog.json",
"sha256": "def",
"lastUpdated": "2025-10-08T10:00:00Z"
}
]
}
""";
[Fact]
public async Task LoadAsync_FetchesAndCachesIndex()
{
var handler = new TestHttpMessageHandler(new Dictionary<Uri, HttpResponseMessage>
{
[new Uri("https://ubuntu.com/security/csaf/index.json")] = CreateResponse(SampleIndex),
});
var client = new HttpClient(handler);
var factory = new SingleClientHttpClientFactory(client);
var cache = new MemoryCache(new MemoryCacheOptions());
var fileSystem = new MockFileSystem();
var loader = new UbuntuCatalogLoader(factory, cache, fileSystem, NullLogger<UbuntuCatalogLoader>.Instance, new AdjustableTimeProvider());
var options = new UbuntuConnectorOptions
{
IndexUri = new Uri("https://ubuntu.com/security/csaf/index.json"),
OfflineSnapshotPath = "/snapshots/ubuntu-index.json",
};
var result = await loader.LoadAsync(options, CancellationToken.None);
result.Metadata.Channels.Should().HaveCount(1);
result.Metadata.Channels[0].Name.Should().Be("stable");
fileSystem.FileExists(options.OfflineSnapshotPath!).Should().BeTrue();
result.FromCache.Should().BeFalse();
handler.ResetInvocationCount();
var cached = await loader.LoadAsync(options, CancellationToken.None);
cached.FromCache.Should().BeTrue();
handler.InvocationCount.Should().Be(0);
}
[Fact]
public async Task LoadAsync_UsesOfflineSnapshotWhenPreferred()
{
var handler = new TestHttpMessageHandler(new Dictionary<Uri, HttpResponseMessage>());
var client = new HttpClient(handler);
var factory = new SingleClientHttpClientFactory(client);
var cache = new MemoryCache(new MemoryCacheOptions());
var fileSystem = new MockFileSystem();
fileSystem.AddFile("/snapshots/ubuntu-index.json", new MockFileData($"{{\"metadata\":{SampleIndex},\"fetchedAt\":\"2025-10-10T00:00:00Z\"}}"));
var loader = new UbuntuCatalogLoader(factory, cache, fileSystem, NullLogger<UbuntuCatalogLoader>.Instance, new AdjustableTimeProvider());
var options = new UbuntuConnectorOptions
{
IndexUri = new Uri("https://ubuntu.com/security/csaf/index.json"),
OfflineSnapshotPath = "/snapshots/ubuntu-index.json",
PreferOfflineSnapshot = true,
Channels = { "stable" }
};
var result = await loader.LoadAsync(options, CancellationToken.None);
result.FromOfflineSnapshot.Should().BeTrue();
result.Metadata.Channels.Should().NotBeEmpty();
}
[Fact]
public async Task LoadAsync_ThrowsWhenNoChannelsMatch()
{
var handler = new TestHttpMessageHandler(new Dictionary<Uri, HttpResponseMessage>
{
[new Uri("https://ubuntu.com/security/csaf/index.json")] = CreateResponse(SampleIndex),
});
var client = new HttpClient(handler);
var factory = new SingleClientHttpClientFactory(client);
var cache = new MemoryCache(new MemoryCacheOptions());
var fileSystem = new MockFileSystem();
var loader = new UbuntuCatalogLoader(factory, cache, fileSystem, NullLogger<UbuntuCatalogLoader>.Instance, new AdjustableTimeProvider());
var options = new UbuntuConnectorOptions
{
IndexUri = new Uri("https://ubuntu.com/security/csaf/index.json"),
};
options.Channels.Clear();
options.Channels.Add("nonexistent");
await Assert.ThrowsAsync<InvalidOperationException>(() => loader.LoadAsync(options, CancellationToken.None));
}
private static HttpResponseMessage CreateResponse(string payload)
=> new(HttpStatusCode.OK)
{
Content = new StringContent(payload, Encoding.UTF8, "application/json"),
};
private sealed class SingleClientHttpClientFactory : IHttpClientFactory
{
private readonly HttpClient _client;
public SingleClientHttpClientFactory(HttpClient client)
{
_client = client;
}
public HttpClient CreateClient(string name) => _client;
}
private sealed class AdjustableTimeProvider : TimeProvider
{
private DateTimeOffset _now = DateTimeOffset.UtcNow;
public override DateTimeOffset GetUtcNow() => _now;
}
private sealed class TestHttpMessageHandler : HttpMessageHandler
{
private readonly Dictionary<Uri, HttpResponseMessage> _responses;
public TestHttpMessageHandler(Dictionary<Uri, HttpResponseMessage> responses)
{
_responses = responses;
}
public int InvocationCount { get; private set; }
public void ResetInvocationCount() => InvocationCount = 0;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
InvocationCount++;
if (request.RequestUri is not null && _responses.TryGetValue(request.RequestUri, out var response))
{
var payload = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
return new HttpResponseMessage(response.StatusCode)
{
Content = new StringContent(payload, Encoding.UTF8, "application/json"),
};
}
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("unexpected request"),
};
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.Excititor.Connectors.Ubuntu.CSAF\StellaOps.Excititor.Connectors.Ubuntu.CSAF.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="20.0.28" />
</ItemGroup>
</Project>