WebApiManager.test.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import { vi, describe, beforeEach, it } from 'vitest'
  2. /* eslint-disable
  3. no-return-assign,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. import sinon from 'sinon'
  14. const modulePath = '../../../app/js/WebApiManager.js'
  15. describe('WebApiManager', function () {
  16. beforeEach(async function (ctx) {
  17. ctx.project_id = 'project-id-123'
  18. ctx.user_id = 'user-id-123'
  19. ctx.user = { _id: ctx.user_id }
  20. ctx.callback = sinon.stub()
  21. vi.doMock('request', () => ({
  22. default: (ctx.request = {}),
  23. }))
  24. vi.doMock('@overleaf/settings', () => ({
  25. default: (ctx.settings = {
  26. apis: {
  27. web: {
  28. url: 'http://web.example.com',
  29. user: 'username',
  30. pass: 'password',
  31. },
  32. },
  33. }),
  34. }))
  35. return (ctx.WebApiManager = (await import(modulePath)).default)
  36. })
  37. return describe('joinProject', function () {
  38. describe('successfully', function () {
  39. beforeEach(function (ctx) {
  40. ctx.response = {
  41. project: { name: 'Test project' },
  42. privilegeLevel: 'owner',
  43. isRestrictedUser: true,
  44. isTokenMember: true,
  45. isInvitedMember: true,
  46. }
  47. ctx.request.post = sinon
  48. .stub()
  49. .callsArgWith(1, null, { statusCode: 200 }, ctx.response)
  50. return ctx.WebApiManager.joinProject(
  51. ctx.project_id,
  52. ctx.user,
  53. ctx.callback
  54. )
  55. })
  56. it('should send a request to web to join the project', function (ctx) {
  57. return ctx.request.post
  58. .calledWith({
  59. url: `${ctx.settings.apis.web.url}/project/${ctx.project_id}/join`,
  60. auth: {
  61. user: ctx.settings.apis.web.user,
  62. pass: ctx.settings.apis.web.pass,
  63. sendImmediately: true,
  64. },
  65. json: {
  66. userId: ctx.user_id,
  67. anonymousAccessToken: undefined,
  68. },
  69. jar: false,
  70. })
  71. .should.equal(true)
  72. })
  73. return it('should return the project, privilegeLevel, and restricted flag', function (ctx) {
  74. return ctx.callback
  75. .calledWith(null, ctx.response.project, ctx.response.privilegeLevel, {
  76. isRestrictedUser: ctx.response.isRestrictedUser,
  77. isTokenMember: ctx.response.isTokenMember,
  78. isInvitedMember: ctx.response.isInvitedMember,
  79. })
  80. .should.equal(true)
  81. })
  82. })
  83. describe('with anon user', function () {
  84. beforeEach(function (ctx) {
  85. ctx.user_id = 'anonymous-user'
  86. ctx.token = 'a-ro-token'
  87. ctx.user = {
  88. _id: ctx.user_id,
  89. anonymousAccessToken: ctx.token,
  90. }
  91. ctx.response = {
  92. project: { name: 'Test project' },
  93. privilegeLevel: 'readOnly',
  94. isRestrictedUser: true,
  95. isTokenMember: false,
  96. isInvitedMember: false,
  97. }
  98. ctx.request.post = sinon
  99. .stub()
  100. .yields(null, { statusCode: 200 }, ctx.response)
  101. ctx.WebApiManager.joinProject(ctx.project_id, ctx.user, ctx.callback)
  102. })
  103. it('should send a request to web to join the project', function (ctx) {
  104. ctx.request.post.should.have.been.calledWith({
  105. url: `${ctx.settings.apis.web.url}/project/${ctx.project_id}/join`,
  106. auth: {
  107. user: ctx.settings.apis.web.user,
  108. pass: ctx.settings.apis.web.pass,
  109. sendImmediately: true,
  110. },
  111. json: {
  112. userId: ctx.user_id,
  113. anonymousAccessToken: ctx.token,
  114. },
  115. jar: false,
  116. })
  117. })
  118. it('should return the project, privilegeLevel, and restricted flag', function (ctx) {
  119. ctx.callback.should.have.been.calledWith(
  120. null,
  121. ctx.response.project,
  122. ctx.response.privilegeLevel,
  123. {
  124. isRestrictedUser: ctx.response.isRestrictedUser,
  125. isTokenMember: ctx.response.isTokenMember,
  126. isInvitedMember: ctx.response.isInvitedMember,
  127. }
  128. )
  129. })
  130. })
  131. describe('when web replies with a 403', function () {
  132. beforeEach(function (ctx) {
  133. ctx.request.post = sinon
  134. .stub()
  135. .callsArgWith(1, null, { statusCode: 403 }, null)
  136. ctx.WebApiManager.joinProject(ctx.project_id, ctx.user_id, ctx.callback)
  137. })
  138. it('should call the callback with an error', function (ctx) {
  139. ctx.callback
  140. .calledWith(
  141. sinon.match({
  142. message: 'not authorized',
  143. })
  144. )
  145. .should.equal(true)
  146. })
  147. })
  148. describe('when web replies with a 404', function () {
  149. beforeEach(function (ctx) {
  150. ctx.request.post = sinon
  151. .stub()
  152. .callsArgWith(1, null, { statusCode: 404 }, null)
  153. ctx.WebApiManager.joinProject(ctx.project_id, ctx.user_id, ctx.callback)
  154. })
  155. it('should call the callback with an error', function (ctx) {
  156. ctx.callback
  157. .calledWith(
  158. sinon.match({
  159. message: 'project not found',
  160. info: { code: 'ProjectNotFound' },
  161. })
  162. )
  163. .should.equal(true)
  164. })
  165. })
  166. describe('with an error from web', function () {
  167. beforeEach(function (ctx) {
  168. ctx.request.post = sinon
  169. .stub()
  170. .callsArgWith(1, null, { statusCode: 500 }, null)
  171. return ctx.WebApiManager.joinProject(
  172. ctx.project_id,
  173. ctx.user_id,
  174. ctx.callback
  175. )
  176. })
  177. return it('should call the callback with an error', function (ctx) {
  178. return ctx.callback
  179. .calledWith(
  180. sinon.match({
  181. message: 'non-success status code from web',
  182. info: { statusCode: 500 },
  183. })
  184. )
  185. .should.equal(true)
  186. })
  187. })
  188. describe('with no data from web', function () {
  189. beforeEach(function (ctx) {
  190. ctx.request.post = sinon
  191. .stub()
  192. .callsArgWith(1, null, { statusCode: 200 }, null)
  193. return ctx.WebApiManager.joinProject(
  194. ctx.project_id,
  195. ctx.user_id,
  196. ctx.callback
  197. )
  198. })
  199. return it('should call the callback with an error', function (ctx) {
  200. return ctx.callback
  201. .calledWith(
  202. sinon.match({
  203. message: 'no data returned from joinProject request',
  204. })
  205. )
  206. .should.equal(true)
  207. })
  208. })
  209. return describe('when the project is over its rate limit', function () {
  210. beforeEach(function (ctx) {
  211. ctx.request.post = sinon
  212. .stub()
  213. .callsArgWith(1, null, { statusCode: 429 }, null)
  214. return ctx.WebApiManager.joinProject(
  215. ctx.project_id,
  216. ctx.user_id,
  217. ctx.callback
  218. )
  219. })
  220. return it('should call the callback with a TooManyRequests error code', function (ctx) {
  221. return ctx.callback
  222. .calledWith(
  223. sinon.match({
  224. message: 'rate-limit hit when joining project',
  225. info: {
  226. code: 'TooManyRequests',
  227. },
  228. })
  229. )
  230. .should.equal(true)
  231. })
  232. })
  233. })
  234. })