Add unit tests for RabbitMq and Udp transport servers and clients
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Implemented comprehensive unit tests for RabbitMqTransportServer, covering constructor, disposal, connection management, event handlers, and exception handling.
- Added configuration tests for RabbitMqTransportServer to validate SSL, durable queues, auto-recovery, and custom virtual host options.
- Created unit tests for UdpFrameProtocol, including frame parsing and serialization, header size validation, and round-trip data preservation.
- Developed tests for UdpTransportClient, focusing on connection handling, event subscriptions, and exception scenarios.
- Established tests for UdpTransportServer, ensuring proper start/stop behavior, connection state management, and event handling.
- Included tests for UdpTransportOptions to verify default values and modification capabilities.
- Enhanced service registration tests for Udp transport services in the dependency injection container.
This commit is contained in:
master
2025-12-05 19:01:12 +02:00
parent 53508ceccb
commit cc69d332e3
245 changed files with 22440 additions and 27719 deletions

View File

@@ -3,6 +3,7 @@ using System.Text.Json;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using StellaOps.Auth.Abstractions;
using StellaOps.Cryptography;
using StellaOps.Policy.Engine.Services;
using StellaOps.Policy.RiskProfile.Export;
@@ -145,7 +146,8 @@ internal static class ProfileExportEndpoints
HttpContext context,
[FromBody] ImportProfilesRequest request,
RiskProfileConfigurationService profileService,
ProfileExportService exportService)
ProfileExportService exportService,
ICryptoHash cryptoHash)
{
var scopeResult = ScopeAuthorization.RequireScope(context, StellaOpsScopes.PolicyEdit);
if (scopeResult is not null)
@@ -167,6 +169,7 @@ internal static class ProfileExportEndpoints
// Create an export service with save capability
var importExportService = new ProfileExportService(
cryptoHash: cryptoHash,
timeProvider: TimeProvider.System,
profileLookup: id => profileService.GetProfile(id),
lifecycleLookup: null,

View File

@@ -1,7 +1,7 @@
using System.Collections.Concurrent;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography;
using StellaOps.Policy.Engine.Services;
using StellaOps.Policy.Engine.Telemetry;
using StellaOps.Policy.RiskProfile.Hashing;
@@ -18,6 +18,7 @@ public sealed class RiskScoringTriggerService
private readonly RiskProfileConfigurationService _profileService;
private readonly IRiskScoringJobStore _jobStore;
private readonly RiskProfileHasher _hasher;
private readonly ICryptoHash _cryptoHash;
private readonly ConcurrentDictionary<string, DateTimeOffset> _recentTriggers;
private readonly TimeSpan _deduplicationWindow;
@@ -25,13 +26,15 @@ public sealed class RiskScoringTriggerService
ILogger<RiskScoringTriggerService> logger,
TimeProvider timeProvider,
RiskProfileConfigurationService profileService,
IRiskScoringJobStore jobStore)
IRiskScoringJobStore jobStore,
ICryptoHash cryptoHash)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
_profileService = profileService ?? throw new ArgumentNullException(nameof(profileService));
_jobStore = jobStore ?? throw new ArgumentNullException(nameof(jobStore));
_hasher = new RiskProfileHasher();
_cryptoHash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
_hasher = new RiskProfileHasher(cryptoHash);
_recentTriggers = new ConcurrentDictionary<string, DateTimeOffset>();
_deduplicationWindow = TimeSpan.FromMinutes(5);
}
@@ -256,10 +259,10 @@ public sealed class RiskScoringTriggerService
}
}
private static string GenerateJobId(string tenantId, string contextId, DateTimeOffset timestamp)
private string GenerateJobId(string tenantId, string contextId, DateTimeOffset timestamp)
{
var seed = $"{tenantId}|{contextId}|{timestamp:O}|{Guid.NewGuid()}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(seed));
return $"rsj-{Convert.ToHexStringLower(hash)[..16]}";
var hash = _cryptoHash.ComputeHashHexForPurpose(Encoding.UTF8.GetBytes(seed), HashPurpose.Content);
return $"rsj-{hash[..16]}";
}
}

View File

