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

Merge pull request #4325 from overleaf/jpa-core-tests-in-saas-ce-pro

[misc] run core tests in SAAS/Server CE/Server Pro environment

GitOrigin-RevId: 6278ae1eb760a4c0c16da1b71efdde844764a526
Jakob Ackermann 5 лет назад
Родитель
Сommit
fe4c48b7fb
36 измененных файлов с 360 добавлено и 5173 удалено
  1. 21 18
      services/web/Makefile
  2. 5 2
      services/web/Makefile.module
  3. 6 1
      services/web/app/src/Features/Project/ProjectCreationHandler.js
  4. 7 4
      services/web/app/src/Features/Project/ProjectDeleter.js
  5. 13 8
      services/web/app/src/infrastructure/Features.js
  6. 1 1
      services/web/app/src/router.js
  7. 1 1
      services/web/app/views/layout/navbar.pug
  8. 196 0
      services/web/test/acceptance/config/settings.test.defaults.js
  9. 26 215
      services/web/test/acceptance/config/settings.test.saas.js
  10. 2 5
      services/web/test/acceptance/config/settings.test.server-ce.js
  11. 8 6
      services/web/test/acceptance/config/settings.test.server-pro.js
  12. 0 28
      services/web/test/acceptance/src/BonusTests.js
  13. 1 1
      services/web/test/acceptance/src/CloseSiteTests.js
  14. 0 171
      services/web/test/acceptance/src/DeletionTests.js
  15. 0 111
      services/web/test/acceptance/src/ExportsTests.js
  16. 0 395
      services/web/test/acceptance/src/FeatureUpdaterTests.js
  17. 0 227
      services/web/test/acceptance/src/HistoryTests.js
  18. 8 6
      services/web/test/acceptance/src/Init.js
  19. 0 128
      services/web/test/acceptance/src/LabelsTests.js
  20. 2 2
      services/web/test/acceptance/src/ProjectInviteTests.js
  21. 0 1005
      services/web/test/acceptance/src/ProjectStructureTests.js
  22. 0 76
      services/web/test/acceptance/src/ProxyUrls.js
  23. 0 43
      services/web/test/acceptance/src/RecurlySubscriptionUpdateTests.js
  24. 3 39
      services/web/test/acceptance/src/RegistrationTests.js
  25. 1 246
      services/web/test/acceptance/src/RestoringFilesTest.js
  26. 0 42
      services/web/test/acceptance/src/SettingsTests.js
  27. 0 516
      services/web/test/acceptance/src/SubscriptionDashboardTests.js
  28. 0 112
      services/web/test/acceptance/src/SubscriptionDeletionTests.js
  29. 0 74
      services/web/test/acceptance/src/SubscriptionFeaturesTests.js
  30. 1 267
      services/web/test/acceptance/src/TokenAccessTests.js
  31. 0 1188
      services/web/test/acceptance/src/UserEmailsTests.js
  32. 2 2
      services/web/test/acceptance/src/UserHelperTests.js
  33. 0 229
      services/web/test/acceptance/src/UserThirdPartyIdentityTests.js
  34. 0 2
      services/web/test/acceptance/src/helpers/MongoHelper.js
  35. 51 0
      services/web/test/unit/src/Project/ProjectDeleterTests.js
  36. 5 2
      services/web/test/unit/src/infrastructure/FeaturesTests.js

+ 21 - 18
services/web/Makefile

@@ -129,11 +129,27 @@ test_frontend:
 #
 #
 
 
 test_acceptance: test_acceptance_app test_acceptance_modules
 test_acceptance: test_acceptance_app test_acceptance_modules
-
-test_acceptance_app:
-	COMPOSE_PROJECT_NAME=acceptance_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down -v -t 0
-	COMPOSE_PROJECT_NAME=acceptance_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) run --rm test_acceptance
-	COMPOSE_PROJECT_NAME=acceptance_test_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) down -v -t 0
+test_acceptance_saas: test_acceptance_app_saas test_acceptance_modules_merged_saas
+test_acceptance_server_ce: test_acceptance_app_server_ce test_acceptance_modules_merged_server_ce
+test_acceptance_server_pro: test_acceptance_app_server_pro test_acceptance_modules_merged_server_pro
+
+TEST_ACCEPTANCE_APP :=  \
+	test_acceptance_app_saas \
+	test_acceptance_app_server_ce \
+	test_acceptance_app_server_pro \
+
+test_acceptance_app: $(TEST_ACCEPTANCE_APP)
+test_acceptance_app_saas: export COMPOSE_PROJECT_NAME=acceptance_test_saas_$(BUILD_DIR_NAME)
+test_acceptance_app_saas: export SHARELATEX_CONFIG=$(CFG_SAAS)
+test_acceptance_app_server_ce: export COMPOSE_PROJECT_NAME=acceptance_test_server_ce_$(BUILD_DIR_NAME)
+test_acceptance_app_server_ce: export SHARELATEX_CONFIG=$(CFG_SERVER_CE)
+test_acceptance_app_server_pro: export COMPOSE_PROJECT_NAME=acceptance_test_server_pro_$(BUILD_DIR_NAME)
+test_acceptance_app_server_pro: export SHARELATEX_CONFIG=$(CFG_SERVER_PRO)
+
+$(TEST_ACCEPTANCE_APP):
+	$(DOCKER_COMPOSE) down -v -t 0
+	$(DOCKER_COMPOSE) run --rm test_acceptance
+	$(DOCKER_COMPOSE) down -v -t 0
 
 
 # We are using _make magic_ for turning these file-targets into calls to
 # We are using _make magic_ for turning these file-targets into calls to
 #  sub-Makefiles in the individual modules.
 #  sub-Makefiles in the individual modules.
@@ -260,19 +276,6 @@ $(TEST_ACCEPTANCE_MODULES_MERGED_VARIANTS):
 
 
 test_acceptance_modules: $(TEST_ACCEPTANCE_MODULES_MERGED_VARIANTS)
 test_acceptance_modules: $(TEST_ACCEPTANCE_MODULES_MERGED_VARIANTS)
 
 
-test_acceptance_app_merged_inner:
-	npm run --silent test:acceptance:app
-
-test_acceptance_merged_inner: test_acceptance_app_merged_inner
-test_acceptance_merged_inner: test_acceptance_modules_merged_inner
-
-test_acceptance_merged: export COMPOSE_PROJECT_NAME = \
-	acceptance_test_merged_$(BUILD_DIR_NAME)
-test_acceptance_merged:
-	$(DOCKER_COMPOSE) down -v -t 0
-	$(DOCKER_COMPOSE) run --rm test_acceptance make test_acceptance_merged_inner
-	$(DOCKER_COMPOSE) down -v -t 0
-
 #
 #
 # CI tests
 # CI tests
 #
 #

+ 5 - 2
services/web/Makefile.module

@@ -44,8 +44,11 @@ ALL_TEST_ACCEPTANCE_VARIANTS := \
   test_acceptance_server_pro \
   test_acceptance_server_pro \
 
 
 ifeq (,$(wildcard test/acceptance))
 ifeq (,$(wildcard test/acceptance))
-$(ALL_TEST_ACCEPTANCE_VARIANTS):
-test_acceptance_merged_inner:
+$(ALL_TEST_ACCEPTANCE_VARIANTS) test_acceptance_merged_inner:
+	@echo
+	@echo Module $(MODULE_NAME) does not have acceptance tests.
+	@echo
+
 clean_test_acceptance:
 clean_test_acceptance:
 
 
 else
 else

+ 6 - 1
services/web/app/src/Features/Project/ProjectCreationHandler.js

@@ -3,6 +3,7 @@ const OError = require('@overleaf/o-error')
 const metrics = require('@overleaf/metrics')
 const metrics = require('@overleaf/metrics')
 const Settings = require('@overleaf/settings')
 const Settings = require('@overleaf/settings')
 const { ObjectId } = require('mongodb')
 const { ObjectId } = require('mongodb')
+const Features = require('../../infrastructure/Features')
 const { Project } = require('../../models/Project')
 const { Project } = require('../../models/Project')
 const { Folder } = require('../../models/Folder')
 const { Folder } = require('../../models/Folder')
 const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
 const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
