up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
This commit is contained in:
@@ -645,6 +645,211 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region Multi-Module Gradle Lock & Runtime Image Tests (Sprint 0403)
|
||||
|
||||
[Fact]
|
||||
public async Task MultiModuleGradleLockFilesEmitLockModulePathMetadataAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Create root lockfile
|
||||
var rootLockPath = Path.Combine(root, "gradle.lockfile");
|
||||
await File.WriteAllTextAsync(rootLockPath, """
|
||||
# Root lockfile
|
||||
com.example:root-dep:1.0.0=compileClasspath
|
||||
com.example:shared-dep:2.0.0=runtimeClasspath
|
||||
""", cancellationToken);
|
||||
|
||||
// Create submodule directory and lockfile
|
||||
var appModuleDir = Path.Combine(root, "app");
|
||||
Directory.CreateDirectory(appModuleDir);
|
||||
var appLockPath = Path.Combine(appModuleDir, "gradle.lockfile");
|
||||
await File.WriteAllTextAsync(appLockPath, """
|
||||
# App module lockfile
|
||||
com.example:app-dep:3.0.0=compileClasspath
|
||||
com.example:shared-dep:2.5.0=runtimeClasspath
|
||||
""", cancellationToken);
|
||||
|
||||
// Create lib submodule directory and lockfile
|
||||
var libModuleDir = Path.Combine(root, "lib");
|
||||
Directory.CreateDirectory(libModuleDir);
|
||||
var libLockPath = Path.Combine(libModuleDir, "gradle.lockfile");
|
||||
await File.WriteAllTextAsync(libLockPath, """
|
||||
# Lib module lockfile
|
||||
com.example:lib-dep:4.0.0=testCompileClasspath
|
||||
""", cancellationToken);
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
root,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify root-dep has lockModulePath="."
|
||||
var rootDep = components.FirstOrDefault(c => c.GetProperty("name").GetString() == "root-dep");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, rootDep.ValueKind);
|
||||
Assert.Equal(".", rootDep.GetProperty("metadata").GetProperty("lockModulePath").GetString());
|
||||
Assert.Equal("gradle.lockfile", rootDep.GetProperty("metadata").GetProperty("lockLocator").GetString());
|
||||
|
||||
// Verify app-dep has lockModulePath="app"
|
||||
var appDep = components.FirstOrDefault(c => c.GetProperty("name").GetString() == "app-dep");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, appDep.ValueKind);
|
||||
Assert.Equal("app", appDep.GetProperty("metadata").GetProperty("lockModulePath").GetString());
|
||||
Assert.Equal("app/gradle.lockfile", appDep.GetProperty("metadata").GetProperty("lockLocator").GetString());
|
||||
|
||||
// Verify lib-dep has lockModulePath="lib"
|
||||
var libDep = components.FirstOrDefault(c => c.GetProperty("name").GetString() == "lib-dep");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, libDep.ValueKind);
|
||||
Assert.Equal("lib", libDep.GetProperty("metadata").GetProperty("lockModulePath").GetString());
|
||||
|
||||
// Verify shared-dep: different versions result in both being emitted with conflict detection
|
||||
// (first-wins only applies to identical GAV, different versions are separate entries)
|
||||
var sharedDeps = components.Where(c => c.GetProperty("name").GetString() == "shared-dep").ToArray();
|
||||
Assert.Equal(2, sharedDeps.Length);
|
||||
|
||||
// Verify version conflict is detected
|
||||
foreach (var sharedDep in sharedDeps)
|
||||
{
|
||||
var metadata = sharedDep.GetProperty("metadata");
|
||||
Assert.Equal("true", metadata.GetProperty("conflict.detected").GetString());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RuntimeImageEmitsExplicitKeyComponentAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Create a runtime image with release file
|
||||
var runtimeRoot = Path.Combine(root, "jdk", "jdk-21.0.1");
|
||||
Directory.CreateDirectory(runtimeRoot);
|
||||
Directory.CreateDirectory(Path.Combine(runtimeRoot, "bin"));
|
||||
Directory.CreateDirectory(Path.Combine(runtimeRoot, "lib"));
|
||||
|
||||
var javaBinary = OperatingSystem.IsWindows() ? "java.exe" : "java";
|
||||
await File.WriteAllTextAsync(Path.Combine(runtimeRoot, "bin", javaBinary), string.Empty, cancellationToken);
|
||||
await File.WriteAllTextAsync(Path.Combine(runtimeRoot, "lib", "modules"), string.Empty, cancellationToken);
|
||||
await File.WriteAllTextAsync(
|
||||
Path.Combine(runtimeRoot, "release"),
|
||||
"JAVA_VERSION=\"21.0.1\"\nIMPLEMENTOR=\"Eclipse Adoptium\"\n",
|
||||
cancellationToken);
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
root,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Find the runtime component
|
||||
var runtimeComponent = components.FirstOrDefault(c =>
|
||||
c.TryGetProperty("type", out var typeElement) &&
|
||||
typeElement.GetString() == "java-runtime");
|
||||
|
||||
Assert.NotEqual(JsonValueKind.Undefined, runtimeComponent.ValueKind);
|
||||
|
||||
// Verify no PURL (explicit-key)
|
||||
if (runtimeComponent.TryGetProperty("purl", out var purlElement))
|
||||
{
|
||||
Assert.Equal(JsonValueKind.Null, purlElement.ValueKind);
|
||||
}
|
||||
|
||||
// Verify metadata
|
||||
var metadata = runtimeComponent.GetProperty("metadata");
|
||||
Assert.Equal("21.0.1", metadata.GetProperty("java.version").GetString());
|
||||
Assert.Equal("Eclipse Adoptium", metadata.GetProperty("java.vendor").GetString());
|
||||
Assert.Equal("java-runtime", metadata.GetProperty("componentType").GetString());
|
||||
Assert.Equal("jdk/jdk-21.0.1", metadata.GetProperty("runtimeImagePath").GetString());
|
||||
|
||||
// Verify name format
|
||||
Assert.Equal("java-runtime-21.0.1 (Eclipse Adoptium)", runtimeComponent.GetProperty("name").GetString());
|
||||
Assert.Equal("21.0.1", runtimeComponent.GetProperty("version").GetString());
|
||||
|
||||
// Verify evidence references release file
|
||||
var evidence = runtimeComponent.GetProperty("evidence").EnumerateArray().ToArray();
|
||||
var releaseEvidence = evidence.FirstOrDefault(e =>
|
||||
e.GetProperty("source").GetString() == "release");
|
||||
Assert.NotEqual(JsonValueKind.Undefined, releaseEvidence.ValueKind);
|
||||
Assert.Equal("jdk/jdk-21.0.1/release", releaseEvidence.GetProperty("locator").GetString());
|
||||
Assert.True(releaseEvidence.TryGetProperty("sha256", out var sha256) && !string.IsNullOrWhiteSpace(sha256.GetString()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DuplicateRuntimeImagesAreDeduplicatedAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Create two identical runtime images in different locations
|
||||
foreach (var subPath in new[] { "runtime1/jdk", "runtime2/jdk" })
|
||||
{
|
||||
var runtimeRoot = Path.Combine(root, subPath);
|
||||
Directory.CreateDirectory(runtimeRoot);
|
||||
Directory.CreateDirectory(Path.Combine(runtimeRoot, "bin"));
|
||||
Directory.CreateDirectory(Path.Combine(runtimeRoot, "lib"));
|
||||
|
||||
var javaBinary = OperatingSystem.IsWindows() ? "java.exe" : "java";
|
||||
await File.WriteAllTextAsync(Path.Combine(runtimeRoot, "bin", javaBinary), string.Empty, cancellationToken);
|
||||
await File.WriteAllTextAsync(Path.Combine(runtimeRoot, "lib", "modules"), string.Empty, cancellationToken);
|
||||
await File.WriteAllTextAsync(
|
||||
Path.Combine(runtimeRoot, "release"),
|
||||
"JAVA_VERSION=\"17.0.8\"\nIMPLEMENTOR=\"Azul Systems\"\n",
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
root,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Both should be emitted because they have different paths
|
||||
var runtimeComponents = components.Where(c =>
|
||||
c.TryGetProperty("type", out var typeElement) &&
|
||||
typeElement.GetString() == "java-runtime").ToArray();
|
||||
|
||||
Assert.Equal(2, runtimeComponents.Length);
|
||||
|
||||
// Verify they have different runtimeImagePath
|
||||
var paths = runtimeComponents.Select(c =>
|
||||
c.GetProperty("metadata").GetProperty("runtimeImagePath").GetString()).ToHashSet();
|
||||
Assert.Contains("runtime1/jdk", paths);
|
||||
Assert.Contains("runtime2/jdk", paths);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static bool ComponentHasMetadata(JsonElement root, string componentName, string key, string expected)
|
||||
{
|
||||
foreach (var element in root.EnumerateArray())
|
||||
|
||||
Reference in New Issue
Block a user