Files
git.stella-ops.org/src/Scanner/__Tests/StellaOps.Scanner.Analyzers.OS.Homebrew.Tests/HomebrewPackageAnalyzerTests.cs
2026-01-08 08:54:27 +02:00

236 lines
7.4 KiB
C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Scanner.Analyzers.OS.Homebrew;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Scanner.Analyzers.OS.Homebrew.Tests;
public sealed class HomebrewPackageAnalyzerTests
{
private static readonly string FixturesRoot = Path.Combine(
AppContext.BaseDirectory,
"Fixtures");
private readonly HomebrewPackageAnalyzer _analyzer;
private readonly ILogger _logger;
public HomebrewPackageAnalyzerTests()
{
_logger = NullLoggerFactory.Instance.CreateLogger<HomebrewPackageAnalyzer>();
_analyzer = new HomebrewPackageAnalyzer((ILogger<HomebrewPackageAnalyzer>)_logger);
}
private OSPackageAnalyzerContext CreateContext(string rootPath)
{
return new OSPackageAnalyzerContext(
rootPath,
workspacePath: null,
TimeProvider.System,
_logger);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void AnalyzerId_ReturnsHomebrew()
{
Assert.Equal("homebrew", _analyzer.AnalyzerId);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_WithValidCellar_ReturnsPackages()
{
// Arrange
var context = CreateContext(FixturesRoot);
// Act
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
// Assert
Assert.NotNull(result);
Assert.Equal("homebrew", result.AnalyzerId);
Assert.True(result.Packages.Count > 0, "Expected at least one package");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_FindsIntelCellarPackages()
{
// Arrange
var context = CreateContext(FixturesRoot);
// Act
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
// Assert
var openssl = result.Packages.FirstOrDefault(p => p.Name == "openssl@3");
Assert.NotNull(openssl);
Assert.Equal("3.1.0", openssl.Version);
Assert.Equal("x86_64", openssl.Architecture);
Assert.Contains("pkg:brew/homebrew%2Fcore/openssl%403@3.1.0", openssl.PackageUrl);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_FindsAppleSiliconCellarPackages()
{
// Arrange
var context = CreateContext(FixturesRoot);
// Act
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
// Assert
var jq = result.Packages.FirstOrDefault(p => p.Name == "jq");
Assert.NotNull(jq);
Assert.Equal("1.7", jq.Version);
Assert.Equal("arm64", jq.Architecture);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_PackageWithRevision_IncludesRevisionInPurl()
{
// Arrange
var context = CreateContext(FixturesRoot);
// Act
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
// Assert
var wget = result.Packages.FirstOrDefault(p => p.Name == "wget");
Assert.NotNull(wget);
Assert.Contains("?revision=1", wget.PackageUrl);
Assert.Equal("1", wget.Release);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_ExtractsDependencies()
{
// Arrange
var context = CreateContext(FixturesRoot);
// Act
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
// Assert
var wget = result.Packages.FirstOrDefault(p => p.Name == "wget");
Assert.NotNull(wget);
Assert.Contains("openssl@3", wget.Depends);
Assert.Contains("gettext", wget.Depends);
}
[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 openssl = result.Packages.FirstOrDefault(p => p.Name == "openssl@3");
Assert.NotNull(openssl);
Assert.Equal("homebrew/core", openssl.VendorMetadata["brew:tap"]);
Assert.Equal("true", openssl.VendorMetadata["brew:poured_from_bottle"]);
Assert.Equal("Cryptography and SSL/TLS Toolkit", openssl.VendorMetadata["description"]);
Assert.Equal("https://openssl.org/", openssl.VendorMetadata["homepage"]);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_SetsEvidenceSourceToHomebrewCellar()
{
// 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.HomebrewCellar, package.EvidenceSource);
}
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_DiscoversBinFiles()
{
// Arrange
var context = CreateContext(FixturesRoot);
// Act
var result = await _analyzer.AnalyzeAsync(context, TestContext.Current.CancellationToken);
// Assert
var wget = result.Packages.FirstOrDefault(p => p.Name == "wget");
Assert.NotNull(wget);
Assert.Contains(wget.Files, f => f.Path.Contains("wget"));
}
[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_NoCellar_ReturnsEmptyPackages()
{
// Arrange - use temp directory without Cellar structure
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);
}
}