PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/ToMigrate/Raven.Tests.FileSystem/ClientApi/FileSessionListenersTests.cs

https://github.com/georgiosd/ravendb
C# | 512 lines | 391 code | 115 blank | 6 comment | 7 complexity | 2de71841d5e92542c5baf125208f753b MD5 | raw file
Possible License(s): BSD-3-Clause, CC-BY-SA-3.0, GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. using Raven.Abstractions.FileSystem;
  2. using Raven.Abstractions.FileSystem.Notifications;
  3. using Raven.Client.FileSystem;
  4. using Raven.Client.FileSystem.Extensions;
  5. using Raven.Client.FileSystem.Listeners;
  6. using Raven.Json.Linq;
  7. using System;
  8. using System.Linq;
  9. using System.Reactive.Linq;
  10. using System.Reactive.Threading.Tasks;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using Xunit;
  14. namespace Raven.Tests.FileSystem.ClientApi
  15. {
  16. public class FileSessionListenersTests : RavenFilesTestWithLogs
  17. {
  18. [Fact]
  19. public async Task DoNotDeleteReadOnlyFiles()
  20. {
  21. var store = this.NewStore(1);
  22. var anotherStore = this.NewStore(2);
  23. var deleteListener = new DeleteNotReadOnlyFilesListener();
  24. store.Listeners.RegisterListener(deleteListener);
  25. using (var session = store.OpenAsyncSession())
  26. {
  27. session.RegisterUpload("/b/test1.file", CreateUniformFileStream(128));
  28. session.RegisterUpload("/b/test2.file", CreateUniformFileStream(128));
  29. await session.SaveChangesAsync();
  30. var file = await session.LoadFileAsync("/b/test1.file");
  31. file.Metadata.Add("Read-Only", true);
  32. await session.SaveChangesAsync();
  33. session.RegisterFileDeletion("/b/test1.file");
  34. session.RegisterFileDeletion("/b/test2.file");
  35. await session.SaveChangesAsync();
  36. Assert.Equal(1, deleteListener.AfterCount);
  37. file = await session.LoadFileAsync("/b/test1.file");
  38. var file2 = await session.LoadFileAsync("/b/test2.file");
  39. Assert.NotNull(file);
  40. Assert.Null(file2);
  41. }
  42. }
  43. [Fact]
  44. public async Task NoOpDeleteListener()
  45. {
  46. var store = this.NewStore(1);
  47. var anotherStore = this.NewStore(2);
  48. var noOpListener = new NoOpDeleteFilesListener();
  49. store.Listeners.RegisterListener(noOpListener);
  50. using (var session = store.OpenAsyncSession())
  51. {
  52. session.RegisterUpload("/b/test1.file", CreateUniformFileStream(128));
  53. session.RegisterUpload("/b/test2.file", CreateUniformFileStream(128));
  54. await session.SaveChangesAsync();
  55. session.RegisterFileDeletion("/b/test1.file");
  56. session.RegisterFileDeletion("/b/test2.file");
  57. await session.SaveChangesAsync();
  58. Assert.Equal(2, noOpListener.AfterCount);
  59. var file = await session.LoadFileAsync("/b/test1.file");
  60. var file2 = await session.LoadFileAsync("/b/test2.file");
  61. Assert.Null(file);
  62. Assert.Null(file2);
  63. }
  64. }
  65. [Fact]
  66. public async Task MultipleDeleteListeners()
  67. {
  68. var store = this.NewStore(1);
  69. var anotherStore = this.NewStore(2);
  70. var deleteListener = new DeleteNotReadOnlyFilesListener();
  71. var noOpListener = new NoOpDeleteFilesListener();
  72. store.Listeners.RegisterListener(deleteListener);
  73. store.Listeners.RegisterListener(noOpListener);
  74. using (var session = store.OpenAsyncSession())
  75. {
  76. session.RegisterUpload("/b/test1.file", CreateUniformFileStream(128));
  77. session.RegisterUpload("/b/test2.file", CreateUniformFileStream(128));
  78. await session.SaveChangesAsync();
  79. var file = await session.LoadFileAsync("/b/test1.file");
  80. file.Metadata.Add("Read-Only", true);
  81. await session.SaveChangesAsync();
  82. session.RegisterFileDeletion("/b/test1.file");
  83. session.RegisterFileDeletion("/b/test2.file");
  84. await session.SaveChangesAsync();
  85. Assert.Equal(2, deleteListener.AfterCount + noOpListener.AfterCount);
  86. Assert.Equal(4, deleteListener.BeforeCount + noOpListener.BeforeCount);
  87. }
  88. }
  89. [Fact]
  90. public async Task ConflictListeners_LocalVersion()
  91. {
  92. var store = this.NewStore(1);
  93. var anotherStore = this.NewStore(2);
  94. var conflictsListener = new TakeLocalConflictListener();
  95. anotherStore.Listeners.RegisterListener(conflictsListener);
  96. using (var sessionDestination1 = store.OpenAsyncSession())
  97. using (var sessionDestination2 = anotherStore.OpenAsyncSession())
  98. {
  99. sessionDestination1.RegisterUpload("test1.file", CreateUniformFileStream(128));
  100. await sessionDestination1.SaveChangesAsync();
  101. sessionDestination2.RegisterUpload("test1.file", CreateUniformFileStream(130));
  102. await sessionDestination2.SaveChangesAsync();
  103. var notificationTask = await WaitForConflictResolved(anotherStore, 1, 10);
  104. var syncDestinations = new SynchronizationDestination[] { sessionDestination2.Commands.ToSynchronizationDestination() };
  105. await sessionDestination1.Commands.Synchronization.SetDestinationsAsync(syncDestinations);
  106. await sessionDestination1.Commands.Synchronization.StartAsync();
  107. await notificationTask;
  108. Assert.Equal(1, conflictsListener.DetectedCount);
  109. Assert.Equal(1, conflictsListener.ResolvedCount);
  110. var file = await sessionDestination1.LoadFileAsync("test1.file");
  111. var file2 = await sessionDestination2.LoadFileAsync("test1.file");
  112. Assert.Equal(128, file.TotalSize);
  113. Assert.Equal(130, file2.TotalSize);
  114. }
  115. }
  116. [Fact]
  117. public async Task ConflictListeners_RemoteVersion()
  118. {
  119. var filename = FileHeader.Canonize("test1.file");
  120. int firstStreamSize = 130;
  121. int secondStreamSize = 128;
  122. var store = this.NewStore(1);
  123. var anotherStore = this.NewStore(2);
  124. var conflictsListener = new TakeNewestConflictsListener();
  125. anotherStore.Listeners.RegisterListener(conflictsListener);
  126. using (var sessionDestination1 = store.OpenAsyncSession())
  127. using (var sessionDestination2 = anotherStore.OpenAsyncSession())
  128. {
  129. sessionDestination2.RegisterUpload(filename, CreateUniformFileStream(firstStreamSize));
  130. await sessionDestination2.SaveChangesAsync();
  131. sessionDestination1.RegisterUpload(filename, CreateUniformFileStream(secondStreamSize));
  132. await sessionDestination1.SaveChangesAsync();
  133. var notificationTask = await WaitForConflictResolved(anotherStore, 1, 30);
  134. var syncDestinations = new SynchronizationDestination[] { sessionDestination2.Commands.ToSynchronizationDestination() };
  135. await sessionDestination1.Commands.Synchronization.SetDestinationsAsync(syncDestinations);
  136. var syncResult = await sessionDestination1.Commands.Synchronization.StartAsync();
  137. Assert.Equal(string.Format("File {0} is conflicted", filename), syncResult[0].Reports.ToList()[0].Exception.Message);
  138. // conflict should be resolved by the registered listener
  139. Assert.True(SpinWait.SpinUntil(() => conflictsListener.DetectedCount == 1 && conflictsListener.ResolvedCount == 1, TimeSpan.FromMinutes(1)),
  140. string.Format("DetectedCount: {0}, ResolvedCount: {1}", conflictsListener.DetectedCount, conflictsListener.ResolvedCount));
  141. // We need to sync again after conflict resolution because the strategy was to resolve with remote
  142. await sessionDestination1.Commands.Synchronization.StartAsync();
  143. await notificationTask;
  144. Assert.Equal(1, conflictsListener.DetectedCount);
  145. Assert.Equal(1, conflictsListener.ResolvedCount);
  146. var file = await sessionDestination1.LoadFileAsync(filename);
  147. var file2 = await sessionDestination2.LoadFileAsync(filename);
  148. Assert.Equal(secondStreamSize, file.TotalSize);
  149. Assert.Equal(secondStreamSize, file2.TotalSize);
  150. }
  151. }
  152. [Fact]
  153. public async Task MultipleConflictListeners_OnlyOneWithShortCircuitResolution()
  154. {
  155. var store = this.NewStore(1);
  156. var anotherStore = this.NewStore(2);
  157. var conflictsListener = new TakeLocalConflictListener();
  158. var noOpListener = new NoOpConflictListener();
  159. anotherStore.Listeners.RegisterListener(conflictsListener);
  160. anotherStore.Listeners.RegisterListener(noOpListener);
  161. using (var sessionDestination1 = store.OpenAsyncSession())
  162. using (var sessionDestination2 = anotherStore.OpenAsyncSession())
  163. {
  164. sessionDestination2.RegisterUpload("test1.file", CreateUniformFileStream(130));
  165. await sessionDestination2.SaveChangesAsync();
  166. sessionDestination1.RegisterUpload("test1.file", CreateUniformFileStream(128));
  167. await sessionDestination1.SaveChangesAsync();
  168. var notificationTask = await WaitForConflictResolved(anotherStore, 1, 5);
  169. var syncDestinatios = new SynchronizationDestination[] { sessionDestination2.Commands.ToSynchronizationDestination() };
  170. await sessionDestination1.Commands.Synchronization.SetDestinationsAsync(syncDestinatios);
  171. await sessionDestination1.Commands.Synchronization.StartAsync();
  172. await notificationTask;
  173. Assert.Equal(1, conflictsListener.DetectedCount);
  174. Assert.Equal(1, conflictsListener.ResolvedCount);
  175. Assert.Equal(0, noOpListener.DetectedCount);
  176. Assert.Equal(1, noOpListener.ResolvedCount);
  177. }
  178. }
  179. [Fact]
  180. public async Task MultipleConflictListeners_MultipleResolutionListeners()
  181. {
  182. var store = this.NewStore(1);
  183. var anotherStore = this.NewStore(2);
  184. var conflictsListener = new TakeLocalConflictListener();
  185. var noOpListener = new NoOpConflictListener();
  186. anotherStore.Listeners.RegisterListener(noOpListener);
  187. anotherStore.Listeners.RegisterListener(conflictsListener);
  188. using (var sessionDestination1 = store.OpenAsyncSession())
  189. using (var sessionDestination2 = anotherStore.OpenAsyncSession())
  190. {
  191. sessionDestination2.RegisterUpload("test1.file", CreateUniformFileStream(130));
  192. await sessionDestination2.SaveChangesAsync();
  193. sessionDestination1.RegisterUpload("test1.file", CreateUniformFileStream(128));
  194. await sessionDestination1.SaveChangesAsync();
  195. var notificationTask = await WaitForConflictResolved(anotherStore, 1, 5);
  196. var syncDestinatios = new SynchronizationDestination[] { sessionDestination2.Commands.ToSynchronizationDestination() };
  197. await sessionDestination1.Commands.Synchronization.SetDestinationsAsync(syncDestinatios);
  198. await sessionDestination1.Commands.Synchronization.StartAsync();
  199. await notificationTask;
  200. Assert.Equal(1, conflictsListener.DetectedCount);
  201. Assert.Equal(1, conflictsListener.ResolvedCount);
  202. Assert.Equal(1, noOpListener.DetectedCount);
  203. Assert.Equal(1, noOpListener.ResolvedCount);
  204. }
  205. }
  206. [Fact]
  207. public async Task MultipleConflictListeners_ConflictNotResolved()
  208. {
  209. var store = this.NewStore(1);
  210. var anotherStore = this.NewStore(2);
  211. var takeLocalConflictListener = new TakeLocalConflictListener();
  212. var noOpListener = new NoOpConflictListener();
  213. anotherStore.Listeners.RegisterListener(noOpListener);
  214. anotherStore.Listeners.RegisterListener(takeLocalConflictListener);
  215. using (var sessionDestination1 = store.OpenAsyncSession())
  216. using (var sessionDestination2 = anotherStore.OpenAsyncSession())
  217. {
  218. sessionDestination2.RegisterUpload("test1.file", CreateUniformFileStream(130));
  219. await sessionDestination2.SaveChangesAsync();
  220. sessionDestination1.RegisterUpload("test1.file", CreateUniformFileStream(128));
  221. await sessionDestination1.SaveChangesAsync();
  222. var notificationTask = WaitForConflictDetected(anotherStore, 1, 10);
  223. var resolveTask = WaitForConflictResolved(anotherStore, 1, 10);
  224. var syncDestinatios = new SynchronizationDestination[] { sessionDestination2.Commands.ToSynchronizationDestination() };
  225. await sessionDestination1.Commands.Synchronization.SetDestinationsAsync(syncDestinatios);
  226. await sessionDestination1.Commands.Synchronization.StartAsync();
  227. await notificationTask;
  228. await await resolveTask;
  229. Assert.Equal(1, noOpListener.DetectedCount);
  230. Assert.Equal(1, takeLocalConflictListener.DetectedCount);
  231. Assert.Equal(2, takeLocalConflictListener.ResolvedCount + noOpListener.ResolvedCount);
  232. // try to change content of file in destination2
  233. sessionDestination2.RegisterUpload("test1.file", CreateUniformFileStream(140));
  234. // Assert an exception is not thrown because the conflict was already resolved
  235. Assert.DoesNotThrow(() => sessionDestination2.SaveChangesAsync().Wait());
  236. // try to change content of file in destination2
  237. sessionDestination2.RegisterRename("test1.file", "test2.file");
  238. // Assert an exception is not thrown
  239. Assert.DoesNotThrow(() => sessionDestination2.SaveChangesAsync().Wait());
  240. }
  241. }
  242. [Fact]
  243. public async Task MetadataUpdateListeners()
  244. {
  245. var store = this.NewStore(1);
  246. var anotherStore = this.NewStore(2);
  247. var metadataListener = new DoNotUpdateEtagListener();
  248. var alwaysUpdateListener = new NoOpUpdateMetadataListener();
  249. store.Listeners.RegisterListener(metadataListener);
  250. using (var sessionDestination = store.OpenAsyncSession())
  251. {
  252. sessionDestination.RegisterUpload("test1.file", CreateUniformFileStream(128));
  253. await sessionDestination.SaveChangesAsync();
  254. var file = await sessionDestination.LoadFileAsync("test1.file");
  255. var oldEtag = file.Metadata.Value<string>("ETag");
  256. var newEtag = Guid.NewGuid();
  257. file.Metadata["ETag"] = new RavenJValue(newEtag);
  258. await sessionDestination.SaveChangesAsync();
  259. file = await sessionDestination.LoadFileAsync("test1.file");
  260. Assert.Equal(oldEtag, file.Metadata.Value<string>("ETag"));
  261. Assert.Equal(1, metadataListener.BeforeCount);
  262. Assert.Equal(0, metadataListener.AfterCount);
  263. store.Listeners.RegisterListener(alwaysUpdateListener);
  264. file.Metadata["ETag"] = new RavenJValue(newEtag);
  265. await sessionDestination.SaveChangesAsync();
  266. file = await sessionDestination.LoadFileAsync("test1.file");
  267. Assert.Equal(oldEtag, file.Metadata.Value<string>("ETag"));
  268. Assert.Equal(3, alwaysUpdateListener.BeforeCount + metadataListener.BeforeCount);
  269. Assert.Equal(0, alwaysUpdateListener.AfterCount + metadataListener.AfterCount);
  270. }
  271. }
  272. private class NoOpDeleteFilesListener : IFilesDeleteListener
  273. {
  274. public int AfterCount { get; protected set; }
  275. public int BeforeCount { get; protected set; }
  276. public bool BeforeDelete(FileHeader instance)
  277. {
  278. BeforeCount++;
  279. return true;
  280. }
  281. public void AfterDelete(string fileName)
  282. {
  283. AfterCount++;
  284. }
  285. }
  286. private class DeleteNotReadOnlyFilesListener : IFilesDeleteListener
  287. {
  288. public int AfterCount { get; protected set; }
  289. public int BeforeCount { get; protected set; }
  290. public bool BeforeDelete(FileHeader instance)
  291. {
  292. BeforeCount++;
  293. return !instance.Metadata.Value<bool>("Read-Only");
  294. }
  295. public void AfterDelete(string instance)
  296. {
  297. AfterCount++;
  298. }
  299. }
  300. private class TakeNewestConflictsListener : IFilesConflictListener
  301. {
  302. public int ResolvedCount { get; protected set; }
  303. public int DetectedCount { get; protected set; }
  304. public ConflictResolutionStrategy ConflictDetected(FileHeader local, FileHeader remote, string destinationSourceUri)
  305. {
  306. DetectedCount++;
  307. if (local.LastModified.CompareTo(remote.LastModified) >= 0)
  308. return ConflictResolutionStrategy.CurrentVersion;
  309. else
  310. return ConflictResolutionStrategy.RemoteVersion;
  311. }
  312. public void ConflictResolved(FileHeader header)
  313. {
  314. ResolvedCount++;
  315. }
  316. }
  317. private class TakeLocalConflictListener : IFilesConflictListener
  318. {
  319. public int ResolvedCount { get; protected set; }
  320. public int DetectedCount { get; protected set; }
  321. public ConflictResolutionStrategy ConflictDetected(FileHeader local, FileHeader remote, string destinationSourceUri)
  322. {
  323. DetectedCount++;
  324. return ConflictResolutionStrategy.CurrentVersion;
  325. }
  326. public void ConflictResolved(FileHeader header)
  327. {
  328. ResolvedCount++;
  329. }
  330. }
  331. private class NoOpConflictListener : IFilesConflictListener
  332. {
  333. public int ResolvedCount { get; protected set; }
  334. public int DetectedCount { get; protected set; }
  335. public ConflictResolutionStrategy ConflictDetected(FileHeader local, FileHeader remote, string destinationSourceUri)
  336. {
  337. DetectedCount++;
  338. return ConflictResolutionStrategy.NoResolution;
  339. }
  340. public void ConflictResolved(FileHeader header)
  341. {
  342. ResolvedCount++;
  343. }
  344. }
  345. private class DoNotUpdateEtagListener : IMetadataChangeListener
  346. {
  347. public int BeforeCount { get; protected set; }
  348. public int AfterCount { get; protected set; }
  349. public bool BeforeChange(FileHeader instance, RavenJObject metadata, RavenJObject original)
  350. {
  351. BeforeCount++;
  352. return metadata.Value<string>("ETag") == original.Value<string>("ETag");
  353. }
  354. public void AfterChange(FileHeader instance, RavenJObject metadata)
  355. {
  356. AfterCount++;
  357. }
  358. }
  359. private class NoOpUpdateMetadataListener : IMetadataChangeListener
  360. {
  361. public int BeforeCount { get; protected set; }
  362. public int AfterCount { get; protected set; }
  363. public bool BeforeChange(FileHeader instance, RavenJObject metadata, RavenJObject original)
  364. {
  365. BeforeCount++;
  366. return true;
  367. }
  368. public void AfterChange(FileHeader instance, RavenJObject metadata)
  369. {
  370. AfterCount++;
  371. }
  372. }
  373. private async Task<Task<ConflictNotification>> WaitForConflictResolved(IFilesStore store, int notificationsNumber, int time)
  374. {
  375. var changes = store.Changes();
  376. await changes.Task;
  377. var conflicts = changes.ForConflicts();
  378. await conflicts.Task;
  379. return conflicts
  380. .OfType<ConflictNotification>()
  381. .Where(x => x.Status == ConflictStatus.Resolved)
  382. .Timeout(TimeSpan.FromSeconds(time))
  383. .Take(notificationsNumber)
  384. .ToTask();
  385. }
  386. private Task<ConflictNotification> WaitForConflictDetected(IFilesStore store, int notificationsNumber, int time)
  387. {
  388. return store.Changes()
  389. .ForConflicts()
  390. .OfType<ConflictNotification>()
  391. .Where(x => x.Status == ConflictStatus.Detected)
  392. .Timeout(TimeSpan.FromSeconds(time))
  393. .Take(notificationsNumber)
  394. .ToTask();
  395. }
  396. }
  397. }