user-notifications.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
  2. import { expect } from 'chai'
  3. import {
  4. CheckerBaseParams,
  5. checkMyVideoImportIsFinished,
  6. checkNewActorFollow,
  7. checkNewVideoFromSubscription,
  8. checkVideoIsPublished,
  9. checkVideoStudioEditionIsFinished,
  10. FIXTURE_URLS,
  11. MockSmtpServer,
  12. prepareNotificationsTest,
  13. uploadRandomVideoOnServers
  14. } from '@server/tests/shared'
  15. import { wait } from '@shared/core-utils'
  16. import { buildUUID } from '@shared/extra-utils'
  17. import { UserNotification, UserNotificationType, VideoPrivacy, VideoStudioTask } from '@shared/models'
  18. import { cleanupTests, findExternalSavedVideo, PeerTubeServer, stopFfmpeg, waitJobs } from '@shared/server-commands'
  19. describe('Test user notifications', function () {
  20. let servers: PeerTubeServer[] = []
  21. let userAccessToken: string
  22. let userNotifications: UserNotification[] = []
  23. let adminNotifications: UserNotification[] = []
  24. let adminNotificationsServer2: UserNotification[] = []
  25. let emails: object[] = []
  26. let channelId: number
  27. before(async function () {
  28. this.timeout(120000)
  29. const res = await prepareNotificationsTest(3)
  30. emails = res.emails
  31. userAccessToken = res.userAccessToken
  32. servers = res.servers
  33. userNotifications = res.userNotifications
  34. adminNotifications = res.adminNotifications
  35. adminNotificationsServer2 = res.adminNotificationsServer2
  36. channelId = res.channelId
  37. })
  38. describe('New video from my subscription notification', function () {
  39. let baseParams: CheckerBaseParams
  40. before(() => {
  41. baseParams = {
  42. server: servers[0],
  43. emails,
  44. socketNotifications: userNotifications,
  45. token: userAccessToken
  46. }
  47. })
  48. it('Should not send notifications if the user does not follow the video publisher', async function () {
  49. this.timeout(50000)
  50. await uploadRandomVideoOnServers(servers, 1)
  51. const notification = await servers[0].notifications.getLatest({ token: userAccessToken })
  52. expect(notification).to.be.undefined
  53. expect(emails).to.have.lengthOf(0)
  54. expect(userNotifications).to.have.lengthOf(0)
  55. })
  56. it('Should send a new video notification if the user follows the local video publisher', async function () {
  57. this.timeout(15000)
  58. await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@' + servers[0].host })
  59. await waitJobs(servers)
  60. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
  61. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  62. })
  63. it('Should send a new video notification from a remote account', async function () {
  64. this.timeout(150000) // Server 2 has transcoding enabled
  65. await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@' + servers[1].host })
  66. await waitJobs(servers)
  67. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2)
  68. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  69. })
  70. it('Should send a new video notification on a scheduled publication', async function () {
  71. this.timeout(50000)
  72. // In 2 seconds
  73. const updateAt = new Date(new Date().getTime() + 2000)
  74. const data = {
  75. privacy: VideoPrivacy.PRIVATE,
  76. scheduleUpdate: {
  77. updateAt: updateAt.toISOString(),
  78. privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
  79. }
  80. }
  81. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
  82. await wait(6000)
  83. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  84. })
  85. it('Should send a new video notification on a remote scheduled publication', async function () {
  86. this.timeout(100000)
  87. // In 2 seconds
  88. const updateAt = new Date(new Date().getTime() + 2000)
  89. const data = {
  90. privacy: VideoPrivacy.PRIVATE,
  91. scheduleUpdate: {
  92. updateAt: updateAt.toISOString(),
  93. privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
  94. }
  95. }
  96. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
  97. await waitJobs(servers)
  98. await wait(6000)
  99. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  100. })
  101. it('Should not send a notification before the video is published', async function () {
  102. this.timeout(150000)
  103. const updateAt = new Date(new Date().getTime() + 1000000)
  104. const data = {
  105. privacy: VideoPrivacy.PRIVATE,
  106. scheduleUpdate: {
  107. updateAt: updateAt.toISOString(),
  108. privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
  109. }
  110. }
  111. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
  112. await wait(6000)
  113. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
  114. })
  115. it('Should send a new video notification when a video becomes public', async function () {
  116. this.timeout(50000)
  117. const data = { privacy: VideoPrivacy.PRIVATE }
  118. const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
  119. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
  120. await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
  121. await waitJobs(servers)
  122. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  123. })
  124. it('Should send a new video notification when a remote video becomes public', async function () {
  125. this.timeout(120000)
  126. const data = { privacy: VideoPrivacy.PRIVATE }
  127. const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
  128. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
  129. await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
  130. await waitJobs(servers)
  131. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  132. })
  133. it('Should not send a new video notification when a video becomes unlisted', async function () {
  134. this.timeout(50000)
  135. const data = { privacy: VideoPrivacy.PRIVATE }
  136. const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
  137. await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
  138. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
  139. })
  140. it('Should not send a new video notification when a remote video becomes unlisted', async function () {
  141. this.timeout(100000)
  142. const data = { privacy: VideoPrivacy.PRIVATE }
  143. const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
  144. await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
  145. await waitJobs(servers)
  146. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
  147. })
  148. it('Should send a new video notification after a video import', async function () {
  149. this.timeout(100000)
  150. const name = 'video import ' + buildUUID()
  151. const attributes = {
  152. name,
  153. channelId,
  154. privacy: VideoPrivacy.PUBLIC,
  155. targetUrl: FIXTURE_URLS.goodVideo
  156. }
  157. const { video } = await servers[0].imports.importVideo({ attributes })
  158. await waitJobs(servers)
  159. await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
  160. })
  161. })
  162. describe('My video is published', function () {
  163. let baseParams: CheckerBaseParams
  164. before(() => {
  165. baseParams = {
  166. server: servers[1],
  167. emails,
  168. socketNotifications: adminNotificationsServer2,
  169. token: servers[1].accessToken
  170. }
  171. })
  172. it('Should not send a notification if transcoding is not enabled', async function () {
  173. this.timeout(50000)
  174. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
  175. await waitJobs(servers)
  176. await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
  177. })
  178. it('Should not send a notification if the wait transcoding is false', async function () {
  179. this.timeout(100_000)
  180. await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: false })
  181. await waitJobs(servers)
  182. const notification = await servers[0].notifications.getLatest({ token: userAccessToken })
  183. if (notification) {
  184. expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED)
  185. }
  186. })
  187. it('Should send a notification even if the video is not transcoded in other resolutions', async function () {
  188. this.timeout(50000)
  189. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
  190. await waitJobs(servers)
  191. await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  192. })
  193. it('Should send a notification with a transcoded video', async function () {
  194. this.timeout(50000)
  195. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
  196. await waitJobs(servers)
  197. await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  198. })
  199. it('Should send a notification when an imported video is transcoded', async function () {
  200. this.timeout(120000)
  201. const name = 'video import ' + buildUUID()
  202. const attributes = {
  203. name,
  204. channelId,
  205. privacy: VideoPrivacy.PUBLIC,
  206. targetUrl: FIXTURE_URLS.goodVideo,
  207. waitTranscoding: true
  208. }
  209. const { video } = await servers[1].imports.importVideo({ attributes })
  210. await waitJobs(servers)
  211. await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
  212. })
  213. it('Should send a notification when the scheduled update has been proceeded', async function () {
  214. this.timeout(70000)
  215. // In 2 seconds
  216. const updateAt = new Date(new Date().getTime() + 2000)
  217. const data = {
  218. privacy: VideoPrivacy.PRIVATE,
  219. scheduleUpdate: {
  220. updateAt: updateAt.toISOString(),
  221. privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
  222. }
  223. }
  224. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
  225. await wait(6000)
  226. await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  227. })
  228. it('Should not send a notification before the video is published', async function () {
  229. this.timeout(150000)
  230. const updateAt = new Date(new Date().getTime() + 1000000)
  231. const data = {
  232. privacy: VideoPrivacy.PRIVATE,
  233. scheduleUpdate: {
  234. updateAt: updateAt.toISOString(),
  235. privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
  236. }
  237. }
  238. const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
  239. await wait(6000)
  240. await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
  241. })
  242. })
  243. describe('My live replay is published', function () {
  244. let baseParams: CheckerBaseParams
  245. before(() => {
  246. baseParams = {
  247. server: servers[1],
  248. emails,
  249. socketNotifications: adminNotificationsServer2,
  250. token: servers[1].accessToken
  251. }
  252. })
  253. it('Should send a notification is a live replay of a non permanent live is published', async function () {
  254. this.timeout(120000)
  255. const { shortUUID } = await servers[1].live.create({
  256. fields: {
  257. name: 'non permanent live',
  258. privacy: VideoPrivacy.PUBLIC,
  259. channelId: servers[1].store.channel.id,
  260. saveReplay: true,
  261. permanentLive: false
  262. }
  263. })
  264. const ffmpegCommand = await servers[1].live.sendRTMPStreamInVideo({ videoId: shortUUID })
  265. await waitJobs(servers)
  266. await servers[1].live.waitUntilPublished({ videoId: shortUUID })
  267. await stopFfmpeg(ffmpegCommand)
  268. await servers[1].live.waitUntilReplacedByReplay({ videoId: shortUUID })
  269. await waitJobs(servers)
  270. await checkVideoIsPublished({ ...baseParams, videoName: 'non permanent live', shortUUID, checkType: 'presence' })
  271. })
  272. it('Should send a notification is a live replay of a permanent live is published', async function () {
  273. this.timeout(120000)
  274. const { shortUUID } = await servers[1].live.create({
  275. fields: {
  276. name: 'permanent live',
  277. privacy: VideoPrivacy.PUBLIC,
  278. channelId: servers[1].store.channel.id,
  279. saveReplay: true,
  280. permanentLive: true
  281. }
  282. })
  283. const ffmpegCommand = await servers[1].live.sendRTMPStreamInVideo({ videoId: shortUUID })
  284. await waitJobs(servers)
  285. await servers[1].live.waitUntilPublished({ videoId: shortUUID })
  286. const liveDetails = await servers[1].videos.get({ id: shortUUID })
  287. await stopFfmpeg(ffmpegCommand)
  288. await servers[1].live.waitUntilWaiting({ videoId: shortUUID })
  289. await waitJobs(servers)
  290. const video = await findExternalSavedVideo(servers[1], liveDetails)
  291. expect(video).to.exist
  292. await checkVideoIsPublished({ ...baseParams, videoName: video.name, shortUUID: video.shortUUID, checkType: 'presence' })
  293. })
  294. })
  295. describe('Video studio', function () {
  296. let baseParams: CheckerBaseParams
  297. before(() => {
  298. baseParams = {
  299. server: servers[1],
  300. emails,
  301. socketNotifications: adminNotificationsServer2,
  302. token: servers[1].accessToken
  303. }
  304. })
  305. it('Should send a notification after studio edition', async function () {
  306. this.timeout(240000)
  307. const { name, shortUUID, id } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
  308. await waitJobs(servers)
  309. await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  310. const tasks: VideoStudioTask[] = [
  311. {
  312. name: 'cut',
  313. options: {
  314. start: 0,
  315. end: 1
  316. }
  317. }
  318. ]
  319. await servers[1].videoStudio.createEditionTasks({ videoId: id, tasks })
  320. await waitJobs(servers)
  321. await checkVideoStudioEditionIsFinished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
  322. })
  323. })
  324. describe('My video is imported', function () {
  325. let baseParams: CheckerBaseParams
  326. before(() => {
  327. baseParams = {
  328. server: servers[0],
  329. emails,
  330. socketNotifications: adminNotifications,
  331. token: servers[0].accessToken
  332. }
  333. })
  334. it('Should send a notification when the video import failed', async function () {
  335. this.timeout(70000)
  336. const name = 'video import ' + buildUUID()
  337. const attributes = {
  338. name,
  339. channelId,
  340. privacy: VideoPrivacy.PRIVATE,
  341. targetUrl: FIXTURE_URLS.badVideo
  342. }
  343. const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
  344. await waitJobs(servers)
  345. const url = FIXTURE_URLS.badVideo
  346. await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: false, checkType: 'presence' })
  347. })
  348. it('Should send a notification when the video import succeeded', async function () {
  349. this.timeout(70000)
  350. const name = 'video import ' + buildUUID()
  351. const attributes = {
  352. name,
  353. channelId,
  354. privacy: VideoPrivacy.PRIVATE,
  355. targetUrl: FIXTURE_URLS.goodVideo
  356. }
  357. const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
  358. await waitJobs(servers)
  359. const url = FIXTURE_URLS.goodVideo
  360. await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: true, checkType: 'presence' })
  361. })
  362. })
  363. describe('New actor follow', function () {
  364. let baseParams: CheckerBaseParams
  365. const myChannelName = 'super channel name'
  366. const myUserName = 'super user name'
  367. before(async () => {
  368. baseParams = {
  369. server: servers[0],
  370. emails,
  371. socketNotifications: userNotifications,
  372. token: userAccessToken
  373. }
  374. await servers[0].users.updateMe({ displayName: 'super root name' })
  375. await servers[0].users.updateMe({
  376. token: userAccessToken,
  377. displayName: myUserName
  378. })
  379. await servers[1].users.updateMe({ displayName: 'super root 2 name' })
  380. await servers[0].channels.update({
  381. token: userAccessToken,
  382. channelName: 'user_1_channel',
  383. attributes: { displayName: myChannelName }
  384. })
  385. })
  386. it('Should notify when a local channel is following one of our channel', async function () {
  387. this.timeout(50000)
  388. await servers[0].subscriptions.add({ targetUri: 'user_1_channel@' + servers[0].host })
  389. await waitJobs(servers)
  390. await checkNewActorFollow({
  391. ...baseParams,
  392. followType: 'channel',
  393. followerName: 'root',
  394. followerDisplayName: 'super root name',
  395. followingDisplayName: myChannelName,
  396. checkType: 'presence'
  397. })
  398. await servers[0].subscriptions.remove({ uri: 'user_1_channel@' + servers[0].host })
  399. })
  400. it('Should notify when a remote channel is following one of our channel', async function () {
  401. this.timeout(50000)
  402. await servers[1].subscriptions.add({ targetUri: 'user_1_channel@' + servers[0].host })
  403. await waitJobs(servers)
  404. await checkNewActorFollow({
  405. ...baseParams,
  406. followType: 'channel',
  407. followerName: 'root',
  408. followerDisplayName: 'super root 2 name',
  409. followingDisplayName: myChannelName,
  410. checkType: 'presence'
  411. })
  412. await servers[1].subscriptions.remove({ uri: 'user_1_channel@' + servers[0].host })
  413. })
  414. // PeerTube does not support account -> account follows
  415. // it('Should notify when a local account is following one of our channel', async function () {
  416. // this.timeout(50000)
  417. //
  418. // await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@' + servers[0].host)
  419. //
  420. // await waitJobs(servers)
  421. //
  422. // await checkNewActorFollow(baseParams, 'account', 'root', 'super root name', myUserName, 'presence')
  423. // })
  424. // it('Should notify when a remote account is following one of our channel', async function () {
  425. // this.timeout(50000)
  426. //
  427. // await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@' + servers[0].host)
  428. //
  429. // await waitJobs(servers)
  430. //
  431. // await checkNewActorFollow(baseParams, 'account', 'root', 'super root 2 name', myUserName, 'presence')
  432. // })
  433. })
  434. after(async function () {
  435. MockSmtpServer.Instance.kill()
  436. await cleanupTests(servers)
  437. })
  438. })