aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/tests/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'packages/tests/src/cli')
-rw-r--r--packages/tests/src/cli/create-generate-storyboard-job.ts121
-rw-r--r--packages/tests/src/cli/create-import-video-file-job.ts168
-rw-r--r--packages/tests/src/cli/create-move-video-storage-job.ts125
-rw-r--r--packages/tests/src/cli/index.ts10
-rw-r--r--packages/tests/src/cli/peertube.ts257
-rw-r--r--packages/tests/src/cli/plugins.ts76
-rw-r--r--packages/tests/src/cli/prune-storage.ts224
-rw-r--r--packages/tests/src/cli/regenerate-thumbnails.ts122
-rw-r--r--packages/tests/src/cli/reset-password.ts26
-rw-r--r--packages/tests/src/cli/update-host.ts134
10 files changed, 1263 insertions, 0 deletions
diff --git a/packages/tests/src/cli/create-generate-storyboard-job.ts b/packages/tests/src/cli/create-generate-storyboard-job.ts
new file mode 100644
index 000000000..5a1c61ef1
--- /dev/null
+++ b/packages/tests/src/cli/create-generate-storyboard-job.ts
@@ -0,0 +1,121 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { remove } from 'fs-extra/esm'
5import { readdir } from 'fs/promises'
6import { join } from 'path'
7import { HttpStatusCode } from '@peertube/peertube-models'
8import {
9 cleanupTests,
10 createMultipleServers,
11 doubleFollow,
12 makeGetRequest,
13 PeerTubeServer,
14 setAccessTokensToServers,
15 waitJobs
16} from '@peertube/peertube-server-commands'
17import { SQLCommand } from '../shared/sql-command.js'
18
19function listStoryboardFiles (server: PeerTubeServer) {
20 const storage = server.getDirectoryPath('storyboards')
21
22 return readdir(storage)
23}
24
25describe('Test create generate storyboard job', function () {
26 let servers: PeerTubeServer[] = []
27 const uuids: string[] = []
28 let sql: SQLCommand
29 let existingStoryboardName: string
30
31 before(async function () {
32 this.timeout(120000)
33
34 // Run server 2 to have transcoding enabled
35 servers = await createMultipleServers(2)
36 await setAccessTokensToServers(servers)
37
38 await doubleFollow(servers[0], servers[1])
39
40 for (let i = 0; i < 3; i++) {
41 const { uuid } = await servers[0].videos.quickUpload({ name: 'video ' + i })
42 uuids.push(uuid)
43 }
44
45 await waitJobs(servers)
46
47 const storage = servers[0].getDirectoryPath('storyboards')
48 for (const storyboard of await listStoryboardFiles(servers[0])) {
49 await remove(join(storage, storyboard))
50 }
51
52 sql = new SQLCommand(servers[0])
53 await sql.deleteAll('storyboard')
54
55 const { uuid } = await servers[0].videos.quickUpload({ name: 'video 4' })
56 uuids.push(uuid)
57
58 await waitJobs(servers)
59
60 const storyboards = await listStoryboardFiles(servers[0])
61 existingStoryboardName = storyboards[0]
62 })
63
64 it('Should create a storyboard of a video', async function () {
65 this.timeout(120000)
66
67 for (const uuid of [ uuids[0], uuids[3] ]) {
68 const command = `npm run create-generate-storyboard-job -- -v ${uuid}`
69 await servers[0].cli.execWithEnv(command)
70 }
71
72 await waitJobs(servers)
73
74 {
75 const storyboards = await listStoryboardFiles(servers[0])
76 expect(storyboards).to.have.lengthOf(2)
77 expect(storyboards).to.not.include(existingStoryboardName)
78
79 existingStoryboardName = storyboards[0]
80 }
81
82 for (const server of servers) {
83 for (const uuid of [ uuids[0], uuids[3] ]) {
84 const { storyboards } = await server.storyboard.list({ id: uuid })
85 expect(storyboards).to.have.lengthOf(1)
86
87 await makeGetRequest({ url: server.url, path: storyboards[0].storyboardPath, expectedStatus: HttpStatusCode.OK_200 })
88 }
89 }
90 })
91
92 it('Should create missing storyboards', async function () {
93 this.timeout(120000)
94
95 const command = `npm run create-generate-storyboard-job -- -a`
96 await servers[0].cli.execWithEnv(command)
97
98 await waitJobs(servers)
99
100 {
101 const storyboards = await listStoryboardFiles(servers[0])
102 expect(storyboards).to.have.lengthOf(4)
103 expect(storyboards).to.include(existingStoryboardName)
104 }
105
106 for (const server of servers) {
107 for (const uuid of uuids) {
108 const { storyboards } = await server.storyboard.list({ id: uuid })
109 expect(storyboards).to.have.lengthOf(1)
110
111 await makeGetRequest({ url: server.url, path: storyboards[0].storyboardPath, expectedStatus: HttpStatusCode.OK_200 })
112 }
113 }
114 })
115
116 after(async function () {
117 await sql.cleanup()
118
119 await cleanupTests(servers)
120 })
121})
diff --git a/packages/tests/src/cli/create-import-video-file-job.ts b/packages/tests/src/cli/create-import-video-file-job.ts
new file mode 100644
index 000000000..fa934510c
--- /dev/null
+++ b/packages/tests/src/cli/create-import-video-file-job.ts
@@ -0,0 +1,168 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { HttpStatusCode, VideoDetails, VideoFile, VideoInclude } from '@peertube/peertube-models'
4import { areMockObjectStorageTestsDisabled, buildAbsoluteFixturePath } from '@peertube/peertube-node-utils'
5import {
6 ObjectStorageCommand,
7 PeerTubeServer,
8 cleanupTests,
9 createMultipleServers,
10 doubleFollow,
11 makeRawRequest,
12 setAccessTokensToServers,
13 waitJobs
14} from '@peertube/peertube-server-commands'
15import { expect } from 'chai'
16import { expectStartWith } from '../shared/checks.js'
17
18function assertVideoProperties (video: VideoFile, resolution: number, extname: string, size?: number) {
19 expect(video).to.have.nested.property('resolution.id', resolution)
20 expect(video).to.have.property('torrentUrl').that.includes(`-${resolution}.torrent`)
21 expect(video).to.have.property('fileUrl').that.includes(`.${extname}`)
22 expect(video).to.have.property('magnetUri').that.includes(`.${extname}`)
23 expect(video).to.have.property('size').that.is.above(0)
24
25 if (size) expect(video.size).to.equal(size)
26}
27
28async function checkFiles (video: VideoDetails, objectStorage: ObjectStorageCommand) {
29 for (const file of video.files) {
30 if (objectStorage) expectStartWith(file.fileUrl, objectStorage.getMockWebVideosBaseUrl())
31
32 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
33 }
34}
35
36function runTests (enableObjectStorage: boolean) {
37 let video1ShortId: string
38 let video2UUID: string
39
40 let servers: PeerTubeServer[] = []
41
42 const objectStorage = new ObjectStorageCommand()
43
44 before(async function () {
45 this.timeout(90000)
46
47 const config = enableObjectStorage
48 ? objectStorage.getDefaultMockConfig()
49 : {}
50
51 // Run server 2 to have transcoding enabled
52 servers = await createMultipleServers(2, config)
53 await setAccessTokensToServers(servers)
54
55 await doubleFollow(servers[0], servers[1])
56
57 if (enableObjectStorage) await objectStorage.prepareDefaultMockBuckets()
58
59 // Upload two videos for our needs
60 {
61 const { shortUUID } = await servers[0].videos.upload({ attributes: { name: 'video1' } })
62 video1ShortId = shortUUID
63 }
64
65 {
66 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video2' } })
67 video2UUID = uuid
68 }
69
70 await waitJobs(servers)
71
72 for (const server of servers) {
73 await server.config.enableTranscoding()
74 }
75 })
76
77 it('Should run a import job on video 1 with a lower resolution', async function () {
78 const command = `npm run create-import-video-file-job -- -v ${video1ShortId} -i ${buildAbsoluteFixturePath('video_short_480.webm')}`
79 await servers[0].cli.execWithEnv(command)
80
81 await waitJobs(servers)
82
83 for (const server of servers) {
84 const { data: videos } = await server.videos.list()
85 expect(videos).to.have.lengthOf(2)
86
87 const video = videos.find(({ shortUUID }) => shortUUID === video1ShortId)
88 const videoDetails = await server.videos.get({ id: video.shortUUID })
89
90 expect(videoDetails.files).to.have.lengthOf(2)
91 const [ originalVideo, transcodedVideo ] = videoDetails.files
92 assertVideoProperties(originalVideo, 720, 'webm', 218910)
93 assertVideoProperties(transcodedVideo, 480, 'webm', 69217)
94
95 await checkFiles(videoDetails, enableObjectStorage && objectStorage)
96 }
97 })
98
99 it('Should run a import job on video 2 with the same resolution and a different extension', async function () {
100 const command = `npm run create-import-video-file-job -- -v ${video2UUID} -i ${buildAbsoluteFixturePath('video_short.ogv')}`
101 await servers[1].cli.execWithEnv(command)
102
103 await waitJobs(servers)
104
105 for (const server of servers) {
106 const { data: videos } = await server.videos.listWithToken({ include: VideoInclude.NOT_PUBLISHED_STATE })
107 expect(videos).to.have.lengthOf(2)
108
109 const video = videos.find(({ uuid }) => uuid === video2UUID)
110 const videoDetails = await server.videos.get({ id: video.uuid })
111
112 expect(videoDetails.files).to.have.lengthOf(4)
113 const [ originalVideo, transcodedVideo420, transcodedVideo320, transcodedVideo240 ] = videoDetails.files
114 assertVideoProperties(originalVideo, 720, 'ogv', 140849)
115 assertVideoProperties(transcodedVideo420, 480, 'mp4')
116 assertVideoProperties(transcodedVideo320, 360, 'mp4')
117 assertVideoProperties(transcodedVideo240, 240, 'mp4')
118
119 await checkFiles(videoDetails, enableObjectStorage && objectStorage)
120 }
121 })
122
123 it('Should run a import job on video 2 with the same resolution and the same extension', async function () {
124 const command = `npm run create-import-video-file-job -- -v ${video1ShortId} -i ${buildAbsoluteFixturePath('video_short2.webm')}`
125 await servers[0].cli.execWithEnv(command)
126
127 await waitJobs(servers)
128
129 for (const server of servers) {
130 const { data: videos } = await server.videos.listWithToken({ include: VideoInclude.NOT_PUBLISHED_STATE })
131 expect(videos).to.have.lengthOf(2)
132
133 const video = videos.find(({ shortUUID }) => shortUUID === video1ShortId)
134 const videoDetails = await server.videos.get({ id: video.uuid })
135
136 expect(videoDetails.files).to.have.lengthOf(2)
137 const [ video720, video480 ] = videoDetails.files
138 assertVideoProperties(video720, 720, 'webm', 942961)
139 assertVideoProperties(video480, 480, 'webm', 69217)
140
141 await checkFiles(videoDetails, enableObjectStorage && objectStorage)
142 }
143 })
144
145 it('Should not have run transcoding after an import job', async function () {
146 const { data } = await servers[0].jobs.list({ jobType: 'video-transcoding' })
147 expect(data).to.have.lengthOf(0)
148 })
149
150 after(async function () {
151 await objectStorage.cleanupMock()
152
153 await cleanupTests(servers)
154 })
155}
156
157describe('Test create import video jobs', function () {
158
159 describe('On filesystem', function () {
160 runTests(false)
161 })
162
163 describe('On object storage', function () {
164 if (areMockObjectStorageTestsDisabled()) return
165
166 runTests(true)
167 })
168})
diff --git a/packages/tests/src/cli/create-move-video-storage-job.ts b/packages/tests/src/cli/create-move-video-storage-job.ts
new file mode 100644
index 000000000..1bee7414f
--- /dev/null
+++ b/packages/tests/src/cli/create-move-video-storage-job.ts
@@ -0,0 +1,125 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { join } from 'path'
4import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils'
5import { HttpStatusCode, VideoDetails } from '@peertube/peertube-models'
6import {
7 cleanupTests,
8 createMultipleServers,
9 doubleFollow,
10 makeRawRequest,
11 ObjectStorageCommand,
12 PeerTubeServer,
13 setAccessTokensToServers,
14 waitJobs
15} from '@peertube/peertube-server-commands'
16import { expectStartWith } from '../shared/checks.js'
17import { checkDirectoryIsEmpty } from '@tests/shared/directories.js'
18
19async function checkFiles (origin: PeerTubeServer, video: VideoDetails, objectStorage?: ObjectStorageCommand) {
20 for (const file of video.files) {
21 const start = objectStorage
22 ? objectStorage.getMockWebVideosBaseUrl()
23 : origin.url
24
25 expectStartWith(file.fileUrl, start)
26
27 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
28 }
29
30 const start = objectStorage
31 ? objectStorage.getMockPlaylistBaseUrl()
32 : origin.url
33
34 const hls = video.streamingPlaylists[0]
35 expectStartWith(hls.playlistUrl, start)
36 expectStartWith(hls.segmentsSha256Url, start)
37
38 for (const file of hls.files) {
39 expectStartWith(file.fileUrl, start)
40
41 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
42 }
43}
44
45describe('Test create move video storage job', function () {
46 if (areMockObjectStorageTestsDisabled()) return
47
48 let servers: PeerTubeServer[] = []
49 const uuids: string[] = []
50 const objectStorage = new ObjectStorageCommand()
51
52 before(async function () {
53 this.timeout(360000)
54
55 // Run server 2 to have transcoding enabled
56 servers = await createMultipleServers(2)
57 await setAccessTokensToServers(servers)
58
59 await doubleFollow(servers[0], servers[1])
60
61 await objectStorage.prepareDefaultMockBuckets()
62
63 await servers[0].config.enableTranscoding()
64
65 for (let i = 0; i < 3; i++) {
66 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' + i } })
67 uuids.push(uuid)
68 }
69
70 await waitJobs(servers)
71
72 await servers[0].kill()
73 await servers[0].run(objectStorage.getDefaultMockConfig())
74 })
75
76 it('Should move only one file', async function () {
77 this.timeout(120000)
78
79 const command = `npm run create-move-video-storage-job -- --to-object-storage -v ${uuids[1]}`
80 await servers[0].cli.execWithEnv(command, objectStorage.getDefaultMockConfig())
81 await waitJobs(servers)
82
83 for (const server of servers) {
84 const video = await server.videos.get({ id: uuids[1] })
85
86 await checkFiles(servers[0], video, objectStorage)
87
88 for (const id of [ uuids[0], uuids[2] ]) {
89 const video = await server.videos.get({ id })
90
91 await checkFiles(servers[0], video)
92 }
93 }
94 })
95
96 it('Should move all files', async function () {
97 this.timeout(120000)
98
99 const command = `npm run create-move-video-storage-job -- --to-object-storage --all-videos`
100 await servers[0].cli.execWithEnv(command, objectStorage.getDefaultMockConfig())
101 await waitJobs(servers)
102
103 for (const server of servers) {
104 for (const id of [ uuids[0], uuids[2] ]) {
105 const video = await server.videos.get({ id })
106
107 await checkFiles(servers[0], video, objectStorage)
108 }
109 }
110 })
111
112 it('Should not have files on disk anymore', async function () {
113 await checkDirectoryIsEmpty(servers[0], 'web-videos', [ 'private' ])
114 await checkDirectoryIsEmpty(servers[0], join('web-videos', 'private'))
115
116 await checkDirectoryIsEmpty(servers[0], join('streaming-playlists', 'hls'), [ 'private' ])
117 await checkDirectoryIsEmpty(servers[0], join('streaming-playlists', 'hls', 'private'))
118 })
119
120 after(async function () {
121 await objectStorage.cleanupMock()
122
123 await cleanupTests(servers)
124 })
125})
diff --git a/packages/tests/src/cli/index.ts b/packages/tests/src/cli/index.ts
new file mode 100644
index 000000000..94444ace3
--- /dev/null
+++ b/packages/tests/src/cli/index.ts
@@ -0,0 +1,10 @@
1// Order of the tests we want to execute
2import './create-import-video-file-job'
3import './create-generate-storyboard-job'
4import './create-move-video-storage-job'
5import './peertube'
6import './plugins'
7import './prune-storage'
8import './regenerate-thumbnails'
9import './reset-password'
10import './update-host'
diff --git a/packages/tests/src/cli/peertube.ts b/packages/tests/src/cli/peertube.ts
new file mode 100644
index 000000000..2c66b7a18
--- /dev/null
+++ b/packages/tests/src/cli/peertube.ts
@@ -0,0 +1,257 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { buildAbsoluteFixturePath } from '@peertube/peertube-node-utils'
5import {
6 cleanupTests,
7 CLICommand,
8 createSingleServer,
9 doubleFollow,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 waitJobs
13} from '@peertube/peertube-server-commands'
14import { testHelloWorldRegisteredSettings } from '../shared/plugins.js'
15
16describe('Test CLI wrapper', function () {
17 let server: PeerTubeServer
18 let userAccessToken: string
19
20 let cliCommand: CLICommand
21
22 const cmd = 'node ./apps/peertube-cli/dist/peertube.js'
23
24 before(async function () {
25 this.timeout(30000)
26
27 server = await createSingleServer(1, {
28 rates_limit: {
29 login: {
30 max: 30
31 }
32 }
33 })
34 await setAccessTokensToServers([ server ])
35
36 await server.users.create({ username: 'user_1', password: 'super_password' })
37
38 userAccessToken = await server.login.getAccessToken({ username: 'user_1', password: 'super_password' })
39
40 {
41 const attributes = { name: 'user_channel', displayName: 'User channel', support: 'super support text' }
42 await server.channels.create({ token: userAccessToken, attributes })
43 }
44
45 cliCommand = server.cli
46 })
47
48 describe('Authentication and instance selection', function () {
49
50 it('Should get an access token', async function () {
51 const stdout = await cliCommand.execWithEnv(`${cmd} token --url ${server.url} --username user_1 --password super_password`)
52 const token = stdout.trim()
53
54 const body = await server.users.getMyInfo({ token })
55 expect(body.username).to.equal('user_1')
56 })
57
58 it('Should display no selected instance', async function () {
59 this.timeout(60000)
60
61 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
62 expect(stdout).to.contain('no instance selected')
63 })
64
65 it('Should add a user', async function () {
66 this.timeout(60000)
67
68 await cliCommand.execWithEnv(`${cmd} auth add -u ${server.url} -U user_1 -p super_password`)
69 })
70
71 it('Should not fail to add a user if there is a slash at the end of the instance URL', async function () {
72 this.timeout(60000)
73
74 let fullServerURL = server.url + '/'
75
76 await cliCommand.execWithEnv(`${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
77
78 fullServerURL = server.url + '/asdfasdf'
79 await cliCommand.execWithEnv(`${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
80 })
81
82 it('Should default to this user', async function () {
83 this.timeout(60000)
84
85 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
86 expect(stdout).to.contain(`instance ${server.url} selected`)
87 })
88
89 it('Should remember the user', async function () {
90 this.timeout(60000)
91
92 const stdout = await cliCommand.execWithEnv(`${cmd} auth list`)
93 expect(stdout).to.contain(server.url)
94 })
95 })
96
97 describe('Video upload', function () {
98
99 it('Should upload a video', async function () {
100 this.timeout(60000)
101
102 const fixture = buildAbsoluteFixturePath('60fps_720p_small.mp4')
103 const params = `-f ${fixture} --video-name 'test upload' --channel-name user_channel --support 'support_text'`
104
105 await cliCommand.execWithEnv(`${cmd} upload ${params}`)
106 })
107
108 it('Should have the video uploaded', async function () {
109 const { total, data } = await server.videos.list()
110 expect(total).to.equal(1)
111
112 const video = await server.videos.get({ id: data[0].uuid })
113 expect(video.name).to.equal('test upload')
114 expect(video.support).to.equal('support_text')
115 expect(video.channel.name).to.equal('user_channel')
116 })
117 })
118
119 describe('Admin auth', function () {
120
121 it('Should remove the auth user', async function () {
122 await cliCommand.execWithEnv(`${cmd} auth del ${server.url}`)
123
124 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
125 expect(stdout).to.contain('no instance selected')
126 })
127
128 it('Should add the admin user', async function () {
129 await cliCommand.execWithEnv(`${cmd} auth add -u ${server.url} -U root -p test${server.internalServerNumber}`)
130 })
131 })
132
133 describe('Manage plugins', function () {
134
135 it('Should install a plugin', async function () {
136 this.timeout(60000)
137
138 await cliCommand.execWithEnv(`${cmd} plugins install --npm-name peertube-plugin-hello-world`)
139 })
140
141 it('Should have registered settings', async function () {
142 await testHelloWorldRegisteredSettings(server)
143 })
144
145 it('Should list installed plugins', async function () {
146 const res = await cliCommand.execWithEnv(`${cmd} plugins list`)
147
148 expect(res).to.contain('peertube-plugin-hello-world')
149 })
150
151 it('Should uninstall the plugin', async function () {
152 const res = await cliCommand.execWithEnv(`${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
153
154 expect(res).to.not.contain('peertube-plugin-hello-world')
155 })
156
157 it('Should install a plugin in requested version', async function () {
158 this.timeout(60000)
159
160 await cliCommand.execWithEnv(`${cmd} plugins install --npm-name peertube-plugin-hello-world --plugin-version 0.0.17`)
161 })
162
163 it('Should list installed plugins, in correct version', async function () {
164 const res = await cliCommand.execWithEnv(`${cmd} plugins list`)
165
166 expect(res).to.contain('peertube-plugin-hello-world')
167 expect(res).to.contain('0.0.17')
168 })
169
170 it('Should uninstall the plugin again', async function () {
171 const res = await cliCommand.execWithEnv(`${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
172
173 expect(res).to.not.contain('peertube-plugin-hello-world')
174 })
175
176 it('Should install a plugin in requested beta version', async function () {
177 this.timeout(60000)
178
179 await cliCommand.execWithEnv(`${cmd} plugins install --npm-name peertube-plugin-hello-world --plugin-version 0.0.21-beta.1`)
180
181 const res = await cliCommand.execWithEnv(`${cmd} plugins list`)
182
183 expect(res).to.contain('peertube-plugin-hello-world')
184 expect(res).to.contain('0.0.21-beta.1')
185
186 await cliCommand.execWithEnv(`${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
187 })
188 })
189
190 describe('Manage video redundancies', function () {
191 let anotherServer: PeerTubeServer
192 let video1Server2: number
193 let servers: PeerTubeServer[]
194
195 before(async function () {
196 this.timeout(120000)
197
198 anotherServer = await createSingleServer(2)
199 await setAccessTokensToServers([ anotherServer ])
200
201 await doubleFollow(server, anotherServer)
202
203 servers = [ server, anotherServer ]
204 await waitJobs(servers)
205
206 const { uuid } = await anotherServer.videos.quickUpload({ name: 'super video' })
207 await waitJobs(servers)
208
209 video1Server2 = await server.videos.getId({ uuid })
210 })
211
212 it('Should add a redundancy', async function () {
213 this.timeout(60000)
214
215 const params = `add --video ${video1Server2}`
216 await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
217
218 await waitJobs(servers)
219 })
220
221 it('Should list redundancies', async function () {
222 this.timeout(60000)
223
224 {
225 const params = 'list-my-redundancies'
226 const stdout = await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
227
228 expect(stdout).to.contain('super video')
229 expect(stdout).to.contain(server.host)
230 }
231 })
232
233 it('Should remove a redundancy', async function () {
234 this.timeout(60000)
235
236 const params = `remove --video ${video1Server2}`
237 await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
238
239 await waitJobs(servers)
240
241 {
242 const params = 'list-my-redundancies'
243 const stdout = await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
244
245 expect(stdout).to.not.contain('super video')
246 }
247 })
248
249 after(async function () {
250 await cleanupTests([ anotherServer ])
251 })
252 })
253
254 after(async function () {
255 await cleanupTests([ server ])
256 })
257})
diff --git a/packages/tests/src/cli/plugins.ts b/packages/tests/src/cli/plugins.ts
new file mode 100644
index 000000000..ab7f7dd85
--- /dev/null
+++ b/packages/tests/src/cli/plugins.ts
@@ -0,0 +1,76 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import {
5 cleanupTests,
6 createSingleServer,
7 killallServers,
8 PeerTubeServer,
9 PluginsCommand,
10 setAccessTokensToServers
11} from '@peertube/peertube-server-commands'
12
13describe('Test plugin scripts', function () {
14 let server: PeerTubeServer
15
16 before(async function () {
17 this.timeout(30000)
18
19 server = await createSingleServer(1)
20 await setAccessTokensToServers([ server ])
21 })
22
23 it('Should install a plugin from stateless CLI', async function () {
24 this.timeout(60000)
25
26 const packagePath = PluginsCommand.getPluginTestPath()
27
28 await server.cli.execWithEnv(`npm run plugin:install -- --plugin-path ${packagePath}`)
29 })
30
31 it('Should install a theme from stateless CLI', async function () {
32 this.timeout(60000)
33
34 await server.cli.execWithEnv(`npm run plugin:install -- --npm-name peertube-theme-background-red`)
35 })
36
37 it('Should have the theme and the plugin registered when we restart peertube', async function () {
38 this.timeout(30000)
39
40 await killallServers([ server ])
41 await server.run()
42
43 const config = await server.config.getConfig()
44
45 const plugin = config.plugin.registered
46 .find(p => p.name === 'test')
47 expect(plugin).to.not.be.undefined
48
49 const theme = config.theme.registered
50 .find(t => t.name === 'background-red')
51 expect(theme).to.not.be.undefined
52 })
53
54 it('Should uninstall a plugin from stateless CLI', async function () {
55 this.timeout(60000)
56
57 await server.cli.execWithEnv(`npm run plugin:uninstall -- --npm-name peertube-plugin-test`)
58 })
59
60 it('Should have removed the plugin on another peertube restart', async function () {
61 this.timeout(30000)
62
63 await killallServers([ server ])
64 await server.run()
65
66 const config = await server.config.getConfig()
67
68 const plugin = config.plugin.registered
69 .find(p => p.name === 'test')
70 expect(plugin).to.be.undefined
71 })
72
73 after(async function () {
74 await cleanupTests([ server ])
75 })
76})
diff --git a/packages/tests/src/cli/prune-storage.ts b/packages/tests/src/cli/prune-storage.ts
new file mode 100644
index 000000000..c07a2a975
--- /dev/null
+++ b/packages/tests/src/cli/prune-storage.ts
@@ -0,0 +1,224 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { createFile } from 'fs-extra/esm'
5import { readdir } from 'fs/promises'
6import { join } from 'path'
7import { wait } from '@peertube/peertube-core-utils'
8import { buildUUID } from '@peertube/peertube-node-utils'
9import { HttpStatusCode, VideoPlaylistPrivacy, VideoPrivacy } from '@peertube/peertube-models'
10import {
11 cleanupTests,
12 CLICommand,
13 createMultipleServers,
14 doubleFollow,
15 killallServers,
16 makeGetRequest,
17 PeerTubeServer,
18 setAccessTokensToServers,
19 setDefaultVideoChannel,
20 waitJobs
21} from '@peertube/peertube-server-commands'
22
23async function assertNotExists (server: PeerTubeServer, directory: string, substring: string) {
24 const files = await readdir(server.servers.buildDirectory(directory))
25
26 for (const f of files) {
27 expect(f).to.not.contain(substring)
28 }
29}
30
31async function assertCountAreOkay (servers: PeerTubeServer[]) {
32 for (const server of servers) {
33 const videosCount = await server.servers.countFiles('web-videos')
34 expect(videosCount).to.equal(9) // 2 videos with 4 resolutions + private directory
35
36 const privateVideosCount = await server.servers.countFiles('web-videos/private')
37 expect(privateVideosCount).to.equal(4)
38
39 const torrentsCount = await server.servers.countFiles('torrents')
40 expect(torrentsCount).to.equal(24)
41
42 const previewsCount = await server.servers.countFiles('previews')
43 expect(previewsCount).to.equal(3)
44
45 const thumbnailsCount = await server.servers.countFiles('thumbnails')
46 expect(thumbnailsCount).to.equal(5) // 3 local videos, 1 local playlist, 2 remotes videos (lazy downloaded) and 1 remote playlist
47
48 const avatarsCount = await server.servers.countFiles('avatars')
49 expect(avatarsCount).to.equal(4)
50
51 const hlsRootCount = await server.servers.countFiles(join('streaming-playlists', 'hls'))
52 expect(hlsRootCount).to.equal(3) // 2 videos + private directory
53
54 const hlsPrivateRootCount = await server.servers.countFiles(join('streaming-playlists', 'hls', 'private'))
55 expect(hlsPrivateRootCount).to.equal(1)
56 }
57}
58
59describe('Test prune storage scripts', function () {
60 let servers: PeerTubeServer[]
61 const badNames: { [directory: string]: string[] } = {}
62
63 before(async function () {
64 this.timeout(120000)
65
66 servers = await createMultipleServers(2, { transcoding: { enabled: true } })
67 await setAccessTokensToServers(servers)
68 await setDefaultVideoChannel(servers)
69
70 for (const server of servers) {
71 await server.videos.upload({ attributes: { name: 'video 1', privacy: VideoPrivacy.PUBLIC } })
72 await server.videos.upload({ attributes: { name: 'video 2', privacy: VideoPrivacy.PUBLIC } })
73
74 await server.videos.upload({ attributes: { name: 'video 3', privacy: VideoPrivacy.PRIVATE } })
75
76 await server.users.updateMyAvatar({ fixture: 'avatar.png' })
77
78 await server.playlists.create({
79 attributes: {
80 displayName: 'playlist',
81 privacy: VideoPlaylistPrivacy.PUBLIC,
82 videoChannelId: server.store.channel.id,
83 thumbnailfile: 'custom-thumbnail.jpg'
84 }
85 })
86 }
87
88 await doubleFollow(servers[0], servers[1])
89
90 // Lazy load the remote avatars
91 {
92 const account = await servers[0].accounts.get({ accountName: 'root@' + servers[1].host })
93
94 for (const avatar of account.avatars) {
95 await makeGetRequest({
96 url: servers[0].url,
97 path: avatar.path,
98 expectedStatus: HttpStatusCode.OK_200
99 })
100 }
101 }
102
103 {
104 const account = await servers[1].accounts.get({ accountName: 'root@' + servers[0].host })
105 for (const avatar of account.avatars) {
106 await makeGetRequest({
107 url: servers[1].url,
108 path: avatar.path,
109 expectedStatus: HttpStatusCode.OK_200
110 })
111 }
112 }
113
114 await wait(1000)
115
116 await waitJobs(servers)
117 await killallServers(servers)
118
119 await wait(1000)
120 })
121
122 it('Should have the files on the disk', async function () {
123 await assertCountAreOkay(servers)
124 })
125
126 it('Should create some dirty files', async function () {
127 for (let i = 0; i < 2; i++) {
128 {
129 const basePublic = servers[0].servers.buildDirectory('web-videos')
130 const basePrivate = servers[0].servers.buildDirectory(join('web-videos', 'private'))
131
132 const n1 = buildUUID() + '.mp4'
133 const n2 = buildUUID() + '.webm'
134
135 await createFile(join(basePublic, n1))
136 await createFile(join(basePublic, n2))
137 await createFile(join(basePrivate, n1))
138 await createFile(join(basePrivate, n2))
139
140 badNames['web-videos'] = [ n1, n2 ]
141 }
142
143 {
144 const base = servers[0].servers.buildDirectory('torrents')
145
146 const n1 = buildUUID() + '-240.torrent'
147 const n2 = buildUUID() + '-480.torrent'
148
149 await createFile(join(base, n1))
150 await createFile(join(base, n2))
151
152 badNames['torrents'] = [ n1, n2 ]
153 }
154
155 {
156 const base = servers[0].servers.buildDirectory('thumbnails')
157
158 const n1 = buildUUID() + '.jpg'
159 const n2 = buildUUID() + '.jpg'
160
161 await createFile(join(base, n1))
162 await createFile(join(base, n2))
163
164 badNames['thumbnails'] = [ n1, n2 ]
165 }
166
167 {
168 const base = servers[0].servers.buildDirectory('previews')
169
170 const n1 = buildUUID() + '.jpg'
171 const n2 = buildUUID() + '.jpg'
172
173 await createFile(join(base, n1))
174 await createFile(join(base, n2))
175
176 badNames['previews'] = [ n1, n2 ]
177 }
178
179 {
180 const base = servers[0].servers.buildDirectory('avatars')
181
182 const n1 = buildUUID() + '.png'
183 const n2 = buildUUID() + '.jpg'
184
185 await createFile(join(base, n1))
186 await createFile(join(base, n2))
187
188 badNames['avatars'] = [ n1, n2 ]
189 }
190
191 {
192 const directory = join('streaming-playlists', 'hls')
193 const basePublic = servers[0].servers.buildDirectory(directory)
194 const basePrivate = servers[0].servers.buildDirectory(join(directory, 'private'))
195
196 const n1 = buildUUID()
197 await createFile(join(basePublic, n1))
198 await createFile(join(basePrivate, n1))
199 badNames[directory] = [ n1 ]
200 }
201 }
202 })
203
204 it('Should run prune storage', async function () {
205 this.timeout(30000)
206
207 const env = servers[0].cli.getEnv()
208 await CLICommand.exec(`echo y | ${env} npm run prune-storage`)
209 })
210
211 it('Should have removed files', async function () {
212 await assertCountAreOkay(servers)
213
214 for (const directory of Object.keys(badNames)) {
215 for (const name of badNames[directory]) {
216 await assertNotExists(servers[0], directory, name)
217 }
218 }
219 })
220
221 after(async function () {
222 await cleanupTests(servers)
223 })
224})
diff --git a/packages/tests/src/cli/regenerate-thumbnails.ts b/packages/tests/src/cli/regenerate-thumbnails.ts
new file mode 100644
index 000000000..1448e5cfc
--- /dev/null
+++ b/packages/tests/src/cli/regenerate-thumbnails.ts
@@ -0,0 +1,122 @@
1import { expect } from 'chai'
2import { writeFile } from 'fs/promises'
3import { basename, join } from 'path'
4import { HttpStatusCode, Video } from '@peertube/peertube-models'
5import {
6 cleanupTests,
7 createMultipleServers,
8 doubleFollow,
9 makeGetRequest,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 waitJobs
13} from '@peertube/peertube-server-commands'
14
15async function testThumbnail (server: PeerTubeServer, videoId: number | string) {
16 const video = await server.videos.get({ id: videoId })
17
18 const requests = [
19 makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 }),
20 makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
21 ]
22
23 for (const req of requests) {
24 const res = await req
25 expect(res.body).to.not.have.lengthOf(0)
26 }
27}
28
29describe('Test regenerate thumbnails script', function () {
30 let servers: PeerTubeServer[]
31
32 let video1: Video
33 let video2: Video
34 let remoteVideo: Video
35
36 let thumbnail1Path: string
37 let thumbnailRemotePath: string
38
39 before(async function () {
40 this.timeout(60000)
41
42 servers = await createMultipleServers(2)
43 await setAccessTokensToServers(servers)
44
45 await doubleFollow(servers[0], servers[1])
46
47 {
48 const videoUUID1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).uuid
49 video1 = await servers[0].videos.get({ id: videoUUID1 })
50
51 thumbnail1Path = join(servers[0].servers.buildDirectory('thumbnails'), basename(video1.thumbnailPath))
52
53 const videoUUID2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).uuid
54 video2 = await servers[0].videos.get({ id: videoUUID2 })
55 }
56
57 {
58 const videoUUID = (await servers[1].videos.quickUpload({ name: 'video 3' })).uuid
59 await waitJobs(servers)
60
61 remoteVideo = await servers[0].videos.get({ id: videoUUID })
62
63 // Load remote thumbnail on disk
64 await makeGetRequest({ url: servers[0].url, path: remoteVideo.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
65
66 thumbnailRemotePath = join(servers[0].servers.buildDirectory('thumbnails'), basename(remoteVideo.thumbnailPath))
67 }
68
69 await writeFile(thumbnail1Path, '')
70 await writeFile(thumbnailRemotePath, '')
71 })
72
73 it('Should have empty thumbnails', async function () {
74 {
75 const res = await makeGetRequest({ url: servers[0].url, path: video1.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
76 expect(res.body).to.have.lengthOf(0)
77 }
78
79 {
80 const res = await makeGetRequest({ url: servers[0].url, path: video2.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
81 expect(res.body).to.not.have.lengthOf(0)
82 }
83
84 {
85 const res = await makeGetRequest({ url: servers[0].url, path: remoteVideo.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
86 expect(res.body).to.have.lengthOf(0)
87 }
88 })
89
90 it('Should regenerate local thumbnails from the CLI', async function () {
91 this.timeout(15000)
92
93 await servers[0].cli.execWithEnv(`npm run regenerate-thumbnails`)
94 })
95
96 it('Should have generated new thumbnail files', async function () {
97 await testThumbnail(servers[0], video1.uuid)
98 await testThumbnail(servers[0], video2.uuid)
99
100 const res = await makeGetRequest({ url: servers[0].url, path: remoteVideo.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
101 expect(res.body).to.have.lengthOf(0)
102 })
103
104 it('Should have deleted old thumbnail files', async function () {
105 {
106 await makeGetRequest({ url: servers[0].url, path: video1.thumbnailPath, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
107 }
108
109 {
110 await makeGetRequest({ url: servers[0].url, path: video2.thumbnailPath, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
111 }
112
113 {
114 const res = await makeGetRequest({ url: servers[0].url, path: remoteVideo.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
115 expect(res.body).to.have.lengthOf(0)
116 }
117 })
118
119 after(async function () {
120 await cleanupTests(servers)
121 })
122})
diff --git a/packages/tests/src/cli/reset-password.ts b/packages/tests/src/cli/reset-password.ts
new file mode 100644
index 000000000..62e1a37a0
--- /dev/null
+++ b/packages/tests/src/cli/reset-password.ts
@@ -0,0 +1,26 @@
1import { cleanupTests, CLICommand, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@peertube/peertube-server-commands'
2
3describe('Test reset password scripts', function () {
4 let server: PeerTubeServer
5
6 before(async function () {
7 this.timeout(30000)
8 server = await createSingleServer(1)
9 await setAccessTokensToServers([ server ])
10
11 await server.users.create({ username: 'user_1', password: 'super password' })
12 })
13
14 it('Should change the user password from CLI', async function () {
15 this.timeout(60000)
16
17 const env = server.cli.getEnv()
18 await CLICommand.exec(`echo coucou | ${env} npm run reset-password -- -u user_1`)
19
20 await server.login.login({ user: { username: 'user_1', password: 'coucou' } })
21 })
22
23 after(async function () {
24 await cleanupTests([ server ])
25 })
26})
diff --git a/packages/tests/src/cli/update-host.ts b/packages/tests/src/cli/update-host.ts
new file mode 100644
index 000000000..e5f165e5e
--- /dev/null
+++ b/packages/tests/src/cli/update-host.ts
@@ -0,0 +1,134 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { getAllFiles } from '@peertube/peertube-core-utils'
5import {
6 cleanupTests,
7 createSingleServer,
8 killallServers,
9 makeActivityPubGetRequest,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 waitJobs
13} from '@peertube/peertube-server-commands'
14import { parseTorrentVideo } from '@tests/shared/webtorrent.js'
15
16describe('Test update host scripts', function () {
17 let server: PeerTubeServer
18
19 before(async function () {
20 this.timeout(60000)
21
22 const overrideConfig = {
23 webserver: {
24 port: 9256
25 }
26 }
27 // Run server 2 to have transcoding enabled
28 server = await createSingleServer(2, overrideConfig)
29 await setAccessTokensToServers([ server ])
30
31 // Upload two videos for our needs
32 const { uuid: video1UUID } = await server.videos.upload()
33 await server.videos.upload()
34
35 // Create a user
36 await server.users.create({ username: 'toto', password: 'coucou' })
37
38 // Create channel
39 const videoChannel = {
40 name: 'second_channel',
41 displayName: 'second video channel',
42 description: 'super video channel description'
43 }
44 await server.channels.create({ attributes: videoChannel })
45
46 // Create comments
47 const text = 'my super first comment'
48 await server.comments.createThread({ videoId: video1UUID, text })
49
50 await waitJobs(server)
51 })
52
53 it('Should run update host', async function () {
54 this.timeout(30000)
55
56 await killallServers([ server ])
57 // Run server with standard configuration
58 await server.run()
59
60 await server.cli.execWithEnv(`npm run update-host`)
61 })
62
63 it('Should have updated videos url', async function () {
64 const { total, data } = await server.videos.list()
65 expect(total).to.equal(2)
66
67 for (const video of data) {
68 const { body } = await makeActivityPubGetRequest(server.url, '/videos/watch/' + video.uuid)
69
70 expect(body.id).to.equal('http://127.0.0.1:9002/videos/watch/' + video.uuid)
71
72 const videoDetails = await server.videos.get({ id: video.uuid })
73
74 expect(videoDetails.trackerUrls[0]).to.include(server.host)
75 expect(videoDetails.streamingPlaylists[0].playlistUrl).to.include(server.host)
76 expect(videoDetails.streamingPlaylists[0].segmentsSha256Url).to.include(server.host)
77 }
78 })
79
80 it('Should have updated video channels url', async function () {
81 const { data, total } = await server.channels.list({ sort: '-name' })
82 expect(total).to.equal(3)
83
84 for (const channel of data) {
85 const { body } = await makeActivityPubGetRequest(server.url, '/video-channels/' + channel.name)
86
87 expect(body.id).to.equal('http://127.0.0.1:9002/video-channels/' + channel.name)
88 }
89 })
90
91 it('Should have updated accounts url', async function () {
92 const body = await server.accounts.list()
93 expect(body.total).to.equal(3)
94
95 for (const account of body.data) {
96 const usernameWithDomain = account.name
97 const { body } = await makeActivityPubGetRequest(server.url, '/accounts/' + usernameWithDomain)
98
99 expect(body.id).to.equal('http://127.0.0.1:9002/accounts/' + usernameWithDomain)
100 }
101 })
102
103 it('Should have updated torrent hosts', async function () {
104 this.timeout(30000)
105
106 const { data } = await server.videos.list()
107 expect(data).to.have.lengthOf(2)
108
109 for (const video of data) {
110 const videoDetails = await server.videos.get({ id: video.id })
111 const files = getAllFiles(videoDetails)
112
113 expect(files).to.have.lengthOf(8)
114
115 for (const file of files) {
116 expect(file.magnetUri).to.contain('127.0.0.1%3A9002%2Ftracker%2Fsocket')
117 expect(file.magnetUri).to.contain('127.0.0.1%3A9002%2Fstatic%2F')
118
119 const torrent = await parseTorrentVideo(server, file)
120 const announceWS = torrent.announce.find(a => a === 'ws://127.0.0.1:9002/tracker/socket')
121 expect(announceWS).to.not.be.undefined
122
123 const announceHttp = torrent.announce.find(a => a === 'http://127.0.0.1:9002/tracker/announce')
124 expect(announceHttp).to.not.be.undefined
125
126 expect(torrent.urlList[0]).to.contain('http://127.0.0.1:9002/static/')
127 }
128 }
129 })
130
131 after(async function () {
132 await cleanupTests([ server ])
133 })
134})