diff options
Diffstat (limited to 'server/tests/api')
-rw-r--r-- | server/tests/api/check-params/live.ts | 7 | ||||
-rw-r--r-- | server/tests/api/live/index.ts | 2 | ||||
-rw-r--r-- | server/tests/api/live/live-constraints.ts | 199 | ||||
-rw-r--r-- | server/tests/api/live/live-save-replay.ts | 307 | ||||
-rw-r--r-- | server/tests/api/live/live.ts | 213 |
5 files changed, 552 insertions, 176 deletions
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts index 3e97dffdc..2b2d1beec 100644 --- a/server/tests/api/check-params/live.ts +++ b/server/tests/api/check-params/live.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | ||
5 | import { omit } from 'lodash' | 4 | import { omit } from 'lodash' |
6 | import { join } from 'path' | 5 | import { join } from 'path' |
7 | import { LiveVideo, VideoPrivacy } from '@shared/models' | 6 | import { LiveVideo, VideoPrivacy } from '@shared/models' |
@@ -14,11 +13,11 @@ import { | |||
14 | immutableAssign, | 13 | immutableAssign, |
15 | makePostBodyRequest, | 14 | makePostBodyRequest, |
16 | makeUploadRequest, | 15 | makeUploadRequest, |
16 | runAndTestFfmpegStreamError, | ||
17 | sendRTMPStream, | 17 | sendRTMPStream, |
18 | ServerInfo, | 18 | ServerInfo, |
19 | setAccessTokensToServers, | 19 | setAccessTokensToServers, |
20 | stopFfmpeg, | 20 | stopFfmpeg, |
21 | testFfmpegStreamError, | ||
22 | updateCustomSubConfig, | 21 | updateCustomSubConfig, |
23 | updateLive, | 22 | updateLive, |
24 | uploadVideoAndGetId, | 23 | uploadVideoAndGetId, |
@@ -30,9 +29,7 @@ describe('Test video lives API validator', function () { | |||
30 | const path = '/api/v1/videos/live' | 29 | const path = '/api/v1/videos/live' |
31 | let server: ServerInfo | 30 | let server: ServerInfo |
32 | let userAccessToken = '' | 31 | let userAccessToken = '' |
33 | let accountName: string | ||
34 | let channelId: number | 32 | let channelId: number |
35 | let channelName: string | ||
36 | let videoId: number | 33 | let videoId: number |
37 | let videoIdNotLive: number | 34 | let videoIdNotLive: number |
38 | 35 | ||
@@ -414,7 +411,7 @@ describe('Test video lives API validator', function () { | |||
414 | 411 | ||
415 | await waitUntilLiveStarts(server.url, server.accessToken, videoId) | 412 | await waitUntilLiveStarts(server.url, server.accessToken, videoId) |
416 | 413 | ||
417 | await testFfmpegStreamError(server.url, server.accessToken, videoId, true) | 414 | await runAndTestFfmpegStreamError(server.url, server.accessToken, videoId, true) |
418 | 415 | ||
419 | await stopFfmpeg(command) | 416 | await stopFfmpeg(command) |
420 | }) | 417 | }) |
diff --git a/server/tests/api/live/index.ts b/server/tests/api/live/index.ts index 280daf423..ee77af286 100644 --- a/server/tests/api/live/index.ts +++ b/server/tests/api/live/index.ts | |||
@@ -1 +1,3 @@ | |||
1 | export * from './live-constraints' | ||
2 | export * from './live-save-replay' | ||
1 | export * from './live' | 3 | export * from './live' |
diff --git a/server/tests/api/live/live-constraints.ts b/server/tests/api/live/live-constraints.ts new file mode 100644 index 000000000..23c8e3b0a --- /dev/null +++ b/server/tests/api/live/live-constraints.ts | |||
@@ -0,0 +1,199 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import * as chai from 'chai' | ||
5 | import { User, VideoDetails, VideoPrivacy } from '@shared/models' | ||
6 | import { | ||
7 | checkLiveCleanup, | ||
8 | cleanupTests, | ||
9 | createLive, | ||
10 | createUser, | ||
11 | doubleFollow, | ||
12 | flushAndRunMultipleServers, | ||
13 | getMyUserInformation, | ||
14 | getVideo, | ||
15 | runAndTestFfmpegStreamError, | ||
16 | ServerInfo, | ||
17 | setAccessTokensToServers, | ||
18 | setDefaultVideoChannel, | ||
19 | updateCustomSubConfig, | ||
20 | updateUser, | ||
21 | userLogin, | ||
22 | wait, | ||
23 | waitJobs | ||
24 | } from '../../../../shared/extra-utils' | ||
25 | |||
26 | const expect = chai.expect | ||
27 | |||
28 | describe('Test live constraints', function () { | ||
29 | let servers: ServerInfo[] = [] | ||
30 | let userId: number | ||
31 | let userAccessToken: string | ||
32 | let userChannelId: number | ||
33 | |||
34 | async function createLiveWrapper (saveReplay: boolean) { | ||
35 | const liveAttributes = { | ||
36 | name: 'user live', | ||
37 | channelId: userChannelId, | ||
38 | privacy: VideoPrivacy.PUBLIC, | ||
39 | saveReplay | ||
40 | } | ||
41 | |||
42 | const res = await createLive(servers[0].url, userAccessToken, liveAttributes) | ||
43 | return res.body.video.uuid as string | ||
44 | } | ||
45 | |||
46 | async function checkSaveReplay (videoId: string, resolutions = [ 720 ]) { | ||
47 | for (const server of servers) { | ||
48 | const res = await getVideo(server.url, videoId) | ||
49 | |||
50 | const video: VideoDetails = res.body | ||
51 | expect(video.isLive).to.be.false | ||
52 | expect(video.duration).to.be.greaterThan(0) | ||
53 | } | ||
54 | |||
55 | await checkLiveCleanup(servers[0], videoId, resolutions) | ||
56 | } | ||
57 | |||
58 | before(async function () { | ||
59 | this.timeout(120000) | ||
60 | |||
61 | servers = await flushAndRunMultipleServers(2) | ||
62 | |||
63 | // Get the access tokens | ||
64 | await setAccessTokensToServers(servers) | ||
65 | await setDefaultVideoChannel(servers) | ||
66 | |||
67 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | ||
68 | live: { | ||
69 | enabled: true, | ||
70 | allowReplay: true, | ||
71 | transcoding: { | ||
72 | enabled: false | ||
73 | } | ||
74 | } | ||
75 | }) | ||
76 | |||
77 | { | ||
78 | const user = { username: 'user1', password: 'superpassword' } | ||
79 | const res = await createUser({ | ||
80 | url: servers[0].url, | ||
81 | accessToken: servers[0].accessToken, | ||
82 | username: user.username, | ||
83 | password: user.password | ||
84 | }) | ||
85 | userId = res.body.user.id | ||
86 | |||
87 | userAccessToken = await userLogin(servers[0], user) | ||
88 | |||
89 | const resMe = await getMyUserInformation(servers[0].url, userAccessToken) | ||
90 | userChannelId = (resMe.body as User).videoChannels[0].id | ||
91 | |||
92 | await updateUser({ | ||
93 | url: servers[0].url, | ||
94 | userId, | ||
95 | accessToken: servers[0].accessToken, | ||
96 | videoQuota: 1, | ||
97 | videoQuotaDaily: -1 | ||
98 | }) | ||
99 | } | ||
100 | |||
101 | // Server 1 and server 2 follow each other | ||
102 | await doubleFollow(servers[0], servers[1]) | ||
103 | }) | ||
104 | |||
105 | it('Should not have size limit if save replay is disabled', async function () { | ||
106 | this.timeout(60000) | ||
107 | |||
108 | const userVideoLiveoId = await createLiveWrapper(false) | ||
109 | await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) | ||
110 | }) | ||
111 | |||
112 | it('Should have size limit depending on user global quota if save replay is enabled', async function () { | ||
113 | this.timeout(60000) | ||
114 | |||
115 | // Wait for user quota memoize cache invalidation | ||
116 | await wait(5000) | ||
117 | |||
118 | const userVideoLiveoId = await createLiveWrapper(true) | ||
119 | await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) | ||
120 | |||
121 | await waitJobs(servers) | ||
122 | |||
123 | await checkSaveReplay(userVideoLiveoId) | ||
124 | }) | ||
125 | |||
126 | it('Should have size limit depending on user daily quota if save replay is enabled', async function () { | ||
127 | this.timeout(60000) | ||
128 | |||
129 | // Wait for user quota memoize cache invalidation | ||
130 | await wait(5000) | ||
131 | |||
132 | await updateUser({ | ||
133 | url: servers[0].url, | ||
134 | userId, | ||
135 | accessToken: servers[0].accessToken, | ||
136 | videoQuota: -1, | ||
137 | videoQuotaDaily: 1 | ||
138 | }) | ||
139 | |||
140 | const userVideoLiveoId = await createLiveWrapper(true) | ||
141 | await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) | ||
142 | |||
143 | await waitJobs(servers) | ||
144 | |||
145 | await checkSaveReplay(userVideoLiveoId) | ||
146 | }) | ||
147 | |||
148 | it('Should succeed without quota limit', async function () { | ||
149 | this.timeout(60000) | ||
150 | |||
151 | // Wait for user quota memoize cache invalidation | ||
152 | await wait(5000) | ||
153 | |||
154 | await updateUser({ | ||
155 | url: servers[0].url, | ||
156 | userId, | ||
157 | accessToken: servers[0].accessToken, | ||
158 | videoQuota: 10 * 1000 * 1000, | ||
159 | videoQuotaDaily: -1 | ||
160 | }) | ||
161 | |||
162 | const userVideoLiveoId = await createLiveWrapper(true) | ||
163 | await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) | ||
164 | }) | ||
165 | |||
166 | it('Should have max duration limit', async function () { | ||
167 | this.timeout(30000) | ||
168 | |||
169 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | ||
170 | live: { | ||
171 | enabled: true, | ||
172 | allowReplay: true, | ||
173 | maxDuration: 1, | ||
174 | transcoding: { | ||
175 | enabled: true, | ||
176 | resolutions: { | ||
177 | '240p': true, | ||
178 | '360p': true, | ||
179 | '480p': true, | ||
180 | '720p': true, | ||
181 | '1080p': true, | ||
182 | '2160p': true | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | }) | ||
187 | |||
188 | const userVideoLiveoId = await createLiveWrapper(true) | ||
189 | await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) | ||
190 | |||
191 | await waitJobs(servers) | ||
192 | |||
193 | await checkSaveReplay(userVideoLiveoId, [ 720, 480, 360, 240 ]) | ||
194 | }) | ||
195 | |||
196 | after(async function () { | ||
197 | await cleanupTests(servers) | ||
198 | }) | ||
199 | }) | ||
diff --git a/server/tests/api/live/live-save-replay.ts b/server/tests/api/live/live-save-replay.ts new file mode 100644 index 000000000..3ffa0c093 --- /dev/null +++ b/server/tests/api/live/live-save-replay.ts | |||
@@ -0,0 +1,307 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import * as chai from 'chai' | ||
5 | import { FfmpegCommand } from 'fluent-ffmpeg' | ||
6 | import { LiveVideoCreate, VideoDetails, VideoPrivacy, VideoState } from '@shared/models' | ||
7 | import { | ||
8 | addVideoToBlacklist, | ||
9 | checkLiveCleanup, | ||
10 | cleanupTests, | ||
11 | createLive, | ||
12 | doubleFollow, | ||
13 | flushAndRunMultipleServers, | ||
14 | getVideo, | ||
15 | getVideosList, | ||
16 | removeVideo, | ||
17 | sendRTMPStreamInVideo, | ||
18 | ServerInfo, | ||
19 | setAccessTokensToServers, | ||
20 | setDefaultVideoChannel, | ||
21 | stopFfmpeg, | ||
22 | testFfmpegStreamError, | ||
23 | updateCustomSubConfig, | ||
24 | updateVideo, | ||
25 | waitJobs, | ||
26 | waitUntilLiveStarts | ||
27 | } from '../../../../shared/extra-utils' | ||
28 | |||
29 | const expect = chai.expect | ||
30 | |||
31 | describe('Save replay setting', function () { | ||
32 | let servers: ServerInfo[] = [] | ||
33 | let liveVideoUUID: string | ||
34 | let ffmpegCommand: FfmpegCommand | ||
35 | |||
36 | async function createLiveWrapper (saveReplay: boolean) { | ||
37 | if (liveVideoUUID) { | ||
38 | try { | ||
39 | await removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
40 | await waitJobs(servers) | ||
41 | } catch {} | ||
42 | } | ||
43 | |||
44 | const attributes: LiveVideoCreate = { | ||
45 | channelId: servers[0].videoChannel.id, | ||
46 | privacy: VideoPrivacy.PUBLIC, | ||
47 | name: 'my super live', | ||
48 | saveReplay | ||
49 | } | ||
50 | |||
51 | const res = await createLive(servers[0].url, servers[0].accessToken, attributes) | ||
52 | return res.body.video.uuid | ||
53 | } | ||
54 | |||
55 | async function checkVideosExist (videoId: string, existsInList: boolean, getStatus?: number) { | ||
56 | for (const server of servers) { | ||
57 | const length = existsInList ? 1 : 0 | ||
58 | |||
59 | const resVideos = await getVideosList(server.url) | ||
60 | expect(resVideos.body.data).to.have.lengthOf(length) | ||
61 | expect(resVideos.body.total).to.equal(length) | ||
62 | |||
63 | if (getStatus) { | ||
64 | await getVideo(server.url, videoId, getStatus) | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | async function checkVideoState (videoId: string, state: VideoState) { | ||
70 | for (const server of servers) { | ||
71 | const res = await getVideo(server.url, videoId) | ||
72 | expect((res.body as VideoDetails).state.id).to.equal(state) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | before(async function () { | ||
77 | this.timeout(120000) | ||
78 | |||
79 | servers = await flushAndRunMultipleServers(2) | ||
80 | |||
81 | // Get the access tokens | ||
82 | await setAccessTokensToServers(servers) | ||
83 | await setDefaultVideoChannel(servers) | ||
84 | |||
85 | // Server 1 and server 2 follow each other | ||
86 | await doubleFollow(servers[0], servers[1]) | ||
87 | |||
88 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | ||
89 | live: { | ||
90 | enabled: true, | ||
91 | allowReplay: true, | ||
92 | maxDuration: null, | ||
93 | transcoding: { | ||
94 | enabled: false, | ||
95 | resolutions: { | ||
96 | '240p': true, | ||
97 | '360p': true, | ||
98 | '480p': true, | ||
99 | '720p': true, | ||
100 | '1080p': true, | ||
101 | '2160p': true | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | }) | ||
106 | }) | ||
107 | |||
108 | describe('With save replay disabled', function () { | ||
109 | |||
110 | before(async function () { | ||
111 | this.timeout(10000) | ||
112 | }) | ||
113 | |||
114 | it('Should correctly create and federate the "waiting for stream" live', async function () { | ||
115 | this.timeout(20000) | ||
116 | |||
117 | liveVideoUUID = await createLiveWrapper(false) | ||
118 | |||
119 | await waitJobs(servers) | ||
120 | |||
121 | await checkVideosExist(liveVideoUUID, false, 200) | ||
122 | await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE) | ||
123 | }) | ||
124 | |||
125 | it('Should correctly have updated the live and federated it when streaming in the live', async function () { | ||
126 | this.timeout(20000) | ||
127 | |||
128 | ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
129 | await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
130 | |||
131 | await waitJobs(servers) | ||
132 | |||
133 | await checkVideosExist(liveVideoUUID, true, 200) | ||
134 | await checkVideoState(liveVideoUUID, VideoState.PUBLISHED) | ||
135 | }) | ||
136 | |||
137 | it('Should correctly delete the video files after the stream ended', async function () { | ||
138 | this.timeout(30000) | ||
139 | |||
140 | await stopFfmpeg(ffmpegCommand) | ||
141 | |||
142 | await waitJobs(servers) | ||
143 | |||
144 | // Live still exist, but cannot be played anymore | ||
145 | await checkVideosExist(liveVideoUUID, false, 200) | ||
146 | await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED) | ||
147 | |||
148 | // No resolutions saved since we did not save replay | ||
149 | await checkLiveCleanup(servers[0], liveVideoUUID, []) | ||
150 | }) | ||
151 | |||
152 | it('Should correctly terminate the stream on blacklist and delete the live', async function () { | ||
153 | this.timeout(40000) | ||
154 | |||
155 | liveVideoUUID = await createLiveWrapper(false) | ||
156 | |||
157 | ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
158 | await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
159 | |||
160 | await waitJobs(servers) | ||
161 | await checkVideosExist(liveVideoUUID, true, 200) | ||
162 | |||
163 | await Promise.all([ | ||
164 | addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), | ||
165 | testFfmpegStreamError(ffmpegCommand, true) | ||
166 | ]) | ||
167 | |||
168 | await waitJobs(servers) | ||
169 | |||
170 | await checkVideosExist(liveVideoUUID, false) | ||
171 | |||
172 | await getVideo(servers[0].url, liveVideoUUID, 401) | ||
173 | await getVideo(servers[1].url, liveVideoUUID, 404) | ||
174 | |||
175 | await checkLiveCleanup(servers[0], liveVideoUUID, []) | ||
176 | }) | ||
177 | |||
178 | it('Should correctly terminate the stream on delete and delete the video', async function () { | ||
179 | this.timeout(40000) | ||
180 | |||
181 | liveVideoUUID = await createLiveWrapper(false) | ||
182 | |||
183 | ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
184 | await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
185 | |||
186 | await waitJobs(servers) | ||
187 | await checkVideosExist(liveVideoUUID, true, 200) | ||
188 | |||
189 | await Promise.all([ | ||
190 | testFfmpegStreamError(ffmpegCommand, true), | ||
191 | removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
192 | ]) | ||
193 | |||
194 | await waitJobs(servers) | ||
195 | |||
196 | await checkVideosExist(liveVideoUUID, false, 404) | ||
197 | await checkLiveCleanup(servers[0], liveVideoUUID, []) | ||
198 | }) | ||
199 | }) | ||
200 | |||
201 | describe('With save replay enabled', function () { | ||
202 | |||
203 | it('Should correctly create and federate the "waiting for stream" live', async function () { | ||
204 | this.timeout(20000) | ||
205 | |||
206 | liveVideoUUID = await createLiveWrapper(true) | ||
207 | |||
208 | await waitJobs(servers) | ||
209 | |||
210 | await checkVideosExist(liveVideoUUID, false, 200) | ||
211 | await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE) | ||
212 | }) | ||
213 | |||
214 | it('Should correctly have updated the live and federated it when streaming in the live', async function () { | ||
215 | this.timeout(20000) | ||
216 | |||
217 | ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
218 | await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
219 | |||
220 | await waitJobs(servers) | ||
221 | |||
222 | await checkVideosExist(liveVideoUUID, true, 200) | ||
223 | await checkVideoState(liveVideoUUID, VideoState.PUBLISHED) | ||
224 | }) | ||
225 | |||
226 | it('Should correctly have saved the live and federated it after the streaming', async function () { | ||
227 | this.timeout(30000) | ||
228 | |||
229 | await stopFfmpeg(ffmpegCommand) | ||
230 | |||
231 | await waitJobs(servers) | ||
232 | |||
233 | // Live has been transcoded | ||
234 | await checkVideosExist(liveVideoUUID, true, 200) | ||
235 | await checkVideoState(liveVideoUUID, VideoState.PUBLISHED) | ||
236 | }) | ||
237 | |||
238 | it('Should update the saved live and correctly federate the updated attributes', async function () { | ||
239 | this.timeout(30000) | ||
240 | |||
241 | await updateVideo(servers[0].url, servers[0].accessToken, liveVideoUUID, { name: 'video updated' }) | ||
242 | await waitJobs(servers) | ||
243 | |||
244 | for (const server of servers) { | ||
245 | const res = await getVideo(server.url, liveVideoUUID) | ||
246 | expect(res.body.name).to.equal('video updated') | ||
247 | expect(res.body.isLive).to.be.false | ||
248 | } | ||
249 | }) | ||
250 | |||
251 | it('Should have cleaned up the live files', async function () { | ||
252 | await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ]) | ||
253 | }) | ||
254 | |||
255 | it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () { | ||
256 | this.timeout(40000) | ||
257 | |||
258 | liveVideoUUID = await createLiveWrapper(true) | ||
259 | |||
260 | ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
261 | await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
262 | |||
263 | await waitJobs(servers) | ||
264 | await checkVideosExist(liveVideoUUID, true, 200) | ||
265 | |||
266 | await Promise.all([ | ||
267 | addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), | ||
268 | testFfmpegStreamError(ffmpegCommand, true) | ||
269 | ]) | ||
270 | |||
271 | await waitJobs(servers) | ||
272 | |||
273 | await checkVideosExist(liveVideoUUID, false) | ||
274 | |||
275 | await getVideo(servers[0].url, liveVideoUUID, 401) | ||
276 | await getVideo(servers[1].url, liveVideoUUID, 404) | ||
277 | |||
278 | await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ]) | ||
279 | }) | ||
280 | |||
281 | it('Should correctly terminate the stream on delete and delete the video', async function () { | ||
282 | this.timeout(40000) | ||
283 | |||
284 | liveVideoUUID = await createLiveWrapper(true) | ||
285 | |||
286 | ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
287 | await waitUntilLiveStarts(servers[0].url, servers[0].accessToken, liveVideoUUID) | ||
288 | |||
289 | await waitJobs(servers) | ||
290 | await checkVideosExist(liveVideoUUID, true, 200) | ||
291 | |||
292 | await Promise.all([ | ||
293 | removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID), | ||
294 | testFfmpegStreamError(ffmpegCommand, true) | ||
295 | ]) | ||
296 | |||
297 | await waitJobs(servers) | ||
298 | |||
299 | await checkVideosExist(liveVideoUUID, false, 404) | ||
300 | await checkLiveCleanup(servers[0], liveVideoUUID, []) | ||
301 | }) | ||
302 | }) | ||
303 | |||
304 | after(async function () { | ||
305 | await cleanupTests(servers) | ||
306 | }) | ||
307 | }) | ||
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts index f351e9650..f7ccb453d 100644 --- a/server/tests/api/live/live.ts +++ b/server/tests/api/live/live.ts | |||
@@ -4,6 +4,7 @@ import 'mocha' | |||
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { LiveVideo, LiveVideoCreate, User, VideoDetails, VideoPrivacy } from '@shared/models' | 5 | import { LiveVideo, LiveVideoCreate, User, VideoDetails, VideoPrivacy } from '@shared/models' |
6 | import { | 6 | import { |
7 | addVideoToBlacklist, | ||
7 | cleanupTests, | 8 | cleanupTests, |
8 | createLive, | 9 | createLive, |
9 | createUser, | 10 | createUser, |
@@ -15,6 +16,7 @@ import { | |||
15 | getVideosList, | 16 | getVideosList, |
16 | makeRawRequest, | 17 | makeRawRequest, |
17 | removeVideo, | 18 | removeVideo, |
19 | sendRTMPStream, | ||
18 | ServerInfo, | 20 | ServerInfo, |
19 | setAccessTokensToServers, | 21 | setAccessTokensToServers, |
20 | setDefaultVideoChannel, | 22 | setDefaultVideoChannel, |
@@ -22,9 +24,7 @@ import { | |||
22 | testImage, | 24 | testImage, |
23 | updateCustomSubConfig, | 25 | updateCustomSubConfig, |
24 | updateLive, | 26 | updateLive, |
25 | updateUser, | ||
26 | userLogin, | 27 | userLogin, |
27 | wait, | ||
28 | waitJobs | 28 | waitJobs |
29 | } from '../../../../shared/extra-utils' | 29 | } from '../../../../shared/extra-utils' |
30 | 30 | ||
@@ -32,7 +32,6 @@ const expect = chai.expect | |||
32 | 32 | ||
33 | describe('Test live', function () { | 33 | describe('Test live', function () { |
34 | let servers: ServerInfo[] = [] | 34 | let servers: ServerInfo[] = [] |
35 | let liveVideoUUID: string | ||
36 | let userId: number | 35 | let userId: number |
37 | let userAccessToken: string | 36 | let userAccessToken: string |
38 | let userChannelId: number | 37 | let userChannelId: number |
@@ -49,7 +48,10 @@ describe('Test live', function () { | |||
49 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | 48 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { |
50 | live: { | 49 | live: { |
51 | enabled: true, | 50 | enabled: true, |
52 | allowReplay: true | 51 | allowReplay: true, |
52 | transcoding: { | ||
53 | enabled: false | ||
54 | } | ||
53 | } | 55 | } |
54 | }) | 56 | }) |
55 | 57 | ||
@@ -74,6 +76,7 @@ describe('Test live', function () { | |||
74 | }) | 76 | }) |
75 | 77 | ||
76 | describe('Live creation, update and delete', function () { | 78 | describe('Live creation, update and delete', function () { |
79 | let liveVideoUUID: string | ||
77 | 80 | ||
78 | it('Should create a live with the appropriate parameters', async function () { | 81 | it('Should create a live with the appropriate parameters', async function () { |
79 | this.timeout(20000) | 82 | this.timeout(20000) |
@@ -220,206 +223,74 @@ describe('Test live', function () { | |||
220 | }) | 223 | }) |
221 | }) | 224 | }) |
222 | 225 | ||
223 | describe('Test live constraints', function () { | 226 | describe('Stream checks', function () { |
227 | let liveVideo: LiveVideo & VideoDetails | ||
228 | let rtmpUrl: string | ||
229 | |||
230 | before(function () { | ||
231 | rtmpUrl = 'rtmp://' + servers[0].hostname + ':1936' | ||
232 | }) | ||
224 | 233 | ||
225 | async function createLiveWrapper (saveReplay: boolean) { | 234 | async function createLiveWrapper () { |
226 | const liveAttributes = { | 235 | const liveAttributes = { |
227 | name: 'user live', | 236 | name: 'user live', |
228 | channelId: userChannelId, | 237 | channelId: userChannelId, |
229 | privacy: VideoPrivacy.PUBLIC, | 238 | privacy: VideoPrivacy.PUBLIC, |
230 | saveReplay | 239 | saveReplay: false |
231 | } | 240 | } |
232 | 241 | ||
233 | const res = await createLive(servers[0].url, userAccessToken, liveAttributes) | 242 | const res = await createLive(servers[0].url, userAccessToken, liveAttributes) |
234 | return res.body.video.uuid as string | 243 | const uuid = res.body.video.uuid |
235 | } | ||
236 | |||
237 | before(async function () { | ||
238 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | ||
239 | live: { | ||
240 | enabled: true, | ||
241 | allowReplay: true | ||
242 | } | ||
243 | }) | ||
244 | |||
245 | await updateUser({ | ||
246 | url: servers[0].url, | ||
247 | userId, | ||
248 | accessToken: servers[0].accessToken, | ||
249 | videoQuota: 1, | ||
250 | videoQuotaDaily: -1 | ||
251 | }) | ||
252 | }) | ||
253 | 244 | ||
254 | it('Should not have size limit if save replay is disabled', async function () { | 245 | const resLive = await getLive(servers[0].url, servers[0].accessToken, uuid) |
255 | this.timeout(30000) | 246 | const resVideo = await getVideo(servers[0].url, uuid) |
256 | 247 | ||
257 | const userVideoLiveoId = await createLiveWrapper(false) | 248 | return Object.assign(resVideo.body, resLive.body) as LiveVideo & VideoDetails |
258 | await testFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) | 249 | } |
259 | }) | ||
260 | 250 | ||
261 | it('Should have size limit depending on user global quota if save replay is enabled', async function () { | 251 | it('Should not allow a stream without the appropriate path', async function () { |
262 | this.timeout(30000) | 252 | this.timeout(30000) |
263 | 253 | ||
264 | const userVideoLiveoId = await createLiveWrapper(true) | 254 | liveVideo = await createLiveWrapper() |
265 | await testFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) | ||
266 | |||
267 | await waitJobs(servers) | ||
268 | |||
269 | for (const server of servers) { | ||
270 | const res = await getVideo(server.url, userVideoLiveoId) | ||
271 | 255 | ||
272 | const video: VideoDetails = res.body | 256 | const command = sendRTMPStream(rtmpUrl + '/bad-live', liveVideo.streamKey) |
273 | expect(video.isLive).to.be.false | 257 | await testFfmpegStreamError(command, true) |
274 | expect(video.duration).to.be.greaterThan(0) | ||
275 | } | ||
276 | |||
277 | // TODO: check stream correctly saved + cleaned | ||
278 | }) | 258 | }) |
279 | 259 | ||
280 | it('Should have size limit depending on user daily quota if save replay is enabled', async function () { | 260 | it('Should not allow a stream without the appropriate stream key', async function () { |
281 | this.timeout(30000) | 261 | this.timeout(30000) |
282 | 262 | ||
283 | await updateUser({ | 263 | const command = sendRTMPStream(rtmpUrl + '/live', 'bad-stream-key') |
284 | url: servers[0].url, | 264 | await testFfmpegStreamError(command, true) |
285 | userId, | ||
286 | accessToken: servers[0].accessToken, | ||
287 | videoQuota: -1, | ||
288 | videoQuotaDaily: 1 | ||
289 | }) | ||
290 | |||
291 | const userVideoLiveoId = await createLiveWrapper(true) | ||
292 | await testFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) | ||
293 | |||
294 | // TODO: check stream correctly saved + cleaned | ||
295 | }) | 265 | }) |
296 | 266 | ||
297 | it('Should succeed without quota limit', async function () { | 267 | it('Should succeed with the correct params', async function () { |
298 | this.timeout(30000) | 268 | this.timeout(30000) |
299 | 269 | ||
300 | // Wait for user quota memoize cache invalidation | 270 | const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) |
301 | await wait(5000) | 271 | await testFfmpegStreamError(command, false) |
302 | |||
303 | await updateUser({ | ||
304 | url: servers[0].url, | ||
305 | userId, | ||
306 | accessToken: servers[0].accessToken, | ||
307 | videoQuota: 10 * 1000 * 1000, | ||
308 | videoQuotaDaily: -1 | ||
309 | }) | ||
310 | |||
311 | const userVideoLiveoId = await createLiveWrapper(true) | ||
312 | await testFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) | ||
313 | }) | 272 | }) |
314 | 273 | ||
315 | it('Should have max duration limit', async function () { | 274 | it('Should not allow a stream on a live that was blacklisted', async function () { |
316 | this.timeout(30000) | 275 | this.timeout(30000) |
317 | 276 | ||
318 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { | 277 | liveVideo = await createLiveWrapper() |
319 | live: { | ||
320 | enabled: true, | ||
321 | allowReplay: true, | ||
322 | maxDuration: 1 | ||
323 | } | ||
324 | }) | ||
325 | |||
326 | const userVideoLiveoId = await createLiveWrapper(true) | ||
327 | await testFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) | ||
328 | |||
329 | // TODO: check stream correctly saved + cleaned | ||
330 | }) | ||
331 | }) | ||
332 | |||
333 | describe('With save replay disabled', function () { | ||
334 | 278 | ||
335 | it('Should correctly create and federate the "waiting for stream" live', async function () { | 279 | await addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideo.uuid) |
336 | 280 | ||
281 | const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) | ||
282 | await testFfmpegStreamError(command, true) | ||
337 | }) | 283 | }) |
338 | 284 | ||
339 | it('Should correctly have updated the live and federated it when streaming in the live', async function () { | 285 | it('Should not allow a stream on a live that was deleted', async function () { |
340 | 286 | this.timeout(30000) | |
341 | }) | ||
342 | |||
343 | it('Should correctly delete the video and the live after the stream ended', async function () { | ||
344 | // Wait 10 seconds | ||
345 | // get video 404 | ||
346 | // get video federation 404 | ||
347 | |||
348 | // check cleanup | ||
349 | }) | ||
350 | |||
351 | it('Should correctly terminate the stream on blacklist and delete the live', async function () { | ||
352 | // Wait 10 seconds | ||
353 | // get video 404 | ||
354 | // get video federation 404 | ||
355 | |||
356 | // check cleanup | ||
357 | }) | ||
358 | |||
359 | it('Should correctly terminate the stream on delete and delete the video', async function () { | ||
360 | // Wait 10 seconds | ||
361 | // get video 404 | ||
362 | // get video federation 404 | ||
363 | |||
364 | // check cleanup | ||
365 | }) | ||
366 | }) | ||
367 | |||
368 | describe('With save replay enabled', function () { | ||
369 | |||
370 | it('Should correctly create and federate the "waiting for stream" live', async function () { | ||
371 | |||
372 | }) | ||
373 | |||
374 | it('Should correctly have updated the live and federated it when streaming in the live', async function () { | ||
375 | |||
376 | }) | ||
377 | |||
378 | it('Should correctly have saved the live and federated it after the streaming', async function () { | ||
379 | |||
380 | }) | ||
381 | |||
382 | it('Should update the saved live and correctly federate the updated attributes', async function () { | ||
383 | |||
384 | }) | ||
385 | |||
386 | it('Should have cleaned up the live files', async function () { | ||
387 | |||
388 | }) | ||
389 | |||
390 | it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () { | ||
391 | // Wait 10 seconds | ||
392 | // get video -> blacklisted | ||
393 | // get video federation -> blacklisted | ||
394 | |||
395 | // check cleanup live files quand meme | ||
396 | }) | ||
397 | |||
398 | it('Should correctly terminate the stream on delete and delete the video', async function () { | ||
399 | // Wait 10 seconds | ||
400 | // get video 404 | ||
401 | // get video federation 404 | ||
402 | |||
403 | // check cleanup | ||
404 | }) | ||
405 | }) | ||
406 | |||
407 | describe('Stream checks', function () { | ||
408 | |||
409 | it('Should not allow a stream without the appropriate path', async function () { | ||
410 | |||
411 | }) | ||
412 | |||
413 | it('Should not allow a stream without the appropriate stream key', async function () { | ||
414 | |||
415 | }) | ||
416 | |||
417 | it('Should not allow a stream on a live that was blacklisted', async function () { | ||
418 | 287 | ||
419 | }) | 288 | liveVideo = await createLiveWrapper() |
420 | 289 | ||
421 | it('Should not allow a stream on a live that was deleted', async function () { | 290 | await removeVideo(servers[0].url, servers[0].accessToken, liveVideo.uuid) |
422 | 291 | ||
292 | const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) | ||
293 | await testFfmpegStreamError(command, true) | ||
423 | }) | 294 | }) |
424 | }) | 295 | }) |
425 | 296 | ||