audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using Moq;
|
||||
using StellaOps.Eventing.Models;
|
||||
using StellaOps.Eventing.Storage;
|
||||
using StellaOps.HybridLogicalClock;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Eventing.Tests;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class TimelineEventEmitterTests
|
||||
{
|
||||
private readonly FakeTimeProvider _timeProvider;
|
||||
private readonly Mock<IHybridLogicalClock> _hlcMock;
|
||||
private readonly InMemoryTimelineEventStore _eventStore;
|
||||
private readonly IOptions<EventingOptions> _options;
|
||||
private readonly TimelineEventEmitter _emitter;
|
||||
|
||||
public TimelineEventEmitterTests()
|
||||
{
|
||||
_timeProvider = new FakeTimeProvider(new DateTimeOffset(2026, 1, 7, 12, 0, 0, TimeSpan.Zero));
|
||||
_hlcMock = new Mock<IHybridLogicalClock>();
|
||||
_eventStore = new InMemoryTimelineEventStore();
|
||||
_options = Options.Create(new EventingOptions
|
||||
{
|
||||
ServiceName = "TestService",
|
||||
EngineVersion = new EngineVersionRef("TestEngine", "1.0.0", "sha256:test")
|
||||
});
|
||||
|
||||
_emitter = new TimelineEventEmitter(
|
||||
_hlcMock.Object,
|
||||
_timeProvider,
|
||||
_eventStore,
|
||||
_options,
|
||||
NullLogger<TimelineEventEmitter>.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitAsync_StoresEventWithCorrectFields()
|
||||
{
|
||||
// Arrange
|
||||
var correlationId = "scan-abc123";
|
||||
var kind = EventKinds.Enqueue;
|
||||
var payload = new { JobId = "job-1", Status = "pending" };
|
||||
var expectedHlc = new HlcTimestamp(1704585600000, 0, "node1");
|
||||
|
||||
_hlcMock.Setup(h => h.Tick()).Returns(expectedHlc);
|
||||
|
||||
// Act
|
||||
var result = await _emitter.EmitAsync(correlationId, kind, payload);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result.CorrelationId.Should().Be(correlationId);
|
||||
result.Kind.Should().Be(kind);
|
||||
result.Service.Should().Be("TestService");
|
||||
result.THlc.Should().Be(expectedHlc);
|
||||
result.TsWall.Should().Be(_timeProvider.GetUtcNow());
|
||||
result.SchemaVersion.Should().Be(1);
|
||||
result.EngineVersion.EngineName.Should().Be("TestEngine");
|
||||
result.EngineVersion.Version.Should().Be("1.0.0");
|
||||
result.PayloadDigest.Should().NotBeEmpty();
|
||||
result.EventId.Should().HaveLength(32);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitAsync_GeneratesDeterministicEventId()
|
||||
{
|
||||
// Arrange
|
||||
var correlationId = "scan-abc123";
|
||||
var kind = EventKinds.Execute;
|
||||
var payload = new { Step = 1 };
|
||||
var hlc = new HlcTimestamp(1704585600000, 0, "node1");
|
||||
|
||||
_hlcMock.Setup(h => h.Tick()).Returns(hlc);
|
||||
|
||||
// Act
|
||||
var result1 = await _emitter.EmitAsync(correlationId, kind, payload);
|
||||
|
||||
// Create a second emitter with same config
|
||||
var emitter2 = new TimelineEventEmitter(
|
||||
_hlcMock.Object,
|
||||
_timeProvider,
|
||||
new InMemoryTimelineEventStore(),
|
||||
_options,
|
||||
NullLogger<TimelineEventEmitter>.Instance);
|
||||
|
||||
var result2 = await emitter2.EmitAsync(correlationId, kind, payload);
|
||||
|
||||
// Assert - Same inputs should produce same EventId
|
||||
result1.EventId.Should().Be(result2.EventId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitAsync_StoresEventInStore()
|
||||
{
|
||||
// Arrange
|
||||
var correlationId = "scan-abc123";
|
||||
var hlc = new HlcTimestamp(1704585600000, 0, "node1");
|
||||
_hlcMock.Setup(h => h.Tick()).Returns(hlc);
|
||||
|
||||
// Act
|
||||
var emitted = await _emitter.EmitAsync(correlationId, EventKinds.Enqueue, new { Test = true });
|
||||
|
||||
// Assert
|
||||
var stored = await _eventStore.GetByIdAsync(emitted.EventId);
|
||||
stored.Should().NotBeNull();
|
||||
stored!.EventId.Should().Be(emitted.EventId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitBatchAsync_StoresAllEvents()
|
||||
{
|
||||
// Arrange
|
||||
var hlcCounter = 0L;
|
||||
_hlcMock.Setup(h => h.Tick())
|
||||
.Returns(() => new HlcTimestamp(1704585600000, hlcCounter++, "node1"));
|
||||
|
||||
var pendingEvents = new[]
|
||||
{
|
||||
new PendingEvent("scan-1", EventKinds.Enqueue, new { Step = 1 }),
|
||||
new PendingEvent("scan-1", EventKinds.Execute, new { Step = 2 }),
|
||||
new PendingEvent("scan-1", EventKinds.Complete, new { Step = 3 })
|
||||
};
|
||||
|
||||
// Act
|
||||
var results = await _emitter.EmitBatchAsync(pendingEvents);
|
||||
|
||||
// Assert
|
||||
results.Should().HaveCount(3);
|
||||
results.Select(r => r.Kind).Should().BeEquivalentTo(
|
||||
new[] { EventKinds.Enqueue, EventKinds.Execute, EventKinds.Complete });
|
||||
|
||||
var stored = _eventStore.GetAll();
|
||||
stored.Should().HaveCount(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitBatchAsync_EmptyBatch_ReturnsEmptyList()
|
||||
{
|
||||
// Act
|
||||
var results = await _emitter.EmitBatchAsync(Array.Empty<PendingEvent>());
|
||||
|
||||
// Assert
|
||||
results.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitAsync_IncludesPayloadDigest()
|
||||
{
|
||||
// Arrange
|
||||
var hlc = new HlcTimestamp(1704585600000, 0, "node1");
|
||||
_hlcMock.Setup(h => h.Tick()).Returns(hlc);
|
||||
|
||||
// Act
|
||||
var result = await _emitter.EmitAsync("corr-1", EventKinds.Emit, new { Data = "test" });
|
||||
|
||||
// Assert
|
||||
result.PayloadDigest.Should().NotBeNull();
|
||||
result.PayloadDigest.Should().HaveCount(32); // SHA-256
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitAsync_DifferentPayloads_DifferentDigests()
|
||||
{
|
||||
// Arrange
|
||||
var hlcCounter = 0L;
|
||||
_hlcMock.Setup(h => h.Tick())
|
||||
.Returns(() => new HlcTimestamp(1704585600000, hlcCounter++, "node1"));
|
||||
|
||||
// Act
|
||||
var result1 = await _emitter.EmitAsync("corr-1", EventKinds.Emit, new { Value = 1 });
|
||||
var result2 = await _emitter.EmitAsync("corr-1", EventKinds.Emit, new { Value = 2 });
|
||||
|
||||
// Assert
|
||||
result1.PayloadDigest.Should().NotBeEquivalentTo(result2.PayloadDigest);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user