diff options
author | Chocobozzz <me@florianbigard.com> | 2023-07-31 14:34:36 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-08-11 15:02:33 +0200 |
commit | 3a4992633ee62d5edfbb484d9c6bcb3cf158489d (patch) | |
tree | e4510b39bdac9c318fdb4b47018d08f15368b8f0 /packages/tests/src/cli | |
parent | 04d1da5621d25d59bd5fa1543b725c497bf5d9a8 (diff) | |
download | PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.gz PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.zst PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.zip |
Migrate server to ESM
Sorry for the very big commit that may lead to git log issues and merge
conflicts, but it's a major step forward:
* Server can be faster at startup because imports() are async and we can
easily lazy import big modules
* Angular doesn't seem to support ES import (with .js extension), so we
had to correctly organize peertube into a monorepo:
* Use yarn workspace feature
* Use typescript reference projects for dependencies
* Shared projects have been moved into "packages", each one is now a
node module (with a dedicated package.json/tsconfig.json)
* server/tools have been moved into apps/ and is now a dedicated app
bundled and published on NPM so users don't have to build peertube
cli tools manually
* server/tests have been moved into packages/ so we don't compile
them every time we want to run the server
* Use isolatedModule option:
* Had to move from const enum to const
(https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums)
* Had to explictely specify "type" imports when used in decorators
* Prefer tsx (that uses esbuild under the hood) instead of ts-node to
load typescript files (tests with mocha or scripts):
* To reduce test complexity as esbuild doesn't support decorator
metadata, we only test server files that do not import server
models
* We still build tests files into js files for a faster CI
* Remove unmaintained peertube CLI import script
* Removed some barrels to speed up execution (less imports)
Diffstat (limited to 'packages/tests/src/cli')
-rw-r--r-- | packages/tests/src/cli/create-generate-storyboard-job.ts | 121 | ||||
-rw-r--r-- | packages/tests/src/cli/create-import-video-file-job.ts | 168 | ||||
-rw-r--r-- | packages/tests/src/cli/create-move-video-storage-job.ts | 125 | ||||
-rw-r--r-- | packages/tests/src/cli/index.ts | 10 | ||||
-rw-r--r-- | packages/tests/src/cli/peertube.ts | 257 | ||||
-rw-r--r-- | packages/tests/src/cli/plugins.ts | 76 | ||||
-rw-r--r-- | packages/tests/src/cli/prune-storage.ts | 224 | ||||
-rw-r--r-- | packages/tests/src/cli/regenerate-thumbnails.ts | 122 | ||||
-rw-r--r-- | packages/tests/src/cli/reset-password.ts | 26 | ||||
-rw-r--r-- | packages/tests/src/cli/update-host.ts | 134 |
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 | |||
3 | import { expect } from 'chai' | ||
4 | import { remove } from 'fs-extra/esm' | ||
5 | import { readdir } from 'fs/promises' | ||
6 | import { join } from 'path' | ||
7 | import { HttpStatusCode } from '@peertube/peertube-models' | ||
8 | import { | ||
9 | cleanupTests, | ||
10 | createMultipleServers, | ||
11 | doubleFollow, | ||
12 | makeGetRequest, | ||
13 | PeerTubeServer, | ||
14 | setAccessTokensToServers, | ||
15 | waitJobs | ||
16 | } from '@peertube/peertube-server-commands' | ||
17 | import { SQLCommand } from '../shared/sql-command.js' | ||
18 | |||
19 | function listStoryboardFiles (server: PeerTubeServer) { | ||
20 | const storage = server.getDirectoryPath('storyboards') | ||
21 | |||
22 | return readdir(storage) | ||
23 | } | ||
24 | |||
25 | describe('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 | |||
3 | import { HttpStatusCode, VideoDetails, VideoFile, VideoInclude } from '@peertube/peertube-models' | ||
4 | import { areMockObjectStorageTestsDisabled, buildAbsoluteFixturePath } from '@peertube/peertube-node-utils' | ||
5 | import { | ||
6 | ObjectStorageCommand, | ||
7 | PeerTubeServer, | ||
8 | cleanupTests, | ||
9 | createMultipleServers, | ||
10 | doubleFollow, | ||
11 | makeRawRequest, | ||
12 | setAccessTokensToServers, | ||
13 | waitJobs | ||
14 | } from '@peertube/peertube-server-commands' | ||
15 | import { expect } from 'chai' | ||
16 | import { expectStartWith } from '../shared/checks.js' | ||
17 | |||
18 | function 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 | |||
28 | async 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 | |||
36 | function 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 | |||
157 | describe('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 | |||
3 | import { join } from 'path' | ||
4 | import { areMockObjectStorageTestsDisabled } from '@peertube/peertube-node-utils' | ||
5 | import { HttpStatusCode, VideoDetails } from '@peertube/peertube-models' | ||
6 | import { | ||
7 | cleanupTests, | ||
8 | createMultipleServers, | ||
9 | doubleFollow, | ||
10 | makeRawRequest, | ||
11 | ObjectStorageCommand, | ||
12 | PeerTubeServer, | ||
13 | setAccessTokensToServers, | ||
14 | waitJobs | ||
15 | } from '@peertube/peertube-server-commands' | ||
16 | import { expectStartWith } from '../shared/checks.js' | ||
17 | import { checkDirectoryIsEmpty } from '@tests/shared/directories.js' | ||
18 | |||
19 | async 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 | |||
45 | describe('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 | ||
2 | import './create-import-video-file-job' | ||
3 | import './create-generate-storyboard-job' | ||
4 | import './create-move-video-storage-job' | ||
5 | import './peertube' | ||
6 | import './plugins' | ||
7 | import './prune-storage' | ||
8 | import './regenerate-thumbnails' | ||
9 | import './reset-password' | ||
10 | import './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 | |||
3 | import { expect } from 'chai' | ||
4 | import { buildAbsoluteFixturePath } from '@peertube/peertube-node-utils' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | CLICommand, | ||
8 | createSingleServer, | ||
9 | doubleFollow, | ||
10 | PeerTubeServer, | ||
11 | setAccessTokensToServers, | ||
12 | waitJobs | ||
13 | } from '@peertube/peertube-server-commands' | ||
14 | import { testHelloWorldRegisteredSettings } from '../shared/plugins.js' | ||
15 | |||
16 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { | ||
5 | cleanupTests, | ||
6 | createSingleServer, | ||
7 | killallServers, | ||
8 | PeerTubeServer, | ||
9 | PluginsCommand, | ||
10 | setAccessTokensToServers | ||
11 | } from '@peertube/peertube-server-commands' | ||
12 | |||
13 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { createFile } from 'fs-extra/esm' | ||
5 | import { readdir } from 'fs/promises' | ||
6 | import { join } from 'path' | ||
7 | import { wait } from '@peertube/peertube-core-utils' | ||
8 | import { buildUUID } from '@peertube/peertube-node-utils' | ||
9 | import { HttpStatusCode, VideoPlaylistPrivacy, VideoPrivacy } from '@peertube/peertube-models' | ||
10 | import { | ||
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 | |||
23 | async 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 | |||
31 | async 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 | |||
59 | describe('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 @@ | |||
1 | import { expect } from 'chai' | ||
2 | import { writeFile } from 'fs/promises' | ||
3 | import { basename, join } from 'path' | ||
4 | import { HttpStatusCode, Video } from '@peertube/peertube-models' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | createMultipleServers, | ||
8 | doubleFollow, | ||
9 | makeGetRequest, | ||
10 | PeerTubeServer, | ||
11 | setAccessTokensToServers, | ||
12 | waitJobs | ||
13 | } from '@peertube/peertube-server-commands' | ||
14 | |||
15 | async 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 | |||
29 | describe('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 @@ | |||
1 | import { cleanupTests, CLICommand, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@peertube/peertube-server-commands' | ||
2 | |||
3 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { getAllFiles } from '@peertube/peertube-core-utils' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | createSingleServer, | ||
8 | killallServers, | ||
9 | makeActivityPubGetRequest, | ||
10 | PeerTubeServer, | ||
11 | setAccessTokensToServers, | ||
12 | waitJobs | ||
13 | } from '@peertube/peertube-server-commands' | ||
14 | import { parseTorrentVideo } from '@tests/shared/webtorrent.js' | ||
15 | |||
16 | describe('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 | }) | ||