Implement incident mode management service and models
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Added IPackRunIncidentModeService interface for managing incident mode activation, deactivation, and status retrieval. - Created PackRunIncidentModeService class implementing the service interface with methods for activating, deactivating, and escalating incident modes. - Introduced incident mode status model (PackRunIncidentModeStatus) and related enums for escalation levels and activation sources. - Developed retention policy, telemetry settings, and debug capture settings models to manage incident mode configurations. - Implemented SLO breach notification handling to activate incident mode based on severity. - Added in-memory store (InMemoryPackRunIncidentModeStore) for testing purposes. - Created comprehensive unit tests for incident mode service, covering activation, deactivation, status retrieval, and SLO breach handling.
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.BuildMetadata;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.PropertyResolution;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class JavaPropertyResolverTests
|
||||
{
|
||||
[Fact]
|
||||
public void ResolvesSimpleProperty()
|
||||
{
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["version"] = "1.0.0"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
var result = resolver.Resolve("${version}");
|
||||
|
||||
Assert.Equal("1.0.0", result.ResolvedValue);
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Empty(result.UnresolvedProperties);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesMultipleProperties()
|
||||
{
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["groupId"] = "com.example",
|
||||
["artifactId"] = "demo",
|
||||
["version"] = "2.0.0"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
var result = resolver.Resolve("${groupId}:${artifactId}:${version}");
|
||||
|
||||
Assert.Equal("com.example:demo:2.0.0", result.ResolvedValue);
|
||||
Assert.True(result.IsFullyResolved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesNestedProperties()
|
||||
{
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["slf4j.version"] = "2.0.7",
|
||||
["logging.version"] = "${slf4j.version}"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
var result = resolver.Resolve("${logging.version}");
|
||||
|
||||
Assert.Equal("2.0.7", result.ResolvedValue);
|
||||
Assert.True(result.IsFullyResolved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesCircularReference()
|
||||
{
|
||||
// Circular: a → b → a (should stop at max depth)
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["a"] = "${b}",
|
||||
["b"] = "${a}"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
var result = resolver.Resolve("${a}");
|
||||
|
||||
// Should stop recursing and return whatever state it reaches
|
||||
Assert.False(result.IsFullyResolved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesMaxRecursionDepth()
|
||||
{
|
||||
// Create a chain of 15 nested properties (exceeds max depth of 10)
|
||||
var properties = new Dictionary<string, string>();
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
properties[$"prop{i}"] = $"${{prop{i + 1}}}";
|
||||
}
|
||||
properties["prop15"] = "final-value";
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties.ToImmutableDictionary());
|
||||
var result = resolver.Resolve("${prop0}");
|
||||
|
||||
// Should not reach final-value due to depth limit
|
||||
Assert.Contains("${", result.ResolvedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PreservesUnresolvedPlaceholder()
|
||||
{
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["known"] = "value"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
var result = resolver.Resolve("${unknown}");
|
||||
|
||||
Assert.Equal("${unknown}", result.ResolvedValue);
|
||||
Assert.False(result.IsFullyResolved);
|
||||
Assert.Single(result.UnresolvedProperties);
|
||||
Assert.Contains("unknown", result.UnresolvedProperties);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesMavenStandardProperties()
|
||||
{
|
||||
var resolver = new JavaPropertyResolver();
|
||||
|
||||
var basedir = resolver.Resolve("${project.basedir}");
|
||||
Assert.Equal(".", basedir.ResolvedValue);
|
||||
|
||||
var buildDir = resolver.Resolve("${project.build.directory}");
|
||||
Assert.Equal("target", buildDir.ResolvedValue);
|
||||
|
||||
var sourceDir = resolver.Resolve("${project.build.sourceDirectory}");
|
||||
Assert.Equal("src/main/java", sourceDir.ResolvedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesEmptyInput()
|
||||
{
|
||||
var resolver = new JavaPropertyResolver();
|
||||
|
||||
var nullResult = resolver.Resolve(null);
|
||||
Assert.Equal(PropertyResolutionResult.Empty, nullResult);
|
||||
|
||||
var emptyResult = resolver.Resolve(string.Empty);
|
||||
Assert.Equal(PropertyResolutionResult.Empty, emptyResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesInputWithoutPlaceholders()
|
||||
{
|
||||
var resolver = new JavaPropertyResolver();
|
||||
var result = resolver.Resolve("plain-text-value");
|
||||
|
||||
Assert.Equal("plain-text-value", result.ResolvedValue);
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Empty(result.UnresolvedProperties);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesFromParentChain()
|
||||
{
|
||||
var childProps = new Dictionary<string, string>
|
||||
{
|
||||
["child.version"] = "1.0.0"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var parentProps = new Dictionary<string, string>
|
||||
{
|
||||
["parent.version"] = "2.0.0",
|
||||
["shared.version"] = "parent-value"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var grandparentProps = new Dictionary<string, string>
|
||||
{
|
||||
["grandparent.version"] = "3.0.0"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(childProps, [parentProps, grandparentProps]);
|
||||
|
||||
// Child property
|
||||
Assert.Equal("1.0.0", resolver.Resolve("${child.version}").ResolvedValue);
|
||||
|
||||
// Parent property
|
||||
Assert.Equal("2.0.0", resolver.Resolve("${parent.version}").ResolvedValue);
|
||||
|
||||
// Grandparent property
|
||||
Assert.Equal("3.0.0", resolver.Resolve("${grandparent.version}").ResolvedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChildPropertyOverridesParent()
|
||||
{
|
||||
var childProps = new Dictionary<string, string>
|
||||
{
|
||||
["version"] = "child-value"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var parentProps = new Dictionary<string, string>
|
||||
{
|
||||
["version"] = "parent-value"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(childProps, [parentProps]);
|
||||
var result = resolver.Resolve("${version}");
|
||||
|
||||
Assert.Equal("child-value", result.ResolvedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesProjectCoordinateProperties()
|
||||
{
|
||||
var builder = new JavaPropertyBuilder()
|
||||
.AddProjectCoordinates("com.example", "demo", "1.0.0");
|
||||
|
||||
var resolver = new JavaPropertyResolver(builder.Build());
|
||||
|
||||
Assert.Equal("com.example", resolver.Resolve("${project.groupId}").ResolvedValue);
|
||||
Assert.Equal("com.example", resolver.Resolve("${groupId}").ResolvedValue);
|
||||
Assert.Equal("demo", resolver.Resolve("${project.artifactId}").ResolvedValue);
|
||||
Assert.Equal("1.0.0", resolver.Resolve("${project.version}").ResolvedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesDependencyVersion()
|
||||
{
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["slf4j.version"] = "2.0.7"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
|
||||
var dependency = new JavaDependencyDeclaration
|
||||
{
|
||||
GroupId = "org.slf4j",
|
||||
ArtifactId = "slf4j-api",
|
||||
Version = "${slf4j.version}",
|
||||
Source = "pom.xml"
|
||||
};
|
||||
|
||||
var resolved = resolver.ResolveDependency(dependency);
|
||||
|
||||
Assert.Equal("2.0.7", resolved.Version);
|
||||
Assert.Equal(JavaVersionSource.Property, resolved.VersionSource);
|
||||
Assert.Equal("slf4j.version", resolved.VersionProperty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesDependencyWithUnresolvedVersion()
|
||||
{
|
||||
var resolver = new JavaPropertyResolver();
|
||||
|
||||
var dependency = new JavaDependencyDeclaration
|
||||
{
|
||||
GroupId = "org.unknown",
|
||||
ArtifactId = "unknown",
|
||||
Version = "${unknown.version}",
|
||||
Source = "pom.xml"
|
||||
};
|
||||
|
||||
var resolved = resolver.ResolveDependency(dependency);
|
||||
|
||||
Assert.Equal("${unknown.version}", resolved.Version);
|
||||
Assert.Equal(JavaVersionSource.Unresolved, resolved.VersionSource);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PropertyBuilderAddRange()
|
||||
{
|
||||
var existing = new Dictionary<string, string>
|
||||
{
|
||||
["existing"] = "value1",
|
||||
["override"] = "original"
|
||||
};
|
||||
|
||||
var builder = new JavaPropertyBuilder()
|
||||
.Add("override", "new-value") // Added first
|
||||
.AddRange(existing); // Won't override
|
||||
|
||||
var props = builder.Build();
|
||||
|
||||
Assert.Equal("new-value", props["override"]);
|
||||
Assert.Equal("value1", props["existing"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PropertyBuilderAddParentCoordinates()
|
||||
{
|
||||
var parent = new JavaParentReference
|
||||
{
|
||||
GroupId = "org.springframework.boot",
|
||||
ArtifactId = "spring-boot-starter-parent",
|
||||
Version = "3.1.0"
|
||||
};
|
||||
|
||||
var builder = new JavaPropertyBuilder().AddParentCoordinates(parent);
|
||||
var props = builder.Build();
|
||||
|
||||
Assert.Equal("org.springframework.boot", props["project.parent.groupId"]);
|
||||
Assert.Equal("spring-boot-starter-parent", props["project.parent.artifactId"]);
|
||||
Assert.Equal("3.1.0", props["project.parent.version"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvesComplexMavenExpression()
|
||||
{
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["spring.version"] = "6.0.0",
|
||||
["project.version"] = "1.0.0-SNAPSHOT"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
var result = resolver.Resolve("spring-${spring.version}-app-${project.version}");
|
||||
|
||||
Assert.Equal("spring-6.0.0-app-1.0.0-SNAPSHOT", result.ResolvedValue);
|
||||
Assert.True(result.IsFullyResolved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HandlesMixedResolvedAndUnresolved()
|
||||
{
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
["known"] = "resolved"
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
var resolver = new JavaPropertyResolver(properties);
|
||||
var result = resolver.Resolve("${known}-${unknown}");
|
||||
|
||||
Assert.Equal("resolved-${unknown}", result.ResolvedValue);
|
||||
Assert.False(result.IsFullyResolved);
|
||||
Assert.Single(result.UnresolvedProperties);
|
||||
Assert.Contains("unknown", result.UnresolvedProperties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.BuildMetadata;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Maven;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class MavenEffectivePomBuilderTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task BuildsEffectivePomWithParentPropertiesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with properties
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
</properties>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child using parent properties
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Equal("17", result.EffectiveProperties["java.version"]);
|
||||
Assert.Single(result.ResolvedDependencies);
|
||||
|
||||
var dep = result.ResolvedDependencies[0];
|
||||
Assert.Equal("31.1-jre", dep.Version);
|
||||
Assert.Equal(JavaVersionSource.Property, dep.VersionSource);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergesParentDependencyManagementAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with dependencyManagement
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child with version-less dependencies
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.Equal(2, result.ResolvedDependencies.Length);
|
||||
|
||||
var slf4j = result.ResolvedDependencies.First(d => d.ArtifactId == "slf4j-api");
|
||||
Assert.Equal("2.0.7", slf4j.Version);
|
||||
|
||||
var junit = result.ResolvedDependencies.First(d => d.ArtifactId == "junit");
|
||||
Assert.Equal("4.13.2", junit.Version);
|
||||
Assert.Equal("test", junit.Scope);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChildDependencyManagementOverridesParentAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with version
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.36</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child overriding version
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(childPom, cancellationToken);
|
||||
|
||||
var dep = result.ResolvedDependencies.First(d => d.ArtifactId == "slf4j-api");
|
||||
Assert.Equal("2.0.9", dep.Version); // Child's version wins
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandlesStandalonePomAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var pomPath = Path.Combine(root, "pom.xml");
|
||||
await File.WriteAllTextAsync(pomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>standalone</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<properties>
|
||||
<encoding>UTF-8</encoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var pom = await MavenPomParser.ParseAsync(pomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(pom, cancellationToken);
|
||||
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Single(result.ParentChain); // Only the POM itself
|
||||
Assert.Equal("UTF-8", result.EffectiveProperties["encoding"]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolvesPropertyInDependencyManagementVersionAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var pomPath = Path.Combine(root, "pom.xml");
|
||||
await File.WriteAllTextAsync(pomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>app</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<properties>
|
||||
<commons.version>3.12.0</commons.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var pom = await MavenPomParser.ParseAsync(pomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(pom, cancellationToken);
|
||||
|
||||
var dep = result.ResolvedDependencies.First(d => d.ArtifactId == "commons-lang3");
|
||||
Assert.Equal("3.12.0", dep.Version);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TracksVersionSourceCorrectlyAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with dependencyManagement
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child with various version sources
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
<properties>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- Direct version -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
</dependency>
|
||||
<!-- Property version -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<!-- Dependency management version -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(childPom, cancellationToken);
|
||||
|
||||
var junit = result.ResolvedDependencies.First(d => d.ArtifactId == "junit");
|
||||
Assert.Equal("4.13.2", junit.Version);
|
||||
Assert.Equal(JavaVersionSource.Direct, junit.VersionSource);
|
||||
|
||||
var guava = result.ResolvedDependencies.First(d => d.ArtifactId == "guava");
|
||||
Assert.Equal("31.1-jre", guava.Version);
|
||||
Assert.Equal(JavaVersionSource.Property, guava.VersionSource);
|
||||
Assert.Equal("guava.version", guava.VersionProperty);
|
||||
|
||||
var slf4j = result.ResolvedDependencies.First(d => d.ArtifactId == "slf4j-api");
|
||||
Assert.Equal("2.0.7", slf4j.Version);
|
||||
// From parent's dependencyManagement
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CollectsAllLicensesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with Apache license
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
</license>
|
||||
</licenses>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child (inherits license)
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.Single(result.Licenses);
|
||||
Assert.Equal("Apache-2.0", result.Licenses[0].SpdxId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetsUnresolvedDependenciesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var pomPath = Path.Combine(root, "pom.xml");
|
||||
await File.WriteAllTextAsync(pomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>app</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<dependencies>
|
||||
<!-- Unresolved property -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${undefined.version}</version>
|
||||
</dependency>
|
||||
<!-- No version and not in dependencyManagement -->
|
||||
<dependency>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>missing</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var pom = await MavenPomParser.ParseAsync(pomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(pom, cancellationToken);
|
||||
|
||||
var unresolved = result.GetUnresolvedDependencies().ToList();
|
||||
Assert.Equal(2, unresolved.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PopulatesManagedVersionsIndexAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var pomPath = Path.Combine(root, "pom.xml");
|
||||
await File.WriteAllTextAsync(pomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>app</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>31.1-jre</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var pom = await MavenPomParser.ParseAsync(pomPath, cancellationToken);
|
||||
var builder = new MavenEffectivePomBuilder(root);
|
||||
var result = await builder.BuildAsync(pom, cancellationToken);
|
||||
|
||||
Assert.Equal(2, result.ManagedVersions.Count);
|
||||
Assert.True(result.ManagedVersions.ContainsKey("org.slf4j:slf4j-api"));
|
||||
Assert.True(result.ManagedVersions.ContainsKey("com.google.guava:guava"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Maven;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests.Parsers;
|
||||
|
||||
public sealed class MavenParentResolverTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ResolvesRelativePathParentAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Create parent/pom.xml
|
||||
var parentDir = Path.Combine(root, "parent");
|
||||
Directory.CreateDirectory(parentDir);
|
||||
await File.WriteAllTextAsync(Path.Combine(parentDir, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Create child/pom.xml with relativePath to parent
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Equal(2, result.ParentChain.Length); // child + parent
|
||||
Assert.Equal("17", result.EffectiveProperties["java.version"]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolvesDefaultRelativePathAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Create parent pom.xml in parent directory (default ../pom.xml)
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Create child in subdirectory with no relativePath (defaults to ../pom.xml)
|
||||
var childDir = Path.Combine(root, "module");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</parent>
|
||||
<artifactId>module</artifactId>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Equal("2.0.0", result.EffectiveVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolvesMultiLevelParentChainAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Grandparent
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>grandparent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<grandparent.prop>gp-value</grandparent.prop>
|
||||
</properties>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Parent
|
||||
var parentDir = Path.Combine(root, "parent");
|
||||
Directory.CreateDirectory(parentDir);
|
||||
await File.WriteAllTextAsync(Path.Combine(parentDir, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>grandparent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<properties>
|
||||
<parent.prop>parent-value</parent.prop>
|
||||
</properties>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child
|
||||
var childDir = Path.Combine(parentDir, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Equal(3, result.ParentChain.Length); // child, parent, grandparent
|
||||
Assert.Equal("gp-value", result.EffectiveProperties["grandparent.prop"]);
|
||||
Assert.Equal("parent-value", result.EffectiveProperties["parent.prop"]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnsUnresolvedForMissingParentAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var childPomPath = Path.Combine(root, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>missing-parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>orphan</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.False(result.IsFullyResolved);
|
||||
Assert.Single(result.UnresolvedParents);
|
||||
Assert.Contains("com.example:missing-parent:1.0.0", result.UnresolvedParents);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandlesNoParentAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var pomPath = Path.Combine(root, "pom.xml");
|
||||
await File.WriteAllTextAsync(pomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>standalone</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var pom = await MavenPomParser.ParseAsync(pomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(pom, cancellationToken);
|
||||
|
||||
Assert.True(result.IsFullyResolved);
|
||||
Assert.Single(result.ParentChain); // Only the original POM
|
||||
Assert.Equal("com.example", result.EffectiveGroupId);
|
||||
Assert.Equal("1.0.0", result.EffectiveVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InheritsGroupIdFromParentAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with groupId
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>org.parent</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child without explicit groupId
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>org.parent</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.Equal("org.parent", result.EffectiveGroupId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InheritsVersionFromParentAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>2.5.0</version>
|
||||
<packaging>pom</packaging>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.Equal("2.5.0", result.EffectiveVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolvesDependencyVersionFromManagementAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with dependencyManagement
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child with dependency without version
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.Single(result.ResolvedDependencies);
|
||||
var dep = result.ResolvedDependencies[0];
|
||||
Assert.Equal("2.0.7", dep.Version);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolvesPropertyInDependencyVersionAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var pomPath = Path.Combine(root, "pom.xml");
|
||||
await File.WriteAllTextAsync(pomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>app</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<properties>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var pom = await MavenPomParser.ParseAsync(pomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(pom, cancellationToken);
|
||||
|
||||
Assert.Single(result.ResolvedDependencies);
|
||||
var dep = result.ResolvedDependencies[0];
|
||||
Assert.Equal("31.1-jre", dep.Version);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CollectsLicensesFromChainAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with license
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
</license>
|
||||
</licenses>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
Assert.Single(result.AllLicenses);
|
||||
Assert.Equal("Apache-2.0", result.AllLicenses[0].SpdxId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChildPropertyOverridesParentPropertyAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
// Parent with property
|
||||
await File.WriteAllTextAsync(Path.Combine(root, "pom.xml"), """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
// Child overriding property
|
||||
var childDir = Path.Combine(root, "child");
|
||||
Directory.CreateDirectory(childDir);
|
||||
var childPomPath = Path.Combine(childDir, "pom.xml");
|
||||
await File.WriteAllTextAsync(childPomPath, """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>child</artifactId>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
</project>
|
||||
""", cancellationToken);
|
||||
|
||||
var childPom = await MavenPomParser.ParseAsync(childPomPath, cancellationToken);
|
||||
var resolver = new MavenParentResolver(root);
|
||||
var result = await resolver.ResolveAsync(childPom, cancellationToken);
|
||||
|
||||
// Child property should win
|
||||
Assert.Equal("17", result.EffectiveProperties["java.version"]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user