@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
using StellaOps.Policy.Engine.Options;
using StellaOps.Policy.RiskProfile.Hashing;
using StellaOps.Policy.RiskProfile.Merge;
@@ -27,12 +28,14 @@ public sealed class RiskProfileConfigurationService
public RiskProfileConfigurationService(
ILogger<RiskProfileConfigurationService> logger,
IOptions<PolicyEngineOptions> options)
IOptions<PolicyEngineOptions> options,
ICryptoHash cryptoHash)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options?.Value.RiskProfile ?? throw new ArgumentNullException(nameof(options));
ArgumentNullException.ThrowIfNull(cryptoHash);
_mergeService = new RiskProfileMergeService();
_hasher = new RiskProfileHasher();
_hasher = new RiskProfileHasher(cryptoHash);
_validator = new RiskProfileValidator();
_profileCache = new ConcurrentDictionary<string, RiskProfileModel>(StringComparer.OrdinalIgnoreCase);
_resolvedCache = new ConcurrentDictionary<string, RiskProfileModel>(StringComparer.OrdinalIgnoreCase);

View File

@@ -1,8 +1,8 @@
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using StellaOps.Cryptography;
using StellaOps.Policy.Engine.Services;
using StellaOps.Policy.Engine.Telemetry;
using StellaOps.Policy.RiskProfile.Hashing;
@@ -19,6 +19,7 @@ public sealed class RiskSimulationService
private readonly TimeProvider _timeProvider;
private readonly RiskProfileConfigurationService _profileService;
private readonly RiskProfileHasher _hasher;
private readonly ICryptoHash _cryptoHash;
private static readonly double[] PercentileLevels = { 0.25, 0.50, 0.75, 0.90, 0.95, 0.99 };
private const int TopMoverCount = 10;
@@ -27,12 +28,14 @@ public sealed class RiskSimulationService
public RiskSimulationService(
ILogger<RiskSimulationService> logger,
TimeProvider timeProvider,
RiskProfileConfigurationService profileService)
RiskProfileConfigurationService profileService,
ICryptoHash cryptoHash)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
_profileService = profileService ?? throw new ArgumentNullException(nameof(profileService));
_hasher = new RiskProfileHasher();
_cryptoHash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
_hasher = new RiskProfileHasher(cryptoHash);
}
/// <summary>
@@ -452,10 +455,10 @@ public sealed class RiskSimulationService
InformationalCount: scores.Count(s => s.Severity == RiskSeverity.Informational));
}
private static string GenerateSimulationId(RiskSimulationRequest request, string profileHash)
private string GenerateSimulationId(RiskSimulationRequest request, string profileHash)
{
var seed = $"{request.ProfileId}|{profileHash}|{request.Findings.Count}|{Guid.NewGuid()}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(seed));
return $"rsim-{Convert.ToHexStringLower(hash)[..16]}";
var hash = _cryptoHash.ComputeHashHexForPurpose(Encoding.UTF8.GetBytes(seed), HashPurpose.Content);
return $"rsim-{hash[..16]}";
}
}

View File

@@ -27,6 +27,7 @@
<ProjectReference Include="../__Libraries/StellaOps.Policy/StellaOps.Policy.csproj" />
<ProjectReference Include="../StellaOps.PolicyDsl/StellaOps.PolicyDsl.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj" />
<ProjectReference Include="../../Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOps.Auth.Abstractions.csproj" />
<ProjectReference Include="../../Authority/StellaOps.Authority/StellaOps.Auth.Client/StellaOps.Auth.Client.csproj" />

View File

@@ -1,6 +1,7 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using StellaOps.Cryptography;
using StellaOps.Policy.RiskProfile.Hashing;
using StellaOps.Policy.RiskProfile.Lifecycle;
using StellaOps.Policy.RiskProfile.Models;
@@ -17,6 +18,7 @@ public sealed class ProfileExportService
private const string DefaultAlgorithm = "HMAC-SHA256";
private readonly TimeProvider _timeProvider;
private readonly ICryptoHash _cryptoHash;
private readonly RiskProfileHasher _hasher;
private readonly Func<string, RiskProfileModel?>? _profileLookup;
private readonly Func<string, RiskProfileVersionInfo?>? _lifecycleLookup;
@@ -30,14 +32,16 @@ public sealed class ProfileExportService
};
public ProfileExportService(
ICryptoHash cryptoHash,
TimeProvider? timeProvider = null,
Func<string, RiskProfileModel?>? profileLookup = null,
Func<string, RiskProfileVersionInfo?>? lifecycleLookup = null,
Action<RiskProfileModel>? profileSave = null,
Func<string, string?>? keyLookup = null)
{
_cryptoHash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
_timeProvider = timeProvider ?? TimeProvider.System;
_hasher = new RiskProfileHasher();
_hasher = new RiskProfileHasher(cryptoHash);
_profileLookup = profileLookup;
_lifecycleLookup = lifecycleLookup;
_profileSave = profileSave;
@@ -331,15 +335,14 @@ public sealed class ProfileExportService
.ThenBy(p => p.Profile.Version)
.Select(p => p.ContentHash));
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(combined));
return Convert.ToHexStringLower(hashBytes);
return _cryptoHash.ComputeHashHexForPurpose(Encoding.UTF8.GetBytes(combined), HashPurpose.Content);
}
private static string GenerateBundleId(DateTimeOffset timestamp)
private string GenerateBundleId(DateTimeOffset timestamp)
{
var seed = $"{timestamp:O}|{Guid.NewGuid()}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(seed));
return $"rpb-{Convert.ToHexStringLower(hash)[..16]}";
var hash = _cryptoHash.ComputeHashHexForPurpose(Encoding.UTF8.GetBytes(seed), HashPurpose.Content);
return $"rpb-{hash[..16]}";
}
private static string GetSourceVersion()

