Files
git.stella-ops.org/src/Scanner/__Tests/StellaOps.Scanner.Emit.Tests/Evidence/LicenseEvidenceBuilderTests.cs
2026-01-08 20:46:43 +02:00

142 lines
4.3 KiB
C#

// <copyright file="LicenseEvidenceBuilderTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using System.Collections.Immutable;
using FluentAssertions;
using StellaOps.Scanner.Core.Contracts;
using StellaOps.Scanner.Emit.Evidence;
using Xunit;
namespace StellaOps.Scanner.Emit.Tests.Evidence;
/// <summary>
/// Unit tests for <see cref="LicenseEvidenceBuilder"/>.
/// Sprint: SPRINT_20260107_005_001 Task EV-011
/// </summary>
[Trait("Category", "Unit")]
public sealed class LicenseEvidenceBuilderTests
{
private readonly LicenseEvidenceBuilder _sut = new();
[Fact]
public void Build_WithLicenseEvidence_ReturnsLicenseChoices()
{
// Arrange
var evidence = ImmutableArray.Create(
new ComponentEvidence { Kind = "license", Value = "MIT", Source = "/app/LICENSE" });
var component = CreateComponent(evidence);
// Act
var result = _sut.Build(component);
// Assert
result.Should().HaveCount(1);
result[0].License.Should().NotBeNull();
result[0].License!.License.Should().NotBeNull();
result[0].License!.License!.Id.Should().Be("MIT");
}
[Fact]
public void Build_WithMultipleLicenses_ReturnsAllLicenses()
{
// Arrange
var evidence = ImmutableArray.Create(
new ComponentEvidence { Kind = "license", Value = "MIT", Source = "/app/LICENSE" },
new ComponentEvidence { Kind = "license", Value = "Apache-2.0", Source = "/app/LICENSE.apache" },
new ComponentEvidence { Kind = "license", Value = "GPL-3.0", Source = "/app/COPYING" });
var component = CreateComponent(evidence);
// Act
var result = _sut.Build(component);
// Assert
result.Should().HaveCount(3);
}
[Fact]
public void Build_WithNonLicenseEvidence_FiltersOutNonLicenses()
{
// Arrange
var evidence = ImmutableArray.Create(
new ComponentEvidence { Kind = "license", Value = "MIT", Source = "/app/LICENSE" },
new ComponentEvidence { Kind = "file", Value = "readme.md", Source = "/app/README.md" });
var component = CreateComponent(evidence);
// Act
var result = _sut.Build(component);
// Assert
result.Should().HaveCount(1);
result[0].License!.License!.Id.Should().Be("MIT");
}
[Fact]
public void Build_WithNoEvidence_ReturnsEmptyArray()
{
// Arrange
var component = CreateComponent(ImmutableArray<ComponentEvidence>.Empty);
// Act
var result = _sut.Build(component);
// Assert
result.Should().BeEmpty();
}
[Fact]
public void Build_NullComponent_ThrowsArgumentNullException()
{
// Act & Assert
var act = () => _sut.Build(null!);
act.Should().Throw<ArgumentNullException>();
}
[Fact]
public void Build_WithExpression_ParsesAsExpression()
{
// Arrange
var evidence = ImmutableArray.Create(
new ComponentEvidence { Kind = "license", Value = "MIT OR Apache-2.0", Source = "/app/LICENSE" });
var component = CreateComponent(evidence);
// Act
var result = _sut.Build(component);
// Assert
result.Should().HaveCount(1);
// SPDX expressions are parsed as expression rather than ID
result[0].License.Expression.Should().NotBeNullOrWhiteSpace();
}
[Fact]
public void Build_DeduplicatesSameLicense()
{
// Arrange
var evidence = ImmutableArray.Create(
new ComponentEvidence { Kind = "license", Value = "MIT", Source = "/app/LICENSE" },
new ComponentEvidence { Kind = "license", Value = "MIT", Source = "/app/package.json" }); // Same license
var component = CreateComponent(evidence);
// Act
var result = _sut.Build(component);
// Assert
result.Should().HaveCount(1);
}
private static AggregatedComponent CreateComponent(ImmutableArray<ComponentEvidence> evidence)
{
var identity = ComponentIdentity.Create(
key: "pkg:npm/lodash@4.17.21",
name: "lodash",
purl: "pkg:npm/lodash@4.17.21");
return new AggregatedComponent
{
Identity = identity,
Evidence = evidence,
};
}
}