mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-05 10:49:28 +02:00
Move test functions outside extra-utils
This commit is contained in:
parent
bf54587a3e
commit
c55e3d7227
202 changed files with 798 additions and 895 deletions
|
@ -4,7 +4,6 @@ export * from './custom-pages'
|
|||
export * from './feeds'
|
||||
export * from './logs'
|
||||
export * from './miscs'
|
||||
export * from './mock-servers'
|
||||
export * from './moderation'
|
||||
export * from './overviews'
|
||||
export * from './requests'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { HttpStatusCode } from '@shared/models'
|
||||
import { LogLevel } from '../../models/server/log-level.type'
|
||||
import { HttpStatusCode, LogLevel } from '@shared/models'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
export class LogsCommand extends AbstractCommand {
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { pathExists, readFile } from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
import { root } from '@shared/core-utils'
|
||||
import { HttpStatusCode } from '@shared/models'
|
||||
import { makeGetRequest } from '../requests'
|
||||
import { PeerTubeServer } from '../server'
|
||||
|
||||
// Default interval -> 5 minutes
|
||||
function dateIsValid (dateString: string, interval = 300000) {
|
||||
const dateToCheck = new Date(dateString)
|
||||
const now = new Date()
|
||||
|
||||
return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
|
||||
}
|
||||
|
||||
function expectStartWith (str: string, start: string) {
|
||||
expect(str.startsWith(start), `${str} does not start with ${start}`).to.be.true
|
||||
}
|
||||
|
||||
async function expectLogDoesNotContain (server: PeerTubeServer, str: string) {
|
||||
const content = await server.servers.getLogContent()
|
||||
|
||||
expect(content.toString()).to.not.contain(str)
|
||||
}
|
||||
|
||||
async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
|
||||
const res = await makeGetRequest({
|
||||
url,
|
||||
path: imagePath,
|
||||
expectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
|
||||
const body = res.body
|
||||
|
||||
const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
|
||||
const minLength = body.length - ((30 * body.length) / 100)
|
||||
const maxLength = body.length + ((30 * body.length) / 100)
|
||||
|
||||
expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
|
||||
expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
|
||||
}
|
||||
|
||||
async function testFileExistsOrNot (server: PeerTubeServer, directory: string, filePath: string, exist: boolean) {
|
||||
const base = server.servers.buildDirectory(directory)
|
||||
|
||||
expect(await pathExists(join(base, filePath))).to.equal(exist)
|
||||
}
|
||||
|
||||
export {
|
||||
dateIsValid,
|
||||
testImage,
|
||||
expectLogDoesNotContain,
|
||||
testFileExistsOrNot,
|
||||
expectStartWith
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
import { expect } from 'chai'
|
||||
import ffmpeg from 'fluent-ffmpeg'
|
||||
import { ensureDir, pathExists } from 'fs-extra'
|
||||
import { dirname } from 'path'
|
||||
import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '@shared/extra-utils/ffprobe'
|
||||
import { getMaxBitrate } from '@shared/core-utils'
|
||||
import { buildAbsoluteFixturePath } from './tests'
|
||||
|
||||
async function ensureHasTooBigBitrate (fixturePath: string) {
|
||||
const bitrate = await getVideoFileBitrate(fixturePath)
|
||||
const dataResolution = await getVideoFileResolution(fixturePath)
|
||||
const fps = await getVideoFileFPS(fixturePath)
|
||||
|
||||
const maxBitrate = getMaxBitrate({ ...dataResolution, fps })
|
||||
expect(bitrate).to.be.above(maxBitrate)
|
||||
}
|
||||
|
||||
async function generateHighBitrateVideo () {
|
||||
const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
|
||||
|
||||
await ensureDir(dirname(tempFixturePath))
|
||||
|
||||
const exists = await pathExists(tempFixturePath)
|
||||
if (!exists) {
|
||||
console.log('Generating high bitrate video.')
|
||||
|
||||
// Generate a random, high bitrate video on the fly, so we don't have to include
|
||||
// a large file in the repo. The video needs to have a certain minimum length so
|
||||
// that FFmpeg properly applies bitrate limits.
|
||||
// https://stackoverflow.com/a/15795112
|
||||
return new Promise<string>((res, rej) => {
|
||||
ffmpeg()
|
||||
.outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
|
||||
.outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
|
||||
.outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
|
||||
.output(tempFixturePath)
|
||||
.on('error', rej)
|
||||
.on('end', () => res(tempFixturePath))
|
||||
.run()
|
||||
})
|
||||
}
|
||||
|
||||
await ensureHasTooBigBitrate(tempFixturePath)
|
||||
|
||||
return tempFixturePath
|
||||
}
|
||||
|
||||
async function generateVideoWithFramerate (fps = 60) {
|
||||
const tempFixturePath = buildAbsoluteFixturePath(`video_${fps}fps.mp4`, true)
|
||||
|
||||
await ensureDir(dirname(tempFixturePath))
|
||||
|
||||
const exists = await pathExists(tempFixturePath)
|
||||
if (!exists) {
|
||||
console.log('Generating video with framerate %d.', fps)
|
||||
|
||||
return new Promise<string>((res, rej) => {
|
||||
ffmpeg()
|
||||
.outputOptions([ '-f rawvideo', '-video_size 1280x720', '-i /dev/urandom' ])
|
||||
.outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
|
||||
.outputOptions([ `-r ${fps}` ])
|
||||
.output(tempFixturePath)
|
||||
.on('error', rej)
|
||||
.on('end', () => res(tempFixturePath))
|
||||
.run()
|
||||
})
|
||||
}
|
||||
|
||||
return tempFixturePath
|
||||
}
|
||||
|
||||
export {
|
||||
generateHighBitrateVideo,
|
||||
generateVideoWithFramerate
|
||||
}
|
|
@ -1,5 +1,2 @@
|
|||
export * from './checks'
|
||||
export * from './generate'
|
||||
export * from './sql-command'
|
||||
export * from './tests'
|
||||
export * from './webtorrent'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { QueryTypes, Sequelize } from 'sequelize'
|
||||
import { AbstractCommand } from '../shared/abstract-command'
|
||||
import { AbstractCommand } from '../shared'
|
||||
|
||||
export class SQLCommand extends AbstractCommand {
|
||||
private sequelize: Sequelize
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
import { stat } from 'fs-extra'
|
||||
import { basename, isAbsolute, join, resolve } from 'path'
|
||||
|
||||
const FIXTURE_URLS = {
|
||||
peertube_long: 'https://peertube2.cpy.re/videos/watch/122d093a-1ede-43bd-bd34-59d2931ffc5e',
|
||||
peertube_short: 'https://peertube2.cpy.re/w/3fbif9S3WmtTP8gGsC5HBd',
|
||||
|
||||
youtube: 'https://www.youtube.com/watch?v=msX3jv1XdvM',
|
||||
|
||||
/**
|
||||
* The video is used to check format-selection correctness wrt. HDR,
|
||||
* which brings its own set of oddities outside of a MediaSource.
|
||||
*
|
||||
* The video needs to have the following format_ids:
|
||||
* (which you can check by using `youtube-dl <url> -F`):
|
||||
* - (webm vp9)
|
||||
* - (mp4 avc1)
|
||||
* - (webm vp9.2 HDR)
|
||||
*/
|
||||
youtubeHDR: 'https://www.youtube.com/watch?v=RQgnBB9z_N4',
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
magnet: 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Flazy-static%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4',
|
||||
|
||||
badVideo: 'https://download.cpy.re/peertube/bad_video.mp4',
|
||||
goodVideo: 'https://download.cpy.re/peertube/good_video.mp4',
|
||||
goodVideo720: 'https://download.cpy.re/peertube/good_video_720.mp4',
|
||||
|
||||
file4K: 'https://download.cpy.re/peertube/4k_file.txt'
|
||||
}
|
||||
|
||||
function parallelTests () {
|
||||
return process.env.MOCHA_PARALLEL === 'true'
|
||||
}
|
||||
|
||||
function isGithubCI () {
|
||||
return !!process.env.GITHUB_WORKSPACE
|
||||
}
|
||||
|
||||
function areHttpImportTestsDisabled () {
|
||||
const disabled = process.env.DISABLE_HTTP_IMPORT_TESTS === 'true'
|
||||
|
||||
if (disabled) console.log('DISABLE_HTTP_IMPORT_TESTS env set to "true" so import tests are disabled')
|
||||
|
||||
return disabled
|
||||
}
|
||||
|
||||
function areObjectStorageTestsDisabled () {
|
||||
const disabled = process.env.ENABLE_OBJECT_STORAGE_TESTS !== 'true'
|
||||
|
||||
if (disabled) console.log('ENABLE_OBJECT_STORAGE_TESTS env is not set to "true" so object storage tests are disabled')
|
||||
|
||||
return disabled
|
||||
}
|
||||
|
||||
function buildAbsoluteFixturePath (path: string, customCIPath = false) {
|
||||
if (isAbsolute(path)) return path
|
||||
|
||||
if (customCIPath && process.env.GITHUB_WORKSPACE) {
|
||||
return join(process.env.GITHUB_WORKSPACE, 'fixtures', path)
|
||||
}
|
||||
|
||||
return join(root(), 'server', 'tests', 'fixtures', path)
|
||||
}
|
||||
|
||||
function root () {
|
||||
// We are in /miscs
|
||||
let root = join(__dirname, '..', '..', '..')
|
||||
|
||||
if (basename(root) === 'dist') root = resolve(root, '..')
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
function wait (milliseconds: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds))
|
||||
}
|
||||
|
||||
async function getFileSize (path: string) {
|
||||
const stats = await stat(path)
|
||||
|
||||
return stats.size
|
||||
}
|
||||
|
||||
function buildRequestStub (): any {
|
||||
return { }
|
||||
}
|
||||
|
||||
export {
|
||||
FIXTURE_URLS,
|
||||
|
||||
parallelTests,
|
||||
isGithubCI,
|
||||
areHttpImportTestsDisabled,
|
||||
buildAbsoluteFixturePath,
|
||||
getFileSize,
|
||||
buildRequestStub,
|
||||
areObjectStorageTestsDisabled,
|
||||
wait,
|
||||
root
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
export * from './mock-email'
|
||||
export * from './mock-instances-index'
|
||||
export * from './mock-joinpeertube-versions'
|
||||
export * from './mock-plugin-blocklist'
|
||||
export * from './mock-object-storage'
|
|
@ -1,33 +0,0 @@
|
|||
import express from 'express'
|
||||
import { Server } from 'http'
|
||||
import { getPort, randomListen, terminateServer } from './utils'
|
||||
|
||||
export class Mock429 {
|
||||
private server: Server
|
||||
private responseSent = false
|
||||
|
||||
async initialize () {
|
||||
const app = express()
|
||||
|
||||
app.get('/', (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
|
||||
if (!this.responseSent) {
|
||||
this.responseSent = true
|
||||
|
||||
// Retry after 5 seconds
|
||||
res.header('retry-after', '2')
|
||||
return res.sendStatus(429)
|
||||
}
|
||||
|
||||
return res.sendStatus(200)
|
||||
})
|
||||
|
||||
this.server = await randomListen(app)
|
||||
|
||||
return getPort(this.server)
|
||||
}
|
||||
|
||||
terminate () {
|
||||
return terminateServer(this.server)
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
import { ChildProcess } from 'child_process'
|
||||
import MailDev from '@peertube/maildev'
|
||||
import { randomInt } from '@shared/core-utils'
|
||||
import { parallelTests } from '../miscs'
|
||||
|
||||
class MockSmtpServer {
|
||||
|
||||
private static instance: MockSmtpServer
|
||||
private started = false
|
||||
private emailChildProcess: ChildProcess
|
||||
private emails: object[]
|
||||
|
||||
private constructor () { }
|
||||
|
||||
collectEmails (emailsCollection: object[]) {
|
||||
return new Promise<number>((res, rej) => {
|
||||
const port = parallelTests() ? randomInt(1000, 2000) : 1025
|
||||
this.emails = emailsCollection
|
||||
|
||||
if (this.started) {
|
||||
return res(undefined)
|
||||
}
|
||||
|
||||
const maildev = new MailDev({
|
||||
ip: '127.0.0.1',
|
||||
smtp: port,
|
||||
disableWeb: true,
|
||||
silent: true
|
||||
})
|
||||
|
||||
maildev.on('new', email => {
|
||||
this.emails.push(email)
|
||||
})
|
||||
|
||||
maildev.listen(err => {
|
||||
if (err) return rej(err)
|
||||
|
||||
this.started = true
|
||||
|
||||
return res(port)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
kill () {
|
||||
if (!this.emailChildProcess) return
|
||||
|
||||
process.kill(this.emailChildProcess.pid)
|
||||
|
||||
this.emailChildProcess = null
|
||||
MockSmtpServer.instance = null
|
||||
}
|
||||
|
||||
static get Instance () {
|
||||
return this.instance || (this.instance = new this())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
MockSmtpServer
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
import express from 'express'
|
||||
import { Server } from 'http'
|
||||
import { getPort, randomListen, terminateServer } from './utils'
|
||||
|
||||
export class MockInstancesIndex {
|
||||
private server: Server
|
||||
|
||||
private readonly indexInstances: { host: string, createdAt: string }[] = []
|
||||
|
||||
async initialize () {
|
||||
const app = express()
|
||||
|
||||
app.use('/', (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (process.env.DEBUG) console.log('Receiving request on mocked server %s.', req.url)
|
||||
|
||||
return next()
|
||||
})
|
||||
|
||||
app.get('/api/v1/instances/hosts', (req: express.Request, res: express.Response) => {
|
||||
const since = req.query.since
|
||||
|
||||
const filtered = this.indexInstances.filter(i => {
|
||||
if (!since) return true
|
||||
|
||||
return i.createdAt > since
|
||||
})
|
||||
|
||||
return res.json({
|
||||
total: filtered.length,
|
||||
data: filtered
|
||||
})
|
||||
})
|
||||
|
||||
this.server = await randomListen(app)
|
||||
|
||||
return getPort(this.server)
|
||||
}
|
||||
|
||||
addInstance (host: string) {
|
||||
this.indexInstances.push({ host, createdAt: new Date().toISOString() })
|
||||
}
|
||||
|
||||
terminate () {
|
||||
return terminateServer(this.server)
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import express from 'express'
|
||||
import { Server } from 'http'
|
||||
import { getPort, randomListen } from './utils'
|
||||
|
||||
export class MockJoinPeerTubeVersions {
|
||||
private server: Server
|
||||
private latestVersion: string
|
||||
|
||||
async initialize () {
|
||||
const app = express()
|
||||
|
||||
app.use('/', (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
if (process.env.DEBUG) console.log('Receiving request on mocked server %s.', req.url)
|
||||
|
||||
return next()
|
||||
})
|
||||
|
||||
app.get('/versions.json', (req: express.Request, res: express.Response) => {
|
||||
return res.json({
|
||||
peertube: {
|
||||
latestVersion: this.latestVersion
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.server = await randomListen(app)
|
||||
|
||||
return getPort(this.server)
|
||||
}
|
||||
|
||||
setLatestVersion (latestVersion: string) {
|
||||
this.latestVersion = latestVersion
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
import express from 'express'
|
||||
import got, { RequestError } from 'got'
|
||||
import { Server } from 'http'
|
||||
import { pipeline } from 'stream'
|
||||
import { ObjectStorageCommand } from '../server'
|
||||
import { getPort, randomListen, terminateServer } from './utils'
|
||||
|
||||
export class MockObjectStorage {
|
||||
private server: Server
|
||||
|
||||
async initialize () {
|
||||
const app = express()
|
||||
|
||||
app.get('/:bucketName/:path(*)', (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
const url = `http://${req.params.bucketName}.${ObjectStorageCommand.getEndpointHost()}/${req.params.path}`
|
||||
|
||||
if (process.env.DEBUG) {
|
||||
console.log('Receiving request on mocked server %s.', req.url)
|
||||
console.log('Proxifying request to %s', url)
|
||||
}
|
||||
|
||||
return pipeline(
|
||||
got.stream(url, { throwHttpErrors: false }),
|
||||
res,
|
||||
(err: RequestError) => {
|
||||
if (!err) return
|
||||
|
||||
console.error('Pipeline failed.', err)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
this.server = await randomListen(app)
|
||||
|
||||
return getPort(this.server)
|
||||
}
|
||||
|
||||
terminate () {
|
||||
return terminateServer(this.server)
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
import express, { Request, Response } from 'express'
|
||||
import { Server } from 'http'
|
||||
import { getPort, randomListen, terminateServer } from './utils'
|
||||
|
||||
type BlocklistResponse = {
|
||||
data: {
|
||||
value: string
|
||||
action?: 'add' | 'remove'
|
||||
updatedAt?: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export class MockBlocklist {
|
||||
private body: BlocklistResponse
|
||||
private server: Server
|
||||
|
||||
async initialize () {
|
||||
const app = express()
|
||||
|
||||
app.get('/blocklist', (req: Request, res: Response) => {
|
||||
return res.json(this.body)
|
||||
})
|
||||
|
||||
this.server = await randomListen(app)
|
||||
|
||||
return getPort(this.server)
|
||||
}
|
||||
|
||||
replace (body: BlocklistResponse) {
|
||||
this.body = body
|
||||
}
|
||||
|
||||
terminate () {
|
||||
return terminateServer(this.server)
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
|
||||
import { createServer, Server } from 'http'
|
||||
import proxy from 'proxy'
|
||||
import { getPort, terminateServer } from './utils'
|
||||
|
||||
class MockProxy {
|
||||
private server: Server
|
||||
|
||||
initialize () {
|
||||
return new Promise<number>(res => {
|
||||
this.server = proxy(createServer())
|
||||
this.server.listen(0, () => res(getPort(this.server)))
|
||||
})
|
||||
}
|
||||
|
||||
terminate () {
|
||||
return terminateServer(this.server)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
MockProxy
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import { Express } from 'express'
|
||||
import { Server } from 'http'
|
||||
import { AddressInfo } from 'net'
|
||||
|
||||
function randomListen (app: Express) {
|
||||
return new Promise<Server>(res => {
|
||||
const server = app.listen(0, () => res(server))
|
||||
})
|
||||
}
|
||||
|
||||
function getPort (server: Server) {
|
||||
const address = server.address() as AddressInfo
|
||||
|
||||
return address.port
|
||||
}
|
||||
|
||||
function terminateServer (server: Server) {
|
||||
if (!server) return Promise.resolve()
|
||||
|
||||
return new Promise<void>((res, rej) => {
|
||||
server.close(err => {
|
||||
if (err) return rej(err)
|
||||
|
||||
return res()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
randomListen,
|
||||
getPort,
|
||||
terminateServer
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import { HttpStatusCode } from '@shared/models'
|
||||
import { makeGetRequest } from './requests'
|
||||
|
||||
function checkBadStartPagination (url: string, path: string, token?: string, query = {}) {
|
||||
return makeGetRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
query: { ...query, start: 'hello' },
|
||||
expectedStatus: HttpStatusCode.BAD_REQUEST_400
|
||||
})
|
||||
}
|
||||
|
||||
async function checkBadCountPagination (url: string, path: string, token?: string, query = {}) {
|
||||
await makeGetRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
query: { ...query, count: 'hello' },
|
||||
expectedStatus: HttpStatusCode.BAD_REQUEST_400
|
||||
})
|
||||
|
||||
await makeGetRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
query: { ...query, count: 2000 },
|
||||
expectedStatus: HttpStatusCode.BAD_REQUEST_400
|
||||
})
|
||||
}
|
||||
|
||||
function checkBadSortPagination (url: string, path: string, token?: string, query = {}) {
|
||||
return makeGetRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
query: { ...query, sort: 'hello' },
|
||||
expectedStatus: HttpStatusCode.BAD_REQUEST_400
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
checkBadStartPagination,
|
||||
checkBadCountPagination,
|
||||
checkBadSortPagination
|
||||
}
|
|
@ -1,3 +1 @@
|
|||
// Don't include activitypub that import stuff from server
|
||||
export * from './check-api-params'
|
||||
export * from './requests'
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
import { decode } from 'querystring'
|
||||
import request from 'supertest'
|
||||
import { URL } from 'url'
|
||||
import { buildAbsoluteFixturePath } from '@shared/core-utils'
|
||||
import { HttpStatusCode } from '@shared/models'
|
||||
import { buildAbsoluteFixturePath } from '../miscs/tests'
|
||||
|
||||
export type CommonRequestParams = {
|
||||
url: string
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { merge } from 'lodash'
|
||||
import { About, CustomConfig, HttpStatusCode, ServerConfig } from '@shared/models'
|
||||
import { DeepPartial } from '@shared/typescript-utils'
|
||||
import { About, HttpStatusCode, ServerConfig } from '@shared/models'
|
||||
import { CustomConfig } from '../../models/server/custom-config.model'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared/abstract-command'
|
||||
|
||||
export class ConfigCommand extends AbstractCommand {
|
||||
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { pathExists, readdir } from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
import { root } from '@shared/core-utils'
|
||||
import { PeerTubeServer } from './server'
|
||||
|
||||
async function checkTmpIsEmpty (server: PeerTubeServer) {
|
||||
await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ])
|
||||
|
||||
if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) {
|
||||
await checkDirectoryIsEmpty(server, 'tmp/hls')
|
||||
}
|
||||
}
|
||||
|
||||
async function checkDirectoryIsEmpty (server: PeerTubeServer, directory: string, exceptions: string[] = []) {
|
||||
const testDirectory = 'test' + server.internalServerNumber
|
||||
|
||||
const directoryPath = join(root(), testDirectory, directory)
|
||||
|
||||
const directoryExists = await pathExists(directoryPath)
|
||||
expect(directoryExists).to.be.true
|
||||
|
||||
const files = await readdir(directoryPath)
|
||||
const filtered = files.filter(f => exceptions.includes(f) === false)
|
||||
|
||||
expect(filtered).to.have.lengthOf(0)
|
||||
}
|
||||
|
||||
export {
|
||||
checkTmpIsEmpty,
|
||||
checkDirectoryIsEmpty
|
||||
}
|
|
@ -1,17 +1,14 @@
|
|||
export * from './config-command'
|
||||
export * from './contact-form-command'
|
||||
export * from './debug-command'
|
||||
export * from './directories'
|
||||
export * from './follows-command'
|
||||
export * from './follows'
|
||||
export * from './jobs'
|
||||
export * from './jobs-command'
|
||||
export * from './object-storage-command'
|
||||
export * from './plugins-command'
|
||||
export * from './plugins'
|
||||
export * from './redundancy-command'
|
||||
export * from './server'
|
||||
export * from './servers-command'
|
||||
export * from './servers'
|
||||
export * from './stats-command'
|
||||
export * from './tracker'
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { pick } from '@shared/core-utils'
|
||||
import { HttpStatusCode } from '@shared/models'
|
||||
import { Job, JobState, JobType, ResultList } from '../../models'
|
||||
import { HttpStatusCode, Job, JobState, JobType, ResultList } from '@shared/models'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
export class JobsCommand extends AbstractCommand {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
import { expect } from 'chai'
|
||||
import { wait } from '@shared/core-utils'
|
||||
import { JobState, JobType } from '../../models'
|
||||
import { wait } from '../miscs'
|
||||
import { PeerTubeServer } from './server'
|
||||
|
||||
async function waitJobs (serversArg: PeerTubeServer[] | PeerTubeServer, skipDelayed = false) {
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { PeerTubeServer } from './server'
|
||||
|
||||
async function testHelloWorldRegisteredSettings (server: PeerTubeServer) {
|
||||
const body = await server.plugins.getRegisteredSettings({ npmName: 'peertube-plugin-hello-world' })
|
||||
|
||||
const registeredSettings = body.registeredSettings
|
||||
expect(registeredSettings).to.have.length.at.least(1)
|
||||
|
||||
const adminNameSettings = registeredSettings.find(s => s.name === 'admin-name')
|
||||
expect(adminNameSettings).to.not.be.undefined
|
||||
}
|
||||
|
||||
export {
|
||||
testHelloWorldRegisteredSettings
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
import { ChildProcess, fork } from 'child_process'
|
||||
import { copy } from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
import { root, randomInt } from '@shared/core-utils'
|
||||
import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '../../models/videos'
|
||||
import { parallelTests, randomInt, root } from '@shared/core-utils'
|
||||
import { Video, VideoChannel, VideoCreateResult, VideoDetails } from '@shared/models'
|
||||
import { BulkCommand } from '../bulk'
|
||||
import { CLICommand } from '../cli'
|
||||
import { CustomPagesCommand } from '../custom-pages'
|
||||
import { FeedCommand } from '../feeds'
|
||||
import { LogsCommand } from '../logs'
|
||||
import { parallelTests, SQLCommand } from '../miscs'
|
||||
import { SQLCommand } from '../miscs'
|
||||
import { AbusesCommand } from '../moderation'
|
||||
import { OverviewsCommand } from '../overviews'
|
||||
import { SearchCommand } from '../search'
|
||||
|
@ -33,11 +33,11 @@ import { ContactFormCommand } from './contact-form-command'
|
|||
import { DebugCommand } from './debug-command'
|
||||
import { FollowsCommand } from './follows-command'
|
||||
import { JobsCommand } from './jobs-command'
|
||||
import { ObjectStorageCommand } from './object-storage-command'
|
||||
import { PluginsCommand } from './plugins-command'
|
||||
import { RedundancyCommand } from './redundancy-command'
|
||||
import { ServersCommand } from './servers-command'
|
||||
import { StatsCommand } from './stats-command'
|
||||
import { ObjectStorageCommand } from './object-storage-command'
|
||||
|
||||
export type RunServerOptions = {
|
||||
hideLogs?: boolean
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { exec } from 'child_process'
|
||||
import { copy, ensureDir, readFile, remove } from 'fs-extra'
|
||||
import { basename, join } from 'path'
|
||||
import { root } from '@shared/core-utils'
|
||||
import { isGithubCI, root, wait } from '@shared/core-utils'
|
||||
import { getFileSize } from '@shared/extra-utils'
|
||||
import { HttpStatusCode } from '@shared/models'
|
||||
import { getFileSize, isGithubCI, wait } from '../miscs'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
export class ServersCommand extends AbstractCommand {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ensureDir } from 'fs-extra'
|
||||
import { isGithubCI } from '../miscs'
|
||||
import { isGithubCI } from '@shared/core-utils'
|
||||
import { PeerTubeServer, RunServerOptions } from './server'
|
||||
|
||||
async function createSingleServer (serverNumber: number, configOverride?: Object, options: RunServerOptions = {}) {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import { expect } from 'chai'
|
||||
import { sha1 } from '@shared/core-utils/crypto'
|
||||
import { makeGetRequest } from '../requests'
|
||||
|
||||
async function hlsInfohashExist (serverUrl: string, masterPlaylistUrl: string, fileNumber: number) {
|
||||
const path = '/tracker/announce'
|
||||
|
||||
const infohash = sha1(`2${masterPlaylistUrl}+V${fileNumber}`)
|
||||
|
||||
// From bittorrent-tracker
|
||||
const infohashBinary = escape(Buffer.from(infohash, 'hex').toString('binary')).replace(/[@*/+]/g, function (char) {
|
||||
return '%' + char.charCodeAt(0).toString(16).toUpperCase()
|
||||
})
|
||||
|
||||
const res = await makeGetRequest({
|
||||
url: serverUrl,
|
||||
path,
|
||||
rawQuery: `peer_id=-WW0105-NkvYO/egUAr4&info_hash=${infohashBinary}&port=42100`,
|
||||
expectedStatus: 200
|
||||
})
|
||||
|
||||
expect(res.text).to.not.contain('failure')
|
||||
}
|
||||
|
||||
export {
|
||||
hlsInfohashExist
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { isAbsolute, join } from 'path'
|
||||
import { root } from '../miscs/tests'
|
||||
import { root } from '@shared/core-utils'
|
||||
import {
|
||||
makeDeleteRequest,
|
||||
makeGetRequest,
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { HttpStatusCode, ResultList } from '@shared/models'
|
||||
import { Account, ActorFollow } from '../../models/actors'
|
||||
import { AccountVideoRate, VideoRateType } from '../../models/videos'
|
||||
import { Account, AccountVideoRate, ActorFollow, HttpStatusCode, ResultList, VideoRateType } from '@shared/models'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
export class AccountsCommand extends AbstractCommand {
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { pathExists, readdir } from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
import { root } from '@shared/core-utils'
|
||||
import { Account, VideoChannel } from '@shared/models'
|
||||
import { PeerTubeServer } from '../server'
|
||||
|
||||
async function expectChannelsFollows (options: {
|
||||
server: PeerTubeServer
|
||||
handle: string
|
||||
followers: number
|
||||
following: number
|
||||
}) {
|
||||
const { server } = options
|
||||
const { data } = await server.channels.list()
|
||||
|
||||
return expectActorFollow({ ...options, data })
|
||||
}
|
||||
|
||||
async function expectAccountFollows (options: {
|
||||
server: PeerTubeServer
|
||||
handle: string
|
||||
followers: number
|
||||
following: number
|
||||
}) {
|
||||
const { server } = options
|
||||
const { data } = await server.accounts.list()
|
||||
|
||||
return expectActorFollow({ ...options, data })
|
||||
}
|
||||
|
||||
async function checkActorFilesWereRemoved (filename: string, serverNumber: number) {
|
||||
const testDirectory = 'test' + serverNumber
|
||||
|
||||
for (const directory of [ 'avatars' ]) {
|
||||
const directoryPath = join(root(), testDirectory, directory)
|
||||
|
||||
const directoryExists = await pathExists(directoryPath)
|
||||
expect(directoryExists).to.be.true
|
||||
|
||||
const files = await readdir(directoryPath)
|
||||
for (const file of files) {
|
||||
expect(file).to.not.contain(filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
expectAccountFollows,
|
||||
expectChannelsFollows,
|
||||
checkActorFilesWereRemoved
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function expectActorFollow (options: {
|
||||
server: PeerTubeServer
|
||||
data: (Account | VideoChannel)[]
|
||||
handle: string
|
||||
followers: number
|
||||
following: number
|
||||
}) {
|
||||
const { server, data, handle, followers, following } = options
|
||||
|
||||
const actor = data.find(a => a.name + '@' + a.host === handle)
|
||||
const message = `${handle} on ${server.url}`
|
||||
|
||||
expect(actor, message).to.exist
|
||||
expect(actor.followersCount).to.equal(followers, message)
|
||||
expect(actor.followingCount).to.equal(following, message)
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
export * from './accounts-command'
|
||||
export * from './actors'
|
||||
export * from './blocklist-command'
|
||||
export * from './login'
|
||||
export * from './login-command'
|
||||
export * from './notifications'
|
||||
export * from './notifications-command'
|
||||
export * from './subscriptions-command'
|
||||
export * from './users-command'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { HttpStatusCode, ResultList } from '@shared/models'
|
||||
import { UserNotification, UserNotificationSetting } from '../../models/users'
|
||||
import { HttpStatusCode, ResultList, UserNotification, UserNotificationSetting } from '@shared/models'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
export class NotificationsCommand extends AbstractCommand {
|
||||
|
|
|
@ -1,795 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { inspect } from 'util'
|
||||
import { AbuseState, PluginType } from '@shared/models'
|
||||
import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users'
|
||||
import { MockSmtpServer } from '../mock-servers/mock-email'
|
||||
import { PeerTubeServer } from '../server'
|
||||
import { doubleFollow } from '../server/follows'
|
||||
import { createMultipleServers } from '../server/servers'
|
||||
import { setAccessTokensToServers } from './login'
|
||||
|
||||
type CheckerBaseParams = {
|
||||
server: PeerTubeServer
|
||||
emails: any[]
|
||||
socketNotifications: UserNotification[]
|
||||
token: string
|
||||
check?: { web: boolean, mail: boolean }
|
||||
}
|
||||
|
||||
type CheckerType = 'presence' | 'absence'
|
||||
|
||||
function getAllNotificationsSettings (): UserNotificationSetting {
|
||||
return {
|
||||
newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
|
||||
newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
|
||||
}
|
||||
}
|
||||
|
||||
async function checkNewVideoFromSubscription (options: CheckerBaseParams & {
|
||||
videoName: string
|
||||
shortUUID: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { videoName, shortUUID } = options
|
||||
const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
checkVideo(notification.video, videoName, shortUUID)
|
||||
checkActor(notification.video.channel)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
return text.indexOf(shortUUID) !== -1 && text.indexOf('Your subscription') !== -1
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkVideoIsPublished (options: CheckerBaseParams & {
|
||||
videoName: string
|
||||
shortUUID: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { videoName, shortUUID } = options
|
||||
const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
checkVideo(notification.video, videoName, shortUUID)
|
||||
checkActor(notification.video.channel)
|
||||
} else {
|
||||
expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text: string = email['text']
|
||||
return text.includes(shortUUID) && text.includes('Your video')
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkMyVideoImportIsFinished (options: CheckerBaseParams & {
|
||||
videoName: string
|
||||
shortUUID: string
|
||||
url: string
|
||||
success: boolean
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { videoName, shortUUID, url, success } = options
|
||||
|
||||
const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.videoImport.targetUrl).to.equal(url)
|
||||
|
||||
if (success) checkVideo(notification.videoImport.video, videoName, shortUUID)
|
||||
} else {
|
||||
expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text: string = email['text']
|
||||
const toFind = success ? ' finished' : ' error'
|
||||
|
||||
return text.includes(url) && text.includes(toFind)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkUserRegistered (options: CheckerBaseParams & {
|
||||
username: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { username } = options
|
||||
const notificationType = UserNotificationType.NEW_USER_REGISTRATION
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
checkActor(notification.account)
|
||||
expect(notification.account.name).to.equal(username)
|
||||
} else {
|
||||
expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username)
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text: string = email['text']
|
||||
|
||||
return text.includes(' registered.') && text.includes(username)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkNewActorFollow (options: CheckerBaseParams & {
|
||||
followType: 'channel' | 'account'
|
||||
followerName: string
|
||||
followerDisplayName: string
|
||||
followingDisplayName: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { followType, followerName, followerDisplayName, followingDisplayName } = options
|
||||
const notificationType = UserNotificationType.NEW_FOLLOW
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
checkActor(notification.actorFollow.follower)
|
||||
expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
|
||||
expect(notification.actorFollow.follower.name).to.equal(followerName)
|
||||
expect(notification.actorFollow.follower.host).to.not.be.undefined
|
||||
|
||||
const following = notification.actorFollow.following
|
||||
expect(following.displayName).to.equal(followingDisplayName)
|
||||
expect(following.type).to.equal(followType)
|
||||
} else {
|
||||
expect(notification).to.satisfy(n => {
|
||||
return n.type !== notificationType ||
|
||||
(n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text: string = email['text']
|
||||
|
||||
return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkNewInstanceFollower (options: CheckerBaseParams & {
|
||||
followerHost: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { followerHost } = options
|
||||
const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
checkActor(notification.actorFollow.follower)
|
||||
expect(notification.actorFollow.follower.name).to.equal('peertube')
|
||||
expect(notification.actorFollow.follower.host).to.equal(followerHost)
|
||||
|
||||
expect(notification.actorFollow.following.name).to.equal('peertube')
|
||||
} else {
|
||||
expect(notification).to.satisfy(n => {
|
||||
return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text: string = email['text']
|
||||
|
||||
return text.includes('instance has a new follower') && text.includes(followerHost)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkAutoInstanceFollowing (options: CheckerBaseParams & {
|
||||
followerHost: string
|
||||
followingHost: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { followerHost, followingHost } = options
|
||||
const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
const following = notification.actorFollow.following
|
||||
checkActor(following)
|
||||
expect(following.name).to.equal('peertube')
|
||||
expect(following.host).to.equal(followingHost)
|
||||
|
||||
expect(notification.actorFollow.follower.name).to.equal('peertube')
|
||||
expect(notification.actorFollow.follower.host).to.equal(followerHost)
|
||||
} else {
|
||||
expect(notification).to.satisfy(n => {
|
||||
return n.type !== notificationType || n.actorFollow.following.host !== followingHost
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text: string = email['text']
|
||||
|
||||
return text.includes(' automatically followed a new instance') && text.includes(followingHost)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkCommentMention (options: CheckerBaseParams & {
|
||||
shortUUID: string
|
||||
commentId: number
|
||||
threadId: number
|
||||
byAccountDisplayName: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { shortUUID, commentId, threadId, byAccountDisplayName } = options
|
||||
const notificationType = UserNotificationType.COMMENT_MENTION
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
checkComment(notification.comment, commentId, threadId)
|
||||
checkActor(notification.comment.account)
|
||||
expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
|
||||
|
||||
checkVideo(notification.comment.video, undefined, shortUUID)
|
||||
} else {
|
||||
expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text: string = email['text']
|
||||
|
||||
return text.includes(' mentioned ') && text.includes(shortUUID) && text.includes(byAccountDisplayName)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
let lastEmailCount = 0
|
||||
|
||||
async function checkNewCommentOnMyVideo (options: CheckerBaseParams & {
|
||||
shortUUID: string
|
||||
commentId: number
|
||||
threadId: number
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { server, shortUUID, commentId, threadId, checkType, emails } = options
|
||||
const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
checkComment(notification.comment, commentId, threadId)
|
||||
checkActor(notification.comment.account)
|
||||
checkVideo(notification.comment.video, undefined, shortUUID)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.comment === undefined || n.comment.id !== commentId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const commentUrl = `http://localhost:${server.port}/w/${shortUUID};threadId=${threadId}`
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
return email['text'].indexOf(commentUrl) !== -1
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
|
||||
if (checkType === 'presence') {
|
||||
// We cannot detect email duplicates, so check we received another email
|
||||
expect(emails).to.have.length.above(lastEmailCount)
|
||||
lastEmailCount = emails.length
|
||||
}
|
||||
}
|
||||
|
||||
async function checkNewVideoAbuseForModerators (options: CheckerBaseParams & {
|
||||
shortUUID: string
|
||||
videoName: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { shortUUID, videoName } = options
|
||||
const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.abuse.id).to.be.a('number')
|
||||
checkVideo(notification.abuse.video, videoName, shortUUID)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.abuse === undefined || n.abuse.video.shortUUID !== shortUUID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkNewAbuseMessage (options: CheckerBaseParams & {
|
||||
abuseId: number
|
||||
message: string
|
||||
toEmail: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { abuseId, message, toEmail } = options
|
||||
const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.abuse.id).to.equal(abuseId)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.type !== notificationType || n.abuse === undefined || n.abuse.id !== abuseId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
const to = email['to'].filter(t => t.address === toEmail)
|
||||
|
||||
return text.indexOf(message) !== -1 && to.length !== 0
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkAbuseStateChange (options: CheckerBaseParams & {
|
||||
abuseId: number
|
||||
state: AbuseState
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { abuseId, state } = options
|
||||
const notificationType = UserNotificationType.ABUSE_STATE_CHANGE
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.abuse.id).to.equal(abuseId)
|
||||
expect(notification.abuse.state).to.equal(state)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.abuse === undefined || n.abuse.id !== abuseId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
|
||||
const contains = state === AbuseState.ACCEPTED
|
||||
? ' accepted'
|
||||
: ' rejected'
|
||||
|
||||
return text.indexOf(contains) !== -1
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkNewCommentAbuseForModerators (options: CheckerBaseParams & {
|
||||
shortUUID: string
|
||||
videoName: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { shortUUID, videoName } = options
|
||||
const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.abuse.id).to.be.a('number')
|
||||
checkVideo(notification.abuse.comment.video, videoName, shortUUID)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.abuse === undefined || n.abuse.comment.video.shortUUID !== shortUUID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
return text.indexOf(shortUUID) !== -1 && text.indexOf('abuse') !== -1
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkNewAccountAbuseForModerators (options: CheckerBaseParams & {
|
||||
displayName: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { displayName } = options
|
||||
const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.abuse.id).to.be.a('number')
|
||||
expect(notification.abuse.account.displayName).to.equal(displayName)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.abuse === undefined || n.abuse.account.displayName !== displayName
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkVideoAutoBlacklistForModerators (options: CheckerBaseParams & {
|
||||
shortUUID: string
|
||||
videoName: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { shortUUID, videoName } = options
|
||||
const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.videoBlacklist.video.id).to.be.a('number')
|
||||
checkVideo(notification.videoBlacklist.video, videoName, shortUUID)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.video === undefined || n.video.shortUUID !== shortUUID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
return text.indexOf(shortUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkNewBlacklistOnMyVideo (options: CheckerBaseParams & {
|
||||
shortUUID: string
|
||||
videoName: string
|
||||
blacklistType: 'blacklist' | 'unblacklist'
|
||||
}) {
|
||||
const { videoName, shortUUID, blacklistType } = options
|
||||
const notificationType = blacklistType === 'blacklist'
|
||||
? UserNotificationType.BLACKLIST_ON_MY_VIDEO
|
||||
: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
|
||||
|
||||
function notificationChecker (notification: UserNotification) {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
|
||||
|
||||
checkVideo(video, videoName, shortUUID)
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
const blacklistText = blacklistType === 'blacklist'
|
||||
? 'blacklisted'
|
||||
: 'unblacklisted'
|
||||
|
||||
return text.includes(shortUUID) && text.includes(blacklistText)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder, checkType: 'presence' })
|
||||
}
|
||||
|
||||
async function checkNewPeerTubeVersion (options: CheckerBaseParams & {
|
||||
latestVersion: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { latestVersion } = options
|
||||
const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.peertube).to.exist
|
||||
expect(notification.peertube.latestVersion).to.equal(latestVersion)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.peertube === undefined || n.peertube.latestVersion !== latestVersion
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
|
||||
return text.includes(latestVersion)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function checkNewPluginVersion (options: CheckerBaseParams & {
|
||||
pluginType: PluginType
|
||||
pluginName: string
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { pluginName, pluginType } = options
|
||||
const notificationType = UserNotificationType.NEW_PLUGIN_VERSION
|
||||
|
||||
function notificationChecker (notification: UserNotification, checkType: CheckerType) {
|
||||
if (checkType === 'presence') {
|
||||
expect(notification).to.not.be.undefined
|
||||
expect(notification.type).to.equal(notificationType)
|
||||
|
||||
expect(notification.plugin.name).to.equal(pluginName)
|
||||
expect(notification.plugin.type).to.equal(pluginType)
|
||||
} else {
|
||||
expect(notification).to.satisfy((n: UserNotification) => {
|
||||
return n === undefined || n.plugin === undefined || n.plugin.name !== pluginName
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function emailNotificationFinder (email: object) {
|
||||
const text = email['text']
|
||||
|
||||
return text.includes(pluginName)
|
||||
}
|
||||
|
||||
await checkNotification({ ...options, notificationChecker, emailNotificationFinder })
|
||||
}
|
||||
|
||||
async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) {
|
||||
const userNotifications: UserNotification[] = []
|
||||
const adminNotifications: UserNotification[] = []
|
||||
const adminNotificationsServer2: UserNotification[] = []
|
||||
const emails: object[] = []
|
||||
|
||||
const port = await MockSmtpServer.Instance.collectEmails(emails)
|
||||
|
||||
const overrideConfig = {
|
||||
smtp: {
|
||||
hostname: 'localhost',
|
||||
port
|
||||
},
|
||||
signup: {
|
||||
limit: 20
|
||||
}
|
||||
}
|
||||
const servers = await createMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg))
|
||||
|
||||
await setAccessTokensToServers(servers)
|
||||
|
||||
if (serversCount > 1) {
|
||||
await doubleFollow(servers[0], servers[1])
|
||||
}
|
||||
|
||||
const user = { username: 'user_1', password: 'super password' }
|
||||
await servers[0].users.create({ ...user, videoQuota: 10 * 1000 * 1000 })
|
||||
const userAccessToken = await servers[0].login.getAccessToken(user)
|
||||
|
||||
await servers[0].notifications.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() })
|
||||
await servers[0].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
|
||||
|
||||
if (serversCount > 1) {
|
||||
await servers[1].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
|
||||
}
|
||||
|
||||
{
|
||||
const socket = servers[0].socketIO.getUserNotificationSocket({ token: userAccessToken })
|
||||
socket.on('new-notification', n => userNotifications.push(n))
|
||||
}
|
||||
{
|
||||
const socket = servers[0].socketIO.getUserNotificationSocket()
|
||||
socket.on('new-notification', n => adminNotifications.push(n))
|
||||
}
|
||||
|
||||
if (serversCount > 1) {
|
||||
const socket = servers[1].socketIO.getUserNotificationSocket()
|
||||
socket.on('new-notification', n => adminNotificationsServer2.push(n))
|
||||
}
|
||||
|
||||
const { videoChannels } = await servers[0].users.getMyInfo()
|
||||
const channelId = videoChannels[0].id
|
||||
|
||||
return {
|
||||
userNotifications,
|
||||
adminNotifications,
|
||||
adminNotificationsServer2,
|
||||
userAccessToken,
|
||||
emails,
|
||||
servers,
|
||||
channelId
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
getAllNotificationsSettings,
|
||||
|
||||
CheckerBaseParams,
|
||||
CheckerType,
|
||||
checkMyVideoImportIsFinished,
|
||||
checkUserRegistered,
|
||||
checkAutoInstanceFollowing,
|
||||
checkVideoIsPublished,
|
||||
checkNewVideoFromSubscription,
|
||||
checkNewActorFollow,
|
||||
checkNewCommentOnMyVideo,
|
||||
checkNewBlacklistOnMyVideo,
|
||||
checkCommentMention,
|
||||
checkNewVideoAbuseForModerators,
|
||||
checkVideoAutoBlacklistForModerators,
|
||||
checkNewAbuseMessage,
|
||||
checkAbuseStateChange,
|
||||
checkNewInstanceFollower,
|
||||
prepareNotificationsTest,
|
||||
checkNewCommentAbuseForModerators,
|
||||
checkNewAccountAbuseForModerators,
|
||||
checkNewPeerTubeVersion,
|
||||
checkNewPluginVersion
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function checkNotification (options: CheckerBaseParams & {
|
||||
notificationChecker: (notification: UserNotification, checkType: CheckerType) => void
|
||||
emailNotificationFinder: (email: object) => boolean
|
||||
checkType: CheckerType
|
||||
}) {
|
||||
const { server, token, checkType, notificationChecker, emailNotificationFinder, socketNotifications, emails } = options
|
||||
|
||||
const check = options.check || { web: true, mail: true }
|
||||
|
||||
if (check.web) {
|
||||
const notification = await server.notifications.getLatest({ token: token })
|
||||
|
||||
if (notification || checkType !== 'absence') {
|
||||
notificationChecker(notification, checkType)
|
||||
}
|
||||
|
||||
const socketNotification = socketNotifications.find(n => {
|
||||
try {
|
||||
notificationChecker(n, 'presence')
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
if (checkType === 'presence') {
|
||||
const obj = inspect(socketNotifications, { depth: 5 })
|
||||
expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined
|
||||
} else {
|
||||
const obj = inspect(socketNotification, { depth: 5 })
|
||||
expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined
|
||||
}
|
||||
}
|
||||
|
||||
if (check.mail) {
|
||||
// Last email
|
||||
const email = emails
|
||||
.slice()
|
||||
.reverse()
|
||||
.find(e => emailNotificationFinder(e))
|
||||
|
||||
if (checkType === 'presence') {
|
||||
const texts = emails.map(e => e.text)
|
||||
expect(email, 'The email is absent when is should be present. ' + inspect(texts)).to.not.be.undefined
|
||||
} else {
|
||||
expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkVideo (video: any, videoName?: string, shortUUID?: string) {
|
||||
if (videoName) {
|
||||
expect(video.name).to.be.a('string')
|
||||
expect(video.name).to.not.be.empty
|
||||
expect(video.name).to.equal(videoName)
|
||||
}
|
||||
|
||||
if (shortUUID) {
|
||||
expect(video.shortUUID).to.be.a('string')
|
||||
expect(video.shortUUID).to.not.be.empty
|
||||
expect(video.shortUUID).to.equal(shortUUID)
|
||||
}
|
||||
|
||||
expect(video.id).to.be.a('number')
|
||||
}
|
||||
|
||||
function checkActor (actor: any) {
|
||||
expect(actor.displayName).to.be.a('string')
|
||||
expect(actor.displayName).to.not.be.empty
|
||||
expect(actor.host).to.not.be.undefined
|
||||
}
|
||||
|
||||
function checkComment (comment: any, commentId: number, threadId: number) {
|
||||
expect(comment.id).to.equal(commentId)
|
||||
expect(comment.threadId).to.equal(threadId)
|
||||
}
|
|
@ -4,6 +4,7 @@ import {
|
|||
HttpStatusCode,
|
||||
MyUser,
|
||||
ResultList,
|
||||
ScopedToken,
|
||||
User,
|
||||
UserAdminFlag,
|
||||
UserCreateResult,
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
UserVideoQuota,
|
||||
UserVideoRate
|
||||
} from '@shared/models'
|
||||
import { ScopedToken } from '@shared/models/users/user-scoped-token'
|
||||
import { unwrapBody } from '../requests'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
import { HttpStatusCode, ResultList } from '@shared/models'
|
||||
import { VideoBlacklist, VideoBlacklistType } from '../../models/videos'
|
||||
import { HttpStatusCode, ResultList, VideoBlacklist, VideoBlacklistType } from '@shared/models'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
export class BlacklistCommand extends AbstractCommand {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { buildAbsoluteFixturePath } from '@shared/core-utils'
|
||||
import { HttpStatusCode, ResultList, VideoCaption } from '@shared/models'
|
||||
import { buildAbsoluteFixturePath } from '../miscs'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
export class CaptionsCommand extends AbstractCommand {
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { expect } from 'chai'
|
||||
import request from 'supertest'
|
||||
import { HttpStatusCode } from '@shared/models'
|
||||
|
||||
async function testCaptionFile (url: string, captionPath: string, toTest: RegExp | string) {
|
||||
const res = await request(url)
|
||||
.get(captionPath)
|
||||
.expect(HttpStatusCode.OK_200)
|
||||
|
||||
if (toTest instanceof RegExp) {
|
||||
expect(res.text).to.match(toTest)
|
||||
} else {
|
||||
expect(res.text).to.contain(toTest)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
testCaptionFile
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
import { pick } from '@shared/core-utils'
|
||||
import { ActorFollow, HttpStatusCode, ResultList, VideoChannel, VideoChannelCreateResult } from '@shared/models'
|
||||
import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model'
|
||||
import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model'
|
||||
import {
|
||||
ActorFollow,
|
||||
HttpStatusCode,
|
||||
ResultList,
|
||||
VideoChannel,
|
||||
VideoChannelCreate,
|
||||
VideoChannelCreateResult,
|
||||
VideoChannelUpdate
|
||||
} from '@shared/models'
|
||||
import { unwrapBody } from '../requests'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export * from './blacklist-command'
|
||||
export * from './captions-command'
|
||||
export * from './captions'
|
||||
export * from './change-ownership-command'
|
||||
export * from './channels'
|
||||
export * from './channels-command'
|
||||
|
@ -10,10 +9,7 @@ export * from './imports-command'
|
|||
export * from './live-command'
|
||||
export * from './live'
|
||||
export * from './playlists-command'
|
||||
export * from './playlists'
|
||||
export * from './services-command'
|
||||
export * from './streaming-playlists-command'
|
||||
export * from './streaming-playlists'
|
||||
export * from './comments-command'
|
||||
export * from './videos-command'
|
||||
export * from './videos'
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
import { readdir } from 'fs-extra'
|
||||
import { omit } from 'lodash'
|
||||
import { join } from 'path'
|
||||
import { wait } from '@shared/core-utils'
|
||||
import { HttpStatusCode, LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models'
|
||||
import { wait } from '../miscs'
|
||||
import { unwrapBody } from '../requests'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
import { sendRTMPStream, testFfmpegStreamError } from './live'
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import ffmpeg, { FfmpegCommand } from 'fluent-ffmpeg'
|
||||
import { pathExists, readdir } from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
import { buildAbsoluteFixturePath, wait } from '../miscs'
|
||||
import { buildAbsoluteFixturePath, wait } from '@shared/core-utils'
|
||||
import { PeerTubeServer } from '../server/server'
|
||||
|
||||
function sendRTMPStream (options: {
|
||||
|
@ -95,43 +90,11 @@ async function waitUntilLiveSavedOnAllServers (servers: PeerTubeServer[], videoI
|
|||
}
|
||||
}
|
||||
|
||||
async function checkLiveCleanupAfterSave (server: PeerTubeServer, videoUUID: string, resolutions: number[] = []) {
|
||||
const basePath = server.servers.buildDirectory('streaming-playlists')
|
||||
const hlsPath = join(basePath, 'hls', videoUUID)
|
||||
|
||||
if (resolutions.length === 0) {
|
||||
const result = await pathExists(hlsPath)
|
||||
expect(result).to.be.false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const files = await readdir(hlsPath)
|
||||
|
||||
// fragmented file and playlist per resolution + master playlist + segments sha256 json file
|
||||
expect(files).to.have.lengthOf(resolutions.length * 2 + 2)
|
||||
|
||||
for (const resolution of resolutions) {
|
||||
const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
|
||||
expect(fragmentedFile).to.exist
|
||||
|
||||
const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
|
||||
expect(playlistFile).to.exist
|
||||
}
|
||||
|
||||
const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
|
||||
expect(masterPlaylistFile).to.exist
|
||||
|
||||
const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
|
||||
expect(shaFile).to.exist
|
||||
}
|
||||
|
||||
export {
|
||||
sendRTMPStream,
|
||||
waitFfmpegUntilError,
|
||||
testFfmpegStreamError,
|
||||
stopFfmpeg,
|
||||
waitUntilLivePublishedOnAllServers,
|
||||
waitUntilLiveSavedOnAllServers,
|
||||
checkLiveCleanupAfterSave
|
||||
waitUntilLiveSavedOnAllServers
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import { expect } from 'chai'
|
||||
import { readdir } from 'fs-extra'
|
||||
import { join } from 'path'
|
||||
import { root } from '../miscs'
|
||||
|
||||
async function checkPlaylistFilesWereRemoved (
|
||||
playlistUUID: string,
|
||||
internalServerNumber: number,
|
||||
directories = [ 'thumbnails' ]
|
||||
) {
|
||||
const testDirectory = 'test' + internalServerNumber
|
||||
|
||||
for (const directory of directories) {
|
||||
const directoryPath = join(root(), testDirectory, directory)
|
||||
|
||||
const files = await readdir(directoryPath)
|
||||
for (const file of files) {
|
||||
expect(file).to.not.contain(playlistUUID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
checkPlaylistFilesWereRemoved
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
import { expect } from 'chai'
|
||||
import { basename } from 'path'
|
||||
import { sha256 } from '@shared/core-utils/crypto'
|
||||
import { removeFragmentedMP4Ext } from '@shared/core-utils'
|
||||
import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models'
|
||||
import { PeerTubeServer } from '../server'
|
||||
|
||||
async function checkSegmentHash (options: {
|
||||
server: PeerTubeServer
|
||||
baseUrlPlaylist: string
|
||||
baseUrlSegment: string
|
||||
resolution: number
|
||||
hlsPlaylist: VideoStreamingPlaylist
|
||||
}) {
|
||||
const { server, baseUrlPlaylist, baseUrlSegment, resolution, hlsPlaylist } = options
|
||||
const command = server.streamingPlaylists
|
||||
|
||||
const file = hlsPlaylist.files.find(f => f.resolution.id === resolution)
|
||||
const videoName = basename(file.fileUrl)
|
||||
|
||||
const playlist = await command.get({ url: `${baseUrlPlaylist}/${removeFragmentedMP4Ext(videoName)}.m3u8` })
|
||||
|
||||
const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
|
||||
|
||||
const length = parseInt(matches[1], 10)
|
||||
const offset = parseInt(matches[2], 10)
|
||||
const range = `${offset}-${offset + length - 1}`
|
||||
|
||||
const segmentBody = await command.getSegment({
|
||||
url: `${baseUrlSegment}/${videoName}`,
|
||||
expectedStatus: HttpStatusCode.PARTIAL_CONTENT_206,
|
||||
range: `bytes=${range}`
|
||||
})
|
||||
|
||||
const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
|
||||
expect(sha256(segmentBody)).to.equal(shaBody[videoName][range])
|
||||
}
|
||||
|
||||
async function checkLiveSegmentHash (options: {
|
||||
server: PeerTubeServer
|
||||
baseUrlSegment: string
|
||||
videoUUID: string
|
||||
segmentName: string
|
||||
hlsPlaylist: VideoStreamingPlaylist
|
||||
}) {
|
||||
const { server, baseUrlSegment, videoUUID, segmentName, hlsPlaylist } = options
|
||||
const command = server.streamingPlaylists
|
||||
|
||||
const segmentBody = await command.getSegment({ url: `${baseUrlSegment}/${videoUUID}/${segmentName}` })
|
||||
const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
|
||||
|
||||
expect(sha256(segmentBody)).to.equal(shaBody[segmentName])
|
||||
}
|
||||
|
||||
async function checkResolutionsInMasterPlaylist (options: {
|
||||
server: PeerTubeServer
|
||||
playlistUrl: string
|
||||
resolutions: number[]
|
||||
}) {
|
||||
const { server, playlistUrl, resolutions } = options
|
||||
|
||||
const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl })
|
||||
|
||||
for (const resolution of resolutions) {
|
||||
const reg = new RegExp(
|
||||
'#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"'
|
||||
)
|
||||
|
||||
expect(masterPlaylist).to.match(reg)
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
checkSegmentHash,
|
||||
checkLiveSegmentHash,
|
||||
checkResolutionsInMasterPlaylist
|
||||
}
|
|
@ -5,8 +5,7 @@ import { createReadStream, stat } from 'fs-extra'
|
|||
import got, { Response as GotResponse } from 'got'
|
||||
import { omit } from 'lodash'
|
||||
import validator from 'validator'
|
||||
import { buildUUID } from '@shared/core-utils/uuid'
|
||||
import { pick } from '@shared/core-utils'
|
||||
import { buildAbsoluteFixturePath, buildUUID, pick, wait } from '@shared/core-utils'
|
||||
import {
|
||||
HttpStatusCode,
|
||||
ResultList,
|
||||
|
@ -20,7 +19,6 @@ import {
|
|||
VideosCommonQuery,
|
||||
VideoTranscodingCreate
|
||||
} from '@shared/models'
|
||||
import { buildAbsoluteFixturePath, wait } from '../miscs'
|
||||
import { unwrapBody } from '../requests'
|
||||
import { waitJobs } from '../server'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared'
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { pathExists, readdir } from 'fs-extra'
|
||||
import { basename, join } from 'path'
|
||||
import { HttpStatusCode, VideoCaption, VideoDetails } from '@shared/models'
|
||||
import { waitJobs } from '../server'
|
||||
import { PeerTubeServer } from '../server/server'
|
||||
import { VideoEdit } from './videos-command'
|
||||
|
||||
async function checkVideoFilesWereRemoved (options: {
|
||||
server: PeerTubeServer
|
||||
video: VideoDetails
|
||||
captions?: VideoCaption[]
|
||||
onlyVideoFiles?: boolean // default false
|
||||
}) {
|
||||
const { video, server, captions = [], onlyVideoFiles = false } = options
|
||||
|
||||
const webtorrentFiles = video.files || []
|
||||
const hlsFiles = video.streamingPlaylists[0]?.files || []
|
||||
|
||||
const thumbnailName = basename(video.thumbnailPath)
|
||||
const previewName = basename(video.previewPath)
|
||||
|
||||
const torrentNames = webtorrentFiles.concat(hlsFiles).map(f => basename(f.torrentUrl))
|
||||
|
||||
const captionNames = captions.map(c => basename(c.captionPath))
|
||||
|
||||
const webtorrentFilenames = webtorrentFiles.map(f => basename(f.fileUrl))
|
||||
const hlsFilenames = hlsFiles.map(f => basename(f.fileUrl))
|
||||
|
||||
let directories: { [ directory: string ]: string[] } = {
|
||||
videos: webtorrentFilenames,
|
||||
redundancy: webtorrentFilenames,
|
||||
[join('playlists', 'hls')]: hlsFilenames,
|
||||
[join('redundancy', 'hls')]: hlsFilenames
|
||||
}
|
||||
|
||||
if (onlyVideoFiles !== true) {
|
||||
directories = {
|
||||
...directories,
|
||||
|
||||
thumbnails: [ thumbnailName ],
|
||||
previews: [ previewName ],
|
||||
torrents: torrentNames,
|
||||
captions: captionNames
|
||||
}
|
||||
}
|
||||
|
||||
for (const directory of Object.keys(directories)) {
|
||||
const directoryPath = server.servers.buildDirectory(directory)
|
||||
|
||||
const directoryExists = await pathExists(directoryPath)
|
||||
if (directoryExists === false) continue
|
||||
|
||||
const existingFiles = await readdir(directoryPath)
|
||||
for (const existingFile of existingFiles) {
|
||||
for (const shouldNotExist of directories[directory]) {
|
||||
expect(existingFile, `File ${existingFile} should not exist in ${directoryPath}`).to.not.contain(shouldNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveVideoInServers (servers: PeerTubeServer[], uuid: string) {
|
||||
for (const server of servers) {
|
||||
server.store.videoDetails = await server.videos.get({ id: uuid })
|
||||
}
|
||||
}
|
||||
|
||||
function checkUploadVideoParam (
|
||||
server: PeerTubeServer,
|
||||
token: string,
|
||||
attributes: Partial<VideoEdit>,
|
||||
expectedStatus = HttpStatusCode.OK_200,
|
||||
mode: 'legacy' | 'resumable' = 'legacy'
|
||||
) {
|
||||
return mode === 'legacy'
|
||||
? server.videos.buildLegacyUpload({ token, attributes, expectedStatus })
|
||||
: server.videos.buildResumeUpload({ token, attributes, expectedStatus })
|
||||
}
|
||||
|
||||
// serverNumber starts from 1
|
||||
async function uploadRandomVideoOnServers (
|
||||
servers: PeerTubeServer[],
|
||||
serverNumber: number,
|
||||
additionalParams?: VideoEdit & { prefixName?: string }
|
||||
) {
|
||||
const server = servers.find(s => s.serverNumber === serverNumber)
|
||||
const res = await server.videos.randomUpload({ wait: false, additionalParams })
|
||||
|
||||
await waitJobs(servers)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
checkUploadVideoParam,
|
||||
uploadRandomVideoOnServers,
|
||||
checkVideoFilesWereRemoved,
|
||||
saveVideoInServers
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue