using MicCheck.Api.Audit; using MicCheck.Api.Data; using MicCheck.Api.Environments; using MicCheck.Api.Features; using MicCheck.Api.Identities; using MicCheck.Api.Organizations; using MicCheck.Api.Projects; using MicCheck.Api.Tests.Unit.TestSupport; using Moq; using NUnit.Framework; using AppEnvironment = MicCheck.Api.Environments.Environment; namespace MicCheck.Api.Tests.Unit.Environments; [TestFixture] public class EnvironmentServiceTests { private Mock _db = null!; private List _environments = null!; private List _features = null!; private List _featureStates = null!; private List _identities = null!; private EnvironmentService _service = null!; private const int OrganizationId = 1; private const int ProjectId = 1; [SetUp] public void SetUp() { _db = new Mock(); _db.SetupDbSet(c => c.Organizations, [ new Organization { Id = OrganizationId, Name = "Test Org", CreatedAt = DateTimeOffset.UtcNow } ]); _db.SetupDbSet(c => c.Projects, [ new Project { Id = ProjectId, Name = "Test Project", OrganizationId = OrganizationId, CreatedAt = DateTimeOffset.UtcNow } ]); _environments = []; _db.SetupDbSetWithGeneratedIds(c => c.Environments, _environments); _features = []; _db.SetupDbSet(c => c.Features, _features); _featureStates = []; _db.SetupDbSet(c => c.FeatureStates, _featureStates); _identities = []; _db.SetupDbSet(c => c.Identities, _identities); var webhookQueue = new MicCheck.Api.Webhooks.WebhookQueue(); var auditService = new Mock(_db.Object, null!, webhookQueue); auditService.Setup(a => a.LogAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(Task.CompletedTask); _service = new EnvironmentService(_db.Object, auditService.Object); } [Test] public async Task WhenCreatingAnEnvironment_ThenFeatureStateIsCreatedForEachExistingFeature() { _features.Add(new Feature { Id = 1, Name = "feature_a", ProjectId = ProjectId, InitialValue = "hello", CreatedAt = DateTimeOffset.UtcNow }); var environment = await _service.CreateAsync(ProjectId, "Staging"); var states = _featureStates.Where(fs => fs.EnvironmentId == environment.Id).ToList(); Assert.That(states, Has.Count.EqualTo(1)); Assert.That(states[0].Value, Is.EqualTo("hello")); } [Test] public async Task WhenCreatingAnEnvironment_ThenAUniqueApiKeyIsGenerated() { var env1 = await _service.CreateAsync(ProjectId, "Env1"); var env2 = await _service.CreateAsync(ProjectId, "Env2"); Assert.That(env1.ApiKey, Is.Not.EqualTo(env2.ApiKey)); } [Test] public async Task WhenCloningAnEnvironment_ThenEnvironmentLevelFeatureStatesAreCopied() { var source = await _service.CreateAsync(ProjectId, "Production"); var feature = new Feature { Id = 1, Name = "flag", ProjectId = ProjectId, CreatedAt = DateTimeOffset.UtcNow }; _features.Add(feature); _featureStates.Add(new FeatureState { FeatureId = feature.Id, EnvironmentId = source.Id, Enabled = true, Value = "prod-value", CreatedAt = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow }); var cloned = await _service.CloneAsync(source.ApiKey, "Staging"); var clonedStates = _featureStates.Where(fs => fs.EnvironmentId == cloned.Id).ToList(); Assert.That(clonedStates, Has.Count.EqualTo(1)); Assert.That(clonedStates[0].Value, Is.EqualTo("prod-value")); Assert.That(clonedStates[0].Enabled, Is.True); } [Test] public async Task WhenCloningAnEnvironment_ThenIdentityOverridesAreNotCopied() { var source = await _service.CreateAsync(ProjectId, "Production"); var feature = new Feature { Id = 1, Name = "flag", ProjectId = ProjectId, CreatedAt = DateTimeOffset.UtcNow }; _features.Add(feature); var identity = new Identity { Id = 1, Identifier = "user-1", EnvironmentId = source.Id, CreatedAt = DateTimeOffset.UtcNow }; _identities.Add(identity); _featureStates.Add(new FeatureState { FeatureId = feature.Id, EnvironmentId = source.Id, IdentityId = identity.Id, Enabled = true, CreatedAt = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow }); var cloned = await _service.CloneAsync(source.ApiKey, "Staging"); var clonedStates = _featureStates.Where(fs => fs.EnvironmentId == cloned.Id).ToList(); Assert.That(clonedStates, Is.Empty); } [Test] public async Task WhenCloningAnEnvironment_ThenClonedEnvironmentHasDifferentApiKey() { var source = await _service.CreateAsync(ProjectId, "Production"); var cloned = await _service.CloneAsync(source.ApiKey, "Staging"); Assert.That(cloned.ApiKey, Is.Not.EqualTo(source.ApiKey)); } }