feat: add Reachability Center and Why Drawer components with tests
- Implemented ReachabilityCenterComponent for displaying asset reachability status with summary and filtering options. - Added ReachabilityWhyDrawerComponent to show detailed reachability evidence and call paths. - Created unit tests for both components to ensure functionality and correctness. - Updated accessibility test results for the new components.
This commit is contained in:
@@ -3,50 +3,45 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Concelier.Connector.Common.Testing;
|
||||
using StellaOps.Concelier.Connector.Common.Http;
|
||||
using StellaOps.Concelier.Connector.Ics.Cisa;
|
||||
using StellaOps.Concelier.Connector.Ics.Cisa.Configuration;
|
||||
using StellaOps.Concelier.Storage.Mongo;
|
||||
using StellaOps.Concelier.Storage.Mongo.Advisories;
|
||||
using StellaOps.Concelier.Testing;
|
||||
using Xunit;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.Concelier.Connector.Common.Testing;
|
||||
using StellaOps.Concelier.Connector.Ics.Cisa;
|
||||
using StellaOps.Concelier.Connector.Ics.Cisa.Configuration;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Ics.Cisa.Tests;
|
||||
|
||||
[Collection("mongo-fixture")]
|
||||
public sealed class IcsCisaConnectorTests : IAsyncLifetime
|
||||
{
|
||||
private readonly MongoIntegrationFixture _fixture;
|
||||
private readonly CannedHttpMessageHandler _handler = new();
|
||||
[Collection(ConcelierFixtureCollection.Name)]
|
||||
public sealed class IcsCisaConnectorTests
|
||||
{
|
||||
private readonly ConcelierPostgresFixture _fixture;
|
||||
|
||||
public IcsCisaConnectorTests(ConcelierPostgresFixture fixture)
|
||||
{
|
||||
_fixture = fixture ?? throw new ArgumentNullException(nameof(fixture));
|
||||
}
|
||||
|
||||
public IcsCisaConnectorTests(MongoIntegrationFixture fixture)
|
||||
{
|
||||
_fixture = fixture ?? throw new ArgumentNullException(nameof(fixture));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FetchParseMap_EndToEnd_ProducesCanonicalAdvisories()
|
||||
{
|
||||
await using var provider = await BuildServiceProviderAsync();
|
||||
RegisterResponses();
|
||||
|
||||
var connector = provider.GetRequiredService<IcsCisaConnector>();
|
||||
await connector.FetchAsync(provider, CancellationToken.None);
|
||||
await connector.ParseAsync(provider, CancellationToken.None);
|
||||
await connector.MapAsync(provider, CancellationToken.None);
|
||||
|
||||
_handler.AssertNoPendingResponses();
|
||||
|
||||
var advisoryStore = provider.GetRequiredService<IAdvisoryStore>();
|
||||
var advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
|
||||
[Fact]
|
||||
public async Task FetchParseMap_EndToEnd_ProducesCanonicalAdvisories()
|
||||
{
|
||||
await using var harness = await BuildHarnessAsync();
|
||||
RegisterResponses(harness.Handler);
|
||||
|
||||
var connector = harness.ServiceProvider.GetRequiredService<IcsCisaConnector>();
|
||||
await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None);
|
||||
await connector.ParseAsync(harness.ServiceProvider, CancellationToken.None);
|
||||
await connector.MapAsync(harness.ServiceProvider, CancellationToken.None);
|
||||
|
||||
harness.Handler.AssertNoPendingResponses();
|
||||
|
||||
var advisoryStore = harness.ServiceProvider.GetRequiredService<IAdvisoryStore>();
|
||||
var advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, advisories.Count);
|
||||
|
||||
@@ -78,78 +73,48 @@ public sealed class IcsCisaConnectorTests : IAsyncLifetime
|
||||
Assert.Contains(icsma.References, reference => reference.Url == "https://www.cisa.gov/sites/default/files/2025-10/ICSMA-25-045-01_Supplement.pdf");
|
||||
var infusionPackage = Assert.Single(icsma.AffectedPackages, package => string.Equals(package.Identifier, "InfusionManager", StringComparison.OrdinalIgnoreCase));
|
||||
var infusionRange = Assert.Single(infusionPackage.VersionRanges);
|
||||
Assert.Equal("2.1", infusionRange.RangeExpression);
|
||||
}
|
||||
|
||||
private async Task<ServiceProvider> BuildServiceProviderAsync()
|
||||
{
|
||||
await _fixture.Client.DropDatabaseAsync(_fixture.Database.DatabaseNamespace.DatabaseName);
|
||||
_handler.Clear();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
|
||||
services.AddSingleton(_handler);
|
||||
|
||||
services.AddMongoStorage(options =>
|
||||
{
|
||||
options.ConnectionString = _fixture.Runner.ConnectionString;
|
||||
options.DatabaseName = _fixture.Database.DatabaseNamespace.DatabaseName;
|
||||
options.CommandTimeout = TimeSpan.FromSeconds(5);
|
||||
});
|
||||
|
||||
services.AddSourceCommon();
|
||||
services.AddIcsCisaConnector(options =>
|
||||
{
|
||||
options.GovDeliveryCode = "TESTCODE";
|
||||
options.TopicsEndpoint = new Uri("https://feed.test/topics.rss", UriKind.Absolute);
|
||||
options.TopicIds.Clear();
|
||||
options.TopicIds.Add("USDHSCISA_TEST");
|
||||
options.RequestDelay = TimeSpan.Zero;
|
||||
options.DetailBaseUri = new Uri("https://www.cisa.gov/", UriKind.Absolute);
|
||||
options.AdditionalHosts.Add("files.cisa.gov");
|
||||
});
|
||||
|
||||
services.Configure<HttpClientFactoryOptions>(IcsCisaOptions.HttpClientName, builder =>
|
||||
{
|
||||
builder.HttpMessageHandlerBuilderActions.Add(handlerBuilder =>
|
||||
{
|
||||
handlerBuilder.PrimaryHandler = _handler;
|
||||
});
|
||||
});
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var bootstrapper = provider.GetRequiredService<MongoBootstrapper>();
|
||||
await bootstrapper.InitializeAsync(CancellationToken.None);
|
||||
return provider;
|
||||
}
|
||||
|
||||
private void RegisterResponses()
|
||||
{
|
||||
var feedUri = new Uri("https://feed.test/topics.rss?code=TESTCODE&format=xml&topic_id=USDHSCISA_TEST", UriKind.Absolute);
|
||||
_handler.AddResponse(feedUri, () => CreateTextResponse("IcsCisa/Fixtures/sample-feed.xml", "application/rss+xml"));
|
||||
|
||||
var icsaDetail = new Uri("https://www.cisa.gov/news-events/ics-advisories/icsa-25-123-01", UriKind.Absolute);
|
||||
_handler.AddResponse(icsaDetail, () => CreateTextResponse("IcsCisa/Fixtures/icsa-25-123-01.html", "text/html"));
|
||||
|
||||
var icsmaDetail = new Uri("https://www.cisa.gov/news-events/ics-medical-advisories/icsma-25-045-01", UriKind.Absolute);
|
||||
_handler.AddResponse(icsmaDetail, () => CreateTextResponse("IcsCisa/Fixtures/icsma-25-045-01.html", "text/html"));
|
||||
}
|
||||
Assert.Equal("2.1", infusionRange.RangeExpression);
|
||||
}
|
||||
|
||||
private async Task<ConnectorTestHarness> BuildHarnessAsync()
|
||||
{
|
||||
var initialTime = new DateTimeOffset(2025, 10, 12, 0, 0, 0, TimeSpan.Zero);
|
||||
var harness = new ConnectorTestHarness(_fixture, initialTime, IcsCisaOptions.HttpClientName);
|
||||
await harness.EnsureServiceProviderAsync(services =>
|
||||
{
|
||||
services.AddIcsCisaConnector(options =>
|
||||
{
|
||||
options.GovDeliveryCode = "TESTCODE";
|
||||
options.TopicsEndpoint = new Uri("https://feed.test/topics.rss", UriKind.Absolute);
|
||||
options.TopicIds.Clear();
|
||||
options.TopicIds.Add("USDHSCISA_TEST");
|
||||
options.RequestDelay = TimeSpan.Zero;
|
||||
options.DetailBaseUri = new Uri("https://www.cisa.gov/", UriKind.Absolute);
|
||||
options.AdditionalHosts.Add("files.cisa.gov");
|
||||
});
|
||||
});
|
||||
return harness;
|
||||
}
|
||||
|
||||
private static void RegisterResponses(CannedHttpMessageHandler handler)
|
||||
{
|
||||
var feedUri = new Uri("https://feed.test/topics.rss?code=TESTCODE&format=xml&topic_id=USDHSCISA_TEST", UriKind.Absolute);
|
||||
handler.AddResponse(feedUri, () => CreateTextResponse("IcsCisa/Fixtures/sample-feed.xml", "application/rss+xml"));
|
||||
|
||||
var icsaDetail = new Uri("https://www.cisa.gov/news-events/ics-advisories/icsa-25-123-01", UriKind.Absolute);
|
||||
handler.AddResponse(icsaDetail, () => CreateTextResponse("IcsCisa/Fixtures/icsa-25-123-01.html", "text/html"));
|
||||
|
||||
var icsmaDetail = new Uri("https://www.cisa.gov/news-events/ics-medical-advisories/icsma-25-045-01", UriKind.Absolute);
|
||||
handler.AddResponse(icsmaDetail, () => CreateTextResponse("IcsCisa/Fixtures/icsma-25-045-01.html", "text/html"));
|
||||
}
|
||||
|
||||
private static HttpResponseMessage CreateTextResponse(string relativePath, string contentType)
|
||||
{
|
||||
var fullPath = Path.Combine(AppContext.BaseDirectory, relativePath);
|
||||
var content = File.ReadAllText(fullPath);
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(content, Encoding.UTF8, contentType),
|
||||
};
|
||||
}
|
||||
|
||||
public Task InitializeAsync() => Task.CompletedTask;
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
_handler.Clear();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(content, Encoding.UTF8, contentType),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user