Просмотр исходного кода

Merge pull request #24977 from overleaf/bg-history-buffer-improve-tests

add more tests for chunk buffer in history-v1

GitOrigin-RevId: 3cfa2492efd67597a2782ca7a5671889a67049d5
Brian Gough 1 год назад
Родитель
Сommit
12b96e40a5
1 измененных файлов с 141 добавлено и 0 удалено
  1. 141 0
      services/history-v1/test/acceptance/js/storage/chunk_buffer.test.js

+ 141 - 0
services/history-v1/test/acceptance/js/storage/chunk_buffer.test.js

@@ -8,6 +8,11 @@ const {
   History,
   File,
   AddFileOperation,
+  EditFileOperation,
+  AddCommentOperation,
+  TextOperation,
+  Range,
+  TrackingProps,
   Change,
 } = require('overleaf-editor-core')
 const cleanup = require('./support/cleanup')
@@ -59,6 +64,9 @@ describe('chunk buffer', function () {
       })
 
       it('should load from chunk store and update cache on first access (cache miss)', async function () {
+        // Load the underlying chunk from the chunk store for verification
+        const storedChunk = await chunkStore.loadLatest(projectId)
+
         // First access should load from chunk store and populate cache
         const firstResult = await chunkBuffer.loadLatest(projectId)
 
@@ -67,6 +75,9 @@ describe('chunk buffer', function () {
         expect(firstResult.getStartVersion()).to.equal(1)
         expect(firstResult.getEndVersion()).to.equal(2)
 
+        // Verify the chunk is the same as the one in the store
+        expect(firstResult).to.deep.equal(storedChunk)
+
         // Verify that we got a cache miss metric
         expect(
           metrics.inc.calledWith('chunk_buffer.loadLatest', 1, {
@@ -85,6 +96,9 @@ describe('chunk buffer', function () {
         expect(secondResult.getStartVersion()).to.equal(1)
         expect(secondResult.getEndVersion()).to.equal(2)
 
+        // Verify the chunk is the same as the one in the store
+        expect(secondResult).to.deep.equal(storedChunk)
+
         // Verify that we got a cache hit metric
         expect(
           metrics.inc.calledWith('chunk_buffer.loadLatest', 1, {
@@ -99,6 +113,7 @@ describe('chunk buffer', function () {
         expect(secondResult.getEndVersion()).to.equal(
           firstResult.getEndVersion()
         )
+        expect(secondResult).to.deep.equal(firstResult)
       })
 
       it('should refresh the cache when chunk changes in the store', async function () {
@@ -129,12 +144,17 @@ describe('chunk buffer', function () {
         // Store the new chunk directly in the chunk store
         await chunkStore.create(projectId, newChunk)
 
+        // Load the underlying chunk from the chunk store for verification
+        const storedChunk = await chunkStore.loadLatest(projectId)
+
         // Access again - should detect the change and refresh cache
         const secondResult = await chunkBuffer.loadLatest(projectId)
 
         // Verify we got the updated chunk
         expect(secondResult.getStartVersion()).to.equal(2)
         expect(secondResult.getEndVersion()).to.equal(3)
+        // Verify that the chunk content is the same
+        expect(secondResult).to.deep.equal(storedChunk)
 
         // Verify that we got a cache miss metric (since the cached chunk was invalidated)
         expect(
@@ -145,6 +165,9 @@ describe('chunk buffer', function () {
       })
 
       it('should continue using cache when chunk in store has not changed', async function () {
+        // Load the underlying chunk from the chunk store for verification
+        const storedChunk = await chunkStore.loadLatest(projectId)
+
         // First access to load into cache
         await chunkBuffer.loadLatest(projectId)
 
@@ -157,6 +180,7 @@ describe('chunk buffer', function () {
         // Verify we got the same chunk
         expect(result.getStartVersion()).to.equal(1)
         expect(result.getEndVersion()).to.equal(2)
+        expect(result).to.deep.equal(storedChunk)
 
         // Verify that we got a cache hit metric
         expect(
@@ -167,11 +191,122 @@ describe('chunk buffer', function () {
       })
     })
 
+    it('should handle a chunk with metadata, comments and tracked changes', async function () {
+      // Create a snapshot and initial file
+      const snapshot = new Snapshot()
+      const initialFileOp = new AddFileOperation(
+        'test.tex',
+        File.fromString('Initial line.\\nSecond line.', {
+          meta1: 'abc',
+          meta2: 'def',
+        })
+      )
+      const initialChange = new Change([initialFileOp], new Date(), [])
+
+      // Add a comment
+      const commentOp = new AddCommentOperation(
+        'comment1',
+        [new Range(0, 7)] // Range for "Initial"
+      )
+      const commentChange = new Change(
+        [new EditFileOperation('test.tex', commentOp)],
+        new Date(),
+        []
+      )
+
+      // Tracked insert
+      const trackedInsertOp = new TextOperation()
+        .retain(14)
+        .insert('Hello', {
+          commentIds: ['comment1'],
+          tracking: TrackingProps.fromRaw({
+            ts: '2024-01-01T00:00:00.000Z',
+            type: 'insert',
+            userId: 'user1',
+          }),
+        })
+        .retain(12)
+      const insertChange = new Change(
+        [new EditFileOperation('test.tex', trackedInsertOp)],
+        new Date(),
+        []
+      )
+
+      // Tracked delete
+      const trackedDeleteOp = new TextOperation().retain(14, {
+        tracking: TrackingProps.fromRaw({
+          ts: '2024-01-01T00:00:00.000Z',
+          type: 'delete',
+          userId: 'user1',
+        }),
+      })
+      const deleteChange = new Change(
+        [new EditFileOperation('test.tex', trackedDeleteOp)],
+        new Date(),
+        []
+      )
+
+      // Combine changes into history and create chunk
+      const history = new History(snapshot, [
+        initialChange,
+        commentChange,
+        insertChange,
+        deleteChange,
+      ])
+      const chunk = new Chunk(history, 1) // Start version 0
+      // Store the chunk
+      await chunkStore.create(projectId, chunk)
+      // Clear the cache
+      await redisBackend.clearCache(projectId)
+      metrics.inc.resetHistory()
+
+      // Load the underlying chunk from the chunk store for verification
+      const storedChunk = await chunkStore.loadLatest(projectId)
+
+      // Load the chunk via buffer (cache miss)
+      const firstResult = await chunkBuffer.loadLatest(projectId)
+
+      // Verify chunk details
+      expect(firstResult.getStartVersion()).to.equal(1)
+      expect(firstResult.getEndVersion()).to.equal(5) // 4 changes
+      expect(firstResult.history.changes.length).to.equal(4)
+      expect(firstResult).to.deep.equal(storedChunk)
+
+      // Verify cache miss metric
+      expect(
+        metrics.inc.calledWith('chunk_buffer.loadLatest', 1, {
+          status: 'cache-miss',
+        })
+      ).to.be.true
+
+      // Reset metrics
+      metrics.inc.resetHistory()
+
+      // Second access should hit the cache
+      const secondResult = await chunkBuffer.loadLatest(projectId)
+
+      // Verify we got the same chunk
+      expect(secondResult.getStartVersion()).to.equal(1)
+      expect(secondResult.getEndVersion()).to.equal(5)
+      expect(secondResult.history.changes.length).to.equal(4)
+      expect(secondResult).to.deep.equal(storedChunk)
+
+      // Verify cache hit metric
+      expect(
+        metrics.inc.calledWith('chunk_buffer.loadLatest', 1, {
+          status: 'cache-hit',
+        })
+      ).to.be.true
+    })
+
     describe('with an empty project', function () {
       it('should handle a case with empty chunks (no changes)', async function () {
         // Clear the cache
         await redisBackend.clearCache(projectId)
 
+        // Load the underlying chunk from the chunk store for verification
+        const storedChunk = await chunkStore.loadLatest(projectId)
+
         // Load the initial empty chunk via buffer
         const result = await chunkBuffer.loadLatest(projectId)
 
@@ -180,6 +315,9 @@ describe('chunk buffer', function () {
         expect(result.getEndVersion()).to.equal(0) // Start equals end for empty chunks
         expect(result.history.changes.length).to.equal(0)
 
+        // Verify that the chunk is the same as the one in the store
+        expect(result).to.deep.equal(storedChunk)
+
         // Verify cache miss metric
         expect(
           metrics.inc.calledWith('chunk_buffer.loadLatest', 1, {
@@ -198,6 +336,9 @@ describe('chunk buffer', function () {
         expect(secondResult.getEndVersion()).to.equal(0)
         expect(secondResult.history.changes.length).to.equal(0)
 
+        // Verify that the chunk is the same as the one in the store
+        expect(secondResult).to.deep.equal(storedChunk)
+
         // Verify cache hit metric
         expect(
           metrics.inc.calledWith('chunk_buffer.loadLatest', 1, {