PageRenderTime 27ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/src/framework/Composable.CQRS.Tests/CQRS/EventRefactoring/Migrations/EventMigrationTestBase.cs

https://github.com/mlidbom/Composable.Monolithic
C# | 248 lines | 209 code | 36 blank | 3 comment | 3 complexity | e36f117a60a803d6ae60b94614ebe83b MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Composable.DependencyInjection;
  5. using Composable.DependencyInjection.Testing;
  6. using Composable.GenericAbstractions.Time;
  7. using Composable.Logging;
  8. using Composable.Persistence.Common.DependencyInjection;
  9. using Composable.Persistence.EventStore;
  10. using Composable.Persistence.EventStore.Refactoring.Migrations;
  11. using Composable.Refactoring.Naming;
  12. using Composable.SystemCE;
  13. using Composable.SystemCE.CollectionsCE.GenericCE;
  14. using Composable.SystemCE.LinqCE;
  15. using FluentAssertions;
  16. using Newtonsoft.Json;
  17. using NUnit.Framework;
  18. using Composable.Testing;
  19. using JetBrains.Annotations;
  20. // ReSharper disable AccessToModifiedClosure
  21. namespace Composable.Tests.CQRS.EventRefactoring.Migrations
  22. {
  23. //refactor: this test. It is too monolithic and hard to read and extend.
  24. public abstract class EventMigrationTestBase : DuplicateByPluggableComponentTest
  25. {
  26. internal void RunMigrationTest(params MigrationScenario[] scenarios)
  27. {
  28. ConsoleCE.WriteLine($"###############$$$$$$$Running {scenarios.Length} scenario(s)");
  29. IList<IEventMigration> migrations = new List<IEventMigration>();
  30. using var serviceLocator = CreateServiceLocatorForEventStoreType(() => migrations.ToArray());
  31. var timeSource = serviceLocator.Resolve<TestingTimeSource>();
  32. timeSource.FreezeAtUtcTime(DateTime.Parse("2001-02-02 01:01:01.011111"));
  33. var scenarioIndex = 1;
  34. foreach(var migrationScenario in scenarios)
  35. {
  36. timeSource.FreezeAtUtcTime(timeSource.UtcNow + 1.Hours()); //No time collision between scenarios please.
  37. migrations = migrationScenario.Migrations.ToList();
  38. RunScenarioWithEventStoreType(migrationScenario, serviceLocator, migrations, scenarioIndex++);
  39. }
  40. }
  41. static void RunScenarioWithEventStoreType(MigrationScenario scenario, IServiceLocator serviceLocator, IList<IEventMigration> migrations, int indexOfScenarioInBatch)
  42. {
  43. var startingMigrations = migrations.ToList();
  44. migrations.Clear();
  45. var timeSource = serviceLocator.Resolve<TestingTimeSource>();
  46. IReadOnlyList<IAggregateEvent> eventsInStoreAtStart;
  47. using(serviceLocator.BeginScope()) //Why is this needed? It fails without it but I do not understand why...
  48. {
  49. var eventStore = serviceLocator.Resolve<IEventStore>();
  50. eventsInStoreAtStart = eventStore.ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize();
  51. }
  52. ConsoleCE.WriteLine($"\n########Running Scenario {indexOfScenarioInBatch}");
  53. var original = TestAggregate.FromEvents(TestingTimeSource.FrozenUtcNow(), scenario.AggregateId, scenario.OriginalHistory)
  54. .History.ToList();
  55. ConsoleCE.WriteLine("Original History: ");
  56. original.ForEach(e => ConsoleCE.WriteLine($" {e}"));
  57. ConsoleCE.WriteLine();
  58. var initialAggregate = TestAggregate.FromEvents(timeSource, scenario.AggregateId, scenario.OriginalHistory);
  59. var expected = TestAggregate.FromEvents(timeSource, scenario.AggregateId, scenario.ExpectedHistory)
  60. .History.ToList();
  61. var expectedCompleteEventStoreStream = eventsInStoreAtStart.Concat(expected)
  62. .ToList();
  63. ConsoleCE.WriteLine("Expected History: ");
  64. expected.ForEach(e => ConsoleCE.WriteLine($" {e}"));
  65. ConsoleCE.WriteLine();
  66. timeSource.FreezeAtUtcTime(timeSource.UtcNow + 1.Hours()); //Bump clock to ensure that times will be be wrong unless the time from the original events are used..
  67. serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStoreUpdater>()
  68. .Save(initialAggregate));
  69. migrations.AddRange(startingMigrations);
  70. ClearCache(serviceLocator);
  71. var migratedHistory = serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStoreUpdater>()
  72. .Get<TestAggregate>(initialAggregate.Id))
  73. .History;
  74. AssertStreamsAreIdentical(expected, migratedHistory, "Loaded un-cached aggregate");
  75. var migratedCachedHistory = serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStoreUpdater>()
  76. .Get<TestAggregate>(initialAggregate.Id))
  77. .History;
  78. AssertStreamsAreIdentical(expected, migratedCachedHistory, "Loaded cached aggregate");
  79. ConsoleCE.WriteLine(" Streaming all events in store");
  80. var streamedEvents = serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStore>()
  81. .ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize()
  82. .ToList());
  83. AssertStreamsAreIdentical(expectedCompleteEventStoreStream, streamedEvents, "Streaming all events in store");
  84. //Make sure that other processes that might be using the same aggregate also keep working as we persist the migrations.
  85. using(var clonedServiceLocator = serviceLocator.Clone())
  86. {
  87. migratedHistory = clonedServiceLocator.ExecuteTransactionInIsolatedScope(() => clonedServiceLocator.Resolve<IEventStoreUpdater>()
  88. .Get<TestAggregate>(initialAggregate.Id))
  89. .History;
  90. AssertStreamsAreIdentical(expected, migratedHistory, "Loaded aggregate");
  91. ConsoleCE.WriteLine(" Persisting migrations");
  92. using(serviceLocator.BeginScope())
  93. {
  94. serviceLocator.Resolve<IEventStore>()
  95. .PersistMigrations();
  96. }
  97. migratedHistory = serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStoreUpdater>()
  98. .Get<TestAggregate>(initialAggregate.Id))
  99. .History;
  100. AssertStreamsAreIdentical(expected, migratedHistory, "Loaded aggregate");
  101. migratedHistory = clonedServiceLocator.ExecuteTransactionInIsolatedScope(() => clonedServiceLocator.Resolve<IEventStoreUpdater>()
  102. .Get<TestAggregate>(initialAggregate.Id))
  103. .History;
  104. }
  105. AssertStreamsAreIdentical(expected, migratedHistory, "Loaded aggregate");
  106. ConsoleCE.WriteLine("Streaming all events in store");
  107. streamedEvents = serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStore>()
  108. .ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize()
  109. .ToList());
  110. AssertStreamsAreIdentical(expectedCompleteEventStoreStream, streamedEvents, "Streaming all events in store");
  111. ConsoleCE.WriteLine(" Disable all migrations so that none are used when reading from the event stores");
  112. migrations.Clear();
  113. migratedHistory = serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStoreUpdater>()
  114. .Get<TestAggregate>(initialAggregate.Id))
  115. .History;
  116. AssertStreamsAreIdentical(expected, migratedHistory, "loaded aggregate");
  117. ConsoleCE.WriteLine("Streaming all events in store");
  118. streamedEvents = serviceLocator.ExecuteTransactionInIsolatedScope(() => serviceLocator.Resolve<IEventStore>()
  119. .ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize()
  120. .ToList());
  121. AssertStreamsAreIdentical(expectedCompleteEventStoreStream, streamedEvents, "Streaming all events in store");
  122. ConsoleCE.WriteLine("Cloning service locator / starting new instance of application");
  123. using var clonedServiceLocator2 = serviceLocator.Clone();
  124. migratedHistory = clonedServiceLocator2.ExecuteTransactionInIsolatedScope(() => clonedServiceLocator2.Resolve<IEventStoreUpdater>()
  125. .Get<TestAggregate>(initialAggregate.Id))
  126. .History;
  127. AssertStreamsAreIdentical(expected, migratedHistory, "Loaded aggregate");
  128. ConsoleCE.WriteLine("Streaming all events in store");
  129. streamedEvents = clonedServiceLocator2.ExecuteTransactionInIsolatedScope(() => clonedServiceLocator2.Resolve<IEventStore>()
  130. .ListAllEventsForTestingPurposesAbsolutelyNotUsableForARealEventStoreOfAnySize()
  131. .ToList());
  132. AssertStreamsAreIdentical(expectedCompleteEventStoreStream, streamedEvents, "Streaming all events in store");
  133. }
  134. protected static void ClearCache(IServiceLocator serviceLocator)
  135. {
  136. serviceLocator.ExecuteInIsolatedScope(() =>
  137. {
  138. if(serviceLocator.Resolve<IEventStore>() is EventStore)
  139. {
  140. serviceLocator.Resolve<EventCache>().Clear();
  141. }
  142. });
  143. }
  144. protected static IServiceLocator CreateServiceLocatorForEventStoreType(Func<IReadOnlyList<IEventMigration>> migrationsfactory)
  145. {
  146. var serviceLocator = DependencyInjectionContainer.CreateServiceLocatorForTesting(
  147. endpointBuilder =>
  148. endpointBuilder.Container.RegisterEventStoreForFlexibleTesting(TestWiringHelper.EventStoreConnectionStringName, migrationsfactory));
  149. serviceLocator.Resolve<ITypeMappingRegistar>()
  150. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.TestAggregate>("dbc5cd48-bc09-4d96-804d-6712493a413d")
  151. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E1>("cdb56e08-9ccb-497a-89cd-230913a51877")
  152. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E2>("808a5fed-4925-4b2c-8992-fd75521959e6")
  153. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E3>("9297ccdd-0a0b-4632-8c86-2634f75822bf")
  154. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E4>("aa67591a-2a91-4e74-9cc3-0991f72473bc")
  155. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E5>("32979722-64d1-4113-af1d-c5f7c2c6862c")
  156. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E6>("08bfb660-adc4-480f-82d5-db64fa9a0ac5")
  157. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E7>("d8a2ea4f-7dad-4658-8530-a50f092f0640")
  158. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E8>("70424c93-f24c-44c9-a1d6-fb2d6fe83e0a")
  159. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.E9>("ec965ddd-5a8a-4fef-890f-4f302069e8ba")
  160. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.Ec1>("117fc595-4756-4695-a907-43d0501bf32c")
  161. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.Ec2>("3d0e3a47-989d-4096-9389-79d6960ee6d6")
  162. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.Ec3>("76b2bbce-b5b4-4293-b707-85cbbaeb7916")
  163. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.EcAbstract>("74797038-5f9b-4660-b853-fa81ad67f193")
  164. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.Events.Ef>("19f36c9a-6f42-429a-9d43-26532e718ceb")
  165. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.IRootEvent>("a846112e-86ce-4dc5-ac7b-97bb44f8e1ce")
  166. .Map<Composable.Tests.CQRS.EventRefactoring.Migrations.RootEvent>("a3714dd8-1c20-47be-bb5a-a17ee2c5656f")
  167. .Map<Composable.Tests.CQRS.IUserEvent>("059a8d68-9b84-4e6b-85b6-fb3e0f7d9d6f")
  168. .Map<Composable.Tests.CQRS.MigratedAfterUserChangedEmailEvent>("ebda8f29-0e76-493f-b4d5-220b9605de13")
  169. .Map<Composable.Tests.CQRS.MigratedBeforeUserRegisteredEvent>("3b3477ab-014b-4dbf-921d-8569d7e934e2")
  170. .Map<Composable.Tests.CQRS.MigratedReplaceUserChangedPasswordEvent>("fa51dab5-d012-491a-b73e-5b343d9aa2d0")
  171. .Map<Composable.Tests.CQRS.UserChangedEmail>("67c06a44-56eb-4b67-b6e5-ef125653ed7c")
  172. .Map<Composable.Tests.CQRS.UserChangedPassword>("bbcad7d4-e5f6-45b1-8dd5-99d54b048e3a")
  173. .Map<Composable.Tests.CQRS.UserEvent>("507c052d-eeaf-402f-9f2b-91941118caf2")
  174. .Map<Composable.Tests.CQRS.UserRegistered>("02feaed0-b540-4402-92b2-30073db53fa1");
  175. return serviceLocator;
  176. }
  177. protected static void AssertStreamsAreIdentical(IReadOnlyList<IAggregateEvent> expected, IReadOnlyList<IAggregateEvent> migratedHistory, string descriptionOfHistory)
  178. {
  179. try
  180. {
  181. expected.ForEach(
  182. (@event, index) =>
  183. {
  184. if(@event.GetType() != migratedHistory.ElementAt(index)
  185. .GetType())
  186. {
  187. throw new AssertionException(
  188. $"Expected event at position {index} to be of type {@event.GetType()} but it was of type: {migratedHistory.ElementAt(index) .GetType()}");
  189. }
  190. });
  191. migratedHistory.Cast<AggregateEvent>()
  192. .Should().BeEquivalentTo(
  193. expected.Cast<AggregateEvent>(),
  194. config => config.RespectingRuntimeTypes()
  195. .WithStrictOrdering()
  196. .ComparingByMembers<AggregateEvent>()
  197. .Excluding(@event => @event.MessageId));
  198. }
  199. catch(Exception)
  200. {
  201. ConsoleCE.WriteLine($" Failed comparing with {descriptionOfHistory}");
  202. ConsoleCE.WriteLine(" Expected: ");
  203. expected.ForEach(e => ConsoleCE.WriteLine($" {e.ToNewtonSoftDebugString(Formatting.None)}"));
  204. ConsoleCE.WriteLine("\n Actual: ");
  205. migratedHistory.ForEach(e => ConsoleCE.WriteLine($" {e.ToNewtonSoftDebugString(Formatting.None)}"));
  206. ConsoleCE.WriteLine("\n");
  207. throw;
  208. }
  209. }
  210. protected EventMigrationTestBase([NotNull] string _) : base(_) {}
  211. }
  212. }