UI work to fill SBOM sourcing management gap. UI planning remaining functionality exposure. Work on CI/Tests stabilization

Introduces CGS determinism test runs to CI workflows for Windows, macOS, Linux, Alpine, and Debian, fulfilling CGS-008 cross-platform requirements. Updates local-ci scripts to support new smoke steps, test timeouts, progress intervals, and project slicing for improved test isolation and diagnostics.
This commit is contained in:
master
2025-12-29 19:12:38 +02:00
parent 41552d26ec
commit a4badc275e
286 changed files with 50918 additions and 992 deletions

View File

@@ -363,6 +363,7 @@ internal sealed class NatsNotifyDeliveryQueue : INotifyDeliveryQueue, IAsyncDisp
var consumerConfig = new ConsumerConfig
{
Name = _options.DurableConsumer,
DurableName = _options.DurableConsumer,
AckPolicy = ConsumerConfigAckPolicy.Explicit,
ReplayPolicy = ConsumerConfigReplayPolicy.Instant,
@@ -373,6 +374,23 @@ internal sealed class NatsNotifyDeliveryQueue : INotifyDeliveryQueue, IAsyncDisp
FilterSubjects = new[] { _options.Subject }
};
try
{
_consumer = await js.GetConsumerAsync(
_options.Stream,
_options.DurableConsumer,
cancellationToken)
.ConfigureAwait(false);
return _consumer;
}
catch (NatsJSApiException apiEx) when (IsConsumerNotFound(apiEx))
{
_logger.LogDebug(
apiEx,
"Durable consumer {Durable} not found; creating new consumer.",
_options.DurableConsumer);
}
try
{
_consumer = await js.CreateConsumerAsync(
@@ -381,12 +399,11 @@ internal sealed class NatsNotifyDeliveryQueue : INotifyDeliveryQueue, IAsyncDisp
cancellationToken)
.ConfigureAwait(false);
}
catch (NatsJSApiException apiEx)
catch (NatsJSApiException apiEx) when (IsConsumerAlreadyExists(apiEx))
{
_logger.LogDebug(
apiEx,
"CreateConsumerAsync failed with code {Code}; attempting to fetch existing durable consumer {Durable}.",
apiEx.Error?.Code,
"Consumer {Durable} already exists; fetching existing durable consumer.",
_options.DurableConsumer);
_consumer = await js.GetConsumerAsync(
@@ -444,7 +461,7 @@ internal sealed class NatsNotifyDeliveryQueue : INotifyDeliveryQueue, IAsyncDisp
{
await js.GetStreamAsync(_options.Stream, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch (NatsJSApiException ex) when (ex.Error?.Code == 404)
catch (NatsJSApiException ex) when (IsStreamNotFound(ex))
{
var config = new StreamConfig(name: _options.Stream, subjects: new[] { _options.Subject })
{
@@ -466,7 +483,7 @@ internal sealed class NatsNotifyDeliveryQueue : INotifyDeliveryQueue, IAsyncDisp
{
await js.GetStreamAsync(_options.DeadLetterStream, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch (NatsJSApiException ex) when (ex.Error?.Code == 404)
catch (NatsJSApiException ex) when (IsStreamNotFound(ex))
{
var config = new StreamConfig(name: _options.DeadLetterStream, subjects: new[] { _options.DeadLetterSubject })
{
@@ -688,6 +705,43 @@ internal sealed class NatsNotifyDeliveryQueue : INotifyDeliveryQueue, IAsyncDisp
private static long ToNanoseconds(TimeSpan value)
=> value <= TimeSpan.Zero ? 0 : value.Ticks * 100L;
private static bool IsStreamNotFound(NatsJSApiException ex)
{
var code = ex.Error?.Code ?? 0;
if (code is 404 or 10059)
{
return true;
}
var message = ex.Error?.Description ?? ex.Message;
return message.Contains("stream not found", StringComparison.OrdinalIgnoreCase);
}
private static bool IsConsumerNotFound(NatsJSApiException ex)
{
var code = ex.Error?.Code ?? 0;
if (code is 404 or 10014)
{
return true;
}
var message = ex.Error?.Description ?? ex.Message;
return message.Contains("consumer not found", StringComparison.OrdinalIgnoreCase);
}
private static bool IsConsumerAlreadyExists(NatsJSApiException ex)
{
var code = ex.Error?.Code ?? 0;
if (code == 10013)
{
return true;
}
var message = ex.Error?.Description ?? ex.Message;
return message.Contains("consumer already exists", StringComparison.OrdinalIgnoreCase)
|| message.Contains("consumer name already in use", StringComparison.OrdinalIgnoreCase);
}
private static class EmptyReadOnlyDictionary<TKey, TValue>
where TKey : notnull
{

View File

@@ -371,6 +371,7 @@ internal sealed class NatsNotifyEventQueue : INotifyEventQueue, IAsyncDisposable
var consumerConfig = new ConsumerConfig
{
Name = _options.DurableConsumer,
DurableName = _options.DurableConsumer,
AckPolicy = ConsumerConfigAckPolicy.Explicit,
ReplayPolicy = ConsumerConfigReplayPolicy.Instant,
@@ -381,6 +382,23 @@ internal sealed class NatsNotifyEventQueue : INotifyEventQueue, IAsyncDisposable
FilterSubjects = new[] { _options.Subject }
};
try
{
_consumer = await js.GetConsumerAsync(
_options.Stream,
_options.DurableConsumer,
cancellationToken)
.ConfigureAwait(false);
return _consumer;
}
catch (NatsJSApiException apiEx) when (IsConsumerNotFound(apiEx))
{
_logger.LogDebug(
apiEx,
"Durable consumer {Durable} not found; creating new consumer.",
_options.DurableConsumer);
}
try
{
_consumer = await js.CreateConsumerAsync(
@@ -389,12 +407,11 @@ internal sealed class NatsNotifyEventQueue : INotifyEventQueue, IAsyncDisposable
cancellationToken)
.ConfigureAwait(false);
}
catch (NatsJSApiException apiEx)
catch (NatsJSApiException apiEx) when (IsConsumerAlreadyExists(apiEx))
{
_logger.LogDebug(
apiEx,
"CreateConsumerAsync failed with code {Code}; attempting to fetch existing durable consumer {Durable}.",
apiEx.Error?.Code,
"Consumer {Durable} already exists; fetching existing durable consumer.",
_options.DurableConsumer);
_consumer = await js.GetConsumerAsync(
@@ -452,7 +469,7 @@ internal sealed class NatsNotifyEventQueue : INotifyEventQueue, IAsyncDisposable
{
await js.GetStreamAsync(_options.Stream, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch (NatsJSApiException ex) when (ex.Error?.Code == 404)
catch (NatsJSApiException ex) when (IsStreamNotFound(ex))
{
var config = new StreamConfig(name: _options.Stream, subjects: new[] { _options.Subject })
{
@@ -474,7 +491,7 @@ internal sealed class NatsNotifyEventQueue : INotifyEventQueue, IAsyncDisposable
{
await js.GetStreamAsync(_options.DeadLetterStream, cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch (NatsJSApiException ex) when (ex.Error?.Code == 404)
catch (NatsJSApiException ex) when (IsStreamNotFound(ex))
{
var config = new StreamConfig(name: _options.DeadLetterStream, subjects: new[] { _options.DeadLetterSubject })
{
@@ -689,6 +706,43 @@ internal sealed class NatsNotifyEventQueue : INotifyEventQueue, IAsyncDisposable
private static long ToNanoseconds(TimeSpan value)
=> value <= TimeSpan.Zero ? 0 : value.Ticks * 100L;
private static bool IsStreamNotFound(NatsJSApiException ex)
{
var code = ex.Error?.Code ?? 0;
if (code is 404 or 10059)
{
return true;
}
var message = ex.Error?.Description ?? ex.Message;
return message.Contains("stream not found", StringComparison.OrdinalIgnoreCase);
}
private static bool IsConsumerNotFound(NatsJSApiException ex)
{
var code = ex.Error?.Code ?? 0;
if (code is 404 or 10014)
{
return true;
}
var message = ex.Error?.Description ?? ex.Message;
return message.Contains("consumer not found", StringComparison.OrdinalIgnoreCase);
}
private static bool IsConsumerAlreadyExists(NatsJSApiException ex)
{
var code = ex.Error?.Code ?? 0;
if (code == 10013)
{
return true;
}
var message = ex.Error?.Description ?? ex.Message;
return message.Contains("consumer already exists", StringComparison.OrdinalIgnoreCase)
|| message.Contains("consumer name already in use", StringComparison.OrdinalIgnoreCase);
}
private static class EmptyReadOnlyDictionary<TKey, TValue>
where TKey : notnull
{