]>
Commit | Line | Data |
---|---|---|
a1587156 | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
fe3a55b0 | 2 | |
fe3a55b0 | 3 | import 'mocha' |
696d83fd | 4 | import * as chai from 'chai' |
2d28b0c2 | 5 | import { XMLParser, XMLValidator } from 'fast-xml-parser' |
d0800f76 | 6 | import { HttpStatusCode, VideoPrivacy } from '@shared/models' |
696d83fd | 7 | import { |
7c3b7976 | 8 | cleanupTests, |
254d3579 C |
9 | createMultipleServers, |
10 | createSingleServer, | |
4c7e60bc | 11 | doubleFollow, |
20bafcb6 | 12 | makeGetRequest, |
254d3579 | 13 | PeerTubeServer, |
fe3a55b0 | 14 | setAccessTokensToServers, |
d0800f76 | 15 | setDefaultChannelAvatar, |
5f8bd4cb | 16 | waitJobs |
bf54587a | 17 | } from '@shared/server-commands' |
fe3a55b0 C |
18 | |
19 | chai.use(require('chai-xml')) | |
20 | chai.use(require('chai-json-schema')) | |
21 | chai.config.includeStack = true | |
22 | const expect = chai.expect | |
23 | ||
24 | describe('Test syndication feeds', () => { | |
254d3579 C |
25 | let servers: PeerTubeServer[] = [] |
26 | let serverHLSOnly: PeerTubeServer | |
662fb3ab | 27 | let userAccessToken: string |
57cfff78 C |
28 | let rootAccountId: number |
29 | let rootChannelId: number | |
30 | let userAccountId: number | |
31 | let userChannelId: number | |
afff310e | 32 | let userFeedToken: string |
fe3a55b0 C |
33 | |
34 | before(async function () { | |
35 | this.timeout(120000) | |
36 | ||
37 | // Run servers | |
254d3579 C |
38 | servers = await createMultipleServers(2) |
39 | serverHLSOnly = await createSingleServer(3, { | |
97816649 C |
40 | transcoding: { |
41 | enabled: true, | |
42 | webtorrent: { enabled: false }, | |
43 | hls: { enabled: true } | |
44 | } | |
45 | }) | |
fe3a55b0 | 46 | |
97816649 | 47 | await setAccessTokensToServers([ ...servers, serverHLSOnly ]) |
d0800f76 | 48 | await setDefaultChannelAvatar(servers[0]) |
fe3a55b0 C |
49 | await doubleFollow(servers[0], servers[1]) |
50 | ||
662fb3ab | 51 | { |
89d241a7 | 52 | const user = await servers[0].users.getMyInfo() |
57cfff78 C |
53 | rootAccountId = user.account.id |
54 | rootChannelId = user.videoChannels[0].id | |
fe3a55b0 | 55 | } |
fe3a55b0 | 56 | |
662fb3ab | 57 | { |
20bafcb6 | 58 | userAccessToken = await servers[0].users.generateUserAndToken('john') |
662fb3ab | 59 | |
89d241a7 | 60 | const user = await servers[0].users.getMyInfo({ token: userAccessToken }) |
57cfff78 C |
61 | userAccountId = user.account.id |
62 | userChannelId = user.videoChannels[0].id | |
afff310e | 63 | |
89d241a7 | 64 | const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken }) |
afff310e | 65 | userFeedToken = token.feedToken |
662fb3ab C |
66 | } |
67 | ||
68 | { | |
89d241a7 | 69 | await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'user video' } }) |
662fb3ab C |
70 | } |
71 | ||
72 | { | |
d23dd9fb | 73 | const attributes = { |
662fb3ab C |
74 | name: 'my super name for server 1', |
75 | description: 'my super description for server 1', | |
76 | fixture: 'video_short.webm' | |
77 | } | |
89d241a7 | 78 | const { id } = await servers[0].videos.upload({ attributes }) |
662fb3ab | 79 | |
89d241a7 C |
80 | await servers[0].comments.createThread({ videoId: id, text: 'super comment 1' }) |
81 | await servers[0].comments.createThread({ videoId: id, text: 'super comment 2' }) | |
662fb3ab | 82 | } |
fe3a55b0 | 83 | |
68b6fd21 | 84 | { |
d23dd9fb | 85 | const attributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED } |
89d241a7 | 86 | const { id } = await servers[0].videos.upload({ attributes }) |
68b6fd21 | 87 | |
89d241a7 | 88 | await servers[0].comments.createThread({ videoId: id, text: 'comment on unlisted video' }) |
68b6fd21 C |
89 | } |
90 | ||
3cd0734f | 91 | await waitJobs(servers) |
fe3a55b0 C |
92 | }) |
93 | ||
94 | describe('All feed', function () { | |
95 | ||
96 | it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () { | |
97 | for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { | |
89d241a7 | 98 | const rss = await servers[0].feed.getXML({ feed }) |
c1bc8ee4 | 99 | expect(rss).xml.to.be.valid() |
fe3a55b0 | 100 | |
89d241a7 | 101 | const atom = await servers[0].feed.getXML({ feed, format: 'atom' }) |
c1bc8ee4 | 102 | expect(atom).xml.to.be.valid() |
fe3a55b0 C |
103 | } |
104 | }) | |
105 | ||
106 | it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () { | |
107 | for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { | |
89d241a7 | 108 | const jsonText = await servers[0].feed.getJSON({ feed }) |
c1bc8ee4 | 109 | expect(JSON.parse(jsonText)).to.be.jsonSchema({ type: 'object' }) |
fe3a55b0 C |
110 | } |
111 | }) | |
20bafcb6 C |
112 | |
113 | it('Should serve the endpoint with a classic request', async function () { | |
114 | await makeGetRequest({ | |
115 | url: servers[0].url, | |
116 | path: '/feeds/videos.xml', | |
117 | accept: 'application/xml', | |
118 | expectedStatus: HttpStatusCode.OK_200 | |
119 | }) | |
120 | }) | |
121 | ||
122 | it('Should serve the endpoint as a cached request', async function () { | |
123 | const res = await makeGetRequest({ | |
124 | url: servers[0].url, | |
125 | path: '/feeds/videos.xml', | |
126 | accept: 'application/xml', | |
127 | expectedStatus: HttpStatusCode.OK_200 | |
128 | }) | |
129 | ||
130 | expect(res.headers['x-api-cache-cached']).to.equal('true') | |
131 | }) | |
132 | ||
133 | it('Should not serve the endpoint as a cached request', async function () { | |
134 | const res = await makeGetRequest({ | |
135 | url: servers[0].url, | |
136 | path: '/feeds/videos.xml?v=186', | |
137 | accept: 'application/xml', | |
138 | expectedStatus: HttpStatusCode.OK_200 | |
139 | }) | |
140 | ||
141 | expect(res.headers['x-api-cache-cached']).to.not.exist | |
142 | }) | |
143 | ||
144 | it('Should refuse to serve the endpoint without accept header', async function () { | |
145 | await makeGetRequest({ url: servers[0].url, path: '/feeds/videos.xml', expectedStatus: HttpStatusCode.NOT_ACCEPTABLE_406 }) | |
146 | }) | |
fe3a55b0 C |
147 | }) |
148 | ||
149 | describe('Videos feed', function () { | |
97816649 | 150 | |
fe3a55b0 C |
151 | it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () { |
152 | for (const server of servers) { | |
89d241a7 | 153 | const rss = await server.feed.getXML({ feed: 'videos' }) |
2d28b0c2 | 154 | expect(XMLValidator.validate(rss)).to.be.true |
b70025bf | 155 | |
2d28b0c2 C |
156 | const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false }) |
157 | const xmlDoc = parser.parse(rss) | |
b70025bf C |
158 | |
159 | const enclosure = xmlDoc.rss.channel.item[0].enclosure | |
160 | expect(enclosure).to.exist | |
4393b255 C |
161 | |
162 | expect(enclosure['@_type']).to.equal('video/webm') | |
b70025bf | 163 | expect(enclosure['@_length']).to.equal(218910) |
4393b255 | 164 | expect(enclosure['@_url']).to.contain('-720.webm') |
fe3a55b0 C |
165 | } |
166 | }) | |
167 | ||
168 | it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () { | |
169 | for (const server of servers) { | |
89d241a7 | 170 | const json = await server.feed.getJSON({ feed: 'videos' }) |
c1bc8ee4 | 171 | const jsonObj = JSON.parse(json) |
662fb3ab | 172 | expect(jsonObj.items.length).to.be.equal(2) |
a1587156 C |
173 | expect(jsonObj.items[0].attachments).to.exist |
174 | expect(jsonObj.items[0].attachments.length).to.be.eq(1) | |
175 | expect(jsonObj.items[0].attachments[0].mime_type).to.be.eq('application/x-bittorrent') | |
176 | expect(jsonObj.items[0].attachments[0].size_in_bytes).to.be.eq(218910) | |
177 | expect(jsonObj.items[0].attachments[0].url).to.contain('720.torrent') | |
fe3a55b0 C |
178 | } |
179 | }) | |
662fb3ab C |
180 | |
181 | it('Should filter by account', async function () { | |
57cfff78 | 182 | { |
89d241a7 | 183 | const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: rootAccountId } }) |
c1bc8ee4 | 184 | const jsonObj = JSON.parse(json) |
57cfff78 | 185 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 C |
186 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
187 | expect(jsonObj.items[0].author.name).to.equal('root') | |
57cfff78 C |
188 | } |
189 | ||
190 | { | |
89d241a7 | 191 | const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: userAccountId } }) |
c1bc8ee4 | 192 | const jsonObj = JSON.parse(json) |
57cfff78 | 193 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 C |
194 | expect(jsonObj.items[0].title).to.equal('user video') |
195 | expect(jsonObj.items[0].author.name).to.equal('john') | |
57cfff78 C |
196 | } |
197 | ||
662fb3ab C |
198 | for (const server of servers) { |
199 | { | |
89d241a7 | 200 | const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'root@localhost:' + servers[0].port } }) |
c1bc8ee4 | 201 | const jsonObj = JSON.parse(json) |
662fb3ab | 202 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 | 203 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
662fb3ab C |
204 | } |
205 | ||
206 | { | |
89d241a7 | 207 | const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'john@localhost:' + servers[0].port } }) |
c1bc8ee4 | 208 | const jsonObj = JSON.parse(json) |
662fb3ab | 209 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 | 210 | expect(jsonObj.items[0].title).to.equal('user video') |
662fb3ab C |
211 | } |
212 | } | |
57cfff78 | 213 | }) |
662fb3ab | 214 | |
57cfff78 | 215 | it('Should filter by video channel', async function () { |
662fb3ab | 216 | { |
89d241a7 | 217 | const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: rootChannelId } }) |
c1bc8ee4 | 218 | const jsonObj = JSON.parse(json) |
662fb3ab | 219 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 C |
220 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
221 | expect(jsonObj.items[0].author.name).to.equal('root') | |
662fb3ab C |
222 | } |
223 | ||
224 | { | |
89d241a7 | 225 | const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: userChannelId } }) |
c1bc8ee4 | 226 | const jsonObj = JSON.parse(json) |
662fb3ab | 227 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 C |
228 | expect(jsonObj.items[0].title).to.equal('user video') |
229 | expect(jsonObj.items[0].author.name).to.equal('john') | |
662fb3ab | 230 | } |
662fb3ab | 231 | |
662fb3ab C |
232 | for (const server of servers) { |
233 | { | |
c1bc8ee4 | 234 | const query = { videoChannelName: 'root_channel@localhost:' + servers[0].port } |
89d241a7 | 235 | const json = await server.feed.getJSON({ feed: 'videos', query }) |
c1bc8ee4 | 236 | const jsonObj = JSON.parse(json) |
662fb3ab | 237 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 | 238 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
662fb3ab C |
239 | } |
240 | ||
241 | { | |
c1bc8ee4 | 242 | const query = { videoChannelName: 'john_channel@localhost:' + servers[0].port } |
89d241a7 | 243 | const json = await server.feed.getJSON({ feed: 'videos', query }) |
c1bc8ee4 | 244 | const jsonObj = JSON.parse(json) |
662fb3ab | 245 | expect(jsonObj.items.length).to.be.equal(1) |
a1587156 | 246 | expect(jsonObj.items[0].title).to.equal('user video') |
662fb3ab C |
247 | } |
248 | } | |
662fb3ab | 249 | }) |
97816649 C |
250 | |
251 | it('Should correctly have videos feed with HLS only', async function () { | |
252 | this.timeout(120000) | |
253 | ||
89d241a7 | 254 | await serverHLSOnly.videos.upload({ attributes: { name: 'hls only video' } }) |
97816649 C |
255 | |
256 | await waitJobs([ serverHLSOnly ]) | |
257 | ||
89d241a7 | 258 | const json = await serverHLSOnly.feed.getJSON({ feed: 'videos' }) |
c1bc8ee4 | 259 | const jsonObj = JSON.parse(json) |
97816649 C |
260 | expect(jsonObj.items.length).to.be.equal(1) |
261 | expect(jsonObj.items[0].attachments).to.exist | |
262 | expect(jsonObj.items[0].attachments.length).to.be.eq(4) | |
263 | ||
264 | for (let i = 0; i < 4; i++) { | |
265 | expect(jsonObj.items[0].attachments[i].mime_type).to.be.eq('application/x-bittorrent') | |
266 | expect(jsonObj.items[0].attachments[i].size_in_bytes).to.be.greaterThan(0) | |
267 | expect(jsonObj.items[0].attachments[i].url).to.exist | |
268 | } | |
269 | }) | |
fe3a55b0 C |
270 | }) |
271 | ||
272 | describe('Video comments feed', function () { | |
68b6fd21 C |
273 | |
274 | it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () { | |
fe3a55b0 | 275 | for (const server of servers) { |
89d241a7 | 276 | const json = await server.feed.getJSON({ feed: 'video-comments' }) |
fe3a55b0 | 277 | |
c1bc8ee4 | 278 | const jsonObj = JSON.parse(json) |
fe3a55b0 | 279 | expect(jsonObj.items.length).to.be.equal(2) |
4393b255 C |
280 | expect(jsonObj.items[0].content_html).to.contain('<p>super comment 2</p>') |
281 | expect(jsonObj.items[1].content_html).to.contain('<p>super comment 1</p>') | |
fe3a55b0 C |
282 | } |
283 | }) | |
1df8a4d7 C |
284 | |
285 | it('Should not list comments from muted accounts or instances', async function () { | |
696d83fd C |
286 | this.timeout(30000) |
287 | ||
288 | const remoteHandle = 'root@localhost:' + servers[0].port | |
289 | ||
89d241a7 | 290 | await servers[1].blocklist.addToServerBlocklist({ account: remoteHandle }) |
1df8a4d7 C |
291 | |
292 | { | |
89d241a7 | 293 | const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 2 } }) |
c1bc8ee4 | 294 | const jsonObj = JSON.parse(json) |
1df8a4d7 C |
295 | expect(jsonObj.items.length).to.be.equal(0) |
296 | } | |
297 | ||
89d241a7 | 298 | await servers[1].blocklist.removeFromServerBlocklist({ account: remoteHandle }) |
696d83fd C |
299 | |
300 | { | |
89d241a7 | 301 | const videoUUID = (await servers[1].videos.quickUpload({ name: 'server 2' })).uuid |
696d83fd | 302 | await waitJobs(servers) |
89d241a7 | 303 | await servers[0].comments.createThread({ videoId: videoUUID, text: 'super comment' }) |
696d83fd C |
304 | await waitJobs(servers) |
305 | ||
89d241a7 | 306 | const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 3 } }) |
c1bc8ee4 | 307 | const jsonObj = JSON.parse(json) |
696d83fd C |
308 | expect(jsonObj.items.length).to.be.equal(3) |
309 | } | |
310 | ||
89d241a7 | 311 | await servers[1].blocklist.addToMyBlocklist({ account: remoteHandle }) |
696d83fd C |
312 | |
313 | { | |
89d241a7 | 314 | const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 4 } }) |
c1bc8ee4 | 315 | const jsonObj = JSON.parse(json) |
696d83fd C |
316 | expect(jsonObj.items.length).to.be.equal(2) |
317 | } | |
1df8a4d7 | 318 | }) |
fe3a55b0 C |
319 | }) |
320 | ||
afff310e | 321 | describe('Video feed from my subscriptions', function () { |
18490b07 C |
322 | let feeduserAccountId: number |
323 | let feeduserFeedToken: string | |
afff310e RK |
324 | |
325 | it('Should list no videos for a user with no videos and no subscriptions', async function () { | |
afff310e | 326 | const attr = { username: 'feeduser', password: 'password' } |
89d241a7 C |
327 | await servers[0].users.create({ username: attr.username, password: attr.password }) |
328 | const feeduserAccessToken = await servers[0].login.getAccessToken(attr) | |
afff310e RK |
329 | |
330 | { | |
89d241a7 | 331 | const user = await servers[0].users.getMyInfo({ token: feeduserAccessToken }) |
afff310e RK |
332 | feeduserAccountId = user.account.id |
333 | } | |
334 | ||
335 | { | |
89d241a7 | 336 | const token = await servers[0].users.getMyScopedTokens({ token: feeduserAccessToken }) |
afff310e RK |
337 | feeduserFeedToken = token.feedToken |
338 | } | |
339 | ||
340 | { | |
89d241a7 | 341 | const body = await servers[0].subscriptions.listVideos({ token: feeduserAccessToken }) |
2c27e704 | 342 | expect(body.total).to.equal(0) |
afff310e | 343 | |
c1bc8ee4 | 344 | const query = { accountId: feeduserAccountId, token: feeduserFeedToken } |
89d241a7 | 345 | const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) |
c1bc8ee4 | 346 | const jsonObj = JSON.parse(json) |
afff310e RK |
347 | expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos |
348 | } | |
349 | }) | |
350 | ||
18490b07 | 351 | it('Should fail with an invalid token', async function () { |
c1bc8ee4 | 352 | const query = { accountId: feeduserAccountId, token: 'toto' } |
89d241a7 | 353 | await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
18490b07 C |
354 | }) |
355 | ||
356 | it('Should fail with a token of another user', async function () { | |
c1bc8ee4 | 357 | const query = { accountId: feeduserAccountId, token: userFeedToken } |
89d241a7 | 358 | await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
18490b07 C |
359 | }) |
360 | ||
afff310e | 361 | it('Should list no videos for a user with videos but no subscriptions', async function () { |
89d241a7 | 362 | const body = await servers[0].subscriptions.listVideos({ token: userAccessToken }) |
2c27e704 | 363 | expect(body.total).to.equal(0) |
afff310e | 364 | |
c1bc8ee4 | 365 | const query = { accountId: userAccountId, token: userFeedToken } |
89d241a7 | 366 | const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) |
c1bc8ee4 | 367 | const jsonObj = JSON.parse(json) |
18490b07 | 368 | expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos |
afff310e RK |
369 | }) |
370 | ||
371 | it('Should list self videos for a user with a subscription to themselves', async function () { | |
372 | this.timeout(30000) | |
373 | ||
89d241a7 | 374 | await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'john_channel@localhost:' + servers[0].port }) |
afff310e RK |
375 | await waitJobs(servers) |
376 | ||
377 | { | |
89d241a7 | 378 | const body = await servers[0].subscriptions.listVideos({ token: userAccessToken }) |
2c27e704 C |
379 | expect(body.total).to.equal(1) |
380 | expect(body.data[0].name).to.equal('user video') | |
afff310e | 381 | |
c1bc8ee4 | 382 | const query = { accountId: userAccountId, token: userFeedToken, version: 1 } |
89d241a7 | 383 | const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) |
c1bc8ee4 | 384 | const jsonObj = JSON.parse(json) |
afff310e RK |
385 | expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's |
386 | } | |
387 | }) | |
388 | ||
389 | it('Should list videos of a user\'s subscription', async function () { | |
390 | this.timeout(30000) | |
391 | ||
89d241a7 | 392 | await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[0].port }) |
afff310e RK |
393 | await waitJobs(servers) |
394 | ||
395 | { | |
89d241a7 | 396 | const body = await servers[0].subscriptions.listVideos({ token: userAccessToken }) |
2c27e704 | 397 | expect(body.total).to.equal(2, "there should be 2 videos part of the subscription") |
afff310e | 398 | |
c1bc8ee4 | 399 | const query = { accountId: userAccountId, token: userFeedToken, version: 2 } |
89d241a7 | 400 | const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) |
c1bc8ee4 | 401 | const jsonObj = JSON.parse(json) |
afff310e RK |
402 | expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's |
403 | } | |
404 | }) | |
405 | ||
18490b07 | 406 | it('Should renew the token, and so have an invalid old token', async function () { |
89d241a7 | 407 | await servers[0].users.renewMyScopedTokens({ token: userAccessToken }) |
18490b07 | 408 | |
c1bc8ee4 | 409 | const query = { accountId: userAccountId, token: userFeedToken, version: 3 } |
89d241a7 | 410 | await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) |
18490b07 C |
411 | }) |
412 | ||
413 | it('Should succeed with the new token', async function () { | |
89d241a7 | 414 | const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken }) |
18490b07 C |
415 | userFeedToken = token.feedToken |
416 | ||
c1bc8ee4 | 417 | const query = { accountId: userAccountId, token: userFeedToken, version: 4 } |
89d241a7 | 418 | await servers[0].feed.getJSON({ feed: 'subscriptions', query }) |
18490b07 C |
419 | }) |
420 | ||
afff310e RK |
421 | }) |
422 | ||
7c3b7976 | 423 | after(async function () { |
97816649 | 424 | await cleanupTests([ ...servers, serverHLSOnly ]) |
fe3a55b0 C |
425 | }) |
426 | }) |