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

@@ -10,6 +10,7 @@ using System.Text;
using FluentAssertions;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.AirGap.Bundle.Tests;
/// <summary>
@@ -22,7 +23,8 @@ public sealed class AirGapCliToolTests
{
#region AIRGAP-5100-013: Exit Code Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExitCode_SuccessfulExport_ReturnsZero()
{
// Arrange
@@ -32,7 +34,8 @@ public sealed class AirGapCliToolTests
expectedExitCode.Should().Be(0, "Successful operations should return exit code 0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExitCode_UserError_ReturnsOne()
{
// Arrange
@@ -43,7 +46,8 @@ public sealed class AirGapCliToolTests
expectedExitCode.Should().Be(1, "User errors should return exit code 1");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExitCode_SystemError_ReturnsTwo()
{
// Arrange
@@ -54,7 +58,8 @@ public sealed class AirGapCliToolTests
expectedExitCode.Should().Be(2, "System errors should return exit code 2");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExitCode_MissingRequiredArgument_ReturnsOne()
{
// Arrange - Missing required argument scenario
@@ -66,7 +71,8 @@ public sealed class AirGapCliToolTests
expectedExitCode.Should().Be(1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExitCode_InvalidFeedPath_ReturnsOne()
{
// Arrange - Invalid feed path scenario
@@ -84,7 +90,8 @@ public sealed class AirGapCliToolTests
expectedExitCode.Should().Be(1, "Invalid feed path should return exit code 1");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExitCode_HelpFlag_ReturnsZero()
{
// Arrange
@@ -96,7 +103,8 @@ public sealed class AirGapCliToolTests
expectedExitCode.Should().Be(0, "--help should return exit code 0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ExitCode_VersionFlag_ReturnsZero()
{
// Arrange
@@ -112,7 +120,8 @@ public sealed class AirGapCliToolTests
#region AIRGAP-5100-014: Golden Output Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GoldenOutput_ExportCommand_IncludesManifestSummary()
{
// Arrange - Expected output structure for export command
@@ -135,7 +144,8 @@ public sealed class AirGapCliToolTests
expectedOutputLines.Should().Contain(l => l.Contains("Digest:"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GoldenOutput_ExportCommand_IncludesBundleDigest()
{
// Arrange
@@ -145,7 +155,8 @@ public sealed class AirGapCliToolTests
digestPattern.Should().Contain("sha256:");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GoldenOutput_ImportCommand_IncludesImportSummary()
{
// Arrange - Expected output structure for import command
@@ -165,7 +176,8 @@ public sealed class AirGapCliToolTests
expectedOutputLines.Should().Contain(l => l.Contains("imported successfully"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GoldenOutput_ListCommand_IncludesBundleTable()
{
// Arrange - Expected output structure for list command
@@ -177,7 +189,8 @@ public sealed class AirGapCliToolTests
expectedHeaders.Should().Contain("Version");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GoldenOutput_ValidateCommand_IncludesValidationResult()
{
// Arrange - Expected output structure for validate command
@@ -195,7 +208,8 @@ public sealed class AirGapCliToolTests
expectedOutputLines.Should().Contain(l => l.Contains("Validation:"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GoldenOutput_ErrorMessage_IncludesContext()
{
// Arrange - Error message format
@@ -210,7 +224,8 @@ public sealed class AirGapCliToolTests
#region AIRGAP-5100-015: CLI Determinism Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CliDeterminism_SameInputs_SameOutputDigest()
{
// Arrange - Simulate CLI determinism
@@ -225,7 +240,8 @@ public sealed class AirGapCliToolTests
digest1.Should().Be(digest2, "Same inputs should produce same digest");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CliDeterminism_OutputBundleName_IsDeterministic()
{
// Arrange
@@ -241,7 +257,8 @@ public sealed class AirGapCliToolTests
filename1.Should().Be(filename2, "Same parameters should produce same filename");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CliDeterminism_ManifestJson_IsDeterministic()
{
// Arrange
@@ -256,7 +273,8 @@ public sealed class AirGapCliToolTests
json1.Should().Be(json2);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CliDeterminism_FeedOrdering_IsDeterministic()
{
// Arrange - Feeds in different order
@@ -272,7 +290,8 @@ public sealed class AirGapCliToolTests
"Canonical ordering should be deterministic");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CliDeterminism_DigestComputation_IsDeterministic()
{
// Arrange
@@ -290,7 +309,8 @@ public sealed class AirGapCliToolTests
digest3.Should().Be(expectedDigest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CliDeterminism_TimestampFormat_IsDeterministic()
{
// Arrange

View File

@@ -14,6 +14,7 @@ using StellaOps.AirGap.Bundle.Serialization;
using StellaOps.AirGap.Bundle.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.AirGap.Bundle.Tests;
/// <summary>
@@ -48,7 +49,8 @@ public sealed class AirGapIntegrationTests : IDisposable
#region AIRGAP-5100-016: Online Offline Bundle Transfer Integration
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Integration_OnlineExport_OfflineImport_DataIntegrity()
{
// Arrange - Create source data in "online" environment
@@ -102,7 +104,8 @@ public sealed class AirGapIntegrationTests : IDisposable
importedFeedContent.Should().Contain("CVE-2024-0001");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Integration_BundleTransfer_PreservesAllComponents()
{
// Arrange - Create multi-component bundle
@@ -143,7 +146,8 @@ public sealed class AirGapIntegrationTests : IDisposable
File.Exists(Path.Combine(offlinePath, "certs/root.pem")).Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Integration_CorruptedBundle_ImportFails()
{
// Arrange
@@ -185,7 +189,8 @@ public sealed class AirGapIntegrationTests : IDisposable
#region AIRGAP-5100-017: Policy Export/Import/Evaluation Integration
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Integration_PolicyExport_PolicyImport_IdenticalVerdict()
{
// Arrange - Create a policy in online environment
@@ -242,7 +247,8 @@ public sealed class AirGapIntegrationTests : IDisposable
importedDigest.Should().Be(originalDigest, "Policy digest should match");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Integration_MultiplePolices_MaintainOrder()
{
// Arrange - Create multiple policies
@@ -289,7 +295,8 @@ public sealed class AirGapIntegrationTests : IDisposable
File.Exists(Path.Combine(offlinePath, "policies/policy3.rego")).Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Integration_PolicyWithCrypto_BothTransferred()
{
// Arrange

View File

@@ -35,7 +35,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
#region Same Inputs Same Hash Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_SameInputs_SameComponentDigests()
{
// Arrange
@@ -55,7 +56,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
manifest1.Feeds[0].Digest.Should().Be(manifest2.Feeds[0].Digest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_SameManifestContent_SameBundleDigest()
{
// Arrange
@@ -70,7 +72,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
digest1.Should().Be(digest2);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_MultipleBuilds_SameDigests()
{
// Arrange
@@ -93,7 +96,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
digests.Distinct().Should().HaveCount(1, "All builds should produce the same digest");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Determinism_Sha256_StableAcrossCalls()
{
// Arrange
@@ -115,7 +119,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
#region Roundtrip Determinism Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Roundtrip_ExportImportReexport_IdenticalBundle()
{
// Arrange
@@ -147,6 +152,7 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
// Re-export using the imported file
var reimportFeedFile = CreateSourceFile("reimport/feed.json", importedContent);
using StellaOps.TestKit;
var request2 = new BundleBuildRequest(
"roundtrip-test",
"1.0.0",
@@ -165,7 +171,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
manifest1.Feeds[0].Digest.Should().Be(manifest2.Feeds[0].Digest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Roundtrip_ManifestSerialize_Deserialize_Identical()
{
// Arrange
@@ -179,7 +186,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
restored.Should().BeEquivalentTo(original);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Roundtrip_ManifestSerialize_Reserialize_SameJson()
{
// Arrange
@@ -198,7 +206,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
#region Content Independence Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_SameContent_DifferentSourcePath_SameDigest()
{
// Arrange
@@ -219,7 +228,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
manifest1.Feeds[0].Digest.Should().Be(manifest2.Feeds[0].Digest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_DifferentContent_DifferentDigest()
{
// Arrange
@@ -243,7 +253,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
#region Multiple Component Determinism
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_MultipleFeeds_EachHasCorrectDigest()
{
// Arrange
@@ -278,7 +289,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
manifest.Feeds[2].Digest.Should().Be(ComputeSha256(content3));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_OrderIndependence_SameManifestDigest()
{
// Note: This test verifies that the bundle digest is computed deterministically
@@ -300,7 +312,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
#region Binary Content Determinism
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_BinaryContent_SameDigest()
{
// Arrange
@@ -340,7 +353,8 @@ public sealed class BundleDeterminismTests : IAsyncLifetime
manifest1.Feeds[0].Digest.Should().Be(manifest2.Feeds[0].Digest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_LargeContent_SameDigest()
{
// Arrange

View File

@@ -44,7 +44,8 @@ public sealed class BundleExportImportTests : IDisposable
#region AIRGAP-5100-001: Bundle Export Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_CreatesValidBundleStructure()
{
// Arrange
@@ -63,7 +64,8 @@ public sealed class BundleExportImportTests : IDisposable
manifest.Feeds.Should().HaveCount(1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_SetsCorrectManifestFields()
{
// Arrange
@@ -83,7 +85,8 @@ public sealed class BundleExportImportTests : IDisposable
manifest.CreatedAt.Should().BeCloseTo(DateTimeOffset.UtcNow, TimeSpan.FromSeconds(5));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_ComputesCorrectFileDigests()
{
// Arrange
@@ -107,7 +110,8 @@ public sealed class BundleExportImportTests : IDisposable
feedDigest.Should().Be(expectedDigest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_ComputesCorrectBundleDigest()
{
// Arrange
@@ -124,7 +128,8 @@ public sealed class BundleExportImportTests : IDisposable
manifest.BundleDigest.Should().HaveLength(64);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_TracksCorrectFileSizes()
{
// Arrange
@@ -146,7 +151,8 @@ public sealed class BundleExportImportTests : IDisposable
#region AIRGAP-5100-002: Bundle Import Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_LoadsManifestCorrectly()
{
// Arrange - First export a bundle
@@ -170,7 +176,8 @@ public sealed class BundleExportImportTests : IDisposable
loaded.Version.Should().Be("1.0.0");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_VerifiesFileIntegrity()
{
// Arrange
@@ -198,7 +205,8 @@ public sealed class BundleExportImportTests : IDisposable
loaded.Feeds[0].Digest.Should().Be(actualDigest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_FailsOnCorruptedFile()
{
// Arrange
@@ -230,7 +238,8 @@ public sealed class BundleExportImportTests : IDisposable
#region AIRGAP-5100-003: Determinism Tests (Same Inputs Same Hash)
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_SameInputs_ProduceSameBundleDigest()
{
// Arrange
@@ -271,7 +280,8 @@ public sealed class BundleExportImportTests : IDisposable
"Same content should produce same file digest");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Determinism_DifferentInputs_ProduceDifferentDigests()
{
// Arrange
@@ -294,7 +304,8 @@ public sealed class BundleExportImportTests : IDisposable
"Different content should produce different digests");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Determinism_ManifestSerialization_IsStable()
{
// Arrange
@@ -314,7 +325,8 @@ public sealed class BundleExportImportTests : IDisposable
#region AIRGAP-5100-004: Roundtrip Determinism (Export Import Re-export)
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Roundtrip_ExportImportReexport_ProducesIdenticalFileDigests()
{
// Arrange - Initial export
@@ -337,6 +349,7 @@ public sealed class BundleExportImportTests : IDisposable
// Re-export using the imported bundle's files
var reexportFeedFile = Path.Combine(bundlePath1, "feeds", "nvd.json");
using StellaOps.TestKit;
var reexportRequest = new BundleBuildRequest(
imported.Name,
imported.Version,
@@ -360,7 +373,8 @@ public sealed class BundleExportImportTests : IDisposable
digest1.Should().Be(digest2, "Roundtrip should produce identical file digests");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Roundtrip_ManifestSerialization_PreservesAllFields()
{
// Arrange

View File

@@ -7,6 +7,7 @@ using StellaOps.AirGap.Bundle.Serialization;
using StellaOps.AirGap.Bundle.Services;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.AirGap.Bundle.Tests;
/// <summary>
@@ -35,7 +36,8 @@ public sealed class BundleExportTests : IAsyncLifetime
#region L0 Export Structure Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_EmptyBundle_CreatesValidManifest()
{
// Arrange
@@ -65,7 +67,8 @@ public sealed class BundleExportTests : IAsyncLifetime
manifest.TotalSizeBytes.Should().Be(0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_WithFeed_CopiesFileAndComputesDigest()
{
// Arrange
@@ -111,7 +114,8 @@ public sealed class BundleExportTests : IAsyncLifetime
File.Exists(Path.Combine(outputPath, "feeds/nvd.json")).Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_WithPolicy_CopiesFileAndComputesDigest()
{
// Arrange
@@ -153,7 +157,8 @@ public sealed class BundleExportTests : IAsyncLifetime
File.Exists(Path.Combine(outputPath, "policies/default.rego")).Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_WithCryptoMaterial_CopiesFileAndComputesDigest()
{
// Arrange
@@ -195,7 +200,8 @@ public sealed class BundleExportTests : IAsyncLifetime
File.Exists(Path.Combine(outputPath, "certs/root.pem")).Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_MultipleComponents_CalculatesTotalSize()
{
// Arrange
@@ -234,7 +240,8 @@ public sealed class BundleExportTests : IAsyncLifetime
#region Digest Computation Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_DigestComputation_MatchesSha256()
{
// Arrange
@@ -263,7 +270,8 @@ public sealed class BundleExportTests : IAsyncLifetime
manifest.Feeds[0].Digest.Should().BeEquivalentTo(expectedDigest, options => options.IgnoringCase());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_BundleDigest_ComputedFromManifest()
{
// Arrange
@@ -294,7 +302,8 @@ public sealed class BundleExportTests : IAsyncLifetime
#region Directory Structure Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_CreatesNestedDirectories()
{
// Arrange
@@ -338,7 +347,8 @@ public sealed class BundleExportTests : IAsyncLifetime
#region Feed Format Tests
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(FeedFormat.StellaOpsNative)]
[InlineData(FeedFormat.TrivyDb)]
[InlineData(FeedFormat.GrypeDb)]
@@ -372,7 +382,8 @@ public sealed class BundleExportTests : IAsyncLifetime
#region Policy Type Tests
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(PolicyType.OpaRego)]
[InlineData(PolicyType.LatticeRules)]
[InlineData(PolicyType.UnknownBudgets)]
@@ -406,7 +417,8 @@ public sealed class BundleExportTests : IAsyncLifetime
#region Crypto Component Type Tests
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(CryptoComponentType.TrustRoot)]
[InlineData(CryptoComponentType.IntermediateCa)]
[InlineData(CryptoComponentType.TimestampRoot)]
@@ -441,7 +453,8 @@ public sealed class BundleExportTests : IAsyncLifetime
#region Expiration Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_WithExpiration_PreservesExpiryDate()
{
// Arrange
@@ -464,7 +477,8 @@ public sealed class BundleExportTests : IAsyncLifetime
manifest.ExpiresAt.Should().BeCloseTo(expiresAt, TimeSpan.FromSeconds(1));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Export_CryptoWithExpiration_PreservesComponentExpiry()
{
// Arrange

View File

@@ -9,6 +9,8 @@ using StellaOps.AirGap.Bundle.Services;
using StellaOps.AirGap.Bundle.Validation;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.AirGap.Bundle.Tests;
/// <summary>
@@ -37,7 +39,8 @@ public sealed class BundleImportTests : IAsyncLifetime
#region Manifest Parsing Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Import_ManifestDeserialization_PreservesAllFields()
{
// Arrange
@@ -51,7 +54,8 @@ public sealed class BundleImportTests : IAsyncLifetime
imported.Should().BeEquivalentTo(manifest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Import_ManifestDeserialization_HandlesEmptyCollections()
{
// Arrange
@@ -67,7 +71,8 @@ public sealed class BundleImportTests : IAsyncLifetime
imported.CryptoMaterials.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Import_ManifestDeserialization_PreservesFeedComponents()
{
// Arrange
@@ -85,7 +90,8 @@ public sealed class BundleImportTests : IAsyncLifetime
imported.Feeds[1].Format.Should().Be(FeedFormat.OsvJson);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Import_ManifestDeserialization_PreservesPolicyComponents()
{
// Arrange
@@ -101,7 +107,8 @@ public sealed class BundleImportTests : IAsyncLifetime
imported.Policies[1].Type.Should().Be(PolicyType.LatticeRules);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Import_ManifestDeserialization_PreservesCryptoComponents()
{
// Arrange
@@ -121,7 +128,8 @@ public sealed class BundleImportTests : IAsyncLifetime
#region Validation Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Validation_FailsWhenFilesMissing()
{
// Arrange
@@ -141,7 +149,8 @@ public sealed class BundleImportTests : IAsyncLifetime
result.Errors.Should().Contain(e => e.Message.Contains("digest mismatch") || e.Message.Contains("FILE_NOT_FOUND"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Validation_FailsWhenDigestMismatch()
{
// Arrange
@@ -158,7 +167,8 @@ public sealed class BundleImportTests : IAsyncLifetime
result.Errors.Should().Contain(e => e.Message.Contains("digest mismatch"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Validation_SucceedsWhenAllDigestsMatch()
{
// Arrange
@@ -175,7 +185,8 @@ public sealed class BundleImportTests : IAsyncLifetime
result.Errors.Should().BeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Validation_WarnsWhenExpired()
{
// Arrange
@@ -195,7 +206,8 @@ public sealed class BundleImportTests : IAsyncLifetime
result.Warnings.Should().Contain(w => w.Message.Contains("expired"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Validation_WarnsWhenFeedsOld()
{
// Arrange
@@ -224,7 +236,8 @@ public sealed class BundleImportTests : IAsyncLifetime
#region Bundle Loader Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Loader_RegistersAllFeeds()
{
// Arrange
@@ -252,7 +265,8 @@ public sealed class BundleImportTests : IAsyncLifetime
feedRegistry.Received(manifest.Feeds.Length).Register(Arg.Any<FeedComponent>(), Arg.Any<string>());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Loader_RegistersAllPolicies()
{
// Arrange
@@ -279,7 +293,8 @@ public sealed class BundleImportTests : IAsyncLifetime
policyRegistry.Received(manifest.Policies.Length).Register(Arg.Any<PolicyComponent>(), Arg.Any<string>());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Loader_ThrowsOnValidationFailure()
{
// Arrange
@@ -306,7 +321,8 @@ public sealed class BundleImportTests : IAsyncLifetime
.WithMessage("*validation failed*");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_Loader_ThrowsOnMissingManifest()
{
// Arrange
@@ -330,7 +346,8 @@ public sealed class BundleImportTests : IAsyncLifetime
#region Digest Verification Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_DigestVerification_MatchesExpected()
{
// Arrange
@@ -346,7 +363,8 @@ public sealed class BundleImportTests : IAsyncLifetime
actualDigest.Should().BeEquivalentTo(expectedDigest, options => options.IgnoringCase());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Import_DigestVerification_FailsOnTamperedFile()
{
// Arrange

View File

@@ -6,11 +6,13 @@ using StellaOps.AirGap.Bundle.Services;
using StellaOps.AirGap.Bundle.Validation;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.AirGap.Bundle.Tests;
public class BundleManifestTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Serializer_RoundTrip_PreservesFields()
{
var manifest = CreateManifest();
@@ -19,7 +21,8 @@ public class BundleManifestTests
deserialized.Should().BeEquivalentTo(manifest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Validator_FlagsMissingFeedFile()
{
var manifest = CreateManifest();
@@ -30,7 +33,8 @@ public class BundleManifestTests
result.Errors.Should().NotBeEmpty();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Builder_CopiesComponentsAndComputesDigest()
{
var tempRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

View File

@@ -16,5 +16,6 @@
<ItemGroup>
<ProjectReference Include="..\StellaOps.AirGap.Bundle\StellaOps.AirGap.Bundle.csproj" />
<ProjectReference Include="../../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>