Files
git.stella-ops.org/src/Scheduler/__Tests/StellaOps.Scheduler.Worker.Tests/PolicyRunTargetingServiceTests.cs
StellaOps Bot 564df71bfb
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
up
2025-12-13 00:20:26 +02:00

256 lines
9.8 KiB
C#

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);
}
}
}