save progress
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
// <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;
|
||||
}
|
||||
Reference in New Issue
Block a user