1
0
Fork 0
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:
Chocobozzz 2021-12-17 11:58:15 +01:00
parent bf54587a3e
commit c55e3d7227
No known key found for this signature in database
GPG key ID: 583A612D890159BE
202 changed files with 798 additions and 895 deletions

View file

@ -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'

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -1,5 +1,2 @@
export * from './checks'
export * from './generate'
export * from './sql-command'
export * from './tests'
export * from './webtorrent'

View file

@ -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

View file

@ -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
}

View file

@ -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'

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -1,3 +1 @@
// Don't include activitypub that import stuff from server
export * from './check-api-params'
export * from './requests'

View file

@ -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

View file

@ -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 {

View file

@ -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
}

View file

@ -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'

View file

@ -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 {

View file

@ -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) {

View file

@ -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
}

View file

@ -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

View file

@ -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 {

View file

@ -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 = {}) {

View file

@ -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
}

View file

@ -1,5 +1,5 @@
import { isAbsolute, join } from 'path'
import { root } from '../miscs/tests'
import { root } from '@shared/core-utils'
import {
makeDeleteRequest,
makeGetRequest,

View file

@ -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 {

View file

@ -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)
}

View file

@ -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'

View file

@ -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 {

View file

@ -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)
}

View file

@ -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'

View file

@ -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 {

View file

@ -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 {

View file

@ -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
}

View file

@ -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'

View file

@ -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'

View file

@ -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'

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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'

View file

@ -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
}