mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 06:33:55 +01:00
Merge pull request #29928 from overleaf/ar-last-infrastructure-conversions
[web] last infrastructure conversions GitOrigin-RevId: ad1aff9b7df0610ed0303157d9e2c8032f32c02b
This commit is contained in:
@@ -2,7 +2,7 @@ import { expect } from 'chai'
|
||||
import UserHelper from './helpers/User.mjs'
|
||||
import logger from '@overleaf/logger'
|
||||
import sinon from 'sinon'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import Features from '../../../app/src/infrastructure/Features.mjs'
|
||||
|
||||
const User = UserHelper.promises
|
||||
|
||||
@@ -2,7 +2,7 @@ import { exec } from 'node:child_process'
|
||||
import { promisify } from 'node:util'
|
||||
import { expect } from 'chai'
|
||||
import logger from '@overleaf/logger'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import UserHelper from './helpers/User.mjs'
|
||||
import { renderObjectId } from '@overleaf/mongo-utils/batchedUpdate.js'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { promisify } from 'node:util'
|
||||
import { exec } from 'node:child_process'
|
||||
import logger from '@overleaf/logger'
|
||||
|
||||
@@ -3,7 +3,7 @@ import { promisify } from 'node:util'
|
||||
import { expect } from 'chai'
|
||||
import logger from '@overleaf/logger'
|
||||
import { filterOutput } from './helpers/settings.mjs'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
|
||||
const DUMMY_NAME = 'unknown.tex'
|
||||
const DUMMY_TIME = new Date('2021-04-12T00:00:00.000Z')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { expect } from 'chai'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
|
||||
describe('BatchedUpdateTests', function () {
|
||||
it('can handle non linear insert order', async function () {
|
||||
@@ -24,7 +24,7 @@ describe('BatchedUpdateTests', function () {
|
||||
spawnSync(process.argv0, [
|
||||
'--input-type=module',
|
||||
'-e',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.js"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.mjs"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
])
|
||||
|
||||
await expect(
|
||||
@@ -56,7 +56,7 @@ describe('BatchedUpdateTests', function () {
|
||||
[
|
||||
'--input-type=module',
|
||||
'-e',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.js"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.mjs"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
],
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
@@ -93,7 +93,7 @@ describe('BatchedUpdateTests', function () {
|
||||
[
|
||||
'--input-type=module',
|
||||
'-e',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.js"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.mjs"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
],
|
||||
{
|
||||
encoding: 'utf-8',
|
||||
@@ -141,7 +141,7 @@ describe('BatchedUpdateTests', function () {
|
||||
[
|
||||
'--input-type=module',
|
||||
'-e',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.js"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
'import { batchedUpdateWithResultHandling } from "@overleaf/mongo-utils/batchedUpdate.js"; import { db } from "./app/src/infrastructure/mongodb.mjs"; batchedUpdateWithResultHandling(db.systemmessages, { content: { $ne: "42" }}, { $set: { content: "42" } })',
|
||||
],
|
||||
{
|
||||
env: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { expect } from 'chai'
|
||||
import Settings from '@overleaf/settings'
|
||||
import UserHelper from './helpers/User.mjs'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { exec } from 'node:child_process'
|
||||
import { promisify } from 'node:util'
|
||||
import { expect } from 'chai'
|
||||
import logger from '@overleaf/logger'
|
||||
import { ObjectId, db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { ObjectId, db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import fs from 'node:fs/promises'
|
||||
import UserHelper from './helpers/User.mjs'
|
||||
import UserGetter from '../../../app/src/Features/User/UserGetter.mjs'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect } from 'chai'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { exec } from 'node:child_process'
|
||||
|
||||
describe('ConvertEmailConfirmedAtToDates', function () {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect } from 'chai'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { exec } from 'node:child_process'
|
||||
|
||||
describe('ConvertSplitTestAssignedAtToDates', function () {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { promisify } from 'node:util'
|
||||
import { expect } from 'chai'
|
||||
import logger from '@overleaf/logger'
|
||||
import { filterOutput } from './helpers/settings.mjs'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
|
||||
const ONE_DAY_IN_S = 60 * 60 * 24
|
||||
const BATCH_SIZE = 3
|
||||
|
||||
@@ -6,7 +6,7 @@ import request from './helpers/request.js'
|
||||
import async from 'async'
|
||||
import { expect } from 'chai'
|
||||
import settings from '@overleaf/settings'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import Features from '../../../app/src/infrastructure/Features.mjs'
|
||||
import MockDocstoreApiClass from './mocks/MockDocstoreApi.mjs'
|
||||
import MockChatApiClass from './mocks/MockChatApi.mjs'
|
||||
|
||||
@@ -2,7 +2,7 @@ import Settings from '@overleaf/settings'
|
||||
import { expect } from 'chai'
|
||||
import UserHelper from './helpers/User.mjs'
|
||||
import MockHaveIBeenPwnedApiClass from './mocks/MockHaveIBeenPwnedApi.mjs'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import MetricsHelper from './helpers/metrics.mjs'
|
||||
|
||||
const User = UserHelper.promises
|
||||
|
||||
@@ -4,7 +4,7 @@ import timekeeper from 'timekeeper'
|
||||
import Settings from '@overleaf/settings'
|
||||
import UserHelper from './helpers/User.mjs'
|
||||
import express from 'express'
|
||||
import { plainTextResponse } from '../../../app/src/infrastructure/Response.js'
|
||||
import { plainTextResponse } from '../../../app/src/infrastructure/Response.mjs'
|
||||
|
||||
const User = UserHelper.promises
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { promisify } from 'node:util'
|
||||
import { expect } from 'chai'
|
||||
import logger from '@overleaf/logger'
|
||||
import { filterOutput } from './helpers/settings.mjs'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
|
||||
const lastUpdated = new Date(42)
|
||||
const lastUpdatedBy = new ObjectId()
|
||||
|
||||
@@ -2,7 +2,7 @@ import { expect } from 'chai'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import mongoose from 'mongoose'
|
||||
import { User as UserModel } from '../../../app/src/models/User.mjs'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import MongoHelpers from '../../../app/src/Features/Helpers/Mongo.mjs'
|
||||
import UserHelper from './helpers/User.mjs'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from 'chai'
|
||||
import UserHelper from './helpers/UserHelper.mjs'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
|
||||
describe('PasswordReset', function () {
|
||||
let email, response, user, userHelper, token, emailQuery
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
|
||||
import LockManager from '../../../app/src/infrastructure/LockManager.js'
|
||||
import LockManager from '../../../app/src/infrastructure/LockManager.mjs'
|
||||
|
||||
import ProjectCreationHandler from '../../../app/src/Features/Project/ProjectCreationHandler.mjs'
|
||||
import ProjectGetter from '../../../app/src/Features/Project/ProjectGetter.mjs'
|
||||
|
||||
@@ -3,7 +3,7 @@ import { promisify } from 'node:util'
|
||||
import { expect } from 'chai'
|
||||
import logger from '@overleaf/logger'
|
||||
import { filterOutput } from './helpers/settings.mjs'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { renderObjectId } from '@overleaf/mongo-utils/batchedUpdate.js'
|
||||
|
||||
const BATCH_SIZE = 100
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { promisify } from 'node:util'
|
||||
import { exec } from 'node:child_process'
|
||||
import logger from '@overleaf/logger'
|
||||
|
||||
@@ -3,7 +3,7 @@ import async from 'async'
|
||||
import User from './helpers/User.mjs'
|
||||
import request from './helpers/request.js'
|
||||
import settings from '@overleaf/settings'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.js'
|
||||
import { db } from '../../../app/src/infrastructure/mongodb.mjs'
|
||||
import expectErrorResponse from './helpers/expectErrorResponse.mjs'
|
||||
import logger from '@overleaf/logger'
|
||||
import sinon from 'sinon'
|
||||
|
||||
@@ -4,12 +4,12 @@ import MongoHelper from './MongoHelper.mjs'
|
||||
import RedisHelper from './RedisHelper.mjs'
|
||||
import Settings from '@overleaf/settings'
|
||||
import MockReCAPTCHAApi from '../mocks/MockReCaptchaApi.mjs'
|
||||
import { gracefulShutdown } from '../../../../app/src/infrastructure/GracefulShutdown.js'
|
||||
import { gracefulShutdown } from '../../../../app/src/infrastructure/GracefulShutdown.mjs'
|
||||
import Server from '../../../../app/src/infrastructure/Server.mjs'
|
||||
import { injectRouteAfter } from './injectRoute.mjs'
|
||||
import SplitTestHandler from '../../../../app/src/Features/SplitTests/SplitTestHandler.mjs'
|
||||
import SplitTestSessionHandler from '../../../../app/src/Features/SplitTests/SplitTestSessionHandler.mjs'
|
||||
import Modules from '../../../../app/src/infrastructure/Modules.js'
|
||||
import Modules from '../../../../app/src/infrastructure/Modules.mjs'
|
||||
|
||||
const app = Server.app
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
connectionPromise,
|
||||
cleanupTestDatabase,
|
||||
dropTestDatabase,
|
||||
} from '../../../../app/src/infrastructure/mongodb.js'
|
||||
} from '../../../../app/src/infrastructure/mongodb.mjs'
|
||||
import Settings from '@overleaf/settings'
|
||||
import { promisify } from 'node:util'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import RedisWrapper from '../../../../app/src/infrastructure/RedisWrapper.js'
|
||||
import RedisWrapper from '../../../../app/src/infrastructure/RedisWrapper.mjs'
|
||||
const client = RedisWrapper.client('ratelimiter')
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { db, ObjectId } from '../../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { expect } from 'chai'
|
||||
import { callbackifyClass } from '@overleaf/promise-utils'
|
||||
import SubscriptionUpdater from '../../../../app/src/Features/Subscription/SubscriptionUpdater.mjs'
|
||||
@@ -6,7 +6,7 @@ import PermissionsManager from '../../../../app/src/Features/Authorization/Permi
|
||||
import SSOConfigManager from '../../../../modules/group-settings/app/src/sso/SSOConfigManager.mjs'
|
||||
import { Subscription as SubscriptionModel } from '../../../../app/src/models/Subscription.mjs'
|
||||
import { DeletedSubscription as DeletedSubscriptionModel } from '../../../../app/src/models/DeletedSubscription.mjs'
|
||||
import Modules from '../../../../app/src/infrastructure/Modules.js'
|
||||
import Modules from '../../../../app/src/infrastructure/Modules.mjs'
|
||||
|
||||
class PromisifiedSubscription {
|
||||
constructor(options = {}) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import OError from '@overleaf/o-error'
|
||||
import request from './request.js'
|
||||
import settings from '@overleaf/settings'
|
||||
import { db, ObjectId } from '../../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../../app/src/infrastructure/mongodb.mjs'
|
||||
import { User as UserModel } from '../../../../app/src/models/User.mjs'
|
||||
import UserUpdater from '../../../../app/src/Features/User/UserUpdater.mjs'
|
||||
import AuthenticationManager from '../../../../app/src/Features/Authentication/AuthenticationManager.mjs'
|
||||
|
||||
@@ -13,7 +13,7 @@ import { UserAuditLogEntry } from '../../../../app/src/models/UserAuditLogEntry.
|
||||
|
||||
// Import the rate limiter so we can clear it between tests
|
||||
|
||||
import { RateLimiter } from '../../../../app/src/infrastructure/RateLimiter.js'
|
||||
import { RateLimiter } from '../../../../app/src/infrastructure/RateLimiter.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AbstractMockApi from './AbstractMockApi.mjs'
|
||||
import { plainTextResponse } from '../../../../app/src/infrastructure/Response.js'
|
||||
import { plainTextResponse } from '../../../../app/src/infrastructure/Response.mjs'
|
||||
|
||||
class MockClsiApi extends AbstractMockApi {
|
||||
static compile(req, res) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { db, ObjectId } from '../../../../app/src/infrastructure/mongodb.js'
|
||||
import { db, ObjectId } from '../../../../app/src/infrastructure/mongodb.mjs'
|
||||
import AbstractMockApi from './AbstractMockApi.mjs'
|
||||
|
||||
class MockDocstoreApi extends AbstractMockApi {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AbstractMockApi from './AbstractMockApi.mjs'
|
||||
import { plainTextResponse } from '../../../../app/src/infrastructure/Response.js'
|
||||
import { plainTextResponse } from '../../../../app/src/infrastructure/Response.mjs'
|
||||
|
||||
class MockHaveIBeenPwnedApi extends AbstractMockApi {
|
||||
reset() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import AbstractMockApi from './AbstractMockApi.mjs'
|
||||
import _ from 'lodash'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import { plainTextResponse } from '../../../../app/src/infrastructure/Response.js'
|
||||
import { plainTextResponse } from '../../../../app/src/infrastructure/Response.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AbstractMockApi from './AbstractMockApi.mjs'
|
||||
import SubscriptionController from '../../../../app/src/Features/Subscription/SubscriptionController.mjs'
|
||||
import { xmlResponse } from '../../../../app/src/infrastructure/Response.js'
|
||||
import { xmlResponse } from '../../../../app/src/infrastructure/Response.mjs'
|
||||
|
||||
class MockRecurlyApi extends AbstractMockApi {
|
||||
reset() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { EventEmitter } from 'node:events'
|
||||
import {
|
||||
zipAttachment,
|
||||
prepareZipAttachment,
|
||||
} from '../../../../app/src/infrastructure/Response.js'
|
||||
} from '../../../../app/src/infrastructure/Response.mjs'
|
||||
import { z } from 'zod'
|
||||
|
||||
class MockV1HistoryApi extends AbstractMockApi {
|
||||
|
||||
@@ -2,7 +2,7 @@ import Settings from '@overleaf/settings'
|
||||
import {
|
||||
overleafLoginRateLimiter,
|
||||
openProjectRateLimiter,
|
||||
} from '../../../../app/src/infrastructure/RateLimiter.js'
|
||||
} from '../../../../app/src/infrastructure/RateLimiter.mjs'
|
||||
import LoginRateLimiter from '../../../../app/src/Features/Security/LoginRateLimiter.mjs'
|
||||
|
||||
async function clearLoginRateLimit() {
|
||||
|
||||
@@ -11,7 +11,7 @@ const MODULE_PATH = path.join(
|
||||
import.meta.dirname,
|
||||
'../../../../app/src/Features/Analytics/AnalyticsManager'
|
||||
)
|
||||
vi.mock('../../../../app/src/infrastructure/Metrics.js', () => ({
|
||||
vi.mock('../../../../app/src/infrastructure/Metrics.mjs', () => ({
|
||||
default: {
|
||||
analyticsQueue: {
|
||||
inc: vi.fn(),
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('ChatController', function () {
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/Modules.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/Modules.mjs', () => ({
|
||||
default: ctx.Modules,
|
||||
}))
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ describe('CompileController', function () {
|
||||
)
|
||||
|
||||
vi.doMock(
|
||||
'../../../../app/src/infrastructure/RateLimiter.js',
|
||||
'../../../../app/src/infrastructure/RateLimiter.mjs',
|
||||
() => ctx.RateLimiter
|
||||
)
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('CompileManager', function () {
|
||||
default: (ctx.ClsiManager = { promises: {} }),
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/RateLimiter.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/RateLimiter.mjs', () => ({
|
||||
RateLimiter: sinon.stub().returns(ctx.rateLimiter),
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { expect } from 'vitest'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import CooldownManager from '../../../../app/src/Features/Cooldown/CooldownManager.mjs'
|
||||
import { cleanupTestRedis } from '../../../../app/src/infrastructure/RedisWrapper.js'
|
||||
import RedisWrapper from '../../../../app/src/infrastructure/RedisWrapper.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
describe('CooldownManager', function () {
|
||||
beforeEach(cleanupTestRedis)
|
||||
beforeEach(RedisWrapper.cleanupTestRedis)
|
||||
|
||||
beforeEach(function (ctx) {
|
||||
ctx.project1Id = new ObjectId().toString()
|
||||
|
||||
@@ -121,7 +121,7 @@ describe('DocumentController', function () {
|
||||
default: ctx.ChatApiHandler,
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/Modules.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/Modules.mjs', () => ({
|
||||
default: ctx.Modules,
|
||||
}))
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ describe('EditorHttpController', function () {
|
||||
default: ctx.SessionManager,
|
||||
})
|
||||
)
|
||||
vi.doMock('../../../../app/src/infrastructure/FileWriter.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/FileWriter.mjs', () => ({
|
||||
default: ctx.FileWriter,
|
||||
}))
|
||||
vi.doMock(
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('HistoryController', function () {
|
||||
default: {},
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/mongodb.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/mongodb.mjs', () => ({
|
||||
default: { ObjectId },
|
||||
}))
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
cleanupTestDatabase,
|
||||
db,
|
||||
waitForDb,
|
||||
} from '../../../../app/src/infrastructure/mongodb.js'
|
||||
} from '../../../../app/src/infrastructure/mongodb.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('RestoreManager', function () {
|
||||
})
|
||||
)
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/Metrics.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/Metrics.mjs', () => ({
|
||||
default: {
|
||||
revertFileDurationSeconds: {
|
||||
startTimer: sinon.stub().returns(sinon.stub()),
|
||||
|
||||
@@ -4,7 +4,7 @@ import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
||||
import {
|
||||
connectionPromise,
|
||||
cleanupTestDatabase,
|
||||
} from '../../../../app/src/infrastructure/mongodb.js'
|
||||
} from '../../../../app/src/infrastructure/mongodb.mjs'
|
||||
import OneTimeTokenHandler from '../../../../app/src/Features/Security/OneTimeTokenHandler.mjs'
|
||||
|
||||
vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('LearnedWordsManager', function () {
|
||||
},
|
||||
}
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/mongodb.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/mongodb.mjs', () => ({
|
||||
default: { db: ctx.db },
|
||||
}))
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ describe('UserMembershipController', () => {
|
||||
},
|
||||
},
|
||||
}
|
||||
vi.doMock('../../../../app/src/infrastructure/Modules.js', () => ({
|
||||
vi.doMock('../../../../app/src/infrastructure/Modules.mjs', () => ({
|
||||
default: ctx.Modules,
|
||||
}))
|
||||
|
||||
|
||||
+20
-22
@@ -1,18 +1,18 @@
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const modulePath = '../../../../app/src/infrastructure/HttpPermissionsPolicy.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const HttpPermissionsPolicyMiddleware = require('../../../../app/src/infrastructure/HttpPermissionsPolicy')
|
||||
const MockRequest = require('../helpers/MockRequest')
|
||||
const MockResponse = require('../helpers/MockResponse')
|
||||
import { expect, vi } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import HttpPermissionsPolicyMiddleware from '../../../../app/src/infrastructure/HttpPermissionsPolicy.mjs'
|
||||
import MockRequest from '../helpers/MockRequestVitest.mjs'
|
||||
import MockResponse from '../helpers/MockResponseVitest.mjs'
|
||||
const modulePath =
|
||||
'../../../../app/src/infrastructure/HttpPermissionsPolicy.mjs'
|
||||
|
||||
describe('HttpPermissionsPolicy', function () {
|
||||
this.beforeEach(function () {
|
||||
this.next = sinon.stub()
|
||||
this.HttpPermissionsPolicy = SandboxedModule.require(modulePath, {})
|
||||
this.path = '/foo/bar'
|
||||
this.req = new MockRequest()
|
||||
return (this.res = new MockResponse())
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.next = sinon.stub()
|
||||
ctx.HttpPermissionsPolicy = (await import(modulePath)).default
|
||||
ctx.path = '/foo/bar'
|
||||
ctx.req = new MockRequest(vi)
|
||||
ctx.res = new MockResponse(vi)
|
||||
})
|
||||
|
||||
describe('when a single blocked policy element is provided', function () {
|
||||
@@ -23,9 +23,7 @@ describe('HttpPermissionsPolicy', function () {
|
||||
const httpPermissionsMiddleware = new HttpPermissionsPolicyMiddleware(
|
||||
policy
|
||||
)
|
||||
return expect(httpPermissionsMiddleware.policy).to.equal(
|
||||
'accelerometer=()'
|
||||
)
|
||||
expect(httpPermissionsMiddleware.policy).to.equal('accelerometer=()')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -37,7 +35,7 @@ describe('HttpPermissionsPolicy', function () {
|
||||
const httpPermissionsMiddleware = new HttpPermissionsPolicyMiddleware(
|
||||
policy
|
||||
)
|
||||
return expect(httpPermissionsMiddleware.policy).to.equal('camera=(self)')
|
||||
expect(httpPermissionsMiddleware.policy).to.equal('camera=(self)')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -50,7 +48,7 @@ describe('HttpPermissionsPolicy', function () {
|
||||
const httpPermissionsMiddleware = new HttpPermissionsPolicyMiddleware(
|
||||
policy
|
||||
)
|
||||
return expect(httpPermissionsMiddleware.policy).to.equal(
|
||||
expect(httpPermissionsMiddleware.policy).to.equal(
|
||||
'usb=(), hid=(), camera=(self https://example.com), fullscreen=(self)'
|
||||
)
|
||||
})
|
||||
@@ -62,7 +60,7 @@ describe('HttpPermissionsPolicy', function () {
|
||||
blocked: ['usb'],
|
||||
allowed: { usb: 'self' },
|
||||
}
|
||||
return expect(() => new HttpPermissionsPolicyMiddleware(policy)).to.throw(
|
||||
expect(() => new HttpPermissionsPolicyMiddleware(policy)).to.throw(
|
||||
'Invalid Permissions-Policy header configuration'
|
||||
)
|
||||
})
|
||||
@@ -74,7 +72,7 @@ describe('HttpPermissionsPolicy', function () {
|
||||
blocked: ['usb'],
|
||||
allowed: { camera: '' },
|
||||
}
|
||||
return expect(() => new HttpPermissionsPolicyMiddleware(policy)).to.throw(
|
||||
expect(() => new HttpPermissionsPolicyMiddleware(policy)).to.throw(
|
||||
'Invalid Permissions-Policy header configuration'
|
||||
)
|
||||
})
|
||||
@@ -89,7 +87,7 @@ describe('HttpPermissionsPolicy', function () {
|
||||
const httpPermissionsMiddleware = new HttpPermissionsPolicyMiddleware(
|
||||
policy
|
||||
)
|
||||
return expect(httpPermissionsMiddleware.policy).to.equal('camera=(self)')
|
||||
expect(httpPermissionsMiddleware.policy).to.equal('camera=(self)')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -102,7 +100,7 @@ describe('HttpPermissionsPolicy', function () {
|
||||
const httpPermissionsMiddleware = new HttpPermissionsPolicyMiddleware(
|
||||
policy
|
||||
)
|
||||
return expect(httpPermissionsMiddleware.policy).to.equal('usb=()')
|
||||
expect(httpPermissionsMiddleware.policy).to.equal('usb=()')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,58 +0,0 @@
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require('sinon')
|
||||
const assert = require('assert')
|
||||
const path = require('path')
|
||||
const modulePath = path.join(
|
||||
__dirname,
|
||||
'../../../../../app/src/infrastructure/LockManager.js'
|
||||
)
|
||||
const lockKey = `lock:web:{${5678}}`
|
||||
const lockValue = '123456'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe('LockManager - releasing the lock', function () {
|
||||
const deleteStub = sinon.stub().callsArgWith(4)
|
||||
const mocks = {
|
||||
'@overleaf/settings': {
|
||||
redis: {},
|
||||
lockManager: {
|
||||
lockTestInterval: 50,
|
||||
maxTestInterval: 1000,
|
||||
maxLockWaitTime: 10000,
|
||||
redisLockExpiry: 30,
|
||||
slowExecutionThreshold: 5000,
|
||||
},
|
||||
},
|
||||
'@overleaf/metrics': {},
|
||||
'./RedisWrapper': {
|
||||
client() {
|
||||
return {
|
||||
auth() {},
|
||||
eval: deleteStub,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const LockManager = SandboxedModule.require(modulePath, { requires: mocks })
|
||||
LockManager.unlockScript = 'this is the unlock script'
|
||||
|
||||
it('should put a all data into memory', function (done) {
|
||||
return LockManager._releaseLock(lockKey, lockValue, () => {
|
||||
deleteStub
|
||||
.calledWith(LockManager.unlockScript, 1, lockKey, lockValue)
|
||||
.should.equal(true)
|
||||
return done()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import sinon from 'sinon'
|
||||
import path from 'node:path'
|
||||
import { vi } from 'vitest'
|
||||
const modulePath = path.join(
|
||||
import.meta.dirname,
|
||||
'../../../../../app/src/infrastructure/LockManager.mjs'
|
||||
)
|
||||
const lockKey = `lock:web:{${5678}}`
|
||||
const lockValue = '123456'
|
||||
|
||||
describe('LockManager - releasing the lock', function () {
|
||||
let LockManager
|
||||
const deleteStub = sinon.stub().callsArgWith(4)
|
||||
beforeEach(async function () {
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: {
|
||||
redis: {},
|
||||
lockManager: {
|
||||
lockTestInterval: 50,
|
||||
maxTestInterval: 1000,
|
||||
maxLockWaitTime: 10000,
|
||||
redisLockExpiry: 30,
|
||||
slowExecutionThreshold: 5000,
|
||||
},
|
||||
},
|
||||
}))
|
||||
vi.doMock('@overleaf/metrics', () => ({}))
|
||||
vi.doMock('../../../../../app/src/infrastructure/RedisWrapper', () => ({
|
||||
default: {
|
||||
client() {
|
||||
return {
|
||||
auth() {},
|
||||
eval: deleteStub,
|
||||
}
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
LockManager = (await import(modulePath)).default
|
||||
LockManager.unlockScript = 'this is the unlock script'
|
||||
})
|
||||
|
||||
it('should put all data into memory', async function () {
|
||||
await new Promise(resolve => {
|
||||
return LockManager._releaseLock(lockKey, lockValue, () => {
|
||||
deleteStub
|
||||
.calledWith(LockManager.unlockScript, 1, lockKey, lockValue)
|
||||
.should.equal(true)
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,209 @@
|
||||
import { vi } from 'vitest'
|
||||
/* eslint-disable
|
||||
n/handle-callback-err,
|
||||
max-len,
|
||||
no-return-assign,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import sinon from 'sinon'
|
||||
|
||||
import path from 'node:path'
|
||||
const modulePath = path.join(
|
||||
import.meta.dirname,
|
||||
'../../../../../app/src/infrastructure/LockManager.mjs'
|
||||
)
|
||||
|
||||
describe('LockManager - getting the lock', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
vi.doMock('../../../../../app/src/infrastructure/RedisWrapper', () => ({
|
||||
default: {
|
||||
client() {
|
||||
return { auth() {} }
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: {
|
||||
redis: {},
|
||||
lockManager: {
|
||||
lockTestInterval: 50,
|
||||
maxTestInterval: 1000,
|
||||
maxLockWaitTime: 10000,
|
||||
redisLockExpiry: 30,
|
||||
slowExecutionThreshold: 5000,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/metrics', () => ({
|
||||
default: {
|
||||
inc() {},
|
||||
gauge() {},
|
||||
},
|
||||
}))
|
||||
|
||||
ctx.LockManager = (await import(modulePath)).default
|
||||
|
||||
ctx.callback = sinon.stub()
|
||||
ctx.key = 'lock:web:lockName:project-id}'
|
||||
return (ctx.namespace = 'lockName')
|
||||
})
|
||||
|
||||
describe('when the lock is not set', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.LockManager._tryLock = sinon.stub().yields(null, true)
|
||||
return ctx.LockManager._getLock(ctx.key, ctx.namespace, (...args) => {
|
||||
ctx.callback(...Array.from(args || []))
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should try to get the lock', function (ctx) {
|
||||
return ctx.LockManager._tryLock
|
||||
.calledWith(ctx.key, ctx.namespace)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should only need to try once', function (ctx) {
|
||||
return ctx.LockManager._tryLock.callCount.should.equal(1)
|
||||
})
|
||||
|
||||
it('should return the callback', function (ctx) {
|
||||
return ctx.callback.calledWith(null).should.equal(true)
|
||||
})
|
||||
|
||||
it('should clear the lock queue', function (ctx) {
|
||||
ctx.LockManager._lockQueuesSize().should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the lock is initially set', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
const startTime = Date.now()
|
||||
let tries = 0
|
||||
ctx.LockManager.LOCK_TEST_INTERVAL = 5
|
||||
ctx.LockManager._tryLock = function (key, namespace, callback) {
|
||||
if (callback == null) {
|
||||
callback = function () {}
|
||||
}
|
||||
if (Date.now() - startTime < 20 || tries < 2) {
|
||||
tries = tries + 1
|
||||
return callback(null, false)
|
||||
} else {
|
||||
return callback(null, true)
|
||||
}
|
||||
}
|
||||
sinon.spy(ctx.LockManager, '_tryLock')
|
||||
|
||||
return ctx.LockManager._getLock(ctx.key, ctx.namespace, (...args) => {
|
||||
ctx.callback(...Array.from(args || []))
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should call tryLock multiple times until free', function (ctx) {
|
||||
return (ctx.LockManager._tryLock.callCount > 1).should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the callback', function (ctx) {
|
||||
return ctx.callback.calledWith(null).should.equal(true)
|
||||
})
|
||||
|
||||
it('should clear the lock queue', function (ctx) {
|
||||
ctx.LockManager._lockQueuesSize().should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the lock times out', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
const time = Date.now()
|
||||
ctx.LockManager.LOCK_TEST_INTERVAL = 1
|
||||
ctx.LockManager.MAX_LOCK_WAIT_TIME = 5
|
||||
ctx.LockManager._tryLock = sinon.stub().yields(null, false)
|
||||
return ctx.LockManager._getLock(ctx.key, ctx.namespace, (...args) => {
|
||||
ctx.callback(...Array.from(args || []))
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the callback with an error', function (ctx) {
|
||||
ctx.callback.should.have.been.calledWith(
|
||||
sinon.match.instanceOf(Error).and(sinon.match.has('message', 'Timeout'))
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there are multiple requests for the same lock', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
let locked = false
|
||||
ctx.results = []
|
||||
ctx.LockManager.LOCK_TEST_INTERVAL = 1
|
||||
ctx.LockManager._tryLock = function (key, namespace, callback) {
|
||||
if (callback == null) {
|
||||
callback = function () {}
|
||||
}
|
||||
if (locked) {
|
||||
return callback(null, false)
|
||||
} else {
|
||||
locked = true // simulate getting the lock
|
||||
return callback(null, true)
|
||||
}
|
||||
}
|
||||
// Start ten lock requests in order at 1ms 2ms 3ms...
|
||||
// with them randomly holding the lock for 0-10ms.
|
||||
// Use predefined values for the random delay to make the test
|
||||
// deterministic.
|
||||
const randomDelays = [5, 4, 1, 8, 6, 8, 3, 4, 2, 4]
|
||||
let startTime = 0
|
||||
return Array.from(randomDelays).map((randomDelay, i) =>
|
||||
((randomDelay, i) => {
|
||||
startTime += 1
|
||||
return setTimeout(() => {
|
||||
// changing the next line to the old method of LockManager._getLockByPolling
|
||||
// should give results in a random order and cause the test to fail.
|
||||
return ctx.LockManager._getLock(
|
||||
ctx.key,
|
||||
ctx.namespace,
|
||||
(...args) => {
|
||||
setTimeout(
|
||||
() => (locked = false), // release the lock after a random amount of time
|
||||
randomDelay
|
||||
)
|
||||
ctx.results.push(i)
|
||||
if (ctx.results.length === 10) {
|
||||
return resolve()
|
||||
}
|
||||
}
|
||||
)
|
||||
}, startTime)
|
||||
})(randomDelay, i)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should process the requests in order', function (ctx) {
|
||||
return ctx.results.should.deep.equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
})
|
||||
|
||||
it('should clear the lock queue', function (ctx) {
|
||||
ctx.LockManager._lockQueuesSize().should.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,194 +0,0 @@
|
||||
/* eslint-disable
|
||||
n/handle-callback-err,
|
||||
max-len,
|
||||
no-return-assign,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require('sinon')
|
||||
const path = require('path')
|
||||
const modulePath = path.join(
|
||||
__dirname,
|
||||
'../../../../../app/src/infrastructure/LockManager.js'
|
||||
)
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe('LockManager - getting the lock', function () {
|
||||
beforeEach(function () {
|
||||
this.LockManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'./RedisWrapper': {
|
||||
client() {
|
||||
return { auth() {} }
|
||||
},
|
||||
},
|
||||
'@overleaf/settings': {
|
||||
redis: {},
|
||||
lockManager: {
|
||||
lockTestInterval: 50,
|
||||
maxTestInterval: 1000,
|
||||
maxLockWaitTime: 10000,
|
||||
redisLockExpiry: 30,
|
||||
slowExecutionThreshold: 5000,
|
||||
},
|
||||
},
|
||||
'@overleaf/metrics': {
|
||||
inc() {},
|
||||
gauge() {},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
this.callback = sinon.stub()
|
||||
this.key = 'lock:web:lockName:project-id}'
|
||||
return (this.namespace = 'lockName')
|
||||
})
|
||||
|
||||
describe('when the lock is not set', function () {
|
||||
beforeEach(function (done) {
|
||||
this.LockManager._tryLock = sinon.stub().yields(null, true)
|
||||
return this.LockManager._getLock(this.key, this.namespace, (...args) => {
|
||||
this.callback(...Array.from(args || []))
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should try to get the lock', function () {
|
||||
return this.LockManager._tryLock
|
||||
.calledWith(this.key, this.namespace)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should only need to try once', function () {
|
||||
return this.LockManager._tryLock.callCount.should.equal(1)
|
||||
})
|
||||
|
||||
it('should return the callback', function () {
|
||||
return this.callback.calledWith(null).should.equal(true)
|
||||
})
|
||||
|
||||
it('should clear the lock queue', function () {
|
||||
this.LockManager._lockQueuesSize().should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the lock is initially set', function () {
|
||||
beforeEach(function (done) {
|
||||
const startTime = Date.now()
|
||||
let tries = 0
|
||||
this.LockManager.LOCK_TEST_INTERVAL = 5
|
||||
this.LockManager._tryLock = function (key, namespace, callback) {
|
||||
if (callback == null) {
|
||||
callback = function () {}
|
||||
}
|
||||
if (Date.now() - startTime < 20 || tries < 2) {
|
||||
tries = tries + 1
|
||||
return callback(null, false)
|
||||
} else {
|
||||
return callback(null, true)
|
||||
}
|
||||
}
|
||||
sinon.spy(this.LockManager, '_tryLock')
|
||||
|
||||
return this.LockManager._getLock(this.key, this.namespace, (...args) => {
|
||||
this.callback(...Array.from(args || []))
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should call tryLock multiple times until free', function () {
|
||||
return (this.LockManager._tryLock.callCount > 1).should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the callback', function () {
|
||||
return this.callback.calledWith(null).should.equal(true)
|
||||
})
|
||||
|
||||
it('should clear the lock queue', function () {
|
||||
this.LockManager._lockQueuesSize().should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the lock times out', function () {
|
||||
beforeEach(function (done) {
|
||||
const time = Date.now()
|
||||
this.LockManager.LOCK_TEST_INTERVAL = 1
|
||||
this.LockManager.MAX_LOCK_WAIT_TIME = 5
|
||||
this.LockManager._tryLock = sinon.stub().yields(null, false)
|
||||
return this.LockManager._getLock(this.key, this.namespace, (...args) => {
|
||||
this.callback(...Array.from(args || []))
|
||||
return done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the callback with an error', function () {
|
||||
this.callback.should.have.been.calledWith(
|
||||
sinon.match.instanceOf(Error).and(sinon.match.has('message', 'Timeout'))
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when there are multiple requests for the same lock', function () {
|
||||
beforeEach(function (done) {
|
||||
let locked = false
|
||||
this.results = []
|
||||
this.LockManager.LOCK_TEST_INTERVAL = 1
|
||||
this.LockManager._tryLock = function (key, namespace, callback) {
|
||||
if (callback == null) {
|
||||
callback = function () {}
|
||||
}
|
||||
if (locked) {
|
||||
return callback(null, false)
|
||||
} else {
|
||||
locked = true // simulate getting the lock
|
||||
return callback(null, true)
|
||||
}
|
||||
}
|
||||
// Start ten lock requests in order at 1ms 2ms 3ms...
|
||||
// with them randomly holding the lock for 0-10ms.
|
||||
// Use predefined values for the random delay to make the test
|
||||
// deterministic.
|
||||
const randomDelays = [5, 4, 1, 8, 6, 8, 3, 4, 2, 4]
|
||||
let startTime = 0
|
||||
return Array.from(randomDelays).map((randomDelay, i) =>
|
||||
((randomDelay, i) => {
|
||||
startTime += 1
|
||||
return setTimeout(() => {
|
||||
// changing the next line to the old method of LockManager._getLockByPolling
|
||||
// should give results in a random order and cause the test to fail.
|
||||
return this.LockManager._getLock(
|
||||
this.key,
|
||||
this.namespace,
|
||||
(...args) => {
|
||||
setTimeout(
|
||||
() => (locked = false), // release the lock after a random amount of time
|
||||
randomDelay
|
||||
)
|
||||
this.results.push(i)
|
||||
if (this.results.length === 10) {
|
||||
return done()
|
||||
}
|
||||
}
|
||||
)
|
||||
}, startTime)
|
||||
})(randomDelay, i)
|
||||
)
|
||||
})
|
||||
|
||||
it('should process the requests in order', function () {
|
||||
return this.results.should.deep.equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
})
|
||||
|
||||
it('should clear the lock queue', function () {
|
||||
this.LockManager._lockQueuesSize().should.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,88 @@
|
||||
import { vi } from 'vitest'
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import sinon from 'sinon'
|
||||
|
||||
import path from 'node:path'
|
||||
const modulePath = path.join(
|
||||
import.meta.dirname,
|
||||
'../../../../../app/src/infrastructure/LockManager.mjs'
|
||||
)
|
||||
|
||||
describe('LockManager - trying the lock', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
vi.doMock('../../../../../app/src/infrastructure/RedisWrapper', () => ({
|
||||
default: {
|
||||
client: () => {
|
||||
return {
|
||||
auth() {},
|
||||
set: (ctx.set = sinon.stub()),
|
||||
}
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: {
|
||||
redis: {},
|
||||
lockManager: {
|
||||
lockTestInterval: 50,
|
||||
maxTestInterval: 1000,
|
||||
maxLockWaitTime: 10000,
|
||||
redisLockExpiry: 30,
|
||||
slowExecutionThreshold: 5000,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/metrics', () => ({
|
||||
default: {
|
||||
inc() {},
|
||||
},
|
||||
}))
|
||||
|
||||
ctx.LockManager = (await import(modulePath)).default
|
||||
ctx.callback = sinon.stub()
|
||||
ctx.key = 'lock:web:lockName:project-id}'
|
||||
return (ctx.namespace = 'lockName')
|
||||
})
|
||||
|
||||
describe('when the lock is not set', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.set.callsArgWith(5, null, 'OK')
|
||||
ctx.LockManager.randomLock = sinon.stub().returns('random-lock-value')
|
||||
return ctx.LockManager._tryLock(ctx.key, ctx.namespace, ctx.callback)
|
||||
})
|
||||
|
||||
it('should set the lock key with an expiry if it is not set', function (ctx) {
|
||||
return ctx.set
|
||||
.calledWith(ctx.key, 'random-lock-value', 'EX', 30, 'NX')
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the callback with true', function (ctx) {
|
||||
return ctx.callback.calledWith(null, true).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the lock is already set', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.set.callsArgWith(5, null, null)
|
||||
return ctx.LockManager._tryLock(ctx.key, ctx.namespace, ctx.callback)
|
||||
})
|
||||
|
||||
it('should return the callback with false', function (ctx) {
|
||||
return ctx.callback.calledWith(null, false).should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,81 +0,0 @@
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const sinon = require('sinon')
|
||||
const path = require('path')
|
||||
const modulePath = path.join(
|
||||
__dirname,
|
||||
'../../../../../app/src/infrastructure/LockManager.js'
|
||||
)
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe('LockManager - trying the lock', function () {
|
||||
beforeEach(function () {
|
||||
this.LockManager = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'./RedisWrapper': {
|
||||
client: () => {
|
||||
return {
|
||||
auth() {},
|
||||
set: (this.set = sinon.stub()),
|
||||
}
|
||||
},
|
||||
},
|
||||
'@overleaf/settings': {
|
||||
redis: {},
|
||||
lockManager: {
|
||||
lockTestInterval: 50,
|
||||
maxTestInterval: 1000,
|
||||
maxLockWaitTime: 10000,
|
||||
redisLockExpiry: 30,
|
||||
slowExecutionThreshold: 5000,
|
||||
},
|
||||
},
|
||||
'@overleaf/metrics': {
|
||||
inc() {},
|
||||
},
|
||||
},
|
||||
})
|
||||
this.callback = sinon.stub()
|
||||
this.key = 'lock:web:lockName:project-id}'
|
||||
return (this.namespace = 'lockName')
|
||||
})
|
||||
|
||||
describe('when the lock is not set', function () {
|
||||
beforeEach(function () {
|
||||
this.set.callsArgWith(5, null, 'OK')
|
||||
this.LockManager.randomLock = sinon.stub().returns('random-lock-value')
|
||||
return this.LockManager._tryLock(this.key, this.namespace, this.callback)
|
||||
})
|
||||
|
||||
it('should set the lock key with an expiry if it is not set', function () {
|
||||
return this.set
|
||||
.calledWith(this.key, 'random-lock-value', 'EX', 30, 'NX')
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should return the callback with true', function () {
|
||||
return this.callback.calledWith(null, true).should.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the lock is already set', function () {
|
||||
beforeEach(function () {
|
||||
this.set.callsArgWith(5, null, null)
|
||||
return this.LockManager._tryLock(this.key, this.namespace, this.callback)
|
||||
})
|
||||
|
||||
it('should return the callback with false', function () {
|
||||
return this.callback.calledWith(null, false).should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,95 @@
|
||||
import { vi, expect } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
const modulePath = '../../../../app/src/infrastructure/RateLimiter'
|
||||
|
||||
describe('RateLimiter', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.rclient = {}
|
||||
ctx.RedisWrapper = {
|
||||
client: sinon.stub().returns(ctx.rclient),
|
||||
}
|
||||
ctx.RateLimiter = {
|
||||
RateLimiter: sinon.stub().withArgs('login').returns(ctx.rateLimiter),
|
||||
}
|
||||
|
||||
ctx.RateLimiterFlexible = {
|
||||
RateLimiterRedis: sinon.stub(),
|
||||
}
|
||||
ctx.Settings = {}
|
||||
|
||||
vi.doMock('../../../../app/src/infrastructure/RedisWrapper', () => ({
|
||||
default: ctx.RedisWrapper,
|
||||
}))
|
||||
|
||||
vi.doMock('rate-limiter-flexible', () => ({
|
||||
default: ctx.RateLimiterFlexible,
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: ctx.Settings,
|
||||
}))
|
||||
|
||||
ctx.RateLimiter = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('getSubnetKeyFromIp', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.rateLimiter = new ctx.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
subnetPoints: 200,
|
||||
duration: 60,
|
||||
})
|
||||
})
|
||||
|
||||
it('should correctly extract a subnet key from an ip address', function (ctx) {
|
||||
expect(ctx.rateLimiter.getSubnetKeyFromIp('255.255.255.255')).to.equal(
|
||||
'255.255.255'
|
||||
)
|
||||
expect(ctx.rateLimiter.getSubnetKeyFromIp('127.0.0.1')).to.equal(
|
||||
'127.0.0'
|
||||
)
|
||||
expect(ctx.rateLimiter.getSubnetKeyFromIp('243.12.3.126')).to.equal(
|
||||
'243.12.3'
|
||||
)
|
||||
})
|
||||
|
||||
it('should throw an error when given an incorrectly formatted ip', function (ctx) {
|
||||
expect(() => ctx.rateLimiter.getSubnetKeyFromIp(1)).to.throw()
|
||||
expect(() => ctx.rateLimiter.getSubnetKeyFromIp('Not an ip')).to.throw()
|
||||
expect(() =>
|
||||
ctx.rateLimiter.getSubnetKeyFromIp('255.255.255.255.255.255')
|
||||
).to.throw()
|
||||
expect(() => ctx.rateLimiter.getSubnetKeyFromIp('255.255.255')).to.throw()
|
||||
})
|
||||
})
|
||||
|
||||
describe('_subnetRateLimiter', function () {
|
||||
it('should be defined by default', function (ctx) {
|
||||
const rateLimiter = new ctx.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
subnetPoints: 200,
|
||||
duration: 60,
|
||||
})
|
||||
expect(rateLimiter._subnetRateLimiter).not.to.be.undefined
|
||||
})
|
||||
|
||||
it('should be undefined when subnet rate limiting is disabled', function (ctx) {
|
||||
ctx.Settings.rateLimit = { subnetRateLimiterDisabled: true }
|
||||
|
||||
const rateLimiter = new ctx.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
subnetPoints: 200,
|
||||
duration: 60,
|
||||
})
|
||||
expect(rateLimiter._subnetRateLimiter).to.be.undefined
|
||||
})
|
||||
|
||||
it('should be undefined when subnetPoints are not passed as an option', function (ctx) {
|
||||
const rateLimiter = new ctx.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
duration: 60,
|
||||
})
|
||||
expect(rateLimiter._subnetRateLimiter).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,95 +0,0 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
const modulePath = require('path').join(
|
||||
__dirname,
|
||||
'../../../../app/src/infrastructure/RateLimiter'
|
||||
)
|
||||
|
||||
describe('RateLimiter', function () {
|
||||
beforeEach(function () {
|
||||
this.rclient = {}
|
||||
this.RedisWrapper = {
|
||||
client: sinon.stub().returns(this.rclient),
|
||||
}
|
||||
this.RateLimiter = {
|
||||
RateLimiter: sinon.stub().withArgs('login').returns(this.rateLimiter),
|
||||
}
|
||||
|
||||
this.RateLimiterFlexible = {
|
||||
RateLimiterRedis: sinon.stub(),
|
||||
}
|
||||
this.Settings = {}
|
||||
|
||||
this.RateLimiter = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'./RedisWrapper': this.RedisWrapper,
|
||||
'rate-limiter-flexible': this.RateLimiterFlexible,
|
||||
'@overleaf/settings': this.Settings,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('getSubnetKeyFromIp', function () {
|
||||
beforeEach(function () {
|
||||
this.rateLimiter = new this.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
subnetPoints: 200,
|
||||
duration: 60,
|
||||
})
|
||||
})
|
||||
|
||||
it('should correctly extract a subnet key from an ip address', function () {
|
||||
expect(this.rateLimiter.getSubnetKeyFromIp('255.255.255.255')).to.equal(
|
||||
'255.255.255'
|
||||
)
|
||||
expect(this.rateLimiter.getSubnetKeyFromIp('127.0.0.1')).to.equal(
|
||||
'127.0.0'
|
||||
)
|
||||
expect(this.rateLimiter.getSubnetKeyFromIp('243.12.3.126')).to.equal(
|
||||
'243.12.3'
|
||||
)
|
||||
})
|
||||
|
||||
it('should throw an error when given an incorrectly formatted ip', function () {
|
||||
expect(() => this.rateLimiter.getSubnetKeyFromIp(1)).to.throw()
|
||||
expect(() => this.rateLimiter.getSubnetKeyFromIp('Not an ip')).to.throw()
|
||||
expect(() =>
|
||||
this.rateLimiter.getSubnetKeyFromIp('255.255.255.255.255.255')
|
||||
).to.throw()
|
||||
expect(() =>
|
||||
this.rateLimiter.getSubnetKeyFromIp('255.255.255')
|
||||
).to.throw()
|
||||
})
|
||||
})
|
||||
|
||||
describe('_subnetRateLimiter', function () {
|
||||
it('should be defined by default', function () {
|
||||
const rateLimiter = new this.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
subnetPoints: 200,
|
||||
duration: 60,
|
||||
})
|
||||
expect(rateLimiter._subnetRateLimiter).not.to.be.undefined
|
||||
})
|
||||
|
||||
it('should be undefined when subnet rate limiting is disabled', function () {
|
||||
this.Settings.rateLimit = { subnetRateLimiterDisabled: true }
|
||||
|
||||
const rateLimiter = new this.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
subnetPoints: 200,
|
||||
duration: 60,
|
||||
})
|
||||
expect(rateLimiter._subnetRateLimiter).to.be.undefined
|
||||
})
|
||||
|
||||
it('should be undefined when subnetPoints are not passed as an option', function () {
|
||||
const rateLimiter = new this.RateLimiter.RateLimiter('some-limit', {
|
||||
points: 20,
|
||||
duration: 60,
|
||||
})
|
||||
expect(rateLimiter._subnetRateLimiter).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
+14
-15
@@ -1,24 +1,23 @@
|
||||
const accepts = require('accepts')
|
||||
const { expect } = require('chai')
|
||||
import accepts from 'accepts'
|
||||
import { expect, vi } from 'vitest'
|
||||
import MockRequest from '../helpers/MockRequestVitest.mjs'
|
||||
const MODULE_PATH =
|
||||
'../../../../app/src/infrastructure/RequestContentTypeDetection.js'
|
||||
const MockRequest = require('../helpers/MockRequest')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
'../../../../app/src/infrastructure/RequestContentTypeDetection.mjs'
|
||||
|
||||
describe('RequestContentTypeDetection', function () {
|
||||
before(function () {
|
||||
this.RequestContentTypeDetection = SandboxedModule.require(MODULE_PATH)
|
||||
this.req = new MockRequest()
|
||||
this.req.accepts = function (...args) {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.RequestContentTypeDetection = await import(MODULE_PATH)
|
||||
ctx.req = new MockRequest(vi)
|
||||
ctx.req.accepts = function (...args) {
|
||||
return accepts(this).type(...args)
|
||||
}
|
||||
})
|
||||
|
||||
describe('isJson=true', function () {
|
||||
function expectJson(type) {
|
||||
it(type, function () {
|
||||
this.req.headers.accept = type
|
||||
expect(this.RequestContentTypeDetection.acceptsJson(this.req)).to.equal(
|
||||
it(type, function (ctx) {
|
||||
ctx.req.headers.accept = type
|
||||
expect(ctx.RequestContentTypeDetection.acceptsJson(ctx.req)).to.equal(
|
||||
true
|
||||
)
|
||||
})
|
||||
@@ -31,9 +30,9 @@ describe('RequestContentTypeDetection', function () {
|
||||
|
||||
describe('isJson=false', function () {
|
||||
function expectNonJson(type) {
|
||||
it(type, function () {
|
||||
this.req.headers.accept = type
|
||||
expect(this.RequestContentTypeDetection.acceptsJson(this.req)).to.equal(
|
||||
it(type, function (ctx) {
|
||||
ctx.req.headers.accept = type
|
||||
expect(ctx.RequestContentTypeDetection.acceptsJson(ctx.req)).to.equal(
|
||||
false
|
||||
)
|
||||
})
|
||||
+20
-20
@@ -1,21 +1,19 @@
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
import { vi, expect } from 'vitest'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const pug = require('pug')
|
||||
const modulePath = '../../../../app/src/infrastructure/Views.js'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
import pug from 'pug'
|
||||
const modulePath = '../../../../app/src/infrastructure/Views.mjs'
|
||||
|
||||
describe('Views', function () {
|
||||
beforeEach(function () {
|
||||
this.Views = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'@overleaf/settings': (this.settings = {
|
||||
viewIncludes: {
|
||||
someInclude: 'path/to/_include.pug',
|
||||
},
|
||||
}),
|
||||
},
|
||||
})
|
||||
beforeEach(async function (ctx) {
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: (ctx.settings = {
|
||||
viewIncludes: {
|
||||
someInclude: 'path/to/_include.pug',
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
ctx.Views = (await import(modulePath)).default
|
||||
})
|
||||
|
||||
describe('_expectMetaFor', function () {
|
||||
@@ -52,8 +50,8 @@ describe('Views', function () {
|
||||
},
|
||||
]
|
||||
for (const { name, filename, firstLine, expectMeta } of cases) {
|
||||
it(name, function () {
|
||||
expect(this.Views._expectMetaFor(filename, firstLine)).to.equal(
|
||||
it(name, function (ctx) {
|
||||
expect(ctx.Views._expectMetaFor(filename, firstLine)).to.equal(
|
||||
expectMeta
|
||||
)
|
||||
})
|
||||
@@ -100,7 +98,9 @@ describe('Views', function () {
|
||||
},
|
||||
{
|
||||
name: 'computed code',
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
src: 'meta(name=`ol-prefix-${foo}` content=1)',
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
found: ['ol-prefix-${foo}'],
|
||||
duplicates: [],
|
||||
},
|
||||
@@ -118,9 +118,9 @@ describe('Views', function () {
|
||||
},
|
||||
]
|
||||
for (const { name, compiled, src, found, duplicates } of cases) {
|
||||
it(name, function () {
|
||||
const res = this.Views._findAllMetaTags(
|
||||
compiled || pug.compileClient(src, this.Views.PUG_COMPILE_ARGUMENTS)
|
||||
it(name, function (ctx) {
|
||||
const res = ctx.Views._findAllMetaTags(
|
||||
compiled || pug.compileClient(src, ctx.Views.PUG_COMPILE_ARGUMENTS)
|
||||
)
|
||||
expect(res).to.deep.equal({ found, duplicates })
|
||||
})
|
||||
Reference in New Issue
Block a user