]>
Commit | Line | Data |
---|---|---|
a1587156 | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
fe3a55b0 | 2 | |
fe3a55b0 | 3 | import 'mocha' |
696d83fd C |
4 | import * as chai from 'chai' |
5 | import * as libxmljs from 'libxmljs' | |
6 | import { | |
7 | addAccountToAccountBlocklist, | |
8 | addAccountToServerBlocklist, | |
9 | removeAccountFromServerBlocklist | |
10 | } from '@shared/extra-utils/users/blocklist' | |
11 | import { VideoPrivacy } from '@shared/models' | |
fe3a55b0 | 12 | import { |
7c3b7976 | 13 | cleanupTests, |
662fb3ab | 14 | createUser, |
fe3a55b0 C |
15 | doubleFollow, |
16 | flushAndRunMultipleServers, | |
57cfff78 C |
17 | getJSONfeed, |
18 | getMyUserInformation, | |
fe3a55b0 | 19 | getXMLfeed, |
fe3a55b0 C |
20 | ServerInfo, |
21 | setAccessTokensToServers, | |
57cfff78 | 22 | uploadVideo, |
696d83fd | 23 | uploadVideoAndGetId, |
97816649 | 24 | userLogin, |
afff310e RK |
25 | flushAndRunServer, |
26 | getUserScopedTokens | |
94565d52 | 27 | } from '../../../shared/extra-utils' |
94565d52 | 28 | import { waitJobs } from '../../../shared/extra-utils/server/jobs' |
696d83fd | 29 | import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments' |
662fb3ab | 30 | import { User } from '../../../shared/models/users' |
afff310e RK |
31 | import { ScopedToken } from '@shared/models/users/user-scoped-token' |
32 | import { listUserSubscriptionVideos, addUserSubscription } from '@shared/extra-utils/users/user-subscriptions' | |
fe3a55b0 C |
33 | |
34 | chai.use(require('chai-xml')) | |
35 | chai.use(require('chai-json-schema')) | |
36 | chai.config.includeStack = true | |
37 | const expect = chai.expect | |
38 | ||
39 | describe('Test syndication feeds', () => { | |
40 | let servers: ServerInfo[] = [] | |
97816649 | 41 | let serverHLSOnly: ServerInfo |
662fb3ab | 42 | let userAccessToken: string |
57cfff78 C |
43 | let rootAccountId: number |
44 | let rootChannelId: number | |
45 | let userAccountId: number | |
46 | let userChannelId: number | |
afff310e | 47 | let userFeedToken: string |
fe3a55b0 C |
48 | |
49 | before(async function () { | |
50 | this.timeout(120000) | |
51 | ||
52 | // Run servers | |
53 | servers = await flushAndRunMultipleServers(2) | |
97816649 C |
54 | serverHLSOnly = await flushAndRunServer(3, { |
55 | transcoding: { | |
56 | enabled: true, | |
57 | webtorrent: { enabled: false }, | |
58 | hls: { enabled: true } | |
59 | } | |
60 | }) | |
fe3a55b0 | 61 | |
97816649 | 62 | await setAccessTokensToServers([ ...servers, serverHLSOnly ]) |
fe3a55b0 C |
63 | await doubleFollow(servers[0], servers[1]) |
64 | ||
662fb3ab C |
65 | { |
66 | const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) | |
67 | const user: User = res.body | |
57cfff78 C |
68 | rootAccountId = user.account.id |
69 | rootChannelId = user.videoChannels[0].id | |
fe3a55b0 | 70 | } |
fe3a55b0 | 71 | |
662fb3ab C |
72 | { |
73 | const attr = { username: 'john', password: 'password' } | |
a1587156 | 74 | await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) |
662fb3ab C |
75 | userAccessToken = await userLogin(servers[0], attr) |
76 | ||
77 | const res = await getMyUserInformation(servers[0].url, userAccessToken) | |
78 | const user: User = res.body | |
57cfff78 C |
79 | userAccountId = user.account.id |
80 | userChannelId = user.videoChannels[0].id | |
afff310e RK |
81 | |
82 | const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) | |
83 | const token: ScopedToken = res2.body | |
84 | userFeedToken = token.feedToken | |
662fb3ab C |
85 | } |
86 | ||
87 | { | |
a1587156 | 88 | await uploadVideo(servers[0].url, userAccessToken, { name: 'user video' }) |
662fb3ab C |
89 | } |
90 | ||
91 | { | |
92 | const videoAttributes = { | |
93 | name: 'my super name for server 1', | |
94 | description: 'my super description for server 1', | |
95 | fixture: 'video_short.webm' | |
96 | } | |
a1587156 | 97 | const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) |
662fb3ab C |
98 | const videoId = res.body.video.id |
99 | ||
a1587156 C |
100 | await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 1') |
101 | await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 2') | |
662fb3ab | 102 | } |
fe3a55b0 | 103 | |
68b6fd21 C |
104 | { |
105 | const videoAttributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED } | |
106 | const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) | |
107 | const videoId = res.body.video.id | |
108 | ||
109 | await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'comment on unlisted video') | |
110 | } | |
111 | ||
3cd0734f | 112 | await waitJobs(servers) |
fe3a55b0 C |
113 | }) |
114 | ||
115 | describe('All feed', function () { | |
116 | ||
117 | it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () { | |
118 | for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { | |
a1587156 | 119 | const rss = await getXMLfeed(servers[0].url, feed) |
fe3a55b0 C |
120 | expect(rss.text).xml.to.be.valid() |
121 | ||
a1587156 | 122 | const atom = await getXMLfeed(servers[0].url, feed, 'atom') |
fe3a55b0 C |
123 | expect(atom.text).xml.to.be.valid() |
124 | } | |
125 | }) | |
126 | ||
127 | it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () { | |
128 | for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { | |
a1587156 C |
129 | const json = await getJSONfeed(servers[0].url, feed) |
130 | expect(JSON.parse(json.text)).to.be.jsonSchema({ type: 'object' }) | |
fe3a55b0 C |
131 | } |
132 | }) | |
133 | }) | |
134 | ||
135 | describe('Videos feed', function () { | |
97816649 | 136 | |
fe3a55b0 C |
137 | it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () { |
138 | for (const server of servers) { | |
139 | const rss = await getXMLfeed(server.url, 'videos') | |
140 | const xmlDoc = libxmljs.parseXmlString(rss.text) | |
141 | const xmlEnclosure = xmlDoc.get('/rss/channel/item/enclosure') | |
142 | expect(xmlEnclosure).to.exist | |
143 | expect(xmlEnclosure.attr('type').value()).to.be.equal('application/x-bittorrent') | |
144 | expect(xmlEnclosure.attr('length').value()).to.be.equal('218910') | |
145 | expect(xmlEnclosure.attr('url').value()).to.contain('720.torrent') | |
146 | } | |
147 | }) | |
148 | ||
149 | it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () { | |
150 | for (const server of servers) { | |
151 | const json = await getJSONfeed(server.url, 'videos') | |
152 | const jsonObj = JSON.parse(json.text) | |
662fb3ab | 153 | expect(jsonObj.items.length).to.be.equal(2) |
a1587156 C |
154 | expect(jsonObj.items[0].attachments).to.exist |
155 | expect(jsonObj.items[0].attachments.length).to.be.eq(1) | |
156 | expect(jsonObj.items[0].attachments[0].mime_type).to.be.eq('application/x-bittorrent') | |
157 | expect(jsonObj.items[0].attachments[0].size_in_bytes).to.be.eq(218910) | |
158 | expect(jsonObj.items[0].attachments[0].url).to.contain('720.torrent') | |
fe3a55b0 C |
159 | } |
160 | }) | |
662fb3ab C |
161 | |
162 | it('Should filter by account', async function () { | |
57cfff78 C |
163 | { |
164 | const json = await getJSONfeed(servers[0].url, 'videos', { accountId: rootAccountId }) | |
165 | const jsonObj = JSON.parse(json.text) | |
166 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 C |
167 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
168 | expect(jsonObj.items[0].author.name).to.equal('root') | |
57cfff78 C |
169 | } |
170 | ||
171 | { | |
172 | const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId }) | |
173 | const jsonObj = JSON.parse(json.text) | |
174 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 C |
175 | expect(jsonObj.items[0].title).to.equal('user video') |
176 | expect(jsonObj.items[0].author.name).to.equal('john') | |
57cfff78 C |
177 | } |
178 | ||
662fb3ab C |
179 | for (const server of servers) { |
180 | { | |
57cfff78 | 181 | const json = await getJSONfeed(server.url, 'videos', { accountName: 'root@localhost:' + servers[0].port }) |
662fb3ab C |
182 | const jsonObj = JSON.parse(json.text) |
183 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 | 184 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
662fb3ab C |
185 | } |
186 | ||
187 | { | |
57cfff78 | 188 | const json = await getJSONfeed(server.url, 'videos', { accountName: 'john@localhost:' + servers[0].port }) |
662fb3ab C |
189 | const jsonObj = JSON.parse(json.text) |
190 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 | 191 | expect(jsonObj.items[0].title).to.equal('user video') |
662fb3ab C |
192 | } |
193 | } | |
57cfff78 | 194 | }) |
662fb3ab | 195 | |
57cfff78 | 196 | it('Should filter by video channel', async function () { |
662fb3ab | 197 | { |
57cfff78 | 198 | const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: rootChannelId }) |
662fb3ab C |
199 | const jsonObj = JSON.parse(json.text) |
200 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 C |
201 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
202 | expect(jsonObj.items[0].author.name).to.equal('root') | |
662fb3ab C |
203 | } |
204 | ||
205 | { | |
57cfff78 | 206 | const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: userChannelId }) |
662fb3ab C |
207 | const jsonObj = JSON.parse(json.text) |
208 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 C |
209 | expect(jsonObj.items[0].title).to.equal('user video') |
210 | expect(jsonObj.items[0].author.name).to.equal('john') | |
662fb3ab | 211 | } |
662fb3ab | 212 | |
662fb3ab C |
213 | for (const server of servers) { |
214 | { | |
57cfff78 | 215 | const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'root_channel@localhost:' + servers[0].port }) |
662fb3ab C |
216 | const jsonObj = JSON.parse(json.text) |
217 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 | 218 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
662fb3ab C |
219 | } |
220 | ||
221 | { | |
57cfff78 | 222 | const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'john_channel@localhost:' + servers[0].port }) |
662fb3ab C |
223 | const jsonObj = JSON.parse(json.text) |
224 | expect(jsonObj.items.length).to.be.equal(1) | |
a1587156 | 225 | expect(jsonObj.items[0].title).to.equal('user video') |
662fb3ab C |
226 | } |
227 | } | |
662fb3ab | 228 | }) |
97816649 C |
229 | |
230 | it('Should correctly have videos feed with HLS only', async function () { | |
231 | this.timeout(120000) | |
232 | ||
233 | await uploadVideo(serverHLSOnly.url, serverHLSOnly.accessToken, { name: 'hls only video' }) | |
234 | ||
235 | await waitJobs([ serverHLSOnly ]) | |
236 | ||
237 | const json = await getJSONfeed(serverHLSOnly.url, 'videos') | |
238 | const jsonObj = JSON.parse(json.text) | |
239 | expect(jsonObj.items.length).to.be.equal(1) | |
240 | expect(jsonObj.items[0].attachments).to.exist | |
241 | expect(jsonObj.items[0].attachments.length).to.be.eq(4) | |
242 | ||
243 | for (let i = 0; i < 4; i++) { | |
244 | expect(jsonObj.items[0].attachments[i].mime_type).to.be.eq('application/x-bittorrent') | |
245 | expect(jsonObj.items[0].attachments[i].size_in_bytes).to.be.greaterThan(0) | |
246 | expect(jsonObj.items[0].attachments[i].url).to.exist | |
247 | } | |
248 | }) | |
fe3a55b0 C |
249 | }) |
250 | ||
251 | describe('Video comments feed', function () { | |
68b6fd21 C |
252 | |
253 | it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () { | |
fe3a55b0 C |
254 | for (const server of servers) { |
255 | const json = await getJSONfeed(server.url, 'video-comments') | |
256 | ||
257 | const jsonObj = JSON.parse(json.text) | |
258 | expect(jsonObj.items.length).to.be.equal(2) | |
a1587156 C |
259 | expect(jsonObj.items[0].html_content).to.equal('super comment 2') |
260 | expect(jsonObj.items[1].html_content).to.equal('super comment 1') | |
fe3a55b0 C |
261 | } |
262 | }) | |
1df8a4d7 C |
263 | |
264 | it('Should not list comments from muted accounts or instances', async function () { | |
696d83fd C |
265 | this.timeout(30000) |
266 | ||
267 | const remoteHandle = 'root@localhost:' + servers[0].port | |
268 | ||
269 | await addAccountToServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) | |
1df8a4d7 C |
270 | |
271 | { | |
272 | const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 2 }) | |
273 | const jsonObj = JSON.parse(json.text) | |
274 | expect(jsonObj.items.length).to.be.equal(0) | |
275 | } | |
276 | ||
696d83fd C |
277 | await removeAccountFromServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) |
278 | ||
279 | { | |
280 | const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' })).uuid | |
281 | await waitJobs(servers) | |
282 | await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'super comment') | |
283 | await waitJobs(servers) | |
284 | ||
285 | const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 3 }) | |
286 | const jsonObj = JSON.parse(json.text) | |
287 | expect(jsonObj.items.length).to.be.equal(3) | |
288 | } | |
289 | ||
290 | await addAccountToAccountBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) | |
291 | ||
292 | { | |
293 | const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 4 }) | |
294 | const jsonObj = JSON.parse(json.text) | |
295 | expect(jsonObj.items.length).to.be.equal(2) | |
296 | } | |
1df8a4d7 | 297 | }) |
fe3a55b0 C |
298 | }) |
299 | ||
afff310e RK |
300 | describe('Video feed from my subscriptions', function () { |
301 | /** | |
302 | * use the 'version' query parameter to bust cache between tests | |
303 | */ | |
304 | ||
305 | it('Should list no videos for a user with no videos and no subscriptions', async function () { | |
306 | let feeduserAccountId: number | |
307 | let feeduserFeedToken: string | |
308 | ||
309 | const attr = { username: 'feeduser', password: 'password' } | |
310 | await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) | |
311 | const feeduserAccessToken = await userLogin(servers[0], attr) | |
312 | ||
313 | { | |
314 | const res = await getMyUserInformation(servers[0].url, feeduserAccessToken) | |
315 | const user: User = res.body | |
316 | feeduserAccountId = user.account.id | |
317 | } | |
318 | ||
319 | { | |
320 | const res = await getUserScopedTokens(servers[0].url, feeduserAccessToken) | |
321 | const token: ScopedToken = res.body | |
322 | feeduserFeedToken = token.feedToken | |
323 | } | |
324 | ||
325 | { | |
326 | const res = await listUserSubscriptionVideos(servers[0].url, feeduserAccessToken) | |
327 | expect(res.body.total).to.equal(0) | |
328 | ||
5beb89f2 | 329 | const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: feeduserFeedToken }) |
afff310e RK |
330 | const jsonObj = JSON.parse(json.text) |
331 | expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos | |
332 | } | |
333 | }) | |
334 | ||
335 | it('Should list no videos for a user with videos but no subscriptions', async function () { | |
336 | { | |
337 | const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) | |
338 | expect(res.body.total).to.equal(0) | |
339 | ||
5beb89f2 | 340 | const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken }) |
afff310e RK |
341 | const jsonObj = JSON.parse(json.text) |
342 | expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos | |
343 | } | |
344 | }) | |
345 | ||
346 | it('Should list self videos for a user with a subscription to themselves', async function () { | |
347 | this.timeout(30000) | |
348 | ||
349 | await addUserSubscription(servers[0].url, userAccessToken, 'john_channel@localhost:' + servers[0].port) | |
350 | await waitJobs(servers) | |
351 | ||
352 | { | |
353 | const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) | |
354 | expect(res.body.total).to.equal(1) | |
355 | expect(res.body.data[0].name).to.equal('user video') | |
356 | ||
5beb89f2 | 357 | const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 1 }) |
afff310e RK |
358 | const jsonObj = JSON.parse(json.text) |
359 | expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's | |
360 | } | |
361 | }) | |
362 | ||
363 | it('Should list videos of a user\'s subscription', async function () { | |
364 | this.timeout(30000) | |
365 | ||
366 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) | |
367 | await waitJobs(servers) | |
368 | ||
369 | { | |
370 | const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) | |
371 | expect(res.body.total).to.equal(2, "there should be 2 videos part of the subscription") | |
372 | ||
5beb89f2 | 373 | const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 2 }) |
afff310e RK |
374 | const jsonObj = JSON.parse(json.text) |
375 | expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's | |
376 | } | |
377 | }) | |
378 | ||
379 | }) | |
380 | ||
7c3b7976 | 381 | after(async function () { |
97816649 | 382 | await cleanupTests([ ...servers, serverHLSOnly ]) |
fe3a55b0 C |
383 | }) |
384 | }) |