aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/tests/src/server-helpers
diff options
context:
space:
mode:
Diffstat (limited to 'packages/tests/src/server-helpers')
-rw-r--r--packages/tests/src/server-helpers/activitypub.ts176
-rw-r--r--packages/tests/src/server-helpers/core-utils.ts150
-rw-r--r--packages/tests/src/server-helpers/crypto.ts33
-rw-r--r--packages/tests/src/server-helpers/dns.ts16
-rw-r--r--packages/tests/src/server-helpers/image.ts97
-rw-r--r--packages/tests/src/server-helpers/index.ts10
-rw-r--r--packages/tests/src/server-helpers/markdown.ts39
-rw-r--r--packages/tests/src/server-helpers/mentions.ts17
-rw-r--r--packages/tests/src/server-helpers/request.ts64
-rw-r--r--packages/tests/src/server-helpers/validator.ts35
-rw-r--r--packages/tests/src/server-helpers/version.ts36
11 files changed, 673 insertions, 0 deletions
diff --git a/packages/tests/src/server-helpers/activitypub.ts b/packages/tests/src/server-helpers/activitypub.ts
new file mode 100644
index 000000000..dfcd0389f
--- /dev/null
+++ b/packages/tests/src/server-helpers/activitypub.ts
@@ -0,0 +1,176 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { buildAbsoluteFixturePath } from '@peertube/peertube-node-utils'
4import { signAndContextify } from '@peertube/peertube-server/server/helpers/activity-pub-utils.js'
5import {
6 isHTTPSignatureVerified,
7 isJsonLDSignatureVerified,
8 parseHTTPSignature
9} from '@peertube/peertube-server/server/helpers/peertube-crypto.js'
10import { buildRequestStub } from '@tests/shared/tests.js'
11import { expect } from 'chai'
12import { readJsonSync } from 'fs-extra/esm'
13import cloneDeep from 'lodash-es/cloneDeep.js'
14
15function fakeFilter () {
16 return (data: any) => Promise.resolve(data)
17}
18
19describe('Test activity pub helpers', function () {
20
21 describe('When checking the Linked Signature', function () {
22
23 it('Should fail with an invalid Mastodon signature', async function () {
24 const body = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/create-bad-signature.json'))
25 const publicKey = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/public-key.json')).publicKey
26 const fromActor = { publicKey, url: 'http://localhost:9002/accounts/peertube' }
27
28 const result = await isJsonLDSignatureVerified(fromActor as any, body)
29
30 expect(result).to.be.false
31 })
32
33 it('Should fail with an invalid public key', async function () {
34 const body = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/create.json'))
35 const publicKey = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/bad-public-key.json')).publicKey
36 const fromActor = { publicKey, url: 'http://localhost:9002/accounts/peertube' }
37
38 const result = await isJsonLDSignatureVerified(fromActor as any, body)
39
40 expect(result).to.be.false
41 })
42
43 it('Should succeed with a valid Mastodon signature', async function () {
44 const body = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/create.json'))
45 const publicKey = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/public-key.json')).publicKey
46 const fromActor = { publicKey, url: 'http://localhost:9002/accounts/peertube' }
47
48 const result = await isJsonLDSignatureVerified(fromActor as any, body)
49
50 expect(result).to.be.true
51 })
52
53 it('Should fail with an invalid PeerTube signature', async function () {
54 const keys = readJsonSync(buildAbsoluteFixturePath('./ap-json/peertube/invalid-keys.json'))
55 const body = readJsonSync(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json'))
56
57 const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
58 const signedBody = await signAndContextify(actorSignature as any, body, 'Announce', fakeFilter())
59
60 const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' }
61 const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
62
63 expect(result).to.be.false
64 })
65
66 it('Should succeed with a valid PeerTube signature', async function () {
67 const keys = readJsonSync(buildAbsoluteFixturePath('./ap-json/peertube/keys.json'))
68 const body = readJsonSync(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json'))
69
70 const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
71 const signedBody = await signAndContextify(actorSignature as any, body, 'Announce', fakeFilter())
72
73 const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' }
74 const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
75
76 expect(result).to.be.true
77 })
78 })
79
80 describe('When checking HTTP signature', function () {
81 it('Should fail with an invalid http signature', async function () {
82 const req = buildRequestStub()
83 req.method = 'POST'
84 req.url = '/accounts/ronan/inbox'
85
86 const mastodonObject = cloneDeep(readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/bad-http-signature.json')))
87 req.body = mastodonObject.body
88 req.headers = mastodonObject.headers
89
90 const parsed = parseHTTPSignature(req, 3600 * 1000 * 365 * 10)
91 const publicKey = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/public-key.json')).publicKey
92
93 const actor = { publicKey }
94 const verified = isHTTPSignatureVerified(parsed, actor as any)
95
96 expect(verified).to.be.false
97 })
98
99 it('Should fail with an invalid public key', async function () {
100 const req = buildRequestStub()
101 req.method = 'POST'
102 req.url = '/accounts/ronan/inbox'
103
104 const mastodonObject = cloneDeep(readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/http-signature.json')))
105 req.body = mastodonObject.body
106 req.headers = mastodonObject.headers
107
108 const parsed = parseHTTPSignature(req, 3600 * 1000 * 365 * 10)
109 const publicKey = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/bad-public-key.json')).publicKey
110
111 const actor = { publicKey }
112 const verified = isHTTPSignatureVerified(parsed, actor as any)
113
114 expect(verified).to.be.false
115 })
116
117 it('Should fail because of clock skew', async function () {
118 const req = buildRequestStub()
119 req.method = 'POST'
120 req.url = '/accounts/ronan/inbox'
121
122 const mastodonObject = cloneDeep(readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/http-signature.json')))
123 req.body = mastodonObject.body
124 req.headers = mastodonObject.headers
125
126 let errored = false
127 try {
128 parseHTTPSignature(req)
129 } catch {
130 errored = true
131 }
132
133 expect(errored).to.be.true
134 })
135
136 it('Should with a scheme', async function () {
137 const req = buildRequestStub()
138 req.method = 'POST'
139 req.url = '/accounts/ronan/inbox'
140
141 const mastodonObject = cloneDeep(readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/http-signature.json')))
142 req.body = mastodonObject.body
143 req.headers = mastodonObject.headers
144 req.headers = 'Signature ' + mastodonObject.headers
145
146 let errored = false
147 try {
148 parseHTTPSignature(req, 3600 * 1000 * 365 * 10)
149 } catch {
150 errored = true
151 }
152
153 expect(errored).to.be.true
154 })
155
156 it('Should succeed with a valid signature', async function () {
157 const req = buildRequestStub()
158 req.method = 'POST'
159 req.url = '/accounts/ronan/inbox'
160
161 const mastodonObject = cloneDeep(readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/http-signature.json')))
162 req.body = mastodonObject.body
163 req.headers = mastodonObject.headers
164
165 const parsed = parseHTTPSignature(req, 3600 * 1000 * 365 * 10)
166 const publicKey = readJsonSync(buildAbsoluteFixturePath('./ap-json/mastodon/public-key.json')).publicKey
167
168 const actor = { publicKey }
169 const verified = isHTTPSignatureVerified(parsed, actor as any)
170
171 expect(verified).to.be.true
172 })
173
174 })
175
176})
diff --git a/packages/tests/src/server-helpers/core-utils.ts b/packages/tests/src/server-helpers/core-utils.ts
new file mode 100644
index 000000000..06c78591e
--- /dev/null
+++ b/packages/tests/src/server-helpers/core-utils.ts
@@ -0,0 +1,150 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import snakeCase from 'lodash-es/snakeCase.js'
5import validator from 'validator'
6import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate } from '@peertube/peertube-core-utils'
7import { VideoResolution } from '@peertube/peertube-models'
8import { objectConverter, parseBytes, parseDurationToMs } from '@peertube/peertube-server/server/helpers/core-utils.js'
9
10describe('Parse Bytes', function () {
11
12 it('Should pass on valid value', async function () {
13 // just return it
14 expect(parseBytes(-1024)).to.equal(-1024)
15 expect(parseBytes(1024)).to.equal(1024)
16 expect(parseBytes(1048576)).to.equal(1048576)
17 expect(parseBytes('1024')).to.equal(1024)
18 expect(parseBytes('1048576')).to.equal(1048576)
19
20 // sizes
21 expect(parseBytes('1B')).to.equal(1024)
22 expect(parseBytes('1MB')).to.equal(1048576)
23 expect(parseBytes('1GB')).to.equal(1073741824)
24 expect(parseBytes('1TB')).to.equal(1099511627776)
25
26 expect(parseBytes('5GB')).to.equal(5368709120)
27 expect(parseBytes('5TB')).to.equal(5497558138880)
28
29 expect(parseBytes('1024B')).to.equal(1048576)
30 expect(parseBytes('1024MB')).to.equal(1073741824)
31 expect(parseBytes('1024GB')).to.equal(1099511627776)
32 expect(parseBytes('1024TB')).to.equal(1125899906842624)
33
34 // with whitespace
35 expect(parseBytes('1 GB')).to.equal(1073741824)
36 expect(parseBytes('1\tGB')).to.equal(1073741824)
37
38 // sum value
39 expect(parseBytes('1TB 1024MB')).to.equal(1100585369600)
40 expect(parseBytes('4GB 1024MB')).to.equal(5368709120)
41 expect(parseBytes('4TB 1024GB')).to.equal(5497558138880)
42 expect(parseBytes('4TB 1024GB 0MB')).to.equal(5497558138880)
43 expect(parseBytes('1024TB 1024GB 1024MB')).to.equal(1127000492212224)
44 })
45
46 it('Should be invalid when given invalid value', async function () {
47 expect(parseBytes('6GB 1GB')).to.equal(6)
48 })
49})
50
51describe('Parse duration', function () {
52
53 it('Should pass when given valid value', async function () {
54 expect(parseDurationToMs(35)).to.equal(35)
55 expect(parseDurationToMs(-35)).to.equal(-35)
56 expect(parseDurationToMs('35 seconds')).to.equal(35 * 1000)
57 expect(parseDurationToMs('1 minute')).to.equal(60 * 1000)
58 expect(parseDurationToMs('1 hour')).to.equal(3600 * 1000)
59 expect(parseDurationToMs('35 hours')).to.equal(3600 * 35 * 1000)
60 })
61
62 it('Should be invalid when given invalid value', async function () {
63 expect(parseBytes('35m 5s')).to.equal(35)
64 })
65})
66
67describe('Object', function () {
68
69 it('Should convert an object', async function () {
70 function keyConverter (k: string) {
71 return snakeCase(k)
72 }
73
74 function valueConverter (v: any) {
75 if (validator.default.isNumeric(v + '')) return parseInt('' + v, 10)
76
77 return v
78 }
79
80 const obj = {
81 mySuperKey: 'hello',
82 mySuper2Key: '45',
83 mySuper3Key: {
84 mySuperSubKey: '15',
85 mySuperSub2Key: 'hello',
86 mySuperSub3Key: [ '1', 'hello', 2 ],
87 mySuperSub4Key: 4
88 },
89 mySuper4Key: 45,
90 toto: {
91 super_key: '15',
92 superKey2: 'hello'
93 },
94 super_key: {
95 superKey4: 15
96 }
97 }
98
99 const res = objectConverter(obj, keyConverter, valueConverter)
100
101 expect(res.my_super_key).to.equal('hello')
102 expect(res.my_super_2_key).to.equal(45)
103 expect(res.my_super_3_key.my_super_sub_key).to.equal(15)
104 expect(res.my_super_3_key.my_super_sub_2_key).to.equal('hello')
105 expect(res.my_super_3_key.my_super_sub_3_key).to.deep.equal([ 1, 'hello', 2 ])
106 expect(res.my_super_3_key.my_super_sub_4_key).to.equal(4)
107 expect(res.toto.super_key).to.equal(15)
108 expect(res.toto.super_key_2).to.equal('hello')
109 expect(res.super_key.super_key_4).to.equal(15)
110
111 // Immutable
112 expect(res.mySuperKey).to.be.undefined
113 expect(obj['my_super_key']).to.be.undefined
114 })
115})
116
117describe('Bitrate', function () {
118
119 it('Should get appropriate max bitrate', function () {
120 const tests = [
121 { resolution: VideoResolution.H_144P, ratio: 16 / 9, fps: 24, min: 200, max: 400 },
122 { resolution: VideoResolution.H_240P, ratio: 16 / 9, fps: 24, min: 600, max: 800 },
123 { resolution: VideoResolution.H_360P, ratio: 16 / 9, fps: 24, min: 1200, max: 1600 },
124 { resolution: VideoResolution.H_480P, ratio: 16 / 9, fps: 24, min: 2000, max: 2300 },
125 { resolution: VideoResolution.H_720P, ratio: 16 / 9, fps: 24, min: 4000, max: 4400 },
126 { resolution: VideoResolution.H_1080P, ratio: 16 / 9, fps: 24, min: 8000, max: 10000 },
127 { resolution: VideoResolution.H_4K, ratio: 16 / 9, fps: 24, min: 25000, max: 30000 }
128 ]
129
130 for (const test of tests) {
131 expect(getMaxTheoreticalBitrate(test)).to.be.above(test.min * 1000).and.below(test.max * 1000)
132 }
133 })
134
135 it('Should get appropriate average bitrate', function () {
136 const tests = [
137 { resolution: VideoResolution.H_144P, ratio: 16 / 9, fps: 24, min: 50, max: 300 },
138 { resolution: VideoResolution.H_240P, ratio: 16 / 9, fps: 24, min: 350, max: 450 },
139 { resolution: VideoResolution.H_360P, ratio: 16 / 9, fps: 24, min: 700, max: 900 },
140 { resolution: VideoResolution.H_480P, ratio: 16 / 9, fps: 24, min: 1100, max: 1300 },
141 { resolution: VideoResolution.H_720P, ratio: 16 / 9, fps: 24, min: 2300, max: 2500 },
142 { resolution: VideoResolution.H_1080P, ratio: 16 / 9, fps: 24, min: 4700, max: 5000 },
143 { resolution: VideoResolution.H_4K, ratio: 16 / 9, fps: 24, min: 15000, max: 17000 }
144 ]
145
146 for (const test of tests) {
147 expect(getAverageTheoreticalBitrate(test)).to.be.above(test.min * 1000).and.below(test.max * 1000)
148 }
149 })
150})
diff --git a/packages/tests/src/server-helpers/crypto.ts b/packages/tests/src/server-helpers/crypto.ts
new file mode 100644
index 000000000..4bf5b8a45
--- /dev/null
+++ b/packages/tests/src/server-helpers/crypto.ts
@@ -0,0 +1,33 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { decrypt, encrypt } from '@peertube/peertube-server/server/helpers/peertube-crypto.js'
5
6describe('Encrypt/Descrypt', function () {
7
8 it('Should encrypt and decrypt the string', async function () {
9 const secret = 'my_secret'
10 const str = 'my super string'
11
12 const encrypted = await encrypt(str, secret)
13 const decrypted = await decrypt(encrypted, secret)
14
15 expect(str).to.equal(decrypted)
16 })
17
18 it('Should not decrypt without the same secret', async function () {
19 const str = 'my super string'
20
21 const encrypted = await encrypt(str, 'my_secret')
22
23 let error = false
24
25 try {
26 await decrypt(encrypted, 'my_sicret')
27 } catch (err) {
28 error = true
29 }
30
31 expect(error).to.be.true
32 })
33})
diff --git a/packages/tests/src/server-helpers/dns.ts b/packages/tests/src/server-helpers/dns.ts
new file mode 100644
index 000000000..64e3112a2
--- /dev/null
+++ b/packages/tests/src/server-helpers/dns.ts
@@ -0,0 +1,16 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { isResolvingToUnicastOnly } from '@peertube/peertube-server/server/helpers/dns.js'
5
6describe('DNS helpers', function () {
7
8 it('Should correctly check unicast IPs', async function () {
9 expect(await isResolvingToUnicastOnly('cpy.re')).to.be.true
10 expect(await isResolvingToUnicastOnly('framasoft.org')).to.be.true
11 expect(await isResolvingToUnicastOnly('8.8.8.8')).to.be.true
12
13 expect(await isResolvingToUnicastOnly('127.0.0.1')).to.be.false
14 expect(await isResolvingToUnicastOnly('127.0.0.1.cpy.re')).to.be.false
15 })
16})
diff --git a/packages/tests/src/server-helpers/image.ts b/packages/tests/src/server-helpers/image.ts
new file mode 100644
index 000000000..34675d385
--- /dev/null
+++ b/packages/tests/src/server-helpers/image.ts
@@ -0,0 +1,97 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { remove } from 'fs-extra/esm'
5import { readFile } from 'fs/promises'
6import { join } from 'path'
7import { buildAbsoluteFixturePath, root } from '@peertube/peertube-node-utils'
8import { execPromise } from '@peertube/peertube-server/server/helpers/core-utils.js'
9import { processImage } from '@peertube/peertube-server/server/helpers/image-utils.js'
10
11async function checkBuffers (path1: string, path2: string, equals: boolean) {
12 const [ buf1, buf2 ] = await Promise.all([
13 readFile(path1),
14 readFile(path2)
15 ])
16
17 if (equals) {
18 expect(buf1.equals(buf2)).to.be.true
19 } else {
20 expect(buf1.equals(buf2)).to.be.false
21 }
22}
23
24async function hasTitleExif (path: string) {
25 const result = JSON.parse(await execPromise(`exiftool -json ${path}`))
26
27 return result[0]?.Title === 'should be removed'
28}
29
30describe('Image helpers', function () {
31 const imageDestDir = join(root(), 'test-images')
32
33 const imageDestJPG = join(imageDestDir, 'test.jpg')
34 const imageDestPNG = join(imageDestDir, 'test.png')
35
36 const thumbnailSize = { width: 280, height: 157 }
37
38 it('Should skip processing if the source image is okay', async function () {
39 const input = buildAbsoluteFixturePath('custom-thumbnail.jpg')
40 await processImage({ path: input, destination: imageDestJPG, newSize: thumbnailSize, keepOriginal: true })
41
42 await checkBuffers(input, imageDestJPG, true)
43 })
44
45 it('Should not skip processing if the source image does not have the appropriate extension', async function () {
46 const input = buildAbsoluteFixturePath('custom-thumbnail.png')
47 await processImage({ path: input, destination: imageDestJPG, newSize: thumbnailSize, keepOriginal: true })
48
49 await checkBuffers(input, imageDestJPG, false)
50 })
51
52 it('Should not skip processing if the source image does not have the appropriate size', async function () {
53 const input = buildAbsoluteFixturePath('custom-preview.jpg')
54 await processImage({ path: input, destination: imageDestJPG, newSize: thumbnailSize, keepOriginal: true })
55
56 await checkBuffers(input, imageDestJPG, false)
57 })
58
59 it('Should not skip processing if the source image does not have the appropriate size', async function () {
60 const input = buildAbsoluteFixturePath('custom-thumbnail-big.jpg')
61 await processImage({ path: input, destination: imageDestJPG, newSize: thumbnailSize, keepOriginal: true })
62
63 await checkBuffers(input, imageDestJPG, false)
64 })
65
66 it('Should strip exif for a jpg file that can not be copied', async function () {
67 const input = buildAbsoluteFixturePath('exif.jpg')
68 expect(await hasTitleExif(input)).to.be.true
69
70 await processImage({ path: input, destination: imageDestJPG, newSize: { width: 100, height: 100 }, keepOriginal: true })
71 await checkBuffers(input, imageDestJPG, false)
72
73 expect(await hasTitleExif(imageDestJPG)).to.be.false
74 })
75
76 it('Should strip exif for a jpg file that could be copied', async function () {
77 const input = buildAbsoluteFixturePath('exif.jpg')
78 expect(await hasTitleExif(input)).to.be.true
79
80 await processImage({ path: input, destination: imageDestJPG, newSize: thumbnailSize, keepOriginal: true })
81 await checkBuffers(input, imageDestJPG, false)
82
83 expect(await hasTitleExif(imageDestJPG)).to.be.false
84 })
85
86 it('Should strip exif for png', async function () {
87 const input = buildAbsoluteFixturePath('exif.png')
88 expect(await hasTitleExif(input)).to.be.true
89
90 await processImage({ path: input, destination: imageDestPNG, newSize: thumbnailSize, keepOriginal: true })
91 expect(await hasTitleExif(imageDestPNG)).to.be.false
92 })
93
94 after(async function () {
95 await remove(imageDestDir)
96 })
97})
diff --git a/packages/tests/src/server-helpers/index.ts b/packages/tests/src/server-helpers/index.ts
new file mode 100644
index 000000000..04a26560c
--- /dev/null
+++ b/packages/tests/src/server-helpers/index.ts
@@ -0,0 +1,10 @@
1import './activitypub.js'
2import './core-utils.js'
3import './crypto.js'
4import './dns.js'
5import './image.js'
6import './markdown.js'
7import './mentions.js'
8import './request.js'
9import './validator.js'
10import './version.js'
diff --git a/packages/tests/src/server-helpers/markdown.ts b/packages/tests/src/server-helpers/markdown.ts
new file mode 100644
index 000000000..96e3c34dc
--- /dev/null
+++ b/packages/tests/src/server-helpers/markdown.ts
@@ -0,0 +1,39 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { mdToOneLinePlainText } from '@peertube/peertube-server/server/helpers/markdown.js'
4import { expect } from 'chai'
5
6describe('Markdown helpers', function () {
7
8 describe('Plain text', function () {
9
10 it('Should convert a list to plain text', function () {
11 const result = mdToOneLinePlainText(`* list 1
12* list 2
13* list 3`)
14
15 expect(result).to.equal('list 1, list 2, list 3')
16 })
17
18 it('Should convert a list with indentation to plain text', function () {
19 const result = mdToOneLinePlainText(`Hello:
20 * list 1
21 * list 2
22 * list 3`)
23
24 expect(result).to.equal('Hello: list 1, list 2, list 3')
25 })
26
27 it('Should convert HTML to plain text', function () {
28 const result = mdToOneLinePlainText(`**Hello** <strong>coucou</strong>`)
29
30 expect(result).to.equal('Hello coucou')
31 })
32
33 it('Should convert tags to plain text', function () {
34 const result = mdToOneLinePlainText(`#déconversion\n#newage\n#histoire`)
35
36 expect(result).to.equal('#déconversion #newage #histoire')
37 })
38 })
39})
diff --git a/packages/tests/src/server-helpers/mentions.ts b/packages/tests/src/server-helpers/mentions.ts
new file mode 100644
index 000000000..153931d60
--- /dev/null
+++ b/packages/tests/src/server-helpers/mentions.ts
@@ -0,0 +1,17 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { extractMentions } from '@peertube/peertube-server/server/helpers/mentions.js'
5
6describe('Comment model', function () {
7 it('Should correctly extract mentions', async function () {
8 const text = '@florian @jean@localhost:9000 @flo @another@localhost:9000 @flo2@jean.com hello ' +
9 'email@localhost:9000 coucou.com no? @chocobozzz @chocobozzz @end'
10
11 const isOwned = true
12
13 const result = extractMentions(text, isOwned).sort((a, b) => a.localeCompare(b))
14
15 expect(result).to.deep.equal([ 'another', 'chocobozzz', 'end', 'flo', 'florian', 'jean' ])
16 })
17})
diff --git a/packages/tests/src/server-helpers/request.ts b/packages/tests/src/server-helpers/request.ts
new file mode 100644
index 000000000..f4b9af52e
--- /dev/null
+++ b/packages/tests/src/server-helpers/request.ts
@@ -0,0 +1,64 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { pathExists, remove } from 'fs-extra/esm'
5import { join } from 'path'
6import { wait } from '@peertube/peertube-core-utils'
7import { root } from '@peertube/peertube-node-utils'
8import { doRequest, doRequestAndSaveToFile } from '@peertube/peertube-server/server/helpers/requests.js'
9import { Mock429 } from '@tests/shared/mock-servers/mock-429.js'
10import { FIXTURE_URLS } from '@tests/shared/tests.js'
11
12describe('Request helpers', function () {
13 const destPath1 = join(root(), 'test-output-1.txt')
14 const destPath2 = join(root(), 'test-output-2.txt')
15
16 it('Should throw an error when the bytes limit is exceeded for request', async function () {
17 try {
18 await doRequest(FIXTURE_URLS.file4K, { bodyKBLimit: 3 })
19 } catch {
20 return
21 }
22
23 throw new Error('No error thrown by do request')
24 })
25
26 it('Should throw an error when the bytes limit is exceeded for request and save file', async function () {
27 try {
28 await doRequestAndSaveToFile(FIXTURE_URLS.file4K, destPath1, { bodyKBLimit: 3 })
29 } catch {
30
31 await wait(500)
32 expect(await pathExists(destPath1)).to.be.false
33 return
34 }
35
36 throw new Error('No error thrown by do request and save to file')
37 })
38
39 it('Should correctly retry on 429 error', async function () {
40 this.timeout(25000)
41
42 const mock = new Mock429()
43 const port = await mock.initialize()
44
45 const before = new Date().getTime()
46 await doRequest('http://127.0.0.1:' + port)
47
48 expect(new Date().getTime() - before).to.be.greaterThan(2000)
49
50 await mock.terminate()
51 })
52
53 it('Should succeed if the file is below the limit', async function () {
54 await doRequest(FIXTURE_URLS.file4K, { bodyKBLimit: 5 })
55 await doRequestAndSaveToFile(FIXTURE_URLS.file4K, destPath2, { bodyKBLimit: 5 })
56
57 expect(await pathExists(destPath2)).to.be.true
58 })
59
60 after(async function () {
61 await remove(destPath1)
62 await remove(destPath2)
63 })
64})
diff --git a/packages/tests/src/server-helpers/validator.ts b/packages/tests/src/server-helpers/validator.ts
new file mode 100644
index 000000000..792bd501c
--- /dev/null
+++ b/packages/tests/src/server-helpers/validator.ts
@@ -0,0 +1,35 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import {
5 isPluginStableOrUnstableVersionValid,
6 isPluginStableVersionValid
7} from '@peertube/peertube-server/server/helpers/custom-validators/plugins.js'
8
9describe('Validators', function () {
10
11 it('Should correctly check stable plugin versions', async function () {
12 expect(isPluginStableVersionValid('3.4.0')).to.be.true
13 expect(isPluginStableVersionValid('0.4.0')).to.be.true
14 expect(isPluginStableVersionValid('0.1.0')).to.be.true
15
16 expect(isPluginStableVersionValid('0.1.0-beta-1')).to.be.false
17 expect(isPluginStableVersionValid('hello')).to.be.false
18 expect(isPluginStableVersionValid('0.x.a')).to.be.false
19 })
20
21 it('Should correctly check unstable plugin versions', async function () {
22 expect(isPluginStableOrUnstableVersionValid('3.4.0')).to.be.true
23 expect(isPluginStableOrUnstableVersionValid('0.4.0')).to.be.true
24 expect(isPluginStableOrUnstableVersionValid('0.1.0')).to.be.true
25
26 expect(isPluginStableOrUnstableVersionValid('0.1.0-beta.1')).to.be.true
27 expect(isPluginStableOrUnstableVersionValid('0.1.0-alpha.45')).to.be.true
28 expect(isPluginStableOrUnstableVersionValid('0.1.0-rc.45')).to.be.true
29
30 expect(isPluginStableOrUnstableVersionValid('hello')).to.be.false
31 expect(isPluginStableOrUnstableVersionValid('0.x.a')).to.be.false
32 expect(isPluginStableOrUnstableVersionValid('0.1.0-rc-45')).to.be.false
33 expect(isPluginStableOrUnstableVersionValid('0.1.0-rc.45d')).to.be.false
34 })
35})
diff --git a/packages/tests/src/server-helpers/version.ts b/packages/tests/src/server-helpers/version.ts
new file mode 100644
index 000000000..76892d1e7
--- /dev/null
+++ b/packages/tests/src/server-helpers/version.ts
@@ -0,0 +1,36 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { compareSemVer } from '@peertube/peertube-core-utils'
5
6describe('Version', function () {
7
8 it('Should correctly compare two stable versions', async function () {
9 expect(compareSemVer('3.4.0', '3.5.0')).to.be.below(0)
10 expect(compareSemVer('3.5.0', '3.4.0')).to.be.above(0)
11
12 expect(compareSemVer('3.4.0', '4.1.0')).to.be.below(0)
13 expect(compareSemVer('4.1.0', '3.4.0')).to.be.above(0)
14
15 expect(compareSemVer('3.4.0', '3.4.1')).to.be.below(0)
16 expect(compareSemVer('3.4.1', '3.4.0')).to.be.above(0)
17 })
18
19 it('Should correctly compare two unstable version', async function () {
20 expect(compareSemVer('3.4.0-alpha', '3.4.0-beta.1')).to.be.below(0)
21 expect(compareSemVer('3.4.0-alpha.1', '3.4.0-beta.1')).to.be.below(0)
22 expect(compareSemVer('3.4.0-beta.1', '3.4.0-beta.2')).to.be.below(0)
23 expect(compareSemVer('3.4.0-beta.1', '3.5.0-alpha.1')).to.be.below(0)
24
25 expect(compareSemVer('3.4.0-alpha.1', '3.4.0-nightly.4')).to.be.below(0)
26 expect(compareSemVer('3.4.0-nightly.3', '3.4.0-nightly.4')).to.be.below(0)
27 expect(compareSemVer('3.3.0-nightly.5', '3.4.0-nightly.4')).to.be.below(0)
28 })
29
30 it('Should correctly compare a stable and unstable versions', async function () {
31 expect(compareSemVer('3.4.0', '3.4.1-beta.1')).to.be.below(0)
32 expect(compareSemVer('3.4.0-beta.1', '3.4.0-beta.2')).to.be.below(0)
33 expect(compareSemVer('3.4.0-beta.1', '3.4.0')).to.be.below(0)
34 expect(compareSemVer('3.4.0-nightly.4', '3.4.0')).to.be.below(0)
35 })
36})