FeatureUpdaterTests.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. const { expect } = require('chai')
  2. const UserHelper = require('./helpers/UserHelper')
  3. const settings = require('settings-sharelatex')
  4. const { ObjectId } = require('mongodb')
  5. const { Subscription } = require('../../../app/src/models/Subscription')
  6. const { User } = require('../../../app/src/models/User')
  7. const FeaturesUpdater = require('../../../app/src/Features/Subscription/FeaturesUpdater')
  8. const MockV1ApiClass = require('./mocks/MockV1Api')
  9. const logger = require('logger-sharelatex')
  10. logger.logger.level('error')
  11. let MockV1Api
  12. before(function() {
  13. MockV1Api = MockV1ApiClass.instance()
  14. })
  15. const syncUserAndGetFeatures = function(user, callback) {
  16. FeaturesUpdater.refreshFeatures(user._id, error => {
  17. if (error) {
  18. return callback(error)
  19. }
  20. User.findById(user._id, (error, user) => {
  21. if (error) {
  22. return callback(error)
  23. }
  24. const { features } = user.toObject()
  25. delete features.$init // mongoose internals
  26. callback(null, features)
  27. })
  28. })
  29. }
  30. describe('FeatureUpdater.refreshFeatures', function() {
  31. let userHelper, user
  32. beforeEach(async function() {
  33. userHelper = await UserHelper.createUser()
  34. user = userHelper.user
  35. })
  36. describe('when user has no subscriptions', function() {
  37. it('should set their features to the basic set', function(done) {
  38. syncUserAndGetFeatures(user, (error, features) => {
  39. if (error) {
  40. throw error
  41. }
  42. expect(features).to.deep.equal(settings.defaultFeatures)
  43. done()
  44. })
  45. })
  46. })
  47. describe('when the user has an individual subscription', function() {
  48. beforeEach(function() {
  49. Subscription.create({
  50. admin_id: user._id,
  51. manager_ids: [user._id],
  52. planCode: 'collaborator',
  53. customAccount: true
  54. })
  55. }) // returns a promise
  56. it('should set their features to the upgraded set', function(done) {
  57. syncUserAndGetFeatures(user, (error, features) => {
  58. if (error) {
  59. throw error
  60. }
  61. const plan = settings.plans.find(
  62. plan => plan.planCode === 'collaborator'
  63. )
  64. expect(features).to.deep.equal(plan.features)
  65. done()
  66. })
  67. })
  68. })
  69. describe('when the user is in a group subscription', function() {
  70. beforeEach(function() {
  71. const groupAdminId = ObjectId()
  72. Subscription.create({
  73. admin_id: groupAdminId,
  74. manager_ids: [groupAdminId],
  75. member_ids: [user._id],
  76. groupAccount: true,
  77. planCode: 'collaborator',
  78. customAccount: true
  79. })
  80. }) // returns a promise
  81. it('should set their features to the upgraded set', function(done) {
  82. syncUserAndGetFeatures(user, (error, features) => {
  83. if (error) {
  84. throw error
  85. }
  86. const plan = settings.plans.find(
  87. plan => plan.planCode === 'collaborator'
  88. )
  89. expect(features).to.deep.equal(plan.features)
  90. done()
  91. })
  92. })
  93. })
  94. describe('when the user has bonus features', function() {
  95. beforeEach(function() {
  96. return User.updateOne(
  97. {
  98. _id: user._id
  99. },
  100. {
  101. refered_user_count: 10
  102. }
  103. )
  104. }) // returns a promise
  105. it('should set their features to the bonus set', function(done) {
  106. syncUserAndGetFeatures(user, (error, features) => {
  107. if (error) {
  108. throw error
  109. }
  110. expect(features).to.deep.equal(
  111. Object.assign(
  112. {},
  113. settings.defaultFeatures,
  114. settings.bonus_features[9]
  115. )
  116. )
  117. done()
  118. })
  119. })
  120. })
  121. describe('when the user has affiliations', function() {
  122. let email2, institutionId, hostname
  123. beforeEach(async function() {
  124. institutionId = MockV1Api.createInstitution({ commonsAccount: true })
  125. hostname = 'institution.edu'
  126. MockV1Api.addInstitutionDomain(institutionId, hostname, {
  127. confirmed: true
  128. })
  129. email2 = `${user._id}@${hostname}`
  130. userHelper = await UserHelper.loginUser(
  131. userHelper.getDefaultEmailPassword()
  132. )
  133. await userHelper.addEmail(email2)
  134. this.institutionPlan = settings.plans.find(
  135. plan => plan.planCode === settings.institutionPlanCode
  136. )
  137. })
  138. it('should not set their features if email is not confirmed', function(done) {
  139. syncUserAndGetFeatures(user, (error, features) => {
  140. expect(features).to.deep.equal(settings.defaultFeatures)
  141. done()
  142. })
  143. })
  144. describe('when email is confirmed', function() {
  145. beforeEach(async function() {
  146. await userHelper.confirmEmail(user._id, email2)
  147. })
  148. it('should set their features', function(done) {
  149. syncUserAndGetFeatures(user, (error, features) => {
  150. expect(features).to.deep.equal(this.institutionPlan.features)
  151. done()
  152. })
  153. })
  154. describe('when domain is not confirmed as part of institution', function() {
  155. beforeEach(function() {
  156. MockV1Api.updateInstitutionDomain(institutionId, hostname, {
  157. confirmed: false
  158. })
  159. })
  160. it('should not set their features', function(done) {
  161. syncUserAndGetFeatures(user, (error, features) => {
  162. expect(features).to.deep.equal(settings.defaultFeatures)
  163. done()
  164. })
  165. })
  166. })
  167. })
  168. })
  169. describe('when the user is due bonus features and has extra features that no longer apply', function() {
  170. beforeEach(function() {
  171. return User.updateOne(
  172. {
  173. _id: user._id
  174. },
  175. {
  176. refered_user_count: 10,
  177. 'features.github': true
  178. }
  179. )
  180. }) // returns a promise
  181. it('should set their features to the bonus set and downgrade the extras', function(done) {
  182. syncUserAndGetFeatures(user, (error, features) => {
  183. if (error) {
  184. throw error
  185. }
  186. expect(features).to.deep.equal(
  187. Object.assign(
  188. {},
  189. settings.defaultFeatures,
  190. settings.bonus_features[9]
  191. )
  192. )
  193. done()
  194. })
  195. })
  196. })
  197. describe('when the user has a v1 plan', function() {
  198. beforeEach(function() {
  199. MockV1Api.setUser(42, { plan_name: 'free' })
  200. return User.updateOne(
  201. {
  202. _id: user._id
  203. },
  204. {
  205. overleaf: {
  206. id: 42
  207. }
  208. }
  209. )
  210. }) // returns a promise
  211. it('should set their features to the v1 plan', function(done) {
  212. syncUserAndGetFeatures(user, (error, features) => {
  213. if (error) {
  214. throw error
  215. }
  216. const plan = settings.plans.find(plan => plan.planCode === 'v1_free')
  217. expect(features).to.deep.equal(plan.features)
  218. done()
  219. })
  220. })
  221. })
  222. describe('when the user has a v1 plan and bonus features', function() {
  223. beforeEach(function() {
  224. MockV1Api.setUser(42, { plan_name: 'free' })
  225. return User.updateOne(
  226. {
  227. _id: user._id
  228. },
  229. {
  230. overleaf: {
  231. id: 42
  232. },
  233. refered_user_count: 10
  234. }
  235. )
  236. }) // returns a promise
  237. it('should set their features to the best of the v1 plan and bonus features', function(done) {
  238. syncUserAndGetFeatures(user, (error, features) => {
  239. if (error) {
  240. throw error
  241. }
  242. const v1plan = settings.plans.find(plan => plan.planCode === 'v1_free')
  243. const expectedFeatures = Object.assign(
  244. {},
  245. v1plan.features,
  246. settings.bonus_features[9]
  247. )
  248. expect(features).to.deep.equal(expectedFeatures)
  249. done()
  250. })
  251. })
  252. })
  253. describe('when the user has a group and personal subscription', function() {
  254. beforeEach(function(done) {
  255. const groupAdminId = ObjectId()
  256. Subscription.create(
  257. {
  258. admin_id: user._id,
  259. manager_ids: [user._id],
  260. planCode: 'professional',
  261. customAccount: true
  262. },
  263. error => {
  264. if (error) {
  265. throw error
  266. }
  267. Subscription.create(
  268. {
  269. admin_id: groupAdminId,
  270. manager_ids: [groupAdminId],
  271. member_ids: [user._id],
  272. groupAccount: true,
  273. planCode: 'collaborator',
  274. customAccount: true
  275. },
  276. done
  277. )
  278. }
  279. )
  280. })
  281. it('should set their features to the best set', function(done) {
  282. syncUserAndGetFeatures(user, (error, features) => {
  283. if (error) {
  284. throw error
  285. }
  286. const plan = settings.plans.find(
  287. plan => plan.planCode === 'professional'
  288. )
  289. expect(features).to.deep.equal(plan.features)
  290. done()
  291. })
  292. })
  293. })
  294. describe('when the notifyV1Flag is passed', function() {
  295. beforeEach(function() {
  296. User.updateOne(
  297. {
  298. _id: user._id
  299. },
  300. {
  301. overleaf: {
  302. id: 42
  303. }
  304. }
  305. )
  306. }) // returns a promise
  307. })
  308. describe('when the user has features overrides', function() {
  309. beforeEach(function() {
  310. const futureDate = new Date()
  311. futureDate.setDate(futureDate.getDate() + 1)
  312. return User.updateOne(
  313. {
  314. _id: user._id
  315. },
  316. {
  317. featuresOverrides: [
  318. {
  319. features: {
  320. github: true
  321. }
  322. },
  323. {
  324. features: {
  325. dropbox: true
  326. },
  327. expiresAt: new Date(1990, 12, 25)
  328. },
  329. {
  330. features: {
  331. trackChanges: true
  332. },
  333. expiresAt: futureDate
  334. }
  335. ]
  336. }
  337. )
  338. }) // returns a promise
  339. it('should set their features to the overridden set', function(done) {
  340. syncUserAndGetFeatures(user, (error, features) => {
  341. if (error) {
  342. throw error
  343. }
  344. let expectedFeatures = Object.assign(settings.defaultFeatures, {
  345. github: true,
  346. trackChanges: true
  347. })
  348. expect(features).to.deep.equal(expectedFeatures)
  349. done()
  350. })
  351. })
  352. })
  353. })