multiple-servers.ts 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  1. /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
  2. import { expect } from 'chai'
  3. import request from 'supertest'
  4. import {
  5. checkTmpIsEmpty,
  6. checkVideoFilesWereRemoved,
  7. completeVideoCheck,
  8. dateIsValid,
  9. saveVideoInServers,
  10. testImage
  11. } from '@server/tests/shared'
  12. import { buildAbsoluteFixturePath, wait } from '@shared/core-utils'
  13. import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models'
  14. import {
  15. cleanupTests,
  16. createMultipleServers,
  17. doubleFollow,
  18. makeGetRequest,
  19. PeerTubeServer,
  20. setAccessTokensToServers,
  21. setDefaultAccountAvatar,
  22. setDefaultChannelAvatar,
  23. waitJobs,
  24. webtorrentAdd
  25. } from '@shared/server-commands'
  26. describe('Test multiple servers', function () {
  27. let servers: PeerTubeServer[] = []
  28. const toRemove = []
  29. let videoUUID = ''
  30. let videoChannelId: number
  31. before(async function () {
  32. this.timeout(120000)
  33. servers = await createMultipleServers(3)
  34. // Get the access tokens
  35. await setAccessTokensToServers(servers)
  36. {
  37. const videoChannel = {
  38. name: 'super_channel_name',
  39. displayName: 'my channel',
  40. description: 'super channel'
  41. }
  42. await servers[0].channels.create({ attributes: videoChannel })
  43. await setDefaultChannelAvatar(servers[0], videoChannel.name)
  44. await setDefaultAccountAvatar(servers)
  45. const { data } = await servers[0].channels.list({ start: 0, count: 1 })
  46. videoChannelId = data[0].id
  47. }
  48. // Server 1 and server 2 follow each other
  49. await doubleFollow(servers[0], servers[1])
  50. // Server 1 and server 3 follow each other
  51. await doubleFollow(servers[0], servers[2])
  52. // Server 2 and server 3 follow each other
  53. await doubleFollow(servers[1], servers[2])
  54. })
  55. it('Should not have videos for all servers', async function () {
  56. for (const server of servers) {
  57. const { data } = await server.videos.list()
  58. expect(data).to.be.an('array')
  59. expect(data.length).to.equal(0)
  60. }
  61. })
  62. describe('Should upload the video and propagate on each server', function () {
  63. it('Should upload the video on server 1 and propagate on each server', async function () {
  64. this.timeout(25000)
  65. const attributes = {
  66. name: 'my super name for server 1',
  67. category: 5,
  68. licence: 4,
  69. language: 'ja',
  70. nsfw: true,
  71. description: 'my super description for server 1',
  72. support: 'my super support text for server 1',
  73. originallyPublishedAt: '2019-02-10T13:38:14.449Z',
  74. tags: [ 'tag1p1', 'tag2p1' ],
  75. channelId: videoChannelId,
  76. fixture: 'video_short1.webm'
  77. }
  78. await servers[0].videos.upload({ attributes })
  79. await waitJobs(servers)
  80. // All servers should have this video
  81. let publishedAt: string = null
  82. for (const server of servers) {
  83. const isLocal = server.port === servers[0].port
  84. const checkAttributes = {
  85. name: 'my super name for server 1',
  86. category: 5,
  87. licence: 4,
  88. language: 'ja',
  89. nsfw: true,
  90. description: 'my super description for server 1',
  91. support: 'my super support text for server 1',
  92. originallyPublishedAt: '2019-02-10T13:38:14.449Z',
  93. account: {
  94. name: 'root',
  95. host: servers[0].host
  96. },
  97. isLocal,
  98. publishedAt,
  99. duration: 10,
  100. tags: [ 'tag1p1', 'tag2p1' ],
  101. privacy: VideoPrivacy.PUBLIC,
  102. commentsEnabled: true,
  103. downloadEnabled: true,
  104. channel: {
  105. displayName: 'my channel',
  106. name: 'super_channel_name',
  107. description: 'super channel',
  108. isLocal
  109. },
  110. fixture: 'video_short1.webm',
  111. files: [
  112. {
  113. resolution: 720,
  114. size: 572456
  115. }
  116. ]
  117. }
  118. const { data } = await server.videos.list()
  119. expect(data).to.be.an('array')
  120. expect(data.length).to.equal(1)
  121. const video = data[0]
  122. await completeVideoCheck(server, video, checkAttributes)
  123. publishedAt = video.publishedAt as string
  124. expect(video.channel.avatars).to.have.lengthOf(2)
  125. expect(video.account.avatars).to.have.lengthOf(2)
  126. for (const image of [ ...video.channel.avatars, ...video.account.avatars ]) {
  127. expect(image.createdAt).to.exist
  128. expect(image.updatedAt).to.exist
  129. expect(image.width).to.be.above(20).and.below(1000)
  130. expect(image.path).to.exist
  131. await makeGetRequest({
  132. url: server.url,
  133. path: image.path,
  134. expectedStatus: HttpStatusCode.OK_200
  135. })
  136. }
  137. }
  138. })
  139. it('Should upload the video on server 2 and propagate on each server', async function () {
  140. this.timeout(240000)
  141. const user = {
  142. username: 'user1',
  143. password: 'super_password'
  144. }
  145. await servers[1].users.create({ username: user.username, password: user.password })
  146. const userAccessToken = await servers[1].login.getAccessToken(user)
  147. const attributes = {
  148. name: 'my super name for server 2',
  149. category: 4,
  150. licence: 3,
  151. language: 'de',
  152. nsfw: true,
  153. description: 'my super description for server 2',
  154. support: 'my super support text for server 2',
  155. tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
  156. fixture: 'video_short2.webm',
  157. thumbnailfile: 'thumbnail.jpg',
  158. previewfile: 'preview.jpg'
  159. }
  160. await servers[1].videos.upload({ token: userAccessToken, attributes, mode: 'resumable' })
  161. // Transcoding
  162. await waitJobs(servers)
  163. // All servers should have this video
  164. for (const server of servers) {
  165. const isLocal = server.url === servers[1].url
  166. const checkAttributes = {
  167. name: 'my super name for server 2',
  168. category: 4,
  169. licence: 3,
  170. language: 'de',
  171. nsfw: true,
  172. description: 'my super description for server 2',
  173. support: 'my super support text for server 2',
  174. account: {
  175. name: 'user1',
  176. host: servers[1].host
  177. },
  178. isLocal,
  179. commentsEnabled: true,
  180. downloadEnabled: true,
  181. duration: 5,
  182. tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
  183. privacy: VideoPrivacy.PUBLIC,
  184. channel: {
  185. displayName: 'Main user1 channel',
  186. name: 'user1_channel',
  187. description: 'super channel',
  188. isLocal
  189. },
  190. fixture: 'video_short2.webm',
  191. files: [
  192. {
  193. resolution: 240,
  194. size: 270000
  195. },
  196. {
  197. resolution: 360,
  198. size: 359000
  199. },
  200. {
  201. resolution: 480,
  202. size: 465000
  203. },
  204. {
  205. resolution: 720,
  206. size: 750000
  207. }
  208. ],
  209. thumbnailfile: 'thumbnail',
  210. previewfile: 'preview'
  211. }
  212. const { data } = await server.videos.list()
  213. expect(data).to.be.an('array')
  214. expect(data.length).to.equal(2)
  215. const video = data[1]
  216. await completeVideoCheck(server, video, checkAttributes)
  217. }
  218. })
  219. it('Should upload two videos on server 3 and propagate on each server', async function () {
  220. this.timeout(45000)
  221. {
  222. const attributes = {
  223. name: 'my super name for server 3',
  224. category: 6,
  225. licence: 5,
  226. language: 'de',
  227. nsfw: true,
  228. description: 'my super description for server 3',
  229. support: 'my super support text for server 3',
  230. tags: [ 'tag1p3' ],
  231. fixture: 'video_short3.webm'
  232. }
  233. await servers[2].videos.upload({ attributes })
  234. }
  235. {
  236. const attributes = {
  237. name: 'my super name for server 3-2',
  238. category: 7,
  239. licence: 6,
  240. language: 'ko',
  241. nsfw: false,
  242. description: 'my super description for server 3-2',
  243. support: 'my super support text for server 3-2',
  244. tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
  245. fixture: 'video_short.webm'
  246. }
  247. await servers[2].videos.upload({ attributes })
  248. }
  249. await waitJobs(servers)
  250. // All servers should have this video
  251. for (const server of servers) {
  252. const isLocal = server.url === servers[2].url
  253. const { data } = await server.videos.list()
  254. expect(data).to.be.an('array')
  255. expect(data.length).to.equal(4)
  256. // We not sure about the order of the two last uploads
  257. let video1 = null
  258. let video2 = null
  259. if (data[2].name === 'my super name for server 3') {
  260. video1 = data[2]
  261. video2 = data[3]
  262. } else {
  263. video1 = data[3]
  264. video2 = data[2]
  265. }
  266. const checkAttributesVideo1 = {
  267. name: 'my super name for server 3',
  268. category: 6,
  269. licence: 5,
  270. language: 'de',
  271. nsfw: true,
  272. description: 'my super description for server 3',
  273. support: 'my super support text for server 3',
  274. account: {
  275. name: 'root',
  276. host: servers[2].host
  277. },
  278. isLocal,
  279. duration: 5,
  280. commentsEnabled: true,
  281. downloadEnabled: true,
  282. tags: [ 'tag1p3' ],
  283. privacy: VideoPrivacy.PUBLIC,
  284. channel: {
  285. displayName: 'Main root channel',
  286. name: 'root_channel',
  287. description: '',
  288. isLocal
  289. },
  290. fixture: 'video_short3.webm',
  291. files: [
  292. {
  293. resolution: 720,
  294. size: 292677
  295. }
  296. ]
  297. }
  298. await completeVideoCheck(server, video1, checkAttributesVideo1)
  299. const checkAttributesVideo2 = {
  300. name: 'my super name for server 3-2',
  301. category: 7,
  302. licence: 6,
  303. language: 'ko',
  304. nsfw: false,
  305. description: 'my super description for server 3-2',
  306. support: 'my super support text for server 3-2',
  307. account: {
  308. name: 'root',
  309. host: servers[2].host
  310. },
  311. commentsEnabled: true,
  312. downloadEnabled: true,
  313. isLocal,
  314. duration: 5,
  315. tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
  316. privacy: VideoPrivacy.PUBLIC,
  317. channel: {
  318. displayName: 'Main root channel',
  319. name: 'root_channel',
  320. description: '',
  321. isLocal
  322. },
  323. fixture: 'video_short.webm',
  324. files: [
  325. {
  326. resolution: 720,
  327. size: 218910
  328. }
  329. ]
  330. }
  331. await completeVideoCheck(server, video2, checkAttributesVideo2)
  332. }
  333. })
  334. })
  335. describe('It should list local videos', function () {
  336. it('Should list only local videos on server 1', async function () {
  337. const { data, total } = await servers[0].videos.list({ isLocal: true })
  338. expect(total).to.equal(1)
  339. expect(data).to.be.an('array')
  340. expect(data.length).to.equal(1)
  341. expect(data[0].name).to.equal('my super name for server 1')
  342. })
  343. it('Should list only local videos on server 2', async function () {
  344. const { data, total } = await servers[1].videos.list({ isLocal: true })
  345. expect(total).to.equal(1)
  346. expect(data).to.be.an('array')
  347. expect(data.length).to.equal(1)
  348. expect(data[0].name).to.equal('my super name for server 2')
  349. })
  350. it('Should list only local videos on server 3', async function () {
  351. const { data, total } = await servers[2].videos.list({ isLocal: true })
  352. expect(total).to.equal(2)
  353. expect(data).to.be.an('array')
  354. expect(data.length).to.equal(2)
  355. expect(data[0].name).to.equal('my super name for server 3')
  356. expect(data[1].name).to.equal('my super name for server 3-2')
  357. })
  358. })
  359. describe('Should seed the uploaded video', function () {
  360. it('Should add the file 1 by asking server 3', async function () {
  361. this.timeout(30000)
  362. const { data } = await servers[2].videos.list()
  363. const video = data[0]
  364. toRemove.push(data[2])
  365. toRemove.push(data[3])
  366. const videoDetails = await servers[2].videos.get({ id: video.id })
  367. const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
  368. expect(torrent.files).to.be.an('array')
  369. expect(torrent.files.length).to.equal(1)
  370. expect(torrent.files[0].path).to.exist.and.to.not.equal('')
  371. })
  372. it('Should add the file 2 by asking server 1', async function () {
  373. this.timeout(30000)
  374. const { data } = await servers[0].videos.list()
  375. const video = data[1]
  376. const videoDetails = await servers[0].videos.get({ id: video.id })
  377. const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
  378. expect(torrent.files).to.be.an('array')
  379. expect(torrent.files.length).to.equal(1)
  380. expect(torrent.files[0].path).to.exist.and.to.not.equal('')
  381. })
  382. it('Should add the file 3 by asking server 2', async function () {
  383. this.timeout(30000)
  384. const { data } = await servers[1].videos.list()
  385. const video = data[2]
  386. const videoDetails = await servers[1].videos.get({ id: video.id })
  387. const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
  388. expect(torrent.files).to.be.an('array')
  389. expect(torrent.files.length).to.equal(1)
  390. expect(torrent.files[0].path).to.exist.and.to.not.equal('')
  391. })
  392. it('Should add the file 3-2 by asking server 1', async function () {
  393. this.timeout(30000)
  394. const { data } = await servers[0].videos.list()
  395. const video = data[3]
  396. const videoDetails = await servers[0].videos.get({ id: video.id })
  397. const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
  398. expect(torrent.files).to.be.an('array')
  399. expect(torrent.files.length).to.equal(1)
  400. expect(torrent.files[0].path).to.exist.and.to.not.equal('')
  401. })
  402. it('Should add the file 2 in 360p by asking server 1', async function () {
  403. this.timeout(30000)
  404. const { data } = await servers[0].videos.list()
  405. const video = data.find(v => v.name === 'my super name for server 2')
  406. const videoDetails = await servers[0].videos.get({ id: video.id })
  407. const file = videoDetails.files.find(f => f.resolution.id === 360)
  408. expect(file).not.to.be.undefined
  409. const torrent = await webtorrentAdd(file.magnetUri)
  410. expect(torrent.files).to.be.an('array')
  411. expect(torrent.files.length).to.equal(1)
  412. expect(torrent.files[0].path).to.exist.and.to.not.equal('')
  413. })
  414. })
  415. describe('Should update video views, likes and dislikes', function () {
  416. let localVideosServer3 = []
  417. let remoteVideosServer1 = []
  418. let remoteVideosServer2 = []
  419. let remoteVideosServer3 = []
  420. before(async function () {
  421. {
  422. const { data } = await servers[0].videos.list()
  423. remoteVideosServer1 = data.filter(video => video.isLocal === false).map(video => video.uuid)
  424. }
  425. {
  426. const { data } = await servers[1].videos.list()
  427. remoteVideosServer2 = data.filter(video => video.isLocal === false).map(video => video.uuid)
  428. }
  429. {
  430. const { data } = await servers[2].videos.list()
  431. localVideosServer3 = data.filter(video => video.isLocal === true).map(video => video.uuid)
  432. remoteVideosServer3 = data.filter(video => video.isLocal === false).map(video => video.uuid)
  433. }
  434. })
  435. it('Should view multiple videos on owned servers', async function () {
  436. this.timeout(30000)
  437. await servers[2].views.simulateView({ id: localVideosServer3[0] })
  438. await wait(1000)
  439. await servers[2].views.simulateView({ id: localVideosServer3[0] })
  440. await servers[2].views.simulateView({ id: localVideosServer3[1] })
  441. await wait(1000)
  442. await servers[2].views.simulateView({ id: localVideosServer3[0] })
  443. await servers[2].views.simulateView({ id: localVideosServer3[0] })
  444. await waitJobs(servers)
  445. for (const server of servers) {
  446. await server.debug.sendCommand({ body: { command: 'process-video-views-buffer' } })
  447. }
  448. await waitJobs(servers)
  449. for (const server of servers) {
  450. const { data } = await server.videos.list()
  451. const video0 = data.find(v => v.uuid === localVideosServer3[0])
  452. const video1 = data.find(v => v.uuid === localVideosServer3[1])
  453. expect(video0.views).to.equal(3)
  454. expect(video1.views).to.equal(1)
  455. }
  456. })
  457. it('Should view multiple videos on each servers', async function () {
  458. this.timeout(45000)
  459. const tasks: Promise<any>[] = []
  460. tasks.push(servers[0].views.simulateView({ id: remoteVideosServer1[0] }))
  461. tasks.push(servers[1].views.simulateView({ id: remoteVideosServer2[0] }))
  462. tasks.push(servers[1].views.simulateView({ id: remoteVideosServer2[0] }))
  463. tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[0] }))
  464. tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[1] }))
  465. tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[1] }))
  466. tasks.push(servers[2].views.simulateView({ id: remoteVideosServer3[1] }))
  467. tasks.push(servers[2].views.simulateView({ id: localVideosServer3[1] }))
  468. tasks.push(servers[2].views.simulateView({ id: localVideosServer3[1] }))
  469. tasks.push(servers[2].views.simulateView({ id: localVideosServer3[1] }))
  470. await Promise.all(tasks)
  471. await waitJobs(servers)
  472. for (const server of servers) {
  473. await server.debug.sendCommand({ body: { command: 'process-video-views-buffer' } })
  474. }
  475. await waitJobs(servers)
  476. let baseVideos = null
  477. for (const server of servers) {
  478. const { data } = await server.videos.list()
  479. // Initialize base videos for future comparisons
  480. if (baseVideos === null) {
  481. baseVideos = data
  482. continue
  483. }
  484. for (const baseVideo of baseVideos) {
  485. const sameVideo = data.find(video => video.name === baseVideo.name)
  486. expect(baseVideo.views).to.equal(sameVideo.views)
  487. }
  488. }
  489. })
  490. it('Should like and dislikes videos on different services', async function () {
  491. this.timeout(50000)
  492. await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
  493. await wait(500)
  494. await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'dislike' })
  495. await wait(500)
  496. await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
  497. await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'like' })
  498. await wait(500)
  499. await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'dislike' })
  500. await servers[2].videos.rate({ id: remoteVideosServer3[1], rating: 'dislike' })
  501. await wait(500)
  502. await servers[2].videos.rate({ id: remoteVideosServer3[0], rating: 'like' })
  503. await waitJobs(servers)
  504. await wait(5000)
  505. await waitJobs(servers)
  506. let baseVideos = null
  507. for (const server of servers) {
  508. const { data } = await server.videos.list()
  509. // Initialize base videos for future comparisons
  510. if (baseVideos === null) {
  511. baseVideos = data
  512. continue
  513. }
  514. for (const baseVideo of baseVideos) {
  515. const sameVideo = data.find(video => video.name === baseVideo.name)
  516. expect(baseVideo.likes).to.equal(sameVideo.likes, `Likes of ${sameVideo.uuid} do not correspond`)
  517. expect(baseVideo.dislikes).to.equal(sameVideo.dislikes, `Dislikes of ${sameVideo.uuid} do not correspond`)
  518. }
  519. }
  520. })
  521. })
  522. describe('Should manipulate these videos', function () {
  523. let updatedAtMin: Date
  524. it('Should update video 3', async function () {
  525. this.timeout(30000)
  526. const attributes = {
  527. name: 'my super video updated',
  528. category: 10,
  529. licence: 7,
  530. language: 'fr',
  531. nsfw: true,
  532. description: 'my super description updated',
  533. support: 'my super support text updated',
  534. tags: [ 'tag_up_1', 'tag_up_2' ],
  535. thumbnailfile: 'thumbnail.jpg',
  536. originallyPublishedAt: '2019-02-11T13:38:14.449Z',
  537. previewfile: 'preview.jpg'
  538. }
  539. updatedAtMin = new Date()
  540. await servers[2].videos.update({ id: toRemove[0].id, attributes })
  541. await waitJobs(servers)
  542. })
  543. it('Should have the video 3 updated on each server', async function () {
  544. this.timeout(30000)
  545. for (const server of servers) {
  546. const { data } = await server.videos.list()
  547. const videoUpdated = data.find(video => video.name === 'my super video updated')
  548. expect(!!videoUpdated).to.be.true
  549. expect(new Date(videoUpdated.updatedAt)).to.be.greaterThan(updatedAtMin)
  550. const isLocal = server.url === servers[2].url
  551. const checkAttributes = {
  552. name: 'my super video updated',
  553. category: 10,
  554. licence: 7,
  555. language: 'fr',
  556. nsfw: true,
  557. description: 'my super description updated',
  558. support: 'my super support text updated',
  559. originallyPublishedAt: '2019-02-11T13:38:14.449Z',
  560. account: {
  561. name: 'root',
  562. host: servers[2].host
  563. },
  564. isLocal,
  565. duration: 5,
  566. commentsEnabled: true,
  567. downloadEnabled: true,
  568. tags: [ 'tag_up_1', 'tag_up_2' ],
  569. privacy: VideoPrivacy.PUBLIC,
  570. channel: {
  571. displayName: 'Main root channel',
  572. name: 'root_channel',
  573. description: '',
  574. isLocal
  575. },
  576. fixture: 'video_short3.webm',
  577. files: [
  578. {
  579. resolution: 720,
  580. size: 292677
  581. }
  582. ],
  583. thumbnailfile: 'thumbnail',
  584. previewfile: 'preview'
  585. }
  586. await completeVideoCheck(server, videoUpdated, checkAttributes)
  587. }
  588. })
  589. it('Should only update thumbnail and update updatedAt attribute', async function () {
  590. this.timeout(30000)
  591. const attributes = {
  592. thumbnailfile: 'thumbnail.jpg'
  593. }
  594. updatedAtMin = new Date()
  595. await servers[2].videos.update({ id: toRemove[0].id, attributes })
  596. await waitJobs(servers)
  597. for (const server of servers) {
  598. const { data } = await server.videos.list()
  599. const videoUpdated = data.find(video => video.name === 'my super video updated')
  600. expect(new Date(videoUpdated.updatedAt)).to.be.greaterThan(updatedAtMin)
  601. }
  602. })
  603. it('Should remove the videos 3 and 3-2 by asking server 3 and correctly delete files', async function () {
  604. this.timeout(30000)
  605. for (const id of [ toRemove[0].id, toRemove[1].id ]) {
  606. await saveVideoInServers(servers, id)
  607. await servers[2].videos.remove({ id })
  608. await waitJobs(servers)
  609. for (const server of servers) {
  610. await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
  611. }
  612. }
  613. })
  614. it('Should have videos 1 and 3 on each server', async function () {
  615. for (const server of servers) {
  616. const { data } = await server.videos.list()
  617. expect(data).to.be.an('array')
  618. expect(data.length).to.equal(2)
  619. expect(data[0].name).not.to.equal(data[1].name)
  620. expect(data[0].name).not.to.equal(toRemove[0].name)
  621. expect(data[1].name).not.to.equal(toRemove[0].name)
  622. expect(data[0].name).not.to.equal(toRemove[1].name)
  623. expect(data[1].name).not.to.equal(toRemove[1].name)
  624. videoUUID = data.find(video => video.name === 'my super name for server 1').uuid
  625. }
  626. })
  627. it('Should get the same video by UUID on each server', async function () {
  628. let baseVideo = null
  629. for (const server of servers) {
  630. const video = await server.videos.get({ id: videoUUID })
  631. if (baseVideo === null) {
  632. baseVideo = video
  633. continue
  634. }
  635. expect(baseVideo.name).to.equal(video.name)
  636. expect(baseVideo.uuid).to.equal(video.uuid)
  637. expect(baseVideo.category.id).to.equal(video.category.id)
  638. expect(baseVideo.language.id).to.equal(video.language.id)
  639. expect(baseVideo.licence.id).to.equal(video.licence.id)
  640. expect(baseVideo.nsfw).to.equal(video.nsfw)
  641. expect(baseVideo.account.name).to.equal(video.account.name)
  642. expect(baseVideo.account.displayName).to.equal(video.account.displayName)
  643. expect(baseVideo.account.url).to.equal(video.account.url)
  644. expect(baseVideo.account.host).to.equal(video.account.host)
  645. expect(baseVideo.tags).to.deep.equal(video.tags)
  646. }
  647. })
  648. it('Should get the preview from each server', async function () {
  649. for (const server of servers) {
  650. const video = await server.videos.get({ id: videoUUID })
  651. await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
  652. }
  653. })
  654. })
  655. describe('Should comment these videos', function () {
  656. let childOfFirstChild: VideoCommentThreadTree
  657. it('Should add comment (threads and replies)', async function () {
  658. this.timeout(25000)
  659. {
  660. const text = 'my super first comment'
  661. await servers[0].comments.createThread({ videoId: videoUUID, text })
  662. }
  663. {
  664. const text = 'my super second comment'
  665. await servers[2].comments.createThread({ videoId: videoUUID, text })
  666. }
  667. await waitJobs(servers)
  668. {
  669. const threadId = await servers[1].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
  670. const text = 'my super answer to thread 1'
  671. await servers[1].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text })
  672. }
  673. await waitJobs(servers)
  674. {
  675. const threadId = await servers[2].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
  676. const body = await servers[2].comments.getThread({ videoId: videoUUID, threadId })
  677. const childCommentId = body.children[0].comment.id
  678. const text3 = 'my second answer to thread 1'
  679. await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text: text3 })
  680. const text2 = 'my super answer to answer of thread 1'
  681. await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: childCommentId, text: text2 })
  682. }
  683. await waitJobs(servers)
  684. })
  685. it('Should have these threads', async function () {
  686. for (const server of servers) {
  687. const body = await server.comments.listThreads({ videoId: videoUUID })
  688. expect(body.total).to.equal(2)
  689. expect(body.data).to.be.an('array')
  690. expect(body.data).to.have.lengthOf(2)
  691. {
  692. const comment = body.data.find(c => c.text === 'my super first comment')
  693. expect(comment).to.not.be.undefined
  694. expect(comment.inReplyToCommentId).to.be.null
  695. expect(comment.account.name).to.equal('root')
  696. expect(comment.account.host).to.equal(servers[0].host)
  697. expect(comment.totalReplies).to.equal(3)
  698. expect(dateIsValid(comment.createdAt as string)).to.be.true
  699. expect(dateIsValid(comment.updatedAt as string)).to.be.true
  700. }
  701. {
  702. const comment = body.data.find(c => c.text === 'my super second comment')
  703. expect(comment).to.not.be.undefined
  704. expect(comment.inReplyToCommentId).to.be.null
  705. expect(comment.account.name).to.equal('root')
  706. expect(comment.account.host).to.equal(servers[2].host)
  707. expect(comment.totalReplies).to.equal(0)
  708. expect(dateIsValid(comment.createdAt as string)).to.be.true
  709. expect(dateIsValid(comment.updatedAt as string)).to.be.true
  710. }
  711. }
  712. })
  713. it('Should have these comments', async function () {
  714. for (const server of servers) {
  715. const body = await server.comments.listThreads({ videoId: videoUUID })
  716. const threadId = body.data.find(c => c.text === 'my super first comment').id
  717. const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
  718. expect(tree.comment.text).equal('my super first comment')
  719. expect(tree.comment.account.name).equal('root')
  720. expect(tree.comment.account.host).equal(servers[0].host)
  721. expect(tree.children).to.have.lengthOf(2)
  722. const firstChild = tree.children[0]
  723. expect(firstChild.comment.text).to.equal('my super answer to thread 1')
  724. expect(firstChild.comment.account.name).equal('root')
  725. expect(firstChild.comment.account.host).equal(servers[1].host)
  726. expect(firstChild.children).to.have.lengthOf(1)
  727. childOfFirstChild = firstChild.children[0]
  728. expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
  729. expect(childOfFirstChild.comment.account.name).equal('root')
  730. expect(childOfFirstChild.comment.account.host).equal(servers[2].host)
  731. expect(childOfFirstChild.children).to.have.lengthOf(0)
  732. const secondChild = tree.children[1]
  733. expect(secondChild.comment.text).to.equal('my second answer to thread 1')
  734. expect(secondChild.comment.account.name).equal('root')
  735. expect(secondChild.comment.account.host).equal(servers[2].host)
  736. expect(secondChild.children).to.have.lengthOf(0)
  737. }
  738. })
  739. it('Should delete a reply', async function () {
  740. this.timeout(30000)
  741. await servers[2].comments.delete({ videoId: videoUUID, commentId: childOfFirstChild.comment.id })
  742. await waitJobs(servers)
  743. })
  744. it('Should have this comment marked as deleted', async function () {
  745. for (const server of servers) {
  746. const { data } = await server.comments.listThreads({ videoId: videoUUID })
  747. const threadId = data.find(c => c.text === 'my super first comment').id
  748. const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
  749. expect(tree.comment.text).equal('my super first comment')
  750. const firstChild = tree.children[0]
  751. expect(firstChild.comment.text).to.equal('my super answer to thread 1')
  752. expect(firstChild.children).to.have.lengthOf(1)
  753. const deletedComment = firstChild.children[0].comment
  754. expect(deletedComment.isDeleted).to.be.true
  755. expect(deletedComment.deletedAt).to.not.be.null
  756. expect(deletedComment.account).to.be.null
  757. expect(deletedComment.text).to.equal('')
  758. const secondChild = tree.children[1]
  759. expect(secondChild.comment.text).to.equal('my second answer to thread 1')
  760. }
  761. })
  762. it('Should delete the thread comments', async function () {
  763. this.timeout(30000)
  764. const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
  765. const commentId = data.find(c => c.text === 'my super first comment').id
  766. await servers[0].comments.delete({ videoId: videoUUID, commentId })
  767. await waitJobs(servers)
  768. })
  769. it('Should have the threads marked as deleted on other servers too', async function () {
  770. for (const server of servers) {
  771. const body = await server.comments.listThreads({ videoId: videoUUID })
  772. expect(body.total).to.equal(2)
  773. expect(body.data).to.be.an('array')
  774. expect(body.data).to.have.lengthOf(2)
  775. {
  776. const comment = body.data[0]
  777. expect(comment).to.not.be.undefined
  778. expect(comment.inReplyToCommentId).to.be.null
  779. expect(comment.account.name).to.equal('root')
  780. expect(comment.account.host).to.equal(servers[2].host)
  781. expect(comment.totalReplies).to.equal(0)
  782. expect(dateIsValid(comment.createdAt as string)).to.be.true
  783. expect(dateIsValid(comment.updatedAt as string)).to.be.true
  784. }
  785. {
  786. const deletedComment = body.data[1]
  787. expect(deletedComment).to.not.be.undefined
  788. expect(deletedComment.isDeleted).to.be.true
  789. expect(deletedComment.deletedAt).to.not.be.null
  790. expect(deletedComment.text).to.equal('')
  791. expect(deletedComment.inReplyToCommentId).to.be.null
  792. expect(deletedComment.account).to.be.null
  793. expect(deletedComment.totalReplies).to.equal(2)
  794. expect(dateIsValid(deletedComment.createdAt as string)).to.be.true
  795. expect(dateIsValid(deletedComment.updatedAt as string)).to.be.true
  796. expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
  797. }
  798. }
  799. })
  800. it('Should delete a remote thread by the origin server', async function () {
  801. this.timeout(5000)
  802. const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
  803. const commentId = data.find(c => c.text === 'my super second comment').id
  804. await servers[0].comments.delete({ videoId: videoUUID, commentId })
  805. await waitJobs(servers)
  806. })
  807. it('Should have the threads marked as deleted on other servers too', async function () {
  808. for (const server of servers) {
  809. const body = await server.comments.listThreads({ videoId: videoUUID })
  810. expect(body.total).to.equal(2)
  811. expect(body.data).to.have.lengthOf(2)
  812. {
  813. const comment = body.data[0]
  814. expect(comment.text).to.equal('')
  815. expect(comment.isDeleted).to.be.true
  816. expect(comment.createdAt).to.not.be.null
  817. expect(comment.deletedAt).to.not.be.null
  818. expect(comment.account).to.be.null
  819. expect(comment.totalReplies).to.equal(0)
  820. }
  821. {
  822. const comment = body.data[1]
  823. expect(comment.text).to.equal('')
  824. expect(comment.isDeleted).to.be.true
  825. expect(comment.createdAt).to.not.be.null
  826. expect(comment.deletedAt).to.not.be.null
  827. expect(comment.account).to.be.null
  828. expect(comment.totalReplies).to.equal(2)
  829. }
  830. }
  831. })
  832. it('Should disable comments and download', async function () {
  833. this.timeout(20000)
  834. const attributes = {
  835. commentsEnabled: false,
  836. downloadEnabled: false
  837. }
  838. await servers[0].videos.update({ id: videoUUID, attributes })
  839. await waitJobs(servers)
  840. for (const server of servers) {
  841. const video = await server.videos.get({ id: videoUUID })
  842. expect(video.commentsEnabled).to.be.false
  843. expect(video.downloadEnabled).to.be.false
  844. const text = 'my super forbidden comment'
  845. await server.comments.createThread({ videoId: videoUUID, text, expectedStatus: HttpStatusCode.CONFLICT_409 })
  846. }
  847. })
  848. })
  849. describe('With minimum parameters', function () {
  850. it('Should upload and propagate the video', async function () {
  851. this.timeout(120000)
  852. const path = '/api/v1/videos/upload'
  853. const req = request(servers[1].url)
  854. .post(path)
  855. .set('Accept', 'application/json')
  856. .set('Authorization', 'Bearer ' + servers[1].accessToken)
  857. .field('name', 'minimum parameters')
  858. .field('privacy', '1')
  859. .field('channelId', '1')
  860. await req.attach('videofile', buildAbsoluteFixturePath('video_short.webm'))
  861. .expect(HttpStatusCode.OK_200)
  862. await waitJobs(servers)
  863. for (const server of servers) {
  864. const { data } = await server.videos.list()
  865. const video = data.find(v => v.name === 'minimum parameters')
  866. const isLocal = server.url === servers[1].url
  867. const checkAttributes = {
  868. name: 'minimum parameters',
  869. category: null,
  870. licence: null,
  871. language: null,
  872. nsfw: false,
  873. description: null,
  874. support: null,
  875. account: {
  876. name: 'root',
  877. host: servers[1].host
  878. },
  879. isLocal,
  880. duration: 5,
  881. commentsEnabled: true,
  882. downloadEnabled: true,
  883. tags: [],
  884. privacy: VideoPrivacy.PUBLIC,
  885. channel: {
  886. displayName: 'Main root channel',
  887. name: 'root_channel',
  888. description: '',
  889. isLocal
  890. },
  891. fixture: 'video_short.webm',
  892. files: [
  893. {
  894. resolution: 720,
  895. size: 61000
  896. },
  897. {
  898. resolution: 480,
  899. size: 40000
  900. },
  901. {
  902. resolution: 360,
  903. size: 32000
  904. },
  905. {
  906. resolution: 240,
  907. size: 23000
  908. }
  909. ]
  910. }
  911. await completeVideoCheck(server, video, checkAttributes)
  912. }
  913. })
  914. })
  915. describe('TMP directory', function () {
  916. it('Should have an empty tmp directory', async function () {
  917. for (const server of servers) {
  918. await checkTmpIsEmpty(server)
  919. }
  920. })
  921. })
  922. after(async function () {
  923. await cleanupTests(servers)
  924. })
  925. })