Restructure solution layout by module
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Scanner.Analyzers.Lang.DotNet;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Tests.DotNet;
|
||||
|
||||
public sealed class DotNetLanguageAnalyzerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SimpleFixtureProducesDeterministicOutputAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "simple");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignedFixtureCapturesAssemblyMetadataAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "signed");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
var inspector = new StubAuthenticodeInspector();
|
||||
var services = new SingleServiceProvider(inspector);
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken,
|
||||
usageHints: null,
|
||||
services: services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SelfContainedFixtureHandlesNativeAssetsAndUsageAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "selfcontained");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var usageHints = new LanguageUsageHints(new[]
|
||||
{
|
||||
Path.Combine(fixturePath, "lib", "net10.0", "StellaOps.Toolkit.dll"),
|
||||
Path.Combine(fixturePath, "runtimes", "linux-x64", "native", "libstellaopsnative.so")
|
||||
});
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken,
|
||||
usageHints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnalyzerIsThreadSafeUnderConcurrencyAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "selfcontained");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
var workers = Math.Max(Environment.ProcessorCount, 4);
|
||||
var tasks = Enumerable.Range(0, workers)
|
||||
.Select(_ => LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken));
|
||||
|
||||
var results = await Task.WhenAll(tasks);
|
||||
var first = results[0];
|
||||
foreach (var result in results)
|
||||
{
|
||||
Assert.Equal(first, result);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiFixtureMergesRuntimeMetadataAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "multi");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
fixturePath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
Assert.True(root.ValueKind == JsonValueKind.Array, "Result root should be an array.");
|
||||
Assert.Equal(2, root.GetArrayLength());
|
||||
|
||||
var loggingComponent = root.EnumerateArray()
|
||||
.First(element => element.GetProperty("name").GetString() == "StellaOps.Logging");
|
||||
|
||||
var metadata = loggingComponent.GetProperty("metadata");
|
||||
Assert.Equal("StellaOps.Logging", loggingComponent.GetProperty("name").GetString());
|
||||
Assert.Equal("2.5.1", loggingComponent.GetProperty("version").GetString());
|
||||
Assert.Equal("pkg:nuget/stellaops.logging@2.5.1", loggingComponent.GetProperty("purl").GetString());
|
||||
|
||||
var ridValues = metadata.EnumerateObject()
|
||||
.Where(property => property.Name.Contains(".rid", StringComparison.Ordinal))
|
||||
.Select(property => property.Value.GetString())
|
||||
.Where(value => !string.IsNullOrEmpty(value))
|
||||
.Select(value => value!)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
Assert.Contains("linux-x64", ridValues);
|
||||
Assert.Contains("osx-arm64", ridValues);
|
||||
Assert.Contains("win-arm64", ridValues);
|
||||
}
|
||||
|
||||
private sealed class StubAuthenticodeInspector : IDotNetAuthenticodeInspector
|
||||
{
|
||||
public DotNetAuthenticodeMetadata? TryInspect(string assemblyPath, CancellationToken cancellationToken)
|
||||
=> new DotNetAuthenticodeMetadata(
|
||||
Subject: "CN=StellaOps Test Signing",
|
||||
Issuer: "CN=StellaOps Root",
|
||||
NotBefore: new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||
NotAfter: new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||
Thumbprint: "AA11BB22CC33DD44EE55FF66GG77HH88II99JJ00",
|
||||
SerialNumber: "0123456789ABCDEF");
|
||||
}
|
||||
|
||||
private sealed class SingleServiceProvider : IServiceProvider
|
||||
{
|
||||
private readonly object _service;
|
||||
|
||||
public SingleServiceProvider(object service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
public object? GetService(Type serviceType)
|
||||
=> serviceType == typeof(IDotNetAuthenticodeInspector) ? _service : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user