182 lines
5.6 KiB
C#
182 lines
5.6 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using StellaOps.Scanner.Analyzers.OS.Pkgutil;
|
|
using Xunit;
|
|
|
|
using StellaOps.TestKit;
|
|
namespace StellaOps.Scanner.Analyzers.OS.Pkgutil.Tests;
|
|
|
|
public sealed class PkgutilPackageAnalyzerTests
|
|
{
|
|
private static readonly string FixturesRoot = Path.Combine(
|
|
AppContext.BaseDirectory,
|
|
"Fixtures");
|
|
|
|
private readonly PkgutilPackageAnalyzer _analyzer;
|
|
private readonly ILogger _logger;
|
|
|
|
public PkgutilPackageAnalyzerTests()
|
|
{
|
|
_logger = NullLoggerFactory.Instance.CreateLogger<PkgutilPackageAnalyzer>();
|
|
_analyzer = new PkgutilPackageAnalyzer((ILogger<PkgutilPackageAnalyzer>)_logger);
|
|
}
|
|
|
|
private OSPackageAnalyzerContext CreateContext(string rootPath)
|
|
{
|
|
return new OSPackageAnalyzerContext(
|
|
rootPath,
|
|
workspacePath: null,
|
|
TimeProvider.System,
|
|
_logger);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void AnalyzerId_ReturnsPkgutil()
|
|
{
|
|
Assert.Equal("pkgutil", _analyzer.AnalyzerId);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_WithValidReceipts_ReturnsPackages()
|
|
{
|
|
// Arrange
|
|
var context = CreateContext(FixturesRoot);
|
|
|
|
// Act
|
|
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal("pkgutil", result.AnalyzerId);
|
|
Assert.True(result.Packages.Count > 0, "Expected at least one package");
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_FindsSafariPackage()
|
|
{
|
|
// Arrange
|
|
var context = CreateContext(FixturesRoot);
|
|
|
|
// Act
|
|
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
var safari = result.Packages.FirstOrDefault(p => p.Name == "Safari");
|
|
Assert.NotNull(safari);
|
|
Assert.Equal("17.1", safari.Version);
|
|
Assert.Contains("pkg:generic/apple/com.apple.pkg.Safari@17.1", safari.PackageUrl);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_ExtractsVendorFromIdentifier()
|
|
{
|
|
// Arrange
|
|
var context = CreateContext(FixturesRoot);
|
|
|
|
// Act
|
|
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
var safari = result.Packages.FirstOrDefault(p => p.Name == "Safari");
|
|
Assert.NotNull(safari);
|
|
Assert.Equal("apple", safari.SourcePackage);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_SetsEvidenceSourceToPkgutilReceipt()
|
|
{
|
|
// Arrange
|
|
var context = CreateContext(FixturesRoot);
|
|
|
|
// Act
|
|
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
foreach (var package in result.Packages)
|
|
{
|
|
Assert.Equal(PackageEvidenceSource.PkgutilReceipt, package.EvidenceSource);
|
|
}
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_ExtractsVendorMetadata()
|
|
{
|
|
// Arrange
|
|
var context = CreateContext(FixturesRoot);
|
|
|
|
// Act
|
|
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
var safari = result.Packages.FirstOrDefault(p => p.Name == "Safari");
|
|
Assert.NotNull(safari);
|
|
Assert.Equal("com.apple.pkg.Safari", safari.VendorMetadata["pkgutil:identifier"]);
|
|
Assert.Equal("/", safari.VendorMetadata["pkgutil:volume"]);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_ResultsAreDeterministicallySorted()
|
|
{
|
|
// Arrange
|
|
var context = CreateContext(FixturesRoot);
|
|
|
|
// Act
|
|
var result1 = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
var result2 = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
Assert.Equal(result1.Packages.Count, result2.Packages.Count);
|
|
for (int i = 0; i < result1.Packages.Count; i++)
|
|
{
|
|
Assert.Equal(result1.Packages[i].PackageUrl, result2.Packages[i].PackageUrl);
|
|
}
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_NoReceiptsDirectory_ReturnsEmptyPackages()
|
|
{
|
|
// Arrange - use temp directory without receipts
|
|
var tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
|
Directory.CreateDirectory(tempPath);
|
|
|
|
try
|
|
{
|
|
var context = CreateContext(tempPath);
|
|
|
|
// Act
|
|
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
Assert.Empty(result.Packages);
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(tempPath, recursive: true);
|
|
}
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task AnalyzeAsync_PopulatesTelemetry()
|
|
{
|
|
// Arrange
|
|
var context = CreateContext(FixturesRoot);
|
|
|
|
// Act
|
|
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
|
|
|
|
// Assert
|
|
Assert.NotNull(result.Telemetry);
|
|
Assert.True(result.Telemetry.PackageCount > 0);
|
|
Assert.True(result.Telemetry.Duration > TimeSpan.Zero);
|
|
}
|
|
}
|