Complete Entrypoint Detection Re-Engineering Program (Sprints 0410-0415) and Sprint 3500.0002.0003 (Proof Replay + API)
Entrypoint Detection Program (100% complete): - Sprint 0411: Semantic Entrypoint Engine - all 25 tasks DONE - Sprint 0412: Temporal & Mesh Entrypoint - all 19 tasks DONE - Sprint 0413: Speculative Execution Engine - all 19 tasks DONE - Sprint 0414: Binary Intelligence - all 19 tasks DONE - Sprint 0415: Predictive Risk Scoring - all tasks DONE Key deliverables: - SemanticEntrypoint schema with ApplicationIntent/CapabilityClass - TemporalEntrypointGraph and MeshEntrypointGraph - ShellSymbolicExecutor with PathEnumerator and PathConfidenceScorer - CodeFingerprint index with symbol recovery - RiskScore with multi-dimensional risk assessment Sprint 3500.0002.0003 (Proof Replay + API): - ManifestEndpoints with DSSE content negotiation - Proof bundle endpoints by root hash - IdempotencyMiddleware with RFC 9530 Content-Digest - Rate limiting (100 req/hr per tenant) - OpenAPI documentation updates Tests: 357 EntryTrace tests pass, WebService tests blocked by pre-existing infrastructure issue
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Signals.Tests;
|
||||
|
||||
public class SchedulerRescanOrchestratorTests
|
||||
{
|
||||
private readonly MockSchedulerJobClient _mockClient = new();
|
||||
private readonly FakeTimeProvider _timeProvider = new();
|
||||
private readonly ILogger<SchedulerRescanOrchestrator> _logger;
|
||||
private readonly SchedulerRescanOrchestrator _sut;
|
||||
|
||||
public SchedulerRescanOrchestratorTests()
|
||||
{
|
||||
_logger = LoggerFactory.Create(b => b.AddDebug()).CreateLogger<SchedulerRescanOrchestrator>();
|
||||
_sut = new SchedulerRescanOrchestrator(_mockClient, _timeProvider, _logger);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_CreatesJobWithCorrectPriority_Immediate()
|
||||
{
|
||||
// Arrange
|
||||
var unknown = CreateUnknown("pkg:npm/lodash@4.17.21", UnknownsBand.Hot);
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerRescanAsync(unknown, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
result.Success.Should().BeTrue();
|
||||
_mockClient.LastRequest.Should().NotBeNull();
|
||||
_mockClient.LastRequest!.Priority.Should().Be(RescanJobPriority.High);
|
||||
_mockClient.LastRequest.PackageUrl.Should().Be("pkg:npm/lodash@4.17.21");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_CreatesJobWithCorrectPriority_Scheduled()
|
||||
{
|
||||
// Arrange
|
||||
var unknown = CreateUnknown("pkg:maven/com.example/lib@1.0.0", UnknownsBand.Warm);
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerRescanAsync(unknown, RescanPriority.Scheduled);
|
||||
|
||||
// Assert
|
||||
result.Success.Should().BeTrue();
|
||||
_mockClient.LastRequest!.Priority.Should().Be(RescanJobPriority.Normal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_CreatesJobWithCorrectPriority_Batch()
|
||||
{
|
||||
// Arrange
|
||||
var unknown = CreateUnknown("pkg:nuget/newtonsoft.json@13.0.1", UnknownsBand.Cold);
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerRescanAsync(unknown, RescanPriority.Batch);
|
||||
|
||||
// Assert
|
||||
result.Success.Should().BeTrue();
|
||||
_mockClient.LastRequest!.Priority.Should().Be(RescanJobPriority.Low);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_PropagatesCorrelationId()
|
||||
{
|
||||
// Arrange
|
||||
var unknown = CreateUnknown("pkg:pypi/requests@2.28.0", UnknownsBand.Hot);
|
||||
unknown.CallgraphId = "tenant123:graph456";
|
||||
|
||||
// Act
|
||||
await _sut.TriggerRescanAsync(unknown, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
_mockClient.LastRequest!.CorrelationId.Should().Be("tenant123:graph456");
|
||||
_mockClient.LastRequest.TenantId.Should().Be("tenant123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_ReturnsNextScheduledRescan_ForImmediate()
|
||||
{
|
||||
// Arrange
|
||||
var now = new DateTimeOffset(2025, 1, 20, 10, 0, 0, TimeSpan.Zero);
|
||||
_timeProvider.SetUtcNow(now);
|
||||
var unknown = CreateUnknown("pkg:npm/axios@1.0.0", UnknownsBand.Hot);
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerRescanAsync(unknown, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
result.NextScheduledRescan.Should().Be(now.AddMinutes(15));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_ReturnsNextScheduledRescan_ForScheduled()
|
||||
{
|
||||
// Arrange
|
||||
var now = new DateTimeOffset(2025, 1, 20, 10, 0, 0, TimeSpan.Zero);
|
||||
_timeProvider.SetUtcNow(now);
|
||||
var unknown = CreateUnknown("pkg:npm/express@4.18.0", UnknownsBand.Warm);
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerRescanAsync(unknown, RescanPriority.Scheduled);
|
||||
|
||||
// Assert
|
||||
result.NextScheduledRescan.Should().Be(now.AddHours(24));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_ReturnsNextScheduledRescan_ForBatch()
|
||||
{
|
||||
// Arrange
|
||||
var now = new DateTimeOffset(2025, 1, 20, 10, 0, 0, TimeSpan.Zero);
|
||||
_timeProvider.SetUtcNow(now);
|
||||
var unknown = CreateUnknown("pkg:npm/mocha@10.0.0", UnknownsBand.Cold);
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerRescanAsync(unknown, RescanPriority.Batch);
|
||||
|
||||
// Assert
|
||||
result.NextScheduledRescan.Should().Be(now.AddDays(7));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_ReturnsFailure_WhenClientFails()
|
||||
{
|
||||
// Arrange
|
||||
_mockClient.ShouldFail = true;
|
||||
_mockClient.FailureMessage = "Queue unavailable";
|
||||
var unknown = CreateUnknown("pkg:npm/fail@1.0.0", UnknownsBand.Hot);
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerRescanAsync(unknown, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
result.Success.Should().BeFalse();
|
||||
result.ErrorMessage.Should().Be("Queue unavailable");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerBatchRescanAsync_ProcessesAllItems()
|
||||
{
|
||||
// Arrange
|
||||
var unknowns = new[]
|
||||
{
|
||||
CreateUnknown("pkg:npm/a@1.0.0", UnknownsBand.Hot),
|
||||
CreateUnknown("pkg:npm/b@1.0.0", UnknownsBand.Hot),
|
||||
CreateUnknown("pkg:npm/c@1.0.0", UnknownsBand.Hot)
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerBatchRescanAsync(unknowns, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
result.TotalRequested.Should().Be(3);
|
||||
result.SuccessCount.Should().Be(3);
|
||||
result.FailureCount.Should().Be(0);
|
||||
result.Results.Should().HaveCount(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerBatchRescanAsync_EmptyList_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var unknowns = Array.Empty<UnknownSymbolDocument>();
|
||||
|
||||
// Act
|
||||
var result = await _sut.TriggerBatchRescanAsync(unknowns, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
result.TotalRequested.Should().Be(0);
|
||||
result.SuccessCount.Should().Be(0);
|
||||
result.FailureCount.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_ExtractsTenantFromCallgraphId()
|
||||
{
|
||||
// Arrange
|
||||
var unknown = CreateUnknown("pkg:npm/test@1.0.0", UnknownsBand.Hot);
|
||||
unknown.CallgraphId = "acme-corp:cg-12345";
|
||||
|
||||
// Act
|
||||
await _sut.TriggerRescanAsync(unknown, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
_mockClient.LastRequest!.TenantId.Should().Be("acme-corp");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TriggerRescanAsync_UsesDefaultTenant_WhenNoCallgraphId()
|
||||
{
|
||||
// Arrange
|
||||
var unknown = CreateUnknown("pkg:npm/orphan@1.0.0", UnknownsBand.Hot);
|
||||
unknown.CallgraphId = null;
|
||||
|
||||
// Act
|
||||
await _sut.TriggerRescanAsync(unknown, RescanPriority.Immediate);
|
||||
|
||||
// Assert
|
||||
_mockClient.LastRequest!.TenantId.Should().Be("default");
|
||||
}
|
||||
|
||||
private static UnknownSymbolDocument CreateUnknown(string purl, UnknownsBand band)
|
||||
{
|
||||
return new UnknownSymbolDocument
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
SubjectKey = purl,
|
||||
Purl = purl,
|
||||
Band = band,
|
||||
Score = band switch
|
||||
{
|
||||
UnknownsBand.Hot => 0.85,
|
||||
UnknownsBand.Warm => 0.55,
|
||||
UnknownsBand.Cold => 0.35,
|
||||
_ => 0.15
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class MockSchedulerJobClient : ISchedulerJobClient
|
||||
{
|
||||
public RescanJobRequest? LastRequest { get; private set; }
|
||||
public bool ShouldFail { get; set; }
|
||||
public string FailureMessage { get; set; } = "Mock failure";
|
||||
|
||||
public Task<SchedulerJobResult> CreateRescanJobAsync(
|
||||
RescanJobRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
LastRequest = request;
|
||||
|
||||
if (ShouldFail)
|
||||
{
|
||||
return Task.FromResult(SchedulerJobResult.Failed(FailureMessage));
|
||||
}
|
||||
|
||||
var jobId = $"mock-job-{Guid.NewGuid():N}";
|
||||
var runId = $"mock-run-{DateTime.UtcNow:yyyyMMddHHmmss}";
|
||||
return Task.FromResult(SchedulerJobResult.Succeeded(jobId, runId));
|
||||
}
|
||||
|
||||
public async Task<BatchSchedulerJobResult> CreateRescanJobsAsync(
|
||||
IReadOnlyList<RescanJobRequest> requests,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var results = new List<SchedulerJobResult>();
|
||||
|
||||
foreach (var request in requests)
|
||||
{
|
||||
var result = await CreateRescanJobAsync(request, cancellationToken);
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
return new BatchSchedulerJobResult(
|
||||
requests.Count,
|
||||
results.Count(r => r.Success),
|
||||
results.Count(r => !r.Success),
|
||||
results);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FakeTimeProvider : TimeProvider
|
||||
{
|
||||
private DateTimeOffset _utcNow = DateTimeOffset.UtcNow;
|
||||
|
||||
public void SetUtcNow(DateTimeOffset value) => _utcNow = value;
|
||||
|
||||
public override DateTimeOffset GetUtcNow() => _utcNow;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user