1
0
Fork 0

Add CLI plugins tests

This commit is contained in:
Chocobozzz 2019-07-19 10:37:35 +02:00 committed by Chocobozzz
parent 32fe001389
commit 9b474844e8
22 changed files with 611 additions and 170 deletions

View file

@ -4,21 +4,16 @@ import { PluginManager } from '../../server/lib/plugins/plugin-manager'
import { isAbsolute } from 'path'
program
.option('-n, --plugin-name [pluginName]', 'Plugin name to install')
.option('-n, --npm-name [npmName]', 'Plugin to install')
.option('-v, --plugin-version [pluginVersion]', 'Plugin version to install')
.option('-p, --plugin-path [pluginPath]', 'Path of the plugin you want to install')
.parse(process.argv)
if (!program['pluginName'] && !program['pluginPath']) {
if (!program['npmName'] && !program['pluginPath']) {
console.error('You need to specify a plugin name with the desired version, or a plugin path.')
process.exit(-1)
}
if (program['pluginName'] && !program['pluginVersion']) {
console.error('You need to specify a the version of the plugin you want to install.')
process.exit(-1)
}
if (program['pluginPath'] && !isAbsolute(program['pluginPath'])) {
console.error('Plugin path should be absolute.')
process.exit(-1)
@ -34,6 +29,6 @@ run()
async function run () {
await initDatabaseModels(true)
const toInstall = program['pluginName'] || program['pluginPath']
const toInstall = program['npmName'] || program['pluginPath']
await PluginManager.Instance.install(toInstall, program['pluginVersion'], !!program['pluginPath'])
}

View file

@ -14,7 +14,8 @@ if [ "$1" = "misc" ]; then
mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/client.ts \
server/tests/feeds/index.ts \
server/tests/misc-endpoints.ts \
server/tests/helpers/index.ts
server/tests/helpers/index.ts \
server/tests/plugins/index.ts
elif [ "$1" = "cli" ]; then
npm run build:server
CC=gcc-4.9 CXX=g++-4.9 npm run setup:cli

View file

@ -4,7 +4,7 @@
*/
import { createHash, HexBase64Latin1Encoding, pseudoRandomBytes } from 'crypto'
import { isAbsolute, join } from 'path'
import { basename, isAbsolute, join, resolve } from 'path'
import * as pem from 'pem'
import { URL } from 'url'
import { truncate } from 'lodash'
@ -136,16 +136,16 @@ function getAppNumber () {
return process.env.NODE_APP_INSTANCE
}
let rootPath: string
function root () {
if (rootPath) return rootPath
// We are in /helpers/utils.js
const paths = [ __dirname, '..', '..' ]
rootPath = join(__dirname, '..', '..')
// We are under /dist directory
if (process.mainModule && process.mainModule.filename.endsWith('_mocha') === false) {
paths.push('..')
}
if (basename(rootPath) === 'dist') rootPath = resolve(rootPath, '..')
return join.apply(null, paths)
return rootPath
}
// Thanks: https://stackoverflow.com/a/12034334

View file

@ -387,12 +387,13 @@ namespace audio {
export namespace bitrate {
const baseKbitrate = 384
const toBits = (kbits: number): number => { return kbits * 8000 }
const toBits = (kbits: number) => kbits * 8000
export const aac = (bitrate: number): number => {
switch (true) {
case bitrate > toBits(baseKbitrate):
return baseKbitrate
default:
return -1 // we interpret it as a signal to copy the audio stream as is
}
@ -407,8 +408,10 @@ namespace audio {
switch (true) {
case bitrate <= toBits(192):
return 128
case bitrate <= toBits(384):
return 256
default:
return baseKbitrate
}

View file

@ -5,12 +5,12 @@ import { CONFIG } from '../../initializers/config'
import { outputJSON, pathExists } from 'fs-extra'
import { join } from 'path'
async function installNpmPlugin (name: string, version?: string) {
async function installNpmPlugin (npmName: string, version?: string) {
// Security check
checkNpmPluginNameOrThrow(name)
checkNpmPluginNameOrThrow(npmName)
if (version) checkPluginVersionOrThrow(version)
let toInstall = name
let toInstall = npmName
if (version) toInstall += `@${version}`
await execYarn('add ' + toInstall)

View file

@ -387,13 +387,24 @@ describe('Test users API validators', function () {
}
})
it('Should fail with an invalid theme', async function () {
const fields = { theme: 'invalid' }
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
})
it('Should fail with an unknown theme', async function () {
const fields = { theme: 'peertube-theme-unknown' }
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
})
it('Should succeed to change password with the correct params', async function () {
const fields = {
currentPassword: 'my super password',
password: 'my super password',
nsfwPolicy: 'blur',
autoPlayVideo: false,
email: 'super_email@example.com'
email: 'super_email@example.com',
theme: 'default'
}
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })

View file

@ -11,3 +11,4 @@ import './reverse-proxy'
import './stats'
import './tracker'
import './no-client'
import './plugins'

View file

@ -0,0 +1,130 @@
/* tslint:disable:no-unused-expression */
import 'mocha'
import * as chai from 'chai'
import { About } from '../../../../shared/models/server/about.model'
import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
import {
cleanupTests,
deleteCustomConfig,
flushAndRunServer,
getAbout,
getConfig,
getCustomConfig, installPlugin,
killallServers, parallelTests,
registerUser,
reRunServer, ServerInfo,
setAccessTokensToServers,
updateCustomConfig, uploadVideo
} from '../../../../shared/extra-utils'
import { ServerConfig } from '../../../../shared/models'
import { PeerTubePlugin } from '../../../../shared/models/plugins/peertube-plugin.model'
const expect = chai.expect
describe('Test plugins', function () {
let server = null
before(async function () {
this.timeout(30000)
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])
{
await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-hello-world' })
}
{
await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-background-color' })
}
})
it('Should list available plugins and themes', async function () {
// List without filter
// List with filter (plugin and theme)
})
it('Should search available plugins', async function () {
// Search with filter (plugin and theme)
// Add pagination
// Add sort
// Add peertube engine
})
it('Should have an empty global css', async function () {
// get /global.css
})
it('Should install a plugin and a theme', async function () {
})
it('Should have the correct global css', async function () {
// get /global.css
})
it('Should have the plugin loaded in the configuration', async function () {
// Check registered themes/plugins
})
it('Should update the default theme in the configuration', async function () {
// Update config
})
it('Should list plugins and themes', async function () {
// List without filter
// List with filter (theme/plugin)
// List with pagination
// List with sort
})
it('Should get a plugin and a theme', async function () {
// Get plugin
// Get theme
})
it('Should get registered settings', async function () {
// Get plugin
})
it('Should update the settings', async function () {
// Update /settings
// get /plugin
})
it('Should update the plugin and the theme', async function () {
// update BDD -> 0.0.1
// update package.json (theme + plugin)
// list to check versions
// update plugin + theme
// list to check they have been updated
// check package.json are upgraded too
})
it('Should uninstall the plugin', async function () {
// uninstall
// list
})
it('Should have an empty global css', async function () {
// get /global.css
})
it('Should list uninstalled plugins', async function () {
// { uninstalled: true }
})
it('Should uninstall the theme', async function () {
// Uninstall
})
it('Should have updated the configuration', async function () {
// get /config (default theme + registered themes + registered plugins)
})
after(async function () {
await cleanupTests([ server ])
})
})

View file

@ -18,7 +18,7 @@ import {
getUsersList,
getUsersListPaginationAndSort,
getVideoChannel,
getVideosList,
getVideosList, installPlugin,
login,
makePutBodyRequest,
rateVideo,
@ -57,6 +57,8 @@ describe('Test users', function () {
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])
await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-theme-background-red' })
})
describe('OAuth client', function () {
@ -551,6 +553,21 @@ describe('Test users', function () {
expect(user.account.displayName).to.equal('new display name')
expect(user.account.description).to.equal('my super description updated')
})
it('Should be able to update my theme', async function () {
for (const theme of [ 'background-red', 'default', 'instance-default' ]) {
await updateMyUser({
url: server.url,
accessToken: accessTokenUser,
theme
})
const res = await getMyUserInformation(server.url, accessTokenUser)
const body: User = res.body
expect(body.theme).to.equal(theme)
}
})
})
describe('Updating another user', function () {

View file

@ -3,5 +3,6 @@ import './create-import-video-file-job'
import './create-transcoding-job'
import './optimize-old-videos'
import './peertube'
import './plugins'
import './reset-password'
import './update-host'

View file

@ -43,6 +43,8 @@ describe('Test CLI wrapper', function () {
}
})
describe('Authentication and instance selection', function () {
it('Should display no selected instance', async function () {
this.timeout(60000)
@ -76,6 +78,9 @@ describe('Test CLI wrapper', function () {
expect(stdout).to.contain(server.url)
})
})
describe('Video upload/import', function () {
it('Should upload a video', async function () {
this.timeout(60000)
@ -96,7 +101,7 @@ describe('Test CLI wrapper', function () {
const videos: Video[] = res.body.data
const video: VideoDetails = (await getVideo(server.url, videos[0].uuid)).body
const video: VideoDetails = (await getVideo(server.url, videos[ 0 ].uuid)).body
expect(video.name).to.equal('test upload')
expect(video.support).to.equal('support_text')
@ -161,6 +166,9 @@ describe('Test CLI wrapper', function () {
expect(videoDetails.commentsEnabled).to.be.true
}
})
})
describe('Admin auth', function () {
it('Should remove the auth user', async function () {
const env = getEnvCli(server)
@ -172,6 +180,36 @@ describe('Test CLI wrapper', function () {
expect(stdout).to.contain('no instance selected')
})
it('Should add the admin user', async function () {
const env = getEnvCli(server)
await execCLI(`${env} ${cmd} auth add -u ${server.url} -U root -p test${server.internalServerNumber}`)
})
})
describe('Manage plugins', function () {
it('Should install a plugin', async function () {
this.timeout(60000)
const env = getEnvCli(server)
await execCLI(`${env} ${cmd} plugins install --npm-name peertube-plugin-hello-world`)
})
it('Should list installed plugins', async function () {
const env = getEnvCli(server)
const res = await execCLI(`${env} ${cmd} plugins list`)
expect(res).to.contain('peertube-plugin-hello-world')
})
it('Should uninstall the plugin', async function () {
const env = getEnvCli(server)
const res = await execCLI(`${env} ${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
expect(res).to.not.contain('peertube-plugin-hello-world')
})
})
after(async function () {
this.timeout(10000)

View file

@ -0,0 +1,87 @@
/* tslint:disable:no-unused-expression */
import 'mocha'
import {
cleanupTests,
execCLI,
flushAndRunServer,
getConfig,
getEnvCli, killallServers,
reRunServer,
root,
ServerInfo,
setAccessTokensToServers
} from '../../../shared/extra-utils'
import { join } from 'path'
import { ServerConfig } from '../../../shared/models/server'
import { expect } from 'chai'
describe('Test plugin scripts', function () {
let server: ServerInfo
before(async function () {
this.timeout(30000)
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])
})
it('Should install a plugin from stateless CLI', async function () {
this.timeout(60000)
const packagePath = join(root(), 'server', 'tests', 'fixtures', 'peertube-plugin-test')
const env = getEnvCli(server)
await execCLI(`${env} npm run plugin:install -- --plugin-path ${packagePath}`)
})
it('Should install a theme from stateless CLI', async function () {
this.timeout(60000)
const env = getEnvCli(server)
await execCLI(`${env} npm run plugin:install -- --npm-name peertube-theme-background-red`)
})
it('Should have the theme and the plugin registered when we restart peertube', async function () {
this.timeout(30000)
killallServers([ server ])
await reRunServer(server)
const res = await getConfig(server.url)
const config: ServerConfig = res.body
const plugin = config.plugin.registered
.find(p => p.name === 'test')
expect(plugin).to.not.be.undefined
const theme = config.theme.registered
.find(t => t.name === 'background-red')
expect(theme).to.not.be.undefined
})
it('Should uninstall a plugin from stateless CLI', async function () {
this.timeout(60000)
const env = getEnvCli(server)
await execCLI(`${env} npm run plugin:uninstall -- --npm-name peertube-plugin-test`)
})
it('Should have removed the plugin on another peertube restart', async function () {
this.timeout(30000)
killallServers([ server ])
await reRunServer(server)
const res = await getConfig(server.url)
const config: ServerConfig = res.body
const plugin = config.plugin.registered
.find(p => p.name === 'test')
expect(plugin).to.be.undefined
})
after(async function () {
await cleanupTests([ server ])
})
})

View file

@ -0,0 +1,39 @@
async function register ({ registerHook, registerSetting, settingsManager, storageManager }) {
const defaultAdmin = 'PeerTube admin'
registerHook({
target: 'action:application.listening',
handler: () => displayHelloWorld(settingsManager, defaultAdmin)
})
registerSetting({
name: 'admin-name',
label: 'Admin name',
type: 'input',
default: defaultAdmin
})
const value = await storageManager.getData('toto')
console.log(value)
console.log(value.coucou)
await storageManager.storeData('toto', { coucou: 'hello' + new Date() })
}
async function unregister () {
return
}
module.exports = {
register,
unregister
}
// ############################################################################
async function displayHelloWorld (settingsManager, defaultAdmin) {
let value = await settingsManager.getSetting('admin-name')
if (!value) value = defaultAdmin
console.log('hello world ' + value)
}

View file

@ -0,0 +1,19 @@
{
"name": "peertube-plugin-test",
"version": "0.0.1",
"description": "Plugin test",
"engine": {
"peertube": ">=1.3.0"
},
"keywords": [
"peertube",
"plugin"
],
"homepage": "https://github.com/Chocobozzz/PeerTube",
"author": "Chocobozzz",
"bugs": "https://github.com/Chocobozzz/PeerTube/issues",
"library": "./main.js",
"staticDirs": {},
"css": [],
"clientScripts": []
}

View file

@ -3,3 +3,4 @@ import './client'
import './feeds/'
import './cli/'
import './api/'
import './plugins/'

View file

@ -0,0 +1,27 @@
/* tslint:disable:no-unused-expression */
import * as chai from 'chai'
import 'mocha'
import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
import { setAccessTokensToServers } from '../../../shared/extra-utils'
const expect = chai.expect
describe('Test plugin filter hooks', function () {
let server: ServerInfo
before(async function () {
this.timeout(30000)
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])
})
it('Should execute ', async function () {
})
after(async function () {
await cleanupTests([ server ])
})
})

View file

@ -0,0 +1,27 @@
/* tslint:disable:no-unused-expression */
import * as chai from 'chai'
import 'mocha'
import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
import { setAccessTokensToServers } from '../../../shared/extra-utils'
const expect = chai.expect
describe('Test plugin filter hooks', function () {
let server: ServerInfo
before(async function () {
this.timeout(30000)
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])
})
it('Should execute ', async function () {
})
after(async function () {
await cleanupTests([ server ])
})
})

View file

@ -0,0 +1,2 @@
export * from './action-hooks'
export * from './filter-hooks'

View file

@ -18,7 +18,7 @@ program
.command('get-access-token', 'get a peertube access token', { noHelp: true }).alias('token')
.command('watch', 'watch a video in the terminal ✩°。⋆').alias('w')
.command('repl', 'initiate a REPL to access internals')
.command('plugins [action]', 'manage plugins on a local instance').alias('p')
.command('plugins [action]', 'manage instance plugins/themes').alias('p')
/* Not Yet Implemented */
program

View file

@ -6,6 +6,7 @@ import { UserRegister } from '../../models/users/user-register.model'
import { UserRole } from '../../models/users/user-role'
import { ServerInfo } from '../server/servers'
import { userLogin } from './login'
import { UserUpdateMe } from '../../models/users'
type CreateUserArgs = { url: string,
accessToken: string,
@ -224,19 +225,21 @@ function updateMyUser (options: {
displayName?: string
description?: string
videosHistoryEnabled?: boolean
theme?: string
}) {
const path = '/api/v1/users/me'
const toSend = {}
if (options.currentPassword !== undefined && options.currentPassword !== null) toSend['currentPassword'] = options.currentPassword
if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy
if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
if (options.description !== undefined && options.description !== null) toSend['description'] = options.description
if (options.displayName !== undefined && options.displayName !== null) toSend['displayName'] = options.displayName
const toSend: UserUpdateMe = {}
if (options.currentPassword !== undefined && options.currentPassword !== null) toSend.currentPassword = options.currentPassword
if (options.newPassword !== undefined && options.newPassword !== null) toSend.password = options.newPassword
if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend.nsfwPolicy = options.nsfwPolicy
if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend.autoPlayVideo = options.autoPlayVideo
if (options.email !== undefined && options.email !== null) toSend.email = options.email
if (options.description !== undefined && options.description !== null) toSend.description = options.description
if (options.displayName !== undefined && options.displayName !== null) toSend.displayName = options.displayName
if (options.theme !== undefined && options.theme !== null) toSend.theme = options.theme
if (options.videosHistoryEnabled !== undefined && options.videosHistoryEnabled !== null) {
toSend['videosHistoryEnabled'] = options.videosHistoryEnabled
toSend.videosHistoryEnabled = options.videosHistoryEnabled
}
return makePutBodyRequest({

View file

@ -22,22 +22,27 @@ function getBaseBitrate (resolution: VideoResolution) {
// quality according to Google Live Encoder: 300 - 700 Kbps
// Quality according to YouTube Video Info: 186 Kbps
return 250 * 1000
case VideoResolution.H_360P:
// quality according to Google Live Encoder: 400 - 1,000 Kbps
// Quality according to YouTube Video Info: 480 Kbps
return 500 * 1000
case VideoResolution.H_480P:
// quality according to Google Live Encoder: 500 - 2,000 Kbps
// Quality according to YouTube Video Info: 879 Kbps
return 900 * 1000
case VideoResolution.H_720P:
// quality according to Google Live Encoder: 1,500 - 4,000 Kbps
// Quality according to YouTube Video Info: 1752 Kbps
return 1750 * 1000
case VideoResolution.H_1080P:
// quality according to Google Live Encoder: 3000 - 6000 Kbps
// Quality according to YouTube Video Info: 3277 Kbps
return 3300 * 1000
case VideoResolution.H_4K: // fallthrough
default:
// quality according to Google Live Encoder: 13000 - 34000 Kbps

View file

@ -75,6 +75,7 @@ You can access it as `peertube` via an alias in your `.bashrc` like `alias peert
import-videos|import import a video from a streaming platform
watch|w watch a video in the terminal ✩°。⋆
repl initiate a REPL to access internals
plugins|p [action] manag instance plugins
help [cmd] display help for [cmd]
```
@ -102,6 +103,15 @@ And now that your video is online, you can watch it from the confort of your ter
$ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10
```
To list, install, uninstall dynamically plugins/themes of an instance:
```bash
$ peertube plugins list
$ peertube plugins install --path /local/plugin/path
$ peertube plugins install --npm-name peertube-plugin-myplugin
$ peertube plugins uninstall --npm-name peertube-plugin-myplugin
```
#### peertube-import-videos.js
You can use this script to import videos from all [supported sites of youtube-dl](https://rg3.github.io/youtube-dl/supportedsites.html) into PeerTube.
@ -233,6 +243,30 @@ To reset a user password from CLI, run:
$ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run reset-password -- -u target_username
```
### plugin install/uninstall
The difference with `peertube plugins` CLI is that these scripts can be used even if PeerTube is not running.
If PeerTube is running, you need to restart it for the changes to take effect (whereas with `peertube plugins` CLI, plugins/themes are dynamically loaded on the server).
To install a plugin or a theme from the disk:
```
$ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run npm run plugin:install -- --plugin-path /local/plugin/path
```
From NPM:
```
$ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run npm run plugin:install -- --npm-name peertube-plugin-myplugin
```
To uninstall a plugin or a theme:
```
$ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run npm run plugin:uninstall -- --npm-name peertube-plugin-myplugin
```
### REPL ([Read Eval Print Loop](https://nodejs.org/docs/latest-v8.x/api/repl.html))
If you want to interact with the application libraries and objects even when PeerTube is not running, there is a REPL for that.