Restructure solution layout by module
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Scheduler.Models;
|
||||
using StellaOps.Scheduler.Worker;
|
||||
using StellaOps.Scheduler.Worker.Options;
|
||||
using StellaOps.Scheduler.Worker.Policy;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scheduler.Worker.Tests;
|
||||
|
||||
public sealed class PolicyRunTargetingServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task EnsureTargetsAsync_ReturnsUnchanged_ForNonIncrementalJob()
|
||||
{
|
||||
var service = CreateService();
|
||||
var job = CreateJob(mode: PolicyRunMode.Full);
|
||||
|
||||
var result = await service.EnsureTargetsAsync(job, CancellationToken.None);
|
||||
|
||||
Assert.Equal(PolicyRunTargetingStatus.Unchanged, result.Status);
|
||||
Assert.Equal(job, result.Job);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureTargetsAsync_ReturnsUnchanged_WhenSbomSetAlreadyPresent()
|
||||
{
|
||||
var service = CreateService();
|
||||
var inputs = new PolicyRunInputs(sbomSet: new[] { "sbom:S-1" });
|
||||
var job = CreateJob(inputs: inputs);
|
||||
|
||||
var result = await service.EnsureTargetsAsync(job, CancellationToken.None);
|
||||
|
||||
Assert.Equal(PolicyRunTargetingStatus.Unchanged, result.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureTargetsAsync_ReturnsNoWork_WhenNoCandidatesResolved()
|
||||
{
|
||||
var impact = new StubImpactTargetingService();
|
||||
var service = CreateService(impact);
|
||||
var metadata = ImmutableSortedDictionary<string, string>.Empty.Add("delta.purls", "pkg:npm/leftpad");
|
||||
var job = CreateJob(metadata: metadata, inputs: PolicyRunInputs.Empty);
|
||||
|
||||
var result = await service.EnsureTargetsAsync(job, CancellationToken.None);
|
||||
|
||||
Assert.Equal(PolicyRunTargetingStatus.NoWork, result.Status);
|
||||
Assert.Equal("no_matches", result.Reason);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureTargetsAsync_TargetsDirectSboms()
|
||||
{
|
||||
var service = CreateService();
|
||||
var metadata = ImmutableSortedDictionary<string, string>.Empty.Add("delta.sboms", "sbom:S-2, sbom:S-1, sbom:S-2");
|
||||
var job = CreateJob(metadata: metadata, inputs: PolicyRunInputs.Empty);
|
||||
|
||||
var result = await service.EnsureTargetsAsync(job, CancellationToken.None);
|
||||
|
||||
Assert.Equal(PolicyRunTargetingStatus.Targeted, result.Status);
|
||||
Assert.Equal(new[] { "sbom:S-1", "sbom:S-2" }, result.Job.Inputs.SbomSet);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureTargetsAsync_TargetsUsingImpactIndex()
|
||||
{
|
||||
var impact = new StubImpactTargetingService
|
||||
{
|
||||
OnResolveByPurls = (keys, usageOnly, selector, _) =>
|
||||
{
|
||||
var image = new ImpactImage(
|
||||
"sha256:111",
|
||||
"registry",
|
||||
"repo",
|
||||
labels: ImmutableSortedDictionary.Create<string, string>(StringComparer.Ordinal).Add("sbomId", "sbom:S-42"));
|
||||
var impactSet = new ImpactSet(
|
||||
selector,
|
||||
new[] { image },
|
||||
usageOnly,
|
||||
DateTimeOffset.UtcNow,
|
||||
total: 1,
|
||||
snapshotId: null,
|
||||
schemaVersion: SchedulerSchemaVersions.ImpactSet);
|
||||
return ValueTask.FromResult(impactSet);
|
||||
}
|
||||
};
|
||||
|
||||
var service = CreateService(impact);
|
||||
var metadata = ImmutableSortedDictionary<string, string>.Empty.Add("delta.purls", "pkg:npm/example");
|
||||
var job = CreateJob(metadata: metadata, inputs: PolicyRunInputs.Empty);
|
||||
|
||||
var result = await service.EnsureTargetsAsync(job, CancellationToken.None);
|
||||
|
||||
Assert.Equal(PolicyRunTargetingStatus.Targeted, result.Status);
|
||||
Assert.Equal(new[] { "sbom:S-42" }, result.Job.Inputs.SbomSet);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureTargetsAsync_FallsBack_WhenLimitExceeded()
|
||||
{
|
||||
var service = CreateService(configure: options => options.MaxSboms = 1);
|
||||
var metadata = ImmutableSortedDictionary<string, string>.Empty.Add("delta.sboms", "sbom:S-1,sbom:S-2");
|
||||
var job = CreateJob(metadata: metadata, inputs: PolicyRunInputs.Empty);
|
||||
|
||||
var result = await service.EnsureTargetsAsync(job, CancellationToken.None);
|
||||
|
||||
Assert.Equal(PolicyRunTargetingStatus.Unchanged, result.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnsureTargetsAsync_FallbacksToDigest_WhenLabelMissing()
|
||||
{
|
||||
var impact = new StubImpactTargetingService
|
||||
{
|
||||
OnResolveByVulnerabilities = (ids, usageOnly, selector, _) =>
|
||||
{
|
||||
var image = new ImpactImage("sha256:aaa", "registry", "repo");
|
||||
var impactSet = new ImpactSet(
|
||||
selector,
|
||||
new[] { image },
|
||||
usageOnly,
|
||||
DateTimeOffset.UtcNow,
|
||||
total: 1,
|
||||
snapshotId: null,
|
||||
schemaVersion: SchedulerSchemaVersions.ImpactSet);
|
||||
return ValueTask.FromResult(impactSet);
|
||||
}
|
||||
};
|
||||
|
||||
var service = CreateService(impact);
|
||||
var metadata = ImmutableSortedDictionary<string, string>.Empty.Add("delta.vulns", "CVE-2025-1234");
|
||||
var job = CreateJob(metadata: metadata, inputs: PolicyRunInputs.Empty);
|
||||
|
||||
var result = await service.EnsureTargetsAsync(job, CancellationToken.None);
|
||||
|
||||
Assert.Equal(PolicyRunTargetingStatus.Targeted, result.Status);
|
||||
Assert.Equal(new[] { "sbom:sha256:aaa" }, result.Job.Inputs.SbomSet);
|
||||
}
|
||||
|
||||
private static PolicyRunTargetingService CreateService(
|
||||
IImpactTargetingService? impact = null,
|
||||
Action<SchedulerWorkerOptions.PolicyOptions.TargetingOptions>? configure = null)
|
||||
{
|
||||
impact ??= new StubImpactTargetingService();
|
||||
var options = CreateOptions(configure);
|
||||
return new PolicyRunTargetingService(
|
||||
impact,
|
||||
Microsoft.Extensions.Options.Options.Create(options),
|
||||
timeProvider: null,
|
||||
NullLogger<PolicyRunTargetingService>.Instance);
|
||||
}
|
||||
|
||||
private static SchedulerWorkerOptions CreateOptions(Action<SchedulerWorkerOptions.PolicyOptions.TargetingOptions>? configure)
|
||||
{
|
||||
var options = new SchedulerWorkerOptions
|
||||
{
|
||||
Policy =
|
||||
{
|
||||
Api =
|
||||
{
|
||||
BaseAddress = new Uri("https://policy.example.com"),
|
||||
RunsPath = "/runs",
|
||||
SimulatePath = "/simulate"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
configure?.Invoke(options.Policy.Targeting);
|
||||
return options;
|
||||
}
|
||||
|
||||
private static PolicyRunJob CreateJob(
|
||||
PolicyRunMode mode = PolicyRunMode.Incremental,
|
||||
ImmutableSortedDictionary<string, string>? metadata = null,
|
||||
PolicyRunInputs? inputs = null)
|
||||
{
|
||||
return new PolicyRunJob(
|
||||
SchemaVersion: SchedulerSchemaVersions.PolicyRunJob,
|
||||
Id: "job-1",
|
||||
TenantId: "tenant-alpha",
|
||||
PolicyId: "P-7",
|
||||
PolicyVersion: 4,
|
||||
Mode: mode,
|
||||
Priority: PolicyRunPriority.Normal,
|
||||
PriorityRank: 0,
|
||||
RunId: null,
|
||||
RequestedBy: null,
|
||||
CorrelationId: null,
|
||||
Metadata: metadata ?? ImmutableSortedDictionary<string, string>.Empty,
|
||||
Inputs: inputs ?? PolicyRunInputs.Empty,
|
||||
QueuedAt: DateTimeOffset.UtcNow,
|
||||
Status: PolicyRunJobStatus.Dispatching,
|
||||
AttemptCount: 0,
|
||||
LastAttemptAt: null,
|
||||
LastError: null,
|
||||
CreatedAt: DateTimeOffset.UtcNow,
|
||||
UpdatedAt: DateTimeOffset.UtcNow,
|
||||
AvailableAt: DateTimeOffset.UtcNow,
|
||||
SubmittedAt: null,
|
||||
CompletedAt: null,
|
||||
LeaseOwner: "lease",
|
||||
LeaseExpiresAt: DateTimeOffset.UtcNow.AddMinutes(1),
|
||||
CancellationRequested: false,
|
||||
CancellationRequestedAt: null,
|
||||
CancellationReason: null,
|
||||
CancelledAt: null);
|
||||
}
|
||||
|
||||
private sealed class StubImpactTargetingService : IImpactTargetingService
|
||||
{
|
||||
public Func<IEnumerable<string>, bool, Selector, CancellationToken, ValueTask<ImpactSet>>? OnResolveByPurls { get; set; }
|
||||
|
||||
public Func<IEnumerable<string>, bool, Selector, CancellationToken, ValueTask<ImpactSet>>? OnResolveByVulnerabilities { get; set; }
|
||||
|
||||
public ValueTask<ImpactSet> ResolveByPurlsAsync(IEnumerable<string> productKeys, bool usageOnly, Selector selector, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (OnResolveByPurls is null)
|
||||
{
|
||||
return ValueTask.FromResult(CreateEmptyImpactSet(selector, usageOnly));
|
||||
}
|
||||
|
||||
return OnResolveByPurls(productKeys, usageOnly, selector, cancellationToken);
|
||||
}
|
||||
|
||||
public ValueTask<ImpactSet> ResolveByVulnerabilitiesAsync(IEnumerable<string> vulnerabilityIds, bool usageOnly, Selector selector, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (OnResolveByVulnerabilities is null)
|
||||
{
|
||||
return ValueTask.FromResult(CreateEmptyImpactSet(selector, usageOnly));
|
||||
}
|
||||
|
||||
return OnResolveByVulnerabilities(vulnerabilityIds, usageOnly, selector, cancellationToken);
|
||||
}
|
||||
|
||||
public ValueTask<ImpactSet> ResolveAllAsync(Selector selector, bool usageOnly, CancellationToken cancellationToken = default)
|
||||
=> ValueTask.FromResult(CreateEmptyImpactSet(selector, usageOnly));
|
||||
|
||||
private static ImpactSet CreateEmptyImpactSet(Selector selector, bool usageOnly)
|
||||
{
|
||||
return new ImpactSet(
|
||||
selector,
|
||||
ImmutableArray<ImpactImage>.Empty,
|
||||
usageOnly,
|
||||
DateTimeOffset.UtcNow,
|
||||
total: 0,
|
||||
snapshotId: null,
|
||||
schemaVersion: SchedulerSchemaVersions.ImpactSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user