This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Globalization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
@@ -60,6 +61,8 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
|
||||
private readonly ITestOutputHelper _output;
|
||||
private MongoDbRunner _runner = null!;
|
||||
private Process? _externalMongo;
|
||||
private string? _externalMongoDataPath;
|
||||
private ConcelierApplicationFactory _factory = null!;
|
||||
|
||||
public WebServiceEndpointsTests(ITestOutputHelper output)
|
||||
@@ -70,8 +73,15 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
PrepareMongoEnvironment();
|
||||
_runner = MongoDbRunner.Start(singleNodeReplSet: true);
|
||||
_factory = new ConcelierApplicationFactory(_runner.ConnectionString);
|
||||
if (TryStartExternalMongo(out var externalConnectionString))
|
||||
{
|
||||
_factory = new ConcelierApplicationFactory(externalConnectionString);
|
||||
}
|
||||
else
|
||||
{
|
||||
_runner = MongoDbRunner.Start(singleNodeReplSet: true);
|
||||
_factory = new ConcelierApplicationFactory(_runner.ConnectionString);
|
||||
}
|
||||
WarmupFactory(_factory);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -79,7 +89,30 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
_factory.Dispose();
|
||||
_runner.Dispose();
|
||||
if (_externalMongo is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_externalMongo.HasExited)
|
||||
{
|
||||
_externalMongo.Kill(true);
|
||||
_externalMongo.WaitForExit(2000);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore cleanup errors in tests
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_externalMongoDataPath) && Directory.Exists(_externalMongoDataPath))
|
||||
{
|
||||
try { Directory.Delete(_externalMongoDataPath, recursive: true); } catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_runner.Dispose();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -2605,6 +2638,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_CACHE_LOCATION", cacheDir);
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_DOWNLOADS", cacheDir);
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_MONGODB_VERSION", "4.4.4");
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_MONGODB_PLATFORM", "linux");
|
||||
|
||||
var opensslPath = Path.Combine(repoRoot, "tests", "native", "openssl-1.1", "linux-x64");
|
||||
if (Directory.Exists(opensslPath))
|
||||
@@ -2616,16 +2650,80 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
}
|
||||
|
||||
// Also drop the OpenSSL libs next to the mongod binary Mongo2Go will spawn, in case LD_LIBRARY_PATH is ignored.
|
||||
var mongoBin = Directory.Exists(Path.Combine(repoRoot, ".nuget"))
|
||||
? Directory.GetFiles(Path.Combine(repoRoot, ".nuget", "packages", "mongo2go"), "mongod", SearchOption.AllDirectories)
|
||||
var repoNuget = Path.Combine(repoRoot, ".nuget", "packages", "mongo2go");
|
||||
var homeNuget = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages", "mongo2go");
|
||||
var mongoBin = Directory.Exists(repoNuget)
|
||||
? Directory.GetFiles(repoNuget, "mongod", SearchOption.AllDirectories)
|
||||
.FirstOrDefault(path => path.Contains("mongodb-linux-4.4.4", StringComparison.OrdinalIgnoreCase))
|
||||
: null;
|
||||
|
||||
// Prefer globally cached Mongo2Go binaries if repo-local cache is missing.
|
||||
mongoBin ??= Directory.Exists(homeNuget)
|
||||
? Directory.GetFiles(homeNuget, "mongod", SearchOption.AllDirectories)
|
||||
.FirstOrDefault(path => path.Contains("mongodb-linux-4.4.4", StringComparison.OrdinalIgnoreCase))
|
||||
: null;
|
||||
|
||||
if (mongoBin is not null && File.Exists(mongoBin) && Directory.Exists(opensslPath))
|
||||
{
|
||||
var binDir = Path.GetDirectoryName(mongoBin)!;
|
||||
|
||||
// Create a tiny wrapper so the loader always gets LD_LIBRARY_PATH even if vstest strips it.
|
||||
var wrapperPath = Path.Combine(cacheDir, "mongod-wrapper.sh");
|
||||
Directory.CreateDirectory(cacheDir);
|
||||
var script = $"#!/usr/bin/env bash\nset -euo pipefail\nexport LD_LIBRARY_PATH=\"{opensslPath}:${{LD_LIBRARY_PATH:-}}\"\nexec \"{mongoBin}\" \"$@\"\n";
|
||||
File.WriteAllText(wrapperPath, script);
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
try
|
||||
{
|
||||
File.SetUnixFileMode(wrapperPath,
|
||||
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
|
||||
UnixFileMode.GroupRead | UnixFileMode.GroupExecute |
|
||||
UnixFileMode.OtherRead | UnixFileMode.OtherExecute);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Best-effort; if not supported, chmod will fall back to default permissions.
|
||||
}
|
||||
}
|
||||
|
||||
// Force Mongo2Go to use the wrapper to avoid downloads and inject OpenSSL search path.
|
||||
Environment.SetEnvironmentVariable("MONGO2GO_MONGODB_BINARY", wrapperPath);
|
||||
|
||||
// Keep direct LD_LIBRARY_PATH/PATH hints for any code paths that still honour parent env.
|
||||
var existing = Environment.GetEnvironmentVariable("LD_LIBRARY_PATH");
|
||||
var combined = string.IsNullOrEmpty(existing) ? binDir : $"{binDir}:{existing}";
|
||||
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", combined);
|
||||
Environment.SetEnvironmentVariable("PATH", $"{binDir}:{Environment.GetEnvironmentVariable("PATH")}");
|
||||
|
||||
foreach (var libName in new[] { "libssl.so.1.1", "libcrypto.so.1.1" })
|
||||
{
|
||||
var target = Path.Combine(binDir, libName);
|
||||
var source = Path.Combine(opensslPath, libName);
|
||||
if (File.Exists(source) && !File.Exists(target))
|
||||
{
|
||||
File.Copy(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
// If the Mongo2Go global cache is different from the first hit, add its bin dir too.
|
||||
var globalBin = Directory.Exists(homeNuget)
|
||||
? Directory.GetFiles(homeNuget, "mongod", SearchOption.AllDirectories)
|
||||
.FirstOrDefault(path => path.Contains("mongodb-linux-4.4.4", StringComparison.OrdinalIgnoreCase))
|
||||
: null;
|
||||
if (mongoBin is not null && File.Exists(mongoBin) && Directory.Exists(opensslPath))
|
||||
if (globalBin is not null)
|
||||
{
|
||||
var binDir = Path.GetDirectoryName(mongoBin)!;
|
||||
var globalDir = Path.GetDirectoryName(globalBin)!;
|
||||
var withGlobal = Environment.GetEnvironmentVariable("LD_LIBRARY_PATH") ?? string.Empty;
|
||||
if (!withGlobal.Split(':', StringSplitOptions.RemoveEmptyEntries).Contains(globalDir))
|
||||
{
|
||||
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", $"{globalDir}:{withGlobal}".TrimEnd(':'));
|
||||
}
|
||||
Environment.SetEnvironmentVariable("PATH", $"{globalDir}:{Environment.GetEnvironmentVariable("PATH")}");
|
||||
foreach (var libName in new[] { "libssl.so.1.1", "libcrypto.so.1.1" })
|
||||
{
|
||||
var target = Path.Combine(binDir, libName);
|
||||
var target = Path.Combine(globalDir, libName);
|
||||
var source = Path.Combine(opensslPath, libName);
|
||||
if (File.Exists(source) && !File.Exists(target))
|
||||
{
|
||||
@@ -2634,29 +2732,142 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string? FindRepoRoot()
|
||||
private bool TryStartExternalMongo(out string? connectionString)
|
||||
{
|
||||
connectionString = null;
|
||||
|
||||
var repoRoot = FindRepoRoot();
|
||||
if (repoRoot is null)
|
||||
{
|
||||
var current = AppContext.BaseDirectory;
|
||||
while (!string.IsNullOrEmpty(current))
|
||||
return false;
|
||||
}
|
||||
|
||||
var mongodCandidates = new List<string>();
|
||||
void AddCandidates(string root)
|
||||
{
|
||||
if (Directory.Exists(root))
|
||||
{
|
||||
if (File.Exists(Path.Combine(current, "Directory.Build.props")))
|
||||
{
|
||||
return current;
|
||||
}
|
||||
mongodCandidates.AddRange(Directory.GetFiles(root, "mongod", SearchOption.AllDirectories)
|
||||
.Where(p => p.Contains("mongodb-linux-4.4.4", StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
}
|
||||
|
||||
var parent = Directory.GetParent(current);
|
||||
if (parent is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
AddCandidates(Path.Combine(repoRoot, ".nuget", "packages", "mongo2go"));
|
||||
AddCandidates(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages", "mongo2go"));
|
||||
|
||||
current = parent.FullName;
|
||||
var mongodPath = mongodCandidates.FirstOrDefault();
|
||||
if (mongodPath is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var dataDir = Path.Combine(repoRoot, ".cache", "mongodb-local", $"manual-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(dataDir);
|
||||
|
||||
var opensslPath = Path.Combine(repoRoot, "tests", "native", "openssl-1.1", "linux-x64");
|
||||
var port = GetEphemeralPort();
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = mongodPath,
|
||||
ArgumentList =
|
||||
{
|
||||
"--dbpath", dataDir,
|
||||
"--bind_ip", "127.0.0.1",
|
||||
"--port", port.ToString(),
|
||||
"--nojournal",
|
||||
"--quiet",
|
||||
"--replSet", "rs0"
|
||||
},
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true
|
||||
};
|
||||
|
||||
var existingLd = Environment.GetEnvironmentVariable("LD_LIBRARY_PATH");
|
||||
var ldCombined = string.IsNullOrEmpty(existingLd) ? opensslPath : $"{opensslPath}:{existingLd}";
|
||||
psi.Environment["LD_LIBRARY_PATH"] = ldCombined;
|
||||
psi.Environment["PATH"] = $"{Path.GetDirectoryName(mongodPath)}:{Environment.GetEnvironmentVariable("PATH")}";
|
||||
|
||||
_externalMongo = Process.Start(psi);
|
||||
_externalMongoDataPath = dataDir;
|
||||
|
||||
if (_externalMongo is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Small ping loop to ensure mongod is ready
|
||||
var client = new MongoClient($"mongodb://127.0.0.1:{port}");
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
while (sw.Elapsed < TimeSpan.FromSeconds(5))
|
||||
{
|
||||
try
|
||||
{
|
||||
client.GetDatabase("admin").RunCommand<BsonDocument>("{ ping: 1 }");
|
||||
// Initiate single-node replica set so features expecting replset work.
|
||||
client.GetDatabase("admin").RunCommand<BsonDocument>(BsonDocument.Parse("{ replSetInitiate: { _id: \"rs0\", members: [ { _id: 0, host: \"127.0.0.1:" + port + "\" } ] } }"));
|
||||
// Wait for primary
|
||||
var readySw = System.Diagnostics.Stopwatch.StartNew();
|
||||
while (readySw.Elapsed < TimeSpan.FromSeconds(5))
|
||||
{
|
||||
var status = client.GetDatabase("admin").RunCommand<BsonDocument>(BsonDocument.Parse("{ replSetGetStatus: 1 }"));
|
||||
var myState = status["members"].AsBsonArray.FirstOrDefault(x => x["self"].AsBoolean);
|
||||
if (myState != null && myState["state"].ToInt32() == 1)
|
||||
{
|
||||
connectionString = $"mongodb://127.0.0.1:{port}/?replicaSet=rs0";
|
||||
return true;
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
// fallback if primary not reached
|
||||
connectionString = $"mongodb://127.0.0.1:{port}";
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
try { _externalMongo.Kill(true); } catch { /* ignore */ }
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int GetEphemeralPort()
|
||||
{
|
||||
var listener = new System.Net.Sockets.TcpListener(IPAddress.Loopback, 0);
|
||||
listener.Start();
|
||||
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
|
||||
private static string? FindRepoRoot()
|
||||
{
|
||||
var current = AppContext.BaseDirectory;
|
||||
string? lastMatch = null;
|
||||
while (!string.IsNullOrEmpty(current))
|
||||
{
|
||||
if (File.Exists(Path.Combine(current, "Directory.Build.props")))
|
||||
{
|
||||
lastMatch = current;
|
||||
}
|
||||
|
||||
return null;
|
||||
var parent = Directory.GetParent(current);
|
||||
if (parent is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
current = parent.FullName;
|
||||
}
|
||||
|
||||
return lastMatch;
|
||||
}
|
||||
|
||||
private static AdvisoryIngestRequest BuildAdvisoryIngestRequest(
|
||||
string? contentHash,
|
||||
string upstreamId,
|
||||
|
||||
Reference in New Issue
Block a user