245 lines
7.3 KiB
C#
245 lines
7.3 KiB
C#
// <copyright file="CycloneDxPedigreeMapperTests.cs" company="StellaOps">
|
|
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
|
|
// </copyright>
|
|
|
|
using System.Collections.Immutable;
|
|
using CycloneDX.Models;
|
|
using FluentAssertions;
|
|
using StellaOps.Scanner.Emit.Pedigree;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Scanner.Emit.Tests.Pedigree;
|
|
|
|
/// <summary>
|
|
/// Unit tests for <see cref="CycloneDxPedigreeMapper"/>.
|
|
/// Sprint: SPRINT_20260107_005_002 Task PD-011
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class CycloneDxPedigreeMapperTests
|
|
{
|
|
private readonly CycloneDxPedigreeMapper _mapper = new();
|
|
|
|
[Fact]
|
|
public void Map_NullData_ReturnsNull()
|
|
{
|
|
// Act
|
|
var result = _mapper.Map(null);
|
|
|
|
// Assert
|
|
result.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_EmptyData_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var data = new PedigreeData();
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_WithAncestors_MapsToComponents()
|
|
{
|
|
// Arrange
|
|
var data = new PedigreeData
|
|
{
|
|
Ancestors = ImmutableArray.Create(
|
|
new AncestorComponent
|
|
{
|
|
Name = "openssl",
|
|
Version = "1.1.1n",
|
|
Purl = "pkg:generic/openssl@1.1.1n",
|
|
ProjectUrl = "https://www.openssl.org",
|
|
Level = 1
|
|
})
|
|
};
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result!.Ancestors.Should().HaveCount(1);
|
|
|
|
var ancestor = result.Ancestors![0];
|
|
ancestor.Name.Should().Be("openssl");
|
|
ancestor.Version.Should().Be("1.1.1n");
|
|
ancestor.Purl.Should().Be("pkg:generic/openssl@1.1.1n");
|
|
ancestor.ExternalReferences.Should().Contain(r =>
|
|
r.Type == ExternalReference.ExternalReferenceType.Website &&
|
|
r.Url == "https://www.openssl.org");
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_WithVariants_MapsToComponents()
|
|
{
|
|
// Arrange
|
|
var data = new PedigreeData
|
|
{
|
|
Variants = ImmutableArray.Create(
|
|
new VariantComponent
|
|
{
|
|
Name = "openssl",
|
|
Version = "1.1.1n-0+deb11u5",
|
|
Purl = "pkg:deb/debian/openssl@1.1.1n-0+deb11u5",
|
|
Distribution = "debian",
|
|
Release = "bullseye"
|
|
})
|
|
};
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result!.Variants.Should().HaveCount(1);
|
|
|
|
var variant = result.Variants![0];
|
|
variant.Name.Should().Be("openssl");
|
|
variant.Purl.Should().Be("pkg:deb/debian/openssl@1.1.1n-0+deb11u5");
|
|
variant.Properties.Should().Contain(p => p.Name == "stellaops:pedigree:distribution" && p.Value == "debian");
|
|
variant.Properties.Should().Contain(p => p.Name == "stellaops:pedigree:release" && p.Value == "bullseye");
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_WithCommits_MapsToCommitList()
|
|
{
|
|
// Arrange
|
|
var timestamp = new DateTimeOffset(2024, 6, 15, 10, 30, 0, TimeSpan.Zero);
|
|
var data = new PedigreeData
|
|
{
|
|
Commits = ImmutableArray.Create(
|
|
new CommitInfo
|
|
{
|
|
Uid = "abc123def456789",
|
|
Url = "https://github.com/openssl/openssl/commit/abc123",
|
|
Message = "Fix CVE-2024-1234",
|
|
Author = new CommitActor
|
|
{
|
|
Name = "Developer",
|
|
Email = "dev@example.com",
|
|
Timestamp = timestamp
|
|
}
|
|
})
|
|
};
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result!.Commits.Should().HaveCount(1);
|
|
|
|
var commit = result.Commits![0];
|
|
commit.Uid.Should().Be("abc123def456789");
|
|
commit.Url.Should().Be("https://github.com/openssl/openssl/commit/abc123");
|
|
commit.Message.Should().Be("Fix CVE-2024-1234");
|
|
commit.Author.Should().NotBeNull();
|
|
commit.Author!.Name.Should().Be("Developer");
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_WithPatches_MapsToPatchList()
|
|
{
|
|
// Arrange
|
|
var data = new PedigreeData
|
|
{
|
|
Patches = ImmutableArray.Create(
|
|
new PatchInfo
|
|
{
|
|
Type = PatchType.Backport,
|
|
DiffUrl = "https://patch.url/fix.patch",
|
|
DiffText = "--- a/file.c\n+++ b/file.c\n@@ -10,3 +10,4 @@",
|
|
Resolves = ImmutableArray.Create(
|
|
new PatchResolution
|
|
{
|
|
Id = "CVE-2024-1234",
|
|
SourceName = "NVD"
|
|
})
|
|
})
|
|
};
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result!.Patches.Should().HaveCount(1);
|
|
|
|
var patch = result.Patches![0];
|
|
patch.Type.Should().Be(Patch.PatchClassification.Backport);
|
|
patch.Diff.Should().NotBeNull();
|
|
patch.Diff!.Url.Should().Be("https://patch.url/fix.patch");
|
|
patch.Resolves.Should().Contain(i => i.Id == "CVE-2024-1234");
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_WithNotes_IncludesNotes()
|
|
{
|
|
// Arrange
|
|
var data = new PedigreeData
|
|
{
|
|
Notes = "Backported security fix from upstream 1.1.1o",
|
|
Ancestors = ImmutableArray.Create(
|
|
new AncestorComponent { Name = "openssl", Version = "1.1.1o" })
|
|
};
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result.Should().NotBeNull();
|
|
result!.Notes.Should().Be("Backported security fix from upstream 1.1.1o");
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_MultipleAncestors_OrdersByLevel()
|
|
{
|
|
// Arrange
|
|
var data = new PedigreeData
|
|
{
|
|
Ancestors = ImmutableArray.Create(
|
|
new AncestorComponent { Name = "grandparent", Version = "1.0", Level = 2 },
|
|
new AncestorComponent { Name = "parent", Version = "2.0", Level = 1 })
|
|
};
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result!.Ancestors![0].Name.Should().Be("parent");
|
|
result.Ancestors[1].Name.Should().Be("grandparent");
|
|
}
|
|
|
|
[Fact]
|
|
public void Map_PatchTypes_MapCorrectly()
|
|
{
|
|
// Arrange
|
|
var data = new PedigreeData
|
|
{
|
|
Patches = ImmutableArray.Create(
|
|
new PatchInfo { Type = PatchType.Backport },
|
|
new PatchInfo { Type = PatchType.CherryPick },
|
|
new PatchInfo { Type = PatchType.Unofficial },
|
|
new PatchInfo { Type = PatchType.Monkey })
|
|
};
|
|
|
|
// Act
|
|
var result = _mapper.Map(data);
|
|
|
|
// Assert
|
|
result!.Patches!.Select(p => p.Type).Should().BeEquivalentTo(new[]
|
|
{
|
|
Patch.PatchClassification.Backport,
|
|
Patch.PatchClassification.Cherry_Pick,
|
|
Patch.PatchClassification.Unofficial,
|
|
Patch.PatchClassification.Monkey
|
|
});
|
|
}
|
|
}
|