diff options
Diffstat (limited to 'server/tests/api/redundancy')
-rw-r--r-- | server/tests/api/redundancy/index.ts | 2 | ||||
-rw-r--r-- | server/tests/api/redundancy/manage-redundancy.ts | 373 | ||||
-rw-r--r-- | server/tests/api/redundancy/redundancy-constraints.ts | 200 | ||||
-rw-r--r-- | server/tests/api/redundancy/redundancy.ts | 182 |
4 files changed, 712 insertions, 45 deletions
diff --git a/server/tests/api/redundancy/index.ts b/server/tests/api/redundancy/index.ts index 8e69b95a6..37dc3f88c 100644 --- a/server/tests/api/redundancy/index.ts +++ b/server/tests/api/redundancy/index.ts | |||
@@ -1 +1,3 @@ | |||
1 | import './redundancy-constraints' | ||
1 | import './redundancy' | 2 | import './redundancy' |
3 | import './manage-redundancy' | ||
diff --git a/server/tests/api/redundancy/manage-redundancy.ts b/server/tests/api/redundancy/manage-redundancy.ts new file mode 100644 index 000000000..4253124c8 --- /dev/null +++ b/server/tests/api/redundancy/manage-redundancy.ts | |||
@@ -0,0 +1,373 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | doubleFollow, | ||
8 | flushAndRunMultipleServers, | ||
9 | getLocalIdByUUID, | ||
10 | ServerInfo, | ||
11 | setAccessTokensToServers, | ||
12 | uploadVideo, | ||
13 | uploadVideoAndGetId, | ||
14 | waitUntilLog | ||
15 | } from '../../../../shared/extra-utils' | ||
16 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
17 | import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy, updateRedundancy } from '@shared/extra-utils/server/redundancy' | ||
18 | import { VideoPrivacy, VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' | ||
19 | |||
20 | const expect = chai.expect | ||
21 | |||
22 | describe('Test manage videos redundancy', function () { | ||
23 | const targets: VideoRedundanciesTarget[] = [ 'my-videos', 'remote-videos' ] | ||
24 | |||
25 | let servers: ServerInfo[] | ||
26 | let video1Server2UUID: string | ||
27 | let video2Server2UUID: string | ||
28 | let redundanciesToRemove: number[] = [] | ||
29 | |||
30 | before(async function () { | ||
31 | this.timeout(120000) | ||
32 | |||
33 | const config = { | ||
34 | transcoding: { | ||
35 | hls: { | ||
36 | enabled: true | ||
37 | } | ||
38 | }, | ||
39 | redundancy: { | ||
40 | videos: { | ||
41 | check_interval: '1 second', | ||
42 | strategies: [ | ||
43 | { | ||
44 | strategy: 'recently-added', | ||
45 | min_lifetime: '1 hour', | ||
46 | size: '10MB', | ||
47 | min_views: 0 | ||
48 | } | ||
49 | ] | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | servers = await flushAndRunMultipleServers(3, config) | ||
54 | |||
55 | // Get the access tokens | ||
56 | await setAccessTokensToServers(servers) | ||
57 | |||
58 | { | ||
59 | const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) | ||
60 | video1Server2UUID = res.body.video.uuid | ||
61 | } | ||
62 | |||
63 | { | ||
64 | const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 2 server 2' }) | ||
65 | video2Server2UUID = res.body.video.uuid | ||
66 | } | ||
67 | |||
68 | await waitJobs(servers) | ||
69 | |||
70 | // Server 1 and server 2 follow each other | ||
71 | await doubleFollow(servers[0], servers[1]) | ||
72 | await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, true) | ||
73 | |||
74 | await waitJobs(servers) | ||
75 | }) | ||
76 | |||
77 | it('Should not have redundancies on server 3', async function () { | ||
78 | for (const target of targets) { | ||
79 | const res = await listVideoRedundancies({ | ||
80 | url: servers[2].url, | ||
81 | accessToken: servers[2].accessToken, | ||
82 | target | ||
83 | }) | ||
84 | |||
85 | expect(res.body.total).to.equal(0) | ||
86 | expect(res.body.data).to.have.lengthOf(0) | ||
87 | } | ||
88 | }) | ||
89 | |||
90 | it('Should not have "remote-videos" redundancies on server 2', async function () { | ||
91 | this.timeout(120000) | ||
92 | |||
93 | await waitJobs(servers) | ||
94 | await waitUntilLog(servers[0], 'Duplicated ', 10) | ||
95 | await waitJobs(servers) | ||
96 | |||
97 | const res = await listVideoRedundancies({ | ||
98 | url: servers[1].url, | ||
99 | accessToken: servers[1].accessToken, | ||
100 | target: 'remote-videos' | ||
101 | }) | ||
102 | |||
103 | expect(res.body.total).to.equal(0) | ||
104 | expect(res.body.data).to.have.lengthOf(0) | ||
105 | }) | ||
106 | |||
107 | it('Should have "my-videos" redundancies on server 2', async function () { | ||
108 | this.timeout(120000) | ||
109 | |||
110 | const res = await listVideoRedundancies({ | ||
111 | url: servers[1].url, | ||
112 | accessToken: servers[1].accessToken, | ||
113 | target: 'my-videos' | ||
114 | }) | ||
115 | |||
116 | expect(res.body.total).to.equal(2) | ||
117 | |||
118 | const videos = res.body.data as VideoRedundancy[] | ||
119 | expect(videos).to.have.lengthOf(2) | ||
120 | |||
121 | const videos1 = videos.find(v => v.uuid === video1Server2UUID) | ||
122 | const videos2 = videos.find(v => v.uuid === video2Server2UUID) | ||
123 | |||
124 | expect(videos1.name).to.equal('video 1 server 2') | ||
125 | expect(videos2.name).to.equal('video 2 server 2') | ||
126 | |||
127 | expect(videos1.redundancies.files).to.have.lengthOf(4) | ||
128 | expect(videos1.redundancies.streamingPlaylists).to.have.lengthOf(1) | ||
129 | |||
130 | const redundancies = videos1.redundancies.files.concat(videos1.redundancies.streamingPlaylists) | ||
131 | |||
132 | for (const r of redundancies) { | ||
133 | expect(r.strategy).to.be.null | ||
134 | expect(r.fileUrl).to.exist | ||
135 | expect(r.createdAt).to.exist | ||
136 | expect(r.updatedAt).to.exist | ||
137 | expect(r.expiresOn).to.exist | ||
138 | } | ||
139 | }) | ||
140 | |||
141 | it('Should not have "my-videos" redundancies on server 1', async function () { | ||
142 | const res = await listVideoRedundancies({ | ||
143 | url: servers[0].url, | ||
144 | accessToken: servers[0].accessToken, | ||
145 | target: 'my-videos' | ||
146 | }) | ||
147 | |||
148 | expect(res.body.total).to.equal(0) | ||
149 | expect(res.body.data).to.have.lengthOf(0) | ||
150 | }) | ||
151 | |||
152 | it('Should have "remote-videos" redundancies on server 1', async function () { | ||
153 | this.timeout(120000) | ||
154 | |||
155 | const res = await listVideoRedundancies({ | ||
156 | url: servers[0].url, | ||
157 | accessToken: servers[0].accessToken, | ||
158 | target: 'remote-videos' | ||
159 | }) | ||
160 | |||
161 | expect(res.body.total).to.equal(2) | ||
162 | |||
163 | const videos = res.body.data as VideoRedundancy[] | ||
164 | expect(videos).to.have.lengthOf(2) | ||
165 | |||
166 | const videos1 = videos.find(v => v.uuid === video1Server2UUID) | ||
167 | const videos2 = videos.find(v => v.uuid === video2Server2UUID) | ||
168 | |||
169 | expect(videos1.name).to.equal('video 1 server 2') | ||
170 | expect(videos2.name).to.equal('video 2 server 2') | ||
171 | |||
172 | expect(videos1.redundancies.files).to.have.lengthOf(4) | ||
173 | expect(videos1.redundancies.streamingPlaylists).to.have.lengthOf(1) | ||
174 | |||
175 | const redundancies = videos1.redundancies.files.concat(videos1.redundancies.streamingPlaylists) | ||
176 | |||
177 | for (const r of redundancies) { | ||
178 | expect(r.strategy).to.equal('recently-added') | ||
179 | expect(r.fileUrl).to.exist | ||
180 | expect(r.createdAt).to.exist | ||
181 | expect(r.updatedAt).to.exist | ||
182 | expect(r.expiresOn).to.exist | ||
183 | } | ||
184 | }) | ||
185 | |||
186 | it('Should correctly paginate and sort results', async function () { | ||
187 | { | ||
188 | const res = await listVideoRedundancies({ | ||
189 | url: servers[0].url, | ||
190 | accessToken: servers[0].accessToken, | ||
191 | target: 'remote-videos', | ||
192 | sort: 'name', | ||
193 | start: 0, | ||
194 | count: 2 | ||
195 | }) | ||
196 | |||
197 | const videos = res.body.data | ||
198 | expect(videos[0].name).to.equal('video 1 server 2') | ||
199 | expect(videos[1].name).to.equal('video 2 server 2') | ||
200 | } | ||
201 | |||
202 | { | ||
203 | const res = await listVideoRedundancies({ | ||
204 | url: servers[0].url, | ||
205 | accessToken: servers[0].accessToken, | ||
206 | target: 'remote-videos', | ||
207 | sort: '-name', | ||
208 | start: 0, | ||
209 | count: 2 | ||
210 | }) | ||
211 | |||
212 | const videos = res.body.data | ||
213 | expect(videos[0].name).to.equal('video 2 server 2') | ||
214 | expect(videos[1].name).to.equal('video 1 server 2') | ||
215 | } | ||
216 | |||
217 | { | ||
218 | const res = await listVideoRedundancies({ | ||
219 | url: servers[0].url, | ||
220 | accessToken: servers[0].accessToken, | ||
221 | target: 'remote-videos', | ||
222 | sort: '-name', | ||
223 | start: 1, | ||
224 | count: 1 | ||
225 | }) | ||
226 | |||
227 | const videos = res.body.data | ||
228 | expect(videos[0].name).to.equal('video 1 server 2') | ||
229 | } | ||
230 | }) | ||
231 | |||
232 | it('Should manually add a redundancy and list it', async function () { | ||
233 | this.timeout(120000) | ||
234 | |||
235 | const uuid = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3 server 2', privacy: VideoPrivacy.UNLISTED })).uuid | ||
236 | await waitJobs(servers) | ||
237 | const videoId = await getLocalIdByUUID(servers[0].url, uuid) | ||
238 | |||
239 | await addVideoRedundancy({ | ||
240 | url: servers[0].url, | ||
241 | accessToken: servers[0].accessToken, | ||
242 | videoId | ||
243 | }) | ||
244 | |||
245 | await waitJobs(servers) | ||
246 | await waitUntilLog(servers[0], 'Duplicated ', 15) | ||
247 | await waitJobs(servers) | ||
248 | |||
249 | { | ||
250 | const res = await listVideoRedundancies({ | ||
251 | url: servers[0].url, | ||
252 | accessToken: servers[0].accessToken, | ||
253 | target: 'remote-videos', | ||
254 | sort: '-name', | ||
255 | start: 0, | ||
256 | count: 5 | ||
257 | }) | ||
258 | |||
259 | const videos = res.body.data | ||
260 | expect(videos[0].name).to.equal('video 3 server 2') | ||
261 | |||
262 | const video = videos[0] | ||
263 | expect(video.redundancies.files).to.have.lengthOf(4) | ||
264 | expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) | ||
265 | |||
266 | const redundancies = video.redundancies.files.concat(video.redundancies.streamingPlaylists) | ||
267 | |||
268 | for (const r of redundancies) { | ||
269 | redundanciesToRemove.push(r.id) | ||
270 | |||
271 | expect(r.strategy).to.equal('manual') | ||
272 | expect(r.fileUrl).to.exist | ||
273 | expect(r.createdAt).to.exist | ||
274 | expect(r.updatedAt).to.exist | ||
275 | expect(r.expiresOn).to.be.null | ||
276 | } | ||
277 | } | ||
278 | |||
279 | const res = await listVideoRedundancies({ | ||
280 | url: servers[1].url, | ||
281 | accessToken: servers[1].accessToken, | ||
282 | target: 'my-videos', | ||
283 | sort: '-name', | ||
284 | start: 0, | ||
285 | count: 5 | ||
286 | }) | ||
287 | |||
288 | const videos = res.body.data | ||
289 | expect(videos[0].name).to.equal('video 3 server 2') | ||
290 | |||
291 | const video = videos[0] | ||
292 | expect(video.redundancies.files).to.have.lengthOf(4) | ||
293 | expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) | ||
294 | |||
295 | const redundancies = video.redundancies.files.concat(video.redundancies.streamingPlaylists) | ||
296 | |||
297 | for (const r of redundancies) { | ||
298 | expect(r.strategy).to.be.null | ||
299 | expect(r.fileUrl).to.exist | ||
300 | expect(r.createdAt).to.exist | ||
301 | expect(r.updatedAt).to.exist | ||
302 | expect(r.expiresOn).to.be.null | ||
303 | } | ||
304 | }) | ||
305 | |||
306 | it('Should manually remove a redundancy and remove it from the list', async function () { | ||
307 | this.timeout(120000) | ||
308 | |||
309 | for (const redundancyId of redundanciesToRemove) { | ||
310 | await removeVideoRedundancy({ | ||
311 | url: servers[0].url, | ||
312 | accessToken: servers[0].accessToken, | ||
313 | redundancyId | ||
314 | }) | ||
315 | } | ||
316 | |||
317 | { | ||
318 | const res = await listVideoRedundancies({ | ||
319 | url: servers[0].url, | ||
320 | accessToken: servers[0].accessToken, | ||
321 | target: 'remote-videos', | ||
322 | sort: '-name', | ||
323 | start: 0, | ||
324 | count: 5 | ||
325 | }) | ||
326 | |||
327 | const videos = res.body.data | ||
328 | expect(videos).to.have.lengthOf(2) | ||
329 | |||
330 | expect(videos[0].name).to.equal('video 2 server 2') | ||
331 | |||
332 | redundanciesToRemove = [] | ||
333 | const video = videos[0] | ||
334 | expect(video.redundancies.files).to.have.lengthOf(4) | ||
335 | expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) | ||
336 | |||
337 | const redundancies = video.redundancies.files.concat(video.redundancies.streamingPlaylists) | ||
338 | |||
339 | for (const r of redundancies) { | ||
340 | redundanciesToRemove.push(r.id) | ||
341 | } | ||
342 | } | ||
343 | }) | ||
344 | |||
345 | it('Should remove another (auto) redundancy', async function () { | ||
346 | { | ||
347 | for (const redundancyId of redundanciesToRemove) { | ||
348 | await removeVideoRedundancy({ | ||
349 | url: servers[0].url, | ||
350 | accessToken: servers[0].accessToken, | ||
351 | redundancyId | ||
352 | }) | ||
353 | } | ||
354 | |||
355 | const res = await listVideoRedundancies({ | ||
356 | url: servers[0].url, | ||
357 | accessToken: servers[0].accessToken, | ||
358 | target: 'remote-videos', | ||
359 | sort: '-name', | ||
360 | start: 0, | ||
361 | count: 5 | ||
362 | }) | ||
363 | |||
364 | const videos = res.body.data | ||
365 | expect(videos[0].name).to.equal('video 1 server 2') | ||
366 | expect(videos).to.have.lengthOf(1) | ||
367 | } | ||
368 | }) | ||
369 | |||
370 | after(async function () { | ||
371 | await cleanupTests(servers) | ||
372 | }) | ||
373 | }) | ||
diff --git a/server/tests/api/redundancy/redundancy-constraints.ts b/server/tests/api/redundancy/redundancy-constraints.ts new file mode 100644 index 000000000..4fd8f065c --- /dev/null +++ b/server/tests/api/redundancy/redundancy-constraints.ts | |||
@@ -0,0 +1,200 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | flushAndRunServer, | ||
8 | follow, | ||
9 | killallServers, | ||
10 | reRunServer, | ||
11 | ServerInfo, | ||
12 | setAccessTokensToServers, | ||
13 | uploadVideo, | ||
14 | waitUntilLog | ||
15 | } from '../../../../shared/extra-utils' | ||
16 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
17 | import { listVideoRedundancies, updateRedundancy } from '@shared/extra-utils/server/redundancy' | ||
18 | |||
19 | const expect = chai.expect | ||
20 | |||
21 | describe('Test redundancy constraints', function () { | ||
22 | let remoteServer: ServerInfo | ||
23 | let localServer: ServerInfo | ||
24 | let servers: ServerInfo[] | ||
25 | |||
26 | async function getTotalRedundanciesLocalServer () { | ||
27 | const res = await listVideoRedundancies({ | ||
28 | url: localServer.url, | ||
29 | accessToken: localServer.accessToken, | ||
30 | target: 'my-videos' | ||
31 | }) | ||
32 | |||
33 | return res.body.total | ||
34 | } | ||
35 | |||
36 | async function getTotalRedundanciesRemoteServer () { | ||
37 | const res = await listVideoRedundancies({ | ||
38 | url: remoteServer.url, | ||
39 | accessToken: remoteServer.accessToken, | ||
40 | target: 'remote-videos' | ||
41 | }) | ||
42 | |||
43 | return res.body.total | ||
44 | } | ||
45 | |||
46 | before(async function () { | ||
47 | this.timeout(120000) | ||
48 | |||
49 | { | ||
50 | const config = { | ||
51 | redundancy: { | ||
52 | videos: { | ||
53 | check_interval: '1 second', | ||
54 | strategies: [ | ||
55 | { | ||
56 | strategy: 'recently-added', | ||
57 | min_lifetime: '1 hour', | ||
58 | size: '100MB', | ||
59 | min_views: 0 | ||
60 | } | ||
61 | ] | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | remoteServer = await flushAndRunServer(1, config) | ||
66 | } | ||
67 | |||
68 | { | ||
69 | const config = { | ||
70 | remote_redundancy: { | ||
71 | videos: { | ||
72 | accept_from: 'nobody' | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | localServer = await flushAndRunServer(2, config) | ||
77 | } | ||
78 | |||
79 | servers = [ remoteServer, localServer ] | ||
80 | |||
81 | // Get the access tokens | ||
82 | await setAccessTokensToServers(servers) | ||
83 | |||
84 | await uploadVideo(localServer.url, localServer.accessToken, { name: 'video 1 server 2' }) | ||
85 | |||
86 | await waitJobs(servers) | ||
87 | |||
88 | // Server 1 and server 2 follow each other | ||
89 | await follow(remoteServer.url, [ localServer.url ], remoteServer.accessToken) | ||
90 | await waitJobs(servers) | ||
91 | await updateRedundancy(remoteServer.url, remoteServer.accessToken, localServer.host, true) | ||
92 | |||
93 | await waitJobs(servers) | ||
94 | }) | ||
95 | |||
96 | it('Should have redundancy on server 1 but not on server 2 with a nobody filter', async function () { | ||
97 | this.timeout(120000) | ||
98 | |||
99 | await waitJobs(servers) | ||
100 | await waitUntilLog(remoteServer, 'Duplicated ', 5) | ||
101 | await waitJobs(servers) | ||
102 | |||
103 | { | ||
104 | const total = await getTotalRedundanciesRemoteServer() | ||
105 | expect(total).to.equal(1) | ||
106 | } | ||
107 | |||
108 | { | ||
109 | const total = await getTotalRedundanciesLocalServer() | ||
110 | expect(total).to.equal(0) | ||
111 | } | ||
112 | }) | ||
113 | |||
114 | it('Should have redundancy on server 1 and on server 2 with an anybody filter', async function () { | ||
115 | this.timeout(120000) | ||
116 | |||
117 | const config = { | ||
118 | remote_redundancy: { | ||
119 | videos: { | ||
120 | accept_from: 'anybody' | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | await killallServers([ localServer ]) | ||
125 | await reRunServer(localServer, config) | ||
126 | |||
127 | await uploadVideo(localServer.url, localServer.accessToken, { name: 'video 2 server 2' }) | ||
128 | |||
129 | await waitJobs(servers) | ||
130 | await waitUntilLog(remoteServer, 'Duplicated ', 10) | ||
131 | await waitJobs(servers) | ||
132 | |||
133 | { | ||
134 | const total = await getTotalRedundanciesRemoteServer() | ||
135 | expect(total).to.equal(2) | ||
136 | } | ||
137 | |||
138 | { | ||
139 | const total = await getTotalRedundanciesLocalServer() | ||
140 | expect(total).to.equal(1) | ||
141 | } | ||
142 | }) | ||
143 | |||
144 | it('Should have redundancy on server 1 but not on server 2 with a followings filter', async function () { | ||
145 | this.timeout(120000) | ||
146 | |||
147 | const config = { | ||
148 | remote_redundancy: { | ||
149 | videos: { | ||
150 | accept_from: 'followings' | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | await killallServers([ localServer ]) | ||
155 | await reRunServer(localServer, config) | ||
156 | |||
157 | await uploadVideo(localServer.url, localServer.accessToken, { name: 'video 3 server 2' }) | ||
158 | |||
159 | await waitJobs(servers) | ||
160 | await waitUntilLog(remoteServer, 'Duplicated ', 15) | ||
161 | await waitJobs(servers) | ||
162 | |||
163 | { | ||
164 | const total = await getTotalRedundanciesRemoteServer() | ||
165 | expect(total).to.equal(3) | ||
166 | } | ||
167 | |||
168 | { | ||
169 | const total = await getTotalRedundanciesLocalServer() | ||
170 | expect(total).to.equal(1) | ||
171 | } | ||
172 | }) | ||
173 | |||
174 | it('Should have redundancy on server 1 and on server 2 with followings filter now server 2 follows server 1', async function () { | ||
175 | this.timeout(120000) | ||
176 | |||
177 | await follow(localServer.url, [ remoteServer.url ], localServer.accessToken) | ||
178 | await waitJobs(servers) | ||
179 | |||
180 | await uploadVideo(localServer.url, localServer.accessToken, { name: 'video 4 server 2' }) | ||
181 | |||
182 | await waitJobs(servers) | ||
183 | await waitUntilLog(remoteServer, 'Duplicated ', 20) | ||
184 | await waitJobs(servers) | ||
185 | |||
186 | { | ||
187 | const total = await getTotalRedundanciesRemoteServer() | ||
188 | expect(total).to.equal(4) | ||
189 | } | ||
190 | |||
191 | { | ||
192 | const total = await getTotalRedundanciesLocalServer() | ||
193 | expect(total).to.equal(2) | ||
194 | } | ||
195 | }) | ||
196 | |||
197 | after(async function () { | ||
198 | await cleanupTests(servers) | ||
199 | }) | ||
200 | }) | ||
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts index 1cdf93aa1..c5037a541 100644 --- a/server/tests/api/redundancy/redundancy.ts +++ b/server/tests/api/redundancy/redundancy.ts | |||
@@ -1,11 +1,12 @@ | |||
1 | /* tslint:disable:no-unused-expression */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { VideoDetails } from '../../../../shared/models/videos' | 5 | import { VideoDetails } from '../../../../shared/models/videos' |
6 | import { | 6 | import { |
7 | checkSegmentHash, | 7 | checkSegmentHash, |
8 | checkVideoFilesWereRemoved, cleanupTests, | 8 | checkVideoFilesWereRemoved, |
9 | cleanupTests, | ||
9 | doubleFollow, | 10 | doubleFollow, |
10 | flushAndRunMultipleServers, | 11 | flushAndRunMultipleServers, |
11 | getFollowingListPaginationAndSort, | 12 | getFollowingListPaginationAndSort, |
@@ -28,11 +29,16 @@ import { | |||
28 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 29 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
29 | 30 | ||
30 | import * as magnetUtil from 'magnet-uri' | 31 | import * as magnetUtil from 'magnet-uri' |
31 | import { updateRedundancy } from '../../../../shared/extra-utils/server/redundancy' | 32 | import { |
33 | addVideoRedundancy, | ||
34 | listVideoRedundancies, | ||
35 | removeVideoRedundancy, | ||
36 | updateRedundancy | ||
37 | } from '../../../../shared/extra-utils/server/redundancy' | ||
32 | import { ActorFollow } from '../../../../shared/models/actors' | 38 | import { ActorFollow } from '../../../../shared/models/actors' |
33 | import { readdir } from 'fs-extra' | 39 | import { readdir } from 'fs-extra' |
34 | import { join } from 'path' | 40 | import { join } from 'path' |
35 | import { VideoRedundancyStrategy } from '../../../../shared/models/redundancy' | 41 | import { VideoRedundancy, VideoRedundancyStrategy, VideoRedundancyStrategyWithManual } from '../../../../shared/models/redundancy' |
36 | import { getStats } from '../../../../shared/extra-utils/server/stats' | 42 | import { getStats } from '../../../../shared/extra-utils/server/stats' |
37 | import { ServerStats } from '../../../../shared/models/server/server-stats.model' | 43 | import { ServerStats } from '../../../../shared/models/server/server-stats.model' |
38 | 44 | ||
@@ -40,6 +46,7 @@ const expect = chai.expect | |||
40 | 46 | ||
41 | let servers: ServerInfo[] = [] | 47 | let servers: ServerInfo[] = [] |
42 | let video1Server2UUID: string | 48 | let video1Server2UUID: string |
49 | let video1Server2Id: number | ||
43 | 50 | ||
44 | function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[], server: ServerInfo) { | 51 | function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[], server: ServerInfo) { |
45 | const parsed = magnetUtil.decode(file.magnetUri) | 52 | const parsed = magnetUtil.decode(file.magnetUri) |
@@ -52,7 +59,19 @@ function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: numbe | |||
52 | expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length) | 59 | expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length) |
53 | } | 60 | } |
54 | 61 | ||
55 | async function flushAndRunServers (strategy: VideoRedundancyStrategy, additionalParams: any = {}) { | 62 | async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}) { |
63 | const strategies: any[] = [] | ||
64 | |||
65 | if (strategy !== null) { | ||
66 | strategies.push( | ||
67 | immutableAssign({ | ||
68 | min_lifetime: '1 hour', | ||
69 | strategy: strategy, | ||
70 | size: '400KB' | ||
71 | }, additionalParams) | ||
72 | ) | ||
73 | } | ||
74 | |||
56 | const config = { | 75 | const config = { |
57 | transcoding: { | 76 | transcoding: { |
58 | hls: { | 77 | hls: { |
@@ -62,36 +81,32 @@ async function flushAndRunServers (strategy: VideoRedundancyStrategy, additional | |||
62 | redundancy: { | 81 | redundancy: { |
63 | videos: { | 82 | videos: { |
64 | check_interval: '5 seconds', | 83 | check_interval: '5 seconds', |
65 | strategies: [ | 84 | strategies |
66 | immutableAssign({ | ||
67 | min_lifetime: '1 hour', | ||
68 | strategy: strategy, | ||
69 | size: '400KB' | ||
70 | }, additionalParams) | ||
71 | ] | ||
72 | } | 85 | } |
73 | } | 86 | } |
74 | } | 87 | } |
88 | |||
75 | servers = await flushAndRunMultipleServers(3, config) | 89 | servers = await flushAndRunMultipleServers(3, config) |
76 | 90 | ||
77 | // Get the access tokens | 91 | // Get the access tokens |
78 | await setAccessTokensToServers(servers) | 92 | await setAccessTokensToServers(servers) |
79 | 93 | ||
80 | { | 94 | { |
81 | const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, { name: 'video 1 server 2' }) | 95 | const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) |
82 | video1Server2UUID = res.body.video.uuid | 96 | video1Server2UUID = res.body.video.uuid |
97 | video1Server2Id = res.body.video.id | ||
83 | 98 | ||
84 | await viewVideo(servers[ 1 ].url, video1Server2UUID) | 99 | await viewVideo(servers[1].url, video1Server2UUID) |
85 | } | 100 | } |
86 | 101 | ||
87 | await waitJobs(servers) | 102 | await waitJobs(servers) |
88 | 103 | ||
89 | // Server 1 and server 2 follow each other | 104 | // Server 1 and server 2 follow each other |
90 | await doubleFollow(servers[ 0 ], servers[ 1 ]) | 105 | await doubleFollow(servers[0], servers[1]) |
91 | // Server 1 and server 3 follow each other | 106 | // Server 1 and server 3 follow each other |
92 | await doubleFollow(servers[ 0 ], servers[ 2 ]) | 107 | await doubleFollow(servers[0], servers[2]) |
93 | // Server 2 and server 3 follow each other | 108 | // Server 2 and server 3 follow each other |
94 | await doubleFollow(servers[ 1 ], servers[ 2 ]) | 109 | await doubleFollow(servers[1], servers[2]) |
95 | 110 | ||
96 | await waitJobs(servers) | 111 | await waitJobs(servers) |
97 | } | 112 | } |
@@ -100,7 +115,7 @@ async function check1WebSeed (videoUUID?: string) { | |||
100 | if (!videoUUID) videoUUID = video1Server2UUID | 115 | if (!videoUUID) videoUUID = video1Server2UUID |
101 | 116 | ||
102 | const webseeds = [ | 117 | const webseeds = [ |
103 | `http://localhost:${servers[ 1 ].port}/static/webseed/${videoUUID}` | 118 | `http://localhost:${servers[1].port}/static/webseed/${videoUUID}` |
104 | ] | 119 | ] |
105 | 120 | ||
106 | for (const server of servers) { | 121 | for (const server of servers) { |
@@ -118,8 +133,8 @@ async function check2Webseeds (videoUUID?: string) { | |||
118 | if (!videoUUID) videoUUID = video1Server2UUID | 133 | if (!videoUUID) videoUUID = video1Server2UUID |
119 | 134 | ||
120 | const webseeds = [ | 135 | const webseeds = [ |
121 | `http://localhost:${servers[ 0 ].port}/static/redundancy/${videoUUID}`, | 136 | `http://localhost:${servers[0].port}/static/redundancy/${videoUUID}`, |
122 | `http://localhost:${servers[ 1 ].port}/static/webseed/${videoUUID}` | 137 | `http://localhost:${servers[1].port}/static/webseed/${videoUUID}` |
123 | ] | 138 | ] |
124 | 139 | ||
125 | for (const server of servers) { | 140 | for (const server of servers) { |
@@ -216,41 +231,50 @@ async function check1PlaylistRedundancies (videoUUID?: string) { | |||
216 | } | 231 | } |
217 | } | 232 | } |
218 | 233 | ||
219 | async function checkStatsWith2Webseed (strategy: VideoRedundancyStrategy) { | 234 | async function checkStatsGlobal (strategy: VideoRedundancyStrategyWithManual) { |
235 | let totalSize: number = null | ||
236 | let statsLength = 1 | ||
237 | |||
238 | if (strategy !== 'manual') { | ||
239 | totalSize = 409600 | ||
240 | statsLength = 2 | ||
241 | } | ||
242 | |||
220 | const res = await getStats(servers[0].url) | 243 | const res = await getStats(servers[0].url) |
221 | const data: ServerStats = res.body | 244 | const data: ServerStats = res.body |
222 | 245 | ||
223 | expect(data.videosRedundancy).to.have.lengthOf(1) | 246 | expect(data.videosRedundancy).to.have.lengthOf(statsLength) |
224 | const stat = data.videosRedundancy[0] | ||
225 | 247 | ||
248 | const stat = data.videosRedundancy[0] | ||
226 | expect(stat.strategy).to.equal(strategy) | 249 | expect(stat.strategy).to.equal(strategy) |
227 | expect(stat.totalSize).to.equal(409600) | 250 | expect(stat.totalSize).to.equal(totalSize) |
251 | |||
252 | return stat | ||
253 | } | ||
254 | |||
255 | async function checkStatsWith2Webseed (strategy: VideoRedundancyStrategyWithManual) { | ||
256 | const stat = await checkStatsGlobal(strategy) | ||
257 | |||
228 | expect(stat.totalUsed).to.be.at.least(1).and.below(409601) | 258 | expect(stat.totalUsed).to.be.at.least(1).and.below(409601) |
229 | expect(stat.totalVideoFiles).to.equal(4) | 259 | expect(stat.totalVideoFiles).to.equal(4) |
230 | expect(stat.totalVideos).to.equal(1) | 260 | expect(stat.totalVideos).to.equal(1) |
231 | } | 261 | } |
232 | 262 | ||
233 | async function checkStatsWith1Webseed (strategy: VideoRedundancyStrategy) { | 263 | async function checkStatsWith1Webseed (strategy: VideoRedundancyStrategyWithManual) { |
234 | const res = await getStats(servers[0].url) | 264 | const stat = await checkStatsGlobal(strategy) |
235 | const data: ServerStats = res.body | ||
236 | 265 | ||
237 | expect(data.videosRedundancy).to.have.lengthOf(1) | ||
238 | |||
239 | const stat = data.videosRedundancy[0] | ||
240 | expect(stat.strategy).to.equal(strategy) | ||
241 | expect(stat.totalSize).to.equal(409600) | ||
242 | expect(stat.totalUsed).to.equal(0) | 266 | expect(stat.totalUsed).to.equal(0) |
243 | expect(stat.totalVideoFiles).to.equal(0) | 267 | expect(stat.totalVideoFiles).to.equal(0) |
244 | expect(stat.totalVideos).to.equal(0) | 268 | expect(stat.totalVideos).to.equal(0) |
245 | } | 269 | } |
246 | 270 | ||
247 | async function enableRedundancyOnServer1 () { | 271 | async function enableRedundancyOnServer1 () { |
248 | await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, true) | 272 | await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, true) |
249 | 273 | ||
250 | const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: '-createdAt' }) | 274 | const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: '-createdAt' }) |
251 | const follows: ActorFollow[] = res.body.data | 275 | const follows: ActorFollow[] = res.body.data |
252 | const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`) | 276 | const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`) |
253 | const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`) | 277 | const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`) |
254 | 278 | ||
255 | expect(server3).to.not.be.undefined | 279 | expect(server3).to.not.be.undefined |
256 | expect(server3.following.hostRedundancyAllowed).to.be.false | 280 | expect(server3.following.hostRedundancyAllowed).to.be.false |
@@ -260,12 +284,12 @@ async function enableRedundancyOnServer1 () { | |||
260 | } | 284 | } |
261 | 285 | ||
262 | async function disableRedundancyOnServer1 () { | 286 | async function disableRedundancyOnServer1 () { |
263 | await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, false) | 287 | await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, false) |
264 | 288 | ||
265 | const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: '-createdAt' }) | 289 | const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: '-createdAt' }) |
266 | const follows: ActorFollow[] = res.body.data | 290 | const follows: ActorFollow[] = res.body.data |
267 | const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`) | 291 | const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`) |
268 | const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`) | 292 | const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`) |
269 | 293 | ||
270 | expect(server3).to.not.be.undefined | 294 | expect(server3).to.not.be.undefined |
271 | expect(server3.following.hostRedundancyAllowed).to.be.false | 295 | expect(server3.following.hostRedundancyAllowed).to.be.false |
@@ -410,8 +434,8 @@ describe('Test videos redundancy', function () { | |||
410 | it('Should view 2 times the first video to have > min_views config', async function () { | 434 | it('Should view 2 times the first video to have > min_views config', async function () { |
411 | this.timeout(80000) | 435 | this.timeout(80000) |
412 | 436 | ||
413 | await viewVideo(servers[ 0 ].url, video1Server2UUID) | 437 | await viewVideo(servers[0].url, video1Server2UUID) |
414 | await viewVideo(servers[ 2 ].url, video1Server2UUID) | 438 | await viewVideo(servers[2].url, video1Server2UUID) |
415 | 439 | ||
416 | await wait(10000) | 440 | await wait(10000) |
417 | await waitJobs(servers) | 441 | await waitJobs(servers) |
@@ -446,6 +470,74 @@ describe('Test videos redundancy', function () { | |||
446 | }) | 470 | }) |
447 | }) | 471 | }) |
448 | 472 | ||
473 | describe('With manual strategy', function () { | ||
474 | before(function () { | ||
475 | this.timeout(120000) | ||
476 | |||
477 | return flushAndRunServers(null) | ||
478 | }) | ||
479 | |||
480 | it('Should have 1 webseed on the first video', async function () { | ||
481 | await check1WebSeed() | ||
482 | await check0PlaylistRedundancies() | ||
483 | await checkStatsWith1Webseed('manual') | ||
484 | }) | ||
485 | |||
486 | it('Should create a redundancy on first video', async function () { | ||
487 | await addVideoRedundancy({ | ||
488 | url: servers[0].url, | ||
489 | accessToken: servers[0].accessToken, | ||
490 | videoId: video1Server2Id | ||
491 | }) | ||
492 | }) | ||
493 | |||
494 | it('Should have 2 webseeds on the first video', async function () { | ||
495 | this.timeout(80000) | ||
496 | |||
497 | await waitJobs(servers) | ||
498 | await waitUntilLog(servers[0], 'Duplicated ', 5) | ||
499 | await waitJobs(servers) | ||
500 | |||
501 | await check2Webseeds() | ||
502 | await check1PlaylistRedundancies() | ||
503 | await checkStatsWith2Webseed('manual') | ||
504 | }) | ||
505 | |||
506 | it('Should manually remove redundancies on server 1 and remove duplicated videos', async function () { | ||
507 | this.timeout(80000) | ||
508 | |||
509 | const res = await listVideoRedundancies({ | ||
510 | url: servers[0].url, | ||
511 | accessToken: servers[0].accessToken, | ||
512 | target: 'remote-videos' | ||
513 | }) | ||
514 | |||
515 | const videos = res.body.data as VideoRedundancy[] | ||
516 | expect(videos).to.have.lengthOf(1) | ||
517 | |||
518 | const video = videos[0] | ||
519 | for (const r of video.redundancies.files.concat(video.redundancies.streamingPlaylists)) { | ||
520 | await removeVideoRedundancy({ | ||
521 | url: servers[0].url, | ||
522 | accessToken: servers[0].accessToken, | ||
523 | redundancyId: r.id | ||
524 | }) | ||
525 | } | ||
526 | |||
527 | await waitJobs(servers) | ||
528 | await wait(5000) | ||
529 | |||
530 | await check1WebSeed() | ||
531 | await check0PlaylistRedundancies() | ||
532 | |||
533 | await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].serverNumber, [ 'videos' ]) | ||
534 | }) | ||
535 | |||
536 | after(async function () { | ||
537 | await cleanupTests(servers) | ||
538 | }) | ||
539 | }) | ||
540 | |||
449 | describe('Test expiration', function () { | 541 | describe('Test expiration', function () { |
450 | const strategy = 'recently-added' | 542 | const strategy = 'recently-added' |
451 | 543 | ||
@@ -528,7 +620,7 @@ describe('Test videos redundancy', function () { | |||
528 | await check1PlaylistRedundancies() | 620 | await check1PlaylistRedundancies() |
529 | await checkStatsWith2Webseed(strategy) | 621 | await checkStatsWith2Webseed(strategy) |
530 | 622 | ||
531 | const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, { name: 'video 2 server 2' }) | 623 | const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 2 server 2' }) |
532 | video2Server2UUID = res.body.video.uuid | 624 | video2Server2UUID = res.body.video.uuid |
533 | }) | 625 | }) |
534 | 626 | ||
@@ -560,8 +652,8 @@ describe('Test videos redundancy', function () { | |||
560 | 652 | ||
561 | await waitJobs(servers) | 653 | await waitJobs(servers) |
562 | 654 | ||
563 | killallServers([ servers[ 0 ] ]) | 655 | killallServers([ servers[0] ]) |
564 | await reRunServer(servers[ 0 ], { | 656 | await reRunServer(servers[0], { |
565 | redundancy: { | 657 | redundancy: { |
566 | videos: { | 658 | videos: { |
567 | check_interval: '1 second', | 659 | check_interval: '1 second', |