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(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"); } } }