From e48f4cfcaccd54498d1cdc241d62ea509d0a101b Mon Sep 17 00:00:00 2001 From: nickbrowne Date: Wed, 12 Nov 2025 14:52:13 +1100 Subject: [PATCH 1/2] Change the versions API to always return ALL operations Previously, if there were not enough ops to "fill" an entire version, they would not be included in the returned array, now the incomplete version is included in the results. This does mean each version is no longer guaranteed to return the same result every time, but in practice this shouldn't be an issue. --- src/server/model.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/model.coffee b/src/server/model.coffee index dd2af24..3381cd6 100644 --- a/src/server/model.coffee +++ b/src/server/model.coffee @@ -513,6 +513,7 @@ module.exports = Model = (db, options) -> buildVersions = (callback, ops, results = []) -> if ops.length + lastOpV = ops[ops.length - 1].v versionOps = ops.slice(0, opBatchAmount) try @@ -521,7 +522,7 @@ module.exports = Model = (db, options) -> docTemplate.snapshot = type.apply(docTemplate.snapshot, op.op) docTemplate.meta = op.meta - if docTemplate.v % n is 0 + if docTemplate.v % n is 0 or op.v is lastOpV results.push({ v: docTemplate.v, type: docTemplate.type.name, From 8752b5d76beee7efd6d97a7d718c7ecaa7af4210 Mon Sep 17 00:00:00 2001 From: nickbrowne Date: Thu, 13 Nov 2025 11:07:54 +1100 Subject: [PATCH 2/2] Add some tests to cover the versions endpoint --- test/rest.coffee | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/test/rest.coffee b/test/rest.coffee index 1fe0182..d70d2ad 100644 --- a/test/rest.coffee +++ b/test/rest.coffee @@ -7,7 +7,7 @@ express = require 'express' server = require '../src/server' types = require '../src/types' -{makePassPart, newDocName, fetch} = require './helpers' +{applyOps, makePassPart, newDocName, fetch} = require './helpers' # Frontend tests module.exports = testCase @@ -84,6 +84,51 @@ module.exports = testCase test.deepEqual data, 'hi' test.done() + 'GET /doc/:name/versions returns [] for a document with no ops': (test) -> + @model.create @name, 'simple', => + fetch 'GET', @port, "/doc/#{@name}/versions", null, (res, data, headers) -> + test.strictEqual res.statusCode, 200 + test.strictEqual headers['content-type'], 'application/json' + test.deepEqual data, [] + test.done() + + 'GET /doc/:name/versions returns the list of applied ops': (test) -> + @model.create @name, 'text', => + applyOps @model, @name, 0, [ + [{p: 0, i: 'h'}] + [{p: 1, i: 'i'}] + [{p: 2, i: ' '}] + [{p: 3, i: 't'}] + [{p: 4, i: 'h'}] + [{p: 5, i: 'i'}] + [{p: 6, i: 's'}] + [{p: 7, i: ' '}] + [{p: 8, i: 'i'}] + [{p: 9, i: 's'}] + [{p: 10, i: ' '}] + [{p: 11, i: 'd'}] + [{p: 12, i: 'o'}] + [{p: 13, i: 'g'}] + ], (error, data) => + test.strictEqual error, null + + fetch 'GET', @port, "/doc/#{@name}/versions?every=10", null, (res, data, headers) -> + test.strictEqual res.statusCode, 200 + test.strictEqual headers['content-type'], 'application/json' + + version1 = data[0] + version2 = data[1] + + test.strictEqual version1.v, 10 + test.strictEqual version1.type, 'text' + test.strictEqual version1.snapshot, 'hi this is' + + test.strictEqual version2.v, 14 + test.strictEqual version2.type, 'text' + test.strictEqual version2.snapshot, 'hi this is dog' + + test.done() + 'PUT a document creates it': (test) -> fetch 'PUT', @port, "/doc/#{@name}", {type:'simple'}, (res, data) => test.strictEqual res.statusCode, 200