tools/ci/with-test-mongod.sh starts mongod (or podman's mongo:8) on a random localhost port with a temporary data directory, runs the command, and removes both. build.yml and deploy.yml test through it, so the ~85 integration tests stop being skipped in CI. MongoFixture refuses production's port and data directory, and in CI anything but the wrapper's mongod; with PRIVAPUB_TEST_REQUIRE_MONGOD=1 a missing mongod fails instead of skipping. The deploy now passes the PRIVAPUB_SMOKE_TOKEN secret to the Mastodon smoke check, so its signed-in half runs once the owner creates the persona and the secret. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELjqpznMFMNrJoJUj6K5p2
81 lines
3.1 KiB
C#
81 lines
3.1 KiB
C#
using MongoDB.Bson;
|
|
using MongoDB.Bson.Serialization;
|
|
using MongoDB.Bson.Serialization.Serializers;
|
|
using MongoDB.Driver;
|
|
using MongoDB.Entities;
|
|
|
|
using PrivaPub.Infrastructure.Data;
|
|
|
|
[assembly: AssemblyFixture(typeof(PrivaPub.Tests.Support.MongoFixture))]
|
|
|
|
namespace PrivaPub.Tests.Support
|
|
{
|
|
public sealed class MongoFixture : IAsyncLifetime
|
|
{
|
|
public const string Skip = "set PRIVAPUB_TEST_MONGOD=1 (and optionally PRIVAPUB_TEST_MONGO) to run the tests that need a mongod";
|
|
const int ProductionPort = 27022;
|
|
const int DefaultPort = 27017;
|
|
|
|
public static bool Enabled => Environment.GetEnvironmentVariable("PRIVAPUB_TEST_MONGOD") == "1";
|
|
static bool Required => Environment.GetEnvironmentVariable("PRIVAPUB_TEST_REQUIRE_MONGOD") == "1";
|
|
static bool InCi => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI")) || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITEA_ACTIONS"));
|
|
|
|
public static string Connection { get; } = Environment.GetEnvironmentVariable("PRIVAPUB_TEST_MONGO") ?? $"mongodb://127.0.0.1:{DefaultPort}";
|
|
public static string Database { get; } = $"PrivaPubTests_{Guid.NewGuid():N}";
|
|
|
|
public async ValueTask InitializeAsync()
|
|
{
|
|
if (Required && !Enabled)
|
|
throw new InvalidOperationException("PRIVAPUB_TEST_REQUIRE_MONGOD=1 but PRIVAPUB_TEST_MONGOD is not 1: the integration tests would be skipped");
|
|
if (!Enabled)
|
|
return;
|
|
var settings = MongoClientSettings.FromConnectionString(Connection);
|
|
Refuse(settings);
|
|
try
|
|
{
|
|
BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));
|
|
}
|
|
catch (BsonSerializationException)
|
|
{
|
|
}
|
|
await DB.InitAsync(Database, settings);
|
|
await RefuseProductionData();
|
|
EntityMaps.Warm();
|
|
await Indexes.Create();
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (Enabled)
|
|
await DB.Default.Database().Client.DropDatabaseAsync(Database);
|
|
}
|
|
|
|
static void Refuse(MongoClientSettings settings)
|
|
{
|
|
var ports = settings.Servers.Select(s => s.Port).ToList();
|
|
if (ports.Contains(ProductionPort))
|
|
throw new InvalidOperationException($"the tests never run against port {ProductionPort}: that is production's mongod");
|
|
if (InCi && (Environment.GetEnvironmentVariable("PRIVAPUB_TEST_MONGO") == default || ports.Contains(DefaultPort)))
|
|
throw new InvalidOperationException("in CI the tests run only against a throwaway mongod: use tools/ci/with-test-mongod.sh");
|
|
}
|
|
|
|
static async Task RefuseProductionData()
|
|
{
|
|
BsonDocument options;
|
|
try
|
|
{
|
|
options = await DB.Default.Database().Client.GetDatabase("admin").RunCommandAsync<BsonDocument>(new BsonDocument("getCmdLineOpts", 1));
|
|
}
|
|
catch (MongoCommandException)
|
|
{
|
|
return;
|
|
}
|
|
var dbPath = options.GetValue("parsed", new BsonDocument()).AsBsonDocument
|
|
.GetValue("storage", new BsonDocument()).AsBsonDocument
|
|
.GetValue("dbPath", BsonNull.Value);
|
|
if (dbPath.IsString && dbPath.AsString.StartsWith("/var/lib/privapub", StringComparison.Ordinal))
|
|
throw new InvalidOperationException($"the mongod at {Connection} keeps its data in {dbPath.AsString}: that is production's");
|
|
}
|
|
}
|
|
}
|