View File

@@ -1,7 +1,7 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using StellaOps.Cryptography;
using StellaOps.Policy.RiskProfile.Models;
namespace StellaOps.Policy.RiskProfile.Hashing;
@@ -11,6 +11,8 @@ namespace StellaOps.Policy.RiskProfile.Hashing;
/// </summary>
public sealed class RiskProfileHasher
{
private readonly ICryptoHash _cryptoHash;
private static readonly JsonSerializerOptions CanonicalJsonOptions = new()
{
WriteIndented = false,
@@ -22,20 +24,24 @@ public sealed class RiskProfileHasher
},
};
public RiskProfileHasher(ICryptoHash cryptoHash)
{
_cryptoHash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
}
/// <summary>
/// Computes a deterministic SHA-256 hash of the risk profile.
/// Computes a deterministic hash of the risk profile using the compliance profile's content algorithm.
/// </summary>
/// <param name="profile">The profile to hash.</param>
/// <returns>Lowercase hex-encoded SHA-256 hash.</returns>
/// <returns>Lowercase hex-encoded hash.</returns>
public string ComputeHash(RiskProfileModel profile)
{
ArgumentNullException.ThrowIfNull(profile);
var canonical = CreateCanonicalForm(profile);
var json = JsonSerializer.Serialize(canonical, CanonicalJsonOptions);
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(json));
return Convert.ToHexStringLower(hash);
return _cryptoHash.ComputeHashHexForPurpose(Encoding.UTF8.GetBytes(json), HashPurpose.Content);
}
/// <summary>
@@ -43,16 +49,15 @@ public sealed class RiskProfileHasher
/// Useful for detecting semantic changes regardless of versioning.
/// </summary>
/// <param name="profile">The profile to hash.</param>
/// <returns>Lowercase hex-encoded SHA-256 hash.</returns>
/// <returns>Lowercase hex-encoded hash.</returns>
public string ComputeContentHash(RiskProfileModel profile)
{
ArgumentNullException.ThrowIfNull(profile);
var canonical = CreateCanonicalContentForm(profile);
var json = JsonSerializer.Serialize(canonical, CanonicalJsonOptions);
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(json));
return Convert.ToHexStringLower(hash);
return _cryptoHash.ComputeHashHexForPurpose(Encoding.UTF8.GetBytes(json), HashPurpose.Content);
}
/// <summary>

View File

@@ -1,6 +1,5 @@
using System.Collections.Concurrent;
using System.Security.Cryptography;
using System.Text;
using StellaOps.Cryptography;
using StellaOps.Policy.RiskProfile.Hashing;
using StellaOps.Policy.RiskProfile.Models;
@@ -16,10 +15,11 @@ public sealed class RiskProfileLifecycleService
private readonly ConcurrentDictionary<string, List<RiskProfileVersionInfo>> _versions;
private readonly ConcurrentDictionary<string, List<RiskProfileLifecycleEvent>> _events;
public RiskProfileLifecycleService(TimeProvider? timeProvider = null)
public RiskProfileLifecycleService(ICryptoHash cryptoHash, TimeProvider? timeProvider = null)
{
ArgumentNullException.ThrowIfNull(cryptoHash);
_timeProvider = timeProvider ?? TimeProvider.System;
_hasher = new RiskProfileHasher();
_hasher = new RiskProfileHasher(cryptoHash);
_versions = new ConcurrentDictionary<string, List<RiskProfileVersionInfo>>(StringComparer.OrdinalIgnoreCase);
_events = new ConcurrentDictionary<string, List<RiskProfileLifecycleEvent>>(StringComparer.OrdinalIgnoreCase);
}

View File

@@ -11,6 +11,10 @@
<PackageReference Include="JsonSchema.Net" Version="5.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Schemas\risk-profile-schema@1.json" />
</ItemGroup>