Refactor code structure and optimize performance across multiple modules

This commit is contained in:
StellaOps Bot
2025-12-26 20:03:22 +02:00
parent c786faae84
commit b4fc66feb6
3353 changed files with 88254 additions and 1590657 deletions

View File

@@ -8,7 +8,7 @@
<ItemGroup Condition="$([System.String]::Copy('$(MSBuildProjectName)').EndsWith('.Tests')) and '$(UseConcelierTestInfra)'=='true'">
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0-rc.2.25502.107" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.0.0" />

View File

@@ -8,11 +8,13 @@
using FluentAssertions;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Cache.Valkey.Tests;
public class AdvisoryCacheKeysTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Advisory_WithDefaultPrefix_GeneratesCorrectKey()
{
// Arrange
@@ -25,7 +27,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:advisory:abc123def456");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Advisory_WithCustomPrefix_GeneratesCorrectKey()
{
// Arrange
@@ -39,7 +42,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("custom:advisory:abc123def456");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void HotSet_WithDefaultPrefix_GeneratesCorrectKey()
{
// Act
@@ -49,7 +53,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:rank:hot");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ByPurl_NormalizesPurl()
{
// Arrange
@@ -62,7 +67,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:by:purl:pkg:npm/@angular/core@12.0.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ByPurl_NormalizesToLowercase()
{
// Arrange
@@ -75,7 +81,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:by:purl:pkg:npm/@angular/core@12.0.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ByCve_NormalizesToUppercase()
{
// Arrange
@@ -88,7 +95,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:by:cve:CVE-2024-1234");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void StatsHits_GeneratesCorrectKey()
{
// Act
@@ -98,7 +106,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:cache:stats:hits");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void StatsMisses_GeneratesCorrectKey()
{
// Act
@@ -108,7 +117,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:cache:stats:misses");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void WarmupLast_GeneratesCorrectKey()
{
// Act
@@ -118,7 +128,8 @@ public class AdvisoryCacheKeysTests
key.Should().Be("concelier:cache:warmup:last");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void NormalizePurl_HandlesEmptyString()
{
// Act
@@ -128,7 +139,8 @@ public class AdvisoryCacheKeysTests
result.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void NormalizePurl_HandlesNull()
{
// Act
@@ -138,7 +150,8 @@ public class AdvisoryCacheKeysTests
result.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void NormalizePurl_ReplacesSpecialCharacters()
{
// Arrange - PURL with unusual characters
@@ -152,7 +165,8 @@ public class AdvisoryCacheKeysTests
result.Should().Be("pkg:npm/test_query_value_fragment");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void NormalizePurl_TruncatesLongPurls()
{
// Arrange - Very long PURL
@@ -165,7 +179,8 @@ public class AdvisoryCacheKeysTests
result.Length.Should().BeLessThanOrEqualTo(500);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExtractMergeHash_ReturnsHashFromAdvisoryKey()
{
// Arrange
@@ -178,7 +193,8 @@ public class AdvisoryCacheKeysTests
result.Should().Be("abc123def456");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExtractMergeHash_ReturnsNullForInvalidKey()
{
// Arrange
@@ -191,7 +207,8 @@ public class AdvisoryCacheKeysTests
result.Should().BeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExtractPurl_ReturnsPurlFromIndexKey()
{
// Arrange
@@ -204,7 +221,8 @@ public class AdvisoryCacheKeysTests
result.Should().Be("pkg:npm/test@1.0.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExtractCve_ReturnsCveFromMappingKey()
{
// Arrange
@@ -217,7 +235,8 @@ public class AdvisoryCacheKeysTests
result.Should().Be("CVE-2024-1234");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AdvisoryPattern_GeneratesCorrectPattern()
{
// Act
@@ -227,7 +246,8 @@ public class AdvisoryCacheKeysTests
pattern.Should().Be("concelier:advisory:*");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PurlIndexPattern_GeneratesCorrectPattern()
{
// Act
@@ -237,7 +257,8 @@ public class AdvisoryCacheKeysTests
pattern.Should().Be("concelier:by:purl:*");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CveMappingPattern_GeneratesCorrectPattern()
{
// Act

View File

@@ -8,11 +8,13 @@
using FluentAssertions;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Cache.Valkey.Tests;
public class CacheTtlPolicyTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithHighScore_ReturnsHighScoreTtl()
{
// Arrange
@@ -25,7 +27,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(24));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithScoreAtHighThreshold_ReturnsHighScoreTtl()
{
// Arrange
@@ -38,7 +41,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(24));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithMediumScore_ReturnsMediumScoreTtl()
{
// Arrange
@@ -51,7 +55,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(4));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithScoreAtMediumThreshold_ReturnsMediumScoreTtl()
{
// Arrange
@@ -64,7 +69,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(4));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithLowScore_ReturnsLowScoreTtl()
{
// Arrange
@@ -77,7 +83,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(1));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithZeroScore_ReturnsLowScoreTtl()
{
// Arrange
@@ -90,7 +97,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(1));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithNullScore_ReturnsLowScoreTtl()
{
// Arrange
@@ -103,7 +111,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(1));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithCustomThresholds_UsesCustomValues()
{
// Arrange
@@ -122,7 +131,8 @@ public class CacheTtlPolicyTests
policy.GetTtl(0.3).Should().Be(TimeSpan.FromMinutes(30));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void DefaultValues_AreCorrect()
{
// Arrange
@@ -138,7 +148,8 @@ public class CacheTtlPolicyTests
policy.CveMappingTtl.Should().Be(TimeSpan.FromHours(24));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithScoreBelowMediumThreshold_ReturnsLowScoreTtl()
{
// Arrange
@@ -151,7 +162,8 @@ public class CacheTtlPolicyTests
ttl.Should().Be(TimeSpan.FromHours(1));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetTtl_WithScoreBelowHighThreshold_ReturnsMediumScoreTtl()
{
// Arrange

View File

@@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\..\__Libraries\StellaOps.Concelier.Cache.Valkey\StellaOps.Concelier.Cache.Valkey.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -16,6 +16,8 @@ using StellaOps.Concelier.Storage.Advisories;
using StellaOps.Concelier.Testing;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Cccs.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -31,7 +33,8 @@ public sealed class CccsConnectorTests
_fixture = fixture;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ProducesCanonicalAdvisory()
{
await using var harness = await BuildHarnessAsync();
@@ -65,7 +68,8 @@ public sealed class CccsConnectorTests
pendingMappings!.AsDocumentArray.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Fetch_PersistsRawDocumentWithMetadata()
{
await using var harness = await BuildHarnessAsync();

View File

@@ -8,6 +8,7 @@
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Cccs/StellaOps.Concelier.Connector.Cccs.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />

View File

@@ -16,6 +16,8 @@ using StellaOps.Concelier.Storage.Advisories;
using StellaOps.Concelier.Testing;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.CertBund.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -32,7 +34,8 @@ public sealed class CertBundConnectorTests
_fixture = fixture;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ProducesCanonicalAdvisory()
{
await using var harness = await BuildHarnessAsync();
@@ -75,7 +78,8 @@ public sealed class CertBundConnectorTests
pendingMappings!.AsDocumentArray.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Fetch_PersistsDocumentWithMetadata()
{
await using var harness = await BuildHarnessAsync();

View File

@@ -8,6 +8,7 @@
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.CertBund/StellaOps.Concelier.Connector.CertBund.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />

View File

@@ -12,6 +12,8 @@ using StellaOps.Concelier.Storage.Advisories;
using StellaOps.Concelier.Testing;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Alpine.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -25,7 +27,8 @@ public sealed class AlpineConnectorTests
_fixture = fixture;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_StoresAdvisoriesAndUpdatesCursor()
{
await using var harness = await BuildHarnessAsync();

View File

@@ -10,11 +10,14 @@ using StellaOps.Concelier.Connector.Distro.Alpine;
using StellaOps.Concelier.Connector.Distro.Alpine.Configuration;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Alpine.Tests;
public sealed class AlpineDependencyInjectionRoutineTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Register_ConfiguresOptionsAndScheduler()
{
var services = new ServiceCollection();

View File

@@ -9,11 +9,13 @@ using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Alpine.Tests;
public sealed class AlpineMapperTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Map_BuildsApkAdvisoriesWithRanges()
{
var dto = new AlpineSecDbDto(

View File

@@ -2,11 +2,13 @@ using System.Linq;
using StellaOps.Concelier.Connector.Distro.Alpine.Dto;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Alpine.Tests;
public sealed class AlpineSecDbParserTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Parse_SecDbFixture_ExtractsPackagesAndMetadata()
{
var dto = AlpineFixtureReader.LoadDto("v3.20-main.json");

View File

@@ -10,11 +10,13 @@ using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Alpine.Tests;
public sealed class AlpineSnapshotTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("v3.18-main.json", "alpine-v3.18-main.snapshot.json", "2025-12-22T00:00:00Z")]
[InlineData("v3.19-main.json", "alpine-v3.19-main.snapshot.json", "2025-12-22T00:10:00Z")]
[InlineData("v3.20-main.json", "alpine-v3.20-main.snapshot.json", "2025-12-22T00:20:00Z")]

View File

@@ -11,6 +11,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj" />
<ProjectReference Include="../../../__Tests/__Libraries/StellaOps.Concelier.Testing/StellaOps.Concelier.Testing.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Source\Distro\Alpine\Fixtures\**\*">

View File

@@ -28,6 +28,8 @@ using StellaOps.Concelier.Testing;
using Xunit;
using Xunit.Abstractions;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Debian.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -65,7 +67,8 @@ public sealed class DebianConnectorTests : IAsyncLifetime
_output = output;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_PopulatesRangePrimitivesAndResumesWithNotModified()
{
await using var provider = await BuildServiceProviderAsync();

View File

@@ -5,11 +5,13 @@ using StellaOps.Concelier.Connector.Distro.Debian;
using StellaOps.Concelier.Connector.Distro.Debian.Internal;
using StellaOps.Concelier.Storage;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Debian.Tests;
public sealed class DebianMapperTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Map_BuildsRangePrimitives_ForResolvedPackage()
{
var dto = new DebianAdvisoryDto(

View File

@@ -9,5 +9,6 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Distro.Debian/StellaOps.Concelier.Connector.Distro.Debian.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -9,6 +9,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Distro.Suse/StellaOps.Concelier.Connector.Distro.Suse.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Source\Distro\Suse\Fixtures\**\*">

View File

@@ -19,6 +19,8 @@ using StellaOps.Concelier.Testing;
using Xunit;
using Xunit.Abstractions;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Suse.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -35,7 +37,8 @@ public sealed class SuseConnectorTests
_fixture = fixture;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ProcessesResolvedAndOpenNotices()
{
await using var harness = await BuildHarnessAsync();

View File

@@ -5,11 +5,13 @@ using System.Text.Json;
using StellaOps.Concelier.Connector.Distro.Suse.Internal;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Suse.Tests;
public sealed class SuseCsafParserTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Parse_ProducesRecommendedAndAffectedPackages()
{
var json = ReadFixture("Source/Distro/Suse/Fixtures/suse-su-2025_0001-1.json");
@@ -25,7 +27,8 @@ public sealed class SuseCsafParserTests
Assert.Equal("openssl-1.1.1w-150500.17.25.1.x86_64", package.CanonicalNevra);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Parse_HandlesOpenInvestigation()
{
var json = ReadFixture("Source/Distro/Suse/Fixtures/suse-su-2025_0002-1.json");

View File

@@ -9,11 +9,13 @@ using StellaOps.Concelier.Connector.Distro.Suse.Internal;
using StellaOps.Concelier.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Suse.Tests;
public sealed class SuseMapperTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Map_BuildsNevraRangePrimitives()
{
var json = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Source", "Distro", "Suse", "Fixtures", "suse-su-2025_0001-1.json"));

View File

@@ -10,6 +10,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Fixtures\**\*">

View File

@@ -19,6 +19,8 @@ using StellaOps.Concelier.Testing;
using StellaOps.Cryptography.DependencyInjection;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Distro.Ubuntu.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -34,7 +36,8 @@ public sealed class UbuntuConnectorTests
_fixture = fixture;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_GeneratesEvrRangePrimitives()
{
await using var harness = await BuildHarnessAsync();

View File

@@ -23,7 +23,8 @@ namespace StellaOps.Concelier.Connector.Epss.Tests;
public sealed class EpssConnectorTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchAsync_StoresDocument_OnSuccess()
{
var options = CreateOptions();
@@ -61,7 +62,8 @@ public sealed class EpssConnectorTests
Assert.Contains(record.Id, cursor.PendingDocuments);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchAsync_ReturnsNotModified_OnEtagMatch()
{
var options = CreateOptions();
@@ -112,7 +114,8 @@ public sealed class EpssConnectorTests
Assert.Empty(cursor.PendingDocuments);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ParseAsync_CreatesDto_AndUpdatesStatus()
{
var options = CreateOptions();
@@ -163,7 +166,8 @@ public sealed class EpssConnectorTests
Assert.Equal(DocumentStatuses.PendingMap, updated!.Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MapAsync_MarksDocumentMapped()
{
var options = CreateOptions();
@@ -228,7 +232,8 @@ public sealed class EpssConnectorTests
Assert.Equal(DocumentStatuses.Mapped, updated!.Status);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(0.75, EpssBand.Critical)]
[InlineData(0.55, EpssBand.High)]
[InlineData(0.25, EpssBand.Medium)]
@@ -242,7 +247,8 @@ public sealed class EpssConnectorTests
Assert.Equal(expected, observation.Band);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EpssCursor_Empty_UsesMinValue()
{
var cursor = EpssCursor.Empty;
@@ -331,6 +337,7 @@ public sealed class EpssConnectorTests
foreach (var line in lines)
{
writer.WriteLine(line);
using StellaOps.TestKit;
}
}

View File

@@ -15,6 +15,8 @@ using StellaOps.Concelier.Storage.Advisories;
using StellaOps.Concelier.Testing;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Ics.Cisa.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -27,7 +29,8 @@ public sealed class IcsCisaConnectorTests
_fixture = fixture ?? throw new ArgumentNullException(nameof(fixture));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_EndToEnd_ProducesCanonicalAdvisories()
{
await using var harness = await BuildHarnessAsync();

View File

@@ -9,6 +9,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Ics.Cisa/StellaOps.Concelier.Connector.Ics.Cisa.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="IcsCisa/Fixtures/**" CopyToOutputDirectory="Always" />

View File

@@ -30,6 +30,8 @@ using StellaOps.Concelier.Testing;
using Xunit;
using Xunit.Abstractions;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Kisa.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -49,7 +51,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
_output = output;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ProducesCanonicalAdvisory()
{
await using var provider = await BuildServiceProviderAsync();
@@ -104,7 +107,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
pendingMappings!.AsDocumentArray.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ExclusiveUpperBound_ProducesExclusiveNormalizedRule()
{
await using var provider = await BuildServiceProviderAsync();
@@ -136,7 +140,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
.WhoseValue.Should().Be(">= 3.2.0 < 4.0.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ExclusiveLowerBound_ProducesExclusiveNormalizedRule()
{
await using var provider = await BuildServiceProviderAsync();
@@ -169,7 +174,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
.WhoseValue.Should().Be("> 1.2.0 <= 2.4.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_SingleBound_ProducesMinimumOnlyConstraint()
{
await using var provider = await BuildServiceProviderAsync();
@@ -208,7 +214,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
.WhoseValue.Should().Be(">= 5.0.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_UpperBoundOnlyExclusive_ProducesLessThanRule()
{
await using var provider = await BuildServiceProviderAsync();
@@ -241,7 +248,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
.WhoseValue.Should().Be("< 3.5.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_UpperBoundOnlyInclusive_ProducesLessThanOrEqualRule()
{
await using var provider = await BuildServiceProviderAsync();
@@ -273,7 +281,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
.WhoseValue.Should().Be("<= 4.2.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_LowerBoundOnlyExclusive_ProducesGreaterThanRule()
{
await using var provider = await BuildServiceProviderAsync();
@@ -306,7 +315,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
.WhoseValue.Should().Be("> 1.9.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_InvalidSegment_ProducesFallbackRange()
{
await using var provider = await BuildServiceProviderAsync();
@@ -332,7 +342,8 @@ public sealed class KisaConnectorTests : IAsyncLifetime
.WhoseValue.Should().Be("지원 버전: 최신 업데이트 적용");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Telemetry_RecordsMetrics()
{
await using var provider = await BuildServiceProviderAsync();

View File

@@ -8,6 +8,7 @@ using StellaOps.Concelier.Connector.Common.Html;
using StellaOps.Concelier.Connector.Kisa.Internal;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Kisa.Tests;
public sealed class KisaDetailParserTests
@@ -15,7 +16,8 @@ public sealed class KisaDetailParserTests
private static readonly Uri DetailApiUri = new("https://test.local/rssDetailData.do?IDX=5868");
private static readonly Uri DetailPageUri = new("https://test.local/detailDos.do?IDX=5868");
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseHtmlPayload_ProducesExpectedModels()
{
var parser = new KisaDetailParser(new HtmlContentSanitizer());

View File

@@ -10,6 +10,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../StellaOps.Concelier.Connector.Common.Tests/StellaOps.Concelier.Connector.Common.Tests.csproj" />
<ProjectReference Include="../../../__Tests/__Libraries/StellaOps.Concelier.Testing/StellaOps.Concelier.Testing.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />

View File

@@ -28,6 +28,8 @@ using StellaOps.Cryptography.DependencyInjection;
using Xunit;
using Xunit.Sdk;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Ru.Bdu.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -44,7 +46,8 @@ public sealed class RuBduConnectorSnapshotTests : IAsyncLifetime
_fixture = fixture;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ProducesDeterministicSnapshots()
{
var harness = await EnsureHarnessAsync();

View File

@@ -6,11 +6,13 @@ using StellaOps.Concelier.Connector.Ru.Bdu.Internal;
using StellaOps.Concelier.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Ru.Bdu.Tests;
public sealed class RuBduMapperTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Map_ConstructsCanonicalAdvisory()
{
var dto = new RuBduVulnerabilityDto(

View File

@@ -3,11 +3,13 @@ using System.Xml.Linq;
using StellaOps.Concelier.Connector.Ru.Bdu.Internal;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Ru.Bdu.Tests;
public sealed class RuBduXmlParserTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryParse_ValidElement_ReturnsDto()
{
const string xml = """
@@ -75,7 +77,8 @@ public sealed class RuBduXmlParserTests
Assert.Contains(dto.Identifiers, identifier => identifier.Type == "GHSA" && identifier.Link == "https://github.com/advisories/GHSA-xxxx-yyyy-zzzz");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryParse_SampleArchiveEntries_ReturnDtos()
{
var path = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "Fixtures", "export-sample.xml"));

View File

@@ -11,5 +11,6 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Ru.Bdu/StellaOps.Concelier.Connector.Ru.Bdu.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -28,6 +28,8 @@ using StellaOps.Concelier.Storage.Postgres;
using StellaOps.Cryptography.DependencyInjection;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Ru.Nkcki.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -49,7 +51,8 @@ public sealed class RuNkckiConnectorTests : IAsyncLifetime
_handler = new CannedHttpMessageHandler();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ProducesExpectedSnapshot()
{
await using var provider = await BuildServiceProviderAsync();
@@ -80,7 +83,8 @@ public sealed class RuNkckiConnectorTests : IAsyncLifetime
Assert.True(IsEmptyArray(state.Cursor, "pendingMappings"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Fetch_ReusesCachedBulletinWhenListingFails()
{
await using var provider = await BuildServiceProviderAsync();

View File

@@ -2,11 +2,14 @@ using System.Text.Json;
using StellaOps.Concelier.Connector.Ru.Nkcki.Internal;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Ru.Nkcki.Tests;
public sealed class RuNkckiJsonParserTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Parse_WellFormedEntry_ReturnsDto()
{
const string json = """

View File

@@ -7,11 +7,13 @@ using StellaOps.Concelier.Storage;
using Xunit;
using System.Reflection;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Ru.Nkcki.Tests;
public sealed class RuNkckiMapperTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Map_ConstructsCanonicalAdvisory()
{
var softwareEntries = ImmutableArray.Create(

View File

@@ -11,5 +11,6 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Ru.Nkcki/StellaOps.Concelier.Connector.Ru.Nkcki.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -3,11 +3,13 @@ using StellaOps.Concelier.Connector.StellaOpsMirror.Internal;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Tests;
public sealed class MirrorAdvisoryMapperTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Map_ProducesCanonicalAdvisoryWithMirrorProvenance()
{
var bundle = SampleData.CreateBundle();

View File

@@ -8,11 +8,14 @@ using StellaOps.Concelier.Connector.StellaOpsMirror.Security;
using StellaOps.Cryptography;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Tests;
public sealed class MirrorSignatureVerifierTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_ValidSignaturePasses()
{
var provider = new DefaultCryptoProvider();
@@ -29,7 +32,8 @@ public sealed class MirrorSignatureVerifierTests
await verifier.VerifyAsync(payload, signature, CancellationToken.None);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_InvalidSignatureThrows()
{
var provider = new DefaultCryptoProvider();
@@ -48,7 +52,8 @@ public sealed class MirrorSignatureVerifierTests
await Assert.ThrowsAsync<InvalidOperationException>(() => verifier.VerifyAsync(payload, tampered, CancellationToken.None));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_KeyMismatchThrows()
{
var provider = new DefaultCryptoProvider();
@@ -71,7 +76,8 @@ public sealed class MirrorSignatureVerifierTests
cancellationToken: CancellationToken.None));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_ThrowsWhenProviderMissingKey()
{
var provider = new DefaultCryptoProvider();
@@ -96,7 +102,8 @@ public sealed class MirrorSignatureVerifierTests
cancellationToken: CancellationToken.None));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_UsesCachedPublicKeyWhenFileRemoved()
{
var provider = new DefaultCryptoProvider();

View File

@@ -9,6 +9,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="Fixtures\**\*.json" CopyToOutputDirectory="Always" />

View File

@@ -29,6 +29,8 @@ using StellaOps.Cryptography.DependencyInjection;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.StellaOpsMirror.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -43,7 +45,8 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
_handler = new CannedHttpMessageHandler();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchAsync_PersistsMirrorArtifacts()
{
var manifestContent = "{\"domain\":\"primary\",\"files\":[]}";
@@ -105,7 +108,8 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
Assert.Empty(pendingMappingsArray);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchAsync_TamperedSignatureThrows()
{
var manifestContent = "{\"domain\":\"primary\"}";
@@ -142,7 +146,8 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
Assert.False(state.Cursor.TryGetValue("bundleDigest", out _));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchAsync_SignatureKeyMismatchThrows()
{
var manifestContent = "{\"domain\":\"primary\"}";
@@ -175,7 +180,8 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
await Assert.ThrowsAsync<InvalidOperationException>(() => connector.FetchAsync(provider, CancellationToken.None));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchAsync_VerifiesSignatureUsingFallbackPublicKey()
{
var manifestContent = "{\"domain\":\"primary\"}";
@@ -218,7 +224,8 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchAsync_DigestMismatchMarksFailure()
{
var manifestExpected = "{\"domain\":\"primary\"}";
@@ -245,7 +252,8 @@ public sealed class StellaOpsMirrorConnectorTests : IAsyncLifetime
Assert.False(cursor.Contains("bundleDigest"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseAndMap_PersistAdvisoriesFromBundle()
{
var bundleDocument = SampleData.CreateBundle();

View File

@@ -6,11 +6,13 @@ using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Concelier.Connector.Vndr.Cisco.Internal;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Vndr.Cisco.Tests;
public sealed class CiscoDtoFactoryTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateAsync_MergesRawAndCsafProducts()
{
const string CsafPayload = @"

View File

@@ -11,11 +11,13 @@ using StellaOps.Concelier.Storage;
using StellaOps.Concelier.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Vndr.Cisco.Tests;
public sealed class CiscoMapperTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Map_ProducesCanonicalAdvisory()
{
var published = new DateTimeOffset(2025, 10, 1, 0, 0, 0, TimeSpan.Zero);

View File

@@ -25,6 +25,8 @@ using StellaOps.Concelier.Testing;
using Xunit;
using StellaOps.Concelier.Connector.Common.Http;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Connector.Vndr.Msrc.Tests;
[Collection(ConcelierFixtureCollection.Name)]
@@ -43,7 +45,8 @@ public sealed class MsrcConnectorTests : IAsyncLifetime
_handler = new CannedHttpMessageHandler();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task FetchParseMap_ProducesCanonicalAdvisory()
{
await using var provider = await BuildServiceProviderAsync();

View File

@@ -10,6 +10,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Vndr.Msrc/StellaOps.Concelier.Connector.Vndr.Msrc.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
<ProjectReference Include="../../../__Tests/__Libraries/StellaOps.Concelier.Testing/StellaOps.Concelier.Testing.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,12 +1,14 @@
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Core.Tests;
public sealed class CanonicalMergerTests
{
private static readonly DateTimeOffset BaseTimestamp = new(2025, 10, 10, 0, 0, 0, TimeSpan.Zero);
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_PrefersGhsaTitleAndSummaryByPrecedence()
{
var merger = new CanonicalMerger(new FixedTimeProvider(BaseTimestamp.AddHours(6)));
@@ -42,7 +44,8 @@ public sealed class CanonicalMergerTests
string.Equals(provenance.DecisionReason, "precedence", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_FreshnessOverrideUsesOsvSummaryWhenNewerByThreshold()
{
var merger = new CanonicalMerger(new FixedTimeProvider(BaseTimestamp.AddHours(10)));
@@ -77,7 +80,8 @@ public sealed class CanonicalMergerTests
string.Equals(provenance.DecisionReason, "freshness_override", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_AffectedPackagesPreferOsvPrecedence()
{
var merger = new CanonicalMerger(new FixedTimeProvider(BaseTimestamp.AddHours(4)));
@@ -163,7 +167,8 @@ public sealed class CanonicalMergerTests
string.Equals(decision.DecisionReason, "precedence", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_CvssMetricsOrderedByPrecedence()
{
var merger = new CanonicalMerger(new FixedTimeProvider(BaseTimestamp.AddHours(5)));
@@ -184,7 +189,8 @@ public sealed class CanonicalMergerTests
Assert.Equal("3.1|CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H", result.Advisory.CanonicalMetricId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_ReferencesNormalizedAndFreshnessOverrides()
{
var merger = new CanonicalMerger(new FixedTimeProvider(BaseTimestamp.AddHours(80)));
@@ -234,7 +240,8 @@ public sealed class CanonicalMergerTests
Assert.Contains("https://example.com/path/resource?a=1&b=2", itemDecision.Field, StringComparison.OrdinalIgnoreCase);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_DescriptionFreshnessOverride()
{
var merger = new CanonicalMerger(new FixedTimeProvider(BaseTimestamp.AddHours(12)));
@@ -272,7 +279,8 @@ public sealed class CanonicalMergerTests
string.Equals(decision.DecisionReason, "freshness_override", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_CwesPreferNvdPrecedence()
{
var merger = new CanonicalMerger(new FixedTimeProvider(BaseTimestamp.AddHours(6)));

View File

@@ -6,11 +6,14 @@ using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Concelier.Core.Jobs;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Core.Tests;
public sealed class JobCoordinatorTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TriggerAsync_RunCompletesSuccessfully()
{
var services = new ServiceCollection();
@@ -56,7 +59,8 @@ public sealed class JobCoordinatorTests
Assert.Equal("bar", completed.Parameters["foo"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TriggerAsync_MarksRunFailed_WhenLeaseReleaseFails()
{
var services = new ServiceCollection();
@@ -106,7 +110,8 @@ public sealed class JobCoordinatorTests
Assert.True(leaseStore.ReleaseAttempts > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TriggerAsync_MarksRunFailed_WhenLeaseHeartbeatFails()
{
var services = new ServiceCollection();
@@ -156,7 +161,8 @@ public sealed class JobCoordinatorTests
Assert.True(leaseStore.HeartbeatCount > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TriggerAsync_ReturnsAlreadyRunning_WhenLeaseUnavailable()
{
var services = new ServiceCollection();
@@ -195,7 +201,8 @@ public sealed class JobCoordinatorTests
Assert.False(jobStore.CreatedRuns.Any());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TriggerAsync_ReturnsInvalidParameters_ForUnsupportedPayload()
{
var services = new ServiceCollection();
@@ -237,7 +244,8 @@ public sealed class JobCoordinatorTests
Assert.False(jobStore.CreatedRuns.Any());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TriggerAsync_CancelsJobOnTimeout()
{
var services = new ServiceCollection();

View File

@@ -7,11 +7,14 @@ using Microsoft.Extensions.Options;
using StellaOps.Concelier.Core.Jobs;
using StellaOps.Plugin.Hosting;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Core.Tests;
public sealed class JobPluginRegistrationExtensionsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RegisterJobPluginRoutines_LoadsPluginsAndRegistersDefinitions()
{
var services = new ServiceCollection();

View File

@@ -3,11 +3,14 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using StellaOps.Concelier.Core.Jobs;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Core.Tests;
public sealed class JobSchedulerBuilderTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AddJob_RegistersDefinitionWithExplicitMetadata()
{
var services = new ServiceCollection();
@@ -32,7 +35,8 @@ public sealed class JobSchedulerBuilderTests
Assert.False(definition.Enabled);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AddJob_UsesDefaults_WhenOptionalMetadataExcluded()
{
var services = new ServiceCollection();

View File

@@ -11,6 +11,7 @@ using StellaOps.Concelier.Exporter.Json;
using StellaOps.Concelier.Models;
using StellaOps.Cryptography;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.Json.Tests;
public sealed class JsonExportSnapshotBuilderTests : IDisposable
@@ -22,7 +23,8 @@ public sealed class JsonExportSnapshotBuilderTests : IDisposable
_root = Directory.CreateTempSubdirectory("concelier-json-export-tests").FullName;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task WritesDeterministicTree()
{
var options = new JsonExportOptions { OutputRoot = _root };
@@ -62,7 +64,8 @@ public sealed class JsonExportSnapshotBuilderTests : IDisposable
Assert.Equal(SnapshotSerializer.ToSnapshot(advisories[0]), actualJson);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ProducesIdenticalBytesAcrossRuns()
{
var options = new JsonExportOptions { OutputRoot = _root };
@@ -82,7 +85,8 @@ public sealed class JsonExportSnapshotBuilderTests : IDisposable
Assert.Equal(Convert.ToHexString(firstDigest), Convert.ToHexString(secondDigest));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task WriteAsync_NormalizesInputOrdering()
{
var options = new JsonExportOptions { OutputRoot = _root };
@@ -98,7 +102,8 @@ public sealed class JsonExportSnapshotBuilderTests : IDisposable
Assert.Equal(expectedOrder, result.FilePaths.ToArray());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task WriteAsync_DifferentInputOrderProducesSameDigest()
{
var options = new JsonExportOptions { OutputRoot = _root };
@@ -124,7 +129,8 @@ public sealed class JsonExportSnapshotBuilderTests : IDisposable
Convert.ToHexString(ComputeDigest(second)));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task WriteAsync_EnumeratesStreamOnlyOnce()
{
var options = new JsonExportOptions { OutputRoot = _root };

View File

@@ -16,11 +16,14 @@ using StellaOps.Cryptography;
using StellaOps.Cryptography.DependencyInjection;
using StellaOps.Provenance;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.Json.Tests;
public sealed class JsonExporterDependencyInjectionRoutineTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Register_AddsJobDefinitionAndServices()
{
var services = new ServiceCollection();

View File

@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using StellaOps.Concelier.Exporter.Json;
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.Json.Tests;
public sealed class JsonExporterParitySmokeTests : IDisposable
@@ -19,7 +20,8 @@ public sealed class JsonExporterParitySmokeTests : IDisposable
_root = Directory.CreateTempSubdirectory("concelier-json-parity-tests").FullName;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportProducesVulnListCompatiblePaths()
{
var options = new JsonExportOptions { OutputRoot = _root };

View File

@@ -22,6 +22,8 @@ using StellaOps.Provenance;
using StellaOps.Cryptography;
using StellaOps.Cryptography.DependencyInjection;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.Json.Tests;
public sealed class JsonFeedExporterTests : IDisposable
@@ -33,7 +35,8 @@ public sealed class JsonFeedExporterTests : IDisposable
_root = Directory.CreateTempSubdirectory("concelier-json-exporter-tests").FullName;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_SkipsWhenDigestUnchanged()
{
var advisory = new Advisory(
@@ -94,7 +97,8 @@ public sealed class JsonFeedExporterTests : IDisposable
Assert.False(Directory.Exists(secondExportPath));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_WritesManifestMetadata()
{
var exportedAt = DateTimeOffset.Parse("2024-08-10T00:00:00Z", CultureInfo.InvariantCulture);
@@ -246,7 +250,8 @@ public sealed class JsonFeedExporterTests : IDisposable
Assert.Equal(ExporterVersion.GetVersion(typeof(JsonFeedExporter)), exporterVersion);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_WritesMirrorBundlesWithSignatures()
{
var exportedAt = DateTimeOffset.Parse("2025-01-05T00:00:00Z", CultureInfo.InvariantCulture);

View File

@@ -10,5 +10,6 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -4,13 +4,15 @@ using System.IO;
using StellaOps.Concelier.Exporter.Json;
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.Json.Tests;
public sealed class VulnListJsonExportPathResolverTests
{
private static readonly DateTimeOffset DefaultPublished = DateTimeOffset.Parse("2024-01-01T00:00:00Z", CultureInfo.InvariantCulture);
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesCvePath()
{
var advisory = CreateAdvisory("CVE-2024-1234");
@@ -21,7 +23,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("nvd", "2024", "CVE-2024-1234.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesGhsaWithPackage()
{
var package = new AffectedPackage(
@@ -43,7 +46,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("ghsa", "go", "github.com%2Facme%2Fwidget", "GHSA-AAAA-BBBB-CCCC.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesUbuntuUsn()
{
var advisory = CreateAdvisory("USN-6620-1");
@@ -53,7 +57,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("ubuntu", "USN-6620-1.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesDebianDla()
{
var advisory = CreateAdvisory("DLA-1234-1");
@@ -63,7 +68,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("debian", "DLA-1234-1.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesRedHatRhsa()
{
var advisory = CreateAdvisory("RHSA-2024:0252");
@@ -73,7 +79,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("redhat", "oval", "RHSA-2024_0252.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesAmazonAlas()
{
var advisory = CreateAdvisory("ALAS2-2024-1234");
@@ -83,7 +90,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("amazon", "2", "ALAS2-2024-1234.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesOracleElsa()
{
var advisory = CreateAdvisory("ELSA-2024-12345");
@@ -93,7 +101,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("oracle", "linux", "ELSA-2024-12345.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesRockyRlsa()
{
var advisory = CreateAdvisory("RLSA-2024:0417");
@@ -103,7 +112,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("rocky", "RLSA-2024_0417.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesByProvenanceFallback()
{
var provenance = new[] { new AdvisoryProvenance("wolfi", "map", "", DefaultPublished) };
@@ -114,7 +124,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("wolfi", "WOLFI-2024-0001.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolvesAcscByProvenance()
{
var provenance = new[] { new AdvisoryProvenance("acsc", "mapping", "acsc-2025-010", DefaultPublished) };
@@ -125,7 +136,8 @@ public sealed class VulnListJsonExportPathResolverTests
Assert.Equal(Path.Combine("cert", "au", "acsc-2025-010.json"), path);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void DefaultsToMiscWhenUnmapped()
{
var advisory = CreateAdvisory("CUSTOM-2024-99");

View File

@@ -9,5 +9,6 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Exporter.TrivyDb/StellaOps.Concelier.Exporter.TrivyDb.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -2,11 +2,13 @@ using System;
using StellaOps.Concelier.Exporter.TrivyDb;
using StellaOps.Concelier.Storage.Exporting;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.TrivyDb.Tests;
public sealed class TrivyDbExportPlannerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CreatePlan_ReturnsFullWhenStateMissing()
{
var planner = new TrivyDbExportPlanner();
@@ -21,7 +23,8 @@ public sealed class TrivyDbExportPlannerTests
Assert.Equal(manifest, plan.Manifest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CreatePlan_ReturnsSkipWhenCursorMatches()
{
var planner = new TrivyDbExportPlanner();
@@ -49,7 +52,8 @@ public sealed class TrivyDbExportPlannerTests
Assert.Empty(plan.RemovedPaths);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CreatePlan_ReturnsFullWhenCursorDiffers()
{
var planner = new TrivyDbExportPlanner();

View File

@@ -17,6 +17,8 @@ using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage.Advisories;
using StellaOps.Concelier.Storage.Exporting;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.TrivyDb.Tests;
public sealed class TrivyDbFeedExporterTests : IDisposable
@@ -30,7 +32,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
_jsonRoot = Path.Combine(_root, "tree");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_SortsAdvisoriesByKeyDeterministically()
{
var advisoryB = CreateSampleAdvisory("CVE-2024-1002", "Second advisory");
@@ -98,7 +101,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.Single(recordingBuilder.ManifestDigests);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_SmallDatasetProducesDeterministicOciLayout()
{
var advisories = new[]
@@ -134,7 +138,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.Equal(TrivyDbMediaTypes.TrivyLayer, layer.GetProperty("mediaType").GetString());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExportOptions_GetExportRoot_NormalizesRelativeRoot()
{
var options = new TrivyDbExportOptions
@@ -149,7 +154,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.EndsWith(Path.Combine("exports", "trivy-test", exportId), path, StringComparison.Ordinal);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_PersistsStateAndSkipsWhenDigestUnchanged()
{
var advisory = CreateSampleAdvisory();
@@ -222,7 +228,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.Empty(orasPusher.Pushes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_CreatesOfflineBundle()
{
var advisory = CreateSampleAdvisory();
@@ -282,7 +289,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.Empty(orasPusher.Pushes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_WritesMirrorBundlesWhenConfigured()
{
var advisoryOne = CreateSampleAdvisory("CVE-2025-1001", "Mirror Advisory One");
@@ -431,7 +439,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.Empty(orasPusher.Pushes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_SkipsOrasPushWhenDeltaPublishingDisabled()
{
var initial = CreateSampleAdvisory("CVE-2024-7100", "Publish toggles");
@@ -492,7 +501,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.Empty(orasPusher.Pushes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_SkipsOfflineBundleForDeltaWhenDisabled()
{
var initial = CreateSampleAdvisory("CVE-2024-7200", "Offline delta toggles");
@@ -562,7 +572,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.False(File.Exists(deltaBundlePath));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_ResetsBaselineWhenDeltaChainExists()
{
var advisory = CreateSampleAdvisory("CVE-2024-5000", "Baseline reset");
@@ -635,7 +646,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.NotEmpty(updated.Files);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_DeltaSequencePromotesBaselineReset()
{
var baseline = CreateSampleAdvisory("CVE-2024-8100", "Baseline advisory");
@@ -725,7 +737,8 @@ public sealed class TrivyDbFeedExporterTests : IDisposable
Assert.Equal(finalExportId, state.BaseExportId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAsync_DeltaReusesBaseLayerOnDisk()
{
var baseline = CreateSampleAdvisory("CVE-2024-8300", "Layer reuse baseline");

View File

@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using StellaOps.Concelier.Storage.Exporting;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.TrivyDb.Tests;
public sealed class TrivyDbOciWriterTests : IDisposable
@@ -21,7 +22,8 @@ public sealed class TrivyDbOciWriterTests : IDisposable
_root = Directory.CreateTempSubdirectory("trivy-writer-tests").FullName;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task WriteAsync_ReusesBlobsFromBaseLayout_WhenDigestMatches()
{
var baseLayout = Path.Combine(_root, "base");

View File

@@ -6,11 +6,13 @@ using System.Text;
using System.Text.Json;
using StellaOps.Concelier.Exporter.TrivyDb;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Exporter.TrivyDb.Tests;
public sealed class TrivyDbPackageBuilderTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BuildsOciManifestWithExpectedMediaTypes()
{
var metadata = Encoding.UTF8.GetBytes("{\"generatedAt\":\"2024-07-15T12:00:00Z\"}");
@@ -56,7 +58,8 @@ public sealed class TrivyDbPackageBuilderTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ThrowsWhenMetadataMissing()
{
var builder = new TrivyDbPackageBuilder();

View File

@@ -9,6 +9,7 @@ using FluentAssertions;
using StellaOps.Concelier.Interest.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Interest.Tests;
public class InterestScoreCalculatorTests
@@ -21,7 +22,8 @@ public class InterestScoreCalculatorTests
_calculator = new InterestScoreCalculator(_defaultWeights);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithNoSignals_ReturnsBaseScore()
{
// Arrange
@@ -43,7 +45,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().HaveCount(1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithSbomMatch_AddsInSbomFactor()
{
// Arrange
@@ -72,7 +75,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().Contain("no_vex_na");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithReachableSbomMatch_AddsReachableFactor()
{
// Arrange
@@ -102,7 +106,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().Contain("no_vex_na");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithDeployedSbomMatch_AddsDeployedFactor()
{
// Arrange
@@ -132,7 +137,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().Contain("no_vex_na");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithFullSbomMatch_AddsAllSbomFactors()
{
// Arrange
@@ -163,7 +169,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().Contain("no_vex_na");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithVexNotAffected_ExcludesVexFactor()
{
// Arrange
@@ -203,7 +210,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().NotContain("no_vex_na");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithRecentLastSeen_AddsRecentFactor()
{
// Arrange
@@ -233,7 +241,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().Contain("recent");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithOldLastSeen_DecaysRecentFactor()
{
// Arrange
@@ -263,7 +272,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().NotContain("recent"); // decayFactor < 0.5
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_WithVeryOldLastSeen_NoRecentFactor()
{
// Arrange
@@ -284,7 +294,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().NotContain("recent");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_MaxScore_IsCappedAt1()
{
// Arrange - use custom weights that exceed 1.0
@@ -322,7 +333,8 @@ public class InterestScoreCalculatorTests
result.Score.Should().Be(1.0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_SetsComputedAtToNow()
{
// Arrange
@@ -338,7 +350,8 @@ public class InterestScoreCalculatorTests
result.ComputedAt.Should().BeOnOrBefore(after);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Calculate_PreservesCanonicalId()
{
// Arrange
@@ -352,7 +365,8 @@ public class InterestScoreCalculatorTests
result.CanonicalId.Should().Be(canonicalId);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(VexStatus.Affected)]
[InlineData(VexStatus.Fixed)]
[InlineData(VexStatus.UnderInvestigation)]
@@ -379,7 +393,8 @@ public class InterestScoreCalculatorTests
result.Reasons.Should().Contain("no_vex_na");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void InterestTier_HighScore_ReturnsHigh()
{
// Arrange
@@ -395,7 +410,8 @@ public class InterestScoreCalculatorTests
score.Tier.Should().Be(InterestTier.High);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void InterestTier_MediumScore_ReturnsMedium()
{
// Arrange
@@ -411,7 +427,8 @@ public class InterestScoreCalculatorTests
score.Tier.Should().Be(InterestTier.Medium);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void InterestTier_LowScore_ReturnsLow()
{
// Arrange
@@ -427,7 +444,8 @@ public class InterestScoreCalculatorTests
score.Tier.Should().Be(InterestTier.Low);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void InterestTier_NoneScore_ReturnsNone()
{
// Arrange

View File

@@ -13,6 +13,7 @@ using Moq;
using StellaOps.Concelier.Interest.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Interest.Tests;
/// <summary>
@@ -57,7 +58,8 @@ public class InterestScoringServiceTests
#region Task 18: Integration Tests - Score Persistence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateScoreAsync_PersistsToRepository()
{
// Arrange
@@ -72,7 +74,8 @@ public class InterestScoringServiceTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetScoreAsync_RetrievesFromRepository()
{
// Arrange
@@ -92,7 +95,8 @@ public class InterestScoringServiceTests
result.Score.Should().Be(0.5);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetScoreAsync_ReturnsNull_WhenNotFound()
{
// Arrange
@@ -107,7 +111,8 @@ public class InterestScoringServiceTests
result.Should().BeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BatchUpdateAsync_UpdatesMultipleScores()
{
// Arrange
@@ -122,7 +127,8 @@ public class InterestScoringServiceTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BatchUpdateAsync_HandlesEmptyInput()
{
// Act
@@ -138,7 +144,8 @@ public class InterestScoringServiceTests
#region Task 23: Job Execution and Score Consistency
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task RecalculateAllAsync_ReturnsZero_WhenNoAdvisoryStore()
{
// The service is created without an ICanonicalAdvisoryStore,
@@ -152,7 +159,8 @@ public class InterestScoringServiceTests
result.Should().Be(0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ComputeScoreAsync_ProducesDeterministicResults()
{
// Arrange
@@ -167,7 +175,8 @@ public class InterestScoringServiceTests
result1.Reasons.Should().BeEquivalentTo(result2.Reasons);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ComputeScoreAsync_ReturnsValidScoreRange()
{
// Arrange
@@ -182,7 +191,8 @@ public class InterestScoringServiceTests
result.ComputedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateScoreAsync_PreservesScoreConsistency()
{
// Arrange
@@ -206,7 +216,8 @@ public class InterestScoringServiceTests
savedScore.Reasons.Should().BeEquivalentTo(["in_sbom", "reachable"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BatchUpdateAsync_MaintainsScoreOrdering()
{
// Arrange
@@ -232,7 +243,8 @@ public class InterestScoringServiceTests
#region Task 28: Degradation/Restoration Cycle
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DegradeToStubsAsync_ReturnsZero_WhenNoAdvisoryStore()
{
// The service is created without an ICanonicalAdvisoryStore,
@@ -246,7 +258,8 @@ public class InterestScoringServiceTests
result.Should().Be(0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task RestoreFromStubsAsync_ReturnsZero_WhenNoAdvisoryStore()
{
// The service is created without an ICanonicalAdvisoryStore,
@@ -259,7 +272,8 @@ public class InterestScoringServiceTests
result.Should().Be(0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DegradeRestoreCycle_MaintainsDataIntegrity()
{
// Arrange
@@ -293,7 +307,8 @@ public class InterestScoringServiceTests
stored.Reasons.Should().Contain("in_sbom");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DegradeToStubsAsync_ReturnsZero_WhenNoLowScores()
{
// Arrange
@@ -312,7 +327,8 @@ public class InterestScoringServiceTests
result.Should().Be(0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task RestoreFromStubsAsync_ReturnsZero_WhenNoHighScores()
{
// Arrange
@@ -334,7 +350,8 @@ public class InterestScoringServiceTests
#region Edge Cases
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateScoreAsync_HandlesBoundaryScores()
{
// Arrange
@@ -350,7 +367,8 @@ public class InterestScoringServiceTests
Times.Exactly(2));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ComputeScoreAsync_HandlesNullInputGracefully()
{
// Act

View File

@@ -24,6 +24,7 @@
<ItemGroup>
<ProjectReference Include="..\..\__Libraries\StellaOps.Concelier.Interest\StellaOps.Concelier.Interest.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -12,7 +12,8 @@ namespace StellaOps.Concelier.Merge.Analyzers.Tests;
public sealed class MergeUsageAnalyzerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ReportsDiagnostic_ForAdvisoryMergeServiceInstantiation()
{
const string source = """
@@ -33,7 +34,8 @@ public sealed class MergeUsageAnalyzerTests
Assert.Contains(diagnostics, d => d.Id == MergeUsageAnalyzer.DiagnosticId && d.GetMessage().Contains("AdvisoryMergeService", StringComparison.Ordinal));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ReportsDiagnostic_ForAddMergeModuleInvocation()
{
const string source = """
@@ -56,7 +58,8 @@ public sealed class MergeUsageAnalyzerTests
Assert.Contains(diagnostics, d => d.Id == MergeUsageAnalyzer.DiagnosticId && d.GetMessage().Contains("AddMergeModule", StringComparison.Ordinal));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ReportsDiagnostic_ForFieldDeclaration()
{
const string source = """
@@ -74,7 +77,8 @@ public sealed class MergeUsageAnalyzerTests
Assert.Contains(diagnostics, d => d.Id == MergeUsageAnalyzer.DiagnosticId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DoesNotReportDiagnostic_InsideMergeAssembly()
{
const string source = """
@@ -92,13 +96,15 @@ public sealed class MergeUsageAnalyzerTests
Assert.DoesNotContain(diagnostics, d => d.Id == MergeUsageAnalyzer.DiagnosticId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ReportsDiagnostic_ForTypeOfUsage()
{
const string source = """
using System;
using StellaOps.Concelier.Merge.Services;
using StellaOps.TestKit;
namespace Sample.TypeOf;
public static class Demo

View File

@@ -21,6 +21,7 @@
<ItemGroup>
<ProjectReference Include="..\\..\\__Analyzers\\StellaOps.Concelier.Merge.Analyzers\\StellaOps.Concelier.Merge.Analyzers.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -4,13 +4,15 @@ using StellaOps.Concelier.Merge.Identity;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AdvisoryIdentityResolverTests
{
private readonly AdvisoryIdentityResolver _resolver = new();
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_GroupsBySharedCveAlias()
{
var nvd = CreateAdvisory("CVE-2025-1234", aliases: new[] { "CVE-2025-1234" }, source: "nvd");
@@ -24,7 +26,8 @@ public sealed class AdvisoryIdentityResolverTests
Assert.True(cluster.Aliases.Any(alias => alias.Value == "CVE-2025-1234"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_PrefersPsirtAliasWhenNoCve()
{
var vendor = CreateAdvisory("VMSA-2025-0001", aliases: new[] { "VMSA-2025-0001" }, source: "vmware");
@@ -38,7 +41,8 @@ public sealed class AdvisoryIdentityResolverTests
Assert.True(cluster.Aliases.Any(alias => alias.Value == "VMSA-2025-0001"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_FallsBackToGhsaWhenOnlyGhsaPresent()
{
var ghsa = CreateAdvisory("GHSA-aaaa-bbbb-cccc", aliases: new[] { "GHSA-aaaa-bbbb-cccc" }, source: "ghsa");
@@ -52,7 +56,8 @@ public sealed class AdvisoryIdentityResolverTests
Assert.True(cluster.Aliases.Any(alias => alias.Value == "GHSA-aaaa-bbbb-cccc"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_GroupsByKeyWhenNoAliases()
{
var first = CreateAdvisory("custom-1", aliases: Array.Empty<string>(), source: "source-a");

View File

@@ -15,11 +15,13 @@ using StellaOps.Concelier.Storage.Aliases;
using StellaOps.Concelier.Storage.MergeEvents;
using StellaOps.Provenance;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AdvisoryMergeServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MergeAsync_AppliesCanonicalRulesAndPersistsDecisions()
{
var aliasStore = new FakeAliasStore();
@@ -167,7 +169,8 @@ public sealed class AdvisoryMergeServiceTests
provenance: new[] { provenance });
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MergeAsync_PersistsConflictSummariesWithHashes()
{
var aliasStore = new FakeAliasStore();

View File

@@ -9,11 +9,14 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AdvisoryPrecedenceMergerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_PrefersVendorPrecedenceOverNvd()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero));
@@ -59,7 +62,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(severityConflict.Tags, tag => string.Equals(tag.Key, "type", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "severity", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_KevOnlyTogglesExploitKnown()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 2, 1, 0, 0, 0, TimeSpan.Zero));
@@ -128,7 +132,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(merged.Provenance, provenance => provenance.Source == "merge");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_UnionsCreditsFromSources()
{
var timeProvider = new FakeTimeProvider();
@@ -231,7 +236,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(merged.Credits, credit => credit.Provenance.Source == "osv");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_AcscActsAsEnrichmentSource()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 10, 12, 0, 0, 0, TimeSpan.Zero));
@@ -333,7 +339,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(merged.Provenance, provenance => provenance.Source == "merge" && (provenance.Value?.Contains("acsc", StringComparison.OrdinalIgnoreCase) ?? false));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_RecordsNormalizedRuleMetrics()
{
var now = new DateTimeOffset(2025, 3, 1, 0, 0, 0, TimeSpan.Zero);
@@ -474,7 +481,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(missingMeasurement.Tags, tag => string.Equals(tag.Key, "package_type", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "semver", StringComparison.Ordinal));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_RespectsConfiguredPrecedenceOverrides()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 3, 1, 0, 0, 0, TimeSpan.Zero));

View File

@@ -4,11 +4,13 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AffectedPackagePrecedenceResolverTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_PrefersRedHatOverNvdForSameCpe()
{
var redHat = new AffectedPackage(
@@ -64,7 +66,8 @@ public sealed class AffectedPackagePrecedenceResolverTests
Assert.Equal(1, rangeOverride.SuppressedRangeCount);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_KeepsNvdWhenNoHigherPrecedence()
{
var nvd = new AffectedPackage(

View File

@@ -6,11 +6,13 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Storage.Aliases;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AliasGraphResolverTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_ReturnsCollisions_WhenAliasesOverlap()
{
var aliasStore = new AliasStore();
@@ -39,7 +41,8 @@ public sealed class AliasGraphResolverTests
Assert.Contains("ADV-2", collision.AdvisoryKeys);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BuildComponentAsync_TracesConnectedAdvisories()
{
var aliasStore = new AliasStore();
@@ -73,7 +76,8 @@ public sealed class AliasGraphResolverTests
Assert.Contains(component.AliasMap["ADV-B"], record => record.Scheme == "OSV" && record.Value == "OSV-2025-1");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BuildComponentAsync_LinksOsvAndGhsaAliases()
{
var aliasStore = new AliasStore();

View File

@@ -4,18 +4,21 @@ using StellaOps.Concelier.Normalization.Distro;
using StellaOps.VersionComparison;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class ApkVersionComparerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComparatorType_Returns_Apk()
{
Assert.Equal(ComparatorType.Apk, ApkVersionComparer.Instance.ComparatorType);
}
public static TheoryData<string, string, int, string> ComparisonCases => BuildComparisonCases();
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[MemberData(nameof(ComparisonCases))]
public void Compare_ApkVersions_ReturnsExpectedOrder(string left, string right, int expected, string note)
{
@@ -23,7 +26,8 @@ public sealed class ApkVersionComparerTests
Assert.True(expected == actual, $"[{note}] '{left}' vs '{right}': expected {expected}, got {actual}");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_ParsesApkVersionComponents()
{
var result = ApkVersionComparer.Instance.Compare(
@@ -84,7 +88,8 @@ public sealed class ApkVersionComparerTests
#region CompareWithProof Tests (SPRINT_4000_0002_0001)
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_BothNull_ReturnsEqual()
{
var result = ApkVersionComparer.Instance.CompareWithProof(null, null);
@@ -94,7 +99,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains("null", result.ProofLines[0].ToLower());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_LeftNull_ReturnsLess()
{
var result = ApkVersionComparer.Instance.CompareWithProof(null, "1.0-r0");
@@ -103,7 +109,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains("null", result.ProofLines[0].ToLower());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_RightNull_ReturnsGreater()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.0-r0", null);
@@ -112,7 +119,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains("null", result.ProofLines[0].ToLower());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_EqualVersions_ReturnsEqualWithProof()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3-r1", "1.2.3-r1");
@@ -122,7 +130,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains(result.ProofLines, line => line.Contains("equal"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_VersionDifference_ReturnsProofLines()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3-r0", "1.2.4-r0");
@@ -133,7 +142,8 @@ public sealed class ApkVersionComparerTests
line.Contains("Version") || line.Contains("older") || line.Contains("<"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_PkgRelDifference_ReturnsProofWithPkgRel()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3-r1", "1.2.3-r2");
@@ -142,7 +152,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains(result.ProofLines, line => line.Contains("release") || line.Contains("-r"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_ImplicitVsExplicitPkgRel_ReturnsProofExplaining()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3", "1.2.3-r0");
@@ -151,7 +162,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains(result.ProofLines, line => line.Contains("implicit") || line.Contains("explicit"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_NewerVersion_ReturnsGreaterThanOrEqual()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.4-r0", "1.2.3-r0");
@@ -160,7 +172,8 @@ public sealed class ApkVersionComparerTests
Assert.True(result.IsGreaterThanOrEqual);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_InvalidVersions_FallsBackToStringComparison()
{
var result = ApkVersionComparer.Instance.CompareWithProof("", "");
@@ -172,7 +185,8 @@ public sealed class ApkVersionComparerTests
line.Contains("equal", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_ReturnsCorrectComparatorType()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.0-r0", "1.0-r1");

View File

@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using StellaOps.Concelier.Merge.Backport;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -35,7 +36,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 1: DistroAdvisory Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier1DistroAdvisory_ExtractsEvidence()
{
// Arrange
@@ -60,7 +62,8 @@ public sealed class BackportEvidenceResolverTests
evidence.DistroRelease.Should().Contain("debian");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier1LowConfidence_ReturnsNull()
{
// Arrange
@@ -83,7 +86,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 2: ChangelogMention Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier2ChangelogMention_ExtractsEvidence()
{
// Arrange
@@ -108,7 +112,8 @@ public sealed class BackportEvidenceResolverTests
evidence.DistroRelease.Should().Contain("redhat");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier2WithUpstreamCommit_ExtractsPatchLineage()
{
// Arrange
@@ -144,7 +149,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 3: PatchHeader Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier3PatchHeader_ExtractsEvidence()
{
// Arrange
@@ -168,7 +174,8 @@ public sealed class BackportEvidenceResolverTests
evidence.PatchOrigin.Should().Be(PatchOrigin.Upstream);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier3DistroPatch_DetectsDistroOrigin()
{
// Arrange
@@ -204,7 +211,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 4: BinaryFingerprint Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier4BinaryFingerprint_ExtractsEvidence()
{
// Arrange
@@ -230,7 +238,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier Priority
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_MultipleTiers_SelectsHighestTier()
{
// Arrange: BinaryFingerprint (Tier 4) should be selected as highest
@@ -256,7 +265,8 @@ public sealed class BackportEvidenceResolverTests
evidence!.Tier.Should().Be(BackportEvidenceTier.BinaryFingerprint);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_PatchHeaderVsChangelog_PrefersPatchHeader()
{
// Arrange: PatchHeader (Tier 3) > ChangelogMention (Tier 2)
@@ -286,7 +296,8 @@ public sealed class BackportEvidenceResolverTests
#region Distro Release Extraction
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("pkg:deb/debian/curl@7.64.0-4+deb11u1", "debian:bullseye")]
[InlineData("pkg:deb/debian/openssl@3.0.11-1~deb12u2", "debian:bookworm")]
[InlineData("pkg:rpm/redhat/nginx@1.20.1-14.el9", "redhat:9")]
@@ -314,7 +325,8 @@ public sealed class BackportEvidenceResolverTests
#region Batch Resolution
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveBatchAsync_ResolvesMultiplePackages()
{
// Arrange
@@ -350,7 +362,8 @@ public sealed class BackportEvidenceResolverTests
#region Edge Cases
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_NullProof_ReturnsNull()
{
// Arrange
@@ -365,7 +378,8 @@ public sealed class BackportEvidenceResolverTests
evidence.Should().BeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_VeryLowConfidence_ReturnsNull()
{
// Arrange
@@ -383,7 +397,8 @@ public sealed class BackportEvidenceResolverTests
evidence.Should().BeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HasEvidenceAsync_ReturnsTrueWhenEvidenceExists()
{
// Arrange
@@ -401,7 +416,8 @@ public sealed class BackportEvidenceResolverTests
hasEvidence.Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HasEvidenceAsync_ReturnsFalseWhenNoEvidence()
{
// Arrange
@@ -416,7 +432,8 @@ public sealed class BackportEvidenceResolverTests
hasEvidence.Should().BeFalse();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_ThrowsOnNullCveId()
{
// Act & Assert
@@ -424,7 +441,8 @@ public sealed class BackportEvidenceResolverTests
() => _resolver.ResolveAsync(null!, "pkg:deb/debian/test@1.0"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_ThrowsOnNullPurl()
{
// Act & Assert

View File

@@ -15,6 +15,7 @@ using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage.MergeEvents;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -65,7 +66,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Debian Backport Advisory Flow
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_IngestDebianAdvisoryWithBackport_CreatesProvenanceScope()
{
// Arrange: Simulate Debian security advisory for CVE-2024-1234
@@ -129,7 +131,8 @@ public sealed class BackportProvenanceE2ETests
capturedScope.PatchId.Should().Be(patchCommit);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_IngestRhelAdvisoryWithBackport_CreatesProvenanceScopeWithDistroOrigin()
{
// Arrange: Simulate RHEL security advisory with distro-specific patch
@@ -186,7 +189,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Multiple Distro Backports for Same CVE
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_SameCveMultipleDistros_CreatesSeparateProvenanceScopes()
{
// Arrange: Same CVE with Debian and Ubuntu backports
@@ -239,7 +243,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Merge Event with Backport Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_MergeWithBackportEvidence_RecordsInAuditLog()
{
// Arrange
@@ -296,7 +301,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Evidence Tier Upgrade
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_EvidenceUpgrade_UpdatesProvenanceScope()
{
// Arrange: Start with low-tier evidence, then upgrade
@@ -355,7 +361,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Provenance Retrieval
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_RetrieveProvenanceForCanonical_ReturnsAllDistroScopes()
{
// Arrange

View File

@@ -2,6 +2,7 @@ using System.Linq;
using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class CanonicalHashCalculatorTests
@@ -41,7 +42,8 @@ public sealed class CanonicalHashCalculatorTests
},
provenance: new[] { AdvisoryProvenance.Empty });
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeHash_ReturnsDeterministicValue()
{
var calculator = new CanonicalHashCalculator();
@@ -51,7 +53,8 @@ public sealed class CanonicalHashCalculatorTests
Assert.Equal(first, second);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeHash_IgnoresOrderingDifferences()
{
var calculator = new CanonicalHashCalculator();
@@ -77,7 +80,8 @@ public sealed class CanonicalHashCalculatorTests
Assert.Equal(originalHash, reorderedHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeHash_NullReturnsEmpty()
{
var calculator = new CanonicalHashCalculator();

View File

@@ -3,11 +3,13 @@ using StellaOps.Concelier.Merge.Comparers;
using StellaOps.Concelier.Normalization.Distro;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class DebianEvrComparerTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("1:1.2.3-1", 1, "1.2.3", "1")]
[InlineData("1.2.3-1", 0, "1.2.3", "1")]
[InlineData("2:4.5", 2, "4.5", "")]
@@ -24,7 +26,8 @@ public sealed class DebianEvrComparerTests
Assert.Equal(input, evr.Original);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("")]
[InlineData(":1.0-1")]
[InlineData("1:")]
@@ -36,7 +39,8 @@ public sealed class DebianEvrComparerTests
Assert.Null(evr);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_PrefersHigherEpoch()
{
var lower = "0:2.0-1";
@@ -45,7 +49,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(higher, lower) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesVersionOrdering()
{
var lower = "0:1.2.3-1";
@@ -54,7 +59,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(higher, lower) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_TildeRanksEarlier()
{
var prerelease = "0:1.0~beta1-1";
@@ -63,7 +69,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(prerelease, stable) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_RevisionBreaksTies()
{
var first = "0:1.0-1";
@@ -72,7 +79,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(second, first) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FallsBackToOrdinalForInvalid()
{
var left = "not-an-evr";
@@ -86,7 +94,8 @@ public sealed class DebianEvrComparerTests
public static TheoryData<string, string, int, string> ComparisonCases => BuildComparisonCases();
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[MemberData(nameof(ComparisonCases))]
public void Compare_DebianEvr_ReturnsExpectedOrder(string left, string right, int expected, string note)
{

View File

@@ -7,6 +7,7 @@ using System.Text;
using StellaOps.Concelier.Merge.Comparers;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class GoldenVersionComparisonTests
@@ -16,7 +17,8 @@ public sealed class GoldenVersionComparisonTests
PropertyNameCaseInsensitive = true
};
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("rpm_version_comparison.golden.ndjson", "rpm")]
[InlineData("deb_version_comparison.golden.ndjson", "deb")]
[InlineData("apk_version_comparison.golden.ndjson", "apk")]

View File

@@ -4,11 +4,13 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage.MergeEvents;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class MergeEventWriterTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AppendAsync_WritesRecordWithComputedHashes()
{
var store = new InMemoryMergeEventStore();
@@ -31,7 +33,8 @@ public sealed class MergeEventWriterTests
Assert.Same(store.LastRecord, record);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AppendAsync_NullBeforeUsesEmptyHash()
{
var store = new InMemoryMergeEventStore();

View File

@@ -9,6 +9,7 @@ using FluentAssertions;
using StellaOps.Concelier.Merge.Identity;
using StellaOps.Concelier.Merge.Identity.Normalizers;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -26,7 +27,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Same Patch Lineage = Same Hash
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_SamePatchLineage_ProducesSameHash()
{
// Arrange
@@ -56,7 +58,8 @@ public sealed class MergeHashBackportDifferentiationTests
hash1.Should().Be(hash2, "same patch lineage should produce same hash");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_NoPatchLineage_ProducesSameHash()
{
// Arrange
@@ -90,7 +93,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Different Patch Lineage = Different Hash
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_DifferentPatchLineage_ProducesDifferentHash()
{
// Arrange - Upstream fix vs distro-specific backport
@@ -121,7 +125,8 @@ public sealed class MergeHashBackportDifferentiationTests
"different patch lineage should produce different hash");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_WithVsWithoutPatchLineage_ProducesDifferentHash()
{
// Arrange
@@ -152,7 +157,8 @@ public sealed class MergeHashBackportDifferentiationTests
"advisory with patch lineage should differ from one without");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_DebianVsRhelBackport_ProducesDifferentHash()
{
// Arrange - Same CVE, different distro backports
@@ -187,7 +193,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Patch Lineage Normalization
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(
"abc123def456abc123def456abc123def456abcd",
"ABC123DEF456ABC123DEF456ABC123DEF456ABCD",
@@ -230,7 +237,8 @@ public sealed class MergeHashBackportDifferentiationTests
hash1.Should().Be(hash2, reason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_AbbreviatedSha_DiffersFromFullSha()
{
// Abbreviated SHA is treated as different from a full different SHA
@@ -265,7 +273,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Real-World Scenarios
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_GoldenCorpus_DebianBackportVsNvd()
{
// Golden corpus test case: CVE-2024-1234 with Debian backport
@@ -300,7 +309,8 @@ public sealed class MergeHashBackportDifferentiationTests
"NVD and Debian entries should produce different hashes due to package and version differences");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_GoldenCorpus_DistroSpecificFix()
{
// Golden corpus test case: Distro-specific fix different from upstream
@@ -331,7 +341,8 @@ public sealed class MergeHashBackportDifferentiationTests
"distro-specific fix should produce different hash from upstream");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_SameUpstreamBackported_ProducesSameHash()
{
// When two distros backport the SAME upstream patch, they should merge
@@ -367,7 +378,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Edge Cases
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_EmptyPatchLineage_TreatedAsNull()
{
var emptyLineage = new MergeHashInput
@@ -397,7 +409,8 @@ public sealed class MergeHashBackportDifferentiationTests
"empty and null patch lineage should produce same hash");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_WhitespacePatchLineage_TreatedAsNull()
{
var whitespaceLineage = new MergeHashInput
@@ -427,7 +440,8 @@ public sealed class MergeHashBackportDifferentiationTests
"whitespace-only patch lineage should be treated as null");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_IsDeterministic()
{
// Verify determinism across multiple calls

View File

@@ -7,6 +7,7 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage.MergeEvents;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class MergePrecedenceIntegrationTests : IAsyncLifetime
@@ -16,7 +17,8 @@ public sealed class MergePrecedenceIntegrationTests : IAsyncLifetime
private AdvisoryPrecedenceMerger? _merger;
private FakeTimeProvider? _timeProvider;
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MergePipeline_PsirtOverridesNvd_AndKevOnlyTogglesExploitKnown()
{
await EnsureInitializedAsync();

View File

@@ -3,11 +3,13 @@ using StellaOps.Concelier.Merge.Comparers;
using StellaOps.Concelier.Normalization.Distro;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class NevraComparerTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("kernel-1:4.18.0-348.7.1.el8_5.x86_64", "kernel", 1, "4.18.0", "348.7.1.el8_5", "x86_64")]
[InlineData("bash-5.1.8-2.fc35.x86_64", "bash", 0, "5.1.8", "2.fc35", "x86_64")]
[InlineData("openssl-libs-1:1.1.1k-7.el8", "openssl-libs", 1, "1.1.1k", "7.el8", null)]
@@ -28,7 +30,8 @@ public sealed class NevraComparerTests
Assert.Equal(input, nevra.Original);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("")]
[InlineData("kernel4.18.0-80.el8")]
[InlineData("kernel-4.18.0")]
@@ -40,7 +43,8 @@ public sealed class NevraComparerTests
Assert.Null(nevra);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryParse_TrimsWhitespace()
{
var success = Nevra.TryParse(" kernel-0:4.18.0-80.el8.x86_64 ", out var nevra);
@@ -51,7 +55,8 @@ public sealed class NevraComparerTests
Assert.Equal("4.18.0", nevra.Version);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_PrefersHigherEpoch()
{
var older = "kernel-0:4.18.0-348.7.1.el8_5.x86_64";
@@ -61,7 +66,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(older, newer) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesRpmVersionOrdering()
{
var lower = "kernel-0:4.18.0-80.el8.x86_64";
@@ -70,7 +76,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(higher, lower) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesReleaseOrdering()
{
var el8 = "bash-0:5.1.0-1.el8.x86_64";
@@ -79,7 +86,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(el9, el8) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_TildeRanksEarlier()
{
var prerelease = "bash-0:5.1.0~beta-1.fc34.x86_64";
@@ -88,7 +96,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(prerelease, stable) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_ConsidersArchitecture()
{
var noarch = "pkg-0:1.0-1.noarch";
@@ -97,7 +106,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(noarch, arch) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FallsBackToOrdinalForInvalid()
{
var left = "not-a-nevra";
@@ -110,7 +120,8 @@ public sealed class NevraComparerTests
public static TheoryData<string, string, int, string> ComparisonCases => BuildComparisonCases();
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[MemberData(nameof(ComparisonCases))]
public void Compare_NevraVersions_ReturnsExpectedOrder(string left, string right, int expected, string note)
{

View File

@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using StellaOps.Concelier.Merge.Backport;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -34,7 +35,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region CreateOrUpdateAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_NewScope_CreatesProvenanceScope()
{
// Arrange
@@ -74,7 +76,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_ExistingScope_UpdatesProvenanceScope()
{
// Arrange
@@ -117,7 +120,8 @@ public sealed class ProvenanceScopeLifecycleTests
result.ProvenanceScopeId.Should().Be(existingScopeId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_WithEvidenceResolver_ResolvesEvidence()
{
// Arrange
@@ -171,7 +175,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_NonDistroSource_StillCreatesScope()
{
// Arrange
@@ -204,7 +209,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region UpdateFromEvidenceAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateFromEvidenceAsync_NewEvidence_CreatesScope()
{
// Arrange
@@ -246,7 +252,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateFromEvidenceAsync_BetterEvidence_UpdatesScope()
{
// Arrange
@@ -300,7 +307,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateFromEvidenceAsync_LowerConfidenceEvidence_SkipsUpdate()
{
// Arrange
@@ -352,7 +360,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region LinkEvidenceRefAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LinkEvidenceRefAsync_LinksEvidenceToScope()
{
// Arrange
@@ -374,7 +383,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region GetByCanonicalIdAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetByCanonicalIdAsync_ReturnsAllScopes()
{
// Arrange
@@ -418,7 +428,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region DeleteByCanonicalIdAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DeleteByCanonicalIdAsync_DeletesAllScopes()
{
// Arrange
@@ -439,7 +450,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region Distro Release Extraction Tests
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("pkg:deb/debian/curl@7.64.0-4+deb11u1", "debian", "debian:bullseye")]
[InlineData("pkg:deb/debian/openssl@3.0.11-1~deb12u2", "debian", "debian:bookworm")]
[InlineData("pkg:rpm/redhat/nginx@1.20.1-14.el9", "redhat", "redhat:9")]

View File

@@ -2,11 +2,13 @@ using FluentAssertions;
using StellaOps.Concelier.Merge.Comparers;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class SemanticVersionRangeResolverTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("1.2.3", true)]
[InlineData("1.2.3-beta.1", true)]
[InlineData("invalid", false)]
@@ -19,14 +21,16 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Equal(expected, version is not null);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_ParsesSemanticVersions()
{
Assert.True(SemanticVersionRangeResolver.Compare("1.2.3", "1.2.2") > 0);
Assert.True(SemanticVersionRangeResolver.Compare("1.2.3-beta", "1.2.3") < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesOrdinalFallbackForInvalid()
{
var left = "zzz";
@@ -37,7 +41,8 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Equal(expected, actual);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolveWindows_WithFixedVersion_ComputesExclusiveUpper()
{
var (introduced, exclusive, inclusive) = SemanticVersionRangeResolver.ResolveWindows("1.0.0", "1.2.0", null);
@@ -47,7 +52,8 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Null(inclusive);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolveWindows_WithLastAffectedOnly_ComputesInclusiveAndExclusive()
{
var (introduced, exclusive, inclusive) = SemanticVersionRangeResolver.ResolveWindows("1.0.0", null, "1.1.5");
@@ -57,7 +63,8 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Equal(SemanticVersionRangeResolver.Parse("1.1.5"), inclusive);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolveWindows_WithNeither_ReturnsNullBounds()
{
var (introduced, exclusive, inclusive) = SemanticVersionRangeResolver.ResolveWindows(null, null, null);

View File

@@ -12,6 +12,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Canonical.Json/StellaOps.Canonical.Json.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />

View File

@@ -2,11 +2,13 @@ using System;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class AdvisoryProvenanceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void FieldMask_NormalizesAndDeduplicates()
{
var timestamp = DateTimeOffset.Parse("2025-01-01T00:00:00Z");
@@ -25,14 +27,16 @@ public sealed class AdvisoryProvenanceTests
Assert.Null(provenance.DecisionReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EmptyProvenance_ExposesEmptyFieldMask()
{
Assert.True(AdvisoryProvenance.Empty.FieldMask.IsEmpty);
Assert.Null(AdvisoryProvenance.Empty.DecisionReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void DecisionReason_IsTrimmed()
{
var timestamp = DateTimeOffset.Parse("2025-03-01T00:00:00Z");

View File

@@ -1,11 +1,13 @@
using System.Linq;
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class AdvisoryTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CanonicalizesAliasesAndReferences()
{
var advisory = new Advisory(

View File

@@ -1,11 +1,13 @@
using System;
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class AffectedPackageStatusTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("Known_Affected", AffectedPackageStatusCatalog.KnownAffected)]
[InlineData("KNOWN-NOT-AFFECTED", AffectedPackageStatusCatalog.KnownNotAffected)]
[InlineData("Under Investigation", AffectedPackageStatusCatalog.UnderInvestigation)]
@@ -28,14 +30,16 @@ public sealed class AffectedPackageStatusTests
Assert.Equal(provenance, status.Provenance);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Constructor_ThrowsForUnknownStatus()
{
var provenance = new AdvisoryProvenance("test", "status", "value", DateTimeOffset.UtcNow);
Assert.Throws<ArgumentOutOfRangeException>(() => new AffectedPackageStatus("unsupported", provenance));
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("Not Impacted", AffectedPackageStatusCatalog.NotAffected)]
[InlineData("Resolved", AffectedPackageStatusCatalog.Fixed)]
[InlineData("Mitigation provided", AffectedPackageStatusCatalog.Mitigated)]
@@ -46,13 +50,15 @@ public sealed class AffectedPackageStatusTests
Assert.Equal(expected, normalized);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryNormalize_ReturnsFalseForUnknown()
{
Assert.False(AffectedPackageStatusCatalog.TryNormalize("unsupported", out _));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Allowed_ReturnsCanonicalStatuses()
{
var expected = new[]

View File

@@ -2,11 +2,13 @@ using System;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class AffectedVersionRangeExtensionsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_UsesNevraPrimitivesWhenAvailable()
{
var range = new AffectedVersionRange(
@@ -33,7 +35,8 @@ public sealed class AffectedVersionRangeExtensionsTests
Assert.Equal("pkg-1.2.0-2.x86_64", rule.Max);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_FallsBackForEvrWhenPrimitivesMissing()
{
var range = new AffectedVersionRange(
@@ -55,7 +58,8 @@ public sealed class AffectedVersionRangeExtensionsTests
Assert.Equal("fallback", rule.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_ReturnsNullForUnknownKind()
{
var range = new AffectedVersionRange(
@@ -72,7 +76,8 @@ public sealed class AffectedVersionRangeExtensionsTests
Assert.Null(rule);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_FallsBackForApkRange()
{
var range = new AffectedVersionRange(

View File

@@ -1,10 +1,12 @@
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class AliasSchemeRegistryTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("cve-2024-1234", AliasSchemes.Cve, "CVE-2024-1234")]
[InlineData("GHSA-xxxx-yyyy-zzzz", AliasSchemes.Ghsa, "GHSA-xxxx-yyyy-zzzz")]
[InlineData("osv-2023-15", AliasSchemes.OsV, "OSV-2023-15")]
@@ -21,7 +23,8 @@ public sealed class AliasSchemeRegistryTests
Assert.Equal(expectedAlias, normalized);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryNormalize_ReturnsFalseForUnknownAlias()
{
var success = AliasSchemeRegistry.TryNormalize("custom-identifier", out var normalized, out var scheme);
@@ -31,7 +34,8 @@ public sealed class AliasSchemeRegistryTests
Assert.Equal(string.Empty, scheme);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validation_NormalizesAliasWhenRecognized()
{
var result = Validation.TryNormalizeAlias(" rhsa-2024:0252 ", out var normalized);
@@ -41,7 +45,8 @@ public sealed class AliasSchemeRegistryTests
Assert.Equal("RHSA-2024:0252", normalized);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validation_RejectsEmptyAlias()
{
var result = Validation.TryNormalizeAlias(" ", out var normalized);

View File

@@ -4,11 +4,14 @@ using System.Linq;
using System.Text.Json;
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class CanonicalJsonSerializerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void SerializesPropertiesInDeterministicOrder()
{
var advisory = new Advisory(
@@ -34,7 +37,8 @@ public sealed class CanonicalJsonSerializerTests
Assert.Equal(sorted, propertyNames);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void SnapshotSerializerProducesStableOutput()
{
var advisory = new Advisory(
@@ -64,7 +68,8 @@ public sealed class CanonicalJsonSerializerTests
Assert.Equal(normalized1, normalized2);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void SerializesRangePrimitivesPayload()
{
var recordedAt = new DateTimeOffset(2025, 2, 1, 0, 0, 0, TimeSpan.Zero);

View File

@@ -1,11 +1,13 @@
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class EvrPrimitiveExtensionsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_ProducesRangeForIntroducedAndFixed()
{
var primitive = new EvrPrimitive(
@@ -25,7 +27,8 @@ public sealed class EvrPrimitiveExtensionsTests
Assert.Equal("ubuntu:focal", rule.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_GreaterThanOrEqualWhenOnlyIntroduced()
{
var primitive = new EvrPrimitive(

View File

@@ -1,11 +1,13 @@
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class NevraPrimitiveExtensionsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_ProducesRangeWhenBoundsAvailable()
{
var primitive = new NevraPrimitive(
@@ -25,7 +27,8 @@ public sealed class NevraPrimitiveExtensionsTests
Assert.Equal("rhel-8", rule.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_UsesLastAffectedAsInclusiveUpperBound()
{
var primitive = new NevraPrimitive(

View File

@@ -3,11 +3,13 @@ using System.Linq;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class NormalizedVersionRuleTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void NormalizedVersions_AreDeduplicatedAndOrdered()
{
var recordedAt = DateTimeOffset.Parse("2025-01-05T00:00:00Z");
@@ -59,7 +61,8 @@ public sealed class NormalizedVersionRuleTests
});
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void NormalizedVersionRule_NormalizesTypeSeparators()
{
var rule = new NormalizedVersionRule("semver", "tie_breaker", value: "1.2.3");

View File

@@ -5,11 +5,14 @@ using System.Diagnostics.Metrics;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class OsvGhsaParityDiagnosticsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RecordReport_EmitsTotalAndIssues()
{
var issues = ImmutableArray.Create(
@@ -45,7 +48,8 @@ public sealed class OsvGhsaParityDiagnosticsTests
Assert.Equal("none", severity.Tags["fieldMask"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RecordReport_NoIssues_OnlyEmitsTotal()
{
var report = new OsvGhsaParityReport(0, ImmutableArray<OsvGhsaParityIssue>.Empty);

View File

@@ -3,11 +3,13 @@ using System.Collections.Generic;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class OsvGhsaParityInspectorTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_ReturnsNoIssues_WhenDatasetsMatch()
{
var ghsaId = "GHSA-1111";
@@ -21,7 +23,8 @@ public sealed class OsvGhsaParityInspectorTests
Assert.Empty(report.Issues);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FlagsMissingOsvEntry()
{
var ghsaId = "GHSA-2222";
@@ -35,7 +38,8 @@ public sealed class OsvGhsaParityInspectorTests
Assert.Contains(ProvenanceFieldMasks.AffectedPackages, issue.FieldMask);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FlagsMissingGhsaEntry()
{
var ghsaId = "GHSA-2424";
@@ -49,7 +53,8 @@ public sealed class OsvGhsaParityInspectorTests
Assert.Contains(ProvenanceFieldMasks.AffectedPackages, issue.FieldMask);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FlagsSeverityMismatch()
{
var ghsaId = "GHSA-3333";
@@ -63,7 +68,8 @@ public sealed class OsvGhsaParityInspectorTests
Assert.Contains(ProvenanceFieldMasks.Advisory, issue.FieldMask);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FlagsRangeMismatch()
{
var ghsaId = "GHSA-4444";

View File

@@ -7,11 +7,14 @@ using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class ProvenanceDiagnosticsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RecordMissing_AddsExpectedTagsAndDeduplicates()
{
ResetState();
@@ -44,7 +47,8 @@ public sealed class ProvenanceDiagnosticsTests
Assert.Equal(ProvenanceFieldMasks.References, second.Tags["fieldMask"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ReportResumeWindow_ClearsTrackedEntries_WhenWindowBackfills()
{
ResetState();
@@ -68,7 +72,8 @@ public sealed class ProvenanceDiagnosticsTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ReportResumeWindow_RetainsEntries_WhenWindowTooRecent()
{
ResetState();
@@ -86,7 +91,8 @@ public sealed class ProvenanceDiagnosticsTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RecordRangePrimitive_EmitsCoverageMetric()
{
var range = new AffectedVersionRange(

View File

@@ -2,11 +2,13 @@ using System.Collections.Generic;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class RangePrimitivesTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetCoverageTag_ReturnsSpecificKinds()
{
var primitives = new RangePrimitives(
@@ -18,7 +20,8 @@ public sealed class RangePrimitivesTests
Assert.Equal("nevra+semver", primitives.GetCoverageTag());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetCoverageTag_ReturnsVendorWhenOnlyExtensions()
{
var primitives = new RangePrimitives(
@@ -31,7 +34,8 @@ public sealed class RangePrimitivesTests
Assert.Equal("vendor", primitives.GetCoverageTag());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetCoverageTag_ReturnsNoneWhenEmpty()
{
var primitives = new RangePrimitives(null, null, null, null);

View File

@@ -1,11 +1,13 @@
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class SemVerPrimitiveTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("1.0.0", true, "2.0.0", false, null, false, null, null, SemVerPrimitiveStyles.Range)]
[InlineData("1.0.0", true, null, false, null, false, null, null, SemVerPrimitiveStyles.GreaterThanOrEqual)]
[InlineData("1.0.0", false, null, false, null, false, null, null, SemVerPrimitiveStyles.GreaterThan)]
@@ -38,7 +40,8 @@ public sealed class SemVerPrimitiveTests
Assert.Equal(expectedStyle, primitive.Style);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EqualityIncludesExactValue()
{
var baseline = new SemVerPrimitive(
@@ -57,7 +60,8 @@ public sealed class SemVerPrimitiveTests
Assert.Equal(SemVerPrimitiveStyles.Range, baseline.Style);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_MapsRangeBounds()
{
var primitive = new SemVerPrimitive(
@@ -82,7 +86,8 @@ public sealed class SemVerPrimitiveTests
Assert.Equal(">=1.0.0 <2.0.0", rule.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_ExactUsesExactValue()
{
var primitive = new SemVerPrimitive(
@@ -106,7 +111,8 @@ public sealed class SemVerPrimitiveTests
Assert.Equal("from-ghsa", rule.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_GreaterThanMapsMinimum()
{
var primitive = new SemVerPrimitive(
@@ -130,7 +136,8 @@ public sealed class SemVerPrimitiveTests
Assert.Null(rule.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_UsesConstraintExpressionAsFallbackNotes()
{
var primitive = new SemVerPrimitive(
@@ -148,7 +155,8 @@ public sealed class SemVerPrimitiveTests
Assert.Equal("> 1.4.0", rule!.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_ExactCarriesConstraintExpressionWhenNotesMissing()
{
var primitive = new SemVerPrimitive(
@@ -169,7 +177,8 @@ public sealed class SemVerPrimitiveTests
Assert.Equal("= 3.2.1", rule.Notes);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToNormalizedVersionRule_ExplicitNotesOverrideConstraintExpression()
{
var primitive = new SemVerPrimitive(

View File

@@ -5,6 +5,7 @@ using System.Linq;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class SerializationDeterminismTests
@@ -18,7 +19,8 @@ public sealed class SerializationDeterminismTests
"ar-SA"
};
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CanonicalSerializer_ProducesStableJsonAcrossCultures()
{
var examples = CanonicalExampleFactory.GetExamples().ToArray();

View File

@@ -1,10 +1,12 @@
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Models.Tests;
public sealed class SeverityNormalizationTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("CRITICAL", "critical")]
[InlineData("Important", "high")]
[InlineData("moderate", "medium")]
@@ -27,7 +29,8 @@ public sealed class SeverityNormalizationTests
Assert.Equal(expected, normalized);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Normalize_ReturnsNullWhenInputNullOrWhitespace()
{
Assert.Null(SeverityNormalization.Normalize(null));

View File

@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(UpdateGoldens)' == 'true'">
<EnvironmentVariables Include="UPDATE_GOLDENS">

Some files were not shown because too many files have changed in this diff Show More