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
256 lines
9.8 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|