Add unit tests and implementations for MongoDB index models and OpenAPI metadata

- Implemented `MongoIndexModelTests` to verify index models for various stores.
- Created `OpenApiMetadataFactory` with methods to generate OpenAPI metadata.
- Added tests for `OpenApiMetadataFactory` to ensure expected defaults and URL overrides.
- Introduced `ObserverSurfaceSecrets` and `WebhookSurfaceSecrets` for managing secrets.
- Developed `RuntimeSurfaceFsClient` and `WebhookSurfaceFsClient` for manifest retrieval.
- Added dependency injection tests for `SurfaceEnvironmentRegistration` in both Observer and Webhook contexts.
- Implemented tests for secret resolution in `ObserverSurfaceSecretsTests` and `WebhookSurfaceSecretsTests`.
- Created `EnsureLinkNotMergeCollectionsMigrationTests` to validate MongoDB migration logic.
- Added project files for MongoDB tests and NuGet package mirroring.
This commit is contained in:
master
2025-11-17 21:21:56 +02:00
parent d3128aec24
commit 9075bad2d9
146 changed files with 152183 additions and 82 deletions

View File

@@ -0,0 +1,215 @@
using System.Text.Json;
namespace StellaOps.Scanner.Analyzers.Lang.DotNet.Internal;
/// <summary>
/// Resolves publish artifacts (deps/runtimeconfig) into deterministic entrypoint identities.
/// </summary>
public static class DotNetEntrypointResolver
{
private static readonly EnumerationOptions Enumeration = new()
{
RecurseSubdirectories = true,
IgnoreInaccessible = true,
AttributesToSkip = FileAttributes.Device | FileAttributes.ReparsePoint
};
public static ValueTask<IReadOnlyList<DotNetEntrypoint>> ResolveAsync(
LanguageAnalyzerContext context,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(context);
var depsFiles = Directory
.EnumerateFiles(context.RootPath, "*.deps.json", Enumeration)
.OrderBy(static path => path, StringComparer.Ordinal)
.ToArray();
if (depsFiles.Length == 0)
{
return ValueTask.FromResult<IReadOnlyList<DotNetEntrypoint>>(Array.Empty<DotNetEntrypoint>());
}
var results = new List<DotNetEntrypoint>(depsFiles.Length);
foreach (var depsPath in depsFiles)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var relativeDepsPath = NormalizeRelative(context.GetRelativePath(depsPath));
var depsFile = DotNetDepsFile.Load(depsPath, relativeDepsPath, cancellationToken);
if (depsFile is null)
{
continue;
}
DotNetRuntimeConfig? runtimeConfig = null;
var runtimeConfigPath = Path.ChangeExtension(depsPath, ".runtimeconfig.json");
string? relativeRuntimeConfig = null;
if (!string.IsNullOrEmpty(runtimeConfigPath) && File.Exists(runtimeConfigPath))
{
relativeRuntimeConfig = NormalizeRelative(context.GetRelativePath(runtimeConfigPath));
runtimeConfig = DotNetRuntimeConfig.Load(runtimeConfigPath, relativeRuntimeConfig, cancellationToken);
}
var tfms = CollectTargetFrameworks(depsFile, runtimeConfig);
var rids = CollectRuntimeIdentifiers(depsFile, runtimeConfig);
var publishKind = DeterminePublishKind(depsFile);
var name = GetEntrypointName(depsPath);
var id = BuildDeterministicId(name, tfms, rids, publishKind);
results.Add(new DotNetEntrypoint(
Id: id,
Name: name,
TargetFrameworks: tfms,
RuntimeIdentifiers: rids,
RelativeDepsPath: relativeDepsPath,
RelativeRuntimeConfigPath: relativeRuntimeConfig,
PublishKind: publishKind));
}
catch (IOException)
{
continue;
}
catch (JsonException)
{
continue;
}
catch (UnauthorizedAccessException)
{
continue;
}
}
return ValueTask.FromResult<IReadOnlyList<DotNetEntrypoint>>(results);
}
private static string GetEntrypointName(string depsPath)
{
// Strip .json then any trailing .deps suffix to yield a logical entrypoint name.
var stem = Path.GetFileNameWithoutExtension(depsPath); // removes .json
if (stem.EndsWith(".deps", StringComparison.OrdinalIgnoreCase))
{
stem = stem[..^".deps".Length];
}
return stem;
}
private static IReadOnlyCollection<string> CollectTargetFrameworks(DotNetDepsFile depsFile, DotNetRuntimeConfig? runtimeConfig)
{
var tfms = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var library in depsFile.Libraries.Values)
{
foreach (var tfm in library.TargetFrameworks)
{
tfms.Add(tfm);
}
}
if (runtimeConfig is not null)
{
foreach (var tfm in runtimeConfig.Tfms)
{
tfms.Add(tfm);
}
foreach (var framework in runtimeConfig.Frameworks)
{
tfms.Add(framework);
}
}
return tfms;
}
private static IReadOnlyCollection<string> CollectRuntimeIdentifiers(DotNetDepsFile depsFile, DotNetRuntimeConfig? runtimeConfig)
{
var rids = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var library in depsFile.Libraries.Values)
{
foreach (var rid in library.RuntimeIdentifiers)
{
rids.Add(rid);
}
}
if (runtimeConfig is not null)
{
foreach (var entry in runtimeConfig.RuntimeGraph)
{
rids.Add(entry.Rid);
foreach (var fallback in entry.Fallbacks)
{
if (!string.IsNullOrWhiteSpace(fallback))
{
rids.Add(fallback);
}
}
}
}
return rids;
}
private static DotNetPublishKind DeterminePublishKind(DotNetDepsFile depsFile)
{
foreach (var library in depsFile.Libraries.Values)
{
if (library.Id.StartsWith("Microsoft.NETCore.App.Runtime.", StringComparison.OrdinalIgnoreCase) ||
library.Id.StartsWith("Microsoft.WindowsDesktop.App.Runtime.", StringComparison.OrdinalIgnoreCase))
{
return DotNetPublishKind.SelfContained;
}
}
return DotNetPublishKind.FrameworkDependent;
}
private static string BuildDeterministicId(
string name,
IReadOnlyCollection<string> tfms,
IReadOnlyCollection<string> rids,
DotNetPublishKind publishKind)
{
var tfmPart = tfms.Count == 0 ? "unknown" : string.Join('+', tfms.OrderBy(t => t, StringComparer.OrdinalIgnoreCase));
var ridPart = rids.Count == 0 ? "none" : string.Join('+', rids.OrderBy(r => r, StringComparer.OrdinalIgnoreCase));
var publishPart = publishKind.ToString().ToLowerInvariant();
return $"{name}:{tfmPart}:{ridPart}:{publishPart}";
}
private static string NormalizeRelative(string path)
{
if (string.IsNullOrWhiteSpace(path) || path == ".")
{
return ".";
}
var normalized = path.Replace('\\', '/');
return string.IsNullOrWhiteSpace(normalized) ? "." : normalized;
}
}
public sealed record DotNetEntrypoint(
string Id,
string Name,
IReadOnlyCollection<string> TargetFrameworks,
IReadOnlyCollection<string> RuntimeIdentifiers,
string RelativeDepsPath,
string? RelativeRuntimeConfigPath,
DotNetPublishKind PublishKind);
public enum DotNetPublishKind
{
Unknown = 0,
FrameworkDependent = 1,
SelfContained = 2
}