aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/extra-utils/miscs
diff options
context:
space:
mode:
Diffstat (limited to 'shared/extra-utils/miscs')
-rw-r--r--shared/extra-utils/miscs/email-child-process.js27
-rw-r--r--shared/extra-utils/miscs/email.ts68
-rw-r--r--shared/extra-utils/miscs/miscs.ts101
-rw-r--r--shared/extra-utils/miscs/sql.ts80
-rw-r--r--shared/extra-utils/miscs/stubs.ts14
5 files changed, 290 insertions, 0 deletions
diff --git a/shared/extra-utils/miscs/email-child-process.js b/shared/extra-utils/miscs/email-child-process.js
new file mode 100644
index 000000000..088a5a08c
--- /dev/null
+++ b/shared/extra-utils/miscs/email-child-process.js
@@ -0,0 +1,27 @@
1const MailDev = require('maildev')
2
3// must run maildev as forked ChildProcess
4// failed instantiation stops main process with exit code 0
5process.on('message', (msg) => {
6 if (msg.start) {
7 const maildev = new MailDev({
8 ip: '127.0.0.1',
9 smtp: msg.port,
10 disableWeb: true,
11 silent: true
12 })
13
14 maildev.on('new', email => {
15 process.send({ email })
16 })
17
18 maildev.listen(err => {
19 if (err) {
20 // cannot send as Error object
21 return process.send({ err: err.message })
22 }
23
24 return process.send({ err: null })
25 })
26 }
27})
diff --git a/shared/extra-utils/miscs/email.ts b/shared/extra-utils/miscs/email.ts
new file mode 100644
index 000000000..b2a1093da
--- /dev/null
+++ b/shared/extra-utils/miscs/email.ts
@@ -0,0 +1,68 @@
1import { ChildProcess, fork } from 'child_process'
2import { randomInt } from '../../core-utils/miscs/miscs'
3import { parallelTests } from '../server/servers'
4
5class MockSmtpServer {
6
7 private static instance: MockSmtpServer
8 private started = false
9 private emailChildProcess: ChildProcess
10 private emails: object[]
11
12 private constructor () {
13 this.emailChildProcess = fork(`${__dirname}/email-child-process`, [])
14
15 this.emailChildProcess.on('message', (msg) => {
16 if (msg.email) {
17 return this.emails.push(msg.email)
18 }
19 })
20
21 process.on('exit', () => this.kill())
22 }
23
24 collectEmails (emailsCollection: object[]) {
25 return new Promise<number>((res, rej) => {
26 const port = parallelTests() ? randomInt(1000, 2000) : 1025
27
28 if (this.started) {
29 this.emails = emailsCollection
30 return res()
31 }
32
33 // ensure maildev isn't started until
34 // unexpected exit can be reported to test runner
35 this.emailChildProcess.send({ start: true, port })
36 this.emailChildProcess.on('exit', () => {
37 return rej(new Error('maildev exited unexpectedly, confirm port not in use'))
38 })
39 this.emailChildProcess.on('message', (msg) => {
40 if (msg.err) {
41 return rej(new Error(msg.err))
42 }
43 this.started = true
44 this.emails = emailsCollection
45 return res(port)
46 })
47 })
48 }
49
50 kill () {
51 if (!this.emailChildProcess) return
52
53 process.kill(this.emailChildProcess.pid)
54
55 this.emailChildProcess = null
56 MockSmtpServer.instance = null
57 }
58
59 static get Instance () {
60 return this.instance || (this.instance = new this())
61 }
62}
63
64// ---------------------------------------------------------------------------
65
66export {
67 MockSmtpServer
68}
diff --git a/shared/extra-utils/miscs/miscs.ts b/shared/extra-utils/miscs/miscs.ts
new file mode 100644
index 000000000..d1ffb7be4
--- /dev/null
+++ b/shared/extra-utils/miscs/miscs.ts
@@ -0,0 +1,101 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import { isAbsolute, join } from 'path'
5import * as request from 'supertest'
6import * as WebTorrent from 'webtorrent'
7import { pathExists, readFile } from 'fs-extra'
8import * as ffmpeg from 'fluent-ffmpeg'
9
10const expect = chai.expect
11let webtorrent = new WebTorrent()
12
13function immutableAssign <T, U> (target: T, source: U) {
14 return Object.assign<{}, T, U>({}, target, source)
15}
16
17 // Default interval -> 5 minutes
18function dateIsValid (dateString: string, interval = 300000) {
19 const dateToCheck = new Date(dateString)
20 const now = new Date()
21
22 return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
23}
24
25function wait (milliseconds: number) {
26 return new Promise(resolve => setTimeout(resolve, milliseconds))
27}
28
29function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
30 if (refreshWebTorrent === true) webtorrent = new WebTorrent()
31
32 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
33}
34
35function root () {
36 // We are in /miscs
37 return join(__dirname, '..', '..', '..')
38}
39
40async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
41 const res = await request(url)
42 .get(imagePath)
43 .expect(200)
44
45 const body = res.body
46
47 const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
48 const minLength = body.length - ((20 * body.length) / 100)
49 const maxLength = body.length + ((20 * body.length) / 100)
50
51 expect(data.length).to.be.above(minLength)
52 expect(data.length).to.be.below(maxLength)
53}
54
55function buildAbsoluteFixturePath (path: string, customTravisPath = false) {
56 if (isAbsolute(path)) {
57 return path
58 }
59
60 if (customTravisPath && process.env.TRAVIS) return join(process.env.HOME, 'fixtures', path)
61
62 return join(root(), 'server', 'tests', 'fixtures', path)
63}
64
65async function generateHighBitrateVideo () {
66 const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
67
68 const exists = await pathExists(tempFixturePath)
69 if (!exists) {
70
71 // Generate a random, high bitrate video on the fly, so we don't have to include
72 // a large file in the repo. The video needs to have a certain minimum length so
73 // that FFmpeg properly applies bitrate limits.
74 // https://stackoverflow.com/a/15795112
75 return new Promise<string>(async (res, rej) => {
76 ffmpeg()
77 .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
78 .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
79 .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
80 .output(tempFixturePath)
81 .on('error', rej)
82 .on('end', () => res(tempFixturePath))
83 .run()
84 })
85 }
86
87 return tempFixturePath
88}
89
90// ---------------------------------------------------------------------------
91
92export {
93 dateIsValid,
94 wait,
95 webtorrentAdd,
96 immutableAssign,
97 testImage,
98 buildAbsoluteFixturePath,
99 root,
100 generateHighBitrateVideo
101}
diff --git a/shared/extra-utils/miscs/sql.ts b/shared/extra-utils/miscs/sql.ts
new file mode 100644
index 000000000..3cfae5c23
--- /dev/null
+++ b/shared/extra-utils/miscs/sql.ts
@@ -0,0 +1,80 @@
1import { QueryTypes, Sequelize } from 'sequelize'
2
3let sequelizes: { [ id: number ]: Sequelize } = {}
4
5function getSequelize (serverNumber: number) {
6 if (sequelizes[serverNumber]) return sequelizes[serverNumber]
7
8 const dbname = 'peertube_test' + serverNumber
9 const username = 'peertube'
10 const password = 'peertube'
11 const host = 'localhost'
12 const port = 5432
13
14 const seq = new Sequelize(dbname, username, password, {
15 dialect: 'postgres',
16 host,
17 port,
18 logging: false
19 })
20
21 sequelizes[serverNumber] = seq
22
23 return seq
24}
25
26function setActorField (serverNumber: number, to: string, field: string, value: string) {
27 const seq = getSequelize(serverNumber)
28
29 const options = { type: QueryTypes.UPDATE }
30
31 return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
32}
33
34function setVideoField (serverNumber: number, uuid: string, field: string, value: string) {
35 const seq = getSequelize(serverNumber)
36
37 const options = { type: QueryTypes.UPDATE }
38
39 return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
40}
41
42function setPlaylistField (serverNumber: number, uuid: string, field: string, value: string) {
43 const seq = getSequelize(serverNumber)
44
45 const options = { type: QueryTypes.UPDATE }
46
47 return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
48}
49
50async function countVideoViewsOf (serverNumber: number, uuid: string) {
51 const seq = getSequelize(serverNumber)
52
53 // tslint:disable
54 const query = `SELECT SUM("videoView"."views") AS "total" FROM "videoView" INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
55
56 const options = { type: QueryTypes.SELECT as QueryTypes.SELECT }
57 const [ { total } ] = await seq.query<{ total: number }>(query, options)
58
59 if (!total) return 0
60
61 // FIXME: check if we really need parseInt
62 return parseInt(total + '', 10)
63}
64
65async function closeAllSequelize (servers: any[]) {
66 for (let i = 1; i <= servers.length; i++) {
67 if (sequelizes[ i ]) {
68 await sequelizes[ i ].close()
69 delete sequelizes[ i ]
70 }
71 }
72}
73
74export {
75 setVideoField,
76 setPlaylistField,
77 setActorField,
78 countVideoViewsOf,
79 closeAllSequelize
80}
diff --git a/shared/extra-utils/miscs/stubs.ts b/shared/extra-utils/miscs/stubs.ts
new file mode 100644
index 000000000..d1eb0e3b2
--- /dev/null
+++ b/shared/extra-utils/miscs/stubs.ts
@@ -0,0 +1,14 @@
1function buildRequestStub (): any {
2 return { }
3}
4
5function buildResponseStub (): any {
6 return {
7 locals: {}
8 }
9}
10
11export {
12 buildResponseStub,
13 buildRequestStub
14}