267 lines
9.5 KiB
C#
267 lines
9.5 KiB
C#
// <copyright file="ScannerConfigDiffTests.cs" company="StellaOps">
|
|
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
|
|
// </copyright>
|
|
// Sprint: SPRINT_20260105_002_005_TEST_cross_cutting
|
|
// Task: CCUT-022
|
|
|
|
using System.Collections.Immutable;
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using StellaOps.TestKit;
|
|
using StellaOps.Testing.ConfigDiff;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Scanner.ConfigDiff.Tests;
|
|
|
|
/// <summary>
|
|
/// Config-diff tests for the Scanner module.
|
|
/// Verifies that configuration changes produce only expected behavioral deltas.
|
|
/// </summary>
|
|
[Trait("Category", TestCategories.ConfigDiff)]
|
|
[Trait("Category", TestCategories.Integration)]
|
|
[Trait("BlastRadius", TestCategories.BlastRadius.Scanning)]
|
|
public class ScannerConfigDiffTests : ConfigDiffTestBase
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ScannerConfigDiffTests"/> class.
|
|
/// </summary>
|
|
public ScannerConfigDiffTests()
|
|
: base(
|
|
new ConfigDiffTestConfig(StrictMode: true),
|
|
NullLogger.Instance)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that changing scan depth only affects traversal behavior.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task ChangingScanDepth_OnlyAffectsTraversal()
|
|
{
|
|
// Arrange
|
|
var baselineConfig = new ScannerTestConfig
|
|
{
|
|
MaxScanDepth = 10,
|
|
EnableReachabilityAnalysis = true,
|
|
MaxConcurrentAnalyzers = 4
|
|
};
|
|
|
|
var changedConfig = baselineConfig with
|
|
{
|
|
MaxScanDepth = 20
|
|
};
|
|
|
|
// Act
|
|
var result = await TestConfigIsolationAsync(
|
|
baselineConfig,
|
|
changedConfig,
|
|
changedSetting: "MaxScanDepth",
|
|
unrelatedBehaviors:
|
|
[
|
|
async config => await GetReachabilityBehaviorAsync(config),
|
|
async config => await GetConcurrencyBehaviorAsync(config),
|
|
async config => await GetOutputFormatBehaviorAsync(config)
|
|
]);
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeTrue(
|
|
because: "changing scan depth should not affect reachability or concurrency");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that enabling reachability analysis produces expected delta.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task EnablingReachability_ProducesExpectedDelta()
|
|
{
|
|
// Arrange
|
|
var baselineConfig = new ScannerTestConfig { EnableReachabilityAnalysis = false };
|
|
var changedConfig = new ScannerTestConfig { EnableReachabilityAnalysis = true };
|
|
|
|
var expectedDelta = new ConfigDelta(
|
|
ChangedBehaviors: ["ReachabilityMode", "ScanDuration", "OutputDetail"],
|
|
BehaviorDeltas:
|
|
[
|
|
new BehaviorDelta("ReachabilityMode", "disabled", "enabled", null),
|
|
new BehaviorDelta("ScanDuration", "increase", null,
|
|
"Reachability analysis adds processing time"),
|
|
new BehaviorDelta("OutputDetail", "basic", "enhanced",
|
|
"Reachability data added to findings")
|
|
]);
|
|
|
|
// Act
|
|
var result = await TestConfigBehavioralDeltaAsync(
|
|
baselineConfig,
|
|
changedConfig,
|
|
getBehavior: async config => await CaptureReachabilityBehaviorAsync(config),
|
|
computeDelta: ComputeBehaviorSnapshotDelta,
|
|
expectedDelta: expectedDelta);
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeTrue(
|
|
because: "enabling reachability should produce expected behavioral delta");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that changing SBOM format only affects output.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task ChangingSbomFormat_OnlyAffectsOutput()
|
|
{
|
|
// Arrange
|
|
var baselineConfig = new ScannerTestConfig { SbomFormat = "spdx-3.0" };
|
|
var changedConfig = new ScannerTestConfig { SbomFormat = "cyclonedx-1.7" };
|
|
|
|
// Act
|
|
var result = await TestConfigIsolationAsync(
|
|
baselineConfig,
|
|
changedConfig,
|
|
changedSetting: "SbomFormat",
|
|
unrelatedBehaviors:
|
|
[
|
|
async config => await GetScanningBehaviorAsync(config),
|
|
async config => await GetVulnMatchingBehaviorAsync(config),
|
|
async config => await GetReachabilityBehaviorAsync(config)
|
|
]);
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeTrue(
|
|
because: "SBOM format should only affect output serialization");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that changing concurrency produces expected delta.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task ChangingConcurrency_ProducesExpectedDelta()
|
|
{
|
|
// Arrange
|
|
var baselineConfig = new ScannerTestConfig { MaxConcurrentAnalyzers = 2 };
|
|
var changedConfig = new ScannerTestConfig { MaxConcurrentAnalyzers = 8 };
|
|
|
|
var expectedDelta = new ConfigDelta(
|
|
ChangedBehaviors: ["ParallelismLevel", "ResourceUsage"],
|
|
BehaviorDeltas:
|
|
[
|
|
new BehaviorDelta("ParallelismLevel", "2", "8", null),
|
|
new BehaviorDelta("ResourceUsage", "increase", null,
|
|
"More concurrent analyzers use more resources")
|
|
]);
|
|
|
|
// Act
|
|
var result = await TestConfigBehavioralDeltaAsync(
|
|
baselineConfig,
|
|
changedConfig,
|
|
getBehavior: async config => await CaptureConcurrencyBehaviorAsync(config),
|
|
computeDelta: ComputeBehaviorSnapshotDelta,
|
|
expectedDelta: expectedDelta);
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that changing vulnerability threshold only affects filtering.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task ChangingVulnThreshold_OnlyAffectsFiltering()
|
|
{
|
|
// Arrange
|
|
var baselineConfig = new ScannerTestConfig { MinimumSeverity = "medium" };
|
|
var changedConfig = new ScannerTestConfig { MinimumSeverity = "critical" };
|
|
|
|
// Act
|
|
var result = await TestConfigIsolationAsync(
|
|
baselineConfig,
|
|
changedConfig,
|
|
changedSetting: "MinimumSeverity",
|
|
unrelatedBehaviors:
|
|
[
|
|
async config => await GetScanningBehaviorAsync(config),
|
|
async config => await GetSbomBehaviorAsync(config)
|
|
]);
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeTrue(
|
|
because: "severity threshold should only affect output filtering");
|
|
}
|
|
|
|
// Helper methods
|
|
|
|
private static Task<object> GetReachabilityBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
return Task.FromResult<object>(new { Enabled = config.EnableReachabilityAnalysis });
|
|
}
|
|
|
|
private static Task<object> GetConcurrencyBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
return Task.FromResult<object>(new { MaxAnalyzers = config.MaxConcurrentAnalyzers });
|
|
}
|
|
|
|
private static Task<object> GetOutputFormatBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
return Task.FromResult<object>(new { Format = config.SbomFormat });
|
|
}
|
|
|
|
private static Task<object> GetScanningBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
return Task.FromResult<object>(new { Depth = config.MaxScanDepth });
|
|
}
|
|
|
|
private static Task<object> GetVulnMatchingBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
return Task.FromResult<object>(new { MatchingMode = "standard" });
|
|
}
|
|
|
|
private static Task<object> GetSbomBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
return Task.FromResult<object>(new { Format = config.SbomFormat });
|
|
}
|
|
|
|
private static Task<BehaviorSnapshot> CaptureReachabilityBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
var snapshot = new BehaviorSnapshot(
|
|
ConfigurationId: $"reachability-{config.EnableReachabilityAnalysis}",
|
|
Behaviors:
|
|
[
|
|
new CapturedBehavior("ReachabilityMode",
|
|
config.EnableReachabilityAnalysis ? "enabled" : "disabled", DateTimeOffset.UtcNow),
|
|
new CapturedBehavior("ScanDuration",
|
|
config.EnableReachabilityAnalysis ? "increase" : "standard", DateTimeOffset.UtcNow),
|
|
new CapturedBehavior("OutputDetail",
|
|
config.EnableReachabilityAnalysis ? "enhanced" : "basic", DateTimeOffset.UtcNow)
|
|
],
|
|
CapturedAt: DateTimeOffset.UtcNow);
|
|
|
|
return Task.FromResult(snapshot);
|
|
}
|
|
|
|
private static Task<BehaviorSnapshot> CaptureConcurrencyBehaviorAsync(ScannerTestConfig config)
|
|
{
|
|
var snapshot = new BehaviorSnapshot(
|
|
ConfigurationId: $"concurrency-{config.MaxConcurrentAnalyzers}",
|
|
Behaviors:
|
|
[
|
|
new CapturedBehavior("ParallelismLevel", config.MaxConcurrentAnalyzers.ToString(), DateTimeOffset.UtcNow),
|
|
new CapturedBehavior("ResourceUsage",
|
|
config.MaxConcurrentAnalyzers > 4 ? "increase" : "standard", DateTimeOffset.UtcNow)
|
|
],
|
|
CapturedAt: DateTimeOffset.UtcNow);
|
|
|
|
return Task.FromResult(snapshot);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test configuration for Scanner module.
|
|
/// </summary>
|
|
public sealed record ScannerTestConfig
|
|
{
|
|
public int MaxScanDepth { get; init; } = 10;
|
|
public bool EnableReachabilityAnalysis { get; init; } = true;
|
|
public int MaxConcurrentAnalyzers { get; init; } = 4;
|
|
public string SbomFormat { get; init; } = "spdx-3.0";
|
|
public string MinimumSeverity { get; init; } = "medium";
|
|
public bool IncludeDevDependencies { get; init; } = false;
|
|
}
|