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:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user