@@ -184,7 +185,11 @@ async function _createBlankProject(ownerId, projectName, attributes = {}) {
   // only display full project history when the project has the overleaf history id attribute
   // only display full project history when the project has the overleaf history id attribute
   // (to allow scripted creation of projects without full project history)
   // (to allow scripted creation of projects without full project history)
   const historyId = _.get(attributes, ['overleaf', 'history', 'id'])
   const historyId = _.get(attributes, ['overleaf', 'history', 'id'])
-  if (Settings.apis.project_history.displayHistoryForNewProjects && historyId) {
+  if (
+    Features.hasFeature('history-v1') &&
+    Settings.apis.project_history.displayHistoryForNewProjects &&
+    historyId
+  ) {
     project.overleaf.history.display = true
     project.overleaf.history.display = true
   }
   }
   if (Settings.currentImageName) {
   if (Settings.currentImageName) {

+ 7 - 4
services/web/app/src/Features/Project/ProjectDeleter.js

@@ -1,3 +1,4 @@
+const Features = require('../../infrastructure/Features')
 const _ = require('lodash')
 const _ = require('lodash')
 const { db, ObjectId } = require('../../infrastructure/mongodb')
 const { db, ObjectId } = require('../../infrastructure/mongodb')
 const { callbackify } = require('util')
 const { callbackify } = require('util')
@@ -375,10 +376,12 @@ async function expireDeletedProject(projectId) {
 
 
     await Promise.all([
     await Promise.all([
       DocstoreManager.promises.destroyProject(deletedProject.project._id),
       DocstoreManager.promises.destroyProject(deletedProject.project._id),
-      HistoryManager.promises.deleteProject(
-        deletedProject.project._id,
-        historyId
-      ),
+      Features.hasFeature('history-v1')
+        ? HistoryManager.promises.deleteProject(
+            deletedProject.project._id,
+            historyId
+          )
+        : Promise.resolve(),
       FilestoreHandler.promises.deleteProject(deletedProject.project._id),
       FilestoreHandler.promises.deleteProject(deletedProject.project._id),
       TpdsUpdateSender.promises.deleteProject({
       TpdsUpdateSender.promises.deleteProject({
         project_id: deletedProject.project._id,
         project_id: deletedProject.project._id,

+ 13 - 8
services/web/app/src/infrastructure/Features.js

@@ -1,17 +1,18 @@
 const _ = require('lodash')
 const _ = require('lodash')
 const Settings = require('@overleaf/settings')
 const Settings = require('@overleaf/settings')
-const fs = require('fs')
 
 
-const publicRegistrationModuleAvailable = fs.existsSync(
-  `${__dirname}/../../../modules/public-registration`
+const publicRegistrationModuleAvailable = Settings.moduleImportSequence.includes(
+  'public-registration'
 )
 )
 
 
-const supportModuleAvailable = fs.existsSync(
-  `${__dirname}/../../../modules/support`
+const supportModuleAvailable = Settings.moduleImportSequence.includes('support')
+
+const historyV1ModuleAvailable = Settings.moduleImportSequence.includes(
+  'history-v1'
 )
 )
 
 
-const trackChangesModuleAvailable = fs.existsSync(
-  `${__dirname}/../../../modules/track-changes`
+const trackChangesModuleAvailable = Settings.moduleImportSequence.includes(
+  'track-changes'
 )
 )
 
 
 /**
 /**
@@ -56,11 +57,13 @@ const Features = {
         return Boolean(Settings.overleaf)
         return Boolean(Settings.overleaf)
       case 'homepage':
       case 'homepage':
         return Boolean(Settings.enableHomepage)
         return Boolean(Settings.enableHomepage)
-      case 'registration':
+      case 'registration-page':
         return (
         return (
           !Features.externalAuthenticationSystemUsed() ||
           !Features.externalAuthenticationSystemUsed() ||
           Boolean(Settings.overleaf)
           Boolean(Settings.overleaf)
         )
         )
+      case 'registration':
+        return publicRegistrationModuleAvailable || Boolean(Settings.overleaf)
       case 'github-sync':
       case 'github-sync':
         return Boolean(Settings.enableGithubSync)
         return Boolean(Settings.enableGithubSync)
       case 'git-bridge':
       case 'git-bridge':
@@ -71,6 +74,8 @@ const Features = {
         return Boolean(Settings.oauth)
         return Boolean(Settings.oauth)
       case 'templates-server-pro':
       case 'templates-server-pro':
         return !Settings.overleaf
         return !Settings.overleaf
+      case 'history-v1':
+        return historyV1ModuleAvailable
       case 'affiliations':
       case 'affiliations':
       case 'analytics':
       case 'analytics':
         return Boolean(_.get(Settings, ['apis', 'v1', 'url']))
         return Boolean(_.get(Settings, ['apis', 'v1', 'url']))

+ 1 - 1
services/web/app/src/router.js

@@ -91,7 +91,7 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) {
 
 
   webRouter.get('/restricted', AuthorizationMiddleware.restricted)
   webRouter.get('/restricted', AuthorizationMiddleware.restricted)
 
 
-  if (Features.hasFeature('registration')) {
+  if (Features.hasFeature('registration-page')) {
     webRouter.get('/register', UserPagesController.registerPage)
     webRouter.get('/register', UserPagesController.registerPage)
     AuthenticationController.addEndpointToLoginWhitelist('/register')
     AuthenticationController.addEndpointToLoginWhitelist('/register')
   }
   }

+ 1 - 1
services/web/app/views/layout/navbar.pug

@@ -64,7 +64,7 @@ nav.navbar.navbar-default.navbar-main
 				// logged out
 				// logged out
 				if !getSessionUser()
 				if !getSessionUser()
 					// register link
 					// register link
-					if hasFeature('registration')
+					if hasFeature('registration-page')
 						li
 						li
 							a(href="/register") #{translate('register')}
 							a(href="/register") #{translate('register')}
 
 

+ 196 - 0
services/web/test/acceptance/config/settings.test.defaults.js

@@ -0,0 +1,196 @@
+const { merge } = require('@overleaf/settings/merge')
+
+let features
+
+const httpAuthUser = 'sharelatex'
+const httpAuthPass = 'password'
+const httpAuthUsers = {}
+httpAuthUsers[httpAuthUser] = httpAuthPass
+
+module.exports = {
+  catchErrors: false,
+  clsiCookie: undefined,
+
+  cacheStaticAssets: true,
+
+  httpAuthUsers,
+  secureCookie: false,
+  security: {
+    sessionSecret: 'static-secret-for-tests',
+  },
+  adminDomains: ['example.com'],
+
+  statusPageUrl: 'status.example.com',
+
+  apis: {
+    linkedUrlProxy: {
+      url: process.env.LINKED_URL_PROXY,
+    },
+
+    web: {
+      user: httpAuthUser,
+      pass: httpAuthPass,
+    },
+  },
+
+  // for registration via SL, set enableLegacyRegistration to true
+  // for registration via Overleaf v1, set enableLegacyLogin to true
+
+  // Currently, acceptance tests require enableLegacyRegistration.
+  enableLegacyRegistration: true,
+
+  features: (features = {
+    v1_free: {
+      collaborators: 1,
+      dropbox: false,
+      versioning: false,
+      github: true,
+      gitBridge: true,
+      templates: false,
+      references: false,
+      referencesSearch: false,
+      mendeley: true,
+      zotero: true,
+      compileTimeout: 60,
+      compileGroup: 'standard',
+      trackChanges: false,
+    },
+    personal: {
+      collaborators: 1,
+      dropbox: false,
+      versioning: false,
+      github: false,
+      gitBridge: false,
+      templates: false,
+      references: false,
+      referencesSearch: false,
+      mendeley: false,
+      zotero: false,
+      compileTimeout: 60,
+      compileGroup: 'standard',
+      trackChanges: false,
+    },
+    collaborator: {
+      collaborators: 10,
+      dropbox: true,
+      versioning: true,
+      github: true,
+      gitBridge: true,
+      templates: true,
+      references: true,
+      referencesSearch: true,
+      mendeley: true,
+      zotero: true,
+      compileTimeout: 180,
+      compileGroup: 'priority',
+      trackChanges: true,
+    },
+    professional: {
+      collaborators: -1,
+      dropbox: true,
+      versioning: true,
+      github: true,
+      gitBridge: true,
+      templates: true,
+      references: true,
+      referencesSearch: true,
+      mendeley: true,
+      zotero: true,
+      compileTimeout: 180,
+      compileGroup: 'priority',
+      trackChanges: true,
+    },
+  }),
+
+  defaultFeatures: features.personal,
+  defaultPlanCode: 'personal',
+  institutionPlanCode: 'professional',
+
+  plans: [
+    {
+      planCode: 'v1_free',
+      name: 'V1 Free',
+      price: 0,
+      features: features.v1_free,
+    },
+    {
+      planCode: 'personal',
+      name: 'Personal',
+      price: 0,
+      features: features.personal,
+    },
+    {
+      planCode: 'collaborator',
+      name: 'Collaborator',
+      price: 1500,
+      features: features.collaborator,
+    },
+    {
+      planCode: 'professional',
+      name: 'Professional',
+      price: 3000,
+      features: features.professional,
+    },
+  ],
+
+  bonus_features: {
+    1: {
+      collaborators: 2,
+      dropbox: false,
+      versioning: false,
+    },
+    3: {
+      collaborators: 4,
+      dropbox: false,
+      versioning: false,
+    },
+    6: {
+      collaborators: 4,
+      dropbox: true,
+      versioning: true,
+    },
+    9: {
+      collaborators: -1,
+      dropbox: true,
+      versioning: true,
+    },
+  },
+
+  redirects: {
+    '/redirect/one': '/destination/one',
+    '/redirect/get_and_post': {
+      methods: ['get', 'post'],
+      url: '/destination/get_and_post',
+    },
+    '/redirect/base_url': {
+      baseUrl: 'https://example.com',
+      url: '/destination/base_url',
+    },
+    '/redirect/params/:id': {
+      url(params) {
+        return `/destination/${params.id}/params`
+      },
+    },
+    '/redirect/qs': '/destination/qs',
+    '/docs_v1': {
+      url: '/docs',
+    },
+  },
+
+  reconfirmNotificationDays: 14,
+
+  unsupportedBrowsers: {
+    ie: '<=11',
+  },
+
+  // No email in tests
+  email: undefined,
+
+  test: {
+    counterInit: 0,
+  },
+}
+
+module.exports.mergeWith = function (overrides) {
+  return merge(overrides, module.exports)
+}

+ 26 - 215
services/web/test/acceptance/config/settings.test.saas.js

@@ -1,215 +1,46 @@
-const base = require('../../../config/settings.overrides.saas')
-
-let features
-const v1Api = {
-  url: 'http://localhost:5000',
-  user: 'overleaf',
-  pass: 'password',
-}
+const { merge } = require('@overleaf/settings/merge')
+const baseApp = require('../../../config/settings.overrides.saas')
+const baseTest = require('./settings.test.defaults')
 
 
 const httpAuthUser = 'sharelatex'
 const httpAuthUser = 'sharelatex'
 const httpAuthPass = 'password'
 const httpAuthPass = 'password'
 const httpAuthUsers = {}
 const httpAuthUsers = {}
 httpAuthUsers[httpAuthUser] = httpAuthPass
 httpAuthUsers[httpAuthUser] = httpAuthPass
 
 
-module.exports = base.mergeWith({
-  catchErrors: false,
-  clsiCookie: undefined,
-
-  cacheStaticAssets: true,
+const overrides = {
   enableSubscriptions: true,
   enableSubscriptions: true,
 
 
-  httpAuthUsers,
-  secureCookie: false,
-  security: {
-    sessionSecret: 'static-secret-for-tests',
-  },
-  adminDomains: ['example.com'],
-
   apis: {
   apis: {
-    web: {
-      user: httpAuthUser,
-      pass: httpAuthPass,
-    },
-    v1: {
-      url: v1Api.url,
-      user: v1Api.user,
-      pass: v1Api.pass,
+    project_history: {
+      sendProjectStructureOps: true,
+      initializeHistoryForNewProjects: true,
+      displayHistoryForNewProjects: true,
+      url: `http://localhost:3054`,
     },
     },
+
     recurly: {
     recurly: {
-      // Set up our own mock recurly server
       url: 'http://localhost:6034',
       url: 'http://localhost:6034',
       subdomain: 'test',
       subdomain: 'test',
       apiKey: 'private-nonsense',
       apiKey: 'private-nonsense',
       webhookUser: 'recurly',
       webhookUser: 'recurly',
       webhookPass: 'webhook',
       webhookPass: 'webhook',
     },
     },
+
     tpdsworker: {
     tpdsworker: {
       // Disable tpdsworker in CI.
       // Disable tpdsworker in CI.
       url: undefined,
       url: undefined,
     },
     },
-  },
-
-  // for registration via SL, set enableLegacyRegistration to true
-  // for registration via Overleaf v1, set enableLegacyLogin to true
-
-  // Currently, acceptance tests require enableLegacyRegistration.
-  enableLegacyRegistration: true,
-
-  features: (features = {
-    v1_free: {
-      collaborators: 1,
-      dropbox: false,
-      versioning: false,
-      github: true,
-      gitBridge: true,
-      templates: false,
-      references: false,
-      referencesSearch: false,
-      mendeley: true,
-      zotero: true,
-      compileTimeout: 60,
-      compileGroup: 'standard',
-      trackChanges: false,
-    },
-    personal: {
-      collaborators: 1,
-      dropbox: false,
-      versioning: false,
-      github: false,
-      gitBridge: false,
-      templates: false,
-      references: false,
-      referencesSearch: false,
-      mendeley: false,
-      zotero: false,
-      compileTimeout: 60,
-      compileGroup: 'standard',
-      trackChanges: false,
-    },
-    collaborator: {
-      collaborators: 10,
-      dropbox: true,
-      versioning: true,
-      github: true,
-      gitBridge: true,
-      templates: true,
-      references: true,
-      referencesSearch: true,
-      mendeley: true,
-      zotero: true,
-      compileTimeout: 180,
-      compileGroup: 'priority',
-      trackChanges: true,
-    },
-    professional: {
-      collaborators: -1,
-      dropbox: true,
-      versioning: true,
-      github: true,
-      gitBridge: true,
-      templates: true,
-      references: true,
-      referencesSearch: true,
-      mendeley: true,
-      zotero: true,
-      compileTimeout: 180,
-      compileGroup: 'priority',
-      trackChanges: true,
-    },
-  }),
-
-  defaultFeatures: features.personal,
-  defaultPlanCode: 'personal',
-  institutionPlanCode: 'professional',
-
-  plans: [
-    {
-      planCode: 'v1_free',
-      name: 'V1 Free',
-      price: 0,
-      features: features.v1_free,
-    },
-    {
-      planCode: 'personal',
-      name: 'Personal',
-      price: 0,
-      features: features.personal,
-    },
-    {
-      planCode: 'collaborator',
-      name: 'Collaborator',
-      price: 1500,
-      features: features.collaborator,
-    },
-    {
-      planCode: 'professional',
-      name: 'Professional',
-      price: 3000,
-      features: features.professional,
-    },
-  ],
 
 
-  bonus_features: {
-    1: {
-      collaborators: 2,
-      dropbox: false,
-      versioning: false,
-    },
-    3: {
-      collaborators: 4,
-      dropbox: false,
-      versioning: false,
-    },
-    6: {
-      collaborators: 4,
-      dropbox: true,
-      versioning: true,
-    },
-    9: {
-      collaborators: -1,
-      dropbox: true,
-      versioning: true,
-    },
-  },
-
-  proxyUrls: {
-    '/institutions/list': { baseUrl: v1Api.url, path: '/universities/list' },
-    '/institutions/list/:id': {
-      baseUrl: v1Api.url,
-      path(params) {
-        return `/universities/list/${params.id}`
-      },
-    },
-    '/institutions/domains': {
-      baseUrl: v1Api.url,
-      path: '/university/domains',
-    },
-    '/proxy/missing/baseUrl': { path: '/foo/bar' },
-    '/proxy/get_and_post': {
-      methods: ['get', 'post'],
-      path: '/destination/get_and_post',
+    v1: {
+      url: 'http://localhost:5000',
+      user: 'overleaf',
+      pass: 'password',
     },
     },
-  },
 
 
-  redirects: {
-    '/redirect/one': '/destination/one',
-    '/redirect/get_and_post': {
-      methods: ['get', 'post'],
-      url: '/destination/get_and_post',
-    },
-    '/redirect/base_url': {
-      baseUrl: 'https://example.com',
-      url: '/destination/base_url',
-    },
-    '/redirect/params/:id': {
-      url(params) {
-        return `/destination/${params.id}/params`
-      },
-    },
-    '/redirect/qs': '/destination/qs',
-    '/docs_v1': {
-      url: '/docs',
+    v1_history: {
+      url: `http://localhost:3100/api`,
+      user: 'overleaf',
+      pass: 'password',
     },
     },
   },
   },
 
 
@@ -225,41 +56,21 @@ module.exports = base.mergeWith({
     },
     },
   },
   },
 
 
-  // for testing /user/bonus
-  social: {
-    twitter: {
-      handle: 'overleaf',
-    },
-
-    facebook: {
-      appId: '400474170024644',
-      picture: 'https://www.overleaf.com/img/ol-brand/logo-horizontal.png',
-      redirectUri: 'https://www.overleaf.com',
-    },
-  },
-
   overleaf: {
   overleaf: {
     oauth: undefined,
     oauth: undefined,
   },
   },
   saml: undefined,
   saml: undefined,
 
 
-  reconfirmNotificationDays: 14,
-
-  unsupportedBrowsers: {
-    ie: '<=11',
-  },
-
   // Disable contentful module.
   // Disable contentful module.
   contentful: undefined,
   contentful: undefined,
+}
 
 
-  // No email in tests
-  email: undefined,
+module.exports = baseApp.mergeWith(baseTest.mergeWith(overrides))
 
 
-  test: {
-    counterInit: 0,
-  },
-})
+for (const redisKey of Object.keys(module.exports.redis)) {
+  module.exports.redis[redisKey].host = process.env.REDIS_HOST || 'localhost'
+}
 
 
-for (const redisKey of Object.keys(base.redis)) {
-  base.redis[redisKey].host = process.env.REDIS_HOST || 'localhost'
+module.exports.mergeWith = function (overrides) {
+  return merge(overrides, module.exports)
 }
 }

+ 2 - 5
services/web/test/acceptance/config/settings.test.server-ce.js

@@ -1,10 +1,7 @@
 const { merge } = require('@overleaf/settings/merge')
 const { merge } = require('@overleaf/settings/merge')
+const base = require('./settings.test.defaults')
 
 
-module.exports = {
-  test: {
-    counterInit: 0,
-  },
-}
+module.exports = base.mergeWith({})
 
 
 module.exports.mergeWith = function (overrides) {
 module.exports.mergeWith = function (overrides) {
   return merge(overrides, module.exports)
   return merge(overrides, module.exports)

+ 8 - 6
services/web/test/acceptance/config/settings.test.server-pro.js

@@ -1,7 +1,9 @@
-const base = require('../../../config/settings.overrides.server-pro')
+const { merge } = require('@overleaf/settings/merge')
+const baseApp = require('../../../config/settings.overrides.server-pro')
+const baseTest = require('./settings.test.defaults')
 
 
-module.exports = base.mergeWith({
-  test: {
-    counterInit: 0,
-  },
-})
+module.exports = baseApp.mergeWith(baseTest.mergeWith({}))
+
+module.exports.mergeWith = function (overrides) {
+  return merge(overrides, module.exports)
+}

+ 0 - 28
services/web/test/acceptance/src/BonusTests.js

@@ -1,28 +0,0 @@
-const { expect } = require('chai')
-const cheerio = require('cheerio')
-const UserHelper = require('../src/helpers/UserHelper')
-
-describe('Bonus', function () {
-  let userHelper
-  beforeEach(async function () {
-    userHelper = new UserHelper()
-    const email = userHelper.getDefaultEmail()
-    userHelper = await UserHelper.createUser({ email })
-    userHelper = await UserHelper.loginUser({
-      email,
-      password: userHelper.getDefaultPassword(),
-    })
-  })
-
-  it('should use the count rather than refered_users', async function () {
-    await UserHelper.updateUser(userHelper.user._id, {
-      $set: { refered_user_count: 1, refered_users: [] },
-    })
-
-    const response = await userHelper.request.get('/user/bonus')
-    expect(response.statusCode).to.equal(200)
-
-    const dom = cheerio.load(response.body)
-    expect(dom('.bonus-status').text()).to.match(/You've introduced 1 person/)
-  })
-})

+ 1 - 1
services/web/test/acceptance/src/CloseSiteTests.js

@@ -44,7 +44,7 @@ describe('siteIsOpen', function () {
       request.get('/some/route', { json: true }, (error, response, body) => {
       request.get('/some/route', { json: true }, (error, response, body) => {
         response.statusCode.should.equal(503)
         response.statusCode.should.equal(503)
         body.message.should.match(/maintenance/)
         body.message.should.match(/maintenance/)
-        body.message.should.match(/status.overleaf.com/)
+        body.message.should.match(/status.example.com/)
         done()
         done()
       })
       })
     })
     })

+ 0 - 171
services/web/test/acceptance/src/DeletionTests.js

@@ -4,8 +4,6 @@ const async = require('async')
 const { expect } = require('chai')
 const { expect } = require('chai')
 const settings = require('@overleaf/settings')
 const settings = require('@overleaf/settings')
 const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
 const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
-const { Subscription } = require('../../../app/src/models/Subscription')
-const SubscriptionViewModelBuilder = require('../../../app/src/Features/Subscription/SubscriptionViewModelBuilder')
 const MockDocstoreApiClass = require('./mocks/MockDocstoreApi')
 const MockDocstoreApiClass = require('./mocks/MockDocstoreApi')
 const MockFilestoreApiClass = require('./mocks/MockFilestoreApi')
 const MockFilestoreApiClass = require('./mocks/MockFilestoreApi')
 
 
@@ -75,29 +73,6 @@ describe('Deleting a user', function () {
     })
     })
   })
   })
 
 
-  it('Should fail if the user has a subscription', function (done) {
-    Subscription.create(
-      {
-        admin_id: this.user._id,
-        manager_ids: [this.user._id],
-        planCode: 'collaborator',
-      },
-      error => {
-        expect(error).not.to.exist
-        SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
-          this.user,
-          error => {
-            expect(error).not.to.exist
-            this.user.deleteUser(error => {
-              expect(error).to.exist
-              done()
-            })
-          }
-        )
-      }
-    )
-  })
-
   it("Should delete the user's projects", function (done) {
   it("Should delete the user's projects", function (done) {
     this.user.createProject('wombat', (error, projectId) => {
     this.user.createProject('wombat', (error, projectId) => {
       expect(error).not.to.exist
       expect(error).not.to.exist
@@ -437,150 +412,4 @@ describe('Deleting a project', function () {
       })
       })
     })
     })
   })
   })
-
-  describe('when the deleted project has deletedFiles', function () {
-    beforeEach('delete project', function (done) {
-      this.user.deleteProject(this.projectId, done)
-    })
-    let fileId1, fileId2
-    beforeEach('create files', function () {
-      // take a short cut and just allocate file ids
-      fileId1 = ObjectId()
-      fileId2 = ObjectId()
-    })
-    const otherFileDetails = {
-      name: 'universe.jpg',
-      linkedFileData: null,
-      hash: 'ed19e7d6779b47d8c63f6fa5a21954dcfb6cac00',
-      deletedAt: new Date(),
-    }
-    beforeEach('insert deletedFiles', async function () {
-      const deletedFiles = [
-        { _id: fileId1, ...otherFileDetails },
-        { _id: fileId2, ...otherFileDetails },
-        // duplicate entry
-        { _id: fileId1, ...otherFileDetails },
-      ]
-      await db.deletedProjects.updateOne(
-        { 'deleterData.deletedProjectId': ObjectId(this.projectId) },
-        { $set: { 'project.deletedFiles': deletedFiles } }
-      )
-    })
-    describe('when undelete the project', function () {
-      let admin
-      beforeEach('create admin', function (done) {
-        admin = new User()
-        async.series(
-          [
-            cb => admin.ensureUserExists(cb),
-            cb => admin.ensureAdmin(cb),
-            cb => admin.login(cb),
-          ],
-          done
-        )
-      })
-      beforeEach('undelete project', function (done) {
-        admin.undeleteProject(this.projectId, done)
-      })
-
-      it('should not insert deletedFiles into the projects collection', function (done) {
-        this.user.getProject(this.projectId, (error, project) => {
-          if (error) return done(error)
-          expect(project.deletedFiles).to.deep.equal([])
-          done()
-        })
-      })
-
-      it('should insert unique entries into the deletedFiles collection', async function () {
-        const docs = await db.deletedFiles
-          .find({}, { sort: { _id: 1 } })
-          .toArray()
-        expect(docs).to.deep.equal([
-          {
-            _id: fileId1,
-            projectId: ObjectId(this.projectId),
-            ...otherFileDetails,
-          },
-          {
-            _id: fileId2,
-            projectId: ObjectId(this.projectId),
-            ...otherFileDetails,
-          },
-        ])
-      })
-    })
-  })
-
-  describe('when the deleted project has deletedDocs', function () {
-    beforeEach('delete project', function (done) {
-      this.user.deleteProject(this.projectId, done)
-    })
-
-    let deletedDocs
-    beforeEach('set deletedDocs', function () {
-      deletedDocs = [
-        { _id: ObjectId(), name: 'foo.tex', deletedAt: new Date() },
-        { _id: ObjectId(), name: 'bar.tex', deletedAt: new Date() },
-      ]
-      deletedDocs.forEach(doc => {
-        MockDocstoreApi.createLegacyDeletedDoc(
-          this.projectId,
-          doc._id.toString()
-        )
-      })
-    })
-
-    beforeEach('insert deletedDocs', async function () {
-      await db.deletedProjects.updateOne(
-        { 'deleterData.deletedProjectId': ObjectId(this.projectId) },
-        { $set: { 'project.deletedDocs': deletedDocs } }
-      )
-    })
-
-    it('should not see any doc names before', async function () {
-      const docs = MockDocstoreApi.getDeletedDocs(this.projectId)
-      expect(docs).to.deep.equal(
-        deletedDocs.map(doc => {
-          const { _id } = doc
-          return { _id: _id.toString(), name: undefined }
-        })
-      )
-    })
-
-    describe('when undeleting the project', function () {
-      let admin
-      beforeEach('create admin', function (done) {
-        admin = new User()
-        async.series(
-          [
-            cb => admin.ensureUserExists(cb),
-            cb => admin.ensureAdmin(cb),
-            cb => admin.login(cb),
-          ],
-          done
-        )
-      })
-      beforeEach('undelete project', function (done) {
-        admin.undeleteProject(this.projectId, done)
-      })
-
-      it('should not insert deletedDocs into the projects collection', function (done) {
-        this.user.getProject(this.projectId, (error, project) => {
-          if (error) return done(error)
-          expect(project.deletedDocs).to.deep.equal([])
-          done()
-        })
-      })
-
-      it('should back fill deleted docs context', async function () {
-        const docs = MockDocstoreApi.getDeletedDocs(this.projectId)
-        expect(docs).to.deep.equal(
-          deletedDocs.map(doc => {
-            const { _id, name } = doc
-            return { _id: _id.toString(), name }
-          })
-        )
-      })
-    })
-  })
 })
 })

+ 0 - 111
services/web/test/acceptance/src/ExportsTests.js

@@ -1,111 +0,0 @@
-/* eslint-disable
-    camelcase,
-    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
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
-
-const { expect } = require('chai')
-const User = require('./helpers/User')
-
-const MockProjectHistoryApiClass = require('./mocks/MockProjectHistoryApi')
-const MockV1ApiClass = require('./mocks/MockV1Api')
-
-let MockProjectHistoryApi, MockV1Api
-
-before(function () {
-  MockV1Api = MockV1ApiClass.instance()
-  MockProjectHistoryApi = MockProjectHistoryApiClass.instance()
-})
-
-describe('Exports', function () {
-  beforeEach(function (done) {
-    this.brand_variation_id = '18'
-    this.owner = new User()
-    return this.owner.login(error => {
-      if (error != null) {
-        throw error
-      }
-      return this.owner.createProject(
-        'example-project',
-        { template: 'example' },
-        (error, project_id) => {
-          this.project_id = project_id
-          if (error != null) {
-            throw error
-          }
-          return done()
-        }
-      )
-    })
-  })
-
-  describe('exporting a project', function () {
-    beforeEach(function (done) {
-      this.version = Math.floor(Math.random() * 10000)
-      MockProjectHistoryApi.setProjectVersion(this.project_id, this.version)
-      this.export_id = Math.floor(Math.random() * 10000)
-      MockV1Api.setExportId(this.export_id)
-      return this.owner.request(
-        {
-          method: 'POST',
-          url: `/project/${this.project_id}/export/${this.brand_variation_id}`,
-          json: true,
-          body: {
-            title: 'title',
-            description: 'description',
-            author: 'author',
-            license: 'other',
-            showSource: true,
-          },
-        },
-        (error, response, body) => {
-          if (error != null) {
-            throw error
-          }
-          expect(response.statusCode).to.equal(200)
-          this.exportResponseBody = body
-          return done()
-        }
-      )
-    })
-
-    it('should have sent correct data to v1', function (done) {
-      const {
-        project,
-        user,
-        destination,
-        options,
-      } = MockV1Api.getLastExportParams()
-      // project details should match
-      expect(project.id).to.equal(this.project_id)
-      expect(project.rootDocPath).to.equal('/main.tex')
-      // gallery details should match
-      expect(project.metadata.title).to.equal('title')
-      expect(project.metadata.description).to.equal('description')
-      expect(project.metadata.author).to.equal('author')
-      expect(project.metadata.license).to.equal('other')
-      expect(project.metadata.showSource).to.equal(true)
-      // version should match what was retrieved from project-history
-      expect(project.historyVersion).to.equal(this.version)
-      // user details should match
-      expect(user.id).to.equal(this.owner.id)
-      expect(user.email).to.equal(this.owner.email)
-      // brand-variation should match
-      expect(destination.brandVariationId).to.equal(this.brand_variation_id)
-      return done()
-    })
-
-    it('should have returned the export ID provided by v1', function (done) {
-      expect(this.exportResponseBody.export_v1_id).to.equal(this.export_id)
-      return done()
-    })
-  })
-})

+ 0 - 395
services/web/test/acceptance/src/FeatureUpdaterTests.js

@@ -1,395 +0,0 @@
-const { expect } = require('chai')
-const UserHelper = require('./helpers/UserHelper')
-const settings = require('@overleaf/settings')
-const { ObjectId } = require('mongodb')
-const { Subscription } = require('../../../app/src/models/Subscription')
-const { User } = require('../../../app/src/models/User')
-const FeaturesUpdater = require('../../../app/src/Features/Subscription/FeaturesUpdater')
-
-const MockV1ApiClass = require('./mocks/MockV1Api')
-const logger = require('logger-sharelatex')
-logger.logger.level('error')
-
-let MockV1Api
-
-before(function () {
-  MockV1Api = MockV1ApiClass.instance()
-})
-
-const syncUserAndGetFeatures = function (user, callback) {
-  FeaturesUpdater.refreshFeatures(user._id, 'test', error => {
-    if (error) {
-      return callback(error)
-    }
-    User.findById(user._id, (error, user) => {
-      if (error) {
-        return callback(error)
-      }
-      const { features } = user.toObject()
-      delete features.$init // mongoose internals
-      callback(null, features)
-    })
-  })
-}
-
-describe('FeatureUpdater.refreshFeatures', function () {
-  let userHelper, user
-  beforeEach(async function () {
-    userHelper = await UserHelper.createUser()
-    user = userHelper.user
-  })
-
-  describe('when user has no subscriptions', function () {
-    it('should set their features to the basic set', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        expect(features).to.deep.equal(settings.defaultFeatures)
-        done()
-      })
-    })
-  })
-
-  describe('when the user has an individual subscription', function () {
-    beforeEach(function () {
-      Subscription.create({
-        admin_id: user._id,
-        manager_ids: [user._id],
-        planCode: 'collaborator',
-        customAccount: true,
-      })
-    }) // returns a promise
-
-    it('should set their features to the upgraded set', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        const plan = settings.plans.find(
-          plan => plan.planCode === 'collaborator'
-        )
-        expect(features).to.deep.equal(plan.features)
-        done()
-      })
-    })
-  })
-
-  describe('when the user is in a group subscription', function () {
-    beforeEach(function () {
-      const groupAdminId = ObjectId()
-      Subscription.create({
-        admin_id: groupAdminId,
-        manager_ids: [groupAdminId],
-        member_ids: [user._id],
-        groupAccount: true,
-        planCode: 'collaborator',
-        customAccount: true,
-      })
-    }) // returns a promise
-
-    it('should set their features to the upgraded set', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        const plan = settings.plans.find(
-          plan => plan.planCode === 'collaborator'
-        )
-        expect(features).to.deep.equal(plan.features)
-        done()
-      })
-    })
-  })
-
-  describe('when the user has bonus features', function () {
-    beforeEach(function () {
-      return User.updateOne(
-        {
-          _id: user._id,
-        },
-        {
-          refered_user_count: 10,
-        }
-      )
-    }) // returns a promise
-
-    it('should set their features to the bonus set', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        expect(features).to.deep.equal(
-          Object.assign(
-            {},
-            settings.defaultFeatures,
-            settings.bonus_features[9]
-          )
-        )
-        done()
-      })
-    })
-  })
-
-  describe('when the user has affiliations', function () {
-    let email2, institutionId, hostname
-    beforeEach(async function () {
-      institutionId = MockV1Api.createInstitution({ commonsAccount: true })
-      hostname = 'institution.edu'
-      MockV1Api.addInstitutionDomain(institutionId, hostname, {
-        confirmed: true,
-      })
-      email2 = `${user._id}@${hostname}`
-      userHelper = await UserHelper.loginUser(
-        userHelper.getDefaultEmailPassword()
-      )
-      await userHelper.addEmail(email2)
-      this.institutionPlan = settings.plans.find(
-        plan => plan.planCode === settings.institutionPlanCode
-      )
-    })
-
-    it('should not set their features if email is not confirmed', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        expect(features).to.deep.equal(settings.defaultFeatures)
-        done()
-      })
-    })
-
-    describe('when email is confirmed', function () {
-      beforeEach(async function () {
-        await userHelper.confirmEmail(user._id, email2)
-      })
-
-      it('should set their features', function (done) {
-        syncUserAndGetFeatures(user, (error, features) => {
-          expect(features).to.deep.equal(this.institutionPlan.features)
-          done()
-        })
-      })
-
-      describe('when domain is not confirmed as part of institution', function () {
-        beforeEach(function () {
-          MockV1Api.updateInstitutionDomain(institutionId, hostname, {
-            confirmed: false,
-          })
-        })
-        it('should not set their features', function (done) {
-          syncUserAndGetFeatures(user, (error, features) => {
-            expect(features).to.deep.equal(settings.defaultFeatures)
-            done()
-          })
-        })
-      })
-    })
-  })
-
-  describe('when the user is due bonus features and has extra features that no longer apply', function () {
-    beforeEach(function () {
-      return User.updateOne(
-        {
-          _id: user._id,
-        },
-        {
-          refered_user_count: 10,
-          'features.github': true,
-        }
-      )
-    }) // returns a promise
-
-    it('should set their features to the bonus set and downgrade the extras', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        expect(features).to.deep.equal(
-          Object.assign(
-            {},
-            settings.defaultFeatures,
-            settings.bonus_features[9]
-          )
-        )
-        done()
-      })
-    })
-  })
-
-  describe('when the user has a v1 plan', function () {
-    beforeEach(function () {
-      MockV1Api.setUser(42, { plan_name: 'free' })
-      return User.updateOne(
-        {
-          _id: user._id,
-        },
-        {
-          overleaf: {
-            id: 42,
-          },
-        }
-      )
-    }) // returns a promise
-
-    it('should set their features to the v1 plan', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        const plan = settings.plans.find(plan => plan.planCode === 'v1_free')
-        expect(features).to.deep.equal(plan.features)
-        done()
-      })
-    })
-  })
-
-  describe('when the user has a v1 plan and bonus features', function () {
-    beforeEach(function () {
-      MockV1Api.setUser(42, { plan_name: 'free' })
-      return User.updateOne(
-        {
-          _id: user._id,
-        },
-        {
-          overleaf: {
-            id: 42,
-          },
-          refered_user_count: 10,
-        }
-      )
-    }) // returns a promise
-
-    it('should set their features to the best of the v1 plan and bonus features', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        const v1plan = settings.plans.find(plan => plan.planCode === 'v1_free')
-        const expectedFeatures = Object.assign(
-          {},
-          v1plan.features,
-          settings.bonus_features[9]
-        )
-        expect(features).to.deep.equal(expectedFeatures)
-        done()
-      })
-    })
-  })
-
-  describe('when the user has a group and personal subscription', function () {
-    beforeEach(function (done) {
-      const groupAdminId = ObjectId()
-
-      Subscription.create(
-        {
-          admin_id: user._id,
-          manager_ids: [user._id],
-          planCode: 'professional',
-          customAccount: true,
-        },
-        error => {
-          if (error) {
-            throw error
-          }
-          Subscription.create(
-            {
-              admin_id: groupAdminId,
-              manager_ids: [groupAdminId],
-              member_ids: [user._id],
-              groupAccount: true,
-              planCode: 'collaborator',
-              customAccount: true,
-            },
-            done
-          )
-        }
-      )
-    })
-
-    it('should set their features to the best set', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        const plan = settings.plans.find(
-          plan => plan.planCode === 'professional'
-        )
-        expect(features).to.deep.equal(plan.features)
-        done()
-      })
-    })
-  })
-
-  describe('when the notifyV1Flag is passed', function () {
-    beforeEach(function () {
-      User.updateOne(
-        {
-          _id: user._id,
-        },
-        {
-          overleaf: {
-            id: 42,
-          },
-        }
-      )
-    }) // returns a promise
-  })
-
-  describe('when the user has features overrides', function () {
-    beforeEach(function () {
-      const futureDate = new Date()
-      futureDate.setDate(futureDate.getDate() + 1)
-      return User.updateOne(
-        {
-          _id: user._id,
-        },
-        {
-          featuresOverrides: [
-            {
-              features: {
-                github: true,
-              },
-            },
-            {
-              features: {
-                dropbox: true,
-              },
-              expiresAt: new Date(1990, 12, 25),
-            },
-            {
-              features: {
-                trackChanges: true,
-              },
-              expiresAt: futureDate,
-            },
-          ],
-        }
-      )
-    }) // returns a promise
-
-    it('should set their features to the overridden set', function (done) {
-      syncUserAndGetFeatures(user, (error, features) => {
-        if (error) {
-          throw error
-        }
-        const expectedFeatures = Object.assign(settings.defaultFeatures, {
-          github: true,
-          trackChanges: true,
-        })
-        expect(features).to.deep.equal(expectedFeatures)
-        done()
-      })
-    })
-  })
-
-  it('should update featuresUpdatedAt', async function () {
-    user = (await UserHelper.getUser({ _id: user._id })).user
-    expect(user.featuresUpdatedAt).to.not.exist // no default set
-    await FeaturesUpdater.promises.refreshFeatures(user._id, 'test')
-    user = (await UserHelper.getUser({ _id: user._id })).user
-    const featuresUpdatedAt = user.featuresUpdatedAt
-    expect(featuresUpdatedAt).to.exist
-    // refresh again
-    await FeaturesUpdater.promises.refreshFeatures(user._id, 'test')
-    user = (await UserHelper.getUser({ _id: user._id })).user
-    expect(user.featuresUpdatedAt > featuresUpdatedAt).to.be.true
-  })
-})

+ 0 - 227
services/web/test/acceptance/src/HistoryTests.js

@@ -1,227 +0,0 @@
-/* eslint-disable
-    camelcase,
-    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
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
-const { expect } = require('chai')
-
-const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
-const User = require('./helpers/User')
-const MockV1HistoryApiClass = require('./mocks/MockV1HistoryApi')
-
-let MockV1HistoryApi
-
-before(function () {
-  MockV1HistoryApi = MockV1HistoryApiClass.instance()
-})
-
-describe('History', function () {
-  beforeEach(function (done) {
-    this.owner = new User()
-    return this.owner.login(done)
-  })
-
-  describe('zip download of version', function () {
-    it('should stream the zip file of a version', function (done) {
-      return this.owner.createProject(
-        'example-project',
-        (error, project_id) => {
-          this.project_id = project_id
-          if (error != null) {
-            return done(error)
-          }
-          this.v1_history_id = 42
-          return db.projects.updateOne(
-            {
-              _id: ObjectId(this.project_id),
-            },
-            {
-              $set: {
-                'overleaf.history.id': this.v1_history_id,
-              },
-            },
-            error => {
-              if (error != null) {
-                return done(error)
-              }
-              return this.owner.request(
-                `/project/${this.project_id}/version/42/zip`,
-                (error, response, body) => {
-                  if (error != null) {
-                    return done(error)
-                  }
-                  expect(response.statusCode).to.equal(200)
-                  expect(response.headers['content-type']).to.equal(
-                    'application/zip'
-                  )
-                  expect(response.headers['content-disposition']).to.equal(
-                    'attachment; filename="example-project%20(Version%2042).zip"'
-                  )
-                  expect(body).to.equal(
-                    `Mock zip for ${this.v1_history_id} at version 42`
-                  )
-                  return done()
-                }
-              )
-            }
-          )
-        }
-      )
-    })
-
-    describe('request abort', function () {
-      // Optional manual verification: add unique logging statements into
-      //   HistoryController._pipeHistoryZipToResponse
-      // in each of the `req.aborted` branches and confirm that each branch
-      //  was covered.
-      beforeEach(function setupNewProject(done) {
-        this.owner.createProject('example-project', (error, project_id) => {
-          this.project_id = project_id
-          if (error) {
-            return done(error)
-          }
-          this.v1_history_id = 42
-          db.projects.updateOne(
-            { _id: ObjectId(this.project_id) },
-            {
-              $set: {
-                'overleaf.history.id': this.v1_history_id,
-              },
-            },
-            done
-          )
-        })
-      })
-
-      it('should abort the upstream request', function (done) {
-        const request = this.owner.request(
-          `/project/${this.project_id}/version/100/zip`
-        )
-        request.on('error', done)
-        request.on('response', response => {
-          expect(response.statusCode).to.equal(200)
-          let receivedChunks = 0
-          response.on('data', () => {
-            receivedChunks++
-          })
-          response.resume()
-
-          setTimeout(() => {
-            request.abort()
-            const receivedSoFar = receivedChunks
-            const sentSoFar = MockV1HistoryApi.sentChunks
-            // Ihe next assertions should verify that chunks are emitted
-            //  and received -- the exact number is not important.
-            // In theory we are now emitting the 3rd chunk,
-            //  so this should be exactly 3, to not make this
-            //  test flaky, we allow +- 2 chunks.
-            expect(sentSoFar).to.be.within(1, 4)
-            expect(receivedSoFar).to.be.within(1, 4)
-            setTimeout(() => {
-              // The fake-s3 service should have stopped emitting chunks.
-              // Ff not, that would be +5 in an ideal world (1 every 100ms).
-              // On the happy-path (it stopped) it emitted +1 which was
-              //  in-flight and another +1 before it received the abort.
-              expect(MockV1HistoryApi.sentChunks).to.be.below(sentSoFar + 5)
-              expect(MockV1HistoryApi.sentChunks).to.be.within(
-                sentSoFar,
-                sentSoFar + 2
-              )
-              done()
-            }, 500)
-          }, 200)
-        })
-      })
-
-      it('should skip the v1-history request', function (done) {
-        const request = this.owner.request(
-          `/project/${this.project_id}/version/100/zip`
-        )
-        setTimeout(() => {
-          // This is a race-condition to abort the request after the
-          //  processing of all the the express middleware completed.
-          // In case we abort before they complete, we do not hit our
-          //  abort logic, but express internal logic, which is OK.
-          request.abort()
-        }, 2)
-        request.on('error', done)
-        setTimeout(() => {
-          expect(MockV1HistoryApi.requestedZipPacks).to.equal(0)
-          done()
-        }, 500)
-      })
-
-      it('should skip the async-polling', function (done) {
-        const request = this.owner.request(
-          `/project/${this.project_id}/version/100/zip`
-        )
-        MockV1HistoryApi.events.on('v1-history-pack-zip', () => {
-          request.abort()
-        })
-        request.on('error', done)
-        setTimeout(() => {
-          expect(MockV1HistoryApi.fakeZipCall).to.equal(0)
-          done()
-        }, 3000) // initial polling delay is 2s
-      })
-
-      it('should skip the upstream request', function (done) {
-        const request = this.owner.request(
-          `/project/${this.project_id}/version/100/zip`
-        )
-        MockV1HistoryApi.events.on('v1-history-pack-zip', () => {
-          setTimeout(() => {
-            request.abort()
-          }, 1000)
-        })
-        request.on('error', done)
-        setTimeout(() => {
-          expect(MockV1HistoryApi.fakeZipCall).to.equal(0)
-          done()
-        }, 3000) // initial polling delay is 2s
-      })
-    })
-
-    it('should return 402 for non-v2-history project', function (done) {
-      return this.owner.createProject('non-v2-project', (error, project_id) => {
-        this.project_id = project_id
-        if (error != null) {
-          return done(error)
-        }
-        return db.projects.updateOne(
-          {
-            _id: ObjectId(this.project_id),
-          },
-          {
-            $unset: {
-              'overleaf.history.id': true,
-            },
-          },
-          error => {
-            if (error != null) {
-              return done(error)
-            }
-            return this.owner.request(
-              `/project/${this.project_id}/version/42/zip`,
-              (error, response, body) => {
-                if (error != null) {
-                  return done(error)
-                }
-                expect(response.statusCode).to.equal(402)
-                return done()
-              }
-            )
-          }
-        )
-      })
-    })
-  })
-})

+ 8 - 6
services/web/test/acceptance/src/Init.js

@@ -1,4 +1,5 @@
 require('./helpers/InitApp')
 require('./helpers/InitApp')
+const Features = require('../../../app/src/infrastructure/Features')
 
 
 const MockAnalyticsApi = require('./mocks/MockAnalyticsApi')
 const MockAnalyticsApi = require('./mocks/MockAnalyticsApi')
 const MockChatApi = require('./mocks/MockChatApi')
 const MockChatApi = require('./mocks/MockChatApi')
@@ -8,7 +9,6 @@ const MockDocUpdaterApi = require('./mocks/MockDocUpdaterApi')
 const MockFilestoreApi = require('./mocks/MockFilestoreApi')
 const MockFilestoreApi = require('./mocks/MockFilestoreApi')
 const MockNotificationsApi = require('./mocks/MockNotificationsApi')
 const MockNotificationsApi = require('./mocks/MockNotificationsApi')
 const MockProjectHistoryApi = require('./mocks/MockProjectHistoryApi')
 const MockProjectHistoryApi = require('./mocks/MockProjectHistoryApi')
-const MockRecurlyApi = require('./mocks/MockRecurlyApi')
 const MockSpellingApi = require('./mocks/MockSpellingApi')
 const MockSpellingApi = require('./mocks/MockSpellingApi')
 const MockV1Api = require('./mocks/MockV1Api')
 const MockV1Api = require('./mocks/MockV1Api')
 const MockV1HistoryApi = require('./mocks/MockV1HistoryApi')
 const MockV1HistoryApi = require('./mocks/MockV1HistoryApi')
@@ -17,15 +17,17 @@ const mockOpts = {
   debug: ['1', 'true', 'TRUE'].includes(process.env.DEBUG_MOCKS),
   debug: ['1', 'true', 'TRUE'].includes(process.env.DEBUG_MOCKS),
 }
 }
 
 
-MockAnalyticsApi.initialize(3050, mockOpts)
 MockChatApi.initialize(3010, mockOpts)
 MockChatApi.initialize(3010, mockOpts)
 MockClsiApi.initialize(3013, mockOpts)
 MockClsiApi.initialize(3013, mockOpts)
 MockDocstoreApi.initialize(3016, mockOpts)
 MockDocstoreApi.initialize(3016, mockOpts)
 MockDocUpdaterApi.initialize(3003, mockOpts)
 MockDocUpdaterApi.initialize(3003, mockOpts)
 MockFilestoreApi.initialize(3009, mockOpts)
 MockFilestoreApi.initialize(3009, mockOpts)
 MockNotificationsApi.initialize(3042, mockOpts)
 MockNotificationsApi.initialize(3042, mockOpts)
-MockProjectHistoryApi.initialize(3054, mockOpts)
-MockRecurlyApi.initialize(6034, mockOpts)
 MockSpellingApi.initialize(3005, mockOpts)
 MockSpellingApi.initialize(3005, mockOpts)
-MockV1Api.initialize(5000, mockOpts)
-MockV1HistoryApi.initialize(3100, mockOpts)
+
+if (Features.hasFeature('saas')) {
+  MockAnalyticsApi.initialize(3050, mockOpts)
+  MockProjectHistoryApi.initialize(3054, mockOpts)
+  MockV1Api.initialize(5000, mockOpts)
+  MockV1HistoryApi.initialize(3100, mockOpts)
+}

+ 0 - 128
services/web/test/acceptance/src/LabelsTests.js

@@ -1,128 +0,0 @@
-/* eslint-disable
-    camelcase,
-    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
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
-const _ = require('underscore')
-const { expect } = require('chai')
-const { ObjectId } = require('mongodb')
-const request = require('./helpers/request')
-
-const User = require('./helpers/User')
-const MockProjectHistoryApiClass = require('./mocks/MockProjectHistoryApi')
-
-let MockProjectHistoryApi
-
-before(function () {
-  MockProjectHistoryApi = MockProjectHistoryApiClass.instance()
-})
-
-describe('Labels', function () {
-  beforeEach(function (done) {
-    this.owner = new User()
-    return this.owner.login(error => {
-      if (error != null) {
-        throw error
-      }
-      return this.owner.createProject(
-        'example-project',
-        { template: 'example' },
-        (error, project_id) => {
-          this.project_id = project_id
-          if (error != null) {
-            throw error
-          }
-          return done()
-        }
-      )
-    })
-  })
-
-  it('getting labels', function (done) {
-    const label_id = new ObjectId().toString()
-    const comment = 'a label comment'
-    const version = 3
-    MockProjectHistoryApi.addLabel(this.project_id, {
-      id: label_id,
-      comment,
-      version,
-    })
-
-    return this.owner.request(
-      {
-        method: 'GET',
-        url: `/project/${this.project_id}/labels`,
-        json: true,
-      },
-      (error, response, body) => {
-        if (error != null) {
-          throw error
-        }
-        expect(response.statusCode).to.equal(200)
-        expect(body).to.deep.equal([{ id: label_id, comment, version }])
-        return done()
-      }
-    )
-  })
-
-  it('creating a label', function (done) {
-    const comment = 'a label comment'
-    const version = 3
-
-    return this.owner.request(
-      {
-        method: 'POST',
-        url: `/project/${this.project_id}/labels`,
-        json: { comment, version },
-      },
-      (error, response, body) => {
-        if (error != null) {
-          throw error
-        }
-        expect(response.statusCode).to.equal(200)
-        const { label_id } = body
-        expect(MockProjectHistoryApi.getLabels(this.project_id)).to.deep.equal([
-          { id: label_id, comment, version },
-        ])
-        return done()
-      }
-    )
-  })
-
-  it('deleting a label', function (done) {
-    const label_id = new ObjectId().toString()
-    const comment = 'a label comment'
-    const version = 3
-    MockProjectHistoryApi.addLabel(this.project_id, {
-      id: label_id,
-      comment,
-      version,
-    })
-
-    return this.owner.request(
-      {
-        method: 'DELETE',
-        url: `/project/${this.project_id}/labels/${label_id}`,
-        json: true,
-      },
-      (error, response, body) => {
-        if (error != null) {
-          throw error
-        }
-        expect(response.statusCode).to.equal(204)
-        expect(MockProjectHistoryApi.getLabels(this.project_id)).to.deep.equal(
-          []
-        )
-        return done()
-      }
-    )
-  })
-})

+ 2 - 2
services/web/test/acceptance/src/ProjectInviteTests.js

@@ -631,7 +631,7 @@ describe('ProjectInviteTests', function () {
     describe('user is not logged in initially', function () {
     describe('user is not logged in initially', function () {
       describe('registration prompt workflow with valid token', function () {
       describe('registration prompt workflow with valid token', function () {
         before(function () {
         before(function () {
-          if (!Features.hasFeature('public-registration')) {
+          if (!Features.hasFeature('registration')) {
             this.skip()
             this.skip()
           }
           }
         })
         })
@@ -662,7 +662,7 @@ describe('ProjectInviteTests', function () {
 
 
       describe('registration prompt workflow with non-valid token', function () {
       describe('registration prompt workflow with non-valid token', function () {
         before(function () {
         before(function () {
-          if (!Features.hasFeature('public-registration')) {
+          if (!Features.hasFeature('registration')) {
             this.skip()
             this.skip()
           }
           }
         })
         })

+ 0 - 1005
services/web/test/acceptance/src/ProjectStructureTests.js

@@ -1,10 +1,7 @@
 const { expect } = require('chai')
 const { expect } = require('chai')
-const mkdirp = require('mkdirp')
 const { ObjectId } = require('mongodb')
 const { ObjectId } = require('mongodb')
 const Path = require('path')
 const Path = require('path')
 const fs = require('fs')
 const fs = require('fs')
-const Settings = require('@overleaf/settings')
-const _ = require('underscore')
 
 
 const { Project } = require('../../../app/src/models/Project')
 const { Project } = require('../../../app/src/models/Project')
 const ProjectGetter = require('../../../app/src/Features/Project/ProjectGetter.js')
 const ProjectGetter = require('../../../app/src/Features/Project/ProjectGetter.js')
@@ -94,34 +91,6 @@ describe('ProjectStructureChanges', function () {
     )
     )
   }
   }
 
 
-  function uploadFile(
-    owner,
-    projectId,
-    folderId,
-    file,
-    name,
-    contentType,
-    callback
-  ) {
-    owner.uploadFileInProject(
-      projectId,
-      folderId,
-      file,
-      name,
-      contentType,
-      callback
-    )
-  }
-
-  function uploadExampleFile(owner, projectId, folderId, callback) {
-    owner.uploadExampleFileInProject(
-      projectId,
-      folderId,
-      '1pixel.png',
-      callback
-    )
-  }
-
   function uploadExampleProject(owner, zipFilename, options, callback) {
   function uploadExampleProject(owner, zipFilename, options, callback) {
     if (typeof options === 'function') {
     if (typeof options === 'function') {
       callback = options
       callback = options
@@ -154,193 +123,10 @@ describe('ProjectStructureChanges', function () {
     )
     )
   }
   }
 
 
-  function moveItem(owner, projectId, type, itemId, folderId, callback) {
-    owner.moveItemInProject(projectId, type, itemId, folderId, callback)
-  }
-
-  function renameItem(owner, projectId, type, itemId, name, callback) {
-    owner.renameItemInProject(projectId, type, itemId, name, callback)
-  }
-
   function deleteItem(owner, projectId, type, itemId, callback) {
   function deleteItem(owner, projectId, type, itemId, callback) {
     owner.deleteItemInProject(projectId, type, itemId, callback)
     owner.deleteItemInProject(projectId, type, itemId, callback)
   }
   }
 
 
-  function verifyVersionIncremented(
-    projectId,
-    oldVersion,
-    updateVersion,
-    increment,
-    callback
-  ) {
-    expect(updateVersion).to.equal(oldVersion + increment)
-
-    ProjectGetter.getProject(projectId, (error, newProject) => {
-      if (error) {
-        return callback(error)
-      }
-
-      expect(newProject.version).to.equal(updateVersion)
-      callback()
-    })
-  }
-
-  describe('creating a project from the example template', function () {
-    let exampleProjectId
-
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId) => {
-        exampleProjectId = projectId
-        done(err)
-      })
-    })
-
-    it('should version creating a doc and a file', function () {
-      const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
-        exampleProjectId
-      )
-      expect(updates.length).to.equal(3)
-      for (const update of updates.slice(0, 2)) {
-        expect(update.type).to.equal('add-doc')
-        expect(update.userId).to.equal(owner._id)
-        expect(update.docLines).to.be.a('string')
-      }
-      expect(_.where(updates, { pathname: '/main.tex' }).length).to.equal(1)
-      expect(_.where(updates, { pathname: '/references.bib' }).length).to.equal(
-        1
-      )
-      expect(updates[2].type).to.equal('add-file')
-      expect(updates[2].userId).to.equal(owner._id)
-      expect(updates[2].pathname).to.equal('/universe.jpg')
-      expect(updates[2].url).to.be.a('string')
-      expect(version).to.equal(3)
-    })
-  })
-
-  describe('duplicating a project', function () {
-    let dupProjectId
-
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId) => {
-        if (err) {
-          return done(err)
-        }
-        owner.request.post(
-          {
-            uri: `/Project/${projectId}/clone`,
-            json: {
-              projectName: 'new.tex',
-            },
-          },
-          (error, res, body) => {
-            if (error) {
-              throw error
-            }
-            if (res.statusCode < 200 || res.statusCode >= 300) {
-              throw new Error(`failed to clone project ${res.statusCode}`)
-            }
-            dupProjectId = body.project_id
-            done()
-          }
-        )
-      })
-    })
-
-    it('should version the docs and files created', function () {
-      const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
-        dupProjectId
-      )
-      expect(updates.length).to.equal(3)
-      for (const update of updates.slice(0, 2)) {
-        expect(update.type).to.equal('add-doc')
-        expect(update.userId).to.equal(owner._id)
-        expect(update.docLines).to.be.a('string')
-      }
-      expect(_.where(updates, { pathname: '/main.tex' }).length).to.equal(1)
-      expect(_.where(updates, { pathname: '/references.bib' }).length).to.equal(
-        1
-      )
-      expect(updates[2].type).to.equal('add-file')
-      expect(updates[2].userId).to.equal(owner._id)
-      expect(updates[2].pathname).to.equal('/universe.jpg')
-      expect(updates[2].url).to.be.a('string')
-      expect(version).to.equal(1)
-    })
-  })
-
-  describe('adding a doc', function () {
-    let exampleProjectId, oldVersion
-
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId) => {
-        if (err) {
-          return done(err)
-        }
-        exampleProjectId = projectId
-        MockDocUpdaterApi.reset()
-
-        ProjectGetter.getProject(projectId, (error, project) => {
-          if (error) {
-            return done(error)
-          }
-          oldVersion = project.version
-          createExampleDoc(owner, projectId, done)
-        })
-      })
-    })
-
-    it('should version the doc added', function (done) {
-      const {
-        updates,
-        version: newVersion,
-      } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-      expect(updates.length).to.equal(1)
-      const update = updates[0]
-      expect(update.type).to.equal('add-doc')
-      expect(update.userId).to.equal(owner._id)
-      expect(update.pathname).to.equal('/new.tex')
-      expect(update.docLines).to.be.a('string')
-
-      verifyVersionIncremented(
-        exampleProjectId,
-        oldVersion,
-        newVersion,
-        1,
-        done
-      )
-    })
-  })
-
-  describe('uploading a project', function () {
-    let exampleProjectId
-
-    beforeEach(function (done) {
-      uploadExampleProject(owner, 'test_project.zip', (err, projectId) => {
-        if (err) {
-          return done(err)
-        }
-        exampleProjectId = projectId
-        done()
-      })
-    })
-
-    it('should version the docs and files created', function () {
-      const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
-        exampleProjectId
-      )
-      expect(updates.length).to.equal(2)
-      expect(updates[0].type).to.equal('add-doc')
-      expect(updates[0].userId).to.equal(owner._id)
-      expect(updates[0].pathname).to.equal('/main.tex')
-      expect(updates[0].docLines).to.equal('Test')
-      expect(updates[1].type).to.equal('add-file')
-      expect(updates[1].userId).to.equal(owner._id)
-      expect(updates[1].pathname).to.equal('/1pixel.png')
-      expect(updates[1].url).to.be.a('string')
-      expect(version).to.equal(1)
-    })
-  })
-
   describe('uploading a project with a name', function () {
   describe('uploading a project with a name', function () {
     let exampleProjectId
     let exampleProjectId
     const testProjectName = 'wombat'
     const testProjectName = 'wombat'
@@ -496,509 +282,6 @@ describe('ProjectStructureChanges', function () {
     })
     })
   })
   })
 
 
-  describe('uploading a project with files in different encodings', function () {
-    let updates
-    beforeEach(function (done) {
-      uploadExampleProject(owner, 'charsets/charsets.zip', (err, projectId) => {
-        if (err) {
-          return done(err)
-        }
-
-        updates = MockDocUpdaterApi.getProjectStructureUpdates(projectId)
-          .updates
-        done()
-      })
-    })
-
-    it('should correctly parse windows-1252', function () {
-      const update = _.find(
-        updates,
-        update => update.pathname === '/test-german-windows-1252.tex'
-      )
-      expect(update.docLines).to.contain(
-        'Der schnelle braune Fuchs sprang träge über den Hund.'
-      )
-    })
-
-    it('should correctly parse German utf8', function () {
-      const update = _.find(
-        updates,
-        update => update.pathname === '/test-german-utf8x.tex'
-      )
-      expect(update.docLines).to.contain(
-        'Der schnelle braune Fuchs sprang träge über den Hund.'
-      )
-    })
-
-    it('should correctly parse little-endian utf16', function () {
-      const update = _.find(
-        updates,
-        update => update.pathname === '/test-greek-utf16-le-bom.tex'
-      )
-      expect(update.docLines).to.contain(
-        'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
-      )
-    })
-
-    it('should correctly parse Greek utf8', function () {
-      const update = _.find(
-        updates,
-        update => update.pathname === '/test-greek-utf8x.tex'
-      )
-      expect(update.docLines).to.contain(
-        'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
-      )
-    })
-  })
-
-  describe('uploading a file', function () {
-    let exampleProjectId, oldVersion, rootFolderId
-
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId, folderId) => {
-        if (err) {
-          return done(err)
-        }
-        exampleProjectId = projectId
-        rootFolderId = folderId
-        MockDocUpdaterApi.reset()
-        ProjectGetter.getProject(projectId, (error, project) => {
-          if (error) {
-            throw error
-          }
-
-          oldVersion = project.version
-
-          uploadExampleFile(owner, projectId, rootFolderId, done)
-        })
-      })
-    })
-
-    it('should version a newly uploaded file', function (done) {
-      const { updates, version } = MockDocUpdaterApi.getProjectStructureUpdates(
-        exampleProjectId
-      )
-      expect(updates.length).to.equal(1)
-      const update = updates[0]
-      expect(update.type).to.equal('add-file')
-      expect(update.userId).to.equal(owner._id)
-      expect(update.pathname).to.equal('/1pixel.png')
-      expect(update.url).to.be.a('string')
-
-      // one file upload
-      verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
-    })
-
-    it('should version a replacement file', function (done) {
-      MockDocUpdaterApi.reset()
-
-      uploadFile(
-        owner,
-        exampleProjectId,
-        rootFolderId,
-        '2pixel.png',
-        '1pixel.png',
-        'image/png',
-        () => {
-          const {
-            updates,
-            version,
-          } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-          expect(updates.length).to.equal(2)
-          expect(updates[0].type).to.equal('rename-file')
-          expect(updates[0].userId).to.equal(owner._id)
-          expect(updates[0].pathname).to.equal('/1pixel.png')
-          expect(updates[0].newPathname).to.equal('')
-          expect(updates[1].type).to.equal('add-file')
-          expect(updates[1].userId).to.equal(owner._id)
-          expect(updates[1].pathname).to.equal('/1pixel.png')
-          expect(updates[1].url).to.be.a('string')
-
-          // two file uploads
-          verifyVersionIncremented(
-            exampleProjectId,
-            oldVersion,
-            version,
-            2,
-            done
-          )
-        }
-      )
-    })
-  })
-
-  describe('moving entities', function () {
-    let exampleProjectId,
-      oldVersion,
-      exampleDocId,
-      exampleFileId,
-      exampleFolderId
-
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId, rootFolderId) => {
-        if (err) {
-          return done(err)
-        }
-        exampleProjectId = projectId
-        createExampleDoc(owner, projectId, (err, docId) => {
-          if (err) {
-            return done(err)
-          }
-          exampleDocId = docId
-          uploadExampleFile(owner, projectId, rootFolderId, (err, fileId) => {
-            if (err) {
-              return done(err)
-            }
-            exampleFileId = fileId
-            createExampleFolder(owner, projectId, (err, folderId) => {
-              if (err) {
-                return done(err)
-              }
-              exampleFolderId = folderId
-
-              ProjectGetter.getProject(projectId, (error, project) => {
-                if (error) {
-                  throw error
-                }
-                oldVersion = project.version
-                MockDocUpdaterApi.reset()
-                done()
-              })
-            })
-          })
-        })
-      })
-    })
-
-    it('should version moving a doc', function (done) {
-      moveItem(
-        owner,
-        exampleProjectId,
-        'doc',
-        exampleDocId,
-        exampleFolderId,
-        () => {
-          const {
-            updates,
-            version,
-          } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-          expect(updates.length).to.equal(1)
-          const update = updates[0]
-          expect(update.type).to.equal('rename-doc')
-          expect(update.userId).to.equal(owner._id)
-          expect(update.pathname).to.equal('/new.tex')
-          expect(update.newPathname).to.equal('/foo/new.tex')
-
-          // 2, because it's a delete and then add
-          verifyVersionIncremented(
-            exampleProjectId,
-            oldVersion,
-            version,
-            2,
-            done
-          )
-        }
-      )
-    })
-
-    it('should version moving a file', function (done) {
-      moveItem(
-        owner,
-        exampleProjectId,
-        'file',
-        exampleFileId,
-        exampleFolderId,
-        () => {
-          const {
-            updates,
-            version,
-          } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-          expect(updates.length).to.equal(1)
-          const update = updates[0]
-          expect(update.type).to.equal('rename-file')
-          expect(update.userId).to.equal(owner._id)
-          expect(update.pathname).to.equal('/1pixel.png')
-          expect(update.newPathname).to.equal('/foo/1pixel.png')
-
-          // 2, because it's a delete and then add
-          verifyVersionIncremented(
-            exampleProjectId,
-            oldVersion,
-            version,
-            2,
-            done
-          )
-        }
-      )
-    })
-
-    it('should version moving a folder', function (done) {
-      moveItem(
-        owner,
-        exampleProjectId,
-        'doc',
-        exampleDocId,
-        exampleFolderId,
-        () => {
-          MockDocUpdaterApi.reset()
-
-          owner.request.post(
-            {
-              uri: `project/${exampleProjectId}/folder`,
-              json: {
-                name: 'bar',
-              },
-            },
-            (error, res, body) => {
-              if (error) {
-                throw error
-              }
-              const newFolderId = body._id
-
-              moveItem(
-                owner,
-                exampleProjectId,
-                'folder',
-                exampleFolderId,
-                newFolderId,
-                () => {
-                  const {
-                    updates,
-                    version,
-                  } = MockDocUpdaterApi.getProjectStructureUpdates(
-                    exampleProjectId
-                  )
-                  expect(updates.length).to.equal(1)
-                  const update = updates[0]
-                  expect(update.type).to.equal('rename-doc')
-                  expect(update.userId).to.equal(owner._id)
-                  expect(update.pathname).to.equal('/foo/new.tex')
-                  expect(update.newPathname).to.equal('/bar/foo/new.tex')
-
-                  // 5, because it's two file moves plus a folder
-                  verifyVersionIncremented(
-                    exampleProjectId,
-                    oldVersion,
-                    version,
-                    5,
-                    done
-                  )
-                }
-              )
-            }
-          )
-        }
-      )
-    })
-  })
-
-  describe('renaming entities', function () {
-    let exampleProjectId,
-      exampleDocId,
-      exampleFileId,
-      exampleFolderId,
-      oldVersion
-
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId, rootFolderId) => {
-        if (err) {
-          return done(err)
-        }
-        exampleProjectId = projectId
-        createExampleDoc(owner, projectId, (err, docId) => {
-          if (err) {
-            return done(err)
-          }
-          exampleDocId = docId
-          uploadExampleFile(owner, projectId, rootFolderId, (err, fileId) => {
-            if (err) {
-              return done(err)
-            }
-            exampleFileId = fileId
-            createExampleFolder(owner, projectId, (err, folderId) => {
-              if (err) {
-                return done(err)
-              }
-              exampleFolderId = folderId
-              moveItem(owner, projectId, 'doc', docId, folderId, () => {
-                moveItem(owner, projectId, 'file', fileId, folderId, () => {
-                  MockDocUpdaterApi.reset()
-                  ProjectGetter.getProject(
-                    exampleProjectId,
-                    (error, project) => {
-                      if (error) {
-                        throw error
-                      }
-                      oldVersion = project.version
-                      done()
-                    }
-                  )
-                })
-              })
-            })
-          })
-        })
-      })
-    })
-
-    it('should version renaming a doc', function (done) {
-      renameItem(
-        owner,
-        exampleProjectId,
-        'Doc',
-        exampleDocId,
-        'wombat.tex',
-        () => {
-          const {
-            updates,
-            version,
-          } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-          expect(updates.length).to.equal(1)
-          const update = updates[0]
-          expect(update.type).to.equal('rename-doc')
-          expect(update.userId).to.equal(owner._id)
-          expect(update.pathname).to.equal('/foo/new.tex')
-          expect(update.newPathname).to.equal('/foo/wombat.tex')
-
-          verifyVersionIncremented(
-            exampleProjectId,
-            oldVersion,
-            version,
-            1,
-            done
-          )
-        }
-      )
-    })
-
-    it('should version renaming a file', function (done) {
-      renameItem(
-        owner,
-        exampleProjectId,
-        'file',
-        exampleFileId,
-        'potato.png',
-        () => {
-          const {
-            updates,
-            version,
-          } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-          expect(updates.length).to.equal(1)
-          const update = updates[0]
-          expect(update.type).to.equal('rename-file')
-          expect(update.userId).to.equal(owner._id)
-          expect(update.pathname).to.equal('/foo/1pixel.png')
-          expect(update.newPathname).to.equal('/foo/potato.png')
-
-          verifyVersionIncremented(
-            exampleProjectId,
-            oldVersion,
-            version,
-            1,
-            done
-          )
-        }
-      )
-    })
-
-    it('should version renaming a folder', function (done) {
-      renameItem(
-        owner,
-        exampleProjectId,
-        'folder',
-        exampleFolderId,
-        'giraffe',
-        () => {
-          const {
-            updates,
-            version,
-          } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-          expect(updates.length).to.equal(2)
-          expect(updates[0].type).to.equal('rename-doc')
-          expect(updates[0].userId).to.equal(owner._id)
-          expect(updates[0].pathname).to.equal('/foo/new.tex')
-          expect(updates[0].newPathname).to.equal('/giraffe/new.tex')
-          expect(updates[1].type).to.equal('rename-file')
-          expect(updates[1].userId).to.equal(owner._id)
-          expect(updates[1].pathname).to.equal('/foo/1pixel.png')
-          expect(updates[1].newPathname).to.equal('/giraffe/1pixel.png')
-
-          verifyVersionIncremented(
-            exampleProjectId,
-            oldVersion,
-            version,
-            1,
-            done
-          )
-        }
-      )
-    })
-  })
-
-  describe('deleting entities', function () {
-    let exampleProjectId, oldVersion, exampleFolderId
-
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId) => {
-        if (err) {
-          return done(err)
-        }
-        exampleProjectId = projectId
-        createExampleFolder(owner, exampleProjectId, (err, folderId) => {
-          if (err) {
-            return done(err)
-          }
-          exampleFolderId = folderId
-          createExampleDoc(owner, projectId, (err, docId) => {
-            if (err) {
-              return done(err)
-            }
-            uploadExampleFile(owner, projectId, folderId, (err, fileId) => {
-              if (err) {
-                return done(err)
-              }
-              moveItem(owner, projectId, 'doc', docId, folderId, () => {
-                moveItem(owner, projectId, 'file', fileId, folderId, () => {
-                  MockDocUpdaterApi.reset()
-                  ProjectGetter.getProject(
-                    exampleProjectId,
-                    (error, project) => {
-                      if (error) {
-                        throw error
-                      }
-                      oldVersion = project.version
-                      done()
-                    }
-                  )
-                })
-              })
-            })
-          })
-        })
-      })
-    })
-
-    it('should version deleting a folder', function (done) {
-      deleteItem(owner, exampleProjectId, 'folder', exampleFolderId, () => {
-        const {
-          updates,
-          version,
-        } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-        expect(updates.length).to.equal(2)
-        expect(updates[0].type).to.equal('rename-doc')
-        expect(updates[0].userId).to.equal(owner._id)
-        expect(updates[0].pathname).to.equal('/foo/new.tex')
-        expect(updates[0].newPathname).to.equal('')
-        expect(updates[1].type).to.equal('rename-file')
-        expect(updates[1].userId).to.equal(owner._id)
-        expect(updates[1].pathname).to.equal('/foo/1pixel.png')
-        expect(updates[1].newPathname).to.equal('')
-
-        verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
-      })
-    })
-  })
-
   describe('deleting docs', function () {
   describe('deleting docs', function () {
     beforeEach(function (done) {
     beforeEach(function (done) {
       createExampleProject(owner, (err, projectId) => {
       createExampleProject(owner, (err, projectId) => {
@@ -1113,292 +396,4 @@ describe('ProjectStructureChanges', function () {
       })
       })
     })
     })
   })
   })
-
-  describe('tpds', function () {
-    let projectName, exampleProjectId, oldVersion, rootFolderId
-
-    beforeEach(function (done) {
-      projectName = `tpds-project-${new ObjectId().toString()}`
-      owner.createProject(projectName, (error, projectId) => {
-        if (error) {
-          throw error
-        }
-        exampleProjectId = projectId
-        mkdirp(Settings.path.dumpFolder, () => {
-          ProjectGetter.getProject(exampleProjectId, (error, project) => {
-            if (error) {
-              throw error
-            }
-            MockDocUpdaterApi.reset()
-            rootFolderId = project.rootFolder[0]._id.toString()
-            oldVersion = project.version
-            done()
-          })
-        })
-      })
-    })
-
-    it('should version adding a doc', function (done) {
-      const texFile = fs.createReadStream(
-        Path.resolve(Path.join(__dirname, '..', 'files', 'test.tex'))
-      )
-
-      const req = owner.request.post({
-        uri: `/user/${owner._id}/update/${projectName}/test.tex`,
-        auth: {
-          user: _.keys(Settings.httpAuthUsers)[0],
-          pass: _.values(Settings.httpAuthUsers)[0],
-          sendImmediately: true,
-        },
-      })
-
-      texFile.on('error', err => {
-        throw err
-      })
-
-      req.on('error', err => {
-        throw err
-      })
-
-      req.on('response', res => {
-        if (res.statusCode < 200 || res.statusCode >= 300) {
-          throw new Error(`failed to upload file ${res.statusCode}`)
-        }
-
-        const {
-          updates,
-          version,
-        } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-        expect(updates.length).to.equal(1)
-        const update = updates[0]
-        expect(update.type).to.equal('add-doc')
-        expect(update.userId).to.equal(owner._id)
-        expect(update.pathname).to.equal('/test.tex')
-        expect(update.docLines).to.equal('Test')
-
-        verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
-      })
-
-      texFile.pipe(req)
-    })
-
-    it('should version adding a new file', function (done) {
-      const imageFile = fs.createReadStream(
-        Path.resolve(Path.join(__dirname, '..', 'files', '1pixel.png'))
-      )
-
-      const req = owner.request.post({
-        uri: `/user/${owner._id}/update/${projectName}/1pixel.png`,
-        auth: {
-          user: _.keys(Settings.httpAuthUsers)[0],
-          pass: _.values(Settings.httpAuthUsers)[0],
-          sendImmediately: true,
-        },
-      })
-
-      imageFile.on('error', err => {
-        throw err
-      })
-
-      req.on('error', err => {
-        throw err
-      })
-
-      req.on('response', res => {
-        if (res.statusCode < 200 || res.statusCode >= 300) {
-          throw new Error(`failed to upload file ${res.statusCode}`)
-        }
-
-        const {
-          updates,
-          version,
-        } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-        expect(updates.length).to.equal(1)
-        const update = updates[0]
-        expect(update.type).to.equal('add-file')
-        expect(update.userId).to.equal(owner._id)
-        expect(update.pathname).to.equal('/1pixel.png')
-        expect(update.url).to.be.a('string')
-
-        verifyVersionIncremented(exampleProjectId, oldVersion, version, 1, done)
-      })
-
-      imageFile.pipe(req)
-    })
-
-    describe('when there are files in the project', function () {
-      beforeEach(function (done) {
-        uploadExampleFile(owner, exampleProjectId, rootFolderId, () => {
-          createExampleDoc(owner, exampleProjectId, () => {
-            ProjectGetter.getProject(exampleProjectId, (error, project) => {
-              if (error) {
-                throw error
-              }
-              MockDocUpdaterApi.reset()
-              oldVersion = project.version
-              done()
-            })
-          })
-        })
-      })
-
-      it('should version replacing a file', function (done) {
-        const imageFile = fs.createReadStream(
-          Path.resolve(Path.join(__dirname, '..', 'files', '2pixel.png'))
-        )
-
-        const req = owner.request.post({
-          uri: `/user/${owner._id}/update/${projectName}/1pixel.png`,
-          auth: {
-            user: _.keys(Settings.httpAuthUsers)[0],
-            pass: _.values(Settings.httpAuthUsers)[0],
-            sendImmediately: true,
-          },
-        })
-
-        imageFile.on('error', err => {
-          throw err
-        })
-
-        req.on('error', err => {
-          throw err
-        })
-
-        req.on('response', res => {
-          if (res.statusCode < 200 || res.statusCode >= 300) {
-            throw new Error(`failed to upload file ${res.statusCode}`)
-          }
-
-          const {
-            updates,
-            version,
-          } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-          expect(updates.length).to.equal(2)
-          expect(updates[0].type).to.equal('rename-file')
-          expect(updates[0].userId).to.equal(owner._id)
-          expect(updates[0].pathname).to.equal('/1pixel.png')
-          expect(updates[0].newPathname).to.equal('')
-          expect(updates[1].type).to.equal('add-file')
-          expect(updates[1].userId).to.equal(owner._id)
-          expect(updates[1].pathname).to.equal('/1pixel.png')
-          expect(updates[1].url).to.be.a('string')
-
-          verifyVersionIncremented(
-            exampleProjectId,
-            oldVersion,
-            version,
-            1,
-            done
-          )
-        })
-
-        imageFile.pipe(req)
-      })
-
-      it('should version deleting a doc', function (done) {
-        owner.request.delete(
-          {
-            uri: `/user/${owner._id}/update/${projectName}/new.tex`,
-            auth: {
-              user: _.keys(Settings.httpAuthUsers)[0],
-              pass: _.values(Settings.httpAuthUsers)[0],
-              sendImmediately: true,
-            },
-          },
-          (error, res) => {
-            if (error) {
-              throw error
-            }
-            if (res.statusCode < 200 || res.statusCode >= 300) {
-              throw new Error(`failed to delete doc ${res.statusCode}`)
-            }
-
-            const {
-              updates,
-              version,
-            } = MockDocUpdaterApi.getProjectStructureUpdates(exampleProjectId)
-            expect(updates.length).to.equal(1)
-            const update = updates[0]
-            expect(update.type).to.equal('rename-doc')
-            expect(update.userId).to.equal(owner._id)
-            expect(update.pathname).to.equal('/new.tex')
-            expect(update.newPathname).to.equal('')
-
-            verifyVersionIncremented(
-              exampleProjectId,
-              oldVersion,
-              version,
-              1,
-              done
-            )
-          }
-        )
-      })
-    })
-  })
-
-  describe('uploading a document', function () {
-    let exampleProjectId, rootFolderId
-    beforeEach(function (done) {
-      createExampleProject(owner, (err, projectId, folderId) => {
-        if (err) {
-          return done(err)
-        }
-        exampleProjectId = projectId
-        rootFolderId = folderId
-        MockDocUpdaterApi.reset()
-        done()
-      })
-    })
-
-    describe('with an unusual character set', function () {
-      it('should correctly handle utf16-le data', function (done) {
-        uploadFile(
-          owner,
-          exampleProjectId,
-          rootFolderId,
-          'charsets/test-greek-utf16-le-bom.tex',
-          'test-greek-utf16-le-bom.tex',
-          'text/x-tex',
-          () => {
-            const { updates } = MockDocUpdaterApi.getProjectStructureUpdates(
-              exampleProjectId
-            )
-            expect(updates.length).to.equal(1)
-            const update = updates[0]
-            expect(update.type).to.equal('add-doc')
-            expect(update.pathname).to.equal('/test-greek-utf16-le-bom.tex')
-            expect(update.docLines).to.contain(
-              'Η γρήγορη καστανή αλεπού πήδηξε χαλαρά πάνω από το σκυλί.'
-            )
-            done()
-          }
-        )
-      })
-
-      it('should correctly handle windows1252/iso-8859-1/latin1 data', function (done) {
-        uploadFile(
-          owner,
-          exampleProjectId,
-          rootFolderId,
-          'charsets/test-german-windows-1252.tex',
-          'test-german-windows-1252.tex',
-          'text/x-tex',
-          () => {
-            const { updates } = MockDocUpdaterApi.getProjectStructureUpdates(
-              exampleProjectId
-            )
-            expect(updates.length).to.equal(1)
-            const update = updates[0]
-            expect(update.type).to.equal('add-doc')
-            expect(update.pathname).to.equal('/test-german-windows-1252.tex')
-            expect(update.docLines).to.contain(
-              'Der schnelle braune Fuchs sprang träge über den Hund.'
-            )
-            done()
-          }
-        )
-      })
-    })
-  })
 })
 })

+ 0 - 76
services/web/test/acceptance/src/ProxyUrls.js

@@ -1,76 +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 { assert, expect } = require('chai')
-const async = require('async')
-const request = require('./helpers/request')
-
-const assertResponse = (path, expectedStatusCode, expectedBody, cb) =>
-  request.get(path, (error, response) => {
-    expect(error).not.to.exist
-    response.statusCode.should.equal(expectedStatusCode)
-    if (expectedBody) {
-      assert.deepEqual(JSON.parse(response.body), expectedBody)
-    }
-    return cb()
-  })
-
-describe('ProxyUrls', function () {
-  beforeEach(function () {
-    return this.timeout(1000)
-  })
-
-  it('proxy static URLs', function (done) {
-    return async.series(
-      [
-        cb => assertResponse('/institutions/list', 200, [], cb),
-        cb => assertResponse('/institutions/domains', 200, [], cb),
-      ],
-      done
-    )
-  })
-
-  it('proxy dynamic URLs', function (done) {
-    return async.series(
-      [
-        cb =>
-          assertResponse(
-            '/institutions/list/123',
-            200,
-            { id: 123, name: 'Institution 123' },
-            cb
-          ),
-        cb =>
-          assertResponse(
-            '/institutions/list/456',
-            200,
-            { id: 456, name: 'Institution 456' },
-            cb
-          ),
-      ],
-      done
-    )
-  })
-
-  it('return 404 if proxy is not set', function (done) {
-    return async.series(
-      [cb => assertResponse('/institutions/foobar', 404, null, cb)],
-      done
-    )
-  })
-
-  it('handle missing baseUrl', function (done) {
-    return async.series(
-      [cb => assertResponse('/proxy/missing/baseUrl', 500, null, cb)],
-      done
-    )
-  })
-})

+ 0 - 43
services/web/test/acceptance/src/RecurlySubscriptionUpdateTests.js

@@ -1,43 +0,0 @@
-const { expect } = require('chai')
-const async = require('async')
-const User = require('./helpers/User')
-const RecurlySubscription = require('./helpers/RecurlySubscription')
-
-describe('Subscriptions', function () {
-  describe('update', function () {
-    beforeEach(function (done) {
-      this.recurlyUser = new User()
-      async.series(
-        [
-          cb => this.recurlyUser.ensureUserExists(cb),
-          cb => {
-            this.recurlySubscription = new RecurlySubscription({
-              adminId: this.recurlyUser._id,
-              account: {
-                email: 'stale-recurly@email.com',
-              },
-            })
-            this.recurlySubscription.ensureExists(cb)
-          },
-          cb => this.recurlyUser.login(cb),
-        ],
-        done
-      )
-    })
-
-    it('updates the email address for the account', function (done) {
-      const url = '/user/subscription/account/email'
-
-      this.recurlyUser.request.post({ url }, (error, { statusCode }) => {
-        if (error) {
-          return done(error)
-        }
-        expect(statusCode).to.equal(200)
-        expect(this.recurlyUser.email).to.equal(
-          this.recurlySubscription.account.email
-        )
-        done()
-      })
-    })
-  })
-})

+ 3 - 39
services/web/test/acceptance/src/RegistrationTests.js

@@ -10,9 +10,6 @@ const UserPromises = require('./helpers/User').promises
 const redis = require('./helpers/redis')
 const redis = require('./helpers/redis')
 const Features = require('../../../app/src/infrastructure/Features')
 const Features = require('../../../app/src/infrastructure/Features')
 
 
-// Currently this is testing registration via the 'public-registration' module,
-// whereas in production we're using the 'overleaf-integration' module.
-
 // Expectations
 // Expectations
 const expectProjectAccess = function (user, projectId, callback) {
 const expectProjectAccess = function (user, projectId, callback) {
   // should have access to project
   // should have access to project
@@ -220,7 +217,7 @@ describe('Registration', function () {
 
 
   describe('CSRF protection', function () {
   describe('CSRF protection', function () {
     before(function () {
     before(function () {
-      if (!Features.hasFeature('public-registration')) {
+      if (!Features.hasFeature('registration')) {
         this.skip()
         this.skip()
       }
       }
     })
     })
@@ -311,7 +308,7 @@ describe('Registration', function () {
 
 
   describe('Register', function () {
   describe('Register', function () {
     before(function () {
     before(function () {
-      if (!Features.hasFeature('public-registration')) {
+      if (!Features.hasFeature('registration')) {
         this.skip()
         this.skip()
       }
       }
     })
     })
@@ -333,39 +330,6 @@ describe('Registration', function () {
     })
     })
   })
   })
 
 
-  describe('Register with bonus referal id', function () {
-    before(function () {
-      if (!Features.hasFeature('public-registration')) {
-        this.skip()
-      }
-    })
-
-    beforeEach(function (done) {
-      this.user1 = new User()
-      this.user2 = new User()
-      async.series(
-        [
-          cb => this.user1.register(cb),
-          cb =>
-            this.user2.registerWithQuery(
-              `?r=${this.user1.referal_id}&rm=d&rs=b`,
-              cb
-            ),
-        ],
-        done
-      )
-    })
-
-    it('Adds a referal when an id is supplied and the referal source is "bonus"', function (done) {
-      this.user1.get((error, user) => {
-        expect(error).to.not.exist
-        user.refered_user_count.should.eql(1)
-
-        return done()
-      })
-    })
-  })
-
   describe('LoginViaRegistration', function () {
   describe('LoginViaRegistration', function () {
     beforeEach(function (done) {
     beforeEach(function (done) {
       this.timeout(60000)
       this.timeout(60000)
@@ -387,7 +351,7 @@ describe('Registration', function () {
 
 
     describe('[Security] Trying to register/login as another user', function () {
     describe('[Security] Trying to register/login as another user', function () {
       before(function () {
       before(function () {
-        if (!Features.hasFeature('public-registration')) {
+        if (!Features.hasFeature('registration')) {
           this.skip()
           this.skip()
         }
         }
       })
       })

+ 1 - 246
services/web/test/acceptance/src/RestoringFilesTest.js

@@ -11,23 +11,16 @@
  * DS207: Consider shorter variations of null checks
  * DS207: Consider shorter variations of null checks
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
  */
-const async = require('async')
 const { expect } = require('chai')
 const { expect } = require('chai')
 const _ = require('underscore')
 const _ = require('underscore')
-const fs = require('fs')
-const Path = require('path')
-
-const ProjectGetter = require('../../../app/src/Features/Project/ProjectGetter.js')
 
 
 const User = require('./helpers/User')
 const User = require('./helpers/User')
-const MockProjectHistoryApiClass = require('./mocks/MockProjectHistoryApi')
 const MockDocstoreApiClass = require('./mocks/MockDocstoreApi')
 const MockDocstoreApiClass = require('./mocks/MockDocstoreApi')
 const MockFilestoreApiClass = require('./mocks/MockFilestoreApi')
 const MockFilestoreApiClass = require('./mocks/MockFilestoreApi')
 
 
-let MockProjectHistoryApi, MockDocstoreApi, MockFilestoreApi
+let MockDocstoreApi, MockFilestoreApi
 
 
 before(function () {
 before(function () {
-  MockProjectHistoryApi = MockProjectHistoryApiClass.instance()
   MockDocstoreApi = MockDocstoreApiClass.instance()
   MockDocstoreApi = MockDocstoreApiClass.instance()
   MockFilestoreApi = MockFilestoreApiClass.instance()
   MockFilestoreApi = MockFilestoreApiClass.instance()
 })
 })
@@ -116,242 +109,4 @@ describe('RestoringFiles', function () {
       })
       })
     })
     })
   })
   })
-
-  describe('restoring from v2 history', function () {
-    describe('restoring a text file', function () {
-      beforeEach(function (done) {
-        MockProjectHistoryApi.addOldFile(
-          this.project_id,
-          42,
-          'foo.tex',
-          'hello world, this is foo.tex!'
-        )
-        return this.owner.request(
-          {
-            method: 'POST',
-            url: `/project/${this.project_id}/restore_file`,
-            json: {
-              pathname: 'foo.tex',
-              version: 42,
-            },
-          },
-          (error, response, body) => {
-            if (error != null) {
-              throw error
-            }
-            expect(response.statusCode).to.equal(200)
-            return done()
-          }
-        )
-      })
-
-      it('should have created a doc', function (done) {
-        return this.owner.getProject(this.project_id, (error, project) => {
-          if (error != null) {
-            throw error
-          }
-          let doc = _.find(
-            project.rootFolder[0].docs,
-            doc => doc.name === 'foo.tex'
-          )
-          doc = MockDocstoreApi.docs[this.project_id][doc._id]
-          expect(doc.lines).to.deep.equal(['hello world, this is foo.tex!'])
-          return done()
-        })
-      })
-    })
-
-    describe('restoring a binary file', function () {
-      beforeEach(function (done) {
-        this.pngData = fs.readFileSync(
-          Path.resolve(__dirname, '../files/1pixel.png'),
-          'binary'
-        )
-        MockProjectHistoryApi.addOldFile(
-          this.project_id,
-          42,
-          'image.png',
-          this.pngData
-        )
-        return this.owner.request(
-          {
-            method: 'POST',
-            url: `/project/${this.project_id}/restore_file`,
-            json: {
-              pathname: 'image.png',
-              version: 42,
-            },
-          },
-          (error, response, body) => {
-            if (error != null) {
-              throw error
-            }
-            expect(response.statusCode).to.equal(200)
-            return done()
-          }
-        )
-      })
-
-      it('should have created a file', function (done) {
-        return this.owner.getProject(this.project_id, (error, project) => {
-          if (error != null) {
-            throw error
-          }
-          let file = _.find(
-            project.rootFolder[0].fileRefs,
-            file => file.name === 'image.png'
-          )
-          file = MockFilestoreApi.files[this.project_id][file._id]
-          expect(file.content).to.equal(this.pngData)
-          return done()
-        })
-      })
-    })
-
-    describe('restoring to a directory that exists', function () {
-      beforeEach(function (done) {
-        MockProjectHistoryApi.addOldFile(
-          this.project_id,
-          42,
-          'foldername/foo2.tex',
-          'hello world, this is foo-2.tex!'
-        )
-        return this.owner.request.post(
-          {
-            uri: `project/${this.project_id}/folder`,
-            json: {
-              name: 'foldername',
-            },
-          },
-          (error, response, body) => {
-            if (error != null) {
-              throw error
-            }
-            expect(response.statusCode).to.equal(200)
-            return this.owner.request(
-              {
-                method: 'POST',
-                url: `/project/${this.project_id}/restore_file`,
-                json: {
-                  pathname: 'foldername/foo2.tex',
-                  version: 42,
-                },
-              },
-              (error, response, body) => {
-                if (error != null) {
-                  throw error
-                }
-                expect(response.statusCode).to.equal(200)
-                return done()
-              }
-            )
-          }
-        )
-      })
-
-      it('should have created the doc in the named folder', function (done) {
-        return this.owner.getProject(this.project_id, (error, project) => {
-          if (error != null) {
-            throw error
-          }
-          const folder = _.find(
-            project.rootFolder[0].folders,
-            folder => folder.name === 'foldername'
-          )
-          let doc = _.find(folder.docs, doc => doc.name === 'foo2.tex')
-          doc = MockDocstoreApi.docs[this.project_id][doc._id]
-          expect(doc.lines).to.deep.equal(['hello world, this is foo-2.tex!'])
-          return done()
-        })
-      })
-    })
-
-    describe('restoring to a directory that no longer exists', function () {
-      beforeEach(function (done) {
-        MockProjectHistoryApi.addOldFile(
-          this.project_id,
-          42,
-          'nothere/foo3.tex',
-          'hello world, this is foo-3.tex!'
-        )
-        return this.owner.request(
-          {
-            method: 'POST',
-            url: `/project/${this.project_id}/restore_file`,
-            json: {
-              pathname: 'nothere/foo3.tex',
-              version: 42,
-            },
-          },
-          (error, response, body) => {
-            if (error != null) {
-              throw error
-            }
-            expect(response.statusCode).to.equal(200)
-            return done()
-          }
-        )
-      })
-
-      it('should have created the folder and restored the doc to it', function (done) {
-        return this.owner.getProject(this.project_id, (error, project) => {
-          if (error != null) {
-            throw error
-          }
-          const folder = _.find(
-            project.rootFolder[0].folders,
-            folder => folder.name === 'nothere'
-          )
-          expect(folder).to.exist
-          let doc = _.find(folder.docs, doc => doc.name === 'foo3.tex')
-          doc = MockDocstoreApi.docs[this.project_id][doc._id]
-          expect(doc.lines).to.deep.equal(['hello world, this is foo-3.tex!'])
-          return done()
-        })
-      })
-    })
-
-    describe('restoring to a filename that already exists', function () {
-      beforeEach(function (done) {
-        MockProjectHistoryApi.addOldFile(
-          this.project_id,
-          42,
-          'main.tex',
-          'hello world, this is main.tex!'
-        )
-        return this.owner.request(
-          {
-            method: 'POST',
-            url: `/project/${this.project_id}/restore_file`,
-            json: {
-              pathname: 'main.tex',
-              version: 42,
-            },
-          },
-          (error, response, body) => {
-            if (error != null) {
-              throw error
-            }
-            expect(response.statusCode).to.equal(200)
-            return done()
-          }
-        )
-      })
-
-      it('should have created the doc in the root folder', function (done) {
-        return this.owner.getProject(this.project_id, (error, project) => {
-          if (error != null) {
-            throw error
-          }
-          let doc = _.find(project.rootFolder[0].docs, doc =>
-            doc.name.match(/main \(Restored on/)
-          )
-          expect(doc).to.exist
-          doc = MockDocstoreApi.docs[this.project_id][doc._id]
-          expect(doc.lines).to.deep.equal(['hello world, this is main.tex!'])
-          return done()
-        })
-      })
-    })
-  })
 })
 })

