|
@@ -4,17 +4,19 @@
|
|
|
|
|
|
|
|
const fs = require('fs')
|
|
const fs = require('fs')
|
|
|
const path = require('path')
|
|
const path = require('path')
|
|
|
-const _ = require('underscore')
|
|
|
|
|
|
|
+const _ = require('lodash')
|
|
|
const parseArgs = require('minimist')
|
|
const parseArgs = require('minimist')
|
|
|
const OError = require('@overleaf/o-error')
|
|
const OError = require('@overleaf/o-error')
|
|
|
const { waitForDb } = require('../app/src/infrastructure/mongodb')
|
|
const { waitForDb } = require('../app/src/infrastructure/mongodb')
|
|
|
const { User } = require('../app/src/models/User')
|
|
const { User } = require('../app/src/models/User')
|
|
|
const ProjectCreationHandler = require('../app/src/Features/Project/ProjectCreationHandler')
|
|
const ProjectCreationHandler = require('../app/src/Features/Project/ProjectCreationHandler')
|
|
|
const ProjectEntityUpdateHandler = require('../app/src/Features/Project/ProjectEntityUpdateHandler')
|
|
const ProjectEntityUpdateHandler = require('../app/src/Features/Project/ProjectEntityUpdateHandler')
|
|
|
|
|
+const ProjectEntityHandler = require('../app/src/Features/Project/ProjectEntityHandler')
|
|
|
|
|
+const EditorController = require('../app/src/Features/Editor/EditorController')
|
|
|
|
|
|
|
|
const argv = parseArgs(process.argv.slice(2), {
|
|
const argv = parseArgs(process.argv.slice(2), {
|
|
|
string: ['user-id', 'name'],
|
|
string: ['user-id', 'name'],
|
|
|
- boolean: ['old-history'],
|
|
|
|
|
|
|
+ boolean: ['old-history', 'random-content'],
|
|
|
unknown: function (arg) {
|
|
unknown: function (arg) {
|
|
|
console.error('unrecognised argument', arg)
|
|
console.error('unrecognised argument', arg)
|
|
|
process.exit(1)
|
|
process.exit(1)
|
|
@@ -26,6 +28,7 @@ console.log('argv', argv)
|
|
|
const userId = argv['user-id']
|
|
const userId = argv['user-id']
|
|
|
const projectName = argv.name || `Test Project ${new Date().toISOString()}`
|
|
const projectName = argv.name || `Test Project ${new Date().toISOString()}`
|
|
|
const oldHistory = argv['old-history']
|
|
const oldHistory = argv['old-history']
|
|
|
|
|
+const randomContent = argv['random-content']
|
|
|
|
|
|
|
|
console.log('userId', userId)
|
|
console.log('userId', userId)
|
|
|
|
|
|
|
@@ -100,6 +103,102 @@ async function _buildTemplate(templateName, userId, projectName) {
|
|
|
return output.split('\n')
|
|
return output.split('\n')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Create a project with some random content and file operations for testing history migrations
|
|
|
|
|
+// Unfortunately we cannot easily change the timestamps of the history entries, so everything
|
|
|
|
|
+// will be created at the same time.
|
|
|
|
|
+
|
|
|
|
|
+async function _pickRandomDoc(project) {
|
|
|
|
|
+ const result = await ProjectEntityHandler.promises.getAllDocs(project._id)
|
|
|
|
|
+ const keys = Object.keys(result)
|
|
|
|
|
+ if (keys.length === 0) {
|
|
|
|
|
+ return null
|
|
|
|
|
+ }
|
|
|
|
|
+ const filepath = _.sample(keys)
|
|
|
|
|
+ result[filepath].path = filepath
|
|
|
|
|
+ return result[filepath]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+let COUNTER = 0
|
|
|
|
|
+// format counter as a 6 digit zero padded number
|
|
|
|
|
+function nextId() {
|
|
|
|
|
+ return ('000000' + COUNTER++).slice(-6)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function _applyRandomDocUpdate(ownerId, project) {
|
|
|
|
|
+ const action = _.sample(['create', 'edit', 'delete', 'rename'])
|
|
|
|
|
+ switch (action) {
|
|
|
|
|
+ case 'create': // create a new doc
|
|
|
|
|
+ await EditorController.promises.upsertDocWithPath(
|
|
|
|
|
+ project._id,
|
|
|
|
|
+ `subdir/new-doc-${nextId()}.tex`,
|
|
|
|
|
+ [`This is a new doc ${new Date().toISOString()}`],
|
|
|
|
|
+ 'create-project-script',
|
|
|
|
|
+ ownerId
|
|
|
|
|
+ )
|
|
|
|
|
+ break
|
|
|
|
|
+ case 'edit': {
|
|
|
|
|
+ // edit an existing doc
|
|
|
|
|
+ const doc = await _pickRandomDoc(project)
|
|
|
|
|
+ if (!doc) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ // pick a random line and either insert or delete a character
|
|
|
|
|
+ const lines = doc.lines
|
|
|
|
|
+ const index = _.random(0, lines.length - 1)
|
|
|
|
|
+ let thisLine = lines[index]
|
|
|
|
|
+ const pos = _.random(0, thisLine.length - 1)
|
|
|
|
|
+ if (Math.random() > 0.5) {
|
|
|
|
|
+ // insert a character
|
|
|
|
|
+ thisLine = thisLine.slice(0, pos) + 'x' + thisLine.slice(pos)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // delete a character
|
|
|
|
|
+ thisLine = thisLine.slice(0, pos) + thisLine.slice(pos + 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ lines[index] = thisLine
|
|
|
|
|
+ await EditorController.promises.upsertDocWithPath(
|
|
|
|
|
+ project._id,
|
|
|
|
|
+ doc.path,
|
|
|
|
|
+ lines,
|
|
|
|
|
+ 'create-project-script',
|
|
|
|
|
+ ownerId
|
|
|
|
|
+ )
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ case 'delete': {
|
|
|
|
|
+ // delete an existing doc (but not the root doc)
|
|
|
|
|
+ const doc = await _pickRandomDoc(project)
|
|
|
|
|
+ if (!doc || doc.path === '/main.tex') {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await EditorController.promises.deleteEntityWithPath(
|
|
|
|
|
+ project._id,
|
|
|
|
|
+ doc.path,
|
|
|
|
|
+ ownerId,
|
|
|
|
|
+ 'create-project-script'
|
|
|
|
|
+ )
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ case 'rename': {
|
|
|
|
|
+ // rename an existing doc (but not the root doc)
|
|
|
|
|
+ const doc = await _pickRandomDoc(project)
|
|
|
|
|
+ if (!doc || doc.path === '/main.tex') {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const newName = `renamed-${nextId()}.tex`
|
|
|
|
|
+ await EditorController.promises.renameEntity(
|
|
|
|
|
+ project._id,
|
|
|
|
|
+ doc._id,
|
|
|
|
|
+ 'doc',
|
|
|
|
|
+ newName,
|
|
|
|
|
+ ownerId,
|
|
|
|
|
+ 'create-project-script'
|
|
|
|
|
+ )
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function createProject() {
|
|
async function createProject() {
|
|
|
await waitForDb()
|
|
await waitForDb()
|
|
|
const user = await User.findById(userId)
|
|
const user = await User.findById(userId)
|
|
@@ -113,6 +212,11 @@ async function createProject() {
|
|
|
attributes
|
|
attributes
|
|
|
)
|
|
)
|
|
|
await _addDefaultExampleProjectFiles(userId, projectName, project)
|
|
await _addDefaultExampleProjectFiles(userId, projectName, project)
|
|
|
|
|
+ if (randomContent) {
|
|
|
|
|
+ for (let i = 0; i < 1000; i++) {
|
|
|
|
|
+ await _applyRandomDocUpdate(userId, project)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
return project
|
|
return project
|
|
|
}
|
|
}
|
|
|
|
|
|