yjs.integ.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import { YDocStatus } from '@growi/core/dist/consts';
  2. import { Types } from 'mongoose';
  3. import type { Server } from 'socket.io';
  4. import { mock } from 'vitest-mock-extended';
  5. import { Revision } from '../../models/revision';
  6. import type { MongodbPersistence } from './extended/mongodb-persistence';
  7. import type { IYjsService } from './yjs';
  8. import { getYjsService, initializeYjsService } from './yjs';
  9. vi.mock('y-socket.io/dist/server', () => {
  10. const YSocketIO = vi.fn();
  11. YSocketIO.prototype.on = vi.fn();
  12. YSocketIO.prototype.initialize = vi.fn();
  13. return { YSocketIO };
  14. });
  15. vi.mock('../revision/normalize-latest-revision-if-broken', () => ({
  16. normalizeLatestRevisionIfBroken: vi.fn(),
  17. }));
  18. const ObjectId = Types.ObjectId;
  19. const getPrivateMdbInstance = (yjsService: IYjsService): MongodbPersistence => {
  20. // eslint-disable-next-line dot-notation
  21. return yjsService['mdb'];
  22. };
  23. describe('YjsService', () => {
  24. describe('getYDocStatus()', () => {
  25. beforeAll(async() => {
  26. const ioMock = mock<Server>();
  27. // initialize
  28. initializeYjsService(ioMock);
  29. });
  30. afterAll(async() => {
  31. // flush revisions
  32. await Revision.deleteMany({});
  33. // flush yjs-writings
  34. const yjsService = getYjsService();
  35. const privateMdb = getPrivateMdbInstance(yjsService);
  36. await privateMdb.flushDB();
  37. });
  38. it('returns ISOLATED when neither revisions nor YDocs exists', async() => {
  39. // arrange
  40. const yjsService = getYjsService();
  41. const pageId = new ObjectId();
  42. // act
  43. const result = await yjsService.getYDocStatus(pageId.toString());
  44. // assert
  45. expect(result).toBe(YDocStatus.ISOLATED);
  46. });
  47. it('returns ISOLATED when no revisions exist', async() => {
  48. // arrange
  49. const yjsService = getYjsService();
  50. const pageId = new ObjectId();
  51. const privateMdb = getPrivateMdbInstance(yjsService);
  52. await privateMdb.setTypedMeta(pageId.toString(), 'updatedAt', 1000);
  53. // act
  54. const result = await yjsService.getYDocStatus(pageId.toString());
  55. // assert
  56. expect(result).toBe(YDocStatus.ISOLATED);
  57. });
  58. it('returns NEW when no YDocs exist', async() => {
  59. // arrange
  60. const yjsService = getYjsService();
  61. const pageId = new ObjectId();
  62. await Revision.insertMany([
  63. { pageId, body: '' },
  64. ]);
  65. // act
  66. const result = await yjsService.getYDocStatus(pageId.toString());
  67. // assert
  68. expect(result).toBe(YDocStatus.NEW);
  69. });
  70. it('returns DRAFT when the newer YDocs exist', async() => {
  71. // arrange
  72. const yjsService = getYjsService();
  73. const pageId = new ObjectId();
  74. await Revision.insertMany([
  75. { pageId, body: '' },
  76. ]);
  77. const privateMdb = getPrivateMdbInstance(yjsService);
  78. await privateMdb.setTypedMeta(pageId.toString(), 'updatedAt', (new Date(2034, 1, 1)).getTime());
  79. // act
  80. const result = await yjsService.getYDocStatus(pageId.toString());
  81. // assert
  82. expect(result).toBe(YDocStatus.DRAFT);
  83. });
  84. it('returns SYNCED', async() => {
  85. // arrange
  86. const yjsService = getYjsService();
  87. const pageId = new ObjectId();
  88. await Revision.insertMany([
  89. { pageId, body: '', createdAt: new Date(2025, 1, 1) },
  90. ]);
  91. const privateMdb = getPrivateMdbInstance(yjsService);
  92. await privateMdb.setTypedMeta(pageId.toString(), 'updatedAt', (new Date(2025, 1, 1)).getTime());
  93. // act
  94. const result = await yjsService.getYDocStatus(pageId.toString());
  95. // assert
  96. expect(result).toBe(YDocStatus.SYNCED);
  97. });
  98. it('returns OUTDATED when the latest revision is newer than meta data', async() => {
  99. // arrange
  100. const yjsService = getYjsService();
  101. const pageId = new ObjectId();
  102. await Revision.insertMany([
  103. { pageId, body: '' },
  104. ]);
  105. const privateMdb = getPrivateMdbInstance(yjsService);
  106. await privateMdb.setTypedMeta(pageId.toString(), 'updatedAt', (new Date(2024, 1, 1)).getTime());
  107. // act
  108. const result = await yjsService.getYDocStatus(pageId.toString());
  109. // assert
  110. expect(result).toBe(YDocStatus.OUTDATED);
  111. });
  112. });
  113. });