+ 0 - 42
services/web/test/acceptance/src/SettingsTests.js

@@ -11,36 +11,14 @@
 const { expect } = require('chai')
 const { expect } = require('chai')
 const async = require('async')
 const async = require('async')
 const User = require('./helpers/User')
 const User = require('./helpers/User')
-const MockV1ApiClass = require('./mocks/MockV1Api')
-
-let MockV1Api
-
-before(function () {
-  MockV1Api = MockV1ApiClass.instance()
-})
 
 
 describe('SettingsPage', function () {
 describe('SettingsPage', function () {
   beforeEach(function (done) {
   beforeEach(function (done) {
     this.user = new User()
     this.user = new User()
-    this.v1Id = 1234
-    this.v1User = {
-      id: this.v1Id,
-      email: this.user.email,
-      password: this.user.password,
-      profile: {
-        id: this.v1Id,
-        email: this.user.email,
-      },
-    }
     return async.series(
     return async.series(
       [
       [
         this.user.ensureUserExists.bind(this.user),
         this.user.ensureUserExists.bind(this.user),
         this.user.login.bind(this.user),
         this.user.login.bind(this.user),
-        cb => this.user.mongoUpdate({ $set: { 'overleaf.id': this.v1Id } }, cb),
-        cb => {
-          MockV1Api.setUser(this.v1Id, this.v1User)
-          return cb()
-        },
       ],
       ],
       done
       done
     )
     )
@@ -65,24 +43,4 @@ describe('SettingsPage', function () {
       })
       })
     })
     })
   })
   })
