This commit is contained in:
StellaOps Bot
2025-12-13 02:22:15 +02:00
parent 564df71bfb
commit 999e26a48e
395 changed files with 25045 additions and 2224 deletions

View File

@@ -16,12 +16,12 @@ public sealed class HealthWebAppFactory : WebApplicationFactory<Program>
public HealthWebAppFactory()
{
// Ensure options binder sees required storage values before Program.Main executes.
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DSN", "mongodb://localhost:27017/test-health");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DRIVER", "mongo");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DSN", "Host=localhost;Port=5432;Database=test-health");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DRIVER", "postgres");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__COMMANDTIMEOUTSECONDS", "30");
Environment.SetEnvironmentVariable("CONCELIER__TELEMETRY__ENABLED", "false");
Environment.SetEnvironmentVariable("CONCELIER_SKIP_OPTIONS_VALIDATION", "1");
Environment.SetEnvironmentVariable("CONCELIER_TEST_STORAGE_DSN", "mongodb://localhost:27017/test-health");
Environment.SetEnvironmentVariable("CONCELIER_TEST_STORAGE_DSN", "Host=localhost;Port=5432;Database=test-health");
Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Testing");
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Testing");
}
@@ -32,8 +32,8 @@ public sealed class HealthWebAppFactory : WebApplicationFactory<Program>
{
var overrides = new Dictionary<string, string?>
{
{"Storage:Dsn", "mongodb://localhost:27017/test-health"},
{"Storage:Driver", "mongo"},
{"Storage:Dsn", "Host=localhost;Port=5432;Database=test-health"},
{"Storage:Driver", "postgres"},
{"Storage:CommandTimeoutSeconds", "30"},
{"Telemetry:Enabled", "false"}
};
@@ -41,8 +41,8 @@ public sealed class HealthWebAppFactory : WebApplicationFactory<Program>
config.AddInMemoryCollection(overrides);
});
builder.UseSetting("CONCELIER__STORAGE__DSN", "mongodb://localhost:27017/test-health");
builder.UseSetting("CONCELIER__STORAGE__DRIVER", "mongo");
builder.UseSetting("CONCELIER__STORAGE__DSN", "Host=localhost;Port=5432;Database=test-health");
builder.UseSetting("CONCELIER__STORAGE__DRIVER", "postgres");
builder.UseSetting("CONCELIER__STORAGE__COMMANDTIMEOUTSECONDS", "30");
builder.UseSetting("CONCELIER__TELEMETRY__ENABLED", "false");
@@ -54,8 +54,8 @@ public sealed class HealthWebAppFactory : WebApplicationFactory<Program>
{
Storage = new ConcelierOptions.StorageOptions
{
Dsn = "mongodb://localhost:27017/test-health",
Driver = "mongo",
Dsn = "Host=localhost;Port=5432;Database=test-health",
Driver = "postgres",
CommandTimeoutSeconds = 30
},
Telemetry = new ConcelierOptions.TelemetryOptions
@@ -67,8 +67,8 @@ public sealed class HealthWebAppFactory : WebApplicationFactory<Program>
services.AddSingleton<IConfigureOptions<ConcelierOptions>>(sp => new ConfigureOptions<ConcelierOptions>(opts =>
{
opts.Storage ??= new ConcelierOptions.StorageOptions();
opts.Storage.Driver = "mongo";
opts.Storage.Dsn = "mongodb://localhost:27017/test-health";
opts.Storage.Driver = "postgres";
opts.Storage.Dsn = "Host=localhost;Port=5432;Database=test-health";
opts.Storage.CommandTimeoutSeconds = 30;
opts.Telemetry ??= new ConcelierOptions.TelemetryOptions();
@@ -77,8 +77,8 @@ public sealed class HealthWebAppFactory : WebApplicationFactory<Program>
services.PostConfigure<ConcelierOptions>(opts =>
{
opts.Storage ??= new ConcelierOptions.StorageOptions();
opts.Storage.Driver = "mongo";
opts.Storage.Dsn = "mongodb://localhost:27017/test-health";
opts.Storage.Driver = "postgres";
opts.Storage.Dsn = "Host=localhost;Port=5432;Database=test-health";
opts.Storage.CommandTimeoutSeconds = 30;
opts.Telemetry ??= new ConcelierOptions.TelemetryOptions();

View File

@@ -5,7 +5,7 @@ using StellaOps.Concelier.Documents.Serialization.Attributes;
namespace StellaOps.Concelier.WebService.Tests;
/// <summary>
/// Minimal linkset document used only for seeding the Mongo collection in WebService integration tests.
/// Minimal linkset document used for seeding the storage in WebService integration tests.
/// Matches the shape written by the linkset ingestion pipeline.
/// </summary>
internal sealed class AdvisoryLinksetDocument

View File

@@ -22,14 +22,14 @@ public sealed class OrchestratorTestWebAppFactory : WebApplicationFactory<Progra
{
public OrchestratorTestWebAppFactory()
{
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DSN", "mongodb://localhost:27017/orch-tests");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DRIVER", "mongo");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DSN", "Host=localhost;Port=5432;Database=orch-tests");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__DRIVER", "postgres");
Environment.SetEnvironmentVariable("CONCELIER__STORAGE__COMMANDTIMEOUTSECONDS", "30");
Environment.SetEnvironmentVariable("CONCELIER__TELEMETRY__ENABLED", "false");
Environment.SetEnvironmentVariable("CONCELIER__AUTHORITY__ENABLED", "false"); // disable auth so tests can hit endpoints without tokens
Environment.SetEnvironmentVariable("CONCELIER_SKIP_OPTIONS_VALIDATION", "1");
Environment.SetEnvironmentVariable("CONCELIER_TEST_STORAGE_DSN", "mongodb://localhost:27017/orch-tests");
Environment.SetEnvironmentVariable("CONCELIER_BYPASS_MONGO", "1");
Environment.SetEnvironmentVariable("CONCELIER_TEST_STORAGE_DSN", "Host=localhost;Port=5432;Database=orch-tests");
Environment.SetEnvironmentVariable("CONCELIER_BYPASS_EXTERNAL_STORAGE", "1");
Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Testing");
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Testing");
}
@@ -42,8 +42,8 @@ public sealed class OrchestratorTestWebAppFactory : WebApplicationFactory<Progra
{
cfg.AddInMemoryCollection(new Dictionary<string, string?>
{
["Concelier:Storage:Dsn"] = "mongodb://localhost:27017/orch-tests",
["Concelier:Storage:Driver"] = "mongo",
["Concelier:Storage:Dsn"] = "Host=localhost;Port=5432;Database=orch-tests",
["Concelier:Storage:Driver"] = "postgres",
["Concelier:Storage:CommandTimeoutSeconds"] = "30",
["Concelier:Telemetry:Enabled"] = "false",
["Concelier:Authority:Enabled"] = "false"
@@ -62,8 +62,8 @@ public sealed class OrchestratorTestWebAppFactory : WebApplicationFactory<Progra
{
Storage = new ConcelierOptions.StorageOptions
{
Dsn = "mongodb://localhost:27017/orch-tests",
Driver = "mongo",
Dsn = "Host=localhost;Port=5432;Database=orch-tests",
Driver = "postgres",
CommandTimeoutSeconds = 30
},
Telemetry = new ConcelierOptions.TelemetryOptions
@@ -78,10 +78,10 @@ public sealed class OrchestratorTestWebAppFactory : WebApplicationFactory<Progra
services.AddSingleton(forcedOptions);
services.AddSingleton<IOptions<ConcelierOptions>>(_ => Microsoft.Extensions.Options.Options.Create(forcedOptions));
// Force Mongo storage options to a deterministic in-memory test DSN.
// Force storage options to a deterministic in-memory test DSN.
services.PostConfigure<StorageOptions>(opts =>
{
opts.ConnectionString = "mongodb://localhost:27017/orch-tests";
opts.ConnectionString = "Host=localhost;Port=5432;Database=orch-tests";
opts.DatabaseName = "orch-tests";
opts.CommandTimeout = TimeSpan.FromSeconds(30);
});

View File

@@ -64,8 +64,6 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private readonly ITestOutputHelper _output;
private InMemoryDbRunner _runner = null!;
private Process? _externalMongo;
private string? _externalMongoDataPath;
private ConcelierApplicationFactory _factory = null!;
public WebServiceEndpointsTests(ITestOutputHelper output)
@@ -1735,12 +1733,12 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private async Task SeedObservationDocumentsAsync(IEnumerable<AdvisoryObservationDocument> documents)
{
var client = new InMemoryClient(_runner.ConnectionString);
var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<AdvisoryObservationDocument>(MongoStorageDefaults.Collections.AdvisoryObservations);
var database = client.GetDatabase(StorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<AdvisoryObservationDocument>(StorageDefaults.Collections.AdvisoryObservations);
try
{
await database.DropCollectionAsync(MongoStorageDefaults.Collections.AdvisoryObservations);
await database.DropCollectionAsync(StorageDefaults.Collections.AdvisoryObservations);
}
catch (StorageCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
{
@@ -1771,12 +1769,12 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private async Task SeedLinksetDocumentsAsync(IEnumerable<AdvisoryLinksetDocument> documents)
{
var client = new InMemoryClient(_runner.ConnectionString);
var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<AdvisoryLinksetDocument>(MongoStorageDefaults.Collections.AdvisoryLinksets);
var database = client.GetDatabase(StorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<AdvisoryLinksetDocument>(StorageDefaults.Collections.AdvisoryLinksets);
try
{
await database.DropCollectionAsync(MongoStorageDefaults.Collections.AdvisoryLinksets);
await database.DropCollectionAsync(StorageDefaults.Collections.AdvisoryLinksets);
}
catch (StorageCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
{
@@ -2004,7 +2002,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
IDictionary<string, string?>? environmentOverrides = null)
{
var defaultPostgresDsn = "Host=localhost;Port=5432;Database=concelier_test;Username=postgres;Password=postgres";
_connectionString = string.IsNullOrWhiteSpace(connectionString) || connectionString.StartsWith("mongodb://", StringComparison.OrdinalIgnoreCase)
_connectionString = string.IsNullOrWhiteSpace(connectionString)
? defaultPostgresDsn
: connectionString;
_authorityConfigure = authorityConfigure;
@@ -2098,7 +2096,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
services.AddSingleton<IJobCoordinator>(sp => sp.GetRequiredService<StubJobCoordinator>());
services.PostConfigure<ConcelierOptions>(options =>
{
options.Storage.Driver = "mongo";
options.Storage.Driver = "postgres";
options.Storage.Dsn = _connectionString;
options.Storage.CommandTimeoutSeconds = 30;
options.Plugins.Directory ??= Path.Combine(AppContext.BaseDirectory, "StellaOps.Concelier.PluginBinaries");
@@ -2362,8 +2360,8 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
using var validationScope = _factory.Services.CreateScope();
var database = validationScope.ServiceProvider.GetRequiredService<IMongoDatabase>();
var statements = database.GetCollection<DocumentObject>(MongoStorageDefaults.Collections.AdvisoryStatements);
var database = validationScope.ServiceProvider.GetRequiredService<IStorageDatabase>();
var statements = database.GetCollection<DocumentObject>(StorageDefaults.Collections.AdvisoryStatements);
var stored = await statements
.Find(Builders<DocumentObject>.Filter.Eq("_id", statementId.ToString()))
@@ -2379,8 +2377,8 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
finally
{
using var cleanupScope = _factory.Services.CreateScope();
var database = cleanupScope.ServiceProvider.GetRequiredService<IMongoDatabase>();
var statements = database.GetCollection<DocumentObject>(MongoStorageDefaults.Collections.AdvisoryStatements);
var database = cleanupScope.ServiceProvider.GetRequiredService<IStorageDatabase>();
var statements = database.GetCollection<DocumentObject>(StorageDefaults.Collections.AdvisoryStatements);
await statements.DeleteOneAsync(Builders<DocumentObject>.Filter.Eq("_id", statementId.ToString()));
}
}
@@ -2461,10 +2459,10 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private async Task SeedCanonicalAdvisoriesAsync(params Advisory[] advisories)
{
using var scope = _factory.Services.CreateScope();
var database = scope.ServiceProvider.GetRequiredService<IMongoDatabase>();
var database = scope.ServiceProvider.GetRequiredService<IStorageDatabase>();
await DropCollectionIfExistsAsync(database, MongoStorageDefaults.Collections.Advisory);
await DropCollectionIfExistsAsync(database, MongoStorageDefaults.Collections.Alias);
await DropCollectionIfExistsAsync(database, StorageDefaults.Collections.Advisory);
await DropCollectionIfExistsAsync(database, StorageDefaults.Collections.Alias);
if (advisories.Length == 0)
{
@@ -2478,7 +2476,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
}
}
private static async Task DropCollectionIfExistsAsync(IMongoDatabase database, string collectionName)
private static async Task DropCollectionIfExistsAsync(IStorageDatabase database, string collectionName)
{
try
{
@@ -2576,8 +2574,8 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private async Task SeedAdvisoryRawDocumentsAsync(params DocumentObject[] documents)
{
var client = new InMemoryClient(_runner.ConnectionString);
var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<DocumentObject>(MongoStorageDefaults.Collections.AdvisoryRaw);
var database = client.GetDatabase(StorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<DocumentObject>(StorageDefaults.Collections.AdvisoryRaw);
await collection.DeleteManyAsync(FilterDefinition<DocumentObject>.Empty);
if (documents.Length > 0)
{
@@ -2705,252 +2703,6 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
using var client = factory.CreateClient();
}
/// <summary>
/// Ensure Mongo2Go can start without external downloads by pointing it to cached binaries and OpenSSL 1.1 libs shipped in repo.
/// </summary>
private static void PrepareMongoEnvironment()
{
var repoRoot = FindRepoRoot();
if (repoRoot is null)
{
return;
}
var cacheDir = Path.Combine(repoRoot, ".cache", "mongodb-local");
Directory.CreateDirectory(cacheDir);
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))
{
// Prepend OpenSSL 1.1 path so Mongo2Go binaries find libssl/libcrypto.
var existing = Environment.GetEnvironmentVariable("LD_LIBRARY_PATH");
var combined = string.IsNullOrEmpty(existing) ? opensslPath : $"{opensslPath}:{existing}";
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", combined);
}
// Also drop the OpenSSL libs next to the mongod binary Mongo2Go will spawn, in case LD_LIBRARY_PATH is ignored.
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 (globalBin is not null)
{
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(globalDir, libName);
var source = Path.Combine(opensslPath, libName);
if (File.Exists(source) && !File.Exists(target))
{
File.Copy(source, target);
}
}
}
}
}
private bool TryStartExternalMongo(out string? connectionString)
{
connectionString = null;
var repoRoot = FindRepoRoot();
if (repoRoot is null)
{
return false;
}
var mongodCandidates = new List<string>();
void AddCandidates(string root)
{
if (Directory.Exists(root))
{
mongodCandidates.AddRange(Directory.GetFiles(root, "mongod", SearchOption.AllDirectories)
.Where(p => p.Contains("mongodb-linux-4.4.4", StringComparison.OrdinalIgnoreCase)));
}
}
AddCandidates(Path.Combine(repoRoot, ".nuget", "packages", "mongo2go"));
AddCandidates(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages", "mongo2go"));
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 InMemoryClient($"mongodb://127.0.0.1:{port}");
var sw = System.Diagnostics.Stopwatch.StartNew();
while (sw.Elapsed < TimeSpan.FromSeconds(5))
{
try
{
client.GetDatabase("admin").RunCommand<DocumentObject>("{ ping: 1 }");
// Initiate single-node replica set so features expecting replset work.
client.GetDatabase("admin").RunCommand<DocumentObject>(DocumentObject.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<DocumentObject>(DocumentObject.Parse("{ replSetGetStatus: 1 }"));
var myState = status["members"].AsDocumentArray.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;
}
var parent = Directory.GetParent(current);
if (parent is null)
{
break;
}
current = parent.FullName;
}
return lastMatch;
}
private static AdvisoryIngestRequest BuildAdvisoryIngestRequest(
string? contentHash,
string upstreamId,