PageRenderTime 54ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/NEventStore.Persistence.AcceptanceTests/PersistenceTests.cs

http://github.com/joliver/EventStore
C# | 1330 lines | 1094 code | 181 blank | 55 comment | 16 complexity | 85b0d54db3b97ace8c9d276a929a1998 MD5 | raw file
  1. #pragma warning disable 169 // ReSharper disable InconsistentNaming
  2. #pragma warning disable IDE1006 // Naming Styles
  3. #pragma warning disable S101 // Types should be named in PascalCase
  4. namespace NEventStore.Persistence.AcceptanceTests
  5. {
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using NEventStore.Persistence.AcceptanceTests.BDD;
  10. using FluentAssertions;
  11. #if MSTEST
  12. using Microsoft.VisualStudio.TestTools.UnitTesting;
  13. #endif
  14. #if NUNIT
  15. using NUnit.Framework;
  16. using System.Threading.Tasks;
  17. using System.Threading;
  18. using System.Globalization;
  19. #endif
  20. #if XUNIT
  21. using Xunit;
  22. using Xunit.Should;
  23. #endif
  24. #if MSTEST
  25. [TestClass]
  26. #endif
  27. public class when_a_commit_header_has_a_name_that_contains_a_period : PersistenceEngineConcern
  28. {
  29. private ICommit _persisted;
  30. private string _streamId;
  31. protected override void Context()
  32. {
  33. _streamId = Guid.NewGuid().ToString();
  34. var attempt = new CommitAttempt(_streamId,
  35. 2,
  36. Guid.NewGuid(),
  37. 1,
  38. DateTime.Now,
  39. new Dictionary<string, object> { { "key.1", "value" } },
  40. new List<EventMessage> { new EventMessage { Body = new ExtensionMethods.SomeDomainEvent { SomeProperty = "Test" } } });
  41. Persistence.Commit(attempt);
  42. }
  43. protected override void Because()
  44. {
  45. _persisted = Persistence.GetFrom(_streamId, 0, int.MaxValue).First();
  46. }
  47. [Fact]
  48. public void should_correctly_deserialize_headers()
  49. {
  50. _persisted.Headers.Keys.Should().Contain("key.1");
  51. }
  52. }
  53. #if MSTEST
  54. [TestClass]
  55. #endif
  56. public class when_a_commit_is_successfully_persisted : PersistenceEngineConcern
  57. {
  58. private CommitAttempt _attempt;
  59. private DateTime _now;
  60. private ICommit _persisted;
  61. private string _streamId;
  62. protected override void Context()
  63. {
  64. _now = SystemTime.UtcNow.AddYears(1);
  65. _streamId = Guid.NewGuid().ToString();
  66. _attempt = _streamId.BuildAttempt(_now);
  67. Persistence.Commit(_attempt);
  68. }
  69. protected override void Because()
  70. {
  71. _persisted = Persistence.GetFrom(_streamId, 0, int.MaxValue).First();
  72. }
  73. [Fact]
  74. public void should_correctly_persist_the_stream_identifier()
  75. {
  76. _persisted.StreamId.Should().Be(_attempt.StreamId);
  77. }
  78. [Fact]
  79. public void should_correctly_persist_the_stream_stream_revision()
  80. {
  81. _persisted.StreamRevision.Should().Be(_attempt.StreamRevision);
  82. }
  83. [Fact]
  84. public void should_correctly_persist_the_commit_identifier()
  85. {
  86. _persisted.CommitId.Should().Be(_attempt.CommitId);
  87. }
  88. [Fact]
  89. public void should_correctly_persist_the_commit_sequence()
  90. {
  91. _persisted.CommitSequence.Should().Be(_attempt.CommitSequence);
  92. }
  93. // persistence engines have varying levels of precision with respect to time.
  94. [Fact]
  95. public void should_correctly_persist_the_commit_stamp()
  96. {
  97. var difference = _persisted.CommitStamp.Subtract(_now);
  98. difference.Days.Should().Be(0);
  99. difference.Hours.Should().Be(0);
  100. difference.Minutes.Should().Be(0);
  101. difference.Should().BeLessOrEqualTo(TimeSpan.FromSeconds(1));
  102. }
  103. [Fact]
  104. public void should_correctly_persist_the_headers()
  105. {
  106. _persisted.Headers.Count.Should().Be(_attempt.Headers.Count);
  107. }
  108. [Fact]
  109. public void should_correctly_persist_the_events()
  110. {
  111. _persisted.Events.Count.Should().Be(_attempt.Events.Count);
  112. }
  113. [Fact]
  114. public void should_cause_the_stream_to_be_found_in_the_list_of_streams_to_snapshot()
  115. {
  116. #pragma warning disable RCS1202 // Avoid NullReferenceException.
  117. Persistence.GetStreamsToSnapshot(1).FirstOrDefault(x => x.StreamId == _streamId).Should().NotBeNull();
  118. #pragma warning restore RCS1202 // Avoid NullReferenceException.
  119. }
  120. }
  121. #if MSTEST
  122. [TestClass]
  123. #endif
  124. public class when_reading_from_a_given_revision : PersistenceEngineConcern
  125. {
  126. private const int LoadFromCommitContainingRevision = 3;
  127. private const int UpToCommitWithContainingRevision = 5;
  128. private ICommit[] _committed;
  129. private ICommit _oldest, _oldest2, _oldest3;
  130. private string _streamId;
  131. protected override void Context()
  132. {
  133. _oldest = Persistence.CommitSingle(); // 2 events, revision 1-2
  134. _oldest2 = Persistence.CommitNext(_oldest); // 2 events, revision 3-4
  135. _oldest3 = Persistence.CommitNext(_oldest2); // 2 events, revision 5-6
  136. Persistence.CommitNext(_oldest3); // 2 events, revision 7-8
  137. _streamId = _oldest.StreamId;
  138. }
  139. protected override void Because()
  140. {
  141. _committed = Persistence.GetFrom(_streamId, LoadFromCommitContainingRevision, UpToCommitWithContainingRevision).ToArray();
  142. }
  143. [Fact]
  144. public void should_start_from_the_commit_which_contains_the_min_stream_revision_specified()
  145. {
  146. _committed[0].CommitId.Should().Be(_oldest2.CommitId); // contains revision 3
  147. }
  148. [Fact]
  149. public void should_read_up_to_the_commit_which_contains_the_max_stream_revision_specified()
  150. {
  151. _committed.Last().CommitId.Should().Be(_oldest3.CommitId); // contains revision 5
  152. }
  153. }
  154. #if MSTEST
  155. [TestClass]
  156. #endif
  157. public class when_reading_from_a_given_revision_to_commit_revision : PersistenceEngineConcern
  158. {
  159. private const int LoadFromCommitContainingRevision = 3;
  160. private const int UpToCommitWithContainingRevision = 6;
  161. private ICommit[] _committed;
  162. private ICommit _oldest, _oldest2, _oldest3;
  163. private string _streamId;
  164. protected override void Context()
  165. {
  166. _oldest = Persistence.CommitSingle(); // 2 events, revision 1-2
  167. _oldest2 = Persistence.CommitNext(_oldest); // 2 events, revision 3-4
  168. _oldest3 = Persistence.CommitNext(_oldest2); // 2 events, revision 5-6
  169. Persistence.CommitNext(_oldest3); // 2 events, revision 7-8
  170. _streamId = _oldest.StreamId;
  171. }
  172. protected override void Because()
  173. {
  174. _committed = Persistence.GetFrom(_streamId, LoadFromCommitContainingRevision, UpToCommitWithContainingRevision).ToArray();
  175. }
  176. [Fact]
  177. public void should_start_from_the_commit_which_contains_the_min_stream_revision_specified()
  178. {
  179. _committed[0].CommitId.Should().Be(_oldest2.CommitId); // contains revision 3
  180. }
  181. [Fact]
  182. public void should_read_up_to_the_commit_which_contains_the_max_stream_revision_specified()
  183. {
  184. _committed.Last().CommitId.Should().Be(_oldest3.CommitId); // contains revision 6
  185. }
  186. }
  187. #if MSTEST
  188. [TestClass]
  189. #endif
  190. public class when_committing_a_stream_with_the_same_revision : PersistenceEngineConcern
  191. {
  192. private CommitAttempt _attemptWithSameRevision;
  193. private Exception _thrown;
  194. protected override void Context()
  195. {
  196. ICommit commit = Persistence.CommitSingle();
  197. _attemptWithSameRevision = commit.StreamId.BuildAttempt();
  198. }
  199. protected override void Because()
  200. {
  201. _thrown = Catch.Exception(() => Persistence.Commit(_attemptWithSameRevision));
  202. }
  203. [Fact]
  204. public void should_throw_a_ConcurrencyException()
  205. {
  206. _thrown.Should().BeOfType<ConcurrencyException>();
  207. }
  208. }
  209. // This test ensure the uniqueness of BucketId+StreamId+CommitSequence
  210. // to avoid concurrency issues
  211. #if MSTEST
  212. [TestClass]
  213. #endif
  214. public class when_committing_a_stream_with_the_same_sequence : PersistenceEngineConcern
  215. {
  216. private CommitAttempt _attempt1, _attempt2;
  217. private Exception _thrown;
  218. protected override void Context()
  219. {
  220. string streamId = Guid.NewGuid().ToString();
  221. _attempt1 = streamId.BuildAttempt();
  222. _attempt2 = new CommitAttempt(
  223. _attempt1.BucketId, // <--- Same bucket
  224. _attempt1.StreamId, // <--- Same stream it
  225. _attempt1.StreamRevision + 10,
  226. Guid.NewGuid(),
  227. _attempt1.CommitSequence, // <--- Same commit seq
  228. DateTime.UtcNow,
  229. _attempt1.Headers,
  230. new[]
  231. {
  232. new EventMessage(){ Body = new ExtensionMethods.SomeDomainEvent {SomeProperty = "Test 3"}}
  233. }
  234. );
  235. Persistence.Commit(_attempt1);
  236. }
  237. protected override void Because()
  238. {
  239. _thrown = Catch.Exception(() => Persistence.Commit(_attempt2));
  240. }
  241. [Fact]
  242. public void should_throw_a_ConcurrencyException()
  243. {
  244. _thrown.Should().BeOfType<ConcurrencyException>();
  245. }
  246. }
  247. //TODO:This test looks exactly like the one above. What are we trying to prove?
  248. #if MSTEST
  249. [TestClass]
  250. #endif
  251. public class when_attempting_to_overwrite_a_committed_sequence : PersistenceEngineConcern
  252. {
  253. private CommitAttempt _failedAttempt;
  254. private Exception _thrown;
  255. protected override void Context()
  256. {
  257. string streamId = Guid.NewGuid().ToString();
  258. CommitAttempt successfulAttempt = streamId.BuildAttempt();
  259. Persistence.Commit(successfulAttempt);
  260. _failedAttempt = streamId.BuildAttempt();
  261. }
  262. protected override void Because()
  263. {
  264. _thrown = Catch.Exception(() => Persistence.Commit(_failedAttempt));
  265. }
  266. [Fact]
  267. public void should_throw_a_ConcurrencyException()
  268. {
  269. _thrown.Should().BeOfType<ConcurrencyException>();
  270. }
  271. }
  272. #if MSTEST
  273. [TestClass]
  274. #endif
  275. public class when_attempting_to_persist_a_commit_twice : PersistenceEngineConcern
  276. {
  277. private CommitAttempt _attemptTwice;
  278. private Exception _thrown;
  279. protected override void Context()
  280. {
  281. var commit = Persistence.CommitSingle();
  282. _attemptTwice = new CommitAttempt(
  283. commit.BucketId,
  284. commit.StreamId,
  285. commit.StreamRevision,
  286. commit.CommitId,
  287. commit.CommitSequence,
  288. commit.CommitStamp,
  289. commit.Headers,
  290. commit.Events);
  291. }
  292. protected override void Because()
  293. {
  294. _thrown = Catch.Exception(() => Persistence.Commit(_attemptTwice));
  295. }
  296. [Fact]
  297. public void should_throw_a_DuplicateCommitException()
  298. {
  299. _thrown.Should().BeOfType<DuplicateCommitException>();
  300. }
  301. }
  302. #if MSTEST
  303. [TestClass]
  304. #endif
  305. public class when_attempting_to_persist_a_commitId_twice_on_same_stream : PersistenceEngineConcern
  306. {
  307. private CommitAttempt _attemptTwice;
  308. private Exception _thrown;
  309. protected override void Context()
  310. {
  311. var commit = Persistence.CommitSingle();
  312. _attemptTwice = new CommitAttempt(
  313. commit.BucketId,
  314. commit.StreamId,
  315. commit.StreamRevision + 1,
  316. commit.CommitId,
  317. commit.CommitSequence + 1,
  318. commit.CommitStamp,
  319. commit.Headers,
  320. commit.Events
  321. );
  322. }
  323. protected override void Because()
  324. {
  325. _thrown = Catch.Exception(() => Persistence.Commit(_attemptTwice));
  326. }
  327. [Fact]
  328. public void should_throw_a_DuplicateCommitException()
  329. {
  330. _thrown.Should().BeOfType<DuplicateCommitException>();
  331. }
  332. }
  333. #if MSTEST
  334. [TestClass]
  335. #endif
  336. public class when_committing_more_events_than_the_configured_page_size : PersistenceEngineConcern
  337. {
  338. private CommitAttempt[] _committed;
  339. private ICommit[] _loaded;
  340. private string _streamId;
  341. protected override void Context()
  342. {
  343. _streamId = Guid.NewGuid().ToString();
  344. _committed = Persistence.CommitMany(ConfiguredPageSizeForTesting + 2, _streamId).ToArray();
  345. }
  346. protected override void Because()
  347. {
  348. _loaded = Persistence.GetFrom(_streamId, 0, int.MaxValue).ToArray();
  349. }
  350. [Fact]
  351. public void should_load_the_same_number_of_commits_which_have_been_persisted()
  352. {
  353. _loaded.Length.Should().Be(_committed.Length);
  354. }
  355. [Fact]
  356. public void should_load_the_same_commits_which_have_been_persisted()
  357. {
  358. _committed
  359. .All(commit => _loaded.SingleOrDefault(loaded => loaded.CommitId == commit.CommitId) != null)
  360. .Should().BeTrue();
  361. }
  362. }
  363. #if MSTEST
  364. [TestClass]
  365. #endif
  366. public class when_saving_a_snapshot : PersistenceEngineConcern
  367. {
  368. private bool _added;
  369. private Snapshot _snapshot;
  370. private string _streamId;
  371. protected override void Context()
  372. {
  373. _streamId = Guid.NewGuid().ToString();
  374. _snapshot = new Snapshot(_streamId, 1, "Snapshot");
  375. Persistence.CommitSingle(_streamId);
  376. }
  377. protected override void Because()
  378. {
  379. _added = Persistence.AddSnapshot(_snapshot);
  380. }
  381. [Fact]
  382. public void should_indicate_the_snapshot_was_added()
  383. {
  384. _added.Should().BeTrue();
  385. }
  386. [Fact]
  387. public void should_be_able_to_retrieve_the_snapshot()
  388. {
  389. Persistence.GetSnapshot(_streamId, _snapshot.StreamRevision).Should().NotBeNull();
  390. }
  391. }
  392. #if MSTEST
  393. [TestClass]
  394. #endif
  395. public class when_retrieving_a_snapshot : PersistenceEngineConcern
  396. {
  397. private ISnapshot _correct;
  398. private ISnapshot _snapshot;
  399. private string _streamId;
  400. private ISnapshot _tooFarForward;
  401. protected override void Context()
  402. {
  403. _streamId = Guid.NewGuid().ToString();
  404. ICommit commit1 = Persistence.CommitSingle(_streamId); // rev 1-2
  405. ICommit commit2 = Persistence.CommitNext(commit1); // rev 3-4
  406. Persistence.CommitNext(commit2); // rev 5-6
  407. Persistence.AddSnapshot(new Snapshot(_streamId, 1, string.Empty)); //Too far back
  408. _correct = new Snapshot(_streamId, 3, "Snapshot");
  409. Persistence.AddSnapshot(_correct);
  410. _tooFarForward = new Snapshot(_streamId, 5, string.Empty);
  411. Persistence.AddSnapshot(_tooFarForward);
  412. }
  413. protected override void Because()
  414. {
  415. _snapshot = Persistence.GetSnapshot(_streamId, _tooFarForward.StreamRevision - 1);
  416. }
  417. [Fact]
  418. public void should_load_the_most_recent_prior_snapshot()
  419. {
  420. _snapshot.StreamRevision.Should().Be(_correct.StreamRevision);
  421. }
  422. [Fact]
  423. public void should_have_the_correct_snapshot_payload()
  424. {
  425. _snapshot.Payload.Should().Be(_correct.Payload);
  426. }
  427. [Fact]
  428. public void should_have_the_correct_stream_id()
  429. {
  430. _snapshot.StreamId.Should().Be(_correct.StreamId);
  431. }
  432. }
  433. #if MSTEST
  434. [TestClass]
  435. #endif
  436. public class when_a_snapshot_has_been_added_to_the_most_recent_commit_of_a_stream : PersistenceEngineConcern
  437. {
  438. private const string SnapshotData = "snapshot";
  439. private ICommit _newest;
  440. private ICommit _oldest, _oldest2;
  441. private string _streamId;
  442. protected override void Context()
  443. {
  444. _streamId = Guid.NewGuid().ToString();
  445. _oldest = Persistence.CommitSingle(_streamId);
  446. _oldest2 = Persistence.CommitNext(_oldest);
  447. _newest = Persistence.CommitNext(_oldest2);
  448. }
  449. protected override void Because()
  450. {
  451. Persistence.AddSnapshot(new Snapshot(_streamId, _newest.StreamRevision, SnapshotData));
  452. }
  453. [Fact]
  454. public void should_no_longer_find_the_stream_in_the_set_of_streams_to_be_snapshot()
  455. {
  456. Persistence.GetStreamsToSnapshot(1).Any(x => x.StreamId == _streamId).Should().BeFalse();
  457. }
  458. }
  459. #if MSTEST
  460. [TestClass]
  461. #endif
  462. public class when_adding_a_commit_after_a_snapshot : PersistenceEngineConcern
  463. {
  464. private const int WithinThreshold = 2;
  465. private const int OverThreshold = 3;
  466. private const string SnapshotData = "snapshot";
  467. private ICommit _oldest, _oldest2;
  468. private string _streamId;
  469. protected override void Context()
  470. {
  471. _streamId = Guid.NewGuid().ToString();
  472. _oldest = Persistence.CommitSingle(_streamId);
  473. _oldest2 = Persistence.CommitNext(_oldest);
  474. Persistence.AddSnapshot(new Snapshot(_streamId, _oldest2.StreamRevision, SnapshotData));
  475. }
  476. protected override void Because()
  477. {
  478. Persistence.Commit(_oldest2.BuildNextAttempt());
  479. }
  480. // Because Raven and Mongo update the stream head asynchronously, occasionally will fail this test
  481. [Fact]
  482. public void should_find_the_stream_in_the_set_of_streams_to_be_snapshot_when_within_the_threshold()
  483. {
  484. #pragma warning disable RCS1202 // Avoid NullReferenceException.
  485. Persistence.GetStreamsToSnapshot(WithinThreshold).FirstOrDefault(x => x.StreamId == _streamId).Should().NotBeNull();
  486. #pragma warning restore RCS1202 // Avoid NullReferenceException.
  487. }
  488. [Fact]
  489. public void should_not_find_the_stream_in_the_set_of_streams_to_be_snapshot_when_over_the_threshold()
  490. {
  491. Persistence.GetStreamsToSnapshot(OverThreshold).Any(x => x.StreamId == _streamId).Should().BeFalse();
  492. }
  493. }
  494. #if MSTEST
  495. [TestClass]
  496. #endif
  497. public class when_reading_all_commits_from_a_particular_point_in_time : PersistenceEngineConcern
  498. {
  499. private ICommit[] _committed;
  500. private CommitAttempt _first;
  501. private DateTime _now;
  502. private ICommit _second;
  503. private string _streamId;
  504. private ICommit _third;
  505. protected override void Context()
  506. {
  507. _streamId = Guid.NewGuid().ToString();
  508. _now = SystemTime.UtcNow.AddYears(1);
  509. _first = _streamId.BuildAttempt(_now.AddSeconds(1));
  510. Persistence.Commit(_first);
  511. _second = Persistence.CommitNext(_first);
  512. _third = Persistence.CommitNext(_second);
  513. Persistence.CommitNext(_third);
  514. }
  515. protected override void Because()
  516. {
  517. _committed = Persistence.GetFrom(Bucket.Default, _now).ToArray();
  518. }
  519. [Fact]
  520. public void should_return_all_commits_on_or_after_the_point_in_time_specified()
  521. {
  522. _committed.Length.Should().Be(4);
  523. }
  524. }
  525. #if MSTEST
  526. [TestClass]
  527. #endif
  528. public class when_paging_over_all_commits_from_a_particular_point_in_time : PersistenceEngineConcern
  529. {
  530. private CommitAttempt[] _committed;
  531. private ICommit[] _loaded;
  532. private DateTime _start;
  533. protected override void Context()
  534. {
  535. _start = SystemTime.UtcNow;
  536. // Due to loss in precision in various storage engines, we're rounding down to the
  537. // nearest second to ensure include all commits from the 'start'.
  538. _start = _start.AddSeconds(-1);
  539. _committed = Persistence.CommitMany(ConfiguredPageSizeForTesting + 2).ToArray();
  540. }
  541. protected override void Because()
  542. {
  543. _loaded = Persistence.GetFrom(Bucket.Default, _start).ToArray();
  544. }
  545. [Fact]
  546. public void should_load_the_same_number_of_commits_which_have_been_persisted()
  547. {
  548. _loaded.Length.Should().Be(_committed.Length);
  549. }
  550. [Fact]
  551. public void should_load_the_same_commits_which_have_been_persisted()
  552. {
  553. _committed
  554. .All(commit => _loaded.SingleOrDefault(loaded => loaded.CommitId == commit.CommitId) != null)
  555. .Should().BeTrue();
  556. }
  557. }
  558. #if MSTEST
  559. [TestClass]
  560. #endif
  561. public class when_paging_over_all_commits_from_a_particular_checkpoint : PersistenceEngineConcern
  562. {
  563. private List<Guid> _committed;
  564. private ICollection<Guid> _loaded;
  565. private const int checkPoint = 2;
  566. protected override void Context()
  567. {
  568. _committed = Persistence.CommitMany(ConfiguredPageSizeForTesting + 1).Select(c => c.CommitId).ToList();
  569. }
  570. protected override void Because()
  571. {
  572. _loaded = Persistence.GetFrom(checkPoint).Select(c => c.CommitId).ToList();
  573. }
  574. [Fact]
  575. public void should_load_the_same_number_of_commits_which_have_been_persisted_starting_from_the_checkpoint()
  576. {
  577. _loaded.Count.Should().Be(_committed.Count - checkPoint);
  578. }
  579. [Fact]
  580. public void should_load_only_the_commits_starting_from_the_checkpoint()
  581. {
  582. _committed.Skip(checkPoint).All(x => _loaded.Contains(x)).Should().BeTrue(); // all commits should be found in loaded collection
  583. }
  584. }
  585. #if MSTEST
  586. [TestClass]
  587. #endif
  588. public class when_paging_over_all_commits_of_a_bucket_from_a_particular_checkpoint : PersistenceEngineConcern
  589. {
  590. private List<Guid> _committedOnBucket1;
  591. private List<Guid> _committedOnBucket2;
  592. private ICollection<Guid> _loaded;
  593. private const int checkPoint = 2;
  594. protected override void Context()
  595. {
  596. _committedOnBucket1 = Persistence.CommitMany(ConfiguredPageSizeForTesting + 1, null, "b1").Select(c => c.CommitId).ToList();
  597. _committedOnBucket2 = Persistence.CommitMany(ConfiguredPageSizeForTesting + 1, null, "b2").Select(c => c.CommitId).ToList();
  598. _committedOnBucket1.AddRange(Persistence.CommitMany(4, null, "b1").Select(c => c.CommitId));
  599. }
  600. protected override void Because()
  601. {
  602. _loaded = Persistence.GetFrom("b1", checkPoint).Select(c => c.CommitId).ToList();
  603. }
  604. [Fact]
  605. public void should_load_the_same_number_of_commits_which_have_been_persisted_starting_from_the_checkpoint()
  606. {
  607. _loaded.Count.Should().Be(_committedOnBucket1.Count - checkPoint);
  608. }
  609. [Fact]
  610. public void should_load_only_the_commits_on_bucket1_starting_from_the_checkpoint()
  611. {
  612. _committedOnBucket1.Skip(checkPoint).All(x => _loaded.Contains(x)).Should().BeTrue(); // all commits should be found in loaded collection
  613. }
  614. [Fact]
  615. public void should_not_load_the_commits_from_bucket2()
  616. {
  617. _committedOnBucket2.All(x => !_loaded.Contains(x)).Should().BeTrue();
  618. }
  619. }
  620. #if MSTEST
  621. [TestClass]
  622. #endif
  623. public class when_paging_over_all_commits_from_a_particular_checkpoint_to_a_checkpoint : PersistenceEngineConcern
  624. {
  625. private readonly List<Guid> _committed = new List<Guid>();
  626. private ICollection<Guid> _loaded;
  627. private const int startCheckpoint = 2;
  628. private int endCheckpoint;
  629. protected override void Context()
  630. {
  631. var committedOnBucket1 = Persistence.CommitMany(ConfiguredPageSizeForTesting + 1, null, Bucket.Default).Select(c => c.CommitId).ToList();
  632. var committedOnBucket2 = Persistence.CommitMany(ConfiguredPageSizeForTesting + 1, null, "Bucket1").Select(c => c.CommitId).ToList();
  633. _committed.AddRange(committedOnBucket1);
  634. _committed.AddRange(committedOnBucket2);
  635. endCheckpoint = (2 * (ConfiguredPageSizeForTesting + 1)) - 1;
  636. }
  637. protected override void Because()
  638. {
  639. _loaded = Persistence.GetFromTo(startCheckpoint, endCheckpoint).Select(c => c.CommitId).ToList();
  640. }
  641. [Fact]
  642. public void should_load_the_same_number_of_commits_which_have_been_persisted_starting_from_the_checkpoint_to_the_checkpoint()
  643. {
  644. _loaded.Count.Should().Be(endCheckpoint - startCheckpoint);
  645. }
  646. [Fact]
  647. public void should_load_only_the_commits_starting_from_the_checkpoint_to_the_checkpoint()
  648. {
  649. _committed
  650. .Skip(startCheckpoint)
  651. .Take(_committed.Count - startCheckpoint - 1)
  652. //.Take(endCheckpoint - startCheckpoint)
  653. .All(x => _loaded.Contains(x)).Should().BeTrue(); // all commits should be found in loaded collection
  654. }
  655. }
  656. #if MSTEST
  657. [TestClass]
  658. #endif
  659. public class when_paging_over_all_commits_of_a_bucket_from_a_particular_checkpoint_to_a_checkpoint : PersistenceEngineConcern
  660. {
  661. private List<Guid> _committedOnBucket1;
  662. private List<Guid> _committedOnBucket2;
  663. private ICollection<Guid> _loaded;
  664. private const int startCheckpoint = 2;
  665. private int endCheckpoint;
  666. protected override void Context()
  667. {
  668. _committedOnBucket1 = Persistence.CommitMany(ConfiguredPageSizeForTesting + 1, null, "b1").Select(c => c.CommitId).ToList();
  669. _committedOnBucket2 = Persistence.CommitMany(ConfiguredPageSizeForTesting + 1, null, "b2").Select(c => c.CommitId).ToList();
  670. _committedOnBucket1.AddRange(Persistence.CommitMany(4, null, "b1").Select(c => c.CommitId));
  671. endCheckpoint = ((2 * (ConfiguredPageSizeForTesting + 1)) + 4) - 1;
  672. }
  673. protected override void Because()
  674. {
  675. _loaded = Persistence.GetFromTo("b1", startCheckpoint, endCheckpoint).Select(c => c.CommitId).ToList();
  676. }
  677. [Fact]
  678. public void should_load_the_same_number_of_commits_which_have_been_persisted_starting_from_the_checkpoint_to_the_checkpoint()
  679. {
  680. _loaded.Count.Should().Be(_committedOnBucket1.Count - startCheckpoint - 1);
  681. }
  682. [Fact]
  683. public void should_load_only_the_commits_on_bucket1_starting_from_the_checkpoint_to_the_checkpoint()
  684. {
  685. _committedOnBucket1
  686. .Skip(startCheckpoint)
  687. .Take(_committedOnBucket1.Count - startCheckpoint - 1)
  688. .All(x => _loaded.Contains(x)).Should().BeTrue(); // all commits should be found in loaded collection
  689. }
  690. [Fact]
  691. public void should_not_load_the_commits_from_bucket2()
  692. {
  693. _committedOnBucket2.All(x => !_loaded.Contains(x)).Should().BeTrue();
  694. }
  695. }
  696. #if MSTEST
  697. [TestClass]
  698. #endif
  699. public class when_reading_all_commits_from_the_year_1_AD : PersistenceEngineConcern
  700. {
  701. private Exception _thrown;
  702. protected override void Because()
  703. {
  704. // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
  705. _thrown = Catch.Exception(() => Persistence.GetFrom(Bucket.Default, DateTime.MinValue).FirstOrDefault());
  706. }
  707. [Fact]
  708. public void should_NOT_throw_an_exception()
  709. {
  710. _thrown.Should().BeNull();
  711. }
  712. }
  713. #if MSTEST
  714. [TestClass]
  715. #endif
  716. public class when_purging_all_commits : PersistenceEngineConcern
  717. {
  718. protected override void Context()
  719. {
  720. Persistence.CommitSingle();
  721. }
  722. protected override void Because()
  723. {
  724. Persistence.Purge();
  725. }
  726. [Fact]
  727. public void should_not_find_any_commits_stored()
  728. {
  729. Persistence.GetFrom(Bucket.Default, DateTime.MinValue).Count().Should().Be(0);
  730. }
  731. [Fact]
  732. public void should_not_find_any_streams_to_snapshot()
  733. {
  734. Persistence.GetStreamsToSnapshot(0).Count().Should().Be(0);
  735. }
  736. }
  737. #if MSTEST
  738. [TestClass]
  739. #endif
  740. public class when_invoking_after_disposal : PersistenceEngineConcern
  741. {
  742. private Exception _thrown;
  743. protected override void Context()
  744. {
  745. Persistence.Dispose();
  746. }
  747. protected override void Because()
  748. {
  749. _thrown = Catch.Exception(() => Persistence.CommitSingle());
  750. }
  751. [Fact]
  752. public void should_throw_an_ObjectDisposedException()
  753. {
  754. _thrown.Should().BeOfType<ObjectDisposedException>();
  755. }
  756. }
  757. #if MSTEST
  758. [TestClass]
  759. #endif
  760. public class when_committing_a_stream_with_the_same_id_as_a_stream_same_bucket : PersistenceEngineConcern
  761. {
  762. private string _streamId;
  763. private static Exception _thrown;
  764. protected override void Context()
  765. {
  766. _streamId = Guid.NewGuid().ToString();
  767. Persistence.Commit(_streamId.BuildAttempt());
  768. }
  769. protected override void Because()
  770. {
  771. _thrown = Catch.Exception(() => Persistence.Commit(_streamId.BuildAttempt()));
  772. }
  773. [Fact]
  774. public void should_throw()
  775. {
  776. _thrown.Should().NotBeNull();
  777. }
  778. [Fact]
  779. public void should_be_duplicate_commit_exception()
  780. {
  781. _thrown.Should().BeOfType<ConcurrencyException>();
  782. }
  783. }
  784. #if MSTEST
  785. [TestClass]
  786. #endif
  787. public class when_committing_a_stream_with_the_same_id_as_a_stream_in_another_bucket : PersistenceEngineConcern
  788. {
  789. private const string _bucketAId = "a";
  790. private const string _bucketBId = "b";
  791. private string _streamId;
  792. private static CommitAttempt _attemptForBucketB;
  793. private static Exception _thrown;
  794. private DateTime _attemptACommitStamp;
  795. protected override void Context()
  796. {
  797. _streamId = Guid.NewGuid().ToString();
  798. DateTime now = SystemTime.UtcNow;
  799. Persistence.Commit(_streamId.BuildAttempt(now, _bucketAId));
  800. _attemptACommitStamp = Persistence.GetFrom(_bucketAId, _streamId, 0, int.MaxValue).First().CommitStamp;
  801. _attemptForBucketB = _streamId.BuildAttempt(now.Subtract(TimeSpan.FromDays(1)), _bucketBId);
  802. }
  803. protected override void Because()
  804. {
  805. _thrown = Catch.Exception(() => Persistence.Commit(_attemptForBucketB));
  806. }
  807. [Fact]
  808. public void should_succeed()
  809. {
  810. _thrown.Should().BeNull();
  811. }
  812. [Fact]
  813. public void should_persist_to_the_correct_bucket()
  814. {
  815. ICommit[] stream = Persistence.GetFrom(_bucketBId, _streamId, 0, int.MaxValue).ToArray();
  816. stream.Should().NotBeNull();
  817. stream.Count().Should().Be(1);
  818. }
  819. [Fact]
  820. public void should_not_affect_the_stream_from_the_other_bucket()
  821. {
  822. ICommit[] stream = Persistence.GetFrom(_bucketAId, _streamId, 0, int.MaxValue).ToArray();
  823. stream.Should().NotBeNull();
  824. stream.Count().Should().Be(1);
  825. stream[0].CommitStamp.Should().Be(_attemptACommitStamp);
  826. }
  827. }
  828. #if MSTEST
  829. [TestClass]
  830. #endif
  831. public class when_saving_a_snapshot_for_a_stream_with_the_same_id_as_a_stream_in_another_bucket : PersistenceEngineConcern
  832. {
  833. private const string _bucketAId = "a";
  834. private const string _bucketBId = "b";
  835. private string _streamId;
  836. private static Snapshot _snapshot;
  837. protected override void Context()
  838. {
  839. _streamId = Guid.NewGuid().ToString();
  840. _snapshot = new Snapshot(_bucketBId, _streamId, 1, "Snapshot");
  841. Persistence.Commit(_streamId.BuildAttempt(bucketId: _bucketAId));
  842. Persistence.Commit(_streamId.BuildAttempt(bucketId: _bucketBId));
  843. }
  844. protected override void Because()
  845. {
  846. Persistence.AddSnapshot(_snapshot);
  847. }
  848. [Fact]
  849. public void should_affect_snapshots_from_another_bucket()
  850. {
  851. Persistence.GetSnapshot(_bucketAId, _streamId, _snapshot.StreamRevision).Should().BeNull();
  852. }
  853. }
  854. #if MSTEST
  855. [TestClass]
  856. #endif
  857. public class when_reading_all_commits_from_a_particular_point_in_time_and_there_are_streams_in_multiple_buckets : PersistenceEngineConcern
  858. {
  859. private const string _bucketAId = "a";
  860. private const string _bucketBId = "b";
  861. private static DateTime _now;
  862. private static ICommit[] _returnedCommits;
  863. private CommitAttempt _commitToBucketB;
  864. protected override void Context()
  865. {
  866. _now = SystemTime.UtcNow.AddYears(1);
  867. var commitToBucketA = Guid.NewGuid().ToString().BuildAttempt(_now.AddSeconds(1), _bucketAId);
  868. Persistence.Commit(commitToBucketA);
  869. commitToBucketA = commitToBucketA.BuildNextAttempt();
  870. Persistence.Commit(commitToBucketA);
  871. commitToBucketA = commitToBucketA.BuildNextAttempt();
  872. Persistence.Commit(commitToBucketA);
  873. Persistence.Commit(commitToBucketA.BuildNextAttempt());
  874. _commitToBucketB = Guid.NewGuid().ToString().BuildAttempt(_now.AddSeconds(1), _bucketBId);
  875. Persistence.Commit(_commitToBucketB);
  876. }
  877. protected override void Because()
  878. {
  879. _returnedCommits = Persistence.GetFrom(_bucketAId, _now).ToArray();
  880. }
  881. [Fact]
  882. public void should_not_return_commits_from_other_buckets()
  883. {
  884. _returnedCommits.Any(c => c.CommitId.Equals(_commitToBucketB.CommitId)).Should().BeFalse();
  885. }
  886. }
  887. #if MSTEST
  888. [TestClass]
  889. #endif
  890. public class when_getting_all_commits_since_checkpoint_and_there_are_streams_in_multiple_buckets : PersistenceEngineConcern
  891. {
  892. private ICommit[] _commits;
  893. protected override void Context()
  894. {
  895. const string bucketAId = "a";
  896. const string bucketBId = "b";
  897. Persistence.Commit(Guid.NewGuid().ToString().BuildAttempt(bucketId: bucketAId));
  898. Persistence.Commit(Guid.NewGuid().ToString().BuildAttempt(bucketId: bucketBId));
  899. Persistence.Commit(Guid.NewGuid().ToString().BuildAttempt(bucketId: bucketAId));
  900. }
  901. protected override void Because()
  902. {
  903. _commits = Persistence.GetFrom(0).ToArray();
  904. }
  905. [Fact]
  906. public void should_not_be_empty()
  907. {
  908. _commits.Should().NotBeEmpty();
  909. }
  910. [Fact]
  911. public void should_be_in_order_by_checkpoint()
  912. {
  913. Int64 checkpoint = 0;
  914. foreach (var commit in _commits)
  915. {
  916. Int64 commitCheckpoint = commit.CheckpointToken;
  917. commitCheckpoint.Should().BeGreaterThan(checkpoint);
  918. checkpoint = commit.CheckpointToken;
  919. }
  920. }
  921. }
  922. #if MSTEST
  923. [TestClass]
  924. #endif
  925. public class when_purging_all_commits_and_there_are_streams_in_multiple_buckets : PersistenceEngineConcern
  926. {
  927. private const string _bucketAId = "a";
  928. private const string _bucketBId = "b";
  929. private string _streamId;
  930. protected override void Context()
  931. {
  932. _streamId = Guid.NewGuid().ToString();
  933. Persistence.Commit(_streamId.BuildAttempt(bucketId: _bucketAId));
  934. Persistence.Commit(_streamId.BuildAttempt(bucketId: _bucketBId));
  935. }
  936. protected override void Because()
  937. {
  938. Persistence.Purge();
  939. }
  940. [Fact]
  941. public void should_purge_all_commits_stored_in_bucket_a()
  942. {
  943. Persistence.GetFrom(_bucketAId, DateTime.MinValue).Count().Should().Be(0);
  944. }
  945. [Fact]
  946. public void should_purge_all_commits_stored_in_bucket_b()
  947. {
  948. Persistence.GetFrom(_bucketBId, DateTime.MinValue).Count().Should().Be(0);
  949. }
  950. [Fact]
  951. public void should_purge_all_streams_to_snapshot_in_bucket_a()
  952. {
  953. Persistence.GetStreamsToSnapshot(_bucketAId, 0).Count().Should().Be(0);
  954. }
  955. [Fact]
  956. public void should_purge_all_streams_to_snapshot_in_bucket_b()
  957. {
  958. Persistence.GetStreamsToSnapshot(_bucketBId, 0).Count().Should().Be(0);
  959. }
  960. }
  961. [Serializable]
  962. public class Pippo
  963. {
  964. public String S { get; set; }
  965. }
  966. #if MSTEST
  967. [TestClass]
  968. #endif
  969. public class when_gettingfromcheckpoint_amount_of_commits_exceeds_pagesize : PersistenceEngineConcern
  970. {
  971. private ICommit[] _commits;
  972. private int _moreThanPageSize;
  973. protected override void Because()
  974. {
  975. _moreThanPageSize = ConfiguredPageSizeForTesting + 1;
  976. var eventStore = new OptimisticEventStore(Persistence, null);
  977. // TODO: Not sure how to set the actual pagesize to the const defined above
  978. for (int i = 0; i < _moreThanPageSize; i++)
  979. {
  980. using (IEventStream stream = eventStore.OpenStream(Guid.NewGuid()))
  981. {
  982. stream.Add(new EventMessage { Body = new Pippo() { S = "Hi " + i } });
  983. stream.CommitChanges(Guid.NewGuid());
  984. }
  985. }
  986. _commits = Persistence.GetFrom(0).ToArray();
  987. }
  988. [Fact]
  989. public void Should_have_expected_number_of_commits()
  990. {
  991. _commits.Length.Should().Be(_moreThanPageSize);
  992. }
  993. }
  994. #if MSTEST
  995. [TestClass]
  996. #endif
  997. public class when_a_payload_is_large : PersistenceEngineConcern
  998. {
  999. [Fact]
  1000. public void can_commit()
  1001. {
  1002. const int bodyLength = 100000;
  1003. var attempt = new CommitAttempt(
  1004. Bucket.Default,
  1005. Guid.NewGuid().ToString(),
  1006. 1,
  1007. Guid.NewGuid(),
  1008. 1,
  1009. DateTime.UtcNow,
  1010. new Dictionary<string, object>(),
  1011. new List<EventMessage> { new EventMessage { Body = new string('a', bodyLength) } });
  1012. Persistence.Commit(attempt);
  1013. ICommit commits = Persistence.GetFrom(0).Single();
  1014. commits.Events.Single().Body.ToString().Length.Should().Be(bodyLength);
  1015. }
  1016. }
  1017. /// <summary>
  1018. /// We are adapting the tests to use 3 different frameworks:
  1019. /// - XUnit: the attached test runner does the job (fixture setup and cleanup)
  1020. /// - NUnit (.net core project)
  1021. /// - MSTest (.net core project)
  1022. /// </summary>
  1023. public abstract class PersistenceEngineConcern : SpecificationBase
  1024. #if XUNIT
  1025. , IUseFixture<PersistenceEngineFixture>
  1026. #endif
  1027. #if NUNIT || MSTEST
  1028. , IDisposable
  1029. #endif
  1030. {
  1031. protected PersistenceEngineFixture Fixture { get; private set; }
  1032. protected IPersistStreams Persistence
  1033. {
  1034. get { return Fixture.Persistence; }
  1035. }
  1036. protected int ConfiguredPageSizeForTesting
  1037. {
  1038. get { return 2; }
  1039. }
  1040. /// <summary>
  1041. /// Can be used by XUNIT to initialize the tests.
  1042. /// </summary>
  1043. /// <param name="data"></param>
  1044. public void SetFixture(PersistenceEngineFixture data)
  1045. {
  1046. Fixture = data;
  1047. Fixture.Initialize(ConfiguredPageSizeForTesting);
  1048. }
  1049. #if NUNIT || MSTEST
  1050. public void Dispose()
  1051. {
  1052. Dispose(true);
  1053. GC.SuppressFinalize(this);
  1054. }
  1055. protected virtual void Dispose(bool disposing)
  1056. {
  1057. Fixture?.Dispose();
  1058. }
  1059. /// <summary>
  1060. /// <para>
  1061. /// This code was meant to be run right before every test in the fixture to give time
  1062. /// to do further initialization before the PersistenceEngineFixture was created.
  1063. /// Unfortunately the 3 frameworks
  1064. /// have very different ways of doing this:
  1065. /// - NUnit: TestFixtureSetUp
  1066. /// - MSTest: ClassInitialize (not inherited, will be ignored if defined on a base class)
  1067. /// - xUnit: IUseFixture + SetFixture
  1068. /// We need a way to also have some configuration before the PersistenceEngineFixture is created.
  1069. /// </para>
  1070. /// <para>
  1071. /// We'de decided to use the test constructor to do the job, it's your responsibility to guarantee
  1072. /// One time initialization (for anything that need it, if you have multiple tests on a fixture)
  1073. /// depending on the framework you are using.
  1074. /// </para>
  1075. /// <para>
  1076. /// quick workaround:
  1077. /// - change the parameters of the "Fixture" properties.
  1078. /// - call the 'Reinitialize()' method can be called to rerun the initialization after we changed the configuration
  1079. /// in the constructor.
  1080. /// or
  1081. /// - call the SetFixture() to reinitialize everything.
  1082. /// </para>
  1083. /// </summary>
  1084. protected PersistenceEngineConcern() : this(new PersistenceEngineFixture())
  1085. { }
  1086. protected PersistenceEngineConcern(PersistenceEngineFixture fixture)
  1087. {
  1088. SetFixture(fixture);
  1089. }
  1090. #endif
  1091. }
  1092. public partial class PersistenceEngineFixture : IDisposable
  1093. {
  1094. public IPersistStreams Persistence { get; private set; }
  1095. private readonly Func<int, IPersistStreams> _createPersistence;
  1096. #if !NETSTANDARD1_6 && !NETSTANDARD2_0
  1097. private bool _tracking = false;
  1098. private string _trackingInstanceName;
  1099. /// <summary>
  1100. /// Automatic Performance Counters and tracking was disabled for full
  1101. /// framework tests because their initialization
  1102. /// can fail when the tests run on build machines (like AppVeyor and similar).
  1103. /// You can enable it back calling this function before <see cref="Initialize(int)"/>
  1104. /// </summary>
  1105. /// <param name="instanceName"></param>
  1106. /// <returns></returns>
  1107. public PersistenceEngineFixture TrackPerformanceInstance(string instanceName = "tests")
  1108. {
  1109. _trackingInstanceName = instanceName;
  1110. _tracking = true;
  1111. return this;
  1112. }
  1113. #endif
  1114. public void Initialize(int pageSize)
  1115. {
  1116. #if !NETSTANDARD1_6 && !NETSTANDARD2_0
  1117. // performance counters cab be disabled for full framework tests because their initialization
  1118. // can fail when the tests run on build machines (like AppVeyor and similar)
  1119. if (_tracking)
  1120. {
  1121. Persistence = new NEventStore.Diagnostics.PerformanceCounterPersistenceEngine(_createPersistence(pageSize), _trackingInstanceName);
  1122. }
  1123. else
  1124. {
  1125. Persistence = _createPersistence(pageSize);
  1126. }
  1127. #else
  1128. Persistence = _createPersistence(pageSize);
  1129. #endif
  1130. Persistence.Initialize();
  1131. }
  1132. public void Dispose()
  1133. {
  1134. Dispose(true);
  1135. GC.SuppressFinalize(this);
  1136. }
  1137. protected virtual void Dispose(bool disposing)
  1138. {
  1139. if (Persistence?.IsDisposed == false)
  1140. {
  1141. Persistence.Drop();
  1142. Persistence.Dispose();
  1143. }
  1144. }
  1145. }
  1146. }
  1147. #pragma warning restore S101 // Types should be named in PascalCase
  1148. #pragma warning restore 169 // ReSharper disable InconsistentNaming
  1149. #pragma warning restore IDE1006 // Naming Styles