-
-  describe('with third-party-references configured', function () {
-    beforeEach(function injectThirdPartyReferencesEntryIntoDb(done) {
-      this.user.mongoUpdate(
-        { $set: { refProviders: { zotero: { encrypted: '2020.9:SNIP' } } } },
-        done
-      )
-    })
-
-    it('should be able to update settings', function (done) {
-      const newName = 'third-party-references'
-      this.user.updateSettings({ first_name: newName }, error => {
-        expect(error).not.to.exist
-        this.user.get((error, user) => {
-          user.first_name.should.equal(newName)
-          done()
-        })
-      })
-    })
-  })
 })
 })

+ 0 - 516
services/web/test/acceptance/src/SubscriptionDashboardTests.js

@@ -1,516 +0,0 @@
-const { expect } = require('chai')
-const async = require('async')
-const UserHelper = require('./helpers/UserHelper')
-const { Subscription } = require('../../../app/src/models/Subscription')
-const { Institution } = require('../../../app/src/models/Institution')
-const SubscriptionViewModelBuilder = require('../../../app/src/Features/Subscription/SubscriptionViewModelBuilder')
-const RecurlySubscription = require('./helpers/RecurlySubscription')
-const MockRecurlyApiClass = require('./mocks/MockRecurlyApi')
-const MockV1ApiClass = require('./mocks/MockV1Api')
-
-async function buildUsersSubscriptionViewModelPromise(userId) {
-  return new Promise((resolve, reject) => {
-    SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
-      userId,
-      (error, data) => {
-        if (error) reject(error)
-        resolve(data)
-      }
-    )
-  })
-}
-
-let MockV1Api, MockRecurlyApi
-
-before(function () {
-  MockV1Api = MockV1ApiClass.instance()
-  MockRecurlyApi = MockRecurlyApiClass.instance()
-})
-
-describe('Subscriptions', function () {
-  describe('dashboard', function () {
-    let userHelper
-    beforeEach(async function () {
-      userHelper = await UserHelper.createUser()
-      this.user = userHelper.user
-    })
-
-    it('should not list personal plan', function () {
-      const plans = SubscriptionViewModelBuilder.buildPlansList()
-      expect(plans.individualMonthlyPlans).to.be.a('Array')
-      const personalMonthlyPlan = plans.individualMonthlyPlans.find(
-        plan => plan.planCode === 'personal'
-      )
-      expect(personalMonthlyPlan).to.be.undefined
-    })
-
-    describe('when the user has no subscription', function () {
-      beforeEach(function (done) {
-        SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
-          this.user,
-          (error, data) => {
-            this.data = data
-            if (error) {
-              return done(error)
-            }
-            done()
-          }
-        )
-      })
-
-      it('should return no personalSubscription', function () {
-        expect(this.data.personalSubscription).to.equal(null)
-      })
-
-      it('should return no memberGroupSubscriptions', function () {
-        expect(this.data.memberGroupSubscriptions).to.deep.equal([])
-      })
-    })
-
-    describe('when the user has a subscription with recurly', function () {
-      beforeEach(function (done) {
-        this.recurlySubscription = new RecurlySubscription({
-          adminId: this.user._id,
-          planCode: 'collaborator',
-          tax_in_cents: 100,
-          tax_rate: 0.2,
-          unit_amount_in_cents: 500,
-          currency: 'GBP',
-          current_period_ends_at: new Date(2018, 4, 5),
-          state: 'active',
-          trial_ends_at: new Date(2018, 6, 7),
-          account: {
-            hosted_login_token: 'mock-login-token',
-            email: 'mock@email.com',
-          },
-        })
-        MockRecurlyApi.coupons = this.coupons = {
-          'test-coupon-1': { description: 'Test Coupon 1' },
-          'test-coupon-2': { description: 'Test Coupon 2' },
-          'test-coupon-3': { name: 'TestCoupon3' },
-        }
-        this.recurlySubscription.ensureExists(error => {
-          if (error) {
-            return done(error)
-          }
-          SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
-            this.user,
-            (error, data) => {
-              this.data = data
-              if (error) {
-                return done(error)
-              }
-              done()
-            }
-          )
-        })
-      })
-
-      after(function (done) {
-        MockRecurlyApi.mockSubscriptions = []
-        MockRecurlyApi.coupons = {}
-        MockRecurlyApi.redemptions = {}
-        Subscription.deleteOne(
-          {
-            admin_id: this.user._id,
-          },
-          done
-        )
-      })
-
-      it('should return a personalSubscription with populated recurly data', function () {
-        const subscription = this.data.personalSubscription
-        expect(subscription).to.exist
-        expect(subscription.planCode).to.equal('collaborator')
-        expect(subscription.recurly).to.exist
-        expect(subscription.recurly).to.deep.equal({
-          activeCoupons: [],
-          billingDetailsLink:
-            'https://test.recurly.com/account/billing_info/edit?ht=mock-login-token',
-          accountManagementLink:
-            'https://test.recurly.com/account/mock-login-token',
-          currency: 'GBP',
-          nextPaymentDueAt: '5th May 2018',
-          price: '£6.00',
-          state: 'active',
-          tax: 100,
-          taxRate: 0.2,
-          trial_ends_at: new Date(2018, 6, 7),
-          trialEndsAtFormatted: '7th July 2018',
-          account: {
-            account_code: this.user._id.toString(),
-            email: 'mock@email.com',
-            hosted_login_token: 'mock-login-token',
-          },
-          additionalLicenses: 0,
-          totalLicenses: 0,
-        })
-      })
-
-      it('should return no memberGroupSubscriptions', function () {
-        expect(this.data.memberGroupSubscriptions).to.deep.equal([])
-      })
-
-      it('should include redeemed coupons', function (done) {
-        MockRecurlyApi.redemptions[this.user._id] = [
-          { state: 'active', coupon_code: 'test-coupon-1' },
-          { state: 'inactive', coupon_code: 'test-coupon-2' },
-          { state: 'active', coupon_code: 'test-coupon-3' },
-        ]
-
-        // rebuild the view model with the redemptions
-        SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
-          this.user,
-          (error, data) => {
-            expect(error).to.not.exist
-            expect(
-              data.personalSubscription.recurly.activeCoupons
-            ).to.deep.equal([
-              {
-                coupon_code: 'test-coupon-1',
-                name: '',
-                description: 'Test Coupon 1',
-              },
-              {
-                coupon_code: 'test-coupon-3',
-                name: 'TestCoupon3',
-                description: '',
-              },
-            ])
-            done()
-          }
-        )
-      })
-
-      it('should return Recurly account email', function () {
-        expect(this.data.personalSubscription.recurly.account.email).to.equal(
-          'mock@email.com'
-        )
-      })
-    })
-
-    describe('when the user has a subscription without recurly', function () {
-      beforeEach(function (done) {
-        Subscription.create(
-          {
-            admin_id: this.user._id,
-            manager_ids: [this.user._id],
-            planCode: 'collaborator',
-          },
-          error => {
-            if (error) {
-              return done(error)
-            }
-            SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
-              this.user,
-              (error, data) => {
-                this.data = data
-                if (error) {
-                  return done(error)
-                }
-                done()
-              }
-            )
-          }
-        )
-      })
-
-      after(function (done) {
-        Subscription.deleteOne(
-          {
-            admin_id: this.user._id,
-          },
-          done
-        )
-      })
-
-      it('should return a personalSubscription with no recurly data', function () {
-        const subscription = this.data.personalSubscription
-        expect(subscription).to.exist
-        expect(subscription.planCode).to.equal('collaborator')
-        expect(subscription.recurly).to.not.exist
-      })
-
-      it('should return no memberGroupSubscriptions', function () {
-        expect(this.data.memberGroupSubscriptions).to.deep.equal([])
-      })
-    })
-
-    describe('when the user is a member of a group subscription', function () {
-      beforeEach(async function () {
-        const userHelperOwner1 = await UserHelper.createUser()
-        const userHelperOwner2 = await UserHelper.createUser()
-        this.owner1 = userHelperOwner1.user
-        this.owner2 = userHelperOwner2.user
-
-        await Subscription.create({
-          admin_id: this.owner1._id,
-          manager_ids: [this.owner1._id],
-          planCode: 'collaborator',
-          groupPlan: true,
-          member_ids: [this.user._id],
-        })
-        await Subscription.create({
-          admin_id: this.owner2._id,
-          manager_ids: [this.owner2._id],
-          planCode: 'collaborator',
-          groupPlan: true,
-          member_ids: [this.user._id],
-        })
-        this.data = await buildUsersSubscriptionViewModelPromise(this.user._id)
-      })
-
-      after(function (done) {
-        Subscription.deleteOne(
-          {
-            admin_id: this.owner1._id,
-          },
-          error => {
-            if (error) {
-              return done(error)
-            }
-            Subscription.deleteOne(
-              {
-                admin_id: this.owner2._id,
-              },
-              done
-            )
-          }
-        )
-      })
-
-      it('should return no personalSubscription', function () {
-        expect(this.data.personalSubscription).to.equal(null)
-      })
-
-      it('should return the two memberGroupSubscriptions', function () {
-        expect(this.data.memberGroupSubscriptions.length).to.equal(2)
-        expect(
-          // Mongoose populates the admin_id with the user
-          this.data.memberGroupSubscriptions[0].admin_id._id.toString()
-        ).to.equal(this.owner1._id.toString())
-        expect(
-          this.data.memberGroupSubscriptions[1].admin_id._id.toString()
-        ).to.equal(this.owner2._id.toString())
-      })
-    })
-
-    describe('when the user is a manager of a group subscription', function () {
-      beforeEach(async function () {
-        const userHelperOwner1 = await UserHelper.createUser()
-        const userHelperOwner2 = await UserHelper.createUser()
-        this.owner1 = userHelperOwner1.user
-        this.owner2 = userHelperOwner2.user
-
-        await Subscription.create({
-          admin_id: this.owner1._id,
-          manager_ids: [this.owner1._id, this.user._id],
-          planCode: 'collaborator',
-          groupPlan: true,
-        })
-        this.data = await buildUsersSubscriptionViewModelPromise(this.user._id)
-      })
-
-      after(function (done) {
-        Subscription.deleteOne(
-          {
-            admin_id: this.owner1._id,
-          },
-          done
-        )
-      })
-
-      it('should return no personalSubscription', function () {
-        expect(this.data.personalSubscription).to.equal(null)
-      })
-
-      it('should return the managedGroupSubscriptions', function () {
-        expect(this.data.managedGroupSubscriptions.length).to.equal(1)
-        const subscription = this.data.managedGroupSubscriptions[0]
-        expect(
-          // Mongoose populates the admin_id with the user
-          subscription.admin_id._id.toString()
-        ).to.equal(this.owner1._id.toString())
-        expect(subscription.groupPlan).to.equal(true)
-      })
-    })
-
-    describe('when the user is a manager of an institution', function () {
-      beforeEach(function (done) {
-        this.v1Id = MockV1Api.nextV1Id()
-        async.series(
-          [
-            cb => {
-              Institution.create(
-                {
-                  v1Id: this.v1Id,
-                  managerIds: [this.user._id],
-                },
-                cb
-              )
-            },
-          ],
-          error => {
-            if (error) {
-              return done(error)
-            }
-            SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
-              this.user,
-              (error, data) => {
-                this.data = data
-                if (error) {
-                  return done(error)
-                }
-                done()
-              }
-            )
-          }
-        )
-      })
-
-      after(function (done) {
-        Institution.deleteOne(
-          {
-            v1Id: this.v1Id,
-          },
-          done
-        )
-      })
-
-      it('should return the managedInstitutions', function () {
-        expect(this.data.managedInstitutions.length).to.equal(1)
-        const institution = this.data.managedInstitutions[0]
-        expect(institution.v1Id).to.equal(this.v1Id)
-        expect(institution.name).to.equal(`Institution ${this.v1Id}`)
-      })
-    })
-
-    describe('when the user is a member of an affiliation', function () {
-      beforeEach(async function () {
-        const v1Id = MockV1Api.nextV1Id()
-        MockV1Api.setUser(v1Id, {
-          subscription: {},
-          subscription_status: {},
-        })
-        await UserHelper.updateUser(this.user._id, {
-          $set: { overleaf: { id: v1Id } },
-        })
-
-        const harvardDomain = 'harvard.example.edu'
-        const mitDomain = 'mit.example.edu'
-        const stanfordDomain = 'stanford.example.edu'
-        const harvardId = MockV1Api.createInstitution({
-          name: 'Harvard',
-          hostname: harvardDomain,
-        })
-        const mitId = MockV1Api.createInstitution({
-          name: 'MIT',
-          hostname: mitDomain,
-        })
-        const stanfordId = MockV1Api.createInstitution({
-          name: 'Stanford',
-          hostname: stanfordDomain,
-        })
-        MockV1Api.updateInstitutionDomain(harvardId, harvardDomain, {
-          confirmed: true,
-        })
-        MockV1Api.updateInstitutionDomain(mitId, mitDomain, {
-          confirmed: false,
-        })
-        MockV1Api.updateInstitutionDomain(stanfordId, stanfordDomain, {
-          confirmed: true,
-        })
-        this.harvardEmail = `unconfirmed-affiliation-email@${harvardDomain}`
-        this.stanfordEmail = `confirmed-affiliation-email@${stanfordDomain}`
-        const mitEmail = `confirmed-affiliation-email@${mitDomain}`
-        userHelper = await UserHelper.loginUser(
-          userHelper.getDefaultEmailPassword()
-        )
-        await userHelper.addEmail(this.harvardEmail)
-        await userHelper.addEmailAndConfirm(this.user._id, this.stanfordEmail)
-        await userHelper.addEmailAndConfirm(this.user._id, mitEmail)
-        this.data = await buildUsersSubscriptionViewModelPromise(this.user._id)
-      })
-
-      it('should return only the affilations with confirmed institutions, and confirmed emails', function () {
-        expect(this.data.confirmedMemberAffiliations.length).to.equal(1)
-        expect(
-          this.data.confirmedMemberAffiliations[0].institution.name
-        ).to.equal('Stanford')
-      })
-    })
-
-    describe('when the user has a v1 subscription', function () {
-      beforeEach(async function () {
-        let v1Id
-        MockV1Api.setUser((v1Id = MockV1Api.nextV1Id()), {
-          subscription: (this.subscription = {
-            trial: false,
-            has_plan: true,
-            teams: [
-              {
-                id: 56,
-                name: 'Test team',
-              },
-            ],
-          }),
-          subscription_status: (this.subscription_status = {
-            product: { mock: 'product' },
-            team: null,
-          }),
-        })
-        await UserHelper.updateUser(this.user._id, {
-          $set: {
-            overleaf: {
-              id: v1Id,
-            },
-          },
-        })
-        this.data = await buildUsersSubscriptionViewModelPromise(this.user._id)
-      })
-
-      it('should return no personalSubscription', function () {
-        expect(this.data.personalSubscription).to.equal(null)
-      })
-
-      it('should return no memberGroupSubscriptions', function () {
-        expect(this.data.memberGroupSubscriptions).to.deep.equal([])
-      })
-
-      it('should return a v1SubscriptionStatus', function () {
-        expect(this.data.v1SubscriptionStatus).to.deep.equal(
-          this.subscription_status
-        )
-      })
-    })
-  })
-
-  describe('canceling', function () {
-    let userHelper, v1Id
-    beforeEach(async function () {
-      v1Id = MockV1Api.nextV1Id()
-      userHelper = await UserHelper.createUser({ overleaf: { id: v1Id } })
-      this.user = userHelper.user
-      MockV1Api.setUser(v1Id, (this.v1_user = {}))
-
-      userHelper = await UserHelper.loginUser(
-        userHelper.getDefaultEmailPassword()
-      )
-      this.response = await userHelper.request.post(
-        '/user/subscription/v1/cancel',
-        {
-          simple: false,
-        }
-      )
-    })
-
-    it('should tell v1 to cancel the subscription', function () {
-      expect(this.v1_user.canceled).to.equal(true)
-    })
-
-    it('should redirect to the subscription dashboard', function () {
-      expect(this.response.statusCode).to.equal(302)
-      expect(this.response.headers.location).to.equal('/user/subscription')
-    })
-  })
-})

+ 0 - 112
services/web/test/acceptance/src/SubscriptionDeletionTests.js

@@ -1,112 +0,0 @@
-const { expect } = require('chai')
-const async = require('async')
-const request = require('./helpers/request')
-const User = require('./helpers/User')
-const RecurlySubscription = require('./helpers/RecurlySubscription')
-const SubscriptionUpdater = require('../../../app/src/Features/Subscription/SubscriptionUpdater')
-const Settings = require('@overleaf/settings')
-
-describe('Subscriptions', function () {
-  describe('deletion', function () {
-    beforeEach(function (done) {
-      this.adminUser = new User()
-      this.memberUser = new User()
-      this.auth = {
-        user: Settings.apis.recurly.webhookUser,
-        pass: Settings.apis.recurly.webhookPass,
-        sendImmediately: true,
-      }
-
-      async.series(
-        [
-          cb => this.adminUser.ensureUserExists(cb),
-          cb => this.memberUser.ensureUserExists(cb),
-          cb => {
-            this.recurlySubscription = new RecurlySubscription({
-              adminId: this.adminUser._id,
-              memberIds: [this.memberUser._id],
-              invitedEmails: ['foo@bar.com'],
-              teamInvites: [{ email: 'foo@baz.com' }],
-              groupPlan: true,
-              state: 'expired',
-              planCode: 'professional',
-            })
-            this.subscription = this.recurlySubscription.subscription
-            this.recurlySubscription.ensureExists(cb)
-          },
-          cb => this.subscription.refreshUsersFeatures(cb),
-        ],
-        done
-      )
-    })
-
-    it('should not allow unauthorized access to the Recurly callback', function (done) {
-      const url = '/user/subscription/callback'
-      const body = this.recurlySubscription.buildCallbackXml()
-
-      request.post({ url, body }, (error, { statusCode }) => {
-        if (error) {
-          return done(error)
-        }
-        expect(statusCode).to.equal(401)
-        done()
-      })
-    })
-
-    it('deletes via Recurly callback', function (done) {
-      const url = '/user/subscription/callback'
-      const body = this.recurlySubscription.buildCallbackXml()
-
-      request.post({ url, body, auth: this.auth }, (error, { statusCode }) => {
-        if (error) {
-          return done(error)
-        }
-        expect(statusCode).to.equal(200)
-        this.subscription.expectDeleted({ ip: '127.0.0.1' }, done)
-      })
-    })
-
-    it('refresh features', function (done) {
-      const url = '/user/subscription/callback'
-      const body = this.recurlySubscription.buildCallbackXml()
-
-      request.post({ url, body, auth: this.auth }, (error, { statusCode }) => {
-        if (error) {
-          return done(error)
-        }
-        this.memberUser.getFeatures((error, features) => {
-          expect(features.collaborators).to.equal(1)
-          done(error)
-        })
-      })
-    })
-
-    it('allows deletion when deletedSubscription exists', function (done) {
-      const url = '/user/subscription/callback'
-      const body = this.recurlySubscription.buildCallbackXml()
-
-      // create fake deletedSubscription
-      SubscriptionUpdater.createDeletedSubscription(
-        this.subscription,
-        {},
-        error => {
-          if (error) {
-            return done(error)
-          }
-
-          // try deleting the subscription
-          request.post(
-            { url, body, auth: this.auth },
-            (error, { statusCode }) => {
-              if (error) {
-                return done(error)
-              }
-              expect(statusCode).to.equal(200)
-              this.subscription.expectDeleted({ ip: '127.0.0.1' }, done)
-            }
-          )
-        }
-      )
-    })
-  })
-})

+ 0 - 74
services/web/test/acceptance/src/SubscriptionFeaturesTests.js

@@ -1,74 +0,0 @@
-const { expect } = require('chai')
-const async = require('async')
-const User = require('./helpers/User')
-const Subscription = require('./helpers/Subscription')
-
-describe('Subscriptions', function () {
-  describe('features', function () {
-    describe('individual subscriptions', function () {
-      beforeEach(function (done) {
-        this.adminUser = new User()
-        async.series(
-          [
-            cb => this.adminUser.ensureUserExists(cb),
-            cb => {
-              this.subscription = new Subscription({
-                adminId: this.adminUser._id,
-                groupPlan: false,
-                planCode: 'professional',
-              })
-              this.subscription.ensureExists(cb)
-            },
-            cb => this.subscription.refreshUsersFeatures(cb),
-          ],
-          done
-        )
-      })
-
-      it('should give features to admin', function (done) {
-        this.adminUser.getFeatures((error, features) => {
-          expect(features.collaborators).to.equal(-1)
-          done(error)
-        })
-      })
-    })
-
-    describe('group subscriptions', function () {
-      beforeEach(function (done) {
-        this.adminUser = new User()
-        this.memberUser = new User()
-        async.series(
-          [
-            cb => this.adminUser.ensureUserExists(cb),
-            cb => this.memberUser.ensureUserExists(cb),
-            cb => {
-              this.subscription = new Subscription({
-                adminId: this.adminUser._id,
-                memberIds: [this.memberUser._id],
-                groupPlan: true,
-                planCode: 'professional',
-              })
-              this.subscription.ensureExists(cb)
-            },
-            cb => this.subscription.refreshUsersFeatures(cb),
-          ],
-          done
-        )
-      })
-
-      it('should give features to member', function (done) {
-        this.memberUser.getFeatures((error, features) => {
-          expect(features.collaborators).to.equal(-1)
-          done(error)
-        })
-      })
-
-      it('should not give features to admin', function (done) {
-        this.adminUser.getFeatures((error, features) => {
-          expect(features.collaborators).to.equal(1)
-          done(error)
-        })
-      })
-    })
-  })
-})

+ 1 - 267
services/web/test/acceptance/src/TokenAccessTests.js

@@ -3,16 +3,9 @@ const async = require('async')
 const User = require('./helpers/User')
 const User = require('./helpers/User')
 const request = require('./helpers/request')
 const request = require('./helpers/request')
 const settings = require('@overleaf/settings')
 const settings = require('@overleaf/settings')
-const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
-const MockV1ApiClass = require('./mocks/MockV1Api')
+const { db } = require('../../../app/src/infrastructure/mongodb')
 const expectErrorResponse = require('./helpers/expectErrorResponse')
 const expectErrorResponse = require('./helpers/expectErrorResponse')
 
 
-let MockV1Api
-
-before(function () {
-  MockV1Api = MockV1ApiClass.instance()
-})
-
 const tryEditorAccess = (user, projectId, test, callback) =>
 const tryEditorAccess = (user, projectId, test, callback) =>
   async.series(
   async.series(
     [
     [
@@ -1213,7 +1206,6 @@ describe('TokenAccess', function () {
 
 
   describe('deleted project', function () {
   describe('deleted project', function () {
     beforeEach(function (done) {
     beforeEach(function (done) {
-      settings.overleaf = { host: 'http://localhost:5000' }
       this.owner.createProject(
       this.owner.createProject(
         `delete-test${Math.random()}`,
         `delete-test${Math.random()}`,
         (err, projectId) => {
         (err, projectId) => {
@@ -1237,10 +1229,6 @@ describe('TokenAccess', function () {
       )
       )
     })
     })
 
 
-    afterEach(function () {
-      delete settings.overleaf
-    })
-
     it('should 404', function (done) {
     it('should 404', function (done) {
       async.series(
       async.series(
         [
         [
@@ -1281,258 +1269,4 @@ describe('TokenAccess', function () {
       )
       )
     })
     })
   })
   })
-
-  describe('unimported v1 project', function () {
-    beforeEach(function () {
-      settings.overleaf = { host: 'http://localhost:5000' }
-    })
-
-    afterEach(function () {
-      delete settings.overleaf
-    })
-
-    it('should show download option for read and write token', function (done) {
-      const unimportedV1Token = '123abcdefabcdef'
-      const docInfo = {
-        exists: true,
-        exported: false,
-        has_owner: true,
-        name: 'test',
-      }
-      MockV1Api.setDocInfo(unimportedV1Token, docInfo)
-      tryReadAndWriteTokenAccess(
-        this.owner,
-        unimportedV1Token,
-        (response, body) => {
-          expect(response.statusCode).to.equal(200)
-        },
-        (response, body) => {
-          expect(response.statusCode).to.equal(200)
-          expect(body).to.deep.equal({
-            v1Import: {
-              hasOwner: true,
-              name: 'test',
-              projectId: unimportedV1Token,
-              status: 'canDownloadZip',
-            },
-          })
-        },
-        done
-      )
-    })
-
-    it('should show download option for read only token to v1', function (done) {
-      const unimportedV1Token = 'aaaaaabbbbbb'
-      const docInfo = {
-        exists: true,
-        exported: false,
-        has_owner: true,
-        name: 'test',
-      }
-      MockV1Api.setDocInfo(unimportedV1Token, docInfo)
-      tryReadOnlyTokenAccess(
-        this.owner,
-        unimportedV1Token,
-        (response, body) => {
-          expect(response.statusCode).to.equal(200)
-        },
-        (response, body) => {
-          expect(response.statusCode).to.equal(200)
-          expect(body).to.deep.equal({
-            v1Import: {
-              hasOwner: true,
-              name: 'test',
-              projectId: unimportedV1Token,
-              status: 'canDownloadZip',
-            },
-          })
-        },
-        done
-      )
-    })
-  })
-
-  describe('importing v1 project', function () {
-    beforeEach(function (done) {
-      settings.projectImportingCheckMaxCreateDelta = 3600
-      settings.overleaf = { host: 'http://localhost:5000' }
-      this.owner.createProject(
-        `token-rw-test${Math.random()}`,
-        (err, projectId) => {
-          if (err != null) {
-            return done(err)
-          }
-          this.projectId = projectId
-          db.users.updateOne(
-            { _id: ObjectId(this.owner._id.toString()) },
-            { $set: { 'overleaf.id': 321321 } },
-            err => {
-              if (err) {
-                return done(err)
-              }
-              this.owner.makeTokenBased(this.projectId, err => {
-                if (err != null) {
-                  return done(err)
-                }
-                db.projects.updateOne(
-                  { _id: ObjectId(projectId) },
-                  { $set: { overleaf: { id: 1234 } } },
-                  err => {
-                    if (err != null) {
-                      return done(err)
-                    }
-                    this.owner.getProject(this.projectId, (err, project) => {
-                      if (err != null) {
-                        return done(err)
-                      }
-                      this.tokens = project.tokens
-                      const docInfo = {
-                        exists: true,
-                        exported: false,
-                        has_owner: true,
-                        name: 'Test Project Import Example',
-                      }
-                      MockV1Api.setDocInfo(this.tokens.readAndWrite, docInfo)
-                      MockV1Api.setDocInfo(this.tokens.readOnly, docInfo)
-                      db.projects.deleteOne({ _id: ObjectId(projectId) }, done)
-                    })
-                  }
-                )
-              })
-            }
-          )
-        }
-      )
-    })
-
-    afterEach(function () {
-      delete settings.projectImportingCheckMaxCreateDelta
-      delete settings.overleaf
-    })
-
-    it('should show importing page for read, and read-write tokens', function (done) {
-      async.series(
-        [
-          cb =>
-            tryReadAndWriteTokenAccess(
-              this.owner,
-              this.tokens.readAndWrite,
-              (response, body) => {
-                expect(response.statusCode).to.equal(200)
-              },
-              (response, body) => {
-                expect(response.statusCode).to.equal(200)
-                expect(body).to.deep.equal({
-                  v1Import: {
-                    status: 'canDownloadZip',
-                    projectId: this.tokens.readAndWrite,
-                    hasOwner: true,
-                    name: 'Test Project Import Example',
-                  },
-                })
-              },
-              cb
-            ),
-          cb =>
-            tryReadOnlyTokenAccess(
-              this.owner,
-              this.tokens.readOnly,
-              (response, body) => {
-                expect(response.statusCode).to.equal(200)
-              },
-              (response, body) => {
-                expect(response.statusCode).to.equal(200)
-                expect(body).to.deep.equal({
-                  v1Import: {
-                    status: 'canDownloadZip',
-                    projectId: this.tokens.readOnly,
-                    hasOwner: true,
-                    name: 'Test Project Import Example',
-                  },
-                })
-              },
-              cb
-            ),
-          cb =>
-            tryEditorAccess(
-              this.owner,
-              this.projectId,
-              (response, body) => {
-                expect(response.statusCode).to.equal(404)
-              },
-              cb
-            ),
-          cb =>
-            tryContentAccess(
-              this.other2,
-              this.projectId,
-              (response, body) => {
-                expect(response.statusCode).to.equal(404)
-              },
-              cb
-            ),
-        ],
-        done
-      )
-    })
-
-    describe('when the v1 doc does not exist', function (done) {
-      beforeEach(function (done) {
-        const docInfo = null
-        MockV1Api.setDocInfo(this.tokens.readAndWrite, docInfo)
-        MockV1Api.setDocInfo(this.tokens.readOnly, docInfo)
-        done()
-      })
-
-      it('should get a 404 response on the post endpoint', function (done) {
-        async.series(
-          [
-            cb =>
-              tryReadAndWriteTokenAccess(
-                this.owner,
-                this.tokens.readAndWrite,
-                (response, body) => {
-                  expect(response.statusCode).to.equal(200)
-                },
-                (response, body) => {
-                  expect(response.statusCode).to.equal(404)
-                },
-                cb
-              ),
-            cb =>
-              tryReadOnlyTokenAccess(
-                this.owner,
-                this.tokens.readOnly,
-                (response, body) => {
-                  expect(response.statusCode).to.equal(200)
-                },
-                (response, body) => {
-                  expect(response.statusCode).to.equal(404)
-                },
-                cb
-              ),
-            cb =>
-              tryEditorAccess(
-                this.owner,
-                this.projectId,
-                (response, body) => {
-                  expect(response.statusCode).to.equal(404)
-                },
-                cb
-              ),
-            cb =>
-              tryContentAccess(
-                this.other2,
-                this.projectId,
-                (response, body) => {
-                  expect(response.statusCode).to.equal(404)
-                },
-                cb
-              ),
-          ],
-          done
-        )
-      })
-    })
-  })
 })
 })

+ 0 - 1188
services/web/test/acceptance/src/UserEmailsTests.js

@@ -1,1188 +0,0 @@
-const { expect } = require('chai')
-const async = require('async')
-const moment = require('moment')
-const Features = require('../../../app/src/infrastructure/Features')
-const User = require('./helpers/User')
-const UserHelper = require('./helpers/UserHelper')
-const UserUpdater = require('../../../app/src/Features/User/UserUpdater')
-const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
-const MockV1ApiClass = require('./mocks/MockV1Api')
-const expectErrorResponse = require('./helpers/expectErrorResponse')
-
-let MockV1Api
-
-before(function () {
-  MockV1Api = MockV1ApiClass.instance()
-})
-
-describe('UserEmails', function () {
-  beforeEach(function (done) {
-    this.timeout(20000)
-    this.user = new User()
-    this.user.login(done)
-  })
-
-  describe('confirming an email', function () {
-    it('should confirm the email', function (done) {
-      let token = null
-      async.series(
-        [
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: {
-                  email: 'newly-added-email@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(204)
-                cb()
-              }
-            )
-          },
-          cb => {
-            this.user.request(
-              { url: '/user/emails', json: true },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                expect(body[0].confirmedAt).to.not.exist
-                expect(body[0].reconfirmedAt).to.not.exist
-                expect(body[1].confirmedAt).to.not.exist
-                expect(body[1].reconfirmedAt).to.not.exist
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // There should only be one confirmation token at the moment
-                expect(tokens.length).to.equal(1)
-                expect(tokens[0].data.email).to.equal(
-                  'newly-added-email@example.com'
-                )
-                expect(tokens[0].data.user_id).to.equal(this.user._id)
-                ;({ token } = tokens[0])
-                cb()
-              })
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/confirm',
-                json: {
-                  token,
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                cb()
-              }
-            )
-          },
-          cb => {
-            this.user.request(
-              { url: '/user/emails', json: true },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                expect(body[0].confirmedAt).to.not.exist
-                expect(body[0].reconfirmedAt).to.not.exist
-                expect(body[1].confirmedAt).to.exist
-                expect(body[1].reconfirmedAt).to.exist
-                expect(body[1].reconfirmedAt).to.deep.equal(body[1].confirmedAt)
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // Token should be deleted after use
-                expect(tokens.length).to.equal(0)
-                cb()
-              })
-          },
-        ],
-        done
-      )
-    })
-
-    it('should not allow confirmation of the email if the user has changed', function (done) {
-      let token1 = null
-      let token2 = null
-      this.user2 = new User()
-      this.email = 'duplicate-email@example.com'
-      async.series(
-        [
-          cb => this.user2.login(cb),
-          cb => {
-            // Create email for first user
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: { email: this.email },
-              },
-              cb
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // There should only be one confirmation token at the moment
-                expect(tokens.length).to.equal(1)
-                expect(tokens[0].data.email).to.equal(this.email)
-                expect(tokens[0].data.user_id).to.equal(this.user._id)
-                token1 = tokens[0].token
-                cb()
-              })
-          },
-          cb => {
-            // Delete the email from the first user
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/delete',
-                json: { email: this.email },
-              },
-              cb
-            )
-          },
-          cb => {
-            // Create email for second user
-            this.user2.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: { email: this.email },
-              },
-              cb
-            )
-          },
-          cb => {
-            // Original confirmation token should no longer work
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/confirm',
-                json: {
-                  token: token1,
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(404)
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user2._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // The first token has been used, so this should be token2 now
-                expect(tokens.length).to.equal(1)
-                expect(tokens[0].data.email).to.equal(this.email)
-                expect(tokens[0].data.user_id).to.equal(this.user2._id)
-                token2 = tokens[0].token
-                cb()
-              })
-          },
-          cb => {
-            // Second user should be able to confirm the email
-            this.user2.request(
-              {
-                method: 'POST',
-                url: '/user/emails/confirm',
-                json: {
-                  token: token2,
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                cb()
-              }
-            )
-          },
-          cb => {
-            this.user2.request(
-              { url: '/user/emails', json: true },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                expect(body[0].confirmedAt).to.not.exist
-                expect(body[1].confirmedAt).to.exist
-                cb()
-              }
-            )
-          },
-        ],
-        done
-      )
-    })
-  })
-
-  describe('reconfirm an email', function () {
-    let email, userHelper, confirmedAtDate
-    beforeEach(async function () {
-      userHelper = new UserHelper()
-      email = userHelper.getDefaultEmail()
-      userHelper = await UserHelper.createUser({ email })
-      userHelper = await UserHelper.loginUser({
-        email,
-        password: userHelper.getDefaultPassword(),
-      })
-      // original confirmation
-      await userHelper.confirmEmail(userHelper.user._id, email)
-      const user = (await UserHelper.getUser({ email })).user
-      confirmedAtDate = user.emails[0].confirmedAt
-      expect(user.emails[0].confirmedAt).to.exist
-      expect(user.emails[0].reconfirmedAt).to.exist
-    })
-    it('should set reconfirmedAt and not reset confirmedAt', async function () {
-      await userHelper.confirmEmail(userHelper.user._id, email)
-      const user = (await UserHelper.getUser({ email })).user
-      expect(user.emails[0].confirmedAt).to.exist
-      expect(user.emails[0].reconfirmedAt).to.exist
-      expect(user.emails[0].confirmedAt).to.deep.equal(confirmedAtDate)
-      expect(user.emails[0].reconfirmedAt).to.not.deep.equal(
-        user.emails[0].confirmedAt
-      )
-      expect(user.emails[0].reconfirmedAt > user.emails[0].confirmedAt).to.be
-        .true
-    })
-  })
-
-  describe('with an expired token', function () {
-    it('should not confirm the email', function (done) {
-      let token = null
-      async.series(
-        [
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: {
-                  email: (this.email = 'expired-token-email@example.com'),
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(204)
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // There should only be one confirmation token at the moment
-                expect(tokens.length).to.equal(1)
-                expect(tokens[0].data.email).to.equal(this.email)
-                expect(tokens[0].data.user_id).to.equal(this.user._id)
-                ;({ token } = tokens[0])
-                cb()
-              })
-          },
-          cb => {
-            db.tokens.update(
-              {
-                token,
-              },
-              {
-                $set: {
-                  expiresAt: new Date(Date.now() - 1000000),
-                },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/confirm',
-                json: {
-                  token,
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(404)
-                cb()
-              }
-            )
-          },
-        ],
-        done
-      )
-    })
-  })
-
-  describe('resending the confirmation', function () {
-    it('should generate a new token', function (done) {
-      async.series(
-        [
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: {
-                  email: 'reconfirmation-email@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(204)
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // There should only be one confirmation token at the moment
-                expect(tokens.length).to.equal(1)
-                expect(tokens[0].data.email).to.equal(
-                  'reconfirmation-email@example.com'
-                )
-                expect(tokens[0].data.user_id).to.equal(this.user._id)
-                cb()
-              })
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/resend_confirmation',
-                json: {
-                  email: 'reconfirmation-email@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // There should be two tokens now
-                expect(tokens.length).to.equal(2)
-                expect(tokens[0].data.email).to.equal(
-                  'reconfirmation-email@example.com'
-                )
-                expect(tokens[0].data.user_id).to.equal(this.user._id)
-                expect(tokens[1].data.email).to.equal(
-                  'reconfirmation-email@example.com'
-                )
-                expect(tokens[1].data.user_id).to.equal(this.user._id)
-                cb()
-              })
-          },
-        ],
-        done
-      )
-    })
-
-    it('should create a new token if none exists', function (done) {
-      // This should only be for users that have sign up with their main
-      // emails before the confirmation system existed
-      async.series(
-        [
-          cb => {
-            db.tokens.remove(
-              {
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/resend_confirmation',
-                json: {
-                  email: this.user.email,
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                // There should still only be one confirmation token
-                expect(tokens.length).to.equal(1)
-                expect(tokens[0].data.email).to.equal(this.user.email)
-                expect(tokens[0].data.user_id).to.equal(this.user._id)
-                cb()
-              })
-          },
-        ],
-        done
-      )
-    })
-
-    it("should not allow reconfirmation if the email doesn't match the user", function (done) {
-      async.series(
-        [
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/resend_confirmation',
-                json: {
-                  email: 'non-matching-email@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(422)
-                cb()
-              }
-            )
-          },
-          cb => {
-            db.tokens
-              .find({
-                use: 'email_confirmation',
-                'data.user_id': this.user._id,
-                usedAt: { $exists: false },
-              })
-              .toArray((error, tokens) => {
-                expect(error).to.not.exist
-                expect(tokens.length).to.equal(0)
-                cb()
-              })
-          },
-        ],
-        done
-      )
-    })
-  })
-
-  describe('setting a default email', function () {
-    it('should update confirmed emails for users not in v1', function (done) {
-      async.series(
-        [
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: {
-                  email: 'new-confirmed-default@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(204)
-                cb()
-              }
-            )
-          },
-          cb => {
-            // Mark the email as confirmed
-            db.users.updateOne(
-              {
-                'emails.email': 'new-confirmed-default@example.com',
-              },
-              {
-                $set: {
-                  'emails.$.confirmedAt': new Date(),
-                },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/default',
-                json: {
-                  email: 'new-confirmed-default@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                cb()
-              }
-            )
-          },
-          cb => {
-            this.user.request(
-              { url: '/user/emails', json: true },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                expect(body[0].confirmedAt).to.not.exist
-                expect(body[0].default).to.equal(false)
-                expect(body[1].confirmedAt).to.exist
-                expect(body[1].default).to.equal(true)
-                cb()
-              }
-            )
-          },
-        ],
-        done
-      )
-    })
-
-    it('should not allow changing unconfirmed emails in v1', function (done) {
-      async.series(
-        [
-          cb => {
-            db.users.updateOne(
-              {
-                _id: ObjectId(this.user._id),
-              },
-              {
-                $set: {
-                  'overleaf.id': 42,
-                },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: {
-                  email: 'new-unconfirmed-default@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(204)
-                cb()
-              }
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/default',
-                json: {
-                  email: 'new-unconfirmed-default@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(409)
-                cb()
-              }
-            )
-          },
-          cb => {
-            this.user.request(
-              { url: '/user/emails', json: true },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(body[0].default).to.equal(true)
-                expect(body[1].default).to.equal(false)
-                cb()
-              }
-            )
-          },
-        ],
-        done
-      )
-    })
-
-    it('should not update the email in v1', function (done) {
-      async.series(
-        [
-          cb => {
-            db.users.updateOne(
-              {
-                _id: ObjectId(this.user._id),
-              },
-              {
-                $set: {
-                  'overleaf.id': 42,
-                },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: {
-                  email: 'new-confirmed-default-in-v1@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(204)
-                cb()
-              }
-            )
-          },
-          cb => {
-            // Mark the email as confirmed
-            db.users.updateOne(
-              {
-                'emails.email': 'new-confirmed-default-in-v1@example.com',
-              },
-              {
-                $set: {
-                  'emails.$.confirmedAt': new Date(),
-                },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/default',
-                json: {
-                  email: 'new-confirmed-default-in-v1@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                cb()
-              }
-            )
-          },
-        ],
-        error => {
-          expect(error).to.not.exist
-          expect(MockV1Api.updateEmail.callCount).to.equal(0)
-          done()
-        }
-      )
-    })
-
-    it('should not return an error if the email exists in v1', function (done) {
-      MockV1Api.existingEmails.push('exists-in-v1@example.com')
-      async.series(
-        [
-          cb => {
-            db.users.updateOne(
-              {
-                _id: ObjectId(this.user._id),
-              },
-              {
-                $set: {
-                  'overleaf.id': 42,
-                },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails',
-                json: {
-                  email: 'exists-in-v1@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(204)
-                cb()
-              }
-            )
-          },
-          cb => {
-            // Mark the email as confirmed
-            db.users.updateOne(
-              {
-                'emails.email': 'exists-in-v1@example.com',
-              },
-              {
-                $set: {
-                  'emails.$.confirmedAt': new Date(),
-                },
-              },
-              cb
-            )
-          },
-          cb => {
-            this.user.request(
-              {
-                method: 'POST',
-                url: '/user/emails/default',
-                json: {
-                  email: 'exists-in-v1@example.com',
-                },
-              },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(response.statusCode).to.equal(200)
-                cb()
-              }
-            )
-          },
-          cb => {
-            this.user.request(
-              { url: '/user/emails', json: true },
-              (error, response, body) => {
-                expect(error).to.not.exist
-                expect(body[0].default).to.equal(false)
-                expect(body[1].default).to.equal(true)
-                cb()
-              }
-            )
-          },
-        ],
-        done
-      )
-    })
-
-    describe('audit log', function () {
-      const originalEmail = 'original@overleaf.com'
-      let otherEmail, response, userHelper, user, userId
-      beforeEach(async function () {
-        otherEmail = 'other@overleaf.com'
-        userHelper = new UserHelper()
-        userHelper = await UserHelper.createUser({
-          email: originalEmail,
-        })
-        userHelper = await UserHelper.loginUser({
-          email: originalEmail,
-          password: userHelper.getDefaultPassword(),
-        })
-        userId = userHelper.user._id
-        response = await userHelper.request.post({
-          form: {
-            email: otherEmail,
-          },
-          simple: false,
-          uri: '/user/emails',
-        })
-        expect(response.statusCode).to.equal(204)
-        const token = (
-          await db.tokens.findOne({
-            'data.user_id': userId.toString(),
-            'data.email': otherEmail,
-          })
-        ).token
-        response = await userHelper.request.post(`/user/emails/confirm`, {
-          form: {
-            token,
-          },
-          simple: false,
-        })
-        expect(response.statusCode).to.equal(200)
-        response = await userHelper.request.post('/user/emails/default', {
-          form: {
-            email: otherEmail,
-          },
-          simple: false,
-        })
-        expect(response.statusCode).to.equal(200)
-        userHelper = await UserHelper.getUser(userId)
-        user = userHelper.user
-      })
-      it('should be updated', function () {
-        const auditLog = userHelper.getAuditLogWithoutNoise()
-        const entry = auditLog[auditLog.length - 1]
-        expect(typeof entry.initiatorId).to.equal('object')
-        expect(entry.initiatorId).to.deep.equal(user._id)
-        expect(entry.ipAddress).to.equal('127.0.0.1')
-        expect(entry.info).to.deep.equal({
-          newPrimaryEmail: otherEmail,
-          oldPrimaryEmail: originalEmail,
-        })
-      })
-    })
-
-    describe('session cleanup', function () {
-      beforeEach(function setupSecondSession(done) {
-        this.userSession2 = new User()
-        this.userSession2.email = this.user.email
-        this.userSession2.emails = this.user.emails
-        this.userSession2.password = this.user.password
-        // login before adding the new email address
-        // User.login() performs a mongo update and resets the .emails field.
-        this.userSession2.login(done)
-      })
-
-      beforeEach(function checkSecondSessionLiveness(done) {
-        this.userSession2.request(
-          { method: 'GET', url: '/project', followRedirect: false },
-          (error, response) => {
-            expect(error).to.not.exist
-            expect(response.statusCode).to.equal(200)
-            done()
-          }
-        )
-      })
-
-      beforeEach(function addSecondaryEmail(done) {
-        this.user.request(
-          {
-            method: 'POST',
-            url: '/user/emails',
-            json: { email: 'new-confirmed-default@example.com' },
-          },
-          (error, response) => {
-            expect(error).to.not.exist
-            expect(response.statusCode).to.equal(204)
-            done()
-          }
-        )
-      })
-
-      beforeEach(function confirmSecondaryEmail(done) {
-        db.users.updateOne(
-          { 'emails.email': 'new-confirmed-default@example.com' },
-          { $set: { 'emails.$.confirmedAt': new Date() } },
-          done
-        )
-      })
-
-      beforeEach(function setDefault(done) {
-        this.user.request(
-          {
-            method: 'POST',
-            url: '/user/emails/default',
-            json: { email: 'new-confirmed-default@example.com' },
-          },
-          (error, response) => {
-            expect(error).to.not.exist
-            expect(response.statusCode).to.equal(200)
-            done()
-          }
-        )
-      })
-
-      it('should logout the other sessions', function (done) {
-        this.userSession2.request(
-          { method: 'GET', url: '/project', followRedirect: false },
-          (error, response) => {
-            expect(error).to.not.exist
-            expect(response.statusCode).to.equal(302)
-            expect(response.headers)
-              .to.have.property('location')
-              .to.match(/^\/login/)
-            done()
-          }
-        )
-      })
-    })
-  })
-
-  describe('when not logged in', function () {
-    beforeEach(function (done) {
-      this.anonymous = new User()
-      this.anonymous.getCsrfToken(done)
-    })
-    it('should return a plain 403 when setting the email', function (done) {
-      this.anonymous.request(
-        {
-          method: 'POST',
-          url: '/user/emails',
-          json: {
-            email: 'newly-added-email@example.com',
-          },
-        },
-        (error, response, body) => {
-          if (error) {
-            return done(error)
-          }
-          expectErrorResponse.requireLogin.json(response, body)
-          done()
-        }
-      )
-    })
-  })
-
-  describe('secondary email', function () {
-    let newEmail, userHelper, userId, user
-    beforeEach(async function () {
-      newEmail = 'a-new-email@overleaf.com'
-      userHelper = new UserHelper()
-      userHelper = await UserHelper.createUser()
-      userHelper = await UserHelper.loginUser({
-        email: userHelper.getDefaultEmail(),
-        password: userHelper.getDefaultPassword(),
-      })
-      userId = userHelper.user._id
-      await userHelper.request.post({
-        form: {
-          email: newEmail,
-        },
-        simple: false,
-        uri: '/user/emails',
-      })
-      userHelper = await UserHelper.getUser(userId)
-      user = userHelper.user
-    })
-    it('should add the email', async function () {
-      expect(user.emails[1].email).to.equal(newEmail)
-    })
-    it('should add to the user audit log', async function () {
-      const auditLog = userHelper.getAuditLogWithoutNoise()
-      expect(typeof auditLog[0].initiatorId).to.equal('object')
-      expect(auditLog[0].initiatorId).to.deep.equal(user._id)
-      expect(auditLog[0].info.newSecondaryEmail).to.equal(newEmail)
-      expect(auditLog[0].ip).to.equal(this.user.request.ip)
-    })
-  })
-
-  describe('notification period', function () {
-    let defaultEmail, userHelper, email1, email2, email3
-    const maxConfirmationMonths = 12
-
-    beforeEach(async function () {
-      if (!Features.hasFeature('affiliations')) {
-        this.skip()
-      }
-      userHelper = new UserHelper()
-      defaultEmail = userHelper.getDefaultEmail()
-      userHelper = await UserHelper.createUser({ email: defaultEmail })
-      userHelper = await UserHelper.loginUser({
-        email: defaultEmail,
-        password: userHelper.getDefaultPassword(),
-      })
-      const institutionId = MockV1Api.createInstitution({
-        commonsAccount: true,
-        ssoEnabled: false,
-        maxConfirmationMonths,
-      })
-      const domain = 'example-affiliation.com'
-      MockV1Api.addInstitutionDomain(institutionId, domain, { confirmed: true })
-
-      email1 = `leonard@${domain}`
-      email2 = `mccoy@${domain}`
-      email3 = `bones@${domain}`
-    })
-
-    describe('non SSO affiliations', function () {
-      beforeEach(async function () {
-        // create a user with 3 affiliations at the institution.
-        // all are within in the notification period
-        const userId = userHelper.user._id
-        await userHelper.addEmailAndConfirm(userId, email1)
-        await userHelper.addEmailAndConfirm(userId, email2)
-        await userHelper.addEmailAndConfirm(userId, email3)
-        await userHelper.changeConfirmedToNotificationPeriod(
-          userId,
-          email1,
-          maxConfirmationMonths
-        )
-        await userHelper.changeConfirmedToNotificationPeriod(
-          userId,
-          email2,
-          maxConfirmationMonths
-        )
-        await userHelper.changeConfirmedToPastReconfirmation(
-          userId,
-          email3,
-          maxConfirmationMonths
-        )
-      })
-
-      describe('when all affiliations in notification period or past reconfirm date', function () {
-        it('should flag inReconfirmNotificationPeriod for all affiliations in period', async function () {
-          const response = await userHelper.request.get('/user/emails')
-          expect(response.statusCode).to.equal(200)
-          const fullEmails = JSON.parse(response.body)
-          expect(fullEmails.length).to.equal(4)
-          expect(fullEmails[0].affiliation).to.not.exist
-          expect(
-            fullEmails[1].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-          expect(
-            fullEmails[2].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-          expect(
-            fullEmails[3].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-        })
-
-        it('should set pastReconfirmDate and emailHasInstitutionLicence:false for lapsed confirmations', async function () {
-          const response = await userHelper.request.get('/user/emails')
-          expect(response.statusCode).to.equal(200)
-          const fullEmails = JSON.parse(response.body)
-          expect(fullEmails.length).to.equal(4)
-          expect(fullEmails[0].affiliation).to.not.exist
-          expect(fullEmails[1].affiliation.pastReconfirmDate).to.equal(false)
-          expect(fullEmails[1].emailHasInstitutionLicence).to.equal(true)
-          expect(fullEmails[2].affiliation.pastReconfirmDate).to.equal(false)
-          expect(fullEmails[2].emailHasInstitutionLicence).to.equal(true)
-          expect(fullEmails[3].affiliation.pastReconfirmDate).to.equal(true)
-          expect(fullEmails[3].emailHasInstitutionLicence).to.equal(false)
-        })
-      })
-
-      describe('should flag emails before their confirmation expires, but within the notification period', function () {
-        beforeEach(async function () {
-          const dateInPeriodButNotExpired = moment()
-            .subtract(maxConfirmationMonths, 'months')
-            .add(14, 'days')
-            .toDate()
-          const backdatedDays = moment().diff(dateInPeriodButNotExpired, 'days')
-          await userHelper.changeConfirmationDate(
-            userHelper.user._id,
-            email1,
-            backdatedDays
-          )
-          await userHelper.changeConfirmationDate(
-            userHelper.user._id,
-            email2,
-            backdatedDays
-          )
-          await userHelper.changeConfirmationDate(
-            userHelper.user._id,
-            email3,
-            backdatedDays
-          )
-        })
-
-        it('should flag the emails', async function () {
-          const response = await userHelper.request.get('/user/emails')
-          expect(response.statusCode).to.equal(200)
-          const fullEmails = JSON.parse(response.body)
-          expect(fullEmails.length).to.equal(4)
-          expect(fullEmails[0].affiliation).to.not.exist
-          expect(
-            fullEmails[1].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-          expect(
-            fullEmails[2].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-          expect(
-            fullEmails[3].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-          // ensure dates are not past reconfirmation period
-          function _getLastDayToReconfirm(date) {
-            return moment(date).add(maxConfirmationMonths, 'months')
-          }
-
-          expect(
-            moment(fullEmails[1].reconfirmedAt).isAfter(
-              _getLastDayToReconfirm(fullEmails[1].reconfirmedAt)
-            )
-          ).to.equal(false)
-
-          expect(
-            moment(fullEmails[2].reconfirmedAt).isAfter(
-              _getLastDayToReconfirm(fullEmails[2].reconfirmedAt)
-            )
-          ).to.equal(false)
-          expect(
-            moment(fullEmails[3].reconfirmedAt).isAfter(
-              _getLastDayToReconfirm(fullEmails[3].reconfirmedAt)
-            )
-          ).to.equal(false)
-        })
-      })
-
-      describe('missing reconfirmedAt', function () {
-        beforeEach(async function () {
-          const userId = userHelper.user._id
-          const query = {
-            _id: userId,
-            'emails.email': email2,
-          }
-          const update = {
-            $unset: { 'emails.$.reconfirmedAt': true },
-          }
-          await UserUpdater.promises.updateUser(query, update)
-        })
-
-        it('should fallback to confirmedAt for date check', async function () {
-          const response = await userHelper.request.get('/user/emails')
-          expect(response.statusCode).to.equal(200)
-          const fullEmails = JSON.parse(response.body)
-          expect(fullEmails.length).to.equal(4)
-          expect(fullEmails[0].affiliation).to.not.exist
-          expect(fullEmails[2].reconfirmedAt).to.not.exist
-          expect(
-            fullEmails[1].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-          expect(
-            fullEmails[2].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-          expect(
-            fullEmails[3].affiliation.inReconfirmNotificationPeriod
-          ).to.equal(true)
-        })
-      })
-    })
-  })
-})

+ 2 - 2
services/web/test/acceptance/src/UserHelperTests.js

@@ -4,9 +4,9 @@ const Features = require('../../../app/src/infrastructure/Features')
 const { expect } = require('chai')
 const { expect } = require('chai')
 
 
 describe('UserHelper', function () {
 describe('UserHelper', function () {
-  // Disable all tests unless the public-registration feature is enabled
+  // Disable all tests unless the registration feature is enabled
   beforeEach(function () {
   beforeEach(function () {
-    if (!Features.hasFeature('public-registration')) {
+    if (!Features.hasFeature('registration')) {
       this.skip()
       this.skip()
     }
     }
   })
   })

+ 0 - 229
services/web/test/acceptance/src/UserThirdPartyIdentityTests.js

@@ -1,229 +0,0 @@
-/* eslint-disable
-    node/handle-callback-err,
-    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 Errors = require('../../../app/src/Features/Errors/Errors')
-const Settings = require('@overleaf/settings')
-const User = require('./helpers/User')
-const ThirdPartyIdentityManager = require('../../../app/src/Features/User/ThirdPartyIdentityManager')
-const { expect } = require('chai')
-
-describe('ThirdPartyIdentityManager', function () {
-  beforeEach(function (done) {
-    this.provider = 'provider'
-    this.externalUserId = 'external-user-id'
-    this.externalData = { test: 'data' }
-    this.user = new User()
-    this.user.ensureUserExists(done)
-  })
-
-  afterEach(function (done) {
-    return this.user.fullDeleteUser(this.user.email, done)
-  })
-
-  describe('login', function () {
-    describe('when third party identity exists', function () {
-      beforeEach(function (done) {
-        return ThirdPartyIdentityManager.link(
-          this.user.id,
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          done
-        )
-      })
-
-      it('should return user', function (done) {
-        ThirdPartyIdentityManager.login(
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          (err, user) => {
-            expect(err).to.be.null
-            expect(user._id.toString()).to.equal(this.user.id)
-            return done()
-          }
-        )
-      })
-
-      it('should merge external data', function (done) {
-        this.externalData = {
-          test: 'different',
-          another: 'key',
-        }
-        ThirdPartyIdentityManager.login(
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          (err, user) => {
-            expect(err).to.be.null
-            expect(user.thirdPartyIdentifiers[0].externalData).to.deep.equal(
-              this.externalData
-            )
-            return done()
-          }
-        )
-      })
-    })
-
-    describe('when third party identity does not exists', function () {
-      it('should return error', function (done) {
-        ThirdPartyIdentityManager.login(
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          (err, user) => {
-            expect(err.name).to.equal('ThirdPartyUserNotFoundError')
-            return done()
-          }
-        )
-      })
-    })
-  })
-
-  describe('link', function () {
-    describe('when provider not already linked', function () {
-      it('should link provider to user', function (done) {
-        ThirdPartyIdentityManager.link(
-          this.user.id,
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          (err, res) => {
-            expect(res.thirdPartyIdentifiers.length).to.equal(1)
-            return done()
-          }
-        )
-      })
-    })
-
-    describe('when provider is already linked', function () {
-      beforeEach(function (done) {
-        ThirdPartyIdentityManager.link(
-          this.user.id,
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          done
-        )
-      })
-
-      it('should link provider to user', function (done) {
-        ThirdPartyIdentityManager.link(
-          this.user.id,
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          (err, res) => {
-            expect(res).to.exist
-            done()
-          }
-        )
-      })
-
-      it('should not create duplicate thirdPartyIdentifiers', function (done) {
-        ThirdPartyIdentityManager.link(
-          this.user.id,
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          (err, user) => {
-            expect(user.thirdPartyIdentifiers.length).to.equal(1)
-            return done()
-          }
-        )
-      })
-
-      it('should replace existing data', function (done) {
-        this.externalData = { replace: 'data' }
-        return ThirdPartyIdentityManager.link(
-          this.user.id,
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          (err, user) => {
-            expect(user.thirdPartyIdentifiers.length).to.equal(1)
-            return done()
-          }
-        )
-      })
-
-      // describe('when another account tries to link same provider/externalUserId', function() {
-      // NOTE: Cannot run this test because we do not have indexes on the test DB
-      //   beforeEach(function(done) {
-      //     this.user2 = new User()
-      //     this.user2.ensureUserExists(done)
-      //   })
-      //   it('should not link provider', function(done) {
-      //     ThirdPartyIdentityManager.link(
-      //       this.user2.id,
-      //       this.provider,
-      //       this.externalUserId,
-      //       this.externalData,
-      //       (err, user) => {
-      //         expect(err.name).to.equal('ThirdPartyIdentityExistsError')
-      //         return done()
-      //       }
-      //     )
-      //     this.user2.fullDeleteUser(this.user2.email, done)
-      //   })
-      // })
-    })
-  })
-
-  describe('unlink', function () {
-    describe('when provider not already linked', function () {
-      it('should succeed', function (done) {
-        return ThirdPartyIdentityManager.unlink(
-          this.user.id,
-          this.provider,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          (err, res) => {
-            expect(err).to.be.null
-            expect(res.thirdPartyIdentifiers.length).to.equal(0)
-            return done()
-          }
-        )
-      })
-    })
-
-    describe('when provider is already linked', function () {
-      beforeEach(function (done) {
-        return ThirdPartyIdentityManager.link(
-          this.user.id,
-          this.provider,
-          this.externalUserId,
-          this.externalData,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          done
-        )
-      })
-
-      it('should remove thirdPartyIdentifiers entry', function (done) {
-        return ThirdPartyIdentityManager.unlink(
-          this.user.id,
-          this.provider,
-          { initiatorId: this.user.id, ipAddress: '0:0:0:0' },
-          (err, user) => {
-            expect(user.thirdPartyIdentifiers.length).to.equal(0)
-            return done()
-          }
-        )
-      })
-    })
-  })
-})

+ 0 - 2
services/web/test/acceptance/src/helpers/MongoHelper.js

@@ -7,8 +7,6 @@ module.exports = {
 
 
     before(function (done) {
     before(function (done) {
       exec('bin/east migrate', (error, stdout, stderr) => {
       exec('bin/east migrate', (error, stdout, stderr) => {
-        console.log(stdout)
-        console.error(stderr)
         if (error) {
         if (error) {
           throw error
           throw error
         }
         }

+ 51 - 0
services/web/test/unit/src/Project/ProjectDeleterTests.js

@@ -132,9 +132,13 @@ describe('ProjectDeleter', function () {
         deleteProject: sinon.stub().resolves(),
         deleteProject: sinon.stub().resolves(),
       },
       },
     }
     }
+    this.Features = {
+      hasFeature: sinon.stub().returns(true),
+    }
 
 
     this.ProjectDeleter = SandboxedModule.require(modulePath, {
     this.ProjectDeleter = SandboxedModule.require(modulePath, {
       requires: {
       requires: {
+        '../../infrastructure/Features': this.Features,
         '../Editor/EditorRealTimeController': this.EditorRealTimeController,
         '../Editor/EditorRealTimeController': this.EditorRealTimeController,
         '../../models/Project': { Project: Project },
         '../../models/Project': { Project: Project },
         './ProjectHelper': this.ProjectHelper,
         './ProjectHelper': this.ProjectHelper,
@@ -486,6 +490,53 @@ describe('ProjectDeleter', function () {
       })
       })
     })
     })
 
 
+    describe('when history-v1 is not available', function () {
+      beforeEach(async function () {
+        this.Features.hasFeature.returns(false)
+
+        this.ProjectMock.expects('findById')
+          .withArgs(this.deletedProjects[0].deleterData.deletedProjectId)
+          .chain('exec')
+          .resolves(null)
+        this.DeletedProjectMock.expects('updateOne')
+          .withArgs(
+            {
+              _id: this.deletedProjects[0]._id,
+            },
+            {
+              $set: {
+                'deleterData.deleterIpAddress': null,
+                project: null,
+              },
+            }
+          )
+          .chain('exec')
+          .resolves()
+
+        this.DeletedProjectMock.expects('findOne')
+          .withArgs({
+            'deleterData.deletedProjectId': this.deletedProjects[0].project._id,
+          })
+          .chain('exec')
+          .resolves(this.deletedProjects[0])
+
+        await this.ProjectDeleter.promises.expireDeletedProject(
+          this.deletedProjects[0].project._id
+        )
+      })
+
+      it('should destroy the docs in docstore', function () {
+        expect(
+          this.DocstoreManager.promises.destroyProject
+        ).to.have.been.calledWith(this.deletedProjects[0].project._id)
+      })
+
+      it('should not call project history', function () {
+        expect(this.HistoryManager.promises.deleteProject).to.not.have.been
+          .called
+      })
+    })
+
     describe('on an active project (from an incomplete delete)', function () {
     describe('on an active project (from an incomplete delete)', function () {
       beforeEach(async function () {
       beforeEach(async function () {
         this.ProjectMock.expects('findById')
         this.ProjectMock.expects('findById')

+ 5 - 2
services/web/test/unit/src/infrastructure/FeaturesTests.js

@@ -6,7 +6,9 @@ describe('Features', function () {
   beforeEach(function () {
   beforeEach(function () {
     this.Features = SandboxedModule.require(modulePath, {
     this.Features = SandboxedModule.require(modulePath, {
       requires: {
       requires: {
-        '@overleaf/settings': (this.settings = {}),
+        '@overleaf/settings': (this.settings = {
+          moduleImportSequence: [],
+        }),
       },
       },
     })
     })
   })
   })
@@ -45,10 +47,11 @@ describe('Features', function () {
   describe('hasFeature', function () {
   describe('hasFeature', function () {
     describe('without any settings', function () {
     describe('without any settings', function () {
       it('should return true', function () {
       it('should return true', function () {
-        expect(this.Features.hasFeature('registration')).to.be.true
+        expect(this.Features.hasFeature('registration-page')).to.be.true
         expect(this.Features.hasFeature('templates-server-pro')).to.be.true
         expect(this.Features.hasFeature('templates-server-pro')).to.be.true
       })
       })
       it('should return false', function () {
       it('should return false', function () {
+        expect(this.Features.hasFeature('registration')).to.be.false
         expect(this.Features.hasFeature('affiliations')).to.be.false
         expect(this.Features.hasFeature('affiliations')).to.be.false
         expect(this.Features.hasFeature('analytics')).to.be.false
         expect(this.Features.hasFeature('analytics')).to.be.false
         expect(this.Features.hasFeature('custom-togglers')).to.be.false
         expect(this.Features.hasFeature('custom-togglers')).to.be.false