diff --git a/Feature.Manager.Api/FeatureRuns/Exceptions/FeatureRunAlreadyStoppedException.cs b/Feature.Manager.Api/FeatureRuns/Exceptions/FeatureRunAlreadyStoppedException.cs new file mode 100644 index 0000000..b6bccbe --- /dev/null +++ b/Feature.Manager.Api/FeatureRuns/Exceptions/FeatureRunAlreadyStoppedException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Feature.Manager.Api.FeatureRuns.Exceptions +{ + public class FeatureRunAlreadyStoppedException : Exception + { + + } +} diff --git a/Feature.Manager.Api/FeatureRuns/FeatureRunService.cs b/Feature.Manager.Api/FeatureRuns/FeatureRunService.cs index 674ff20..1b05910 100644 --- a/Feature.Manager.Api/FeatureRuns/FeatureRunService.cs +++ b/Feature.Manager.Api/FeatureRuns/FeatureRunService.cs @@ -34,7 +34,7 @@ public async Task CreateFeatureRun(CreateFeatureRunRequest request) try { var runs = await _featureRunRepository.GetRunsForFeatureByFeatId(request.FeatId); - if (runs.Any(x => x.EndAt == null)) + if (runs.Any(x => x.EndAt == null || x.StopResult == StopResult.AllB)) { throw new FeatureAlreadyRunningException(); } @@ -82,6 +82,11 @@ public async Task StopFeatureRun(StopFeatureRunRequest request) throw new FeatureRunNotFoundException(); } + if (result.EndAt?.ToUniversalTime() <= DateTime.UtcNow && result.StopResult != StopResult.AllB) + { + throw new FeatureRunAlreadyStoppedException(); + } + if (!Enum.TryParse(request.StopResult, out StopResult stopResult)) { throw new InvalidStopResultValueException(); @@ -89,6 +94,10 @@ public async Task StopFeatureRun(StopFeatureRunRequest request) return await _featureRunRepository.StopFeatureRun(request); } + catch (FeatureRunAlreadyStoppedException) + { + throw; + } catch (FeatureRunNotFoundException) { throw; diff --git a/Feature.Manager.Api/FeatureRuns/FeatureRunsController.cs b/Feature.Manager.Api/FeatureRuns/FeatureRunsController.cs index 0abd414..8da5e0a 100644 --- a/Feature.Manager.Api/FeatureRuns/FeatureRunsController.cs +++ b/Feature.Manager.Api/FeatureRuns/FeatureRunsController.cs @@ -119,6 +119,13 @@ public async Task StopRun(StopFeatureRunRequest request) var result = await _featureRunService.StopFeatureRun(request); return Ok(result); } + catch (FeatureRunAlreadyStoppedException) + { + return BadRequest(new ProblemDetails + { + Title = "Run Already stopped" + }); + } catch (InvalidStopResultValueException) { return BadRequest(new ProblemDetails diff --git a/Feature.Manager.UnitTest/FeatureRuns/FeatureRunCreateServiceTest.cs b/Feature.Manager.UnitTest/FeatureRuns/FeatureRunCreateServiceTest.cs index b205eb3..cae5492 100644 --- a/Feature.Manager.UnitTest/FeatureRuns/FeatureRunCreateServiceTest.cs +++ b/Feature.Manager.UnitTest/FeatureRuns/FeatureRunCreateServiceTest.cs @@ -75,6 +75,10 @@ public void Setup() { MakeFeatureRun(StopResult.AllB, "APP-5", DateTime.Now.Subtract(TimeSpan.FromHours(12))) }); + mock.Setup(x => x.GetRunsForFeatureByFeatId("APP-6")).ReturnsAsync(new List + { + MakeFeatureRun(StopResult.AllA, "APP-6", DateTime.Now.Subtract(TimeSpan.FromHours(12))) + }); mock.Setup(x => x.GetRunsForFeatureByFeatId("APP-19")).ReturnsAsync(new List()); _mock = mock; var featureRepository = new Mock(); @@ -116,19 +120,35 @@ public async Task TestCannotCreateNewRunIfARunIsAlreadyRunning() StartAt = DateTime.Now, FeatId = "APP-2" })); + Assert.ThrowsAsync(() => _featureRunService.CreateFeatureRun( + new CreateFeatureRunRequest + { + Allocation = 100, + EndAt = DateTime.Now.Add(TimeSpan.FromDays(20)), + StartAt = DateTime.Now, + FeatId = "APP-5" + })); } [Test] public async Task TestCreatesNewRunWhenNoFeaturesAreRunning() { - var result = await _featureRunService.CreateFeatureRun(new CreateFeatureRunRequest + var app3 = await _featureRunService.CreateFeatureRun(new CreateFeatureRunRequest { Allocation = 100, EndAt = DateTime.Now.Add(TimeSpan.FromDays(20)), StartAt = DateTime.Now, FeatId = "APP-3" }); - Assert.NotNull(result); + var app6 = await _featureRunService.CreateFeatureRun(new CreateFeatureRunRequest + { + Allocation = 100, + EndAt = DateTime.Now.Add(TimeSpan.FromDays(20)), + StartAt = DateTime.Now, + FeatId = "APP-6" + }); + Assert.NotNull(app3); + Assert.NotNull(app6); } [Test] diff --git a/Feature.Manager.UnitTest/FeatureRuns/FeatureRunServiceTest.cs b/Feature.Manager.UnitTest/FeatureRuns/FeatureRunServiceTest.cs index 7833665..ef75514 100644 --- a/Feature.Manager.UnitTest/FeatureRuns/FeatureRunServiceTest.cs +++ b/Feature.Manager.UnitTest/FeatureRuns/FeatureRunServiceTest.cs @@ -38,6 +38,46 @@ public void Setup() FeatId = "APP-1", RunToken = "RAND-UUID-TOKEN", }); + mock.Setup(x => x.GetById("stopped-run-all-b")).ReturnsAsync(new FeatureRun + { + Allocation = 100, + Id = "stopped-run-all-b", + EndAt = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)), + StartAt = DateTime.Now.Subtract(TimeSpan.FromDays(2)), + StopResult = StopResult.AllB, + FeatId = "APP-1", + RunToken = "RAND-UUID-TOKEN", + }); + mock.Setup(x => x.GetById("stopped-run-changed-settings")).ReturnsAsync(new FeatureRun + { + Allocation = 100, + Id = "stopped-run-changed-settings", + EndAt = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)), + StartAt = DateTime.Now.Subtract(TimeSpan.FromDays(2)), + StopResult = StopResult.ChangeSettings, + FeatId = "APP-1", + RunToken = "RAND-UUID-TOKEN", + }); + mock.Setup(x => x.GetById("stopped-run-all-a")).ReturnsAsync(new FeatureRun + { + Allocation = 100, + Id = "stopped-run-all-a", + EndAt = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)), + StopResult = StopResult.AllA, + StartAt = DateTime.Now.Subtract(TimeSpan.FromDays(2)), + FeatId = "APP-1", + RunToken = "RAND-UUID-TOKEN", + }); + mock.Setup(x => x.GetById("stopped-run-removed-from-code")).ReturnsAsync(new FeatureRun + { + Allocation = 100, + Id = "stopped-run-removed-from-code", + EndAt = DateTime.Now.Subtract(TimeSpan.FromSeconds(5)), + StopResult = StopResult.Removed, + StartAt = DateTime.Now.Subtract(TimeSpan.FromDays(2)), + FeatId = "APP-1", + RunToken = "RAND-UUID-TOKEN", + }); mock.Setup(x => x.GetById("RUN-UUID-3")).ThrowsAsync(new InvalidCastException()); mock.Setup(x => x.StopFeatureRun(It.IsAny())).ReturnsAsync( (StopFeatureRunRequest request) => @@ -199,5 +239,35 @@ public async Task TestGetRunningFeaturesHandlesException() var systemUnderTest = new FeatureRunService(mock.Object, null); Assert.ThrowsAsync(() => systemUnderTest.GetRunningFeatures()); } + + [Test] + public async Task StoppedRunsCantBeStoppedFurther() + { + Assert.ThrowsAsync(() => _featureRunService.StopFeatureRun(new StopFeatureRunRequest + { + RunId = "stopped-run-changed-settings", + StopResult = "AllB" + })); + Assert.ThrowsAsync(() => _featureRunService.StopFeatureRun(new StopFeatureRunRequest + { + RunId = "stopped-run-all-a", + StopResult = "AllB" + })); + Assert.ThrowsAsync(() => _featureRunService.StopFeatureRun(new StopFeatureRunRequest + { + RunId = "stopped-run-removed-from-code", + StopResult = "AllB" + })); + } + + [Test] + public async Task StoppedAllBRunsCanBeChanged() + { + Assert.DoesNotThrowAsync(() => _featureRunService.StopFeatureRun(new StopFeatureRunRequest + { + RunId = "stopped-run-all-b", + StopResult = "AllA" + })); + } } }