Add unit tests for PackRunAttestation and SealedInstallEnforcer
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
release-manifest-verify / verify (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
release-manifest-verify / verify (push) Has been cancelled
- Implement comprehensive tests for PackRunAttestationService, covering attestation generation, verification, and event emission. - Add tests for SealedInstallEnforcer to validate sealed install requirements and enforcement logic. - Introduce a MonacoLoaderService stub for testing purposes to prevent Monaco workers/styles from loading during Karma runs.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Gradle;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class GradleGroovyParserTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ParsesStringNotationDependenciesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
dependencies {
|
||||
implementation 'org.slf4j:slf4j-api:1.7.36'
|
||||
api "com.google.guava:guava:31.1-jre"
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
}
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var result = await GradleGroovyParser.ParseAsync(tempFile, null, cancellationToken);
|
||||
|
||||
Assert.Equal(3, result.Dependencies.Length);
|
||||
|
||||
var slf4j = result.Dependencies.First(d => d.ArtifactId == "slf4j-api");
|
||||
Assert.Equal("org.slf4j", slf4j.GroupId);
|
||||
Assert.Equal("1.7.36", slf4j.Version);
|
||||
Assert.Equal("implementation", slf4j.Scope);
|
||||
|
||||
var guava = result.Dependencies.First(d => d.ArtifactId == "guava");
|
||||
Assert.Equal("com.google.guava", guava.GroupId);
|
||||
Assert.Equal("31.1-jre", guava.Version);
|
||||
Assert.Equal("api", guava.Scope);
|
||||
|
||||
var junit = result.Dependencies.First(d => d.ArtifactId == "junit");
|
||||
Assert.Equal("junit", junit.GroupId);
|
||||
Assert.Equal("4.13.2", junit.Version);
|
||||
Assert.Equal("testImplementation", junit.Scope);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesMapNotationDependenciesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
dependencies {
|
||||
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
|
||||
compileOnly(group: "javax.servlet", name: "servlet-api", version: "2.5")
|
||||
}
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var result = await GradleGroovyParser.ParseAsync(tempFile, null, cancellationToken);
|
||||
|
||||
Assert.Equal(2, result.Dependencies.Length);
|
||||
|
||||
var commons = result.Dependencies.First(d => d.ArtifactId == "commons-lang3");
|
||||
Assert.Equal("org.apache.commons", commons.GroupId);
|
||||
Assert.Equal("3.12.0", commons.Version);
|
||||
Assert.Equal("implementation", commons.Scope);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolvesPropertyPlaceholdersAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
dependencies {
|
||||
implementation "org.slf4j:slf4j-api:${slf4jVersion}"
|
||||
}
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var properties = new GradleProperties(
|
||||
new Dictionary<string, string> { ["slf4jVersion"] = "2.0.7" }.ToImmutableDictionary(),
|
||||
ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
var result = await GradleGroovyParser.ParseAsync(tempFile, properties, cancellationToken);
|
||||
|
||||
Assert.Single(result.Dependencies);
|
||||
var dep = result.Dependencies[0];
|
||||
Assert.Equal("2.0.7", dep.Version);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Maven;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class MavenPomParserTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ParsesDependenciesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>demo</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.36</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var result = await MavenPomParser.ParseAsync(tempFile, cancellationToken);
|
||||
|
||||
Assert.NotEqual(MavenPom.Empty, result);
|
||||
Assert.Equal("com.example", result.GroupId);
|
||||
Assert.Equal("demo", result.ArtifactId);
|
||||
Assert.Equal("1.0.0", result.Version);
|
||||
Assert.Equal(2, result.Dependencies.Length);
|
||||
|
||||
var slf4j = result.Dependencies.First(d => d.ArtifactId == "slf4j-api");
|
||||
Assert.Equal("org.slf4j", slf4j.GroupId);
|
||||
Assert.Equal("1.7.36", slf4j.Version);
|
||||
Assert.Null(slf4j.Scope);
|
||||
|
||||
var junit = result.Dependencies.First(d => d.ArtifactId == "junit");
|
||||
Assert.Equal("test", junit.Scope);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesPropertiesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>demo</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<properties>
|
||||
<slf4j.version>2.0.7</slf4j.version>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var result = await MavenPomParser.ParseAsync(tempFile, cancellationToken);
|
||||
|
||||
Assert.Equal(2, result.Properties.Count);
|
||||
Assert.Equal("2.0.7", result.Properties["slf4j.version"]);
|
||||
Assert.Equal("17", result.Properties["java.version"]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesLicensesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>demo</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
</license>
|
||||
</licenses>
|
||||
</project>
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var result = await MavenPomParser.ParseAsync(tempFile, cancellationToken);
|
||||
|
||||
Assert.Single(result.Licenses);
|
||||
var license = result.Licenses[0];
|
||||
Assert.Equal("Apache License, Version 2.0", license.Name);
|
||||
Assert.Equal("https://www.apache.org/licenses/LICENSE-2.0", license.Url);
|
||||
Assert.Equal("Apache-2.0", license.SpdxId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesParentReferenceAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</parent>
|
||||
<artifactId>demo</artifactId>
|
||||
</project>
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var result = await MavenPomParser.ParseAsync(tempFile, cancellationToken);
|
||||
|
||||
Assert.NotNull(result.Parent);
|
||||
Assert.Equal("org.springframework.boot", result.Parent.GroupId);
|
||||
Assert.Equal("spring-boot-starter-parent", result.Parent.ArtifactId);
|
||||
Assert.Equal("3.1.0", result.Parent.Version);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesDependencyManagementAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var content = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>demo</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
""";
|
||||
|
||||
var tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(tempFile, content, cancellationToken);
|
||||
var result = await MavenPomParser.ParseAsync(tempFile, cancellationToken);
|
||||
|
||||
Assert.Single(result.DependencyManagement);
|
||||
var bom = result.DependencyManagement[0];
|
||||
Assert.Equal("org.springframework.boot", bom.GroupId);
|
||||
Assert.Equal("spring-boot-dependencies", bom.ArtifactId);
|
||||
Assert.Equal("pom", bom.Type);
|
||||
Assert.Equal("import", bom.Scope);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Osgi;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class OsgiBundleParserTests
|
||||
{
|
||||
[Fact]
|
||||
public void ParsesBasicBundleManifest()
|
||||
{
|
||||
var manifest = """
|
||||
Manifest-Version: 1.0
|
||||
Bundle-SymbolicName: com.example.bundle
|
||||
Bundle-Version: 1.0.0.SNAPSHOT
|
||||
Bundle-Name: Example Bundle
|
||||
Bundle-Vendor: Example Corp
|
||||
""";
|
||||
|
||||
var dict = OsgiBundleParser.ParseManifest(manifest);
|
||||
var result = OsgiBundleParser.Parse(dict);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("com.example.bundle", result.SymbolicName);
|
||||
Assert.Equal("1.0.0.SNAPSHOT", result.Version);
|
||||
Assert.Equal("Example Bundle", result.Name);
|
||||
Assert.Equal("Example Corp", result.Vendor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesImportPackage()
|
||||
{
|
||||
var manifest = """
|
||||
Manifest-Version: 1.0
|
||||
Bundle-SymbolicName: com.example.bundle
|
||||
Bundle-Version: 1.0.0
|
||||
Import-Package: org.osgi.framework;version="[1.8,2)",
|
||||
org.slf4j;version="[1.7,2)",
|
||||
javax.servlet;resolution:=optional
|
||||
""";
|
||||
|
||||
var dict = OsgiBundleParser.ParseManifest(manifest);
|
||||
var result = OsgiBundleParser.Parse(dict);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(3, result.ImportPackage.Length);
|
||||
|
||||
var osgi = result.ImportPackage.First(p => p.PackageName == "org.osgi.framework");
|
||||
Assert.Equal("[1.8,2)", osgi.Version);
|
||||
Assert.False(osgi.IsOptional);
|
||||
|
||||
var servlet = result.ImportPackage.First(p => p.PackageName == "javax.servlet");
|
||||
Assert.True(servlet.IsOptional);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesExportPackage()
|
||||
{
|
||||
var manifest = """
|
||||
Manifest-Version: 1.0
|
||||
Bundle-SymbolicName: com.example.bundle
|
||||
Bundle-Version: 1.0.0
|
||||
Export-Package: com.example.api;version="1.0.0",
|
||||
com.example.impl;version="1.0.0"
|
||||
""";
|
||||
|
||||
var dict = OsgiBundleParser.ParseManifest(manifest);
|
||||
var result = OsgiBundleParser.Parse(dict);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.ExportPackage.Length);
|
||||
|
||||
var api = result.ExportPackage.First(p => p.PackageName == "com.example.api");
|
||||
Assert.Equal("1.0.0", api.Version);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesSingletonBundle()
|
||||
{
|
||||
var manifest = """
|
||||
Manifest-Version: 1.0
|
||||
Bundle-SymbolicName: com.example.singleton;singleton:=true
|
||||
Bundle-Version: 1.0.0
|
||||
""";
|
||||
|
||||
var dict = OsgiBundleParser.ParseManifest(manifest);
|
||||
var result = OsgiBundleParser.Parse(dict);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("com.example.singleton", result.SymbolicName);
|
||||
Assert.True(result.IsSingleton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsesFragmentHost()
|
||||
{
|
||||
var manifest = """
|
||||
Manifest-Version: 1.0
|
||||
Bundle-SymbolicName: com.example.fragment
|
||||
Bundle-Version: 1.0.0
|
||||
Fragment-Host: com.example.host;bundle-version="[1.0,2.0)"
|
||||
""";
|
||||
|
||||
var dict = OsgiBundleParser.ParseManifest(manifest);
|
||||
var result = OsgiBundleParser.Parse(dict);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.True(result.IsFragment);
|
||||
Assert.Contains("com.example.host", result.FragmentHost);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsNullForNonOsgiManifest()
|
||||
{
|
||||
var manifest = """
|
||||
Manifest-Version: 1.0
|
||||
Implementation-Title: Regular JAR
|
||||
Implementation-Version: 1.0.0
|
||||
""";
|
||||
|
||||
var dict = OsgiBundleParser.ParseManifest(manifest);
|
||||
var result = OsgiBundleParser.Parse(dict);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesManifestContinuationLines()
|
||||
{
|
||||
var manifest = "Manifest-Version: 1.0\r\n" +
|
||||
"Bundle-SymbolicName: com.example.bundle\r\n" +
|
||||
"Import-Package: org.osgi.framework;version=\"[1.8,2)\",org.slf4j;v\r\n" +
|
||||
" ersion=\"[1.7,2)\"\r\n" +
|
||||
"Bundle-Version: 1.0.0\r\n";
|
||||
|
||||
var dict = OsgiBundleParser.ParseManifest(manifest);
|
||||
var result = OsgiBundleParser.Parse(dict);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.ImportPackage.Length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Shading;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class ShadedJarDetectorTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task DetectsMultiplePomPropertiesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var jarPath = Path.Combine(Path.GetTempPath(), $"shaded-{Guid.NewGuid()}.jar");
|
||||
|
||||
try
|
||||
{
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "shaded", "1.0.0");
|
||||
WritePomProperties(archive, "org.slf4j", "slf4j-api", "1.7.36");
|
||||
WritePomProperties(archive, "com.google.guava", "guava", "31.1-jre");
|
||||
}
|
||||
|
||||
var result = await ShadedJarDetector.AnalyzeAsync(jarPath, cancellationToken);
|
||||
|
||||
Assert.True(result.IsShaded);
|
||||
Assert.Contains("multiple-pom-properties", result.Markers);
|
||||
Assert.Equal(3, result.EmbeddedArtifacts.Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(jarPath);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DetectsDependencyReducedPomAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var jarPath = Path.Combine(Path.GetTempPath(), $"shade-plugin-{Guid.NewGuid()}.jar");
|
||||
|
||||
try
|
||||
{
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "shaded", "1.0.0");
|
||||
var entry = archive.CreateEntry("META-INF/maven/com.example/shaded/dependency-reduced-pom.xml");
|
||||
using var writer = new StreamWriter(entry.Open(), Encoding.UTF8);
|
||||
writer.Write("<project></project>");
|
||||
}
|
||||
|
||||
var result = await ShadedJarDetector.AnalyzeAsync(jarPath, cancellationToken);
|
||||
|
||||
Assert.True(result.IsShaded);
|
||||
Assert.Contains("dependency-reduced-pom.xml", result.Markers);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(jarPath);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DetectsRelocatedPackagesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var jarPath = Path.Combine(Path.GetTempPath(), $"relocated-{Guid.NewGuid()}.jar");
|
||||
|
||||
try
|
||||
{
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "shaded", "1.0.0");
|
||||
// Create relocated class files
|
||||
CreateEmptyClass(archive, "shaded/com/google/common/collect/ImmutableList.class");
|
||||
CreateEmptyClass(archive, "shaded/com/google/common/base/Preconditions.class");
|
||||
}
|
||||
|
||||
var result = await ShadedJarDetector.AnalyzeAsync(jarPath, cancellationToken);
|
||||
|
||||
Assert.True(result.IsShaded);
|
||||
Assert.Contains("relocated-packages", result.Markers);
|
||||
Assert.NotEmpty(result.RelocatedPrefixes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(jarPath);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnsNotShadedForRegularJarAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var jarPath = Path.Combine(Path.GetTempPath(), $"regular-{Guid.NewGuid()}.jar");
|
||||
|
||||
try
|
||||
{
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "regular", "1.0.0");
|
||||
CreateEmptyClass(archive, "com/example/Main.class");
|
||||
}
|
||||
|
||||
var result = await ShadedJarDetector.AnalyzeAsync(jarPath, cancellationToken);
|
||||
|
||||
Assert.False(result.IsShaded);
|
||||
Assert.Empty(result.Markers);
|
||||
Assert.Equal(ShadingConfidence.None, result.Confidence);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(jarPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WritePomProperties(ZipArchive archive, string groupId, string artifactId, string version)
|
||||
{
|
||||
var path = $"META-INF/maven/{groupId}/{artifactId}/pom.properties";
|
||||
var entry = archive.CreateEntry(path);
|
||||
using var writer = new StreamWriter(entry.Open(), Encoding.UTF8);
|
||||
writer.WriteLine($"groupId={groupId}");
|
||||
writer.WriteLine($"artifactId={artifactId}");
|
||||
writer.WriteLine($"version={version}");
|
||||
}
|
||||
|
||||
private static void CreateEmptyClass(ZipArchive archive, string path)
|
||||
{
|
||||
var entry = archive.CreateEntry(path);
|
||||
using var stream = entry.Open();
|
||||
// Minimal class file header
|
||||
stream.Write([0xCA, 0xFE, 0xBA, 0xBE]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.BuildMetadata;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Conflicts;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class VersionConflictDetectorTests
|
||||
{
|
||||
[Fact]
|
||||
public void DetectsMajorVersionConflicts()
|
||||
{
|
||||
var dependencies = new[]
|
||||
{
|
||||
CreateDependency("org.slf4j", "slf4j-api", "1.7.36", "pom.xml"),
|
||||
CreateDependency("org.slf4j", "slf4j-api", "2.0.7", "gradle.lockfile")
|
||||
};
|
||||
|
||||
var result = VersionConflictDetector.Analyze(dependencies);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Equal(1, result.TotalConflicts);
|
||||
Assert.Equal(ConflictSeverity.High, result.MaxSeverity);
|
||||
|
||||
var conflict = result.Conflicts[0];
|
||||
Assert.Equal("org.slf4j", conflict.GroupId);
|
||||
Assert.Equal("slf4j-api", conflict.ArtifactId);
|
||||
Assert.Contains("1.7.36", conflict.UniqueVersions);
|
||||
Assert.Contains("2.0.7", conflict.UniqueVersions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DetectsMinorVersionConflicts()
|
||||
{
|
||||
var dependencies = new[]
|
||||
{
|
||||
CreateDependency("com.google.guava", "guava", "31.0-jre", "pom.xml"),
|
||||
CreateDependency("com.google.guava", "guava", "31.1-jre", "gradle.lockfile")
|
||||
};
|
||||
|
||||
var result = VersionConflictDetector.Analyze(dependencies);
|
||||
|
||||
Assert.True(result.HasConflicts);
|
||||
Assert.Equal(ConflictSeverity.Medium, result.MaxSeverity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IgnoresIdenticalVersions()
|
||||
{
|
||||
var dependencies = new[]
|
||||
{
|
||||
CreateDependency("org.slf4j", "slf4j-api", "1.7.36", "pom.xml"),
|
||||
CreateDependency("org.slf4j", "slf4j-api", "1.7.36", "gradle.lockfile")
|
||||
};
|
||||
|
||||
var result = VersionConflictDetector.Analyze(dependencies);
|
||||
|
||||
Assert.False(result.HasConflicts);
|
||||
Assert.Equal(0, result.TotalConflicts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsEmptyForNoDependencies()
|
||||
{
|
||||
var result = VersionConflictDetector.Analyze(Array.Empty<JavaDependencyDeclaration>());
|
||||
|
||||
Assert.False(result.HasConflicts);
|
||||
Assert.Equal(VersionConflictAnalysis.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConflictReturnsNullForNonConflicting()
|
||||
{
|
||||
var dependencies = new[]
|
||||
{
|
||||
CreateDependency("org.slf4j", "slf4j-api", "1.7.36", "pom.xml"),
|
||||
CreateDependency("com.google.guava", "guava", "31.1-jre", "pom.xml")
|
||||
};
|
||||
|
||||
var result = VersionConflictDetector.Analyze(dependencies);
|
||||
|
||||
Assert.Null(result.GetConflict("org.slf4j", "slf4j-api"));
|
||||
Assert.Null(result.GetConflict("com.google.guava", "guava"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConflictFindsConflictingArtifact()
|
||||
{
|
||||
var dependencies = new[]
|
||||
{
|
||||
CreateDependency("org.slf4j", "slf4j-api", "1.7.36", "pom.xml"),
|
||||
CreateDependency("org.slf4j", "slf4j-api", "2.0.7", "gradle.lockfile"),
|
||||
CreateDependency("com.google.guava", "guava", "31.1-jre", "pom.xml")
|
||||
};
|
||||
|
||||
var result = VersionConflictDetector.Analyze(dependencies);
|
||||
|
||||
var conflict = result.GetConflict("org.slf4j", "slf4j-api");
|
||||
Assert.NotNull(conflict);
|
||||
Assert.Equal(2, conflict.UniqueVersions.Count());
|
||||
|
||||
Assert.Null(result.GetConflict("com.google.guava", "guava"));
|
||||
}
|
||||
|
||||
private static JavaDependencyDeclaration CreateDependency(
|
||||
string groupId,
|
||||
string artifactId,
|
||||
string version,
|
||||
string source)
|
||||
{
|
||||
return new JavaDependencyDeclaration
|
||||
{
|
||||
GroupId = groupId,
|
||||
ArtifactId = artifactId,
|
||||
Version = version,
|
||||
Source = source,
|
||||
Locator = source
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user