elsa-core/test/unit/Elsa.Diagnostics.StructuredLogs.UnitTests/Sources/StructuredLogSourceRegistryTests.cs
Sipke Schoorstra ab3e46bbe2
[codex] Add live server log streaming diagnostics (#7438)
* Add live server logs Spec Kit plan

* Implement live server logs diagnostics module

* Add server log sources and redaction hardening

* Add diagnostics unit tests

* Harden server log hub subscriptions

* Secure server log hub permissions

* Validate server log filter updates

* Add diagnostics logger and source tests

* Add diagnostics integration test project

* Add multi-source diagnostics provider coverage

* Broadcast server log source changes

* Document diagnostics server log streaming

* Add diagnostics sample host wiring

* Record diagnostics validation results

* Address server log PR feedback

* Rename diagnostics module to server logs

* Add server logs shell feature

* Make server logs shell options bindable

* Accept read wildcard for server logs

* Align server logs authorization with API patterns

* Update CShells structure and logging levels, add diagnostics module

* Rename PostgreSql shell feature classes for consistency

* Switch from Sqlite to PostgreSQL for workflow and identity persistence, add QuartzPostgreSql configuration

* Refactor server logs into diagnostics structured logs (#7440)

* Specify diagnostics structured logs refactor

* docs: clarify structured logs spec

* docs: plan diagnostics structured logs

* docs: add diagnostics structured logs tasks

* refactor: rename server logs to diagnostics structured logs

* Refactor PostgreSql persistence features to use centralized entity model handler registration.

* Refactor EFCore persistence features to centralize entity model handler registration for MySql, Sqlite, and Oracle providers.

* Integrate structured logs by renaming server logs, adjusting appsettings, and updating project references.

* Switch from PostgreSQL to Sqlite for workflow and identity persistence, update appsettings configuration.
2026-05-11 00:08:52 +02:00

137 lines
4.6 KiB
C#

using Elsa.Diagnostics.StructuredLogs.Models;
using Elsa.Diagnostics.StructuredLogs.Options;
using Elsa.Diagnostics.StructuredLogs.Services;
using MicrosoftOptions = Microsoft.Extensions.Options.Options;
namespace Elsa.Diagnostics.StructuredLogs.UnitTests.Sources;
public class StructuredLogSourceRegistryTests : IDisposable
{
private readonly Dictionary<string, string?> _originalEnvironment = new();
private readonly StructuredLogsOptions _options = new()
{
SourceHeartbeatTimeout = TimeSpan.FromSeconds(30)
};
public StructuredLogSourceRegistryTests()
{
CaptureEnvironment("HOSTNAME");
CaptureEnvironment("OTEL_SERVICE_NAME");
CaptureEnvironment("POD_NAMESPACE");
CaptureEnvironment("CONTAINER_NAME");
CaptureEnvironment("NODE_NAME");
}
[Fact]
public void Current_WhenKubernetesMetadataExists_UsesEnvironmentMetadata()
{
SetEnvironment("HOSTNAME", "elsa-pod-7");
SetEnvironment("OTEL_SERVICE_NAME", "elsa-api");
SetEnvironment("POD_NAMESPACE", "workflows");
SetEnvironment("CONTAINER_NAME", "server");
SetEnvironment("NODE_NAME", "node-a");
var registry = CreateRegistry();
Assert.Equal("elsa-pod-7", registry.Current.DisplayName);
Assert.Equal("elsa-api", registry.Current.ServiceName);
Assert.Equal("workflows", registry.Current.Namespace);
Assert.Equal("server", registry.Current.ContainerName);
Assert.Equal("node-a", registry.Current.NodeName);
}
[Fact]
public void MarkSeen_WhenSourceIsUnknown_AddsSourceWithMatchingId()
{
var registry = CreateRegistry();
registry.MarkSeen("pod-b", DateTimeOffset.UtcNow);
var source = Assert.Single(registry.List(), x => x.Id == "pod-b");
Assert.Equal("pod-b", source.DisplayName);
Assert.Equal(StructuredLogSourceStatus.Connected, source.Status);
}
[Fact]
public void MarkSeen_WhenSourceIsUnknown_DoesNotCopyLocalContainerMetadata()
{
SetEnvironment("HOSTNAME", "local-pod");
SetEnvironment("OTEL_SERVICE_NAME", "local-service");
SetEnvironment("POD_NAMESPACE", "local-namespace");
SetEnvironment("CONTAINER_NAME", "local-container");
SetEnvironment("NODE_NAME", "local-node");
var registry = CreateRegistry();
registry.MarkSeen("pod-b", DateTimeOffset.UtcNow);
var source = Assert.Single(registry.List(), x => x.Id == "pod-b");
Assert.Equal("pod-b", source.MachineName);
Assert.Equal(0, source.ProcessId);
Assert.Null(source.ServiceName);
Assert.Null(source.PodName);
Assert.Null(source.Namespace);
Assert.Null(source.ContainerName);
Assert.Null(source.NodeName);
}
[Fact]
public void MarkSeen_WhenSourceIsUnknown_RaisesSourceChanged()
{
var registry = CreateRegistry();
StructuredLogSource? changedSource = null;
registry.SourceChanged += source => changedSource = source;
registry.MarkSeen("pod-b", DateTimeOffset.UtcNow);
Assert.NotNull(changedSource);
Assert.Equal("pod-b", changedSource.Id);
Assert.Equal(StructuredLogSourceStatus.Connected, changedSource.Status);
}
[Fact]
public void MarkSeen_WhenSourceIsKnown_DoesNotRaiseSourceChanged()
{
var registry = CreateRegistry();
registry.MarkSeen("pod-b", DateTimeOffset.UtcNow);
StructuredLogSource? changedSource = null;
registry.SourceChanged += source => changedSource = source;
registry.MarkSeen("pod-b", DateTimeOffset.UtcNow.AddSeconds(1));
Assert.Null(changedSource);
}
[Fact]
public void List_WhenSourceHasNotBeenSeenRecently_MarksSourceAsStale()
{
_options.SourceHeartbeatTimeout = TimeSpan.FromSeconds(5);
var registry = CreateRegistry();
registry.MarkSeen("pod-b", DateTimeOffset.UtcNow.AddMinutes(-1));
var source = Assert.Single(registry.List(), x => x.Id == "pod-b");
Assert.Equal(StructuredLogSourceStatus.Stale, source.Status);
}
public void Dispose()
{
foreach (var item in _originalEnvironment)
Environment.SetEnvironmentVariable(item.Key, item.Value);
}
private StructuredLogSourceRegistry CreateRegistry()
{
return new(MicrosoftOptions.Create(_options));
}
private void CaptureEnvironment(string name)
{
_originalEnvironment[name] = Environment.GetEnvironmentVariable(name);
}
private static void SetEnvironment(string name, string? value)
{
Environment.SetEnvironmentVariable(name, value);
}
}