/src/NServiceBus.Core.Tests/Features/FeatureDependencyTests.cs

https://github.com/MikeEast/NServiceBus · C# · 360 lines · 291 code · 68 blank · 1 comment · 0 complexity · fe44bedba5246a7b82f6b78d1544b003 MD5 · raw file

  1. namespace NServiceBus.Core.Tests.Features
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using NServiceBus.Features;
  7. using NUnit.Framework;
  8. using Settings;
  9. [TestFixture]
  10. public class FeatureDependencyTests
  11. {
  12. static IEnumerable<FeatureCombinations> FeatureCombinationsForTests
  13. {
  14. get
  15. {
  16. yield return new FeatureCombinations
  17. {
  18. DependingFeature = new DependsOnOne_Feature(),
  19. AvailableFeatures = new Feature[] { new MyFeature1(), new MyFeature2(), new MyFeature3() },
  20. ShouldBeActive = false,
  21. };
  22. yield return new FeatureCombinations
  23. {
  24. DependingFeature = new DependsOnOne_Feature(),
  25. AvailableFeatures = new Feature[] { new MyFeature1 { Enabled = true }, new MyFeature2(), new MyFeature3() },
  26. ShouldBeActive = true,
  27. };
  28. yield return new FeatureCombinations
  29. {
  30. DependingFeature = new DependsOnOne_Feature(),
  31. AvailableFeatures = new Feature[] { new MyFeature1(), new MyFeature2 { Enabled = true }, new MyFeature3() },
  32. ShouldBeActive = false,
  33. };
  34. yield return new FeatureCombinations
  35. {
  36. DependingFeature = new DependsOnAtLeastOne_Feature(),
  37. AvailableFeatures = new Feature[] { new MyFeature1 { Enabled = true }, new MyFeature2(), new MyFeature3() },
  38. ShouldBeActive = true,
  39. };
  40. yield return new FeatureCombinations
  41. {
  42. DependingFeature = new DependsOnAll_Feature(),
  43. AvailableFeatures = new Feature[] { new MyFeature1 { Enabled = true }, new MyFeature2(), new MyFeature3() },
  44. ShouldBeActive = false,
  45. };
  46. }
  47. }
  48. [TestCaseSource(nameof(FeatureCombinationsForTests))]
  49. public void Should_only_activate_features_if_dependencies_are_met(FeatureCombinations setup)
  50. {
  51. var featureSettings = new FeatureActivator(new SettingsHolder());
  52. var dependingFeature = setup.DependingFeature;
  53. featureSettings.Add(dependingFeature);
  54. Array.ForEach(setup.AvailableFeatures, featureSettings.Add);
  55. featureSettings.SetupFeatures(new FakeFeatureConfigurationContext());
  56. Assert.AreEqual(setup.ShouldBeActive, dependingFeature.IsActive);
  57. }
  58. [Test]
  59. public void Should_activate_upstream_dependencies_first()
  60. {
  61. var order = new List<Feature>();
  62. var dependingFeature = new DependsOnOne_Feature
  63. {
  64. OnActivation = f => order.Add(f)
  65. };
  66. var feature = new MyFeature1
  67. {
  68. OnActivation = f => order.Add(f)
  69. };
  70. var settings = new SettingsHolder();
  71. var featureSettings = new FeatureActivator(settings);
  72. featureSettings.Add(dependingFeature);
  73. featureSettings.Add(feature);
  74. settings.EnableFeatureByDefault<MyFeature1>();
  75. featureSettings.SetupFeatures(new FakeFeatureConfigurationContext());
  76. Assert.True(dependingFeature.IsActive);
  77. Assert.IsInstanceOf<MyFeature1>(order.First(), "Upstream dependencies should be activated first");
  78. }
  79. [Test]
  80. public void Should_activate_named_dependency_first()
  81. {
  82. var order = new List<Feature>();
  83. var dependingFeature = new DependsOnOneByName_Feature
  84. {
  85. OnActivation = f => order.Add(f)
  86. };
  87. var feature = new MyFeature2
  88. {
  89. OnActivation = f => order.Add(f)
  90. };
  91. var settings = new SettingsHolder();
  92. var featureSettings = new FeatureActivator(settings);
  93. featureSettings.Add(dependingFeature);
  94. featureSettings.Add(feature);
  95. settings.EnableFeatureByDefault<MyFeature2>();
  96. featureSettings.SetupFeatures(new FakeFeatureConfigurationContext());
  97. Assert.True(dependingFeature.IsActive);
  98. Assert.IsInstanceOf<MyFeature2>(order.First(), "Upstream dependencies should be activated first");
  99. }
  100. [Test]
  101. public void Should_not_activate_feature_when_named_dependency_disabled()
  102. {
  103. var order = new List<Feature>();
  104. var dependingFeature = new DependsOnOneByName_Feature
  105. {
  106. OnActivation = f => order.Add(f)
  107. };
  108. var feature = new MyFeature2
  109. {
  110. OnActivation = f => order.Add(f)
  111. };
  112. var settings = new SettingsHolder();
  113. var featureSettings = new FeatureActivator(settings);
  114. featureSettings.Add(dependingFeature);
  115. featureSettings.Add(feature);
  116. featureSettings.SetupFeatures(new FakeFeatureConfigurationContext());
  117. Assert.False(dependingFeature.IsActive);
  118. Assert.IsEmpty(order);
  119. }
  120. [Test]
  121. public void Should_activate_all_upstream_dependencies_first()
  122. {
  123. var order = new List<Feature>();
  124. var dependingFeature = new DependsOnAtLeastOne_Feature
  125. {
  126. OnActivation = f => order.Add(f)
  127. };
  128. var feature = new MyFeature1
  129. {
  130. OnActivation = f => order.Add(f)
  131. };
  132. var feature2 = new MyFeature2
  133. {
  134. OnActivation = f => order.Add(f)
  135. };
  136. var feature3 = new MyFeature3
  137. {
  138. OnActivation = f => order.Add(f)
  139. };
  140. var settings = new SettingsHolder();
  141. var featureSettings = new FeatureActivator(settings);
  142. featureSettings.Add(dependingFeature);
  143. featureSettings.Add(feature);
  144. featureSettings.Add(feature2);
  145. featureSettings.Add(feature3);
  146. settings.EnableFeatureByDefault<MyFeature1>();
  147. settings.EnableFeatureByDefault<MyFeature2>();
  148. settings.EnableFeatureByDefault<MyFeature3>();
  149. featureSettings.SetupFeatures(new FakeFeatureConfigurationContext());
  150. Assert.True(dependingFeature.IsActive);
  151. Assert.IsInstanceOf<MyFeature1>(order[0], "Upstream dependencies should be activated first");
  152. Assert.IsInstanceOf<MyFeature2>(order[1], "Upstream dependencies should be activated first");
  153. Assert.IsInstanceOf<MyFeature3>(order[2], "Upstream dependencies should be activated first");
  154. }
  155. [Test]
  156. public void Should_activate_all_upstream_dependencies_when_chain_deep()
  157. {
  158. var order = new List<Feature>();
  159. var level1 = new Level1
  160. {
  161. OnActivation = f => order.Add(f)
  162. };
  163. var level2 = new Level2
  164. {
  165. OnActivation = f => order.Add(f)
  166. };
  167. var level3 = new Level3
  168. {
  169. OnActivation = f => order.Add(f)
  170. };
  171. var settings = new SettingsHolder();
  172. var featureSettings = new FeatureActivator(settings);
  173. //the orders matter here to expose a bug
  174. featureSettings.Add(level3);
  175. featureSettings.Add(level2);
  176. featureSettings.Add(level1);
  177. featureSettings.SetupFeatures(new FakeFeatureConfigurationContext());
  178. Assert.True(level1.IsActive, "Level1 wasn't activated");
  179. Assert.True(level2.IsActive, "Level2 wasn't activated");
  180. Assert.True(level3.IsActive, "Level3 wasn't activated");
  181. Assert.IsInstanceOf<Level1>(order[0], "Upstream dependencies should be activated first");
  182. Assert.IsInstanceOf<Level2>(order[1], "Upstream dependencies should be activated first");
  183. Assert.IsInstanceOf<Level3>(order[2], "Upstream dependencies should be activated first");
  184. }
  185. [Test]
  186. public void Should_throw_exception_when_dependency_cycle_is_found()
  187. {
  188. var order = new List<Feature>();
  189. var level1 = new CycleLevel1
  190. {
  191. OnActivation = f => order.Add(f)
  192. };
  193. var level2 = new CycleLevel2
  194. {
  195. OnActivation = f => order.Add(f)
  196. };
  197. var settings = new SettingsHolder();
  198. var featureSettings = new FeatureActivator(settings);
  199. featureSettings.Add(level1);
  200. featureSettings.Add(level2);
  201. Assert.Throws<ArgumentException>(() => featureSettings.SetupFeatures(new FakeFeatureConfigurationContext()));
  202. }
  203. public class Level1 : TestFeature
  204. {
  205. public Level1()
  206. {
  207. EnableByDefault();
  208. }
  209. }
  210. public class Level2 : TestFeature
  211. {
  212. public Level2()
  213. {
  214. EnableByDefault();
  215. DependsOn<Level1>();
  216. }
  217. }
  218. public class Level3 : TestFeature
  219. {
  220. public Level3()
  221. {
  222. EnableByDefault();
  223. DependsOn<Level2>();
  224. }
  225. }
  226. public class CycleLevel1 : TestFeature
  227. {
  228. public CycleLevel1()
  229. {
  230. EnableByDefault();
  231. DependsOn<CycleLevel2>();
  232. }
  233. }
  234. public class CycleLevel2 : TestFeature
  235. {
  236. public CycleLevel2()
  237. {
  238. EnableByDefault();
  239. DependsOn<CycleLevel1>();
  240. }
  241. }
  242. public class MyFeature1 : TestFeature
  243. {
  244. }
  245. public class MyFeature2 : TestFeature
  246. {
  247. }
  248. public class MyFeature3 : TestFeature
  249. {
  250. }
  251. public class DependsOnOne_Feature : TestFeature
  252. {
  253. public DependsOnOne_Feature()
  254. {
  255. EnableByDefault();
  256. DependsOn<MyFeature1>();
  257. }
  258. }
  259. public class DependsOnOneByName_Feature : TestFeature
  260. {
  261. public DependsOnOneByName_Feature()
  262. {
  263. EnableByDefault();
  264. DependsOn("NServiceBus.Core.Tests.Features.FeatureDependencyTests+MyFeature2");
  265. }
  266. }
  267. public class DependsOnAll_Feature : TestFeature
  268. {
  269. public DependsOnAll_Feature()
  270. {
  271. EnableByDefault();
  272. DependsOn<MyFeature1>();
  273. DependsOn<MyFeature2>();
  274. DependsOn<MyFeature3>();
  275. }
  276. }
  277. public class DependsOnAtLeastOne_Feature : TestFeature
  278. {
  279. public DependsOnAtLeastOne_Feature()
  280. {
  281. EnableByDefault();
  282. DependsOnAtLeastOne(typeof(MyFeature1), typeof(MyFeature2), typeof(MyFeature3));
  283. }
  284. }
  285. public class FeatureCombinations
  286. {
  287. public Feature DependingFeature { get; set; }
  288. public Feature[] AvailableFeatures { get; set; }
  289. public bool ShouldBeActive { get; set; }
  290. }
  291. }
  292. }