nuget reorganization
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Signals.Tests;
|
||||
|
||||
public class InMemoryEventsPublisherTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task PublishFactUpdatedAsync_EmitsStructuredEvent()
|
||||
{
|
||||
var logger = new TestLogger<InMemoryEventsPublisher>();
|
||||
var publisher = new InMemoryEventsPublisher(logger);
|
||||
|
||||
var fact = new ReachabilityFactDocument
|
||||
{
|
||||
SubjectKey = "tenant:image@sha256:abc",
|
||||
CallgraphId = "cg-123",
|
||||
ComputedAt = System.DateTimeOffset.Parse("2025-11-18T12:00:00Z"),
|
||||
States = new List<ReachabilityStateDocument>
|
||||
{
|
||||
new() { Target = "pkg:pypi/django", Reachable = true, Confidence = 0.9 },
|
||||
new() { Target = "pkg:pypi/requests", Reachable = false, Confidence = 0.2 }
|
||||
},
|
||||
RuntimeFacts = new List<RuntimeFactDocument>
|
||||
{
|
||||
new() { SymbolId = "funcA", HitCount = 3 }
|
||||
}
|
||||
};
|
||||
|
||||
await publisher.PublishFactUpdatedAsync(fact, CancellationToken.None);
|
||||
|
||||
Assert.Contains("signals.fact.updated", logger.LastMessage);
|
||||
Assert.Contains("\"subjectKey\":\"tenant:image@sha256:abc\"", logger.LastMessage);
|
||||
Assert.Contains("\"callgraphId\":\"cg-123\"", logger.LastMessage);
|
||||
Assert.Contains("\"reachableCount\":1", logger.LastMessage);
|
||||
Assert.Contains("\"unreachableCount\":1", logger.LastMessage);
|
||||
Assert.Contains("\"runtimeFactsCount\":1", logger.LastMessage);
|
||||
}
|
||||
|
||||
private sealed class TestLogger<T> : ILogger<T>
|
||||
{
|
||||
public string LastMessage { get; private set; } = string.Empty;
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => true;
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, System.Exception? exception, Func<TState, System.Exception?, string> formatter)
|
||||
{
|
||||
LastMessage = formatter(state, exception);
|
||||
}
|
||||
|
||||
private sealed class NullScope : IDisposable
|
||||
{
|
||||
public static readonly NullScope Instance = new();
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,11 +43,16 @@ public class ReachabilityScoringServiceTests
|
||||
options.Scoring.MaxConfidence = 0.95;
|
||||
options.Scoring.MinConfidence = 0.1;
|
||||
|
||||
var cache = new InMemoryReachabilityCache();
|
||||
var eventsPublisher = new RecordingEventsPublisher();
|
||||
|
||||
var service = new ReachabilityScoringService(
|
||||
callgraphRepository,
|
||||
factRepository,
|
||||
TimeProvider.System,
|
||||
Options.Create(options),
|
||||
cache,
|
||||
eventsPublisher,
|
||||
NullLogger<ReachabilityScoringService>.Instance);
|
||||
|
||||
var request = new ReachabilityRecomputeRequest
|
||||
@@ -108,4 +113,38 @@ public class ReachabilityScoringServiceTests
|
||||
return Task.FromResult(document);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InMemoryReachabilityCache : IReachabilityCache
|
||||
{
|
||||
private readonly Dictionary<string, ReachabilityFactDocument> cache = new(StringComparer.Ordinal);
|
||||
|
||||
public Task<ReachabilityFactDocument?> GetAsync(string subjectKey, CancellationToken cancellationToken)
|
||||
{
|
||||
cache.TryGetValue(subjectKey, out var doc);
|
||||
return Task.FromResult(doc);
|
||||
}
|
||||
|
||||
public Task SetAsync(ReachabilityFactDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
cache[document.SubjectKey] = document;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task InvalidateAsync(string subjectKey, CancellationToken cancellationToken)
|
||||
{
|
||||
cache.Remove(subjectKey);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RecordingEventsPublisher : IEventsPublisher
|
||||
{
|
||||
public ReachabilityFactDocument? Last { get; private set; }
|
||||
|
||||
public Task PublishFactUpdatedAsync(ReachabilityFactDocument fact, CancellationToken cancellationToken)
|
||||
{
|
||||
Last = fact;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,13 @@ public class RuntimeFactsIngestionServiceTests
|
||||
{
|
||||
var factRepository = new InMemoryReachabilityFactRepository();
|
||||
var scoringService = new RecordingScoringService();
|
||||
var cache = new InMemoryReachabilityCache();
|
||||
var eventsPublisher = new RecordingEventsPublisher();
|
||||
var service = new RuntimeFactsIngestionService(
|
||||
factRepository,
|
||||
TimeProvider.System,
|
||||
cache,
|
||||
eventsPublisher,
|
||||
scoringService,
|
||||
NullLogger<RuntimeFactsIngestionService>.Instance);
|
||||
|
||||
@@ -75,6 +79,40 @@ public class RuntimeFactsIngestionServiceTests
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InMemoryReachabilityCache : IReachabilityCache
|
||||
{
|
||||
private readonly Dictionary<string, ReachabilityFactDocument> cache = new(StringComparer.Ordinal);
|
||||
|
||||
public Task<ReachabilityFactDocument?> GetAsync(string subjectKey, CancellationToken cancellationToken)
|
||||
{
|
||||
cache.TryGetValue(subjectKey, out var doc);
|
||||
return Task.FromResult(doc);
|
||||
}
|
||||
|
||||
public Task SetAsync(ReachabilityFactDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
cache[document.SubjectKey] = document;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task InvalidateAsync(string subjectKey, CancellationToken cancellationToken)
|
||||
{
|
||||
cache.Remove(subjectKey);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RecordingEventsPublisher : IEventsPublisher
|
||||
{
|
||||
public ReachabilityFactDocument? Last { get; private set; }
|
||||
|
||||
public Task PublishFactUpdatedAsync(ReachabilityFactDocument fact, CancellationToken cancellationToken)
|
||||
{
|
||||
Last = fact;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RecordingScoringService : IReachabilityScoringService
|
||||
{
|
||||
public ReachabilityRecomputeRequest? LastRequest { get; private set; }
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<!-- Disable Concelier shared test infra to avoid pulling unrelated projects into the Signals test graph -->
|
||||
<UseConcelierTestInfra>false</UseConcelierTestInfra>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user