/test/FastTests/Server/Documents/Revisions/RevisionsTests.cs

https://github.com/fitzchak/ravendb · C# · 631 lines · 564 code · 61 blank · 6 comment · 14 complexity · 5f18e542d427dd33f11a2bbdbb08d141 MD5 · raw file

  1. //-----------------------------------------------------------------------
  2. // <copyright file="RevisionsTests.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using System;
  7. using System.Linq;
  8. using System.Net.Http;
  9. using System.Threading.Tasks;
  10. using Raven.Client;
  11. using Raven.Client.Documents.Conventions;
  12. using Raven.Client.Documents.Operations;
  13. using Raven.Client.Documents.Session;
  14. using Raven.Client.Http;
  15. using Raven.Client.Json;
  16. using Raven.Server.Documents;
  17. using Raven.Server.Documents.Handlers.Admin;
  18. using Raven.Server.Documents.Patch;
  19. using Raven.Tests.Core.Utils.Entities;
  20. using Sparrow.Json;
  21. using Xunit;
  22. namespace FastTests.Server.Documents.Revisions
  23. {
  24. public class RevisionsTests : RavenTestBase
  25. {
  26. [Fact]
  27. public async Task CanGetRevisionsByChangeVectors()
  28. {
  29. using (var store = GetDocumentStore())
  30. {
  31. var id = "users/1";
  32. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  33. using (var session = store.OpenAsyncSession())
  34. {
  35. await session.StoreAsync(new User { Name = "Fitzchak" }, id);
  36. await session.SaveChangesAsync();
  37. }
  38. for (int i = 0; i < 10; i++)
  39. {
  40. using (var session = store.OpenAsyncSession())
  41. {
  42. var user = await session.LoadAsync<Company>(id);
  43. user.Name = "Fitzchak " + i;
  44. await session.SaveChangesAsync();
  45. }
  46. }
  47. using (var session = store.OpenAsyncSession())
  48. {
  49. var revisionsMetadata = await session.Advanced.Revisions.GetMetadataForAsync(id);
  50. Assert.Equal(11, revisionsMetadata.Count);
  51. var changeVectors = revisionsMetadata.Select(x => x.GetString(Constants.Documents.Metadata.ChangeVector)).ToList();
  52. changeVectors.Add("NotExistsChangeVector");
  53. var revisions = await session.Advanced.Revisions.GetAsync<User>(changeVectors);
  54. var first = revisions.First();
  55. var last = revisions.Last();
  56. Assert.NotNull(first.Value);
  57. Assert.Null(last.Value);
  58. Assert.NotNull(await session.Advanced.Revisions.GetAsync<User>(first.Key));
  59. Assert.Null(await session.Advanced.Revisions.GetAsync<User>(last.Key));
  60. }
  61. }
  62. }
  63. [Fact]
  64. public async Task CanGetNullForNotExistsDocument()
  65. {
  66. using (var store = GetDocumentStore())
  67. {
  68. var id = "users/1";
  69. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  70. using (var session = store.OpenAsyncSession())
  71. {
  72. var revisions = await session.Advanced.Revisions.GetForAsync<User>(id);
  73. Assert.NotNull(revisions);
  74. Assert.Empty(revisions);
  75. var metadata = await session.Advanced.Revisions.GetMetadataForAsync(id);
  76. Assert.NotNull(metadata);
  77. Assert.Empty(metadata);
  78. }
  79. }
  80. }
  81. [Fact]
  82. public async Task CanGetAllRevisionsFor()
  83. {
  84. var company = new Company { Name = "Company Name" };
  85. using (var store = GetDocumentStore())
  86. {
  87. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  88. using (var session = store.OpenAsyncSession())
  89. {
  90. await session.StoreAsync(company);
  91. await session.SaveChangesAsync();
  92. }
  93. using (var session = store.OpenAsyncSession())
  94. {
  95. var company3 = await session.LoadAsync<Company>(company.Id);
  96. company3.Name = "Hibernating Rhinos";
  97. await session.SaveChangesAsync();
  98. }
  99. using (var session = store.OpenAsyncSession())
  100. {
  101. var companiesRevisions = await session.Advanced.Revisions.GetForAsync<Company>(company.Id);
  102. Assert.Equal(2, companiesRevisions.Count);
  103. Assert.Equal("Hibernating Rhinos", companiesRevisions[0].Name);
  104. Assert.Equal("Company Name", companiesRevisions[1].Name);
  105. }
  106. }
  107. }
  108. [Fact]
  109. public async Task CanCheckIfDocumentHasRevisions()
  110. {
  111. var company = new Company { Name = "Company Name" };
  112. using (var store = GetDocumentStore())
  113. {
  114. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  115. using (var session = store.OpenAsyncSession())
  116. {
  117. await session.StoreAsync(company);
  118. await session.SaveChangesAsync();
  119. }
  120. using (var session = store.OpenAsyncSession())
  121. {
  122. var company3 = await session.LoadAsync<Company>(company.Id);
  123. var metadata = session.Advanced.GetMetadataFor(company3);
  124. Assert.Equal(DocumentFlags.HasRevisions.ToString(), metadata.GetString(Constants.Documents.Metadata.Flags));
  125. }
  126. }
  127. }
  128. [Fact]
  129. public async Task GetRevisionsOfNotExistKey()
  130. {
  131. using (var store = GetDocumentStore())
  132. {
  133. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  134. using (var session = store.OpenAsyncSession())
  135. {
  136. var companiesRevisions = await session.Advanced.Revisions.GetForAsync<Company>("companies/1");
  137. Assert.Equal(0, companiesRevisions.Count);
  138. }
  139. }
  140. }
  141. [Fact]
  142. public async Task CanExcludeEntitiesFromRevisions()
  143. {
  144. var user = new User { Name = "User Name" };
  145. var comment = new Comment { Name = "foo" };
  146. using (var store = GetDocumentStore())
  147. {
  148. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  149. using (var session = store.OpenAsyncSession())
  150. {
  151. await session.StoreAsync(user);
  152. await session.StoreAsync(comment);
  153. await session.SaveChangesAsync();
  154. }
  155. using (var session = store.OpenAsyncSession())
  156. {
  157. Assert.Empty(await session.Advanced.Revisions.GetForAsync<Comment>(comment.Id));
  158. var users = await session.Advanced.Revisions.GetForAsync<User>(user.Id);
  159. Assert.Equal(1, users.Count);
  160. }
  161. }
  162. }
  163. [Fact]
  164. public async Task ServerSaveBundlesAfterRestart()
  165. {
  166. var path = NewDataPath();
  167. var company = new Company { Name = "Company Name" };
  168. using (var store = GetDocumentStore(new Options
  169. {
  170. Path = path
  171. }))
  172. {
  173. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  174. using (var session = store.OpenAsyncSession())
  175. {
  176. await session.StoreAsync(company);
  177. await session.SaveChangesAsync();
  178. }
  179. using (var session = store.OpenAsyncSession())
  180. {
  181. var company3 = await session.LoadAsync<Company>(company.Id);
  182. company3.Name = "Hibernating Rhinos";
  183. await session.SaveChangesAsync();
  184. }
  185. var old = GetDocumentDatabaseInstanceFor(store).Result;
  186. Server.ServerStore.DatabasesLandlord.UnloadDirectly(store.Database);
  187. using (var session = store.OpenAsyncSession())
  188. {
  189. var companiesRevisions = await session.Advanced.Revisions.GetForAsync<Company>(company.Id);
  190. Assert.Equal(2, companiesRevisions.Count);
  191. Assert.Equal("Hibernating Rhinos", companiesRevisions[0].Name);
  192. Assert.Equal("Company Name", companiesRevisions[1].Name);
  193. }
  194. var newInstance = GetDocumentDatabaseInstanceFor(store).Result;
  195. Assert.NotSame(old, newInstance);
  196. }
  197. }
  198. [Fact]
  199. public async Task WillCreateRevision()
  200. {
  201. var product = new User { Name = "Hibernating" };
  202. using (var store = GetDocumentStore())
  203. {
  204. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  205. using (var session = store.OpenAsyncSession())
  206. {
  207. await session.StoreAsync(product);
  208. await session.SaveChangesAsync();
  209. }
  210. using (var session = store.OpenAsyncSession())
  211. {
  212. product.Name += " Rhinos";
  213. await session.StoreAsync(product);
  214. await session.SaveChangesAsync();
  215. }
  216. using (var session = store.OpenAsyncSession())
  217. {
  218. product.Name += " - RavenDB";
  219. await session.StoreAsync(product);
  220. await session.SaveChangesAsync();
  221. }
  222. using (var session = store.OpenAsyncSession())
  223. {
  224. var users = await session.Advanced.Revisions.GetForAsync<User>(product.Id);
  225. Assert.Equal(3, users.Count);
  226. Assert.Equal("Hibernating Rhinos - RavenDB", users[0].Name);
  227. Assert.Equal("Hibernating Rhinos", users[1].Name);
  228. Assert.Equal("Hibernating", users[2].Name);
  229. }
  230. }
  231. }
  232. [Fact]
  233. public async Task WillCreateValidRevisionWhenCompressionDocumentWasSaved()
  234. {
  235. var user = new User { Name = new string('1', 4096 * 2) }; // create a string which will be compressed
  236. using (var store = GetDocumentStore())
  237. {
  238. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  239. using (var session = store.OpenAsyncSession())
  240. {
  241. await session.StoreAsync(user);
  242. await session.SaveChangesAsync();
  243. }
  244. using (var session = store.OpenAsyncSession())
  245. {
  246. var actualUser = await session.LoadAsync<User>(user.Id);
  247. Assert.Equal(actualUser.Name, user.Name);
  248. var users = await session.Advanced.Revisions.GetForAsync<User>(user.Id);
  249. Assert.Equal(user.Name, users.Single().Name);
  250. }
  251. }
  252. }
  253. [Fact]
  254. public async Task WillNotCreateRevision()
  255. {
  256. var product = new Product { Description = "A fine document db", Quantity = 5 };
  257. using (var store = GetDocumentStore())
  258. {
  259. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  260. using (var session = store.OpenAsyncSession())
  261. {
  262. await session.StoreAsync(product);
  263. await session.SaveChangesAsync();
  264. }
  265. using (var session = store.OpenAsyncSession())
  266. {
  267. product.Description = "desc 2";
  268. await session.StoreAsync(product);
  269. await session.SaveChangesAsync();
  270. }
  271. using (var session = store.OpenAsyncSession())
  272. {
  273. product.Description = "desc 3";
  274. await session.StoreAsync(product);
  275. await session.SaveChangesAsync();
  276. }
  277. using (var session = store.OpenAsyncSession())
  278. {
  279. var products = await session.Advanced.Revisions.GetForAsync<Product>(product.Id);
  280. Assert.Equal(0, products.Count);
  281. }
  282. }
  283. }
  284. [Fact]
  285. public async Task WillDeleteOldRevisions()
  286. {
  287. var company = new Company { Name = "Company #1" };
  288. using (var store = GetDocumentStore())
  289. {
  290. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  291. using (var session = store.OpenAsyncSession())
  292. {
  293. await session.StoreAsync(company);
  294. await session.SaveChangesAsync();
  295. for (var i = 0; i < 10; i++)
  296. {
  297. company.Name = "Company #2: " + i;
  298. await session.SaveChangesAsync();
  299. }
  300. }
  301. using (var session = store.OpenAsyncSession())
  302. {
  303. var revisions = await session.Advanced.Revisions.GetForAsync<Company>(company.Id);
  304. Assert.Equal(5, revisions.Count);
  305. Assert.Equal("Company #2: 9", revisions[0].Name);
  306. Assert.Equal("Company #2: 5", revisions[4].Name);
  307. }
  308. }
  309. }
  310. [Fact]
  311. public async Task WillDeleteRevisionsIfDeleted_OnlyIfPurgeOnDeleteIsTrue()
  312. {
  313. using (var store = GetDocumentStore())
  314. {
  315. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  316. using (var session = store.OpenAsyncSession())
  317. {
  318. var company = new Company { Name = "Hibernating Rhinos " };
  319. var user = new User { Name = "Fitzchak " };
  320. await session.StoreAsync(company, "companies/1");
  321. await session.StoreAsync(user, "users/1");
  322. await session.SaveChangesAsync();
  323. }
  324. for (int i = 0; i < 10; i++)
  325. {
  326. using (var session = store.OpenAsyncSession())
  327. {
  328. var company = await session.LoadAsync<Company>("companies/1");
  329. var user = await session.LoadAsync<User>("users/1");
  330. company.Name += i;
  331. user.Name += i;
  332. await session.StoreAsync(company);
  333. await session.StoreAsync(user);
  334. await session.SaveChangesAsync();
  335. }
  336. }
  337. using (var session = store.OpenAsyncSession())
  338. {
  339. var company = await session.LoadAsync<Company>("companies/1");
  340. var user = await session.LoadAsync<User>("users/1");
  341. Assert.NotNull(company);
  342. Assert.NotNull(user);
  343. session.Delete(company);
  344. session.Delete(user);
  345. await session.SaveChangesAsync();
  346. }
  347. using (var session = store.OpenAsyncSession())
  348. {
  349. var companies = await session.Advanced.Revisions.GetForAsync<Company>("companies/1");
  350. var users = await session.Advanced.Revisions.GetForAsync<User>("users/1");
  351. Assert.Equal(5, companies.Count);
  352. Assert.Empty(users);
  353. }
  354. using (var session = store.OpenAsyncSession())
  355. {
  356. await session.StoreAsync(new Company { Name = "New Company" }, "companies/1");
  357. await session.StoreAsync(new User { Name = "New User" }, "users/1");
  358. await session.SaveChangesAsync();
  359. }
  360. using (var session = store.OpenAsyncSession())
  361. {
  362. var companies = await session.Advanced.Revisions.GetForAsync<Company>("companies/1");
  363. var users = await session.Advanced.Revisions.GetForAsync<User>("users/1");
  364. Assert.Equal(5, companies.Count);
  365. Assert.Equal("New Company", companies.First().Name);
  366. Assert.Equal(1, users.Count);
  367. }
  368. }
  369. }
  370. [Fact]
  371. public async Task RevisionsOrder()
  372. {
  373. using (var store = GetDocumentStore())
  374. {
  375. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database);
  376. using (var session = store.OpenAsyncSession())
  377. {
  378. await session.StoreAsync(new User { Name = "Hibernating" }, "users/1");
  379. await session.StoreAsync(new User { Name = "Hibernating11" }, "users/11");
  380. await session.SaveChangesAsync();
  381. }
  382. using (var session = store.OpenAsyncSession())
  383. {
  384. await session.StoreAsync(new User { Name = "Hibernating Rhinos" }, "users/1");
  385. await session.StoreAsync(new User { Name = "Hibernating Rhinos11" }, "users/11");
  386. await session.SaveChangesAsync();
  387. }
  388. using (var session = store.OpenAsyncSession())
  389. {
  390. await session.StoreAsync(new User { Name = "Hibernating Rhinos - RavenDB" }, "users/1");
  391. await session.StoreAsync(new User { Name = "Hibernating Rhinos - RavenDB11" }, "users/11");
  392. await session.SaveChangesAsync();
  393. }
  394. using (var session = store.OpenAsyncSession())
  395. {
  396. var users = await session.Advanced.Revisions.GetForAsync<User>("users/1");
  397. Assert.Equal(3, users.Count);
  398. Assert.Equal("Hibernating Rhinos - RavenDB", users[0].Name);
  399. Assert.Equal("Hibernating Rhinos", users[1].Name);
  400. Assert.Equal("Hibernating", users[2].Name);
  401. }
  402. }
  403. }
  404. [Theory]
  405. [InlineData(false)]
  406. [InlineData(true)]
  407. public async Task GetRevisionsBinEntries(bool useSession)
  408. {
  409. using (var store = GetDocumentStore())
  410. {
  411. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database, false);
  412. var deletedRevisions = await store.Commands().GetRevisionsBinEntriesAsync(long.MaxValue);
  413. Assert.Equal(0, deletedRevisions.Count);
  414. var id = "users/1";
  415. if (useSession)
  416. {
  417. var user = new User { Name = "Fitzchak" };
  418. for (var i = 0; i < 2; i++)
  419. {
  420. using (var session = store.OpenAsyncSession())
  421. {
  422. await session.StoreAsync(user);
  423. await session.SaveChangesAsync();
  424. }
  425. using (var session = store.OpenAsyncSession())
  426. {
  427. session.Delete(user.Id);
  428. await session.SaveChangesAsync();
  429. }
  430. }
  431. id += "-A";
  432. }
  433. else
  434. {
  435. await store.Commands().PutAsync(id, null, new User { Name = "Fitzchak" });
  436. await store.Commands().DeleteAsync(id, null);
  437. await store.Commands().PutAsync(id, null, new User { Name = "Fitzchak" });
  438. await store.Commands().DeleteAsync(id, null);
  439. }
  440. var statistics = store.Maintenance.Send(new GetStatisticsOperation());
  441. Assert.Equal(useSession ? 1 : 0, statistics.CountOfDocuments);
  442. Assert.Equal(4, statistics.CountOfRevisionDocuments);
  443. deletedRevisions = await store.Commands().GetRevisionsBinEntriesAsync(long.MaxValue);
  444. Assert.Equal(1, deletedRevisions.Count);
  445. using (var session = store.OpenAsyncSession())
  446. {
  447. var users = await session.Advanced.Revisions.GetForAsync<User>(id);
  448. Assert.Equal(4, users.Count);
  449. Assert.Equal(null, users[0].Name);
  450. Assert.Equal("Fitzchak", users[1].Name);
  451. Assert.Equal(null, users[2].Name);
  452. Assert.Equal("Fitzchak", users[3].Name);
  453. // Can get metadata only
  454. var revisionsMetadata = await session.Advanced.Revisions.GetMetadataForAsync(id);
  455. Assert.Equal(4, revisionsMetadata.Count);
  456. Assert.Contains(DocumentFlags.DeleteRevision.ToString(), revisionsMetadata[0].GetString(Constants.Documents.Metadata.Flags));
  457. Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision).ToString(), revisionsMetadata[1].GetString(Constants.Documents.Metadata.Flags));
  458. Assert.Contains(DocumentFlags.DeleteRevision.ToString(), revisionsMetadata[2].GetString(Constants.Documents.Metadata.Flags));
  459. Assert.Equal((DocumentFlags.HasRevisions | DocumentFlags.Revision).ToString(), revisionsMetadata[3].GetString(Constants.Documents.Metadata.Flags));
  460. }
  461. await store.Maintenance.SendAsync(new DeleteRevisionsOperation(new AdminRevisionsHandler.Parameters
  462. {
  463. DocumentIds = new[] { id, "users/not/exists" }
  464. }));
  465. statistics = store.Maintenance.Send(new GetStatisticsOperation());
  466. Assert.Equal(useSession ? 1 : 0, statistics.CountOfDocuments);
  467. Assert.Equal(0, statistics.CountOfRevisionDocuments);
  468. }
  469. }
  470. [Theory]
  471. [InlineData(false)]
  472. [InlineData(true)]
  473. public async Task DeleteRevisionsBeforeFromConsole(bool useConsole)
  474. {
  475. using (var store = GetDocumentStore())
  476. {
  477. await RevisionsHelper.SetupRevisions(Server.ServerStore, store.Database, false);
  478. var database = await GetDocumentDatabaseInstanceFor(store);
  479. database.Time.UtcDateTime = () => DateTime.UtcNow.AddDays(-1);
  480. for (var i = 0; i < 10; i++)
  481. {
  482. using (var session = store.OpenAsyncSession())
  483. {
  484. await session.StoreAsync(new User { Name = "Fitzchak " + i });
  485. await session.SaveChangesAsync();
  486. }
  487. }
  488. database.Time.UtcDateTime = () => DateTime.UtcNow.AddDays(1);
  489. for (var i = 0; i < 10; i++)
  490. {
  491. using (var session = store.OpenAsyncSession())
  492. {
  493. await session.StoreAsync(new User { Name = "Fitzchak " + (i + 100) });
  494. await session.SaveChangesAsync();
  495. }
  496. }
  497. database.Time.UtcDateTime = () => DateTime.UtcNow;
  498. var statistics = store.Maintenance.Send(new GetStatisticsOperation());
  499. Assert.Equal(21, statistics.CountOfDocuments);
  500. Assert.Equal(20, statistics.CountOfRevisionDocuments);
  501. if (useConsole)
  502. {
  503. new AdminJsConsole(Server, database).ApplyScript(new AdminJsScript(
  504. "database.DocumentsStorage.RevisionsStorage.Operations.DeleteRevisionsBefore('Users', new Date());"));
  505. }
  506. else
  507. {
  508. database.DocumentsStorage.RevisionsStorage.Operations.DeleteRevisionsBefore("Users", DateTime.UtcNow);
  509. }
  510. statistics = store.Maintenance.Send(new GetStatisticsOperation());
  511. Assert.Equal(21, statistics.CountOfDocuments);
  512. Assert.Equal(10, statistics.CountOfRevisionDocuments);
  513. }
  514. }
  515. public class DeleteRevisionsOperation : IMaintenanceOperation
  516. {
  517. private readonly AdminRevisionsHandler.Parameters _parameters;
  518. public DeleteRevisionsOperation(AdminRevisionsHandler.Parameters parameters)
  519. {
  520. _parameters = parameters;
  521. }
  522. public RavenCommand GetCommand(DocumentConventions conventions, JsonOperationContext context)
  523. {
  524. return new DeleteRevisionsCommand(conventions, context, _parameters);
  525. }
  526. private class DeleteRevisionsCommand : RavenCommand
  527. {
  528. private readonly BlittableJsonReaderObject _parameters;
  529. public DeleteRevisionsCommand(DocumentConventions conventions, JsonOperationContext context, AdminRevisionsHandler.Parameters parameters)
  530. {
  531. if (conventions == null)
  532. throw new ArgumentNullException(nameof(conventions));
  533. if (context == null)
  534. throw new ArgumentNullException(nameof(context));
  535. if (parameters == null)
  536. throw new ArgumentNullException(nameof(parameters));
  537. _parameters = EntityToBlittable.ConvertEntityToBlittable(parameters, conventions, context);
  538. }
  539. public override HttpRequestMessage CreateRequest(JsonOperationContext ctx, ServerNode node, out string url)
  540. {
  541. url = $"{node.Url}/databases/{node.Database}/admin/revisions";
  542. return new HttpRequestMessage
  543. {
  544. Method = HttpMethod.Delete,
  545. Content = new BlittableJsonContent(stream =>
  546. {
  547. ctx.Write(stream, _parameters);
  548. })
  549. };
  550. }
  551. }
  552. }
  553. private class Comment
  554. {
  555. public string Id { get; set; }
  556. public string Name { get; set; }
  557. }
  558. private class User
  559. {
  560. public string Id { get; set; }
  561. public string Name { get; set; }
  562. }
  563. private class Product
  564. {
  565. public string Id { get; set; }
  566. public string Description { get; set; }
  567. public int Quantity { get; set; }
  568. }
  569. }
  570. }