aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/activitypub/client.ts4
-rw-r--r--server/controllers/activitypub/inbox.ts2
-rw-r--r--server/controllers/api/abuse.ts2
-rw-r--r--server/controllers/api/accounts.ts20
-rw-r--r--server/controllers/api/bulk.ts6
-rw-r--r--server/controllers/api/config.ts2
-rw-r--r--server/controllers/api/custom-page.ts3
-rw-r--r--server/controllers/api/index.ts2
-rw-r--r--server/controllers/api/oauth-clients.ts2
-rw-r--r--server/controllers/api/overviews.ts25
-rw-r--r--server/controllers/api/plugins.ts2
-rw-r--r--server/controllers/api/search/search-video-channels.ts24
-rw-r--r--server/controllers/api/search/search-video-playlists.ts20
-rw-r--r--server/controllers/api/search/search-videos.ts27
-rw-r--r--server/controllers/api/server/contact.ts10
-rw-r--r--server/controllers/api/server/debug.ts8
-rw-r--r--server/controllers/api/server/follows.ts23
-rw-r--r--server/controllers/api/server/index.ts8
-rw-r--r--server/controllers/api/server/logs.ts11
-rw-r--r--server/controllers/api/server/redundancy.ts16
-rw-r--r--server/controllers/api/server/server-blocklist.ts4
-rw-r--r--server/controllers/api/server/stats.ts4
-rw-r--r--server/controllers/api/users/index.ts6
-rw-r--r--server/controllers/api/users/me.ts4
-rw-r--r--server/controllers/api/users/my-blocklist.ts10
-rw-r--r--server/controllers/api/users/my-history.ts6
-rw-r--r--server/controllers/api/users/my-notifications.ts2
-rw-r--r--server/controllers/api/users/my-subscriptions.ts17
-rw-r--r--server/controllers/api/users/my-video-playlists.ts2
-rw-r--r--server/controllers/api/video-channel.ts18
-rw-r--r--server/controllers/api/video-playlist.ts9
-rw-r--r--server/controllers/api/videos/blacklist.ts2
-rw-r--r--server/controllers/api/videos/captions.ts2
-rw-r--r--server/controllers/api/videos/comment.ts8
-rw-r--r--server/controllers/api/videos/index.ts18
-rw-r--r--server/controllers/api/videos/live.ts2
-rw-r--r--server/controllers/api/videos/ownership.ts14
-rw-r--r--server/controllers/api/videos/rate.ts4
-rw-r--r--server/controllers/api/videos/update.ts4
-rw-r--r--server/controllers/api/videos/upload.ts18
-rw-r--r--server/controllers/api/videos/watching.ts2
-rw-r--r--server/controllers/bots.ts18
-rw-r--r--server/controllers/client.ts2
-rw-r--r--server/controllers/download.ts3
-rw-r--r--server/controllers/feeds.ts26
-rw-r--r--server/controllers/lazy-static.ts2
-rw-r--r--server/controllers/live.ts2
-rw-r--r--server/controllers/plugins.ts2
-rw-r--r--server/controllers/static.ts14
-rw-r--r--server/helpers/custom-validators/follows.ts20
-rw-r--r--server/helpers/custom-validators/misc.ts15
-rw-r--r--server/helpers/custom-validators/servers.ts1
-rw-r--r--server/helpers/custom-validators/video-channels.ts11
-rw-r--r--server/helpers/custom-validators/video-ownership.ts2
-rw-r--r--server/helpers/database-utils.ts34
-rw-r--r--server/helpers/express-utils.ts2
-rw-r--r--server/helpers/ffmpeg-utils.ts21
-rw-r--r--server/helpers/logger.ts23
-rw-r--r--server/helpers/query.ts74
-rw-r--r--server/helpers/webtorrent.ts5
-rw-r--r--server/helpers/youtube-dl.ts2
-rw-r--r--server/initializers/constants.ts4
-rw-r--r--server/initializers/migrations/0655-streaming-playlist-filenames.ts66
-rw-r--r--server/lib/activitypub/actors/refresh.ts2
-rw-r--r--server/lib/activitypub/crawl.ts3
-rw-r--r--server/lib/activitypub/follow.ts17
-rw-r--r--server/lib/activitypub/playlists/refresh.ts2
-rw-r--r--server/lib/activitypub/url.ts12
-rw-r--r--server/lib/activitypub/videos/refresh.ts2
-rw-r--r--server/lib/activitypub/videos/shared/abstract-builder.ts12
-rw-r--r--server/lib/activitypub/videos/shared/object-to-model-attributes.ts13
-rw-r--r--server/lib/client-html.ts15
-rw-r--r--server/lib/emailer.ts440
-rw-r--r--server/lib/hls.ts39
-rw-r--r--server/lib/job-queue/handlers/activitypub-cleaner.ts2
-rw-r--r--server/lib/job-queue/handlers/video-file-import.ts7
-rw-r--r--server/lib/job-queue/handlers/video-import.ts8
-rw-r--r--server/lib/job-queue/handlers/video-live-ending.ts15
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts16
-rw-r--r--server/lib/live/live-manager.ts32
-rw-r--r--server/lib/live/shared/muxing-session.ts7
-rw-r--r--server/lib/moderation.ts2
-rw-r--r--server/lib/notifier.ts796
-rw-r--r--server/lib/notifier/index.ts1
-rw-r--r--server/lib/notifier/notifier.ts259
-rw-r--r--server/lib/notifier/shared/abuse/abstract-new-abuse-message.ts67
-rw-r--r--server/lib/notifier/shared/abuse/abuse-state-change-for-reporter.ts74
-rw-r--r--server/lib/notifier/shared/abuse/index.ts4
-rw-r--r--server/lib/notifier/shared/abuse/new-abuse-for-moderators.ts119
-rw-r--r--server/lib/notifier/shared/abuse/new-abuse-message-for-moderators.ts32
-rw-r--r--server/lib/notifier/shared/abuse/new-abuse-message-for-reporter.ts36
-rw-r--r--server/lib/notifier/shared/blacklist/index.ts3
-rw-r--r--server/lib/notifier/shared/blacklist/new-auto-blacklist-for-moderators.ts60
-rw-r--r--server/lib/notifier/shared/blacklist/new-blacklist-for-owner.ts58
-rw-r--r--server/lib/notifier/shared/blacklist/unblacklist-for-owner.ts55
-rw-r--r--server/lib/notifier/shared/comment/comment-mention.ts111
-rw-r--r--server/lib/notifier/shared/comment/index.ts2
-rw-r--r--server/lib/notifier/shared/comment/new-comment-for-video-owner.ts76
-rw-r--r--server/lib/notifier/shared/common/abstract-notification.ts23
-rw-r--r--server/lib/notifier/shared/common/index.ts1
-rw-r--r--server/lib/notifier/shared/follow/auto-follow-for-instance.ts51
-rw-r--r--server/lib/notifier/shared/follow/follow-for-instance.ts68
-rw-r--r--server/lib/notifier/shared/follow/follow-for-user.ts82
-rw-r--r--server/lib/notifier/shared/follow/index.ts3
-rw-r--r--server/lib/notifier/shared/index.ts7
-rw-r--r--server/lib/notifier/shared/instance/index.ts3
-rw-r--r--server/lib/notifier/shared/instance/new-peertube-version-for-admins.ts54
-rw-r--r--server/lib/notifier/shared/instance/new-plugin-version-for-admins.ts58
-rw-r--r--server/lib/notifier/shared/instance/registration-for-moderators.ts49
-rw-r--r--server/lib/notifier/shared/video-publication/abstract-owned-video-publication.ts57
-rw-r--r--server/lib/notifier/shared/video-publication/import-finished-for-owner.ts97
-rw-r--r--server/lib/notifier/shared/video-publication/index.ts5
-rw-r--r--server/lib/notifier/shared/video-publication/new-video-for-subscribers.ts61
-rw-r--r--server/lib/notifier/shared/video-publication/owned-publication-after-auto-unblacklist.ts11
-rw-r--r--server/lib/notifier/shared/video-publication/owned-publication-after-schedule-update.ts10
-rw-r--r--server/lib/notifier/shared/video-publication/owned-publication-after-transcoding.ts9
-rw-r--r--server/lib/plugins/register-helpers.ts222
-rw-r--r--server/lib/plugins/video-constant-manager-factory.ts139
-rw-r--r--server/lib/schedulers/plugins-check-scheduler.ts10
-rw-r--r--server/lib/schedulers/videos-redundancy-scheduler.ts7
-rw-r--r--server/lib/transcoding/video-transcoding.ts81
-rw-r--r--server/lib/video-paths.ts56
-rw-r--r--server/lib/video.ts4
-rw-r--r--server/middlewares/activitypub.ts2
-rw-r--r--server/middlewares/auth.ts2
-rw-r--r--server/middlewares/cache.ts28
-rw-r--r--server/middlewares/cache/cache.ts32
-rw-r--r--server/middlewares/cache/index.ts1
-rw-r--r--server/middlewares/cache/shared/api-cache.ts269
-rw-r--r--server/middlewares/cache/shared/index.ts1
-rw-r--r--server/middlewares/error.ts2
-rw-r--r--server/middlewares/index.ts1
-rw-r--r--server/middlewares/servers.ts2
-rw-r--r--server/middlewares/user-right.ts2
-rw-r--r--server/middlewares/validators/abuse.ts2
-rw-r--r--server/middlewares/validators/activitypub/activity.ts4
-rw-r--r--server/middlewares/validators/blocklist.ts2
-rw-r--r--server/middlewares/validators/bulk.ts3
-rw-r--r--server/middlewares/validators/feeds.ts3
-rw-r--r--server/middlewares/validators/follows.ts39
-rw-r--r--server/middlewares/validators/oembed.ts2
-rw-r--r--server/middlewares/validators/plugins.ts2
-rw-r--r--server/middlewares/validators/redundancy.ts2
-rw-r--r--server/middlewares/validators/search.ts58
-rw-r--r--server/middlewares/validators/server.ts2
-rw-r--r--server/middlewares/validators/shared/abuses.ts2
-rw-r--r--server/middlewares/validators/shared/accounts.ts2
-rw-r--r--server/middlewares/validators/shared/video-blacklists.ts2
-rw-r--r--server/middlewares/validators/shared/video-captions.ts2
-rw-r--r--server/middlewares/validators/shared/video-channels.ts2
-rw-r--r--server/middlewares/validators/shared/video-comments.ts2
-rw-r--r--server/middlewares/validators/shared/video-imports.ts2
-rw-r--r--server/middlewares/validators/shared/video-ownerships.ts2
-rw-r--r--server/middlewares/validators/shared/video-playlists.ts2
-rw-r--r--server/middlewares/validators/shared/videos.ts3
-rw-r--r--server/middlewares/validators/themes.ts2
-rw-r--r--server/middlewares/validators/user-subscriptions.ts2
-rw-r--r--server/middlewares/validators/users.ts14
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts2
-rw-r--r--server/middlewares/validators/videos/video-channels.ts16
-rw-r--r--server/middlewares/validators/videos/video-comments.ts2
-rw-r--r--server/middlewares/validators/videos/video-imports.ts2
-rw-r--r--server/middlewares/validators/videos/video-live.ts3
-rw-r--r--server/middlewares/validators/videos/video-ownership-changes.ts10
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts2
-rw-r--r--server/middlewares/validators/videos/video-rates.ts2
-rw-r--r--server/middlewares/validators/videos/video-shares.ts2
-rw-r--r--server/middlewares/validators/videos/video-watch.ts2
-rw-r--r--server/middlewares/validators/videos/videos.ts4
-rw-r--r--server/middlewares/validators/webfinger.ts2
-rw-r--r--server/models/account/account.ts8
-rw-r--r--server/models/actor/actor-follow.ts36
-rw-r--r--server/models/redundancy/video-redundancy.ts4
-rw-r--r--server/models/shared/index.ts2
-rw-r--r--server/models/shared/query.ts17
-rw-r--r--server/models/shared/update.ts18
-rw-r--r--server/models/user/user-notification.ts3
-rw-r--r--server/models/video/formatter/video-format-utils.ts8
-rw-r--r--server/models/video/sql/shared/video-tables.ts3
-rw-r--r--server/models/video/sql/videos-id-list-query-builder.ts35
-rw-r--r--server/models/video/video-channel.ts169
-rw-r--r--server/models/video/video-file.ts45
-rw-r--r--server/models/video/video-playlist.ts64
-rw-r--r--server/models/video/video-streaming-playlist.ts85
-rw-r--r--server/models/video/video.ts131
-rw-r--r--server/tests/api/activitypub/cleaner.ts105
-rw-r--r--server/tests/api/activitypub/client.ts24
-rw-r--r--server/tests/api/activitypub/fetch.ts51
-rw-r--r--server/tests/api/activitypub/helpers.ts5
-rw-r--r--server/tests/api/activitypub/refresher.ts96
-rw-r--r--server/tests/api/activitypub/security.ts46
-rw-r--r--server/tests/api/check-params/abuses.ts205
-rw-r--r--server/tests/api/check-params/accounts.ts19
-rw-r--r--server/tests/api/check-params/blocklist.ts107
-rw-r--r--server/tests/api/check-params/bulk.ts30
-rw-r--r--server/tests/api/check-params/config.ts65
-rw-r--r--server/tests/api/check-params/contact-form.ts84
-rw-r--r--server/tests/api/check-params/custom-pages.ts31
-rw-r--r--server/tests/api/check-params/debug.ts27
-rw-r--r--server/tests/api/check-params/follows.ts112
-rw-r--r--server/tests/api/check-params/jobs.ts33
-rw-r--r--server/tests/api/check-params/live.ts225
-rw-r--r--server/tests/api/check-params/logs.ts35
-rw-r--r--server/tests/api/check-params/plugins.ts95
-rw-r--r--server/tests/api/check-params/redundancy.ts60
-rw-r--r--server/tests/api/check-params/search.ts201
-rw-r--r--server/tests/api/check-params/services.ts49
-rw-r--r--server/tests/api/check-params/upload-quota.ts97
-rw-r--r--server/tests/api/check-params/user-notifications.ts57
-rw-r--r--server/tests/api/check-params/user-subscriptions.ts80
-rw-r--r--server/tests/api/check-params/users.ts425
-rw-r--r--server/tests/api/check-params/video-blacklist.ts140
-rw-r--r--server/tests/api/check-params/video-captions.ts60
-rw-r--r--server/tests/api/check-params/video-channels.ts88
-rw-r--r--server/tests/api/check-params/video-comments.ts107
-rw-r--r--server/tests/api/check-params/video-imports.ts124
-rw-r--r--server/tests/api/check-params/video-playlists.ts394
-rw-r--r--server/tests/api/check-params/videos-filter.ts44
-rw-r--r--server/tests/api/check-params/videos-history.ts43
-rw-r--r--server/tests/api/check-params/videos-overviews.ts13
-rw-r--r--server/tests/api/check-params/videos.ts243
-rw-r--r--server/tests/api/live/live-constraints.ts81
-rw-r--r--server/tests/api/live/live-permanent.ts99
-rw-r--r--server/tests/api/live/live-save-replay.ts126
-rw-r--r--server/tests/api/live/live-socket-messages.ts71
-rw-r--r--server/tests/api/live/live-views.ts51
-rw-r--r--server/tests/api/live/live.ts289
-rw-r--r--server/tests/api/moderation/abuses.ts756
-rw-r--r--server/tests/api/moderation/blocklist-notification.ts121
-rw-r--r--server/tests/api/moderation/blocklist.ts446
-rw-r--r--server/tests/api/moderation/video-blacklist.ts304
-rw-r--r--server/tests/api/notifications/admin-notifications.ts49
-rw-r--r--server/tests/api/notifications/comments-notifications.ts207
-rw-r--r--server/tests/api/notifications/moderation-notifications.ts288
-rw-r--r--server/tests/api/notifications/notifications-api.ts132
-rw-r--r--server/tests/api/notifications/user-notifications.ts197
-rw-r--r--server/tests/api/redundancy/manage-redundancy.ts193
-rw-r--r--server/tests/api/redundancy/redundancy-constraints.ts69
-rw-r--r--server/tests/api/redundancy/redundancy.ts298
-rw-r--r--server/tests/api/search/search-activitypub-video-channels.ts171
-rw-r--r--server/tests/api/search/search-activitypub-video-playlists.ts150
-rw-r--r--server/tests/api/search/search-activitypub-videos.ts124
-rw-r--r--server/tests/api/search/search-channels.ts116
-rw-r--r--server/tests/api/search/search-index.ts422
-rw-r--r--server/tests/api/search/search-playlists.ts159
-rw-r--r--server/tests/api/search/search-videos.ts393
-rw-r--r--server/tests/api/server/auto-follows.ts71
-rw-r--r--server/tests/api/server/bulk.ts123
-rw-r--r--server/tests/api/server/config.ts86
-rw-r--r--server/tests/api/server/contact-form.ts37
-rw-r--r--server/tests/api/server/email.ts116
-rw-r--r--server/tests/api/server/follow-constraints.ts178
-rw-r--r--server/tests/api/server/follows-moderation.ts109
-rw-r--r--server/tests/api/server/follows.ts689
-rw-r--r--server/tests/api/server/handle-down.ts218
-rw-r--r--server/tests/api/server/homepage.ts45
-rw-r--r--server/tests/api/server/jobs.ts61
-rw-r--r--server/tests/api/server/logs.ts88
-rw-r--r--server/tests/api/server/no-client.ts9
-rw-r--r--server/tests/api/server/plugins.ts252
-rw-r--r--server/tests/api/server/reverse-proxy.ts68
-rw-r--r--server/tests/api/server/services.ts56
-rw-r--r--server/tests/api/server/stats.ts170
-rw-r--r--server/tests/api/server/tracker.ts31
-rw-r--r--server/tests/api/users/user-subscriptions.ts230
-rw-r--r--server/tests/api/users/users-multiple-servers.ts148
-rw-r--r--server/tests/api/users/users-verification.ts107
-rw-r--r--server/tests/api/users/users.ts657
-rw-r--r--server/tests/api/videos/audio-only.ts37
-rw-r--r--server/tests/api/videos/multiple-servers.ts472
-rw-r--r--server/tests/api/videos/resumable-upload.ts70
-rw-r--r--server/tests/api/videos/single-server.ts287
-rw-r--r--server/tests/api/videos/video-captions.ts96
-rw-r--r--server/tests/api/videos/video-change-ownership.ts320
-rw-r--r--server/tests/api/videos/video-channels.ts352
-rw-r--r--server/tests/api/videos/video-comments.ts225
-rw-r--r--server/tests/api/videos/video-description.ts47
-rw-r--r--server/tests/api/videos/video-hls.ts121
-rw-r--r--server/tests/api/videos/video-imports.ts172
-rw-r--r--server/tests/api/videos/video-nsfw.ts226
-rw-r--r--server/tests/api/videos/video-playlist-thumbnails.ts144
-rw-r--r--server/tests/api/videos/video-playlists.ts727
-rw-r--r--server/tests/api/videos/video-privacy.ts157
-rw-r--r--server/tests/api/videos/video-schedule-update.ts77
-rw-r--r--server/tests/api/videos/video-transcoder.ts373
-rw-r--r--server/tests/api/videos/videos-filter.ts49
-rw-r--r--server/tests/api/videos/videos-history.ts139
-rw-r--r--server/tests/api/videos/videos-overview.ts97
-rw-r--r--server/tests/api/videos/videos-views-cleaner.ts47
-rw-r--r--server/tests/cli/create-import-video-file-job.ts70
-rw-r--r--server/tests/cli/create-transcoding-job.ts110
-rw-r--r--server/tests/cli/optimize-old-videos.ts65
-rw-r--r--server/tests/cli/peertube.ts163
-rw-r--r--server/tests/cli/plugins.ts42
-rw-r--r--server/tests/cli/print-transcode-command.ts7
-rw-r--r--server/tests/cli/prune-storage.ts124
-rw-r--r--server/tests/cli/regenerate-thumbnails.ts52
-rw-r--r--server/tests/cli/reset-password.ts25
-rw-r--r--server/tests/cli/update-host.ts90
-rw-r--r--server/tests/client.ts234
-rw-r--r--server/tests/external-plugins/auth-ldap.ts69
-rw-r--r--server/tests/external-plugins/auto-block-videos.ts75
-rw-r--r--server/tests/external-plugins/auto-mute.ts98
-rw-r--r--server/tests/feeds/feeds.ts275
-rw-r--r--server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js7
-rw-r--r--server/tests/fixtures/peertube-plugin-test-video-constants/main.js46
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js9
-rw-r--r--server/tests/fixtures/video_very_short_240p.mp4bin0 -> 9352 bytes
-rw-r--r--server/tests/helpers/comment-model.ts2
-rw-r--r--server/tests/helpers/core-utils.ts4
-rw-r--r--server/tests/helpers/image.ts2
-rw-r--r--server/tests/helpers/request.ts10
-rw-r--r--server/tests/index.ts1
-rw-r--r--server/tests/lib/index.ts1
-rw-r--r--server/tests/lib/video-constant-registry-factory.ts155
-rw-r--r--server/tests/misc-endpoints.ts56
-rw-r--r--server/tests/plugins/action-hooks.ts111
-rw-r--r--server/tests/plugins/external-auth.ts184
-rw-r--r--server/tests/plugins/filter-hooks.ts408
-rw-r--r--server/tests/plugins/html-injection.ts41
-rw-r--r--server/tests/plugins/id-and-pass-auth.ts141
-rw-r--r--server/tests/plugins/plugin-helpers.ts114
-rw-r--r--server/tests/plugins/plugin-router.ts43
-rw-r--r--server/tests/plugins/plugin-storage.ts45
-rw-r--r--server/tests/plugins/plugin-transcoding.ts259
-rw-r--r--server/tests/plugins/plugin-unloading.ts45
-rw-r--r--server/tests/plugins/translations.ts51
-rw-r--r--server/tests/plugins/video-constants.ts118
-rw-r--r--server/tools/cli.ts44
-rw-r--r--server/tools/peertube-auth.ts6
-rw-r--r--server/tools/peertube-get-access-token.ts23
-rw-r--r--server/tools/peertube-import-videos.ts110
-rw-r--r--server/tools/peertube-plugins.ts49
-rw-r--r--server/tools/peertube-redundancy.ts60
-rw-r--r--server/tools/peertube-upload.ts17
-rw-r--r--server/tools/test-live.ts (renamed from server/tools/test.ts)61
-rw-r--r--server/types/models/video/video-streaming-playlist.ts2
-rw-r--r--server/typings/express/index.d.ts14
338 files changed, 12753 insertions, 13167 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index d7de1b9bd..bef4bc068 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -24,7 +24,7 @@ import {
24 videosCustomGetValidator, 24 videosCustomGetValidator,
25 videosShareValidator 25 videosShareValidator
26} from '../../middlewares' 26} from '../../middlewares'
27import { cacheRoute } from '../../middlewares/cache' 27import { cacheRoute } from '../../middlewares/cache/cache'
28import { getAccountVideoRateValidatorFactory, videoCommentGetValidator } from '../../middlewares/validators' 28import { getAccountVideoRateValidatorFactory, videoCommentGetValidator } from '../../middlewares/validators'
29import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy' 29import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy'
30import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' 30import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists'
@@ -77,7 +77,7 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
77activityPubClientRouter.get( 77activityPubClientRouter.get(
78 [ '/videos/watch/:id', '/w/:id' ], 78 [ '/videos/watch/:id', '/w/:id' ],
79 executeIfActivityPub, 79 executeIfActivityPub,
80 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS)), 80 cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
81 asyncMiddleware(videosCustomGetValidator('all')), 81 asyncMiddleware(videosCustomGetValidator('all')),
82 asyncMiddleware(videoController) 82 asyncMiddleware(videoController)
83) 83)
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts
index 14f301ab7..30662990a 100644
--- a/server/controllers/activitypub/inbox.ts
+++ b/server/controllers/activitypub/inbox.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { InboxManager } from '@server/lib/activitypub/inbox-manager' 2import { InboxManager } from '@server/lib/activitypub/inbox-manager'
3import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, RootActivity } from '../../../shared' 3import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, RootActivity } from '../../../shared'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity' 5import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChannelValidator, signatureValidator } from '../../middlewares' 7import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChannelValidator, signatureValidator } from '../../middlewares'
diff --git a/server/controllers/api/abuse.ts b/server/controllers/api/abuse.ts
index ba5b94840..e851365e9 100644
--- a/server/controllers/api/abuse.ts
+++ b/server/controllers/api/abuse.ts
@@ -6,7 +6,7 @@ import { AbuseModel } from '@server/models/abuse/abuse'
6import { AbuseMessageModel } from '@server/models/abuse/abuse-message' 6import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
7import { getServerActor } from '@server/models/application/application' 7import { getServerActor } from '@server/models/application/application'
8import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 8import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode } from '@shared/models'
10import { AbuseCreate, AbuseState, UserRight } from '../../../shared' 10import { AbuseCreate, AbuseState, UserRight } from '../../../shared'
11import { getFormattedObjects } from '../../helpers/utils' 11import { getFormattedObjects } from '../../helpers/utils'
12import { sequelizeTypescript } from '../../initializers/database' 12import { sequelizeTypescript } from '../../initializers/database'
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 49a8e3195..55e2aaf62 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { pickCommonVideoQuery } from '@server/helpers/query'
2import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
3import { VideosWithSearchCommonQuery } from '@shared/models'
4import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 4import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
5import { getFormattedObjects } from '../../helpers/utils' 5import { getFormattedObjects } from '../../helpers/utils'
6import { JobQueue } from '../../lib/job-queue' 6import { JobQueue } from '../../lib/job-queue'
@@ -159,27 +159,19 @@ async function listAccountVideos (req: express.Request, res: express.Response) {
159 const account = res.locals.account 159 const account = res.locals.account
160 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined 160 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined
161 const countVideos = getCountVideos(req) 161 const countVideos = getCountVideos(req)
162 const query = req.query as VideosWithSearchCommonQuery 162 const query = pickCommonVideoQuery(req.query)
163 163
164 const apiOptions = await Hooks.wrapObject({ 164 const apiOptions = await Hooks.wrapObject({
165 ...query,
166
165 followerActorId, 167 followerActorId,
166 start: query.start, 168 search: req.query.search,
167 count: query.count,
168 sort: query.sort,
169 includeLocalVideos: true, 169 includeLocalVideos: true,
170 categoryOneOf: query.categoryOneOf,
171 licenceOneOf: query.licenceOneOf,
172 languageOneOf: query.languageOneOf,
173 tagsOneOf: query.tagsOneOf,
174 tagsAllOf: query.tagsAllOf,
175 filter: query.filter,
176 isLive: query.isLive,
177 nsfw: buildNSFWFilter(res, query.nsfw), 170 nsfw: buildNSFWFilter(res, query.nsfw),
178 withFiles: false, 171 withFiles: false,
179 accountId: account.id, 172 accountId: account.id,
180 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 173 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
181 countVideos, 174 countVideos
182 search: query.search
183 }, 'filter:api.accounts.videos.list.params') 175 }, 'filter:api.accounts.videos.list.params')
184 176
185 const resultList = await Hooks.wrapPromiseFun( 177 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/bulk.ts b/server/controllers/api/bulk.ts
index 192daccde..62121ece5 100644
--- a/server/controllers/api/bulk.ts
+++ b/server/controllers/api/bulk.ts
@@ -1,10 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware, authenticate } from '../../middlewares' 2import { removeComment } from '@server/lib/video-comment'
3import { bulkRemoveCommentsOfValidator } from '@server/middlewares/validators/bulk' 3import { bulkRemoveCommentsOfValidator } from '@server/middlewares/validators/bulk'
4import { VideoCommentModel } from '@server/models/video/video-comment' 4import { VideoCommentModel } from '@server/models/video/video-comment'
5import { removeComment } from '@server/lib/video-comment' 5import { HttpStatusCode } from '@shared/models'
6import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model' 6import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model'
7import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 7import { asyncMiddleware, authenticate } from '../../middlewares'
8 8
9const bulkRouter = express.Router() 9const bulkRouter = express.Router()
10 10
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index 9bd8c21c5..ee733a38c 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -1,8 +1,8 @@
1import { ServerConfigManager } from '@server/lib/server-config-manager'
2import * as express from 'express' 1import * as express from 'express'
3import { remove, writeJSON } from 'fs-extra' 2import { remove, writeJSON } from 'fs-extra'
4import { snakeCase } from 'lodash' 3import { snakeCase } from 'lodash'
5import validator from 'validator' 4import validator from 'validator'
5import { ServerConfigManager } from '@server/lib/server-config-manager'
6import { UserRight } from '../../../shared' 6import { UserRight } from '../../../shared'
7import { About } from '../../../shared/models/server/about.model' 7import { About } from '../../../shared/models/server/about.model'
8import { CustomConfig } from '../../../shared/models/server/custom-config.model' 8import { CustomConfig } from '../../../shared/models/server/custom-config.model'
diff --git a/server/controllers/api/custom-page.ts b/server/controllers/api/custom-page.ts
index c19f03c56..68d8c2ea4 100644
--- a/server/controllers/api/custom-page.ts
+++ b/server/controllers/api/custom-page.ts
@@ -1,8 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { ServerConfigManager } from '@server/lib/server-config-manager' 2import { ServerConfigManager } from '@server/lib/server-config-manager'
3import { ActorCustomPageModel } from '@server/models/account/actor-custom-page' 3import { ActorCustomPageModel } from '@server/models/account/actor-custom-page'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode, UserRight } from '@shared/models'
5import { UserRight } from '@shared/models'
6import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares' 5import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
7 6
8const customPageRouter = express.Router() 7const customPageRouter = express.Router()
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts
index 28378654a..93b14dadb 100644
--- a/server/controllers/api/index.ts
+++ b/server/controllers/api/index.ts
@@ -1,7 +1,7 @@
1import * as cors from 'cors' 1import * as cors from 'cors'
2import * as express from 'express' 2import * as express from 'express'
3import * as RateLimit from 'express-rate-limit' 3import * as RateLimit from 'express-rate-limit'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models'
5import { badRequest } from '../../helpers/express-utils' 5import { badRequest } from '../../helpers/express-utils'
6import { CONFIG } from '../../initializers/config' 6import { CONFIG } from '../../initializers/config'
7import { abuseRouter } from './abuse' 7import { abuseRouter } from './abuse'
diff --git a/server/controllers/api/oauth-clients.ts b/server/controllers/api/oauth-clients.ts
index 15bbf5c4d..f95f06864 100644
--- a/server/controllers/api/oauth-clients.ts
+++ b/server/controllers/api/oauth-clients.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { OAuthClientLocal } from '../../../shared' 2import { OAuthClientLocal } from '../../../shared'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
5import { CONFIG } from '../../initializers/config' 5import { CONFIG } from '../../initializers/config'
6import { asyncMiddleware, openapiOperationDoc } from '../../middlewares' 6import { asyncMiddleware, openapiOperationDoc } from '../../middlewares'
diff --git a/server/controllers/api/overviews.ts b/server/controllers/api/overviews.ts
index ad879aad6..2dfac15ef 100644
--- a/server/controllers/api/overviews.ts
+++ b/server/controllers/api/overviews.ts
@@ -1,12 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as memoizee from 'memoizee'
3import { logger } from '@server/helpers/logger'
4import { Hooks } from '@server/lib/plugins/hooks'
5import { VideoModel } from '@server/models/video/video'
6import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
2import { buildNSFWFilter } from '../../helpers/express-utils' 7import { buildNSFWFilter } from '../../helpers/express-utils'
3import { VideoModel } from '../../models/video/video' 8import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
4import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares' 9import { asyncMiddleware, optionalAuthenticate, videosOverviewValidator } from '../../middlewares'
5import { TagModel } from '../../models/video/tag' 10import { TagModel } from '../../models/video/tag'
6import { CategoryOverview, ChannelOverview, TagOverview, VideosOverview } from '../../../shared/models/overviews'
7import { MEMOIZE_TTL, OVERVIEWS } from '../../initializers/constants'
8import * as memoizee from 'memoizee'
9import { logger } from '@server/helpers/logger'
10 11
11const overviewsRouter = express.Router() 12const overviewsRouter = express.Router()
12 13
@@ -108,7 +109,7 @@ async function getVideos (
108 res: express.Response, 109 res: express.Response,
109 where: { videoChannelId?: number, tagsOneOf?: string[], categoryOneOf?: number[] } 110 where: { videoChannelId?: number, tagsOneOf?: string[], categoryOneOf?: number[] }
110) { 111) {
111 const query = Object.assign({ 112 const query = await Hooks.wrapObject({
112 start: 0, 113 start: 0,
113 count: 12, 114 count: 12,
114 sort: '-createdAt', 115 sort: '-createdAt',
@@ -116,10 +117,16 @@ async function getVideos (
116 nsfw: buildNSFWFilter(res), 117 nsfw: buildNSFWFilter(res),
117 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 118 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
118 withFiles: false, 119 withFiles: false,
119 countVideos: false 120 countVideos: false,
120 }, where) 121
122 ...where
123 }, 'filter:api.overviews.videos.list.params')
121 124
122 const { data } = await VideoModel.listForApi(query) 125 const { data } = await Hooks.wrapPromiseFun(
126 VideoModel.listForApi,
127 query,
128 'filter:api.overviews.videos.list.result'
129 )
123 130
124 return data.map(d => d.toFormattedJSON()) 131 return data.map(d => d.toFormattedJSON())
125} 132}
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts
index 1e6a02c49..3a9ef34e8 100644
--- a/server/controllers/api/plugins.ts
+++ b/server/controllers/api/plugins.ts
@@ -23,8 +23,8 @@ import {
23 updatePluginSettingsValidator 23 updatePluginSettingsValidator
24} from '@server/middlewares/validators/plugins' 24} from '@server/middlewares/validators/plugins'
25import { PluginModel } from '@server/models/server/plugin' 25import { PluginModel } from '@server/models/server/plugin'
26import { HttpStatusCode } from '@shared/core-utils'
27import { 26import {
27 HttpStatusCode,
28 InstallOrUpdatePlugin, 28 InstallOrUpdatePlugin,
29 ManagePlugin, 29 ManagePlugin,
30 PeertubePluginIndexList, 30 PeertubePluginIndexList,
diff --git a/server/controllers/api/search/search-video-channels.ts b/server/controllers/api/search/search-video-channels.ts
index 16beeed60..eef222506 100644
--- a/server/controllers/api/search/search-video-channels.ts
+++ b/server/controllers/api/search/search-video-channels.ts
@@ -1,14 +1,14 @@
1import * as express from 'express' 1import * as express from 'express'
2import { sanitizeUrl } from '@server/helpers/core-utils' 2import { sanitizeUrl } from '@server/helpers/core-utils'
3import { pickSearchChannelQuery } from '@server/helpers/query'
3import { doJSONRequest } from '@server/helpers/requests' 4import { doJSONRequest } from '@server/helpers/requests'
4import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
5import { WEBSERVER } from '@server/initializers/constants' 6import { WEBSERVER } from '@server/initializers/constants'
6import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
7import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 8import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
8import { getServerActor } from '@server/models/application/application' 9import { getServerActor } from '@server/models/application/application'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode, ResultList, VideoChannel } from '@shared/models'
10import { ResultList, VideoChannel } from '@shared/models' 11import { VideoChannelsSearchQueryAfterSanitize } from '../../../../shared/models/search'
11import { VideoChannelsSearchQuery } from '../../../../shared/models/search'
12import { isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils' 12import { isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils'
13import { logger } from '../../../helpers/logger' 13import { logger } from '../../../helpers/logger'
14import { getFormattedObjects } from '../../../helpers/utils' 14import { getFormattedObjects } from '../../../helpers/utils'
@@ -46,8 +46,8 @@ export { searchChannelsRouter }
46// --------------------------------------------------------------------------- 46// ---------------------------------------------------------------------------
47 47
48function searchVideoChannels (req: express.Request, res: express.Response) { 48function searchVideoChannels (req: express.Request, res: express.Response) {
49 const query: VideoChannelsSearchQuery = req.query 49 const query = pickSearchChannelQuery(req.query)
50 const search = query.search 50 let search = query.search || ''
51 51
52 const parts = search.split('@') 52 const parts = search.split('@')
53 53
@@ -58,7 +58,7 @@ function searchVideoChannels (req: express.Request, res: express.Response) {
58 if (isURISearch(search) || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res) 58 if (isURISearch(search) || isWebfingerSearch) return searchVideoChannelURI(search, isWebfingerSearch, res)
59 59
60 // @username -> username to search in DB 60 // @username -> username to search in DB
61 if (query.search.startsWith('@')) query.search = query.search.replace(/^@/, '') 61 if (search.startsWith('@')) search = search.replace(/^@/, '')
62 62
63 if (isSearchIndexSearch(query)) { 63 if (isSearchIndexSearch(query)) {
64 return searchVideoChannelsIndex(query, res) 64 return searchVideoChannelsIndex(query, res)
@@ -67,7 +67,7 @@ function searchVideoChannels (req: express.Request, res: express.Response) {
67 return searchVideoChannelsDB(query, res) 67 return searchVideoChannelsDB(query, res)
68} 68}
69 69
70async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: express.Response) { 70async function searchVideoChannelsIndex (query: VideoChannelsSearchQueryAfterSanitize, res: express.Response) {
71 const result = await buildMutedForSearchIndex(res) 71 const result = await buildMutedForSearchIndex(res)
72 72
73 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-channels.index.list.params') 73 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-channels.index.list.params')
@@ -91,15 +91,13 @@ async function searchVideoChannelsIndex (query: VideoChannelsSearchQuery, res: e
91 } 91 }
92} 92}
93 93
94async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: express.Response) { 94async function searchVideoChannelsDB (query: VideoChannelsSearchQueryAfterSanitize, res: express.Response) {
95 const serverActor = await getServerActor() 95 const serverActor = await getServerActor()
96 96
97 const apiOptions = await Hooks.wrapObject({ 97 const apiOptions = await Hooks.wrapObject({
98 actorId: serverActor.id, 98 ...query,
99 search: query.search, 99
100 start: query.start, 100 actorId: serverActor.id
101 count: query.count,
102 sort: query.sort
103 }, 'filter:api.search.video-channels.local.list.params') 101 }, 'filter:api.search.video-channels.local.list.params')
104 102
105 const resultList = await Hooks.wrapPromiseFun( 103 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/search/search-video-playlists.ts b/server/controllers/api/search/search-video-playlists.ts
index b231ff1e2..0a56f19b7 100644
--- a/server/controllers/api/search/search-video-playlists.ts
+++ b/server/controllers/api/search/search-video-playlists.ts
@@ -2,17 +2,18 @@ import * as express from 'express'
2import { sanitizeUrl } from '@server/helpers/core-utils' 2import { sanitizeUrl } from '@server/helpers/core-utils'
3import { isUserAbleToSearchRemoteURI } from '@server/helpers/express-utils' 3import { isUserAbleToSearchRemoteURI } from '@server/helpers/express-utils'
4import { logger } from '@server/helpers/logger' 4import { logger } from '@server/helpers/logger'
5import { pickSearchPlaylistQuery } from '@server/helpers/query'
5import { doJSONRequest } from '@server/helpers/requests' 6import { doJSONRequest } from '@server/helpers/requests'
6import { getFormattedObjects } from '@server/helpers/utils' 7import { getFormattedObjects } from '@server/helpers/utils'
7import { CONFIG } from '@server/initializers/config' 8import { CONFIG } from '@server/initializers/config'
9import { WEBSERVER } from '@server/initializers/constants'
8import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get' 10import { getOrCreateAPVideoPlaylist } from '@server/lib/activitypub/playlists/get'
9import { Hooks } from '@server/lib/plugins/hooks' 11import { Hooks } from '@server/lib/plugins/hooks'
10import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 12import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
11import { getServerActor } from '@server/models/application/application' 13import { getServerActor } from '@server/models/application/application'
12import { VideoPlaylistModel } from '@server/models/video/video-playlist' 14import { VideoPlaylistModel } from '@server/models/video/video-playlist'
13import { MVideoPlaylistFullSummary } from '@server/types/models' 15import { MVideoPlaylistFullSummary } from '@server/types/models'
14import { HttpStatusCode } from '@shared/core-utils' 16import { HttpStatusCode, ResultList, VideoPlaylist, VideoPlaylistsSearchQueryAfterSanitize } from '@shared/models'
15import { ResultList, VideoPlaylist, VideoPlaylistsSearchQuery } from '@shared/models'
16import { 17import {
17 asyncMiddleware, 18 asyncMiddleware,
18 openapiOperationDoc, 19 openapiOperationDoc,
@@ -23,7 +24,6 @@ import {
23 videoPlaylistsListSearchValidator, 24 videoPlaylistsListSearchValidator,
24 videoPlaylistsSearchSortValidator 25 videoPlaylistsSearchSortValidator
25} from '../../../middlewares' 26} from '../../../middlewares'
26import { WEBSERVER } from '@server/initializers/constants'
27 27
28const searchPlaylistsRouter = express.Router() 28const searchPlaylistsRouter = express.Router()
29 29
@@ -45,7 +45,7 @@ export { searchPlaylistsRouter }
45// --------------------------------------------------------------------------- 45// ---------------------------------------------------------------------------
46 46
47function searchVideoPlaylists (req: express.Request, res: express.Response) { 47function searchVideoPlaylists (req: express.Request, res: express.Response) {
48 const query: VideoPlaylistsSearchQuery = req.query 48 const query = pickSearchPlaylistQuery(req.query)
49 const search = query.search 49 const search = query.search
50 50
51 if (isURISearch(search)) return searchVideoPlaylistsURI(search, res) 51 if (isURISearch(search)) return searchVideoPlaylistsURI(search, res)
@@ -57,7 +57,7 @@ function searchVideoPlaylists (req: express.Request, res: express.Response) {
57 return searchVideoPlaylistsDB(query, res) 57 return searchVideoPlaylistsDB(query, res)
58} 58}
59 59
60async function searchVideoPlaylistsIndex (query: VideoPlaylistsSearchQuery, res: express.Response) { 60async function searchVideoPlaylistsIndex (query: VideoPlaylistsSearchQueryAfterSanitize, res: express.Response) {
61 const result = await buildMutedForSearchIndex(res) 61 const result = await buildMutedForSearchIndex(res)
62 62
63 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-playlists.index.list.params') 63 const body = await Hooks.wrapObject(Object.assign(query, result), 'filter:api.search.video-playlists.index.list.params')
@@ -81,15 +81,13 @@ async function searchVideoPlaylistsIndex (query: VideoPlaylistsSearchQuery, res:
81 } 81 }
82} 82}
83 83
84async function searchVideoPlaylistsDB (query: VideoPlaylistsSearchQuery, res: express.Response) { 84async function searchVideoPlaylistsDB (query: VideoPlaylistsSearchQueryAfterSanitize, res: express.Response) {
85 const serverActor = await getServerActor() 85 const serverActor = await getServerActor()
86 86
87 const apiOptions = await Hooks.wrapObject({ 87 const apiOptions = await Hooks.wrapObject({
88 followerActorId: serverActor.id, 88 ...query,
89 search: query.search, 89
90 start: query.start, 90 followerActorId: serverActor.id
91 count: query.count,
92 sort: query.sort
93 }, 'filter:api.search.video-playlists.local.list.params') 91 }, 'filter:api.search.video-playlists.local.list.params')
94 92
95 const resultList = await Hooks.wrapPromiseFun( 93 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/controllers/api/search/search-videos.ts b/server/controllers/api/search/search-videos.ts
index b626baa28..4a6ce0de4 100644
--- a/server/controllers/api/search/search-videos.ts
+++ b/server/controllers/api/search/search-videos.ts
@@ -1,14 +1,14 @@
1import * as express from 'express' 1import * as express from 'express'
2import { sanitizeUrl } from '@server/helpers/core-utils' 2import { sanitizeUrl } from '@server/helpers/core-utils'
3import { pickSearchVideoQuery } from '@server/helpers/query'
3import { doJSONRequest } from '@server/helpers/requests' 4import { doJSONRequest } from '@server/helpers/requests'
4import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
5import { WEBSERVER } from '@server/initializers/constants' 6import { WEBSERVER } from '@server/initializers/constants'
6import { getOrCreateAPVideo } from '@server/lib/activitypub/videos' 7import { getOrCreateAPVideo } from '@server/lib/activitypub/videos'
7import { Hooks } from '@server/lib/plugins/hooks' 8import { Hooks } from '@server/lib/plugins/hooks'
8import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search' 9import { buildMutedForSearchIndex, isSearchIndexSearch, isURISearch } from '@server/lib/search'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 10import { HttpStatusCode, ResultList, Video } from '@shared/models'
10import { ResultList, Video } from '@shared/models' 11import { VideosSearchQueryAfterSanitize } from '../../../../shared/models/search'
11import { VideosSearchQuery } from '../../../../shared/models/search'
12import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils' 12import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../../helpers/express-utils'
13import { logger } from '../../../helpers/logger' 13import { logger } from '../../../helpers/logger'
14import { getFormattedObjects } from '../../../helpers/utils' 14import { getFormattedObjects } from '../../../helpers/utils'
@@ -47,7 +47,7 @@ export { searchVideosRouter }
47// --------------------------------------------------------------------------- 47// ---------------------------------------------------------------------------
48 48
49function searchVideos (req: express.Request, res: express.Response) { 49function searchVideos (req: express.Request, res: express.Response) {
50 const query: VideosSearchQuery = req.query 50 const query = pickSearchVideoQuery(req.query)
51 const search = query.search 51 const search = query.search
52 52
53 if (isURISearch(search)) { 53 if (isURISearch(search)) {
@@ -61,10 +61,10 @@ function searchVideos (req: express.Request, res: express.Response) {
61 return searchVideosDB(query, res) 61 return searchVideosDB(query, res)
62} 62}
63 63
64async function searchVideosIndex (query: VideosSearchQuery, res: express.Response) { 64async function searchVideosIndex (query: VideosSearchQueryAfterSanitize, res: express.Response) {
65 const result = await buildMutedForSearchIndex(res) 65 const result = await buildMutedForSearchIndex(res)
66 66
67 let body: VideosSearchQuery = Object.assign(query, result) 67 let body = { ...query, ...result }
68 68
69 // Use the default instance NSFW policy if not specified 69 // Use the default instance NSFW policy if not specified
70 if (!body.nsfw) { 70 if (!body.nsfw) {
@@ -98,13 +98,18 @@ async function searchVideosIndex (query: VideosSearchQuery, res: express.Respons
98 } 98 }
99} 99}
100 100
101async function searchVideosDB (query: VideosSearchQuery, res: express.Response) { 101async function searchVideosDB (query: VideosSearchQueryAfterSanitize, res: express.Response) {
102 const apiOptions = await Hooks.wrapObject(Object.assign(query, { 102 const apiOptions = await Hooks.wrapObject({
103 ...query,
104
103 includeLocalVideos: true, 105 includeLocalVideos: true,
104 nsfw: buildNSFWFilter(res, query.nsfw),
105 filter: query.filter, 106 filter: query.filter,
106 user: res.locals.oauth ? res.locals.oauth.token.User : undefined 107
107 }), 'filter:api.search.videos.local.list.params') 108 nsfw: buildNSFWFilter(res, query.nsfw),
109 user: res.locals.oauth
110 ? res.locals.oauth.token.User
111 : undefined
112 }, 'filter:api.search.videos.local.list.params')
108 113
109 const resultList = await Hooks.wrapPromiseFun( 114 const resultList = await Hooks.wrapPromiseFun(
110 VideoModel.searchAndPopulateAccountAndServer, 115 VideoModel.searchAndPopulateAccountAndServer,
diff --git a/server/controllers/api/server/contact.ts b/server/controllers/api/server/contact.ts
index caddc0909..b315e99cf 100644
--- a/server/controllers/api/server/contact.ts
+++ b/server/controllers/api/server/contact.ts
@@ -1,9 +1,9 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware, contactAdministratorValidator } from '../../../middlewares' 2import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { Redis } from '../../../lib/redis'
4import { Emailer } from '../../../lib/emailer'
5import { ContactForm } from '../../../../shared/models/server' 3import { ContactForm } from '../../../../shared/models/server'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 4import { Emailer } from '../../../lib/emailer'
5import { Redis } from '../../../lib/redis'
6import { asyncMiddleware, contactAdministratorValidator } from '../../../middlewares'
7 7
8const contactRouter = express.Router() 8const contactRouter = express.Router()
9 9
@@ -15,7 +15,7 @@ contactRouter.post('/contact',
15async function contactAdministrator (req: express.Request, res: express.Response) { 15async function contactAdministrator (req: express.Request, res: express.Response) {
16 const data = req.body as ContactForm 16 const data = req.body as ContactForm
17 17
18 await Emailer.Instance.addContactFormJob(data.fromEmail, data.fromName, data.subject, data.body) 18 Emailer.Instance.addContactFormJob(data.fromEmail, data.fromName, data.subject, data.body)
19 19
20 await Redis.Instance.setContactFormIp(req.ip) 20 await Redis.Instance.setContactFormIp(req.ip)
21 21
diff --git a/server/controllers/api/server/debug.ts b/server/controllers/api/server/debug.ts
index a6e9147f3..0601b89ce 100644
--- a/server/controllers/api/server/debug.ts
+++ b/server/controllers/api/server/debug.ts
@@ -1,8 +1,8 @@
1import * as express from 'express'
1import { InboxManager } from '@server/lib/activitypub/inbox-manager' 2import { InboxManager } from '@server/lib/activitypub/inbox-manager'
2import { RemoveDanglingResumableUploadsScheduler } from '@server/lib/schedulers/remove-dangling-resumable-uploads-scheduler' 3import { RemoveDanglingResumableUploadsScheduler } from '@server/lib/schedulers/remove-dangling-resumable-uploads-scheduler'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 4import { Debug, SendDebugCommand } from '@shared/models'
4import { SendDebugCommand } from '@shared/models' 5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
5import * as express from 'express'
6import { UserRight } from '../../../../shared/models/users' 6import { UserRight } from '../../../../shared/models/users'
7import { authenticate, ensureUserHasRight } from '../../../middlewares' 7import { authenticate, ensureUserHasRight } from '../../../middlewares'
8 8
@@ -32,7 +32,7 @@ function getDebug (req: express.Request, res: express.Response) {
32 return res.json({ 32 return res.json({
33 ip: req.ip, 33 ip: req.ip,
34 activityPubMessagesWaiting: InboxManager.Instance.getActivityPubMessagesWaiting() 34 activityPubMessagesWaiting: InboxManager.Instance.getActivityPubMessagesWaiting()
35 }) 35 } as Debug)
36} 36}
37 37
38async function runCommand (req: express.Request, res: express.Response) { 38async function runCommand (req: express.Request, res: express.Response) {
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index 12357a2ca..cbe6b7e4f 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getServerActor } from '@server/models/application/application' 2import { getServerActor } from '@server/models/application/application'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { UserRight } from '../../../../shared/models/users' 4import { UserRight } from '../../../../shared/models/users'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
@@ -29,6 +29,7 @@ import {
29 removeFollowingValidator 29 removeFollowingValidator
30} from '../../../middlewares/validators' 30} from '../../../middlewares/validators'
31import { ActorFollowModel } from '../../../models/actor/actor-follow' 31import { ActorFollowModel } from '../../../models/actor/actor-follow'
32import { ServerFollowCreate } from '@shared/models'
32 33
33const serverFollowsRouter = express.Router() 34const serverFollowsRouter = express.Router()
34serverFollowsRouter.get('/following', 35serverFollowsRouter.get('/following',
@@ -45,10 +46,10 @@ serverFollowsRouter.post('/following',
45 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), 46 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
46 followValidator, 47 followValidator,
47 setBodyHostsPort, 48 setBodyHostsPort,
48 asyncMiddleware(followInstance) 49 asyncMiddleware(addFollow)
49) 50)
50 51
51serverFollowsRouter.delete('/following/:host', 52serverFollowsRouter.delete('/following/:hostOrHandle',
52 authenticate, 53 authenticate,
53 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), 54 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
54 asyncMiddleware(removeFollowingValidator), 55 asyncMiddleware(removeFollowingValidator),
@@ -125,8 +126,8 @@ async function listFollowers (req: express.Request, res: express.Response) {
125 return res.json(getFormattedObjects(resultList.data, resultList.total)) 126 return res.json(getFormattedObjects(resultList.data, resultList.total))
126} 127}
127 128
128async function followInstance (req: express.Request, res: express.Response) { 129async function addFollow (req: express.Request, res: express.Response) {
129 const hosts = req.body.hosts as string[] 130 const { hosts, handles } = req.body as ServerFollowCreate
130 const follower = await getServerActor() 131 const follower = await getServerActor()
131 132
132 for (const host of hosts) { 133 for (const host of hosts) {
@@ -139,6 +140,18 @@ async function followInstance (req: express.Request, res: express.Response) {
139 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) 140 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
140 } 141 }
141 142
143 for (const handle of handles) {
144 const [ name, host ] = handle.split('@')
145
146 const payload = {
147 host,
148 name,
149 followerActorId: follower.id
150 }
151
152 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
153 }
154
142 return res.status(HttpStatusCode.NO_CONTENT_204).end() 155 return res.status(HttpStatusCode.NO_CONTENT_204).end()
143} 156}
144 157
diff --git a/server/controllers/api/server/index.ts b/server/controllers/api/server/index.ts
index 6b8793a19..32fefefc0 100644
--- a/server/controllers/api/server/index.ts
+++ b/server/controllers/api/server/index.ts
@@ -1,11 +1,11 @@
1import * as express from 'express' 1import * as express from 'express'
2import { contactRouter } from './contact'
3import { debugRouter } from './debug'
2import { serverFollowsRouter } from './follows' 4import { serverFollowsRouter } from './follows'
3import { statsRouter } from './stats' 5import { logsRouter } from './logs'
4import { serverRedundancyRouter } from './redundancy' 6import { serverRedundancyRouter } from './redundancy'
5import { serverBlocklistRouter } from './server-blocklist' 7import { serverBlocklistRouter } from './server-blocklist'
6import { contactRouter } from './contact' 8import { statsRouter } from './stats'
7import { logsRouter } from './logs'
8import { debugRouter } from './debug'
9 9
10const serverRouter = express.Router() 10const serverRouter = express.Router()
11 11
diff --git a/server/controllers/api/server/logs.ts b/server/controllers/api/server/logs.ts
index 4b543d686..39eceb654 100644
--- a/server/controllers/api/server/logs.ts
+++ b/server/controllers/api/server/logs.ts
@@ -1,14 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight } from '../../../../shared/models/users'
3import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
4import { mtimeSortFilesDesc } from '../../../../shared/core-utils/logs/logs'
5import { readdir, readFile } from 'fs-extra' 2import { readdir, readFile } from 'fs-extra'
6import { AUDIT_LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS, LOG_FILENAME } from '../../../initializers/constants'
7import { join } from 'path' 3import { join } from 'path'
8import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs' 4import { logger, mtimeSortFilesDesc } from '@server/helpers/logger'
9import { LogLevel } from '../../../../shared/models/server/log-level.type' 5import { LogLevel } from '../../../../shared/models/server/log-level.type'
6import { UserRight } from '../../../../shared/models/users'
10import { CONFIG } from '../../../initializers/config' 7import { CONFIG } from '../../../initializers/config'
11import { logger } from '@server/helpers/logger' 8import { AUDIT_LOG_FILENAME, LOG_FILENAME, MAX_LOGS_OUTPUT_CHARACTERS } from '../../../initializers/constants'
9import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares'
10import { getAuditLogsValidator, getLogsValidator } from '../../../middlewares/validators/logs'
12 11
13const logsRouter = express.Router() 12const logsRouter = express.Router()
14 13
diff --git a/server/controllers/api/server/redundancy.ts b/server/controllers/api/server/redundancy.ts
index bc593ad43..99d1c762b 100644
--- a/server/controllers/api/server/redundancy.ts
+++ b/server/controllers/api/server/redundancy.ts
@@ -1,5 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { JobQueue } from '@server/lib/job-queue'
3import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
4import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
2import { UserRight } from '../../../../shared/models/users' 5import { UserRight } from '../../../../shared/models/users'
6import { logger } from '../../../helpers/logger'
7import { removeRedundanciesOfServer, removeVideoRedundancy } from '../../../lib/redundancy'
3import { 8import {
4 asyncMiddleware, 9 asyncMiddleware,
5 authenticate, 10 authenticate,
@@ -10,16 +15,11 @@ import {
10 videoRedundanciesSortValidator 15 videoRedundanciesSortValidator
11} from '../../../middlewares' 16} from '../../../middlewares'
12import { 17import {
13 listVideoRedundanciesValidator,
14 updateServerRedundancyValidator,
15 addVideoRedundancyValidator, 18 addVideoRedundancyValidator,
16 removeVideoRedundancyValidator 19 listVideoRedundanciesValidator,
20 removeVideoRedundancyValidator,
21 updateServerRedundancyValidator
17} from '../../../middlewares/validators/redundancy' 22} from '../../../middlewares/validators/redundancy'
18import { removeRedundanciesOfServer, removeVideoRedundancy } from '../../../lib/redundancy'
19import { logger } from '../../../helpers/logger'
20import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
21import { JobQueue } from '@server/lib/job-queue'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 23
24const serverRedundancyRouter = express.Router() 24const serverRedundancyRouter = express.Router()
25 25
diff --git a/server/controllers/api/server/server-blocklist.ts b/server/controllers/api/server/server-blocklist.ts
index a86bc7d19..b3ee50d85 100644
--- a/server/controllers/api/server/server-blocklist.ts
+++ b/server/controllers/api/server/server-blocklist.ts
@@ -1,8 +1,9 @@
1import 'multer' 1import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { logger } from '@server/helpers/logger' 3import { logger } from '@server/helpers/logger'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { getServerActor } from '@server/models/application/application' 4import { getServerActor } from '@server/models/application/application'
5import { UserNotificationModel } from '@server/models/user/user-notification'
6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { UserRight } from '../../../../shared/models/users' 7import { UserRight } from '../../../../shared/models/users'
7import { getFormattedObjects } from '../../../helpers/utils' 8import { getFormattedObjects } from '../../../helpers/utils'
8import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' 9import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
@@ -25,7 +26,6 @@ import {
25} from '../../../middlewares/validators' 26} from '../../../middlewares/validators'
26import { AccountBlocklistModel } from '../../../models/account/account-blocklist' 27import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
27import { ServerBlocklistModel } from '../../../models/server/server-blocklist' 28import { ServerBlocklistModel } from '../../../models/server/server-blocklist'
28import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
29 29
30const serverBlocklistRouter = express.Router() 30const serverBlocklistRouter = express.Router()
31 31
diff --git a/server/controllers/api/server/stats.ts b/server/controllers/api/server/stats.ts
index 3aea12450..397702548 100644
--- a/server/controllers/api/server/stats.ts
+++ b/server/controllers/api/server/stats.ts
@@ -2,12 +2,12 @@ import * as express from 'express'
2import { StatsManager } from '@server/lib/stat-manager' 2import { StatsManager } from '@server/lib/stat-manager'
3import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants' 3import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants'
4import { asyncMiddleware } from '../../../middlewares' 4import { asyncMiddleware } from '../../../middlewares'
5import { cacheRoute } from '../../../middlewares/cache' 5import { cacheRoute } from '../../../middlewares/cache/cache'
6 6
7const statsRouter = express.Router() 7const statsRouter = express.Router()
8 8
9statsRouter.get('/stats', 9statsRouter.get('/stats',
10 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.STATS)), 10 cacheRoute(ROUTE_CACHE_LIFETIME.STATS),
11 asyncMiddleware(getStats) 11 asyncMiddleware(getStats)
12) 12)
13 13
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index d907b49bf..be800e8b5 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -4,8 +4,8 @@ import { tokensRouter } from '@server/controllers/api/users/token'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { OAuthTokenModel } from '@server/models/oauth/oauth-token' 5import { OAuthTokenModel } from '@server/models/oauth/oauth-token'
6import { MUser, MUserAccountDefault } from '@server/types/models' 6import { MUser, MUserAccountDefault } from '@server/types/models'
7import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' 7import { UserCreate, UserCreateResult, UserRight, UserRole, UserUpdate } from '../../../../shared'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
9import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' 9import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
10import { UserRegister } from '../../../../shared/models/users/user-register.model' 10import { UserRegister } from '../../../../shared/models/users/user-register.model'
11import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' 11import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
@@ -220,7 +220,7 @@ async function createUser (req: express.Request, res: express.Response) {
220 account: { 220 account: {
221 id: account.id 221 id: account.id
222 } 222 }
223 } 223 } as UserCreateResult
224 }) 224 })
225} 225}
226 226
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index 1f2b2f9dd..ac6faca9c 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -2,8 +2,9 @@ import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger' 3import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { AttributesOnly } from '@shared/core-utils'
5import { ActorImageType, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' 6import { ActorImageType, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
7import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' 8import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model'
8import { createReqFiles } from '../../../helpers/express-utils' 9import { createReqFiles } from '../../../helpers/express-utils'
9import { getFormattedObjects } from '../../../helpers/utils' 10import { getFormattedObjects } from '../../../helpers/utils'
@@ -31,7 +32,6 @@ import { AccountVideoRateModel } from '../../../models/account/account-video-rat
31import { UserModel } from '../../../models/user/user' 32import { UserModel } from '../../../models/user/user'
32import { VideoModel } from '../../../models/video/video' 33import { VideoModel } from '../../../models/video/video'
33import { VideoImportModel } from '../../../models/video/video-import' 34import { VideoImportModel } from '../../../models/video/video-import'
34import { AttributesOnly } from '@shared/core-utils'
35 35
36const auditLogger = auditLoggerFactory('users') 36const auditLogger = auditLoggerFactory('users')
37 37
diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts
index a1561b751..24fff83e3 100644
--- a/server/controllers/api/users/my-blocklist.ts
+++ b/server/controllers/api/users/my-blocklist.ts
@@ -1,6 +1,10 @@
1import * as express from 'express'
2import 'multer' 1import 'multer'
2import * as express from 'express'
3import { logger } from '@server/helpers/logger'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
4import { 8import {
5 asyncMiddleware, 9 asyncMiddleware,
6 asyncRetryTransactionMiddleware, 10 asyncRetryTransactionMiddleware,
@@ -18,11 +22,7 @@ import {
18 unblockServerByAccountValidator 22 unblockServerByAccountValidator
19} from '../../../middlewares/validators' 23} from '../../../middlewares/validators'
20import { AccountBlocklistModel } from '../../../models/account/account-blocklist' 24import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
21import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
22import { ServerBlocklistModel } from '../../../models/server/server-blocklist' 25import { ServerBlocklistModel } from '../../../models/server/server-blocklist'
23import { UserNotificationModel } from '@server/models/user/user-notification'
24import { logger } from '@server/helpers/logger'
25import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
26 26
27const myBlocklistRouter = express.Router() 27const myBlocklistRouter = express.Router()
28 28
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts
index cff1697ab..a6e723103 100644
--- a/server/controllers/api/users/my-history.ts
+++ b/server/controllers/api/users/my-history.ts
@@ -1,4 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { getFormattedObjects } from '../../../helpers/utils'
4import { sequelizeTypescript } from '../../../initializers/database'
2import { 5import {
3 asyncMiddleware, 6 asyncMiddleware,
4 asyncRetryTransactionMiddleware, 7 asyncRetryTransactionMiddleware,
@@ -8,10 +11,7 @@ import {
8 userHistoryListValidator, 11 userHistoryListValidator,
9 userHistoryRemoveValidator 12 userHistoryRemoveValidator
10} from '../../../middlewares' 13} from '../../../middlewares'
11import { getFormattedObjects } from '../../../helpers/utils'
12import { UserVideoHistoryModel } from '../../../models/user/user-video-history' 14import { UserVideoHistoryModel } from '../../../models/user/user-video-history'
13import { sequelizeTypescript } from '../../../initializers/database'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15 15
16const myVideosHistoryRouter = express.Router() 16const myVideosHistoryRouter = express.Router()
17 17
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts
index 2909770da..3beee07c0 100644
--- a/server/controllers/api/users/my-notifications.ts
+++ b/server/controllers/api/users/my-notifications.ts
@@ -1,7 +1,7 @@
1import 'multer' 1import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { UserNotificationModel } from '@server/models/user/user-notification' 3import { UserNotificationModel } from '@server/models/user/user-notification'
4import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
5import { UserNotificationSetting } from '../../../../shared/models/users' 5import { UserNotificationSetting } from '../../../../shared/models/users'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { 7import {
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts
index 46a73d49e..26a715704 100644
--- a/server/controllers/api/users/my-subscriptions.ts
+++ b/server/controllers/api/users/my-subscriptions.ts
@@ -1,9 +1,9 @@
1import 'multer' 1import 'multer'
2import * as express from 'express' 2import * as express from 'express'
3import { pickCommonVideoQuery } from '@server/helpers/query'
3import { sendUndoFollow } from '@server/lib/activitypub/send' 4import { sendUndoFollow } from '@server/lib/activitypub/send'
4import { VideoChannelModel } from '@server/models/video/video-channel' 5import { VideoChannelModel } from '@server/models/video/video-channel'
5import { VideosCommonQuery } from '@shared/models' 6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' 7import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
8import { getFormattedObjects } from '../../../helpers/utils' 8import { getFormattedObjects } from '../../../helpers/utils'
9import { WEBSERVER } from '../../../initializers/constants' 9import { WEBSERVER } from '../../../initializers/constants'
@@ -170,20 +170,13 @@ async function getUserSubscriptions (req: express.Request, res: express.Response
170async function getUserSubscriptionVideos (req: express.Request, res: express.Response) { 170async function getUserSubscriptionVideos (req: express.Request, res: express.Response) {
171 const user = res.locals.oauth.token.User 171 const user = res.locals.oauth.token.User
172 const countVideos = getCountVideos(req) 172 const countVideos = getCountVideos(req)
173 const query = req.query as VideosCommonQuery 173 const query = pickCommonVideoQuery(req.query)
174 174
175 const resultList = await VideoModel.listForApi({ 175 const resultList = await VideoModel.listForApi({
176 start: query.start, 176 ...query,
177 count: query.count, 177
178 sort: query.sort,
179 includeLocalVideos: false, 178 includeLocalVideos: false,
180 categoryOneOf: query.categoryOneOf,
181 licenceOneOf: query.licenceOneOf,
182 languageOneOf: query.languageOneOf,
183 tagsOneOf: query.tagsOneOf,
184 tagsAllOf: query.tagsAllOf,
185 nsfw: buildNSFWFilter(res, query.nsfw), 179 nsfw: buildNSFWFilter(res, query.nsfw),
186 filter: query.filter,
187 withFiles: false, 180 withFiles: false,
188 followerActorId: user.Account.Actor.id, 181 followerActorId: user.Account.Actor.id,
189 user, 182 user,
diff --git a/server/controllers/api/users/my-video-playlists.ts b/server/controllers/api/users/my-video-playlists.ts
index d0bd99463..76e741ba5 100644
--- a/server/controllers/api/users/my-video-playlists.ts
+++ b/server/controllers/api/users/my-video-playlists.ts
@@ -1,8 +1,8 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
2import { asyncMiddleware, authenticate } from '../../../middlewares' 3import { asyncMiddleware, authenticate } from '../../../middlewares'
3import { doVideosInPlaylistExistValidator } from '../../../middlewares/validators/videos/video-playlists' 4import { doVideosInPlaylistExistValidator } from '../../../middlewares/validators/videos/video-playlists'
4import { VideoPlaylistModel } from '../../../models/video/video-playlist' 5import { VideoPlaylistModel } from '../../../models/video/video-playlist'
5import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
6 6
7const myVideoPlaylistsRouter = express.Router() 7const myVideoPlaylistsRouter = express.Router()
8 8
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index bc8d203b0..7bdb33737 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -1,9 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { pickCommonVideoQuery } from '@server/helpers/query'
2import { Hooks } from '@server/lib/plugins/hooks' 3import { Hooks } from '@server/lib/plugins/hooks'
3import { getServerActor } from '@server/models/application/application' 4import { getServerActor } from '@server/models/application/application'
4import { MChannelBannerAccountDefault } from '@server/types/models' 5import { MChannelBannerAccountDefault } from '@server/types/models'
5import { ActorImageType, VideoChannelCreate, VideoChannelUpdate, VideosCommonQuery } from '../../../shared' 6import { ActorImageType, VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
7import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' 8import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger'
8import { resetSequelizeInstance } from '../../helpers/database-utils' 9import { resetSequelizeInstance } from '../../helpers/database-utils'
9import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 10import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
@@ -309,20 +310,13 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
309 const videoChannelInstance = res.locals.videoChannel 310 const videoChannelInstance = res.locals.videoChannel
310 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined 311 const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined
311 const countVideos = getCountVideos(req) 312 const countVideos = getCountVideos(req)
312 const query = req.query as VideosCommonQuery 313 const query = pickCommonVideoQuery(req.query)
313 314
314 const apiOptions = await Hooks.wrapObject({ 315 const apiOptions = await Hooks.wrapObject({
316 ...query,
317
315 followerActorId, 318 followerActorId,
316 start: query.start,
317 count: query.count,
318 sort: query.sort,
319 includeLocalVideos: true, 319 includeLocalVideos: true,
320 categoryOneOf: query.categoryOneOf,
321 licenceOneOf: query.licenceOneOf,
322 languageOneOf: query.languageOneOf,
323 tagsOneOf: query.tagsOneOf,
324 tagsAllOf: query.tagsAllOf,
325 filter: query.filter,
326 nsfw: buildNSFWFilter(res, query.nsfw), 320 nsfw: buildNSFWFilter(res, query.nsfw),
327 withFiles: false, 321 withFiles: false,
328 videoChannelId: videoChannelInstance.id, 322 videoChannelId: videoChannelInstance.id,
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index 87a6f6bbe..4971d0a77 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -5,7 +5,8 @@ import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists'
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { getServerActor } from '@server/models/application/application' 6import { getServerActor } from '@server/models/application/application'
7import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' 7import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 8import { VideoPlaylistCreateResult, VideoPlaylistElementCreateResult } from '@shared/models'
9import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
9import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model' 10import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model'
10import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model' 11import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model'
11import { VideoPlaylistElementUpdate } from '../../../shared/models/videos/playlist/video-playlist-element-update.model' 12import { VideoPlaylistElementUpdate } from '../../../shared/models/videos/playlist/video-playlist-element-update.model'
@@ -202,7 +203,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
202 id: videoPlaylistCreated.id, 203 id: videoPlaylistCreated.id,
203 shortUUID: uuidToShort(videoPlaylistCreated.uuid), 204 shortUUID: uuidToShort(videoPlaylistCreated.uuid),
204 uuid: videoPlaylistCreated.uuid 205 uuid: videoPlaylistCreated.uuid
205 } 206 } as VideoPlaylistCreateResult
206 }) 207 })
207} 208}
208 209
@@ -338,8 +339,8 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response)
338 return res.json({ 339 return res.json({
339 videoPlaylistElement: { 340 videoPlaylistElement: {
340 id: playlistElement.id 341 id: playlistElement.id
341 } 342 } as VideoPlaylistElementCreateResult
342 }).end() 343 })
343} 344}
344 345
345async function updateVideoPlaylistElement (req: express.Request, res: express.Response) { 346async function updateVideoPlaylistElement (req: express.Request, res: express.Response) {
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index 530e17965..6bc768471 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -1,6 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist' 2import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist'
3import { UserRight, VideoBlacklistCreate } from '../../../../shared' 3import { UserRight, VideoBlacklistCreate } from '../../../../shared'
4import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
5import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
6import { sequelizeTypescript } from '../../../initializers/database' 7import { sequelizeTypescript } from '../../../initializers/database'
@@ -19,7 +20,6 @@ import {
19 videosBlacklistUpdateValidator 20 videosBlacklistUpdateValidator
20} from '../../../middlewares' 21} from '../../../middlewares'
21import { VideoBlacklistModel } from '../../../models/video/video-blacklist' 22import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 23
24const blacklistRouter = express.Router() 24const blacklistRouter = express.Router()
25 25
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts
index ad7423a31..4008de60f 100644
--- a/server/controllers/api/videos/captions.ts
+++ b/server/controllers/api/videos/captions.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { MVideoCaption } from '@server/types/models' 2import { MVideoCaption } from '@server/types/models'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' 4import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
5import { createReqFiles } from '../../../helpers/express-utils' 5import { createReqFiles } from '../../../helpers/express-utils'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index e6f28c1cb..cb696f652 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 2import { ResultList, ThreadsResultList, UserRight, VideoCommentCreate } from '../../../../shared/models'
3import { ResultList, ThreadsResultList, UserRight } from '../../../../shared/models' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { VideoCommentCreate } from '../../../../shared/models/videos/comment/video-comment.model' 4import { VideoCommentThreads } from '../../../../shared/models/videos/comment/video-comment.model'
5import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' 5import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { sequelizeTypescript } from '../../../initializers/database' 7import { sequelizeTypescript } from '../../../initializers/database'
@@ -136,7 +136,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
136 return res.json({ 136 return res.json({
137 ...getFormattedObjects(resultList.data, resultList.total), 137 ...getFormattedObjects(resultList.data, resultList.total),
138 totalNotDeletedComments: resultList.totalNotDeletedComments 138 totalNotDeletedComments: resultList.totalNotDeletedComments
139 }) 139 } as VideoCommentThreads)
140} 140}
141 141
142async function listVideoThreadComments (req: express.Request, res: express.Response) { 142async function listVideoThreadComments (req: express.Request, res: express.Response) {
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 74b100e59..49490f79b 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -1,12 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import toInt from 'validator/lib/toInt' 2import toInt from 'validator/lib/toInt'
3import { pickCommonVideoQuery } from '@server/helpers/query'
3import { doJSONRequest } from '@server/helpers/requests' 4import { doJSONRequest } from '@server/helpers/requests'
4import { LiveManager } from '@server/lib/live' 5import { LiveManager } from '@server/lib/live'
5import { openapiOperationDoc } from '@server/middlewares/doc' 6import { openapiOperationDoc } from '@server/middlewares/doc'
6import { getServerActor } from '@server/models/application/application' 7import { getServerActor } from '@server/models/application/application'
7import { MVideoAccountLight } from '@server/types/models' 8import { MVideoAccountLight } from '@server/types/models'
8import { VideosCommonQuery } from '../../../../shared' 9import { HttpStatusCode } from '../../../../shared/models'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs'
10import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 10import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
11import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' 11import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
12import { logger } from '../../../helpers/logger' 12import { logger } from '../../../helpers/logger'
@@ -211,22 +211,14 @@ async function getVideoFileMetadata (req: express.Request, res: express.Response
211} 211}
212 212
213async function listVideos (req: express.Request, res: express.Response) { 213async function listVideos (req: express.Request, res: express.Response) {
214 const query = req.query as VideosCommonQuery 214 const query = pickCommonVideoQuery(req.query)
215 const countVideos = getCountVideos(req) 215 const countVideos = getCountVideos(req)
216 216
217 const apiOptions = await Hooks.wrapObject({ 217 const apiOptions = await Hooks.wrapObject({
218 start: query.start, 218 ...query,
219 count: query.count, 219
220 sort: query.sort,
221 includeLocalVideos: true, 220 includeLocalVideos: true,
222 categoryOneOf: query.categoryOneOf,
223 licenceOneOf: query.licenceOneOf,
224 languageOneOf: query.languageOneOf,
225 tagsOneOf: query.tagsOneOf,
226 tagsAllOf: query.tagsAllOf,
227 nsfw: buildNSFWFilter(res, query.nsfw), 221 nsfw: buildNSFWFilter(res, query.nsfw),
228 isLive: query.isLive,
229 filter: query.filter,
230 withFiles: false, 222 withFiles: false,
231 user: res.locals.oauth ? res.locals.oauth.token.User : undefined, 223 user: res.locals.oauth ? res.locals.oauth.token.User : undefined,
232 countVideos 224 countVideos
diff --git a/server/controllers/api/videos/live.ts b/server/controllers/api/videos/live.ts
index d8c51c2d4..ed4da8f47 100644
--- a/server/controllers/api/videos/live.ts
+++ b/server/controllers/api/videos/live.ts
@@ -11,7 +11,7 @@ import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator
11import { VideoLiveModel } from '@server/models/video/video-live' 11import { VideoLiveModel } from '@server/models/video/video-live'
12import { MVideoDetails, MVideoFullLight } from '@server/types/models' 12import { MVideoDetails, MVideoFullLight } from '@server/types/models'
13import { LiveVideoCreate, LiveVideoUpdate, VideoState } from '../../../../shared' 13import { LiveVideoCreate, LiveVideoUpdate, VideoState } from '../../../../shared'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 14import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
15import { logger } from '../../../helpers/logger' 15import { logger } from '../../../helpers/logger'
16import { sequelizeTypescript } from '../../../initializers/database' 16import { sequelizeTypescript } from '../../../initializers/database'
17import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail' 17import { updateVideoMiniatureFromExisting } from '../../../lib/thumbnail'
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts
index 1bb96e046..f48acbc68 100644
--- a/server/controllers/api/videos/ownership.ts
+++ b/server/controllers/api/videos/ownership.ts
@@ -1,6 +1,12 @@
1import * as express from 'express' 1import * as express from 'express'
2import { MVideoFullLight } from '@server/types/models'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { VideoChangeOwnershipStatus, VideoState } from '../../../../shared/models/videos'
2import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { getFormattedObjects } from '../../../helpers/utils'
3import { sequelizeTypescript } from '../../../initializers/database' 7import { sequelizeTypescript } from '../../../initializers/database'
8import { sendUpdateVideo } from '../../../lib/activitypub/send'
9import { changeVideoChannelShare } from '../../../lib/activitypub/share'
4import { 10import {
5 asyncMiddleware, 11 asyncMiddleware,
6 asyncRetryTransactionMiddleware, 12 asyncRetryTransactionMiddleware,
@@ -11,15 +17,9 @@ import {
11 videosChangeOwnershipValidator, 17 videosChangeOwnershipValidator,
12 videosTerminateChangeOwnershipValidator 18 videosTerminateChangeOwnershipValidator
13} from '../../../middlewares' 19} from '../../../middlewares'
20import { VideoModel } from '../../../models/video/video'
14import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership' 21import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership'
15import { VideoChangeOwnershipStatus, VideoState } from '../../../../shared/models/videos'
16import { VideoChannelModel } from '../../../models/video/video-channel' 22import { VideoChannelModel } from '../../../models/video/video-channel'
17import { getFormattedObjects } from '../../../helpers/utils'
18import { changeVideoChannelShare } from '../../../lib/activitypub/share'
19import { sendUpdateVideo } from '../../../lib/activitypub/send'
20import { VideoModel } from '../../../models/video/video'
21import { MVideoFullLight } from '@server/types/models'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 23
24const ownershipVideoRouter = express.Router() 24const ownershipVideoRouter = express.Router()
25 25
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts
index 84f42633e..96f6cd886 100644
--- a/server/controllers/api/videos/rate.ts
+++ b/server/controllers/api/videos/rate.ts
@@ -1,13 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserVideoRateUpdate } from '../../../../shared' 2import { UserVideoRateUpdate } from '../../../../shared'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { logger } from '../../../helpers/logger' 4import { logger } from '../../../helpers/logger'
4import { VIDEO_RATE_TYPES } from '../../../initializers/constants' 5import { VIDEO_RATE_TYPES } from '../../../initializers/constants'
6import { sequelizeTypescript } from '../../../initializers/database'
5import { getLocalRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates' 7import { getLocalRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates'
6import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares' 8import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares'
7import { AccountModel } from '../../../models/account/account' 9import { AccountModel } from '../../../models/account/account'
8import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 10import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
9import { sequelizeTypescript } from '../../../initializers/database'
10import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
11 11
12const rateVideoRouter = express.Router() 12const rateVideoRouter = express.Router()
13 13
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts
index 8affe71c6..49639060b 100644
--- a/server/controllers/api/videos/update.ts
+++ b/server/controllers/api/videos/update.ts
@@ -2,10 +2,11 @@ import * as express from 'express'
2import { Transaction } from 'sequelize/types' 2import { Transaction } from 'sequelize/types'
3import { changeVideoChannelShare } from '@server/lib/activitypub/share' 3import { changeVideoChannelShare } from '@server/lib/activitypub/share'
4import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' 4import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
5import { openapiOperationDoc } from '@server/middlewares/doc'
5import { FilteredModelAttributes } from '@server/types' 6import { FilteredModelAttributes } from '@server/types'
6import { MVideoFullLight } from '@server/types/models' 7import { MVideoFullLight } from '@server/types/models'
7import { VideoUpdate } from '../../../../shared' 8import { VideoUpdate } from '../../../../shared'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs' 9import { HttpStatusCode } from '../../../../shared/models'
9import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 10import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
10import { resetSequelizeInstance } from '../../../helpers/database-utils' 11import { resetSequelizeInstance } from '../../../helpers/database-utils'
11import { createReqFiles } from '../../../helpers/express-utils' 12import { createReqFiles } from '../../../helpers/express-utils'
@@ -20,7 +21,6 @@ import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
20import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' 21import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares'
21import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' 22import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
22import { VideoModel } from '../../../models/video/video' 23import { VideoModel } from '../../../models/video/video'
23import { openapiOperationDoc } from '@server/middlewares/doc'
24 24
25const lTags = loggerTagsFactory('api', 'video') 25const lTags = loggerTagsFactory('api', 'video')
26const auditLogger = auditLoggerFactory('videos') 26const auditLogger = auditLoggerFactory('videos')
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts
index bcd21ac99..408f677ff 100644
--- a/server/controllers/api/videos/upload.ts
+++ b/server/controllers/api/videos/upload.ts
@@ -6,12 +6,12 @@ import { uuidToShort } from '@server/helpers/uuid'
6import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 6import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
7import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' 7import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
8import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' 8import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
9import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 9import { generateWebTorrentVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
10import { openapiOperationDoc } from '@server/middlewares/doc' 10import { openapiOperationDoc } from '@server/middlewares/doc'
11import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' 11import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
12import { uploadx } from '@uploadx/core' 12import { uploadx } from '@uploadx/core'
13import { VideoCreate, VideoState } from '../../../../shared' 13import { VideoCreate, VideoState } from '../../../../shared'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs' 14import { HttpStatusCode } from '../../../../shared/models'
15import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 15import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
16import { retryTransactionWrapper } from '../../../helpers/database-utils' 16import { retryTransactionWrapper } from '../../../helpers/database-utils'
17import { createReqFiles } from '../../../helpers/express-utils' 17import { createReqFiles } from '../../../helpers/express-utils'
@@ -209,10 +209,12 @@ async function addVideo (options: {
209 }) 209 })
210 210
211 createTorrentFederate(video, videoFile) 211 createTorrentFederate(video, videoFile)
212 .then(() => {
213 if (video.state !== VideoState.TO_TRANSCODE) return
212 214
213 if (video.state === VideoState.TO_TRANSCODE) { 215 return addOptimizeOrMergeAudioJob(videoCreated, videoFile, user)
214 await addOptimizeOrMergeAudioJob(videoCreated, videoFile, user) 216 })
215 } 217 .catch(err => logger.error('Cannot add optimize/merge audio job for %s.', videoCreated.uuid, { err, ...lTags(videoCreated.uuid) }))
216 218
217 Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) 219 Hooks.runAction('action:api.video.uploaded', { video: videoCreated })
218 220
@@ -240,7 +242,7 @@ async function buildNewFile (video: MVideo, videoPhysicalFile: express.VideoUplo
240 videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution 242 videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution
241 } 243 }
242 244
243 videoFile.filename = generateVideoFilename(video, false, videoFile.resolution, videoFile.extname) 245 videoFile.filename = generateWebTorrentVideoFilename(videoFile.resolution, videoFile.extname)
244 246
245 return videoFile 247 return videoFile
246} 248}
@@ -259,9 +261,9 @@ async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoF
259 return refreshedFile.save() 261 return refreshedFile.save()
260} 262}
261 263
262function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile): void { 264function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile) {
263 // Create the torrent file in async way because it could be long 265 // Create the torrent file in async way because it could be long
264 createTorrentAndSetInfoHashAsync(video, videoFile) 266 return createTorrentAndSetInfoHashAsync(video, videoFile)
265 .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) })) 267 .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) }))
266 .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) 268 .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id))
267 .then(refreshedVideo => { 269 .then(refreshedVideo => {
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts
index 8b15525aa..05c75e543 100644
--- a/server/controllers/api/videos/watching.ts
+++ b/server/controllers/api/videos/watching.ts
@@ -1,5 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserWatchingVideo } from '../../../../shared' 2import { UserWatchingVideo } from '../../../../shared'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
3import { 4import {
4 asyncMiddleware, 5 asyncMiddleware,
5 asyncRetryTransactionMiddleware, 6 asyncRetryTransactionMiddleware,
@@ -8,7 +9,6 @@ import {
8 videoWatchingValidator 9 videoWatchingValidator
9} from '../../../middlewares' 10} from '../../../middlewares'
10import { UserVideoHistoryModel } from '../../../models/user/user-video-history' 11import { UserVideoHistoryModel } from '../../../models/user/user-video-history'
11import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
12 12
13const watchingRouter = express.Router() 13const watchingRouter = express.Router()
14 14
diff --git a/server/controllers/bots.ts b/server/controllers/bots.ts
index 9e92063d4..de0411608 100644
--- a/server/controllers/bots.ts
+++ b/server/controllers/bots.ts
@@ -1,20 +1,20 @@
1import * as express from 'express' 1import * as express from 'express'
2import { asyncMiddleware } from '../middlewares' 2import { truncate } from 'lodash'
3import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
4import { SitemapStream, streamToPromise } from 'sitemap' 3import { SitemapStream, streamToPromise } from 'sitemap'
4import { buildNSFWFilter } from '../helpers/express-utils'
5import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../initializers/constants'
6import { asyncMiddleware } from '../middlewares'
7import { cacheRoute } from '../middlewares/cache/cache'
8import { AccountModel } from '../models/account/account'
5import { VideoModel } from '../models/video/video' 9import { VideoModel } from '../models/video/video'
6import { VideoChannelModel } from '../models/video/video-channel' 10import { VideoChannelModel } from '../models/video/video-channel'
7import { AccountModel } from '../models/account/account'
8import { cacheRoute } from '../middlewares/cache'
9import { buildNSFWFilter } from '../helpers/express-utils'
10import { truncate } from 'lodash'
11 11
12const botsRouter = express.Router() 12const botsRouter = express.Router()
13 13
14// Special route that add OpenGraph and oEmbed tags 14// Special route that add OpenGraph and oEmbed tags
15// Do not use a template engine for a so little thing 15// Do not use a template engine for a so little thing
16botsRouter.use('/sitemap.xml', 16botsRouter.use('/sitemap.xml',
17 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.SITEMAP)), 17 cacheRoute(ROUTE_CACHE_LIFETIME.SITEMAP),
18 asyncMiddleware(getSitemap) 18 asyncMiddleware(getSitemap)
19) 19)
20 20
@@ -75,13 +75,13 @@ async function getSitemapLocalVideoUrls () {
75 }) 75 })
76 76
77 return data.map(v => ({ 77 return data.map(v => ({
78 url: WEBSERVER.URL + '/w/' + v.uuid, 78 url: WEBSERVER.URL + v.getWatchStaticPath(),
79 video: [ 79 video: [
80 { 80 {
81 title: v.name, 81 title: v.name,
82 // Sitemap description should be < 2000 characters 82 // Sitemap description should be < 2000 characters
83 description: truncate(v.description || v.name, { length: 2000, omission: '...' }), 83 description: truncate(v.description || v.name, { length: 2000, omission: '...' }),
84 player_loc: WEBSERVER.URL + '/videos/embed/' + v.uuid, 84 player_loc: WEBSERVER.URL + v.getEmbedStaticPath(),
85 thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath() 85 thumbnail_loc: WEBSERVER.URL + v.getMiniatureStaticPath()
86 } 86 }
87 ] 87 ]
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index eb1ee6cbd..ba3c54440 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -5,7 +5,7 @@ import { join } from 'path'
5import { logger } from '@server/helpers/logger' 5import { logger } from '@server/helpers/logger'
6import { CONFIG } from '@server/initializers/config' 6import { CONFIG } from '@server/initializers/config'
7import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
8import { HttpStatusCode } from '@shared/core-utils' 8import { HttpStatusCode } from '@shared/models'
9import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' 9import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
10import { root } from '../helpers/core-utils' 10import { root } from '../helpers/core-utils'
11import { STATIC_MAX_AGE } from '../initializers/constants' 11import { STATIC_MAX_AGE } from '../initializers/constants'
diff --git a/server/controllers/download.ts b/server/controllers/download.ts
index 4293a32e2..ddacc1b68 100644
--- a/server/controllers/download.ts
+++ b/server/controllers/download.ts
@@ -5,8 +5,7 @@ import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { getVideoFilePath } from '@server/lib/video-paths' 6import { getVideoFilePath } from '@server/lib/video-paths'
7import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' 7import { MStreamingPlaylist, MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
8import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
9import { VideoStreamingPlaylistType } from '@shared/models'
10import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants' 9import { STATIC_DOWNLOAD_PATHS } from '../initializers/constants'
11import { asyncMiddleware, videosDownloadValidator } from '../middlewares' 10import { asyncMiddleware, videosDownloadValidator } from '../middlewares'
12 11
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 435b12193..9fa70a7c8 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -16,20 +16,20 @@ import {
16 videosSortValidator, 16 videosSortValidator,
17 videoSubscriptionFeedsValidator 17 videoSubscriptionFeedsValidator
18} from '../middlewares' 18} from '../middlewares'
19import { cacheRoute } from '../middlewares/cache' 19import { cacheRouteFactory } from '../middlewares/cache/cache'
20import { VideoModel } from '../models/video/video' 20import { VideoModel } from '../models/video/video'
21import { VideoCommentModel } from '../models/video/video-comment' 21import { VideoCommentModel } from '../models/video/video-comment'
22 22
23const feedsRouter = express.Router() 23const feedsRouter = express.Router()
24 24
25const cacheRoute = cacheRouteFactory({
26 headerBlacklist: [ 'Content-Type' ]
27})
28
25feedsRouter.get('/feeds/video-comments.:format', 29feedsRouter.get('/feeds/video-comments.:format',
26 feedsFormatValidator, 30 feedsFormatValidator,
27 setFeedFormatContentType, 31 setFeedFormatContentType,
28 asyncMiddleware(cacheRoute({ 32 cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS),
29 headerBlacklist: [
30 'Content-Type'
31 ]
32 })(ROUTE_CACHE_LIFETIME.FEEDS)),
33 asyncMiddleware(videoFeedsValidator), 33 asyncMiddleware(videoFeedsValidator),
34 asyncMiddleware(videoCommentsFeedsValidator), 34 asyncMiddleware(videoCommentsFeedsValidator),
35 asyncMiddleware(generateVideoCommentsFeed) 35 asyncMiddleware(generateVideoCommentsFeed)
@@ -40,11 +40,7 @@ feedsRouter.get('/feeds/videos.:format',
40 setDefaultVideosSort, 40 setDefaultVideosSort,
41 feedsFormatValidator, 41 feedsFormatValidator,
42 setFeedFormatContentType, 42 setFeedFormatContentType,
43 asyncMiddleware(cacheRoute({ 43 cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS),
44 headerBlacklist: [
45 'Content-Type'
46 ]
47 })(ROUTE_CACHE_LIFETIME.FEEDS)),
48 commonVideosFiltersValidator, 44 commonVideosFiltersValidator,
49 asyncMiddleware(videoFeedsValidator), 45 asyncMiddleware(videoFeedsValidator),
50 asyncMiddleware(generateVideoFeed) 46 asyncMiddleware(generateVideoFeed)
@@ -55,11 +51,7 @@ feedsRouter.get('/feeds/subscriptions.:format',
55 setDefaultVideosSort, 51 setDefaultVideosSort,
56 feedsFormatValidator, 52 feedsFormatValidator,
57 setFeedFormatContentType, 53 setFeedFormatContentType,
58 asyncMiddleware(cacheRoute({ 54 cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS),
59 headerBlacklist: [
60 'Content-Type'
61 ]
62 })(ROUTE_CACHE_LIFETIME.FEEDS)),
63 commonVideosFiltersValidator, 55 commonVideosFiltersValidator,
64 asyncMiddleware(videoSubscriptionFeedsValidator), 56 asyncMiddleware(videoSubscriptionFeedsValidator),
65 asyncMiddleware(generateVideoFeedForSubscriptions) 57 asyncMiddleware(generateVideoFeedForSubscriptions)
@@ -294,7 +286,7 @@ function addVideosToFeed (feed, videos: VideoModel[]) {
294 feed.addItem({ 286 feed.addItem({
295 title: video.name, 287 title: video.name,
296 id: video.url, 288 id: video.url,
297 link: WEBSERVER.URL + '/w/' + video.uuid, 289 link: WEBSERVER.URL + video.getWatchStaticPath(),
298 description: video.getTruncatedDescription(), 290 description: video.getTruncatedDescription(),
299 content: video.description, 291 content: video.description,
300 author: [ 292 author: [
diff --git a/server/controllers/lazy-static.ts b/server/controllers/lazy-static.ts
index 9a7dacba0..632e4dcd8 100644
--- a/server/controllers/lazy-static.ts
+++ b/server/controllers/lazy-static.ts
@@ -1,7 +1,7 @@
1import * as cors from 'cors' 1import * as cors from 'cors'
2import * as express from 'express' 2import * as express from 'express'
3import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache' 3import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache'
4import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants' 6import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants'
7import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' 7import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache'
diff --git a/server/controllers/live.ts b/server/controllers/live.ts
index f2686fb23..95d5c0135 100644
--- a/server/controllers/live.ts
+++ b/server/controllers/live.ts
@@ -2,7 +2,7 @@ import * as cors from 'cors'
2import * as express from 'express' 2import * as express from 'express'
3import { mapToJSON } from '@server/helpers/core-utils' 3import { mapToJSON } from '@server/helpers/core-utils'
4import { LiveSegmentShaStore } from '@server/lib/live' 4import { LiveSegmentShaStore } from '@server/lib/live'
5import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '@shared/models'
6 6
7const liveRouter = express.Router() 7const liveRouter = express.Router()
8 8
diff --git a/server/controllers/plugins.ts b/server/controllers/plugins.ts
index 7213e3f15..11ab3f10a 100644
--- a/server/controllers/plugins.ts
+++ b/server/controllers/plugins.ts
@@ -3,7 +3,7 @@ import { join } from 'path'
3import { logger } from '@server/helpers/logger' 3import { logger } from '@server/helpers/logger'
4import { optionalAuthenticate } from '@server/middlewares/auth' 4import { optionalAuthenticate } from '@server/middlewares/auth'
5import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n' 5import { getCompleteLocale, is18nLocale } from '../../shared/core-utils/i18n'
6import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
7import { PluginType } from '../../shared/models/plugins/plugin.type' 7import { PluginType } from '../../shared/models/plugins/plugin.type'
8import { isTestInstance } from '../helpers/core-utils' 8import { isTestInstance } from '../helpers/core-utils'
9import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants' 9import { PLUGIN_GLOBAL_CSS_PATH } from '../initializers/constants'
diff --git a/server/controllers/static.ts b/server/controllers/static.ts
index 35e024dda..912d7e36c 100644
--- a/server/controllers/static.ts
+++ b/server/controllers/static.ts
@@ -3,7 +3,7 @@ import * as express from 'express'
3import { join } from 'path' 3import { join } from 'path'
4import { serveIndexHTML } from '@server/lib/client-html' 4import { serveIndexHTML } from '@server/lib/client-html'
5import { ServerConfigManager } from '@server/lib/server-config-manager' 5import { ServerConfigManager } from '@server/lib/server-config-manager'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '@shared/models'
7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model' 7import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model'
8import { root } from '../helpers/core-utils' 8import { root } from '../helpers/core-utils'
9import { CONFIG, isEmailEnabled } from '../initializers/config' 9import { CONFIG, isEmailEnabled } from '../initializers/config'
@@ -19,7 +19,7 @@ import {
19} from '../initializers/constants' 19} from '../initializers/constants'
20import { getThemeOrDefault } from '../lib/plugins/theme-utils' 20import { getThemeOrDefault } from '../lib/plugins/theme-utils'
21import { asyncMiddleware } from '../middlewares' 21import { asyncMiddleware } from '../middlewares'
22import { cacheRoute } from '../middlewares/cache' 22import { cacheRoute } from '../middlewares/cache/cache'
23import { UserModel } from '../models/user/user' 23import { UserModel } from '../models/user/user'
24import { VideoModel } from '../models/video/video' 24import { VideoModel } from '../models/video/video'
25import { VideoCommentModel } from '../models/video/video-comment' 25import { VideoCommentModel } from '../models/video/video-comment'
@@ -66,7 +66,7 @@ staticRouter.use(
66 66
67// robots.txt service 67// robots.txt service
68staticRouter.get('/robots.txt', 68staticRouter.get('/robots.txt',
69 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.ROBOTS)), 69 cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS),
70 (_, res: express.Response) => { 70 (_, res: express.Response) => {
71 res.type('text/plain') 71 res.type('text/plain')
72 return res.send(CONFIG.INSTANCE.ROBOTS) 72 return res.send(CONFIG.INSTANCE.ROBOTS)
@@ -86,7 +86,7 @@ staticRouter.get('/security.txt',
86) 86)
87 87
88staticRouter.get('/.well-known/security.txt', 88staticRouter.get('/.well-known/security.txt',
89 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.SECURITYTXT)), 89 cacheRoute(ROUTE_CACHE_LIFETIME.SECURITYTXT),
90 (_, res: express.Response) => { 90 (_, res: express.Response) => {
91 res.type('text/plain') 91 res.type('text/plain')
92 return res.send(CONFIG.INSTANCE.SECURITYTXT + CONFIG.INSTANCE.SECURITYTXT_CONTACT) 92 return res.send(CONFIG.INSTANCE.SECURITYTXT + CONFIG.INSTANCE.SECURITYTXT_CONTACT)
@@ -95,7 +95,7 @@ staticRouter.get('/.well-known/security.txt',
95 95
96// nodeinfo service 96// nodeinfo service
97staticRouter.use('/.well-known/nodeinfo', 97staticRouter.use('/.well-known/nodeinfo',
98 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.NODEINFO)), 98 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
99 (_, res: express.Response) => { 99 (_, res: express.Response) => {
100 return res.json({ 100 return res.json({
101 links: [ 101 links: [
@@ -108,13 +108,13 @@ staticRouter.use('/.well-known/nodeinfo',
108 } 108 }
109) 109)
110staticRouter.use('/nodeinfo/:version.json', 110staticRouter.use('/nodeinfo/:version.json',
111 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.NODEINFO)), 111 cacheRoute(ROUTE_CACHE_LIFETIME.NODEINFO),
112 asyncMiddleware(generateNodeinfo) 112 asyncMiddleware(generateNodeinfo)
113) 113)
114 114
115// dnt-policy.txt service (see https://www.eff.org/dnt-policy) 115// dnt-policy.txt service (see https://www.eff.org/dnt-policy)
116staticRouter.use('/.well-known/dnt-policy.txt', 116staticRouter.use('/.well-known/dnt-policy.txt',
117 asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.DNT_POLICY)), 117 cacheRoute(ROUTE_CACHE_LIFETIME.DNT_POLICY),
118 (_, res: express.Response) => { 118 (_, res: express.Response) => {
119 res.type('text/plain') 119 res.type('text/plain')
120 120
diff --git a/server/helpers/custom-validators/follows.ts b/server/helpers/custom-validators/follows.ts
index fbef7ad87..8f65552c3 100644
--- a/server/helpers/custom-validators/follows.ts
+++ b/server/helpers/custom-validators/follows.ts
@@ -1,4 +1,4 @@
1import { exists } from './misc' 1import { exists, isArray } from './misc'
2import { FollowState } from '@shared/models' 2import { FollowState } from '@shared/models'
3 3
4function isFollowStateValid (value: FollowState) { 4function isFollowStateValid (value: FollowState) {
@@ -7,8 +7,24 @@ function isFollowStateValid (value: FollowState) {
7 return value === 'pending' || value === 'accepted' 7 return value === 'pending' || value === 'accepted'
8} 8}
9 9
10function isRemoteHandleValid (value: string) {
11 if (!exists(value)) return false
12 if (typeof value !== 'string') return false
13
14 return value.includes('@')
15}
16
17function isEachUniqueHandleValid (handles: string[]) {
18 return isArray(handles) &&
19 handles.every(handle => {
20 return isRemoteHandleValid(handle) && handles.indexOf(handle) === handles.lastIndexOf(handle)
21 })
22}
23
10// --------------------------------------------------------------------------- 24// ---------------------------------------------------------------------------
11 25
12export { 26export {
13 isFollowStateValid 27 isFollowStateValid,
28 isRemoteHandleValid,
29 isEachUniqueHandleValid
14} 30}
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index 528bfcfb8..c19a3e5eb 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -23,6 +23,10 @@ function isNotEmptyIntArray (value: any) {
23 return Array.isArray(value) && value.every(v => validator.isInt('' + v)) && value.length !== 0 23 return Array.isArray(value) && value.every(v => validator.isInt('' + v)) && value.length !== 0
24} 24}
25 25
26function isNotEmptyStringArray (value: any) {
27 return Array.isArray(value) && value.every(v => typeof v === 'string' && v.length !== 0) && value.length !== 0
28}
29
26function isArrayOf (value: any, validator: (value: any) => boolean) { 30function isArrayOf (value: any, validator: (value: any) => boolean) {
27 return isArray(value) && value.every(v => validator(v)) 31 return isArray(value) && value.every(v => validator(v))
28} 32}
@@ -39,6 +43,10 @@ function isUUIDValid (value: string) {
39 return exists(value) && validator.isUUID('' + value, 4) 43 return exists(value) && validator.isUUID('' + value, 4)
40} 44}
41 45
46function areUUIDsValid (values: string[]) {
47 return isArray(values) && values.every(v => isUUIDValid(v))
48}
49
42function isIdOrUUIDValid (value: string) { 50function isIdOrUUIDValid (value: string) {
43 return isIdValid(value) || isUUIDValid(value) 51 return isIdValid(value) || isUUIDValid(value)
44} 52}
@@ -132,6 +140,10 @@ function toCompleteUUID (value: string) {
132 return value 140 return value
133} 141}
134 142
143function toCompleteUUIDs (values: string[]) {
144 return values.map(v => toCompleteUUID(v))
145}
146
135function toIntOrNull (value: string) { 147function toIntOrNull (value: string) {
136 const v = toValueOrNull(value) 148 const v = toValueOrNull(value)
137 149
@@ -179,7 +191,9 @@ export {
179 isIntOrNull, 191 isIntOrNull,
180 isIdValid, 192 isIdValid,
181 isSafePath, 193 isSafePath,
194 isNotEmptyStringArray,
182 isUUIDValid, 195 isUUIDValid,
196 toCompleteUUIDs,
183 toCompleteUUID, 197 toCompleteUUID,
184 isIdOrUUIDValid, 198 isIdOrUUIDValid,
185 isDateValid, 199 isDateValid,
@@ -187,6 +201,7 @@ export {
187 toBooleanOrNull, 201 toBooleanOrNull,
188 isBooleanValid, 202 isBooleanValid,
189 toIntOrNull, 203 toIntOrNull,
204 areUUIDsValid,
190 toArray, 205 toArray,
191 toIntArray, 206 toIntArray,
192 isFileFieldValid, 207 isFileFieldValid,
diff --git a/server/helpers/custom-validators/servers.ts b/server/helpers/custom-validators/servers.ts
index adf1ea497..c0f8b6aeb 100644
--- a/server/helpers/custom-validators/servers.ts
+++ b/server/helpers/custom-validators/servers.ts
@@ -19,7 +19,6 @@ function isHostValid (host: string) {
19 19
20function isEachUniqueHostValid (hosts: string[]) { 20function isEachUniqueHostValid (hosts: string[]) {
21 return isArray(hosts) && 21 return isArray(hosts) &&
22 hosts.length !== 0 &&
23 hosts.every(host => { 22 hosts.every(host => {
24 return isHostValid(host) && hosts.indexOf(host) === hosts.lastIndexOf(host) 23 return isHostValid(host) && hosts.indexOf(host) === hosts.lastIndexOf(host)
25 }) 24 })
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index ded5d5171..249083f39 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -1,14 +1,20 @@
1import validator from 'validator' 1import validator from 'validator'
2import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 2import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
3import { exists } from './misc' 3import { exists } from './misc'
4import { isUserUsernameValid } from './users'
4 5
5const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS 6const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
6 7
8function isVideoChannelUsernameValid (value: string) {
9 // Use the same constraints than user username
10 return isUserUsernameValid(value)
11}
12
7function isVideoChannelDescriptionValid (value: string) { 13function isVideoChannelDescriptionValid (value: string) {
8 return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION) 14 return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION)
9} 15}
10 16
11function isVideoChannelNameValid (value: string) { 17function isVideoChannelDisplayNameValid (value: string) {
12 return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME) 18 return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME)
13} 19}
14 20
@@ -19,7 +25,8 @@ function isVideoChannelSupportValid (value: string) {
19// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
20 26
21export { 27export {
28 isVideoChannelUsernameValid,
22 isVideoChannelDescriptionValid, 29 isVideoChannelDescriptionValid,
23 isVideoChannelNameValid, 30 isVideoChannelDisplayNameValid,
24 isVideoChannelSupportValid 31 isVideoChannelSupportValid
25} 32}
diff --git a/server/helpers/custom-validators/video-ownership.ts b/server/helpers/custom-validators/video-ownership.ts
index 0e1c63bad..cf15b385a 100644
--- a/server/helpers/custom-validators/video-ownership.ts
+++ b/server/helpers/custom-validators/video-ownership.ts
@@ -1,7 +1,7 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { MUserId } from '@server/types/models' 2import { MUserId } from '@server/types/models'
3import { MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership' 3import { MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5 5
6function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) { 6function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) {
7 if (videoChangeOwnership.NextOwner.userId === user.id) { 7 if (videoChangeOwnership.NextOwner.userId === user.id) {
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts
index b5dc70c17..ec35295df 100644
--- a/server/helpers/database-utils.ts
+++ b/server/helpers/database-utils.ts
@@ -1,12 +1,12 @@
1import * as retry from 'async/retry' 1import * as retry from 'async/retry'
2import * as Bluebird from 'bluebird' 2import * as Bluebird from 'bluebird'
3import { QueryTypes, Transaction } from 'sequelize' 3import { Transaction } from 'sequelize'
4import { Model } from 'sequelize-typescript' 4import { Model } from 'sequelize-typescript'
5import { sequelizeTypescript } from '@server/initializers/database' 5import { sequelizeTypescript } from '@server/initializers/database'
6import { logger } from './logger' 6import { logger } from './logger'
7 7
8function retryTransactionWrapper <T, A, B, C, D> ( 8function retryTransactionWrapper <T, A, B, C, D> (
9 functionToRetry: (arg1: A, arg2: B, arg3: C, arg4: D) => Promise<T> | Bluebird<T>, 9 functionToRetry: (arg1: A, arg2: B, arg3: C, arg4: D) => Promise<T>,
10 arg1: A, 10 arg1: A,
11 arg2: B, 11 arg2: B,
12 arg3: C, 12 arg3: C,
@@ -14,20 +14,20 @@ function retryTransactionWrapper <T, A, B, C, D> (
14): Promise<T> 14): Promise<T>
15 15
16function retryTransactionWrapper <T, A, B, C> ( 16function retryTransactionWrapper <T, A, B, C> (
17 functionToRetry: (arg1: A, arg2: B, arg3: C) => Promise<T> | Bluebird<T>, 17 functionToRetry: (arg1: A, arg2: B, arg3: C) => Promise<T>,
18 arg1: A, 18 arg1: A,
19 arg2: B, 19 arg2: B,
20 arg3: C 20 arg3: C
21): Promise<T> 21): Promise<T>
22 22
23function retryTransactionWrapper <T, A, B> ( 23function retryTransactionWrapper <T, A, B> (
24 functionToRetry: (arg1: A, arg2: B) => Promise<T> | Bluebird<T>, 24 functionToRetry: (arg1: A, arg2: B) => Promise<T>,
25 arg1: A, 25 arg1: A,
26 arg2: B 26 arg2: B
27): Promise<T> 27): Promise<T>
28 28
29function retryTransactionWrapper <T, A> ( 29function retryTransactionWrapper <T, A> (
30 functionToRetry: (arg1: A) => Promise<T> | Bluebird<T>, 30 functionToRetry: (arg1: A) => Promise<T>,
31 arg1: A 31 arg1: A
32): Promise<T> 32): Promise<T>
33 33
@@ -36,7 +36,7 @@ function retryTransactionWrapper <T> (
36): Promise<T> 36): Promise<T>
37 37
38function retryTransactionWrapper <T> ( 38function retryTransactionWrapper <T> (
39 functionToRetry: (...args: any[]) => Promise<T> | Bluebird<T>, 39 functionToRetry: (...args: any[]) => Promise<T>,
40 ...args: any[] 40 ...args: any[]
41): Promise<T> { 41): Promise<T> {
42 return transactionRetryer<T>(callback => { 42 return transactionRetryer<T>(callback => {
@@ -84,25 +84,15 @@ function resetSequelizeInstance (instance: Model<any>, savedFields: object) {
84 }) 84 })
85} 85}
86 86
87function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Pick<Model, 'destroy'>> ( 87function filterNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean }> (
88 fromDatabase: T[], 88 fromDatabase: T[],
89 newModels: T[], 89 newModels: T[]
90 t: Transaction
91) { 90) {
92 return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f))) 91 return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f)))
93 .map(f => f.destroy({ transaction: t }))
94} 92}
95 93
96// Sequelize always skip the update if we only update updatedAt field 94function deleteAllModels <T extends Pick<Model, 'destroy'>> (models: T[], transaction: Transaction) {
97function setAsUpdated (table: string, id: number, transaction?: Transaction) { 95 return Promise.all(models.map(f => f.destroy({ transaction })))
98 return sequelizeTypescript.query(
99 `UPDATE "${table}" SET "updatedAt" = :updatedAt WHERE id = :id`,
100 {
101 replacements: { table, id, updatedAt: new Date() },
102 type: QueryTypes.UPDATE,
103 transaction
104 }
105 )
106} 96}
107 97
108// --------------------------------------------------------------------------- 98// ---------------------------------------------------------------------------
@@ -127,7 +117,7 @@ export {
127 transactionRetryer, 117 transactionRetryer,
128 updateInstanceWithAnother, 118 updateInstanceWithAnother,
129 afterCommitIfTransaction, 119 afterCommitIfTransaction,
130 deleteNonExistingModels, 120 filterNonExistingModels,
131 setAsUpdated, 121 deleteAllModels,
132 runInReadCommittedTransaction 122 runInReadCommittedTransaction
133} 123}
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts
index 0ff113274..c299b70f1 100644
--- a/server/helpers/express-utils.ts
+++ b/server/helpers/express-utils.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as multer from 'multer' 2import * as multer from 'multer'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
4import { CONFIG } from '../initializers/config' 4import { CONFIG } from '../initializers/config'
5import { REMOTE_SCHEME } from '../initializers/constants' 5import { REMOTE_SCHEME } from '../initializers/constants'
6import { getLowercaseExtension } from './core-utils' 6import { getLowercaseExtension } from './core-utils'
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index 6f5a71b4a..61c8a6db2 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -212,14 +212,17 @@ async function transcode (options: TranscodeOptions) {
212 212
213async function getLiveTranscodingCommand (options: { 213async function getLiveTranscodingCommand (options: {
214 rtmpUrl: string 214 rtmpUrl: string
215
215 outPath: string 216 outPath: string
217 masterPlaylistName: string
218
216 resolutions: number[] 219 resolutions: number[]
217 fps: number 220 fps: number
218 221
219 availableEncoders: AvailableEncoders 222 availableEncoders: AvailableEncoders
220 profile: string 223 profile: string
221}) { 224}) {
222 const { rtmpUrl, outPath, resolutions, fps, availableEncoders, profile } = options 225 const { rtmpUrl, outPath, resolutions, fps, availableEncoders, profile, masterPlaylistName } = options
223 const input = rtmpUrl 226 const input = rtmpUrl
224 227
225 const command = getFFmpeg(input, 'live') 228 const command = getFFmpeg(input, 'live')
@@ -301,14 +304,14 @@ async function getLiveTranscodingCommand (options: {
301 304
302 command.complexFilter(complexFilter) 305 command.complexFilter(complexFilter)
303 306
304 addDefaultLiveHLSParams(command, outPath) 307 addDefaultLiveHLSParams(command, outPath, masterPlaylistName)
305 308
306 command.outputOption('-var_stream_map', varStreamMap.join(' ')) 309 command.outputOption('-var_stream_map', varStreamMap.join(' '))
307 310
308 return command 311 return command
309} 312}
310 313
311function getLiveMuxingCommand (rtmpUrl: string, outPath: string) { 314function getLiveMuxingCommand (rtmpUrl: string, outPath: string, masterPlaylistName: string) {
312 const command = getFFmpeg(rtmpUrl, 'live') 315 const command = getFFmpeg(rtmpUrl, 'live')
313 316
314 command.outputOption('-c:v copy') 317 command.outputOption('-c:v copy')
@@ -316,7 +319,7 @@ function getLiveMuxingCommand (rtmpUrl: string, outPath: string) {
316 command.outputOption('-map 0:a?') 319 command.outputOption('-map 0:a?')
317 command.outputOption('-map 0:v?') 320 command.outputOption('-map 0:v?')
318 321
319 addDefaultLiveHLSParams(command, outPath) 322 addDefaultLiveHLSParams(command, outPath, masterPlaylistName)
320 323
321 return command 324 return command
322} 325}
@@ -371,12 +374,12 @@ function addDefaultEncoderParams (options: {
371 } 374 }
372} 375}
373 376
374function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string) { 377function addDefaultLiveHLSParams (command: ffmpeg.FfmpegCommand, outPath: string, masterPlaylistName: string) {
375 command.outputOption('-hls_time ' + VIDEO_LIVE.SEGMENT_TIME_SECONDS) 378 command.outputOption('-hls_time ' + VIDEO_LIVE.SEGMENT_TIME_SECONDS)
376 command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE) 379 command.outputOption('-hls_list_size ' + VIDEO_LIVE.SEGMENTS_LIST_SIZE)
377 command.outputOption('-hls_flags delete_segments+independent_segments') 380 command.outputOption('-hls_flags delete_segments+independent_segments')
378 command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%06d.ts')}`) 381 command.outputOption(`-hls_segment_filename ${join(outPath, '%v-%06d.ts')}`)
379 command.outputOption('-master_pl_name master.m3u8') 382 command.outputOption('-master_pl_name ' + masterPlaylistName)
380 command.outputOption(`-f hls`) 383 command.outputOption(`-f hls`)
381 384
382 command.output(join(outPath, '%v.m3u8')) 385 command.output(join(outPath, '%v.m3u8'))
@@ -700,6 +703,10 @@ async function runCommand (options: {
700 const { command, silent = false, job } = options 703 const { command, silent = false, job } = options
701 704
702 return new Promise<void>((res, rej) => { 705 return new Promise<void>((res, rej) => {
706 let shellCommand: string
707
708 command.on('start', cmdline => { shellCommand = cmdline })
709
703 command.on('error', (err, stdout, stderr) => { 710 command.on('error', (err, stdout, stderr) => {
704 if (silent !== true) logger.error('Error in ffmpeg.', { stdout, stderr }) 711 if (silent !== true) logger.error('Error in ffmpeg.', { stdout, stderr })
705 712
@@ -707,7 +714,7 @@ async function runCommand (options: {
707 }) 714 })
708 715
709 command.on('end', (stdout, stderr) => { 716 command.on('end', (stdout, stderr) => {
710 logger.debug('FFmpeg command ended.', { stdout, stderr }) 717 logger.debug('FFmpeg command ended.', { stdout, stderr, shellCommand })
711 718
712 res() 719 res()
713 }) 720 })
diff --git a/server/helpers/logger.ts b/server/helpers/logger.ts
index 29e06860d..20c3c3edb 100644
--- a/server/helpers/logger.ts
+++ b/server/helpers/logger.ts
@@ -1,5 +1,5 @@
1// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/ 1// Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/
2import { mkdirpSync } from 'fs-extra' 2import { mkdirpSync, stat } from 'fs-extra'
3import { omit } from 'lodash' 3import { omit } from 'lodash'
4import * as path from 'path' 4import * as path from 'path'
5import { format as sqlFormat } from 'sql-formatter' 5import { format as sqlFormat } from 'sql-formatter'
@@ -158,6 +158,26 @@ function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn {
158 } 158 }
159} 159}
160 160
161async function mtimeSortFilesDesc (files: string[], basePath: string) {
162 const promises = []
163 const out: { file: string, mtime: number }[] = []
164
165 for (const file of files) {
166 const p = stat(basePath + '/' + file)
167 .then(stats => {
168 if (stats.isFile()) out.push({ file, mtime: stats.mtime.getTime() })
169 })
170
171 promises.push(p)
172 }
173
174 await Promise.all(promises)
175
176 out.sort((a, b) => b.mtime - a.mtime)
177
178 return out
179}
180
161// --------------------------------------------------------------------------- 181// ---------------------------------------------------------------------------
162 182
163export { 183export {
@@ -168,6 +188,7 @@ export {
168 labelFormatter, 188 labelFormatter,
169 consoleLoggerFormat, 189 consoleLoggerFormat,
170 jsonLoggerFormat, 190 jsonLoggerFormat,
191 mtimeSortFilesDesc,
171 logger, 192 logger,
172 loggerTagsFactory, 193 loggerTagsFactory,
173 bunyanLogger 194 bunyanLogger
diff --git a/server/helpers/query.ts b/server/helpers/query.ts
new file mode 100644
index 000000000..e711b15f2
--- /dev/null
+++ b/server/helpers/query.ts
@@ -0,0 +1,74 @@
1import { pick } from '@shared/core-utils'
2import {
3 VideoChannelsSearchQueryAfterSanitize,
4 VideoPlaylistsSearchQueryAfterSanitize,
5 VideosCommonQueryAfterSanitize,
6 VideosSearchQueryAfterSanitize
7} from '@shared/models'
8
9function pickCommonVideoQuery (query: VideosCommonQueryAfterSanitize) {
10 return pick(query, [
11 'start',
12 'count',
13 'sort',
14 'nsfw',
15 'isLive',
16 'categoryOneOf',
17 'licenceOneOf',
18 'languageOneOf',
19 'tagsOneOf',
20 'tagsAllOf',
21 'filter',
22 'skipCount'
23 ])
24}
25
26function pickSearchVideoQuery (query: VideosSearchQueryAfterSanitize) {
27 return {
28 ...pickCommonVideoQuery(query),
29
30 ...pick(query, [
31 'searchTarget',
32 'search',
33 'host',
34 'startDate',
35 'endDate',
36 'originallyPublishedStartDate',
37 'originallyPublishedEndDate',
38 'durationMin',
39 'durationMax',
40 'uuids'
41 ])
42 }
43}
44
45function pickSearchChannelQuery (query: VideoChannelsSearchQueryAfterSanitize) {
46 return pick(query, [
47 'searchTarget',
48 'search',
49 'start',
50 'count',
51 'sort',
52 'host',
53 'handles'
54 ])
55}
56
57function pickSearchPlaylistQuery (query: VideoPlaylistsSearchQueryAfterSanitize) {
58 return pick(query, [
59 'searchTarget',
60 'search',
61 'start',
62 'count',
63 'sort',
64 'host',
65 'uuids'
66 ])
67}
68
69export {
70 pickCommonVideoQuery,
71 pickSearchVideoQuery,
72 pickSearchPlaylistQuery,
73 pickSearchChannelQuery
74}
diff --git a/server/helpers/webtorrent.ts b/server/helpers/webtorrent.ts
index d8220ba9c..ecf63e93e 100644
--- a/server/helpers/webtorrent.ts
+++ b/server/helpers/webtorrent.ts
@@ -103,6 +103,11 @@ async function createTorrentAndSetInfoHash (
103 103
104 await writeFile(torrentPath, torrent) 104 await writeFile(torrentPath, torrent)
105 105
106 // Remove old torrent file if it existed
107 if (videoFile.hasTorrent()) {
108 await remove(join(CONFIG.STORAGE.TORRENTS_DIR, videoFile.torrentFilename))
109 }
110
106 const parsedTorrent = parseTorrent(torrent) 111 const parsedTorrent = parseTorrent(torrent)
107 videoFile.infoHash = parsedTorrent.infoHash 112 videoFile.infoHash = parsedTorrent.infoHash
108 videoFile.torrentFilename = torrentFilename 113 videoFile.torrentFilename = torrentFilename
diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts
index fdd361390..3c80e7d41 100644
--- a/server/helpers/youtube-dl.ts
+++ b/server/helpers/youtube-dl.ts
@@ -3,7 +3,7 @@ import { ensureDir, move, pathExists, remove, writeFile } from 'fs-extra'
3import got from 'got' 3import got from 'got'
4import { join } from 'path' 4import { join } from 'path'
5import { CONFIG } from '@server/initializers/config' 5import { CONFIG } from '@server/initializers/config'
6import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
7import { VideoResolution } from '../../shared/models/videos' 7import { VideoResolution } from '../../shared/models/videos'
8import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' 8import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants'
9import { peertubeTruncate, pipelinePromise, root } from './core-utils' 9import { peertubeTruncate, pipelinePromise, root } from './core-utils'
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index ab59320eb..5f121d9a4 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -2,7 +2,7 @@ import { CronRepeatOptions, EveryRepeatOptions } from 'bull'
2import { randomBytes } from 'crypto' 2import { randomBytes } from 'crypto'
3import { invert } from 'lodash' 3import { invert } from 'lodash'
4import { join } from 'path' 4import { join } from 'path'
5import { randomInt } from '../../shared/core-utils/miscs/miscs' 5import { randomInt } from '../../shared/core-utils/common/miscs'
6import { 6import {
7 AbuseState, 7 AbuseState,
8 JobType, 8 JobType,
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
24 24
25// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
26 26
27const LAST_MIGRATION_VERSION = 650 27const LAST_MIGRATION_VERSION = 655
28 28
29// --------------------------------------------------------------------------- 29// ---------------------------------------------------------------------------
30 30
diff --git a/server/initializers/migrations/0655-streaming-playlist-filenames.ts b/server/initializers/migrations/0655-streaming-playlist-filenames.ts
new file mode 100644
index 000000000..9172a22c4
--- /dev/null
+++ b/server/initializers/migrations/0655-streaming-playlist-filenames.ts
@@ -0,0 +1,66 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 {
10 for (const column of [ 'playlistUrl', 'segmentsSha256Url' ]) {
11 const data = {
12 type: Sequelize.STRING,
13 allowNull: true,
14 defaultValue: null
15 }
16
17 await utils.queryInterface.changeColumn('videoStreamingPlaylist', column, data)
18 }
19 }
20
21 {
22 await utils.sequelize.query(
23 `UPDATE "videoStreamingPlaylist" SET "playlistUrl" = NULL, "segmentsSha256Url" = NULL ` +
24 `WHERE "videoId" IN (SELECT id FROM video WHERE remote IS FALSE)`
25 )
26 }
27
28 {
29 for (const column of [ 'playlistFilename', 'segmentsSha256Filename' ]) {
30 const data = {
31 type: Sequelize.STRING,
32 allowNull: true,
33 defaultValue: null
34 }
35
36 await utils.queryInterface.addColumn('videoStreamingPlaylist', column, data)
37 }
38 }
39
40 {
41 await utils.sequelize.query(
42 `UPDATE "videoStreamingPlaylist" SET "playlistFilename" = 'master.m3u8', "segmentsSha256Filename" = 'segments-sha256.json'`
43 )
44 }
45
46 {
47 for (const column of [ 'playlistFilename', 'segmentsSha256Filename' ]) {
48 const data = {
49 type: Sequelize.STRING,
50 allowNull: false,
51 defaultValue: null
52 }
53
54 await utils.queryInterface.changeColumn('videoStreamingPlaylist', column, data)
55 }
56 }
57}
58
59function down (options) {
60 throw new Error('Not implemented.')
61}
62
63export {
64 up,
65 down
66}
diff --git a/server/lib/activitypub/actors/refresh.ts b/server/lib/activitypub/actors/refresh.ts
index b2fe3932f..0acaa9f62 100644
--- a/server/lib/activitypub/actors/refresh.ts
+++ b/server/lib/activitypub/actors/refresh.ts
@@ -4,7 +4,7 @@ import { PeerTubeRequestError } from '@server/helpers/requests'
4import { ActorLoadByUrlType } from '@server/lib/model-loaders' 4import { ActorLoadByUrlType } from '@server/lib/model-loaders'
5import { ActorModel } from '@server/models/actor/actor' 5import { ActorModel } from '@server/models/actor/actor'
6import { MActorAccountChannelId, MActorFull } from '@server/types/models' 6import { MActorAccountChannelId, MActorFull } from '@server/types/models'
7import { HttpStatusCode } from '@shared/core-utils' 7import { HttpStatusCode } from '@shared/models'
8import { fetchRemoteActor } from './shared' 8import { fetchRemoteActor } from './shared'
9import { APActorUpdater } from './updater' 9import { APActorUpdater } from './updater'
10import { getUrlFromWebfinger } from './webfinger' 10import { getUrlFromWebfinger } from './webfinger'
diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts
index cd117f571..28ff5225a 100644
--- a/server/lib/activitypub/crawl.ts
+++ b/server/lib/activitypub/crawl.ts
@@ -1,3 +1,4 @@
1import { retryTransactionWrapper } from '@server/helpers/database-utils'
1import * as Bluebird from 'bluebird' 2import * as Bluebird from 'bluebird'
2import { URL } from 'url' 3import { URL } from 'url'
3import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub' 4import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
@@ -51,7 +52,7 @@ async function crawlCollectionPage <T> (argUrl: string, handler: HandlerFunction
51 } 52 }
52 } 53 }
53 54
54 if (cleaner) await cleaner(startDate) 55 if (cleaner) await retryTransactionWrapper(cleaner, startDate)
55} 56}
56 57
57export { 58export {
diff --git a/server/lib/activitypub/follow.ts b/server/lib/activitypub/follow.ts
index c1bd667e0..741b54df5 100644
--- a/server/lib/activitypub/follow.ts
+++ b/server/lib/activitypub/follow.ts
@@ -31,6 +31,21 @@ async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors, transact
31 } 31 }
32} 32}
33 33
34// If we only have an host, use a default account handle
35function getRemoteNameAndHost (handleOrHost: string) {
36 let name = SERVER_ACTOR_NAME
37 let host = handleOrHost
38
39 const splitted = handleOrHost.split('@')
40 if (splitted.length === 2) {
41 name = splitted[0]
42 host = splitted[1]
43 }
44
45 return { name, host }
46}
47
34export { 48export {
35 autoFollowBackIfNeeded 49 autoFollowBackIfNeeded,
50 getRemoteNameAndHost
36} 51}
diff --git a/server/lib/activitypub/playlists/refresh.ts b/server/lib/activitypub/playlists/refresh.ts
index ef3cb3fe4..493e8c7ec 100644
--- a/server/lib/activitypub/playlists/refresh.ts
+++ b/server/lib/activitypub/playlists/refresh.ts
@@ -2,7 +2,7 @@ import { logger, loggerTagsFactory } from '@server/helpers/logger'
2import { PeerTubeRequestError } from '@server/helpers/requests' 2import { PeerTubeRequestError } from '@server/helpers/requests'
3import { JobQueue } from '@server/lib/job-queue' 3import { JobQueue } from '@server/lib/job-queue'
4import { MVideoPlaylist, MVideoPlaylistOwner } from '@server/types/models' 4import { MVideoPlaylist, MVideoPlaylistOwner } from '@server/types/models'
5import { HttpStatusCode } from '@shared/core-utils' 5import { HttpStatusCode } from '@shared/models'
6import { createOrUpdateVideoPlaylist } from './create-update' 6import { createOrUpdateVideoPlaylist } from './create-update'
7import { fetchRemoteVideoPlaylist } from './shared' 7import { fetchRemoteVideoPlaylist } from './shared'
8 8
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index 7816b0be0..338398f2b 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -1,5 +1,6 @@
1import { WEBSERVER } from '../../initializers/constants' 1import { WEBSERVER } from '../../initializers/constants'
2import { 2import {
3 MAbuseFull,
3 MAbuseId, 4 MAbuseId,
4 MActor, 5 MActor,
5 MActorFollowActors, 6 MActorFollowActors,
@@ -112,6 +113,14 @@ function getUndoActivityPubUrl (originalUrl: string) {
112 return originalUrl + '/undo' 113 return originalUrl + '/undo'
113} 114}
114 115
116// ---------------------------------------------------------------------------
117
118function getAbuseTargetUrl (abuse: MAbuseFull) {
119 return abuse.VideoAbuse?.Video?.url ||
120 abuse.VideoCommentAbuse?.VideoComment?.url ||
121 abuse.FlaggedAccount.Actor.url
122}
123
115export { 124export {
116 getLocalVideoActivityPubUrl, 125 getLocalVideoActivityPubUrl,
117 getLocalVideoPlaylistActivityPubUrl, 126 getLocalVideoPlaylistActivityPubUrl,
@@ -135,5 +144,6 @@ export {
135 getLocalVideoSharesActivityPubUrl, 144 getLocalVideoSharesActivityPubUrl,
136 getLocalVideoCommentsActivityPubUrl, 145 getLocalVideoCommentsActivityPubUrl,
137 getLocalVideoLikesActivityPubUrl, 146 getLocalVideoLikesActivityPubUrl,
138 getLocalVideoDislikesActivityPubUrl 147 getLocalVideoDislikesActivityPubUrl,
148 getAbuseTargetUrl
139} 149}
diff --git a/server/lib/activitypub/videos/refresh.ts b/server/lib/activitypub/videos/refresh.ts
index a7b82f286..3af08acf4 100644
--- a/server/lib/activitypub/videos/refresh.ts
+++ b/server/lib/activitypub/videos/refresh.ts
@@ -4,7 +4,7 @@ import { ActorFollowScoreCache } from '@server/lib/files-cache'
4import { VideoLoadByUrlType } from '@server/lib/model-loaders' 4import { VideoLoadByUrlType } from '@server/lib/model-loaders'
5import { VideoModel } from '@server/models/video/video' 5import { VideoModel } from '@server/models/video/video'
6import { MVideoAccountLightBlacklistAllFiles, MVideoThumbnail } from '@server/types/models' 6import { MVideoAccountLightBlacklistAllFiles, MVideoThumbnail } from '@server/types/models'
7import { HttpStatusCode } from '@shared/core-utils' 7import { HttpStatusCode } from '@shared/models'
8import { fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared' 8import { fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared'
9import { APVideoUpdater } from './updater' 9import { APVideoUpdater } from './updater'
10 10
diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts
index e89c94bcd..f995fe637 100644
--- a/server/lib/activitypub/videos/shared/abstract-builder.ts
+++ b/server/lib/activitypub/videos/shared/abstract-builder.ts
@@ -1,6 +1,6 @@
1import { Transaction } from 'sequelize/types' 1import { Transaction } from 'sequelize/types'
2import { checkUrlsSameHost } from '@server/helpers/activitypub' 2import { checkUrlsSameHost } from '@server/helpers/activitypub'
3import { deleteNonExistingModels } from '@server/helpers/database-utils' 3import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils'
4import { logger, LoggerTagsFn } from '@server/helpers/logger' 4import { logger, LoggerTagsFn } from '@server/helpers/logger'
5import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail' 5import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail'
6import { setVideoTags } from '@server/lib/video' 6import { setVideoTags } from '@server/lib/video'
@@ -111,8 +111,7 @@ export abstract class APVideoAbstractBuilder {
111 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) 111 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
112 112
113 // Remove video files that do not exist anymore 113 // Remove video files that do not exist anymore
114 const destroyTasks = deleteNonExistingModels(video.VideoFiles || [], newVideoFiles, t) 114 await deleteAllModels(filterNonExistingModels(video.VideoFiles || [], newVideoFiles), t)
115 await Promise.all(destroyTasks)
116 115
117 // Update or add other one 116 // Update or add other one
118 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t)) 117 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t))
@@ -124,13 +123,11 @@ export abstract class APVideoAbstractBuilder {
124 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) 123 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
125 124
126 // Remove video playlists that do not exist anymore 125 // Remove video playlists that do not exist anymore
127 const destroyTasks = deleteNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists, t) 126 await deleteAllModels(filterNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists), t)
128 await Promise.all(destroyTasks)
129 127
130 video.VideoStreamingPlaylists = [] 128 video.VideoStreamingPlaylists = []
131 129
132 for (const playlistAttributes of streamingPlaylistAttributes) { 130 for (const playlistAttributes of streamingPlaylistAttributes) {
133
134 const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t) 131 const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t)
135 streamingPlaylistModel.Video = video 132 streamingPlaylistModel.Video = video
136 133
@@ -163,8 +160,7 @@ export abstract class APVideoAbstractBuilder {
163 160
164 const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a)) 161 const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a))
165 162
166 const destroyTasks = deleteNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles, t) 163 await deleteAllModels(filterNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles), t)
167 await Promise.all(destroyTasks)
168 164
169 // Update or add other one 165 // Update or add other one
170 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t)) 166 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t))
diff --git a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
index 85548428c..1fa16295d 100644
--- a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
+++ b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts
@@ -7,10 +7,11 @@ import { logger } from '@server/helpers/logger'
7import { getExtFromMimetype } from '@server/helpers/video' 7import { getExtFromMimetype } from '@server/helpers/video'
8import { ACTIVITY_PUB, MIMETYPES, P2P_MEDIA_LOADER_PEER_VERSION, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '@server/initializers/constants' 8import { ACTIVITY_PUB, MIMETYPES, P2P_MEDIA_LOADER_PEER_VERSION, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '@server/initializers/constants'
9import { generateTorrentFileName } from '@server/lib/video-paths' 9import { generateTorrentFileName } from '@server/lib/video-paths'
10import { VideoCaptionModel } from '@server/models/video/video-caption'
10import { VideoFileModel } from '@server/models/video/video-file' 11import { VideoFileModel } from '@server/models/video/video-file'
11import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 12import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
12import { FilteredModelAttributes } from '@server/types' 13import { FilteredModelAttributes } from '@server/types'
13import { MChannelId, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models' 14import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models'
14import { 15import {
15 ActivityHashTagObject, 16 ActivityHashTagObject,
16 ActivityMagnetUrlObject, 17 ActivityMagnetUrlObject,
@@ -23,7 +24,6 @@ import {
23 VideoPrivacy, 24 VideoPrivacy,
24 VideoStreamingPlaylistType 25 VideoStreamingPlaylistType
25} from '@shared/models' 26} from '@shared/models'
26import { VideoCaptionModel } from '@server/models/video/video-caption'
27 27
28function getThumbnailFromIcons (videoObject: VideoObject) { 28function getThumbnailFromIcons (videoObject: VideoObject) {
29 let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth) 29 let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth)
@@ -80,8 +80,8 @@ function getFileAttributesFromUrl (
80 80
81 const extname = getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, fileUrl.mediaType) 81 const extname = getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, fileUrl.mediaType)
82 const resolution = fileUrl.height 82 const resolution = fileUrl.height
83 const videoId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? null : videoOrPlaylist.id 83 const videoId = isStreamingPlaylist(videoOrPlaylist) ? null : videoOrPlaylist.id
84 const videoStreamingPlaylistId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? videoOrPlaylist.id : null 84 const videoStreamingPlaylistId = isStreamingPlaylist(videoOrPlaylist) ? videoOrPlaylist.id : null
85 85
86 const attribute = { 86 const attribute = {
87 extname, 87 extname,
@@ -130,8 +130,13 @@ function getStreamingPlaylistAttributesFromObject (video: MVideoId, videoObject:
130 130
131 const attribute = { 131 const attribute = {
132 type: VideoStreamingPlaylistType.HLS, 132 type: VideoStreamingPlaylistType.HLS,
133
134 playlistFilename: basename(playlistUrlObject.href),
133 playlistUrl: playlistUrlObject.href, 135 playlistUrl: playlistUrlObject.href,
136
137 segmentsSha256Filename: basename(segmentsSha256UrlObject.href),
134 segmentsSha256Url: segmentsSha256UrlObject.href, 138 segmentsSha256Url: segmentsSha256UrlObject.href,
139
135 p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrlObject.href, files), 140 p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrlObject.href, files),
136 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, 141 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION,
137 videoId: video.id, 142 videoId: video.id,
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 72194416d..e093d35f7 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -5,7 +5,7 @@ import validator from 'validator'
5import { escapeHTML } from '@shared/core-utils/renderer' 5import { escapeHTML } from '@shared/core-utils/renderer'
6import { HTMLServerConfig } from '@shared/models' 6import { HTMLServerConfig } from '@shared/models'
7import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' 7import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n'
8import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
9import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' 9import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos'
10import { isTestInstance, sha256 } from '../helpers/core-utils' 10import { isTestInstance, sha256 } from '../helpers/core-utils'
11import { logger } from '../helpers/logger' 11import { logger } from '../helpers/logger'
@@ -44,6 +44,8 @@ type Tags = {
44 originUrl: string 44 originUrl: string
45 description: string 45 description: string
46 46
47 disallowIndexation?: boolean
48
47 embed?: { 49 embed?: {
48 url: string 50 url: string
49 createdAt: string 51 createdAt: string
@@ -162,7 +164,7 @@ class ClientHtml {
162 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name)) 164 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name))
163 customHtml = ClientHtml.addDescriptionTag(customHtml, mdToPlainText(videoPlaylist.description)) 165 customHtml = ClientHtml.addDescriptionTag(customHtml, mdToPlainText(videoPlaylist.description))
164 166
165 const url = videoPlaylist.getWatchUrl() 167 const url = WEBSERVER.URL + videoPlaylist.getWatchStaticPath()
166 const originUrl = videoPlaylist.url 168 const originUrl = videoPlaylist.url
167 const title = escapeHTML(videoPlaylist.name) 169 const title = escapeHTML(videoPlaylist.name)
168 const siteName = escapeHTML(CONFIG.INSTANCE.NAME) 170 const siteName = escapeHTML(CONFIG.INSTANCE.NAME)
@@ -285,7 +287,8 @@ class ClientHtml {
285 image, 287 image,
286 ogType, 288 ogType,
287 twitterCard, 289 twitterCard,
288 schemaType 290 schemaType,
291 disallowIndexation: !entity.Actor.isOwned()
289 }) 292 })
290 293
291 return customHtml 294 return customHtml
@@ -488,7 +491,7 @@ class ClientHtml {
488 const twitterCardMetaTags = this.generateTwitterCardMetaTags(tagsValues) 491 const twitterCardMetaTags = this.generateTwitterCardMetaTags(tagsValues)
489 const schemaTags = this.generateSchemaTags(tagsValues) 492 const schemaTags = this.generateSchemaTags(tagsValues)
490 493
491 const { url, title, embed, originUrl } = tagsValues 494 const { url, title, embed, originUrl, disallowIndexation } = tagsValues
492 495
493 const oembedLinkTags: { type: string, href: string, title: string }[] = [] 496 const oembedLinkTags: { type: string, href: string, title: string }[] = []
494 497
@@ -536,6 +539,10 @@ class ClientHtml {
536 // SEO, use origin URL 539 // SEO, use origin URL
537 tagsString += `<link rel="canonical" href="${originUrl}" />` 540 tagsString += `<link rel="canonical" href="${originUrl}" />`
538 541
542 if (disallowIndexation) {
543 tagsString += `<meta name="robots" content="noindex" />`
544 }
545
539 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, tagsString) 546 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, tagsString)
540 } 547 }
541} 548}
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 458214f88..6bb61484b 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -1,20 +1,15 @@
1import { readFileSync } from 'fs-extra' 1import { readFileSync } from 'fs-extra'
2import { merge } from 'lodash' 2import { isArray, merge } from 'lodash'
3import { createTransport, Transporter } from 'nodemailer' 3import { createTransport, Transporter } from 'nodemailer'
4import { join } from 'path' 4import { join } from 'path'
5import { VideoChannelModel } from '@server/models/video/video-channel' 5import { EmailPayload } from '@shared/models'
6import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
7import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import'
8import { AbuseState, EmailPayload, UserAbuse } from '@shared/models'
9import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model' 6import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model'
10import { isTestInstance, root } from '../helpers/core-utils' 7import { isTestInstance, root } from '../helpers/core-utils'
11import { bunyanLogger, logger } from '../helpers/logger' 8import { bunyanLogger, logger } from '../helpers/logger'
12import { CONFIG, isEmailEnabled } from '../initializers/config' 9import { CONFIG, isEmailEnabled } from '../initializers/config'
13import { WEBSERVER } from '../initializers/constants' 10import { WEBSERVER } from '../initializers/constants'
14import { MAbuseFull, MAbuseMessage, MAccountDefault, MActorFollowActors, MActorFollowFull, MPlugin, MUser } from '../types/models' 11import { MUser } from '../types/models'
15import { MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../types/models/video'
16import { JobQueue } from './job-queue' 12import { JobQueue } from './job-queue'
17import { toSafeHtml } from '../helpers/markdown'
18 13
19const Email = require('email-templates') 14const Email = require('email-templates')
20 15
@@ -59,429 +54,6 @@ class Emailer {
59 } 54 }
60 } 55 }
61 56
62 addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) {
63 const channelName = video.VideoChannel.getDisplayName()
64 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
65
66 const emailPayload: EmailPayload = {
67 to,
68 subject: channelName + ' just published a new video',
69 text: `Your subscription ${channelName} just published a new video: "${video.name}".`,
70 locals: {
71 title: 'New content ',
72 action: {
73 text: 'View video',
74 url: videoUrl
75 }
76 }
77 }
78
79 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
80 }
81
82 addNewFollowNotification (to: string[], actorFollow: MActorFollowFull, followType: 'account' | 'channel') {
83 const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName()
84
85 const emailPayload: EmailPayload = {
86 template: 'follower-on-channel',
87 to,
88 subject: `New follower on your channel ${followingName}`,
89 locals: {
90 followerName: actorFollow.ActorFollower.Account.getDisplayName(),
91 followerUrl: actorFollow.ActorFollower.url,
92 followingName,
93 followingUrl: actorFollow.ActorFollowing.url,
94 followType
95 }
96 }
97
98 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
99 }
100
101 addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) {
102 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : ''
103
104 const emailPayload: EmailPayload = {
105 to,
106 subject: 'New instance follower',
107 text: `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}.`,
108 locals: {
109 title: 'New instance follower',
110 action: {
111 text: 'Review followers',
112 url: WEBSERVER.URL + '/admin/follows/followers-list'
113 }
114 }
115 }
116
117 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
118 }
119
120 addAutoInstanceFollowingNotification (to: string[], actorFollow: MActorFollowActors) {
121 const instanceUrl = actorFollow.ActorFollowing.url
122 const emailPayload: EmailPayload = {
123 to,
124 subject: 'Auto instance following',
125 text: `Your instance automatically followed a new instance: <a href="${instanceUrl}">${instanceUrl}</a>.`
126 }
127
128 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
129 }
130
131 myVideoPublishedNotification (to: string[], video: MVideo) {
132 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
133
134 const emailPayload: EmailPayload = {
135 to,
136 subject: `Your video ${video.name} has been published`,
137 text: `Your video "${video.name}" has been published.`,
138 locals: {
139 title: 'You video is live',
140 action: {
141 text: 'View video',
142 url: videoUrl
143 }
144 }
145 }
146
147 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
148 }
149
150 myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) {
151 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
152
153 const emailPayload: EmailPayload = {
154 to,
155 subject: `Your video import ${videoImport.getTargetIdentifier()} is complete`,
156 text: `Your video "${videoImport.getTargetIdentifier()}" just finished importing.`,
157 locals: {
158 title: 'Import complete',
159 action: {
160 text: 'View video',
161 url: videoUrl
162 }
163 }
164 }
165
166 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
167 }
168
169 myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) {
170 const importUrl = WEBSERVER.URL + '/my-library/video-imports'
171
172 const text =
173 `Your video import "${videoImport.getTargetIdentifier()}" encountered an error.` +
174 '\n\n' +
175 `See your videos import dashboard for more information: <a href="${importUrl}">${importUrl}</a>.`
176
177 const emailPayload: EmailPayload = {
178 to,
179 subject: `Your video import "${videoImport.getTargetIdentifier()}" encountered an error`,
180 text,
181 locals: {
182 title: 'Import failed',
183 action: {
184 text: 'Review imports',
185 url: importUrl
186 }
187 }
188 }
189
190 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
191 }
192
193 addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) {
194 const video = comment.Video
195 const videoUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath()
196 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
197 const commentHtml = toSafeHtml(comment.text)
198
199 const emailPayload: EmailPayload = {
200 template: 'video-comment-new',
201 to,
202 subject: 'New comment on your video ' + video.name,
203 locals: {
204 accountName: comment.Account.getDisplayName(),
205 accountUrl: comment.Account.Actor.url,
206 comment,
207 commentHtml,
208 video,
209 videoUrl,
210 action: {
211 text: 'View comment',
212 url: commentUrl
213 }
214 }
215 }
216
217 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
218 }
219
220 addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) {
221 const accountName = comment.Account.getDisplayName()
222 const video = comment.Video
223 const videoUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath()
224 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
225 const commentHtml = toSafeHtml(comment.text)
226
227 const emailPayload: EmailPayload = {
228 template: 'video-comment-mention',
229 to,
230 subject: 'Mention on video ' + video.name,
231 locals: {
232 comment,
233 commentHtml,
234 video,
235 videoUrl,
236 accountName,
237 action: {
238 text: 'View comment',
239 url: commentUrl
240 }
241 }
242 }
243
244 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
245 }
246
247 addAbuseModeratorsNotification (to: string[], parameters: {
248 abuse: UserAbuse
249 abuseInstance: MAbuseFull
250 reporter: string
251 }) {
252 const { abuse, abuseInstance, reporter } = parameters
253
254 const action = {
255 text: 'View report #' + abuse.id,
256 url: WEBSERVER.URL + '/admin/moderation/abuses/list?search=%23' + abuse.id
257 }
258
259 let emailPayload: EmailPayload
260
261 if (abuseInstance.VideoAbuse) {
262 const video = abuseInstance.VideoAbuse.Video
263 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
264
265 emailPayload = {
266 template: 'video-abuse-new',
267 to,
268 subject: `New video abuse report from ${reporter}`,
269 locals: {
270 videoUrl,
271 isLocal: video.remote === false,
272 videoCreatedAt: new Date(video.createdAt).toLocaleString(),
273 videoPublishedAt: new Date(video.publishedAt).toLocaleString(),
274 videoName: video.name,
275 reason: abuse.reason,
276 videoChannel: abuse.video.channel,
277 reporter,
278 action
279 }
280 }
281 } else if (abuseInstance.VideoCommentAbuse) {
282 const comment = abuseInstance.VideoCommentAbuse.VideoComment
283 const commentUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath() + ';threadId=' + comment.getThreadId()
284
285 emailPayload = {
286 template: 'video-comment-abuse-new',
287 to,
288 subject: `New comment abuse report from ${reporter}`,
289 locals: {
290 commentUrl,
291 videoName: comment.Video.name,
292 isLocal: comment.isOwned(),
293 commentCreatedAt: new Date(comment.createdAt).toLocaleString(),
294 reason: abuse.reason,
295 flaggedAccount: abuseInstance.FlaggedAccount.getDisplayName(),
296 reporter,
297 action
298 }
299 }
300 } else {
301 const account = abuseInstance.FlaggedAccount
302 const accountUrl = account.getClientUrl()
303
304 emailPayload = {
305 template: 'account-abuse-new',
306 to,
307 subject: `New account abuse report from ${reporter}`,
308 locals: {
309 accountUrl,
310 accountDisplayName: account.getDisplayName(),
311 isLocal: account.isOwned(),
312 reason: abuse.reason,
313 reporter,
314 action
315 }
316 }
317 }
318
319 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
320 }
321
322 addAbuseStateChangeNotification (to: string[], abuse: MAbuseFull) {
323 const text = abuse.state === AbuseState.ACCEPTED
324 ? 'Report #' + abuse.id + ' has been accepted'
325 : 'Report #' + abuse.id + ' has been rejected'
326
327 const abuseUrl = WEBSERVER.URL + '/my-account/abuses?search=%23' + abuse.id
328
329 const action = {
330 text,
331 url: abuseUrl
332 }
333
334 const emailPayload: EmailPayload = {
335 template: 'abuse-state-change',
336 to,
337 subject: text,
338 locals: {
339 action,
340 abuseId: abuse.id,
341 abuseUrl,
342 isAccepted: abuse.state === AbuseState.ACCEPTED
343 }
344 }
345
346 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
347 }
348
349 addAbuseNewMessageNotification (
350 to: string[],
351 options: {
352 target: 'moderator' | 'reporter'
353 abuse: MAbuseFull
354 message: MAbuseMessage
355 accountMessage: MAccountDefault
356 }) {
357 const { abuse, target, message, accountMessage } = options
358
359 const text = 'New message on report #' + abuse.id
360 const abuseUrl = target === 'moderator'
361 ? WEBSERVER.URL + '/admin/moderation/abuses/list?search=%23' + abuse.id
362 : WEBSERVER.URL + '/my-account/abuses?search=%23' + abuse.id
363
364 const action = {
365 text,
366 url: abuseUrl
367 }
368
369 const emailPayload: EmailPayload = {
370 template: 'abuse-new-message',
371 to,
372 subject: text,
373 locals: {
374 abuseId: abuse.id,
375 abuseUrl: action.url,
376 messageAccountName: accountMessage.getDisplayName(),
377 messageText: message.message,
378 action
379 }
380 }
381
382 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
383 }
384
385 async addVideoAutoBlacklistModeratorsNotification (to: string[], videoBlacklist: MVideoBlacklistLightVideo) {
386 const videoAutoBlacklistUrl = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
387 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
388 const channel = (await VideoChannelModel.loadAndPopulateAccount(videoBlacklist.Video.channelId)).toFormattedSummaryJSON()
389
390 const emailPayload: EmailPayload = {
391 template: 'video-auto-blacklist-new',
392 to,
393 subject: 'A new video is pending moderation',
394 locals: {
395 channel,
396 videoUrl,
397 videoName: videoBlacklist.Video.name,
398 action: {
399 text: 'Review autoblacklist',
400 url: videoAutoBlacklistUrl
401 }
402 }
403 }
404
405 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
406 }
407
408 addNewUserRegistrationNotification (to: string[], user: MUser) {
409 const emailPayload: EmailPayload = {
410 template: 'user-registered',
411 to,
412 subject: `a new user registered on ${CONFIG.INSTANCE.NAME}: ${user.username}`,
413 locals: {
414 user
415 }
416 }
417
418 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
419 }
420
421 addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) {
422 const videoName = videoBlacklist.Video.name
423 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
424
425 const reasonString = videoBlacklist.reason ? ` for the following reason: ${videoBlacklist.reason}` : ''
426 const blockedString = `Your video ${videoName} (${videoUrl} on ${CONFIG.INSTANCE.NAME} has been blacklisted${reasonString}.`
427
428 const emailPayload: EmailPayload = {
429 to,
430 subject: `Video ${videoName} blacklisted`,
431 text: blockedString,
432 locals: {
433 title: 'Your video was blacklisted'
434 }
435 }
436
437 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
438 }
439
440 addVideoUnblacklistNotification (to: string[], video: MVideo) {
441 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
442
443 const emailPayload: EmailPayload = {
444 to,
445 subject: `Video ${video.name} unblacklisted`,
446 text: `Your video "${video.name}" (${videoUrl}) on ${CONFIG.INSTANCE.NAME} has been unblacklisted.`,
447 locals: {
448 title: 'Your video was unblacklisted'
449 }
450 }
451
452 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
453 }
454
455 addNewPeerTubeVersionNotification (to: string[], latestVersion: string) {
456 const emailPayload: EmailPayload = {
457 to,
458 template: 'peertube-version-new',
459 subject: `A new PeerTube version is available: ${latestVersion}`,
460 locals: {
461 latestVersion
462 }
463 }
464
465 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
466 }
467
468 addNewPlugionVersionNotification (to: string[], plugin: MPlugin) {
469 const pluginUrl = WEBSERVER.URL + '/admin/plugins/list-installed?pluginType=' + plugin.type
470
471 const emailPayload: EmailPayload = {
472 to,
473 template: 'plugin-version-new',
474 subject: `A new plugin/theme version is available: ${plugin.name}@${plugin.latestVersion}`,
475 locals: {
476 pluginName: plugin.name,
477 latestVersion: plugin.latestVersion,
478 pluginUrl
479 }
480 }
481
482 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
483 }
484
485 addPasswordResetEmailJob (username: string, to: string, resetPasswordUrl: string) { 57 addPasswordResetEmailJob (username: string, to: string, resetPasswordUrl: string) {
486 const emailPayload: EmailPayload = { 58 const emailPayload: EmailPayload = {
487 template: 'password-reset', 59 template: 'password-reset',
@@ -578,7 +150,11 @@ class Emailer {
578 subjectPrefix: CONFIG.EMAIL.SUBJECT.PREFIX 150 subjectPrefix: CONFIG.EMAIL.SUBJECT.PREFIX
579 }) 151 })
580 152
581 for (const to of options.to) { 153 const toEmails = isArray(options.to)
154 ? options.to
155 : [ options.to ]
156
157 for (const to of toEmails) {
582 const baseOptions: SendEmailDefaultOptions = { 158 const baseOptions: SendEmailDefaultOptions = {
583 template: 'common', 159 template: 'common',
584 message: { 160 message: {
diff --git a/server/lib/hls.ts b/server/lib/hls.ts
index 05be403f3..32b02bc26 100644
--- a/server/lib/hls.ts
+++ b/server/lib/hls.ts
@@ -1,7 +1,7 @@
1import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' 1import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra'
2import { flatten, uniq } from 'lodash' 2import { flatten, uniq } from 'lodash'
3import { basename, dirname, join } from 'path' 3import { basename, dirname, join } from 'path'
4import { MVideoWithFile } from '@server/types/models' 4import { MStreamingPlaylistFilesVideo, MVideoWithFile } from '@server/types/models'
5import { sha256 } from '../helpers/core-utils' 5import { sha256 } from '../helpers/core-utils'
6import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils' 6import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils'
7import { logger } from '../helpers/logger' 7import { logger } from '../helpers/logger'
@@ -12,7 +12,7 @@ import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from
12import { sequelizeTypescript } from '../initializers/database' 12import { sequelizeTypescript } from '../initializers/database'
13import { VideoFileModel } from '../models/video/video-file' 13import { VideoFileModel } from '../models/video/video-file'
14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
15import { getVideoFilePath } from './video-paths' 15import { getHlsResolutionPlaylistFilename, getVideoFilePath } from './video-paths'
16 16
17async function updateStreamingPlaylistsInfohashesIfNeeded () { 17async function updateStreamingPlaylistsInfohashesIfNeeded () {
18 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() 18 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion()
@@ -22,25 +22,29 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () {
22 await sequelizeTypescript.transaction(async t => { 22 await sequelizeTypescript.transaction(async t => {
23 const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlist.id, t) 23 const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlist.id, t)
24 24
25 playlist.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlist.playlistUrl, videoFiles) 25 playlist.assignP2PMediaLoaderInfoHashes(playlist.Video, videoFiles)
26 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION 26 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
27
27 await playlist.save({ transaction: t }) 28 await playlist.save({ transaction: t })
28 }) 29 })
29 } 30 }
30} 31}
31 32
32async function updateMasterHLSPlaylist (video: MVideoWithFile) { 33async function updateMasterHLSPlaylist (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) {
33 const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 34 const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
35
34 const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] 36 const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ]
35 const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
36 const streamingPlaylist = video.getHLSPlaylist()
37 37
38 for (const file of streamingPlaylist.VideoFiles) { 38 const masterPlaylistPath = join(directory, playlist.playlistFilename)
39
40 for (const file of playlist.VideoFiles) {
41 const playlistFilename = getHlsResolutionPlaylistFilename(file.filename)
42
39 // If we did not generated a playlist for this resolution, skip 43 // If we did not generated a playlist for this resolution, skip
40 const filePlaylistPath = join(directory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution)) 44 const filePlaylistPath = join(directory, playlistFilename)
41 if (await pathExists(filePlaylistPath) === false) continue 45 if (await pathExists(filePlaylistPath) === false) continue
42 46
43 const videoFilePath = getVideoFilePath(streamingPlaylist, file) 47 const videoFilePath = getVideoFilePath(playlist, file)
44 48
45 const size = await getVideoStreamSize(videoFilePath) 49 const size = await getVideoStreamSize(videoFilePath)
46 50
@@ -58,29 +62,28 @@ async function updateMasterHLSPlaylist (video: MVideoWithFile) {
58 line += `,CODECS="${codecs.filter(c => !!c).join(',')}"` 62 line += `,CODECS="${codecs.filter(c => !!c).join(',')}"`
59 63
60 masterPlaylists.push(line) 64 masterPlaylists.push(line)
61 masterPlaylists.push(VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution)) 65 masterPlaylists.push(playlistFilename)
62 } 66 }
63 67
64 await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') 68 await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n')
65} 69}
66 70
67async function updateSha256VODSegments (video: MVideoWithFile) { 71async function updateSha256VODSegments (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) {
68 const json: { [filename: string]: { [range: string]: string } } = {} 72 const json: { [filename: string]: { [range: string]: string } } = {}
69 73
70 const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 74 const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
71 const hlsPlaylist = video.getHLSPlaylist()
72 75
73 // For all the resolutions available for this video 76 // For all the resolutions available for this video
74 for (const file of hlsPlaylist.VideoFiles) { 77 for (const file of playlist.VideoFiles) {
75 const rangeHashes: { [range: string]: string } = {} 78 const rangeHashes: { [range: string]: string } = {}
76 79
77 const videoPath = getVideoFilePath(hlsPlaylist, file) 80 const videoPath = getVideoFilePath(playlist, file)
78 const playlistPath = join(playlistDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution)) 81 const resolutionPlaylistPath = join(playlistDirectory, getHlsResolutionPlaylistFilename(file.filename))
79 82
80 // Maybe the playlist is not generated for this resolution yet 83 // Maybe the playlist is not generated for this resolution yet
81 if (!await pathExists(playlistPath)) continue 84 if (!await pathExists(resolutionPlaylistPath)) continue
82 85
83 const playlistContent = await readFile(playlistPath) 86 const playlistContent = await readFile(resolutionPlaylistPath)
84 const ranges = getRangesFromPlaylist(playlistContent.toString()) 87 const ranges = getRangesFromPlaylist(playlistContent.toString())
85 88
86 const fd = await open(videoPath, 'r') 89 const fd = await open(videoPath, 'r')
@@ -96,7 +99,7 @@ async function updateSha256VODSegments (video: MVideoWithFile) {
96 json[videoFilename] = rangeHashes 99 json[videoFilename] = rangeHashes
97 } 100 }
98 101
99 const outputPath = join(playlistDirectory, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) 102 const outputPath = join(playlistDirectory, playlist.segmentsSha256Filename)
100 await outputJSON(outputPath, json) 103 await outputJSON(outputPath, json)
101} 104}
102 105
diff --git a/server/lib/job-queue/handlers/activitypub-cleaner.ts b/server/lib/job-queue/handlers/activitypub-cleaner.ts
index 1caca1dcc..56e2b0ceb 100644
--- a/server/lib/job-queue/handlers/activitypub-cleaner.ts
+++ b/server/lib/job-queue/handlers/activitypub-cleaner.ts
@@ -12,7 +12,7 @@ import { AP_CLEANER_CONCURRENCY } from '@server/initializers/constants'
12import { VideoModel } from '@server/models/video/video' 12import { VideoModel } from '@server/models/video/video'
13import { VideoCommentModel } from '@server/models/video/video-comment' 13import { VideoCommentModel } from '@server/models/video/video-comment'
14import { VideoShareModel } from '@server/models/video/video-share' 14import { VideoShareModel } from '@server/models/video/video-share'
15import { HttpStatusCode } from '@shared/core-utils' 15import { HttpStatusCode } from '@shared/models'
16import { logger } from '../../../helpers/logger' 16import { logger } from '../../../helpers/logger'
17import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 17import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
18 18
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts
index 187cb652e..4d199f247 100644
--- a/server/lib/job-queue/handlers/video-file-import.ts
+++ b/server/lib/job-queue/handlers/video-file-import.ts
@@ -2,7 +2,7 @@ import * as Bull from 'bull'
2import { copy, stat } from 'fs-extra' 2import { copy, stat } from 'fs-extra'
3import { getLowercaseExtension } from '@server/helpers/core-utils' 3import { getLowercaseExtension } from '@server/helpers/core-utils'
4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
5import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 5import { generateWebTorrentVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
6import { UserModel } from '@server/models/user/user' 6import { UserModel } from '@server/models/user/user'
7import { MVideoFullLight } from '@server/types/models' 7import { MVideoFullLight } from '@server/types/models'
8import { VideoFileImportPayload } from '@shared/models' 8import { VideoFileImportPayload } from '@shared/models'
@@ -61,8 +61,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) {
61 61
62 if (currentVideoFile) { 62 if (currentVideoFile) {
63 // Remove old file and old torrent 63 // Remove old file and old torrent
64 await video.removeFile(currentVideoFile) 64 await video.removeFileAndTorrent(currentVideoFile)
65 await currentVideoFile.removeTorrent()
66 // Remove the old video file from the array 65 // Remove the old video file from the array
67 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) 66 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
68 67
@@ -72,7 +71,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) {
72 const newVideoFile = new VideoFileModel({ 71 const newVideoFile = new VideoFileModel({
73 resolution: videoFileResolution, 72 resolution: videoFileResolution,
74 extname: fileExt, 73 extname: fileExt,
75 filename: generateVideoFilename(video, false, videoFileResolution, fileExt), 74 filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt),
76 size, 75 size,
77 fps, 76 fps,
78 videoId: video.id 77 videoId: video.id
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 55498003d..5fd2039b1 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -8,7 +8,7 @@ import { Hooks } from '@server/lib/plugins/hooks'
8import { ServerConfigManager } from '@server/lib/server-config-manager' 8import { ServerConfigManager } from '@server/lib/server-config-manager'
9import { isAbleToUploadVideo } from '@server/lib/user' 9import { isAbleToUploadVideo } from '@server/lib/user'
10import { addOptimizeOrMergeAudioJob } from '@server/lib/video' 10import { addOptimizeOrMergeAudioJob } from '@server/lib/video'
11import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 11import { generateWebTorrentVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
12import { ThumbnailModel } from '@server/models/video/thumbnail' 12import { ThumbnailModel } from '@server/models/video/thumbnail'
13import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/types/models/video/video-import' 13import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/types/models/video/video-import'
14import { 14import {
@@ -124,7 +124,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
124 extname: fileExt, 124 extname: fileExt,
125 resolution: videoFileResolution, 125 resolution: videoFileResolution,
126 size: stats.size, 126 size: stats.size,
127 filename: generateVideoFilename(videoImport.Video, false, videoFileResolution, fileExt), 127 filename: generateWebTorrentVideoFilename(videoFileResolution, fileExt),
128 fps, 128 fps,
129 videoId: videoImport.videoId 129 videoId: videoImport.videoId
130 } 130 }
@@ -235,7 +235,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
235 }) 235 })
236 }) 236 })
237 237
238 Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) 238 Notifier.Instance.notifyOnFinishedVideoImport({ videoImport: videoImportUpdated, success: true })
239 239
240 if (video.isBlacklisted()) { 240 if (video.isBlacklisted()) {
241 const videoBlacklist = Object.assign(video.VideoBlacklist, { Video: video }) 241 const videoBlacklist = Object.assign(video.VideoBlacklist, { Video: video })
@@ -263,7 +263,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
263 } 263 }
264 await videoImport.save() 264 await videoImport.save()
265 265
266 Notifier.Instance.notifyOnFinishedVideoImport(videoImport, false) 266 Notifier.Instance.notifyOnFinishedVideoImport({ videoImport, success: false })
267 267
268 throw err 268 throw err
269 } 269 }
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts
index 9eba41bf8..386ccdc7b 100644
--- a/server/lib/job-queue/handlers/video-live-ending.ts
+++ b/server/lib/job-queue/handlers/video-live-ending.ts
@@ -7,12 +7,12 @@ import { buildConcatenatedName, cleanupLive, LiveSegmentShaStore } from '@server
7import { generateVideoMiniature } from '@server/lib/thumbnail' 7import { generateVideoMiniature } from '@server/lib/thumbnail'
8import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding' 8import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding'
9import { publishAndFederateIfNeeded } from '@server/lib/video' 9import { publishAndFederateIfNeeded } from '@server/lib/video'
10import { getHLSDirectory } from '@server/lib/video-paths' 10import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getHLSDirectory } from '@server/lib/video-paths'
11import { VideoModel } from '@server/models/video/video' 11import { VideoModel } from '@server/models/video/video'
12import { VideoFileModel } from '@server/models/video/video-file' 12import { VideoFileModel } from '@server/models/video/video-file'
13import { VideoLiveModel } from '@server/models/video/video-live' 13import { VideoLiveModel } from '@server/models/video/video-live'
14import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 14import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
15import { MVideo, MVideoLive } from '@server/types/models' 15import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models'
16import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' 16import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models'
17import { logger } from '../../../helpers/logger' 17import { logger } from '../../../helpers/logger'
18 18
@@ -43,7 +43,7 @@ async function processVideoLiveEnding (job: Bull.Job) {
43 return cleanupLive(video, streamingPlaylist) 43 return cleanupLive(video, streamingPlaylist)
44 } 44 }
45 45
46 return saveLive(video, live) 46 return saveLive(video, live, streamingPlaylist)
47} 47}
48 48
49// --------------------------------------------------------------------------- 49// ---------------------------------------------------------------------------
@@ -54,14 +54,14 @@ export {
54 54
55// --------------------------------------------------------------------------- 55// ---------------------------------------------------------------------------
56 56
57async function saveLive (video: MVideo, live: MVideoLive) { 57async function saveLive (video: MVideo, live: MVideoLive, streamingPlaylist: MStreamingPlaylist) {
58 const hlsDirectory = getHLSDirectory(video, false) 58 const hlsDirectory = getHLSDirectory(video, false)
59 const replayDirectory = join(hlsDirectory, VIDEO_LIVE.REPLAY_DIRECTORY) 59 const replayDirectory = join(hlsDirectory, VIDEO_LIVE.REPLAY_DIRECTORY)
60 60
61 const rootFiles = await readdir(hlsDirectory) 61 const rootFiles = await readdir(hlsDirectory)
62 62
63 const playlistFiles = rootFiles.filter(file => { 63 const playlistFiles = rootFiles.filter(file => {
64 return file.endsWith('.m3u8') && file !== 'master.m3u8' 64 return file.endsWith('.m3u8') && file !== streamingPlaylist.playlistFilename
65 }) 65 })
66 66
67 await cleanupLiveFiles(hlsDirectory) 67 await cleanupLiveFiles(hlsDirectory)
@@ -80,7 +80,12 @@ async function saveLive (video: MVideo, live: MVideoLive) {
80 80
81 const hlsPlaylist = videoWithFiles.getHLSPlaylist() 81 const hlsPlaylist = videoWithFiles.getHLSPlaylist()
82 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) 82 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id)
83
84 // Reset playlist
83 hlsPlaylist.VideoFiles = [] 85 hlsPlaylist.VideoFiles = []
86 hlsPlaylist.playlistFilename = generateHLSMasterPlaylistFilename()
87 hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename()
88 await hlsPlaylist.save()
84 89
85 let durationDone = false 90 let durationDone = false
86 91
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index f5ba6f435..2abb351ce 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -28,21 +28,10 @@ import { JobQueue } from '../job-queue'
28 28
29type HandlerFunction = (job: Bull.Job, payload: VideoTranscodingPayload, video: MVideoFullLight, user: MUser) => Promise<any> 29type HandlerFunction = (job: Bull.Job, payload: VideoTranscodingPayload, video: MVideoFullLight, user: MUser) => Promise<any>
30 30
31const handlers: { [ id: string ]: HandlerFunction } = { 31const handlers: { [ id in VideoTranscodingPayload['type'] ]: HandlerFunction } = {
32 // Deprecated, introduced in 3.1
33 'hls': handleHLSJob,
34 'new-resolution-to-hls': handleHLSJob, 32 'new-resolution-to-hls': handleHLSJob,
35
36 // Deprecated, introduced in 3.1
37 'new-resolution': handleNewWebTorrentResolutionJob,
38 'new-resolution-to-webtorrent': handleNewWebTorrentResolutionJob, 33 'new-resolution-to-webtorrent': handleNewWebTorrentResolutionJob,
39
40 // Deprecated, introduced in 3.1
41 'merge-audio': handleWebTorrentMergeAudioJob,
42 'merge-audio-to-webtorrent': handleWebTorrentMergeAudioJob, 34 'merge-audio-to-webtorrent': handleWebTorrentMergeAudioJob,
43
44 // Deprecated, introduced in 3.1
45 'optimize': handleWebTorrentOptimizeJob,
46 'optimize-to-webtorrent': handleWebTorrentOptimizeJob 35 'optimize-to-webtorrent': handleWebTorrentOptimizeJob
47} 36}
48 37
@@ -125,8 +114,7 @@ async function onHlsPlaylistGeneration (video: MVideoFullLight, user: MUser, pay
125 if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) { 114 if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) {
126 // Remove webtorrent files if not enabled 115 // Remove webtorrent files if not enabled
127 for (const file of video.VideoFiles) { 116 for (const file of video.VideoFiles) {
128 await video.removeFile(file) 117 await video.removeFileAndTorrent(file)
129 await file.removeTorrent()
130 await file.destroy() 118 await file.destroy()
131 } 119 }
132 120
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts
index 014cd3fcf..f106d69fb 100644
--- a/server/lib/live/live-manager.ts
+++ b/server/lib/live/live-manager.ts
@@ -4,24 +4,25 @@ import { isTestInstance } from '@server/helpers/core-utils'
4import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils' 4import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils'
5import { logger, loggerTagsFactory } from '@server/helpers/logger' 5import { logger, loggerTagsFactory } from '@server/helpers/logger'
6import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' 6import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config'
7import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME, WEBSERVER } from '@server/initializers/constants' 7import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME } from '@server/initializers/constants'
8import { UserModel } from '@server/models/user/user' 8import { UserModel } from '@server/models/user/user'
9import { VideoModel } from '@server/models/video/video' 9import { VideoModel } from '@server/models/video/video'
10import { VideoLiveModel } from '@server/models/video/video-live' 10import { VideoLiveModel } from '@server/models/video/video-live'
11import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 11import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
12import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models' 12import { MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models'
13import { VideoState, VideoStreamingPlaylistType } from '@shared/models' 13import { VideoState, VideoStreamingPlaylistType } from '@shared/models'
14import { federateVideoIfNeeded } from '../activitypub/videos' 14import { federateVideoIfNeeded } from '../activitypub/videos'
15import { JobQueue } from '../job-queue' 15import { JobQueue } from '../job-queue'
16import { PeerTubeSocket } from '../peertube-socket' 16import { PeerTubeSocket } from '../peertube-socket'
17import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '../video-paths'
17import { LiveQuotaStore } from './live-quota-store' 18import { LiveQuotaStore } from './live-quota-store'
18import { LiveSegmentShaStore } from './live-segment-sha-store' 19import { LiveSegmentShaStore } from './live-segment-sha-store'
19import { cleanupLive } from './live-utils' 20import { cleanupLive } from './live-utils'
20import { MuxingSession } from './shared' 21import { MuxingSession } from './shared'
21 22
22const NodeRtmpSession = require('node-media-server/node_rtmp_session') 23const NodeRtmpSession = require('node-media-server/src/node_rtmp_session')
23const context = require('node-media-server/node_core_ctx') 24const context = require('node-media-server/src/node_core_ctx')
24const nodeMediaServerLogger = require('node-media-server/node_core_logger') 25const nodeMediaServerLogger = require('node-media-server/src/node_core_logger')
25 26
26// Disable node media server logs 27// Disable node media server logs
27nodeMediaServerLogger.setLogType(0) 28nodeMediaServerLogger.setLogType(0)
@@ -392,19 +393,18 @@ class LiveManager {
392 return resolutionsEnabled.concat([ originResolution ]) 393 return resolutionsEnabled.concat([ originResolution ])
393 } 394 }
394 395
395 private async createLivePlaylist (video: MVideo, allResolutions: number[]) { 396 private async createLivePlaylist (video: MVideo, allResolutions: number[]): Promise<MStreamingPlaylistVideo> {
396 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid) 397 const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video)
397 const [ videoStreamingPlaylist ] = await VideoStreamingPlaylistModel.upsert({
398 videoId: video.id,
399 playlistUrl,
400 segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid, video.isLive),
401 p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrl, allResolutions),
402 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION,
403 398
404 type: VideoStreamingPlaylistType.HLS 399 playlist.playlistFilename = generateHLSMasterPlaylistFilename(true)
405 }, { returning: true }) as [ MStreamingPlaylist, boolean ] 400 playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(true)
406 401
407 return Object.assign(videoStreamingPlaylist, { Video: video }) 402 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
403 playlist.type = VideoStreamingPlaylistType.HLS
404
405 playlist.assignP2PMediaLoaderInfoHashes(video, allResolutions)
406
407 return playlist.save()
408 } 408 }
409 409
410 static get Instance () { 410 static get Instance () {
diff --git a/server/lib/live/shared/muxing-session.ts b/server/lib/live/shared/muxing-session.ts
index 26467f060..709d6c615 100644
--- a/server/lib/live/shared/muxing-session.ts
+++ b/server/lib/live/shared/muxing-session.ts
@@ -112,13 +112,16 @@ class MuxingSession extends EventEmitter {
112 this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED 112 this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED
113 ? await getLiveTranscodingCommand({ 113 ? await getLiveTranscodingCommand({
114 rtmpUrl: this.rtmpUrl, 114 rtmpUrl: this.rtmpUrl,
115
115 outPath, 116 outPath,
117 masterPlaylistName: this.streamingPlaylist.playlistFilename,
118
116 resolutions: this.allResolutions, 119 resolutions: this.allResolutions,
117 fps: this.fps, 120 fps: this.fps,
118 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), 121 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
119 profile: CONFIG.LIVE.TRANSCODING.PROFILE 122 profile: CONFIG.LIVE.TRANSCODING.PROFILE
120 }) 123 })
121 : getLiveMuxingCommand(this.rtmpUrl, outPath) 124 : getLiveMuxingCommand(this.rtmpUrl, outPath, this.streamingPlaylist.playlistFilename)
122 125
123 logger.info('Running live muxing/transcoding for %s.', this.videoUUID, this.lTags) 126 logger.info('Running live muxing/transcoding for %s.', this.videoUUID, this.lTags)
124 127
@@ -182,7 +185,7 @@ class MuxingSession extends EventEmitter {
182 } 185 }
183 186
184 private watchMasterFile (outPath: string) { 187 private watchMasterFile (outPath: string) {
185 this.masterWatcher = chokidar.watch(outPath + '/master.m3u8') 188 this.masterWatcher = chokidar.watch(outPath + '/' + this.streamingPlaylist.playlistFilename)
186 189
187 this.masterWatcher.on('add', async () => { 190 this.masterWatcher.on('add', async () => {
188 this.emit('master-playlist-created', { videoId: this.videoId }) 191 this.emit('master-playlist-created', { videoId: this.videoId })
diff --git a/server/lib/moderation.ts b/server/lib/moderation.ts
index 14e00518e..a42ab5b7f 100644
--- a/server/lib/moderation.ts
+++ b/server/lib/moderation.ts
@@ -23,7 +23,7 @@ import { ActivityCreate } from '../../shared/models/activitypub'
23import { VideoObject } from '../../shared/models/activitypub/objects' 23import { VideoObject } from '../../shared/models/activitypub/objects'
24import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object' 24import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object'
25import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos' 25import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos'
26import { VideoCommentCreate } from '../../shared/models/videos/comment/video-comment.model' 26import { VideoCommentCreate } from '../../shared/models/videos/comment'
27import { ActorModel } from '../models/actor/actor' 27import { ActorModel } from '../models/actor/actor'
28import { UserModel } from '../models/user/user' 28import { UserModel } from '../models/user/user'
29import { VideoModel } from '../models/video/video' 29import { VideoModel } from '../models/video/video'
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts
deleted file mode 100644
index 1f9ff16df..000000000
--- a/server/lib/notifier.ts
+++ /dev/null
@@ -1,796 +0,0 @@
1import { AccountModel } from '@server/models/account/account'
2import { getServerActor } from '@server/models/application/application'
3import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
4import {
5 MUser,
6 MUserAccount,
7 MUserDefault,
8 MUserNotifSettingAccount,
9 MUserWithNotificationSetting,
10 UserNotificationModelForApi
11} from '@server/types/models/user'
12import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
13import { MVideoImportVideo } from '@server/types/models/video/video-import'
14import { UserAbuse } from '@shared/models'
15import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users'
16import { VideoPrivacy, VideoState } from '../../shared/models/videos'
17import { logger } from '../helpers/logger'
18import { CONFIG } from '../initializers/config'
19import { AccountBlocklistModel } from '../models/account/account-blocklist'
20import { UserModel } from '../models/user/user'
21import { UserNotificationModel } from '../models/user/user-notification'
22import { MAbuseFull, MAbuseMessage, MAccountServer, MActorFollowFull, MApplication, MPlugin } from '../types/models'
23import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video'
24import { isBlockedByServerOrAccount } from './blocklist'
25import { Emailer } from './emailer'
26import { PeerTubeSocket } from './peertube-socket'
27
28class Notifier {
29
30 private static instance: Notifier
31
32 private constructor () {
33 }
34
35 notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void {
36 // Only notify on public and published videos which are not blacklisted
37 if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return
38
39 this.notifySubscribersOfNewVideo(video)
40 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
41 }
42
43 notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void {
44 // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update
45 if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return
46
47 this.notifyOwnedVideoHasBeenPublished(video)
48 .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err }))
49 }
50
51 notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void {
52 // don't notify if video is still blacklisted or waiting for transcoding
53 if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
54
55 this.notifyOwnedVideoHasBeenPublished(video)
56 .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err }))
57 }
58
59 notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void {
60 // don't notify if video is still waiting for transcoding or scheduled update
61 if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
62
63 this.notifyOwnedVideoHasBeenPublished(video)
64 .catch(err => {
65 logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })
66 })
67 }
68
69 notifyOnNewComment (comment: MCommentOwnerVideo): void {
70 this.notifyVideoOwnerOfNewComment(comment)
71 .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err }))
72
73 this.notifyOfCommentMention(comment)
74 .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err }))
75 }
76
77 notifyOnNewAbuse (parameters: { abuse: UserAbuse, abuseInstance: MAbuseFull, reporter: string }): void {
78 this.notifyModeratorsOfNewAbuse(parameters)
79 .catch(err => logger.error('Cannot notify of new abuse %d.', parameters.abuseInstance.id, { err }))
80 }
81
82 notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void {
83 this.notifyModeratorsOfVideoAutoBlacklist(videoBlacklist)
84 .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err }))
85 }
86
87 notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void {
88 this.notifyVideoOwnerOfBlacklist(videoBlacklist)
89 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
90 }
91
92 notifyOnVideoUnblacklist (video: MVideoFullLight): void {
93 this.notifyVideoOwnerOfUnblacklist(video)
94 .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
95 }
96
97 notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void {
98 this.notifyOwnerVideoImportIsFinished(videoImport, success)
99 .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err }))
100 }
101
102 notifyOnNewUserRegistration (user: MUserDefault): void {
103 this.notifyModeratorsOfNewUserRegistration(user)
104 .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err }))
105 }
106
107 notifyOfNewUserFollow (actorFollow: MActorFollowFull): void {
108 this.notifyUserOfNewActorFollow(actorFollow)
109 .catch(err => {
110 logger.error(
111 'Cannot notify owner of channel %s of a new follow by %s.',
112 actorFollow.ActorFollowing.VideoChannel.getDisplayName(),
113 actorFollow.ActorFollower.Account.getDisplayName(),
114 { err }
115 )
116 })
117 }
118
119 notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void {
120 this.notifyAdminsOfNewInstanceFollow(actorFollow)
121 .catch(err => {
122 logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err })
123 })
124 }
125
126 notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void {
127 this.notifyAdminsOfAutoInstanceFollowing(actorFollow)
128 .catch(err => {
129 logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err })
130 })
131 }
132
133 notifyOnAbuseStateChange (abuse: MAbuseFull): void {
134 this.notifyReporterOfAbuseStateChange(abuse)
135 .catch(err => {
136 logger.error('Cannot notify reporter of abuse %d state change.', abuse.id, { err })
137 })
138 }
139
140 notifyOnAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage): void {
141 this.notifyOfNewAbuseMessage(abuse, message)
142 .catch(err => {
143 logger.error('Cannot notify on new abuse %d message.', abuse.id, { err })
144 })
145 }
146
147 notifyOfNewPeerTubeVersion (application: MApplication, latestVersion: string) {
148 this.notifyAdminsOfNewPeerTubeVersion(application, latestVersion)
149 .catch(err => {
150 logger.error('Cannot notify on new PeerTubeb version %s.', latestVersion, { err })
151 })
152 }
153
154 notifyOfNewPluginVersion (plugin: MPlugin) {
155 this.notifyAdminsOfNewPluginVersion(plugin)
156 .catch(err => {
157 logger.error('Cannot notify on new plugin version %s.', plugin.name, { err })
158 })
159 }
160
161 private async notifySubscribersOfNewVideo (video: MVideoAccountLight) {
162 // List all followers that are users
163 const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
164
165 logger.info('Notifying %d users of new video %s.', users.length, video.url)
166
167 function settingGetter (user: MUserWithNotificationSetting) {
168 return user.NotificationSetting.newVideoFromSubscription
169 }
170
171 async function notificationCreator (user: MUserWithNotificationSetting) {
172 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
173 type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION,
174 userId: user.id,
175 videoId: video.id
176 })
177 notification.Video = video
178
179 return notification
180 }
181
182 function emailSender (emails: string[]) {
183 return Emailer.Instance.addNewVideoFromSubscriberNotification(emails, video)
184 }
185
186 return this.notify({ users, settingGetter, notificationCreator, emailSender })
187 }
188
189 private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) {
190 if (comment.Video.isOwned() === false) return
191
192 const user = await UserModel.loadByVideoId(comment.videoId)
193
194 // Not our user or user comments its own video
195 if (!user || comment.Account.userId === user.id) return
196
197 if (await this.isBlockedByServerOrUser(comment.Account, user)) return
198
199 logger.info('Notifying user %s of new comment %s.', user.username, comment.url)
200
201 function settingGetter (user: MUserWithNotificationSetting) {
202 return user.NotificationSetting.newCommentOnMyVideo
203 }
204
205 async function notificationCreator (user: MUserWithNotificationSetting) {
206 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
207 type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO,
208 userId: user.id,
209 commentId: comment.id
210 })
211 notification.Comment = comment
212
213 return notification
214 }
215
216 function emailSender (emails: string[]) {
217 return Emailer.Instance.addNewCommentOnMyVideoNotification(emails, comment)
218 }
219
220 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
221 }
222
223 private async notifyOfCommentMention (comment: MCommentOwnerVideo) {
224 const extractedUsernames = comment.extractMentions()
225 logger.debug(
226 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url,
227 { usernames: extractedUsernames, text: comment.text }
228 )
229
230 let users = await UserModel.listByUsernames(extractedUsernames)
231
232 if (comment.Video.isOwned()) {
233 const userException = await UserModel.loadByVideoId(comment.videoId)
234 users = users.filter(u => u.id !== userException.id)
235 }
236
237 // Don't notify if I mentioned myself
238 users = users.filter(u => u.Account.id !== comment.accountId)
239
240 if (users.length === 0) return
241
242 const serverAccountId = (await getServerActor()).Account.id
243 const sourceAccounts = users.map(u => u.Account.id).concat([ serverAccountId ])
244
245 const accountMutedHash = await AccountBlocklistModel.isAccountMutedByMulti(sourceAccounts, comment.accountId)
246 const instanceMutedHash = await ServerBlocklistModel.isServerMutedByMulti(sourceAccounts, comment.Account.Actor.serverId)
247
248 logger.info('Notifying %d users of new comment %s.', users.length, comment.url)
249
250 function settingGetter (user: MUserNotifSettingAccount) {
251 const accountId = user.Account.id
252 if (
253 accountMutedHash[accountId] === true || instanceMutedHash[accountId] === true ||
254 accountMutedHash[serverAccountId] === true || instanceMutedHash[serverAccountId] === true
255 ) {
256 return UserNotificationSettingValue.NONE
257 }
258
259 return user.NotificationSetting.commentMention
260 }
261
262 async function notificationCreator (user: MUserNotifSettingAccount) {
263 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
264 type: UserNotificationType.COMMENT_MENTION,
265 userId: user.id,
266 commentId: comment.id
267 })
268 notification.Comment = comment
269
270 return notification
271 }
272
273 function emailSender (emails: string[]) {
274 return Emailer.Instance.addNewCommentMentionNotification(emails, comment)
275 }
276
277 return this.notify({ users, settingGetter, notificationCreator, emailSender })
278 }
279
280 private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) {
281 if (actorFollow.ActorFollowing.isOwned() === false) return
282
283 // Account follows one of our account?
284 let followType: 'account' | 'channel' = 'channel'
285 let user = await UserModel.loadByChannelActorId(actorFollow.ActorFollowing.id)
286
287 // Account follows one of our channel?
288 if (!user) {
289 user = await UserModel.loadByAccountActorId(actorFollow.ActorFollowing.id)
290 followType = 'account'
291 }
292
293 if (!user) return
294
295 const followerAccount = actorFollow.ActorFollower.Account
296 const followerAccountWithActor = Object.assign(followerAccount, { Actor: actorFollow.ActorFollower })
297
298 if (await this.isBlockedByServerOrUser(followerAccountWithActor, user)) return
299
300 logger.info('Notifying user %s of new follower: %s.', user.username, followerAccount.getDisplayName())
301
302 function settingGetter (user: MUserWithNotificationSetting) {
303 return user.NotificationSetting.newFollow
304 }
305
306 async function notificationCreator (user: MUserWithNotificationSetting) {
307 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
308 type: UserNotificationType.NEW_FOLLOW,
309 userId: user.id,
310 actorFollowId: actorFollow.id
311 })
312 notification.ActorFollow = actorFollow
313
314 return notification
315 }
316
317 function emailSender (emails: string[]) {
318 return Emailer.Instance.addNewFollowNotification(emails, actorFollow, followType)
319 }
320
321 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
322 }
323
324 private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowFull) {
325 const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
326
327 const follower = Object.assign(actorFollow.ActorFollower.Account, { Actor: actorFollow.ActorFollower })
328 if (await this.isBlockedByServerOrUser(follower)) return
329
330 logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url)
331
332 function settingGetter (user: MUserWithNotificationSetting) {
333 return user.NotificationSetting.newInstanceFollower
334 }
335
336 async function notificationCreator (user: MUserWithNotificationSetting) {
337 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
338 type: UserNotificationType.NEW_INSTANCE_FOLLOWER,
339 userId: user.id,
340 actorFollowId: actorFollow.id
341 })
342 notification.ActorFollow = actorFollow
343
344 return notification
345 }
346
347 function emailSender (emails: string[]) {
348 return Emailer.Instance.addNewInstanceFollowerNotification(emails, actorFollow)
349 }
350
351 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
352 }
353
354 private async notifyAdminsOfAutoInstanceFollowing (actorFollow: MActorFollowFull) {
355 const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
356
357 logger.info('Notifying %d administrators of auto instance following: %s.', admins.length, actorFollow.ActorFollowing.url)
358
359 function settingGetter (user: MUserWithNotificationSetting) {
360 return user.NotificationSetting.autoInstanceFollowing
361 }
362
363 async function notificationCreator (user: MUserWithNotificationSetting) {
364 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
365 type: UserNotificationType.AUTO_INSTANCE_FOLLOWING,
366 userId: user.id,
367 actorFollowId: actorFollow.id
368 })
369 notification.ActorFollow = actorFollow
370
371 return notification
372 }
373
374 function emailSender (emails: string[]) {
375 return Emailer.Instance.addAutoInstanceFollowingNotification(emails, actorFollow)
376 }
377
378 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
379 }
380
381 private async notifyModeratorsOfNewAbuse (parameters: {
382 abuse: UserAbuse
383 abuseInstance: MAbuseFull
384 reporter: string
385 }) {
386 const { abuse, abuseInstance } = parameters
387
388 const moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES)
389 if (moderators.length === 0) return
390
391 const url = this.getAbuseUrl(abuseInstance)
392
393 logger.info('Notifying %s user/moderators of new abuse %s.', moderators.length, url)
394
395 function settingGetter (user: MUserWithNotificationSetting) {
396 return user.NotificationSetting.abuseAsModerator
397 }
398
399 async function notificationCreator (user: MUserWithNotificationSetting) {
400 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
401 type: UserNotificationType.NEW_ABUSE_FOR_MODERATORS,
402 userId: user.id,
403 abuseId: abuse.id
404 })
405 notification.Abuse = abuseInstance
406
407 return notification
408 }
409
410 function emailSender (emails: string[]) {
411 return Emailer.Instance.addAbuseModeratorsNotification(emails, parameters)
412 }
413
414 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
415 }
416
417 private async notifyReporterOfAbuseStateChange (abuse: MAbuseFull) {
418 // Only notify our users
419 if (abuse.ReporterAccount.isOwned() !== true) return
420
421 const url = this.getAbuseUrl(abuse)
422
423 logger.info('Notifying reporter of abuse % of state change.', url)
424
425 const reporter = await UserModel.loadByAccountActorId(abuse.ReporterAccount.actorId)
426
427 function settingGetter (user: MUserWithNotificationSetting) {
428 return user.NotificationSetting.abuseStateChange
429 }
430
431 async function notificationCreator (user: MUserWithNotificationSetting) {
432 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
433 type: UserNotificationType.ABUSE_STATE_CHANGE,
434 userId: user.id,
435 abuseId: abuse.id
436 })
437 notification.Abuse = abuse
438
439 return notification
440 }
441
442 function emailSender (emails: string[]) {
443 return Emailer.Instance.addAbuseStateChangeNotification(emails, abuse)
444 }
445
446 return this.notify({ users: [ reporter ], settingGetter, notificationCreator, emailSender })
447 }
448
449 private async notifyOfNewAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage) {
450 const url = this.getAbuseUrl(abuse)
451 logger.info('Notifying reporter and moderators of new abuse message on %s.', url)
452
453 const accountMessage = await AccountModel.load(message.accountId)
454
455 function settingGetter (user: MUserWithNotificationSetting) {
456 return user.NotificationSetting.abuseNewMessage
457 }
458
459 async function notificationCreator (user: MUserWithNotificationSetting) {
460 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
461 type: UserNotificationType.ABUSE_NEW_MESSAGE,
462 userId: user.id,
463 abuseId: abuse.id
464 })
465 notification.Abuse = abuse
466
467 return notification
468 }
469
470 function emailSenderReporter (emails: string[]) {
471 return Emailer.Instance.addAbuseNewMessageNotification(emails, { target: 'reporter', abuse, message, accountMessage })
472 }
473
474 function emailSenderModerators (emails: string[]) {
475 return Emailer.Instance.addAbuseNewMessageNotification(emails, { target: 'moderator', abuse, message, accountMessage })
476 }
477
478 async function buildReporterOptions () {
479 // Only notify our users
480 if (abuse.ReporterAccount.isOwned() !== true) return undefined
481
482 const reporter = await UserModel.loadByAccountActorId(abuse.ReporterAccount.actorId)
483 // Don't notify my own message
484 if (reporter.Account.id === message.accountId) return undefined
485
486 return { users: [ reporter ], settingGetter, notificationCreator, emailSender: emailSenderReporter }
487 }
488
489 async function buildModeratorsOptions () {
490 let moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES)
491 // Don't notify my own message
492 moderators = moderators.filter(m => m.Account.id !== message.accountId)
493
494 if (moderators.length === 0) return undefined
495
496 return { users: moderators, settingGetter, notificationCreator, emailSender: emailSenderModerators }
497 }
498
499 const options = await Promise.all([
500 buildReporterOptions(),
501 buildModeratorsOptions()
502 ])
503
504 return Promise.all(
505 options
506 .filter(opt => !!opt)
507 .map(opt => this.notify(opt))
508 )
509 }
510
511 private async notifyModeratorsOfVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo) {
512 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
513 if (moderators.length === 0) return
514
515 logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, videoBlacklist.Video.url)
516
517 function settingGetter (user: MUserWithNotificationSetting) {
518 return user.NotificationSetting.videoAutoBlacklistAsModerator
519 }
520
521 async function notificationCreator (user: MUserWithNotificationSetting) {
522 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
523 type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS,
524 userId: user.id,
525 videoBlacklistId: videoBlacklist.id
526 })
527 notification.VideoBlacklist = videoBlacklist
528
529 return notification
530 }
531
532 function emailSender (emails: string[]) {
533 return Emailer.Instance.addVideoAutoBlacklistModeratorsNotification(emails, videoBlacklist)
534 }
535
536 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
537 }
538
539 private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) {
540 const user = await UserModel.loadByVideoId(videoBlacklist.videoId)
541 if (!user) return
542
543 logger.info('Notifying user %s that its video %s has been blacklisted.', user.username, videoBlacklist.Video.url)
544
545 function settingGetter (user: MUserWithNotificationSetting) {
546 return user.NotificationSetting.blacklistOnMyVideo
547 }
548
549 async function notificationCreator (user: MUserWithNotificationSetting) {
550 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
551 type: UserNotificationType.BLACKLIST_ON_MY_VIDEO,
552 userId: user.id,
553 videoBlacklistId: videoBlacklist.id
554 })
555 notification.VideoBlacklist = videoBlacklist
556
557 return notification
558 }
559
560 function emailSender (emails: string[]) {
561 return Emailer.Instance.addVideoBlacklistNotification(emails, videoBlacklist)
562 }
563
564 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
565 }
566
567 private async notifyVideoOwnerOfUnblacklist (video: MVideoFullLight) {
568 const user = await UserModel.loadByVideoId(video.id)
569 if (!user) return
570
571 logger.info('Notifying user %s that its video %s has been unblacklisted.', user.username, video.url)
572
573 function settingGetter (user: MUserWithNotificationSetting) {
574 return user.NotificationSetting.blacklistOnMyVideo
575 }
576
577 async function notificationCreator (user: MUserWithNotificationSetting) {
578 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
579 type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO,
580 userId: user.id,
581 videoId: video.id
582 })
583 notification.Video = video
584
585 return notification
586 }
587
588 function emailSender (emails: string[]) {
589 return Emailer.Instance.addVideoUnblacklistNotification(emails, video)
590 }
591
592 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
593 }
594
595 private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) {
596 const user = await UserModel.loadByVideoId(video.id)
597 if (!user) return
598
599 logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url)
600
601 function settingGetter (user: MUserWithNotificationSetting) {
602 return user.NotificationSetting.myVideoPublished
603 }
604
605 async function notificationCreator (user: MUserWithNotificationSetting) {
606 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
607 type: UserNotificationType.MY_VIDEO_PUBLISHED,
608 userId: user.id,
609 videoId: video.id
610 })
611 notification.Video = video
612
613 return notification
614 }
615
616 function emailSender (emails: string[]) {
617 return Emailer.Instance.myVideoPublishedNotification(emails, video)
618 }
619
620 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
621 }
622
623 private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) {
624 const user = await UserModel.loadByVideoImportId(videoImport.id)
625 if (!user) return
626
627 logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier())
628
629 function settingGetter (user: MUserWithNotificationSetting) {
630 return user.NotificationSetting.myVideoImportFinished
631 }
632
633 async function notificationCreator (user: MUserWithNotificationSetting) {
634 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
635 type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR,
636 userId: user.id,
637 videoImportId: videoImport.id
638 })
639 notification.VideoImport = videoImport
640
641 return notification
642 }
643
644 function emailSender (emails: string[]) {
645 return success
646 ? Emailer.Instance.myVideoImportSuccessNotification(emails, videoImport)
647 : Emailer.Instance.myVideoImportErrorNotification(emails, videoImport)
648 }
649
650 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
651 }
652
653 private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserDefault) {
654 const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS)
655 if (moderators.length === 0) return
656
657 logger.info(
658 'Notifying %s moderators of new user registration of %s.',
659 moderators.length, registeredUser.username
660 )
661
662 function settingGetter (user: MUserWithNotificationSetting) {
663 return user.NotificationSetting.newUserRegistration
664 }
665
666 async function notificationCreator (user: MUserWithNotificationSetting) {
667 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
668 type: UserNotificationType.NEW_USER_REGISTRATION,
669 userId: user.id,
670 accountId: registeredUser.Account.id
671 })
672 notification.Account = registeredUser.Account
673
674 return notification
675 }
676
677 function emailSender (emails: string[]) {
678 return Emailer.Instance.addNewUserRegistrationNotification(emails, registeredUser)
679 }
680
681 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
682 }
683
684 private async notifyAdminsOfNewPeerTubeVersion (application: MApplication, latestVersion: string) {
685 // Use the debug right to know who is an administrator
686 const admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG)
687 if (admins.length === 0) return
688
689 logger.info('Notifying %s admins of new PeerTube version %s.', admins.length, latestVersion)
690
691 function settingGetter (user: MUserWithNotificationSetting) {
692 return user.NotificationSetting.newPeerTubeVersion
693 }
694
695 async function notificationCreator (user: MUserWithNotificationSetting) {
696 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
697 type: UserNotificationType.NEW_PEERTUBE_VERSION,
698 userId: user.id,
699 applicationId: application.id
700 })
701 notification.Application = application
702
703 return notification
704 }
705
706 function emailSender (emails: string[]) {
707 return Emailer.Instance.addNewPeerTubeVersionNotification(emails, latestVersion)
708 }
709
710 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
711 }
712
713 private async notifyAdminsOfNewPluginVersion (plugin: MPlugin) {
714 // Use the debug right to know who is an administrator
715 const admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG)
716 if (admins.length === 0) return
717
718 logger.info('Notifying %s admins of new plugin version %s@%s.', admins.length, plugin.name, plugin.latestVersion)
719
720 function settingGetter (user: MUserWithNotificationSetting) {
721 return user.NotificationSetting.newPluginVersion
722 }
723
724 async function notificationCreator (user: MUserWithNotificationSetting) {
725 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
726 type: UserNotificationType.NEW_PLUGIN_VERSION,
727 userId: user.id,
728 pluginId: plugin.id
729 })
730 notification.Plugin = plugin
731
732 return notification
733 }
734
735 function emailSender (emails: string[]) {
736 return Emailer.Instance.addNewPlugionVersionNotification(emails, plugin)
737 }
738
739 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
740 }
741
742 private async notify<T extends MUserWithNotificationSetting> (options: {
743 users: T[]
744 notificationCreator: (user: T) => Promise<UserNotificationModelForApi>
745 emailSender: (emails: string[]) => void
746 settingGetter: (user: T) => UserNotificationSettingValue
747 }) {
748 const emails: string[] = []
749
750 for (const user of options.users) {
751 if (this.isWebNotificationEnabled(options.settingGetter(user))) {
752 const notification = await options.notificationCreator(user)
753
754 PeerTubeSocket.Instance.sendNotification(user.id, notification)
755 }
756
757 if (this.isEmailEnabled(user, options.settingGetter(user))) {
758 emails.push(user.email)
759 }
760 }
761
762 if (emails.length !== 0) {
763 options.emailSender(emails)
764 }
765 }
766
767 private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) {
768 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false
769
770 return value & UserNotificationSettingValue.EMAIL
771 }
772
773 private isWebNotificationEnabled (value: UserNotificationSettingValue) {
774 return value & UserNotificationSettingValue.WEB
775 }
776
777 private isBlockedByServerOrUser (targetAccount: MAccountServer, user?: MUserAccount) {
778 return isBlockedByServerOrAccount(targetAccount, user?.Account)
779 }
780
781 private getAbuseUrl (abuse: MAbuseFull) {
782 return abuse.VideoAbuse?.Video?.url ||
783 abuse.VideoCommentAbuse?.VideoComment?.url ||
784 abuse.FlaggedAccount.Actor.url
785 }
786
787 static get Instance () {
788 return this.instance || (this.instance = new this())
789 }
790}
791
792// ---------------------------------------------------------------------------
793
794export {
795 Notifier
796}
diff --git a/server/lib/notifier/index.ts b/server/lib/notifier/index.ts
new file mode 100644
index 000000000..5bc2f5f50
--- /dev/null
+++ b/server/lib/notifier/index.ts
@@ -0,0 +1 @@
export * from './notifier'
diff --git a/server/lib/notifier/notifier.ts b/server/lib/notifier/notifier.ts
new file mode 100644
index 000000000..8b68d2e69
--- /dev/null
+++ b/server/lib/notifier/notifier.ts
@@ -0,0 +1,259 @@
1import { MUser, MUserDefault } from '@server/types/models/user'
2import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
3import { UserNotificationSettingValue } from '../../../shared/models/users'
4import { logger } from '../../helpers/logger'
5import { CONFIG } from '../../initializers/config'
6import { MAbuseFull, MAbuseMessage, MActorFollowFull, MApplication, MPlugin } from '../../types/models'
7import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../../types/models/video'
8import { JobQueue } from '../job-queue'
9import { PeerTubeSocket } from '../peertube-socket'
10import {
11 AbstractNotification,
12 AbuseStateChangeForReporter,
13 AutoFollowForInstance,
14 CommentMention,
15 FollowForInstance,
16 FollowForUser,
17 ImportFinishedForOwner,
18 ImportFinishedForOwnerPayload,
19 NewAbuseForModerators,
20 NewAbuseMessageForModerators,
21 NewAbuseMessageForReporter,
22 NewAbusePayload,
23 NewAutoBlacklistForModerators,
24 NewBlacklistForOwner,
25 NewCommentForVideoOwner,
26 NewPeerTubeVersionForAdmins,
27 NewPluginVersionForAdmins,
28 NewVideoForSubscribers,
29 OwnedPublicationAfterAutoUnblacklist,
30 OwnedPublicationAfterScheduleUpdate,
31 OwnedPublicationAfterTranscoding,
32 RegistrationForModerators,
33 UnblacklistForOwner
34} from './shared'
35
36class Notifier {
37
38 private readonly notificationModels = {
39 newVideo: [ NewVideoForSubscribers ],
40 publicationAfterTranscoding: [ OwnedPublicationAfterTranscoding ],
41 publicationAfterScheduleUpdate: [ OwnedPublicationAfterScheduleUpdate ],
42 publicationAfterAutoUnblacklist: [ OwnedPublicationAfterAutoUnblacklist ],
43 newComment: [ CommentMention, NewCommentForVideoOwner ],
44 newAbuse: [ NewAbuseForModerators ],
45 newBlacklist: [ NewBlacklistForOwner ],
46 unblacklist: [ UnblacklistForOwner ],
47 importFinished: [ ImportFinishedForOwner ],
48 userRegistration: [ RegistrationForModerators ],
49 userFollow: [ FollowForUser ],
50 instanceFollow: [ FollowForInstance ],
51 autoInstanceFollow: [ AutoFollowForInstance ],
52 newAutoBlacklist: [ NewAutoBlacklistForModerators ],
53 abuseStateChange: [ AbuseStateChangeForReporter ],
54 newAbuseMessage: [ NewAbuseMessageForReporter, NewAbuseMessageForModerators ],
55 newPeertubeVersion: [ NewPeerTubeVersionForAdmins ],
56 newPluginVersion: [ NewPluginVersionForAdmins ]
57 }
58
59 private static instance: Notifier
60
61 private constructor () {
62 }
63
64 notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void {
65 const models = this.notificationModels.newVideo
66
67 this.sendNotifications(models, video)
68 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
69 }
70
71 notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void {
72 const models = this.notificationModels.publicationAfterTranscoding
73
74 this.sendNotifications(models, video)
75 .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err }))
76 }
77
78 notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void {
79 const models = this.notificationModels.publicationAfterScheduleUpdate
80
81 this.sendNotifications(models, video)
82 .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err }))
83 }
84
85 notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void {
86 const models = this.notificationModels.publicationAfterAutoUnblacklist
87
88 this.sendNotifications(models, video)
89 .catch(err => {
90 logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })
91 })
92 }
93
94 notifyOnNewComment (comment: MCommentOwnerVideo): void {
95 const models = this.notificationModels.newComment
96
97 this.sendNotifications(models, comment)
98 .catch(err => logger.error('Cannot notify of new comment.', comment.url, { err }))
99 }
100
101 notifyOnNewAbuse (payload: NewAbusePayload): void {
102 const models = this.notificationModels.newAbuse
103
104 this.sendNotifications(models, payload)
105 .catch(err => logger.error('Cannot notify of new abuse %d.', payload.abuseInstance.id, { err }))
106 }
107
108 notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void {
109 const models = this.notificationModels.newAutoBlacklist
110
111 this.sendNotifications(models, videoBlacklist)
112 .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err }))
113 }
114
115 notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void {
116 const models = this.notificationModels.newBlacklist
117
118 this.sendNotifications(models, videoBlacklist)
119 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
120 }
121
122 notifyOnVideoUnblacklist (video: MVideoFullLight): void {
123 const models = this.notificationModels.unblacklist
124
125 this.sendNotifications(models, video)
126 .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
127 }
128
129 notifyOnFinishedVideoImport (payload: ImportFinishedForOwnerPayload): void {
130 const models = this.notificationModels.importFinished
131
132 this.sendNotifications(models, payload)
133 .catch(err => {
134 logger.error('Cannot notify owner that its video import %s is finished.', payload.videoImport.getTargetIdentifier(), { err })
135 })
136 }
137
138 notifyOnNewUserRegistration (user: MUserDefault): void {
139 const models = this.notificationModels.userRegistration
140
141 this.sendNotifications(models, user)
142 .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err }))
143 }
144
145 notifyOfNewUserFollow (actorFollow: MActorFollowFull): void {
146 const models = this.notificationModels.userFollow
147
148 this.sendNotifications(models, actorFollow)
149 .catch(err => {
150 logger.error(
151 'Cannot notify owner of channel %s of a new follow by %s.',
152 actorFollow.ActorFollowing.VideoChannel.getDisplayName(),
153 actorFollow.ActorFollower.Account.getDisplayName(),
154 { err }
155 )
156 })
157 }
158
159 notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void {
160 const models = this.notificationModels.instanceFollow
161
162 this.sendNotifications(models, actorFollow)
163 .catch(err => logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }))
164 }
165
166 notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void {
167 const models = this.notificationModels.autoInstanceFollow
168
169 this.sendNotifications(models, actorFollow)
170 .catch(err => logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err }))
171 }
172
173 notifyOnAbuseStateChange (abuse: MAbuseFull): void {
174 const models = this.notificationModels.abuseStateChange
175
176 this.sendNotifications(models, abuse)
177 .catch(err => logger.error('Cannot notify of abuse %d state change.', abuse.id, { err }))
178 }
179
180 notifyOnAbuseMessage (abuse: MAbuseFull, message: MAbuseMessage): void {
181 const models = this.notificationModels.newAbuseMessage
182
183 this.sendNotifications(models, { abuse, message })
184 .catch(err => logger.error('Cannot notify on new abuse %d message.', abuse.id, { err }))
185 }
186
187 notifyOfNewPeerTubeVersion (application: MApplication, latestVersion: string) {
188 const models = this.notificationModels.newPeertubeVersion
189
190 this.sendNotifications(models, { application, latestVersion })
191 .catch(err => logger.error('Cannot notify on new PeerTubeb version %s.', latestVersion, { err }))
192 }
193
194 notifyOfNewPluginVersion (plugin: MPlugin) {
195 const models = this.notificationModels.newPluginVersion
196
197 this.sendNotifications(models, plugin)
198 .catch(err => logger.error('Cannot notify on new plugin version %s.', plugin.name, { err }))
199 }
200
201 private async notify <T> (object: AbstractNotification<T>) {
202 await object.prepare()
203
204 const users = object.getTargetUsers()
205
206 if (users.length === 0) return
207 if (await object.isDisabled()) return
208
209 object.log()
210
211 const toEmails: string[] = []
212
213 for (const user of users) {
214 const setting = object.getSetting(user)
215
216 if (this.isWebNotificationEnabled(setting)) {
217 const notification = await object.createNotification(user)
218
219 PeerTubeSocket.Instance.sendNotification(user.id, notification)
220 }
221
222 if (this.isEmailEnabled(user, setting)) {
223 toEmails.push(user.email)
224 }
225 }
226
227 for (const to of toEmails) {
228 const payload = await object.createEmail(to)
229 JobQueue.Instance.createJob({ type: 'email', payload })
230 }
231 }
232
233 private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) {
234 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false
235
236 return value & UserNotificationSettingValue.EMAIL
237 }
238
239 private isWebNotificationEnabled (value: UserNotificationSettingValue) {
240 return value & UserNotificationSettingValue.WEB
241 }
242
243 private async sendNotifications <T> (models: (new (payload: T) => AbstractNotification<T>)[], payload: T) {
244 for (const model of models) {
245 // eslint-disable-next-line new-cap
246 await this.notify(new model(payload))
247 }
248 }
249
250 static get Instance () {
251 return this.instance || (this.instance = new this())
252 }
253}
254
255// ---------------------------------------------------------------------------
256
257export {
258 Notifier
259}
diff --git a/server/lib/notifier/shared/abuse/abstract-new-abuse-message.ts b/server/lib/notifier/shared/abuse/abstract-new-abuse-message.ts
new file mode 100644
index 000000000..1425c38ec
--- /dev/null
+++ b/server/lib/notifier/shared/abuse/abstract-new-abuse-message.ts
@@ -0,0 +1,67 @@
1import { WEBSERVER } from '@server/initializers/constants'
2import { AccountModel } from '@server/models/account/account'
3import { UserNotificationModel } from '@server/models/user/user-notification'
4import { MAbuseFull, MAbuseMessage, MAccountDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
5import { UserNotificationType } from '@shared/models'
6import { AbstractNotification } from '../common/abstract-notification'
7
8export type NewAbuseMessagePayload = {
9 abuse: MAbuseFull
10 message: MAbuseMessage
11}
12
13export abstract class AbstractNewAbuseMessage extends AbstractNotification <NewAbuseMessagePayload> {
14 protected messageAccount: MAccountDefault
15
16 async loadMessageAccount () {
17 this.messageAccount = await AccountModel.load(this.message.accountId)
18 }
19
20 getSetting (user: MUserWithNotificationSetting) {
21 return user.NotificationSetting.abuseNewMessage
22 }
23
24 async createNotification (user: MUserWithNotificationSetting) {
25 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
26 type: UserNotificationType.ABUSE_NEW_MESSAGE,
27 userId: user.id,
28 abuseId: this.abuse.id
29 })
30 notification.Abuse = this.abuse
31
32 return notification
33 }
34
35 protected createEmailFor (to: string, target: 'moderator' | 'reporter') {
36 const text = 'New message on report #' + this.abuse.id
37 const abuseUrl = target === 'moderator'
38 ? WEBSERVER.URL + '/admin/moderation/abuses/list?search=%23' + this.abuse.id
39 : WEBSERVER.URL + '/my-account/abuses?search=%23' + this.abuse.id
40
41 const action = {
42 text,
43 url: abuseUrl
44 }
45
46 return {
47 template: 'abuse-new-message',
48 to,
49 subject: text,
50 locals: {
51 abuseId: this.abuse.id,
52 abuseUrl: action.url,
53 messageAccountName: this.messageAccount.getDisplayName(),
54 messageText: this.message.message,
55 action
56 }
57 }
58 }
59
60 protected get abuse () {
61 return this.payload.abuse
62 }
63
64 protected get message () {
65 return this.payload.message
66 }
67}
diff --git a/server/lib/notifier/shared/abuse/abuse-state-change-for-reporter.ts b/server/lib/notifier/shared/abuse/abuse-state-change-for-reporter.ts
new file mode 100644
index 000000000..968b5bca9
--- /dev/null
+++ b/server/lib/notifier/shared/abuse/abuse-state-change-for-reporter.ts
@@ -0,0 +1,74 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { getAbuseTargetUrl } from '@server/lib/activitypub/url'
4import { UserModel } from '@server/models/user/user'
5import { UserNotificationModel } from '@server/models/user/user-notification'
6import { MAbuseFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
7import { AbuseState, UserNotificationType } from '@shared/models'
8import { AbstractNotification } from '../common/abstract-notification'
9
10export class AbuseStateChangeForReporter extends AbstractNotification <MAbuseFull> {
11
12 private user: MUserDefault
13
14 async prepare () {
15 const reporter = this.abuse.ReporterAccount
16 if (reporter.isOwned() !== true) return
17
18 this.user = await UserModel.loadByAccountActorId(this.abuse.ReporterAccount.actorId)
19 }
20
21 log () {
22 logger.info('Notifying reporter of abuse % of state change.', getAbuseTargetUrl(this.abuse))
23 }
24
25 getSetting (user: MUserWithNotificationSetting) {
26 return user.NotificationSetting.abuseStateChange
27 }
28
29 getTargetUsers () {
30 if (!this.user) return []
31
32 return [ this.user ]
33 }
34
35 async createNotification (user: MUserWithNotificationSetting) {
36 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
37 type: UserNotificationType.ABUSE_STATE_CHANGE,
38 userId: user.id,
39 abuseId: this.abuse.id
40 })
41 notification.Abuse = this.abuse
42
43 return notification
44 }
45
46 createEmail (to: string) {
47 const text = this.abuse.state === AbuseState.ACCEPTED
48 ? 'Report #' + this.abuse.id + ' has been accepted'
49 : 'Report #' + this.abuse.id + ' has been rejected'
50
51 const abuseUrl = WEBSERVER.URL + '/my-account/abuses?search=%23' + this.abuse.id
52
53 const action = {
54 text,
55 url: abuseUrl
56 }
57
58 return {
59 template: 'abuse-state-change',
60 to,
61 subject: text,
62 locals: {
63 action,
64 abuseId: this.abuse.id,
65 abuseUrl,
66 isAccepted: this.abuse.state === AbuseState.ACCEPTED
67 }
68 }
69 }
70
71 private get abuse () {
72 return this.payload
73 }
74}
diff --git a/server/lib/notifier/shared/abuse/index.ts b/server/lib/notifier/shared/abuse/index.ts
new file mode 100644
index 000000000..7b54c5591
--- /dev/null
+++ b/server/lib/notifier/shared/abuse/index.ts
@@ -0,0 +1,4 @@
1export * from './abuse-state-change-for-reporter'
2export * from './new-abuse-for-moderators'
3export * from './new-abuse-message-for-reporter'
4export * from './new-abuse-message-for-moderators'
diff --git a/server/lib/notifier/shared/abuse/new-abuse-for-moderators.ts b/server/lib/notifier/shared/abuse/new-abuse-for-moderators.ts
new file mode 100644
index 000000000..c3c7c5515
--- /dev/null
+++ b/server/lib/notifier/shared/abuse/new-abuse-for-moderators.ts
@@ -0,0 +1,119 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { getAbuseTargetUrl } from '@server/lib/activitypub/url'
4import { UserModel } from '@server/models/user/user'
5import { UserNotificationModel } from '@server/models/user/user-notification'
6import { MAbuseFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
7import { UserAbuse, UserNotificationType, UserRight } from '@shared/models'
8import { AbstractNotification } from '../common/abstract-notification'
9
10export type NewAbusePayload = { abuse: UserAbuse, abuseInstance: MAbuseFull, reporter: string }
11
12export class NewAbuseForModerators extends AbstractNotification <NewAbusePayload> {
13 private moderators: MUserDefault[]
14
15 async prepare () {
16 this.moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES)
17 }
18
19 log () {
20 logger.info('Notifying %s user/moderators of new abuse %s.', this.moderators.length, getAbuseTargetUrl(this.payload.abuseInstance))
21 }
22
23 getSetting (user: MUserWithNotificationSetting) {
24 return user.NotificationSetting.abuseAsModerator
25 }
26
27 getTargetUsers () {
28 return this.moderators
29 }
30
31 async createNotification (user: MUserWithNotificationSetting) {
32 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
33 type: UserNotificationType.NEW_ABUSE_FOR_MODERATORS,
34 userId: user.id,
35 abuseId: this.payload.abuseInstance.id
36 })
37 notification.Abuse = this.payload.abuseInstance
38
39 return notification
40 }
41
42 createEmail (to: string) {
43 const abuseInstance = this.payload.abuseInstance
44
45 if (abuseInstance.VideoAbuse) return this.createVideoAbuseEmail(to)
46 if (abuseInstance.VideoCommentAbuse) return this.createCommentAbuseEmail(to)
47
48 return this.createAccountAbuseEmail(to)
49 }
50
51 private createVideoAbuseEmail (to: string) {
52 const video = this.payload.abuseInstance.VideoAbuse.Video
53 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
54
55 return {
56 template: 'video-abuse-new',
57 to,
58 subject: `New video abuse report from ${this.payload.reporter}`,
59 locals: {
60 videoUrl,
61 isLocal: video.remote === false,
62 videoCreatedAt: new Date(video.createdAt).toLocaleString(),
63 videoPublishedAt: new Date(video.publishedAt).toLocaleString(),
64 videoName: video.name,
65 reason: this.payload.abuse.reason,
66 videoChannel: this.payload.abuse.video.channel,
67 reporter: this.payload.reporter,
68 action: this.buildEmailAction()
69 }
70 }
71 }
72
73 private createCommentAbuseEmail (to: string) {
74 const comment = this.payload.abuseInstance.VideoCommentAbuse.VideoComment
75 const commentUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath() + ';threadId=' + comment.getThreadId()
76
77 return {
78 template: 'video-comment-abuse-new',
79 to,
80 subject: `New comment abuse report from ${this.payload.reporter}`,
81 locals: {
82 commentUrl,
83 videoName: comment.Video.name,
84 isLocal: comment.isOwned(),
85 commentCreatedAt: new Date(comment.createdAt).toLocaleString(),
86 reason: this.payload.abuse.reason,
87 flaggedAccount: this.payload.abuseInstance.FlaggedAccount.getDisplayName(),
88 reporter: this.payload.reporter,
89 action: this.buildEmailAction()
90 }
91 }
92 }
93
94 private createAccountAbuseEmail (to: string) {
95 const account = this.payload.abuseInstance.FlaggedAccount
96 const accountUrl = account.getClientUrl()
97
98 return {
99 template: 'account-abuse-new',
100 to,
101 subject: `New account abuse report from ${this.payload.reporter}`,
102 locals: {
103 accountUrl,
104 accountDisplayName: account.getDisplayName(),
105 isLocal: account.isOwned(),
106 reason: this.payload.abuse.reason,
107 reporter: this.payload.reporter,
108 action: this.buildEmailAction()
109 }
110 }
111 }
112
113 private buildEmailAction () {
114 return {
115 text: 'View report #' + this.payload.abuseInstance.id,
116 url: WEBSERVER.URL + '/admin/moderation/abuses/list?search=%23' + this.payload.abuseInstance.id
117 }
118 }
119}
diff --git a/server/lib/notifier/shared/abuse/new-abuse-message-for-moderators.ts b/server/lib/notifier/shared/abuse/new-abuse-message-for-moderators.ts
new file mode 100644
index 000000000..9d0629690
--- /dev/null
+++ b/server/lib/notifier/shared/abuse/new-abuse-message-for-moderators.ts
@@ -0,0 +1,32 @@
1import { logger } from '@server/helpers/logger'
2import { getAbuseTargetUrl } from '@server/lib/activitypub/url'
3import { UserModel } from '@server/models/user/user'
4import { MUserDefault } from '@server/types/models'
5import { UserRight } from '@shared/models'
6import { AbstractNewAbuseMessage } from './abstract-new-abuse-message'
7
8export class NewAbuseMessageForModerators extends AbstractNewAbuseMessage {
9 private moderators: MUserDefault[]
10
11 async prepare () {
12 this.moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES)
13
14 // Don't notify my own message
15 this.moderators = this.moderators.filter(m => m.Account.id !== this.message.accountId)
16 if (this.moderators.length === 0) return
17
18 await this.loadMessageAccount()
19 }
20
21 log () {
22 logger.info('Notifying moderators of new abuse message on %s.', getAbuseTargetUrl(this.abuse))
23 }
24
25 getTargetUsers () {
26 return this.moderators
27 }
28
29 createEmail (to: string) {
30 return this.createEmailFor(to, 'moderator')
31 }
32}
diff --git a/server/lib/notifier/shared/abuse/new-abuse-message-for-reporter.ts b/server/lib/notifier/shared/abuse/new-abuse-message-for-reporter.ts
new file mode 100644
index 000000000..c5bbb5447
--- /dev/null
+++ b/server/lib/notifier/shared/abuse/new-abuse-message-for-reporter.ts
@@ -0,0 +1,36 @@
1import { logger } from '@server/helpers/logger'
2import { getAbuseTargetUrl } from '@server/lib/activitypub/url'
3import { UserModel } from '@server/models/user/user'
4import { MUserDefault } from '@server/types/models'
5import { AbstractNewAbuseMessage } from './abstract-new-abuse-message'
6
7export class NewAbuseMessageForReporter extends AbstractNewAbuseMessage {
8 private reporter: MUserDefault
9
10 async prepare () {
11 // Only notify our users
12 if (this.abuse.ReporterAccount.isOwned() !== true) return
13
14 await this.loadMessageAccount()
15
16 const reporter = await UserModel.loadByAccountActorId(this.abuse.ReporterAccount.actorId)
17 // Don't notify my own message
18 if (reporter.Account.id === this.message.accountId) return
19
20 this.reporter = reporter
21 }
22
23 log () {
24 logger.info('Notifying reporter of new abuse message on %s.', getAbuseTargetUrl(this.abuse))
25 }
26
27 getTargetUsers () {
28 if (!this.reporter) return []
29
30 return [ this.reporter ]
31 }
32
33 createEmail (to: string) {
34 return this.createEmailFor(to, 'reporter')
35 }
36}
diff --git a/server/lib/notifier/shared/blacklist/index.ts b/server/lib/notifier/shared/blacklist/index.ts
new file mode 100644
index 000000000..2f98d88ae
--- /dev/null
+++ b/server/lib/notifier/shared/blacklist/index.ts
@@ -0,0 +1,3 @@
1export * from './new-auto-blacklist-for-moderators'
2export * from './new-blacklist-for-owner'
3export * from './unblacklist-for-owner'
diff --git a/server/lib/notifier/shared/blacklist/new-auto-blacklist-for-moderators.ts b/server/lib/notifier/shared/blacklist/new-auto-blacklist-for-moderators.ts
new file mode 100644
index 000000000..a92a49a0c
--- /dev/null
+++ b/server/lib/notifier/shared/blacklist/new-auto-blacklist-for-moderators.ts
@@ -0,0 +1,60 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { UserModel } from '@server/models/user/user'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { VideoChannelModel } from '@server/models/video/video-channel'
6import { MUserDefault, MUserWithNotificationSetting, MVideoBlacklistLightVideo, UserNotificationModelForApi } from '@server/types/models'
7import { UserNotificationType, UserRight } from '@shared/models'
8import { AbstractNotification } from '../common/abstract-notification'
9
10export class NewAutoBlacklistForModerators extends AbstractNotification <MVideoBlacklistLightVideo> {
11 private moderators: MUserDefault[]
12
13 async prepare () {
14 this.moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
15 }
16
17 log () {
18 logger.info('Notifying %s moderators of video auto-blacklist %s.', this.moderators.length, this.payload.Video.url)
19 }
20
21 getSetting (user: MUserWithNotificationSetting) {
22 return user.NotificationSetting.videoAutoBlacklistAsModerator
23 }
24
25 getTargetUsers () {
26 return this.moderators
27 }
28
29 async createNotification (user: MUserWithNotificationSetting) {
30 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
31 type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS,
32 userId: user.id,
33 videoBlacklistId: this.payload.id
34 })
35 notification.VideoBlacklist = this.payload
36
37 return notification
38 }
39
40 async createEmail (to: string) {
41 const videoAutoBlacklistUrl = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
42 const videoUrl = WEBSERVER.URL + this.payload.Video.getWatchStaticPath()
43 const channel = await VideoChannelModel.loadAndPopulateAccount(this.payload.Video.channelId)
44
45 return {
46 template: 'video-auto-blacklist-new',
47 to,
48 subject: 'A new video is pending moderation',
49 locals: {
50 channel: channel.toFormattedSummaryJSON(),
51 videoUrl,
52 videoName: this.payload.Video.name,
53 action: {
54 text: 'Review autoblacklist',
55 url: videoAutoBlacklistUrl
56 }
57 }
58 }
59 }
60}
diff --git a/server/lib/notifier/shared/blacklist/new-blacklist-for-owner.ts b/server/lib/notifier/shared/blacklist/new-blacklist-for-owner.ts
new file mode 100644
index 000000000..45bc30eb2
--- /dev/null
+++ b/server/lib/notifier/shared/blacklist/new-blacklist-for-owner.ts
@@ -0,0 +1,58 @@
1import { logger } from '@server/helpers/logger'
2import { CONFIG } from '@server/initializers/config'
3import { WEBSERVER } from '@server/initializers/constants'
4import { UserModel } from '@server/models/user/user'
5import { UserNotificationModel } from '@server/models/user/user-notification'
6import { MUserDefault, MUserWithNotificationSetting, MVideoBlacklistVideo, UserNotificationModelForApi } from '@server/types/models'
7import { UserNotificationType } from '@shared/models'
8import { AbstractNotification } from '../common/abstract-notification'
9
10export class NewBlacklistForOwner extends AbstractNotification <MVideoBlacklistVideo> {
11 private user: MUserDefault
12
13 async prepare () {
14 this.user = await UserModel.loadByVideoId(this.payload.videoId)
15 }
16
17 log () {
18 logger.info('Notifying user %s that its video %s has been blacklisted.', this.user.username, this.payload.Video.url)
19 }
20
21 getSetting (user: MUserWithNotificationSetting) {
22 return user.NotificationSetting.blacklistOnMyVideo
23 }
24
25 getTargetUsers () {
26 if (!this.user) return []
27
28 return [ this.user ]
29 }
30
31 async createNotification (user: MUserWithNotificationSetting) {
32 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
33 type: UserNotificationType.BLACKLIST_ON_MY_VIDEO,
34 userId: user.id,
35 videoBlacklistId: this.payload.id
36 })
37 notification.VideoBlacklist = this.payload
38
39 return notification
40 }
41
42 createEmail (to: string) {
43 const videoName = this.payload.Video.name
44 const videoUrl = WEBSERVER.URL + this.payload.Video.getWatchStaticPath()
45
46 const reasonString = this.payload.reason ? ` for the following reason: ${this.payload.reason}` : ''
47 const blockedString = `Your video ${videoName} (${videoUrl} on ${CONFIG.INSTANCE.NAME} has been blacklisted${reasonString}.`
48
49 return {
50 to,
51 subject: `Video ${videoName} blacklisted`,
52 text: blockedString,
53 locals: {
54 title: 'Your video was blacklisted'
55 }
56 }
57 }
58}
diff --git a/server/lib/notifier/shared/blacklist/unblacklist-for-owner.ts b/server/lib/notifier/shared/blacklist/unblacklist-for-owner.ts
new file mode 100644
index 000000000..21f5a1c2d
--- /dev/null
+++ b/server/lib/notifier/shared/blacklist/unblacklist-for-owner.ts
@@ -0,0 +1,55 @@
1import { logger } from '@server/helpers/logger'
2import { CONFIG } from '@server/initializers/config'
3import { WEBSERVER } from '@server/initializers/constants'
4import { UserModel } from '@server/models/user/user'
5import { UserNotificationModel } from '@server/models/user/user-notification'
6import { MUserDefault, MUserWithNotificationSetting, MVideoFullLight, UserNotificationModelForApi } from '@server/types/models'
7import { UserNotificationType } from '@shared/models'
8import { AbstractNotification } from '../common/abstract-notification'
9
10export class UnblacklistForOwner extends AbstractNotification <MVideoFullLight> {
11 private user: MUserDefault
12
13 async prepare () {
14 this.user = await UserModel.loadByVideoId(this.payload.id)
15 }
16
17 log () {
18 logger.info('Notifying user %s that its video %s has been unblacklisted.', this.user.username, this.payload.url)
19 }
20
21 getSetting (user: MUserWithNotificationSetting) {
22 return user.NotificationSetting.blacklistOnMyVideo
23 }
24
25 getTargetUsers () {
26 if (!this.user) return []
27
28 return [ this.user ]
29 }
30
31 async createNotification (user: MUserWithNotificationSetting) {
32 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
33 type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO,
34 userId: user.id,
35 videoId: this.payload.id
36 })
37 notification.Video = this.payload
38
39 return notification
40 }
41
42 createEmail (to: string) {
43 const video = this.payload
44 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
45
46 return {
47 to,
48 subject: `Video ${video.name} unblacklisted`,
49 text: `Your video "${video.name}" (${videoUrl}) on ${CONFIG.INSTANCE.NAME} has been unblacklisted.`,
50 locals: {
51 title: 'Your video was unblacklisted'
52 }
53 }
54 }
55}
diff --git a/server/lib/notifier/shared/comment/comment-mention.ts b/server/lib/notifier/shared/comment/comment-mention.ts
new file mode 100644
index 000000000..4f84d8dea
--- /dev/null
+++ b/server/lib/notifier/shared/comment/comment-mention.ts
@@ -0,0 +1,111 @@
1import { logger } from '@server/helpers/logger'
2import { toSafeHtml } from '@server/helpers/markdown'
3import { WEBSERVER } from '@server/initializers/constants'
4import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
5import { getServerActor } from '@server/models/application/application'
6import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
7import { UserModel } from '@server/models/user/user'
8import { UserNotificationModel } from '@server/models/user/user-notification'
9import {
10 MCommentOwnerVideo,
11 MUserDefault,
12 MUserNotifSettingAccount,
13 MUserWithNotificationSetting,
14 UserNotificationModelForApi
15} from '@server/types/models'
16import { UserNotificationSettingValue, UserNotificationType } from '@shared/models'
17import { AbstractNotification } from '../common'
18
19export class CommentMention extends AbstractNotification <MCommentOwnerVideo, MUserNotifSettingAccount> {
20 private users: MUserDefault[]
21
22 private serverAccountId: number
23
24 private accountMutedHash: { [ id: number ]: boolean }
25 private instanceMutedHash: { [ id: number ]: boolean }
26
27 async prepare () {
28 const extractedUsernames = this.payload.extractMentions()
29 logger.debug(
30 'Extracted %d username from comment %s.', extractedUsernames.length, this.payload.url,
31 { usernames: extractedUsernames, text: this.payload.text }
32 )
33
34 this.users = await UserModel.listByUsernames(extractedUsernames)
35
36 if (this.payload.Video.isOwned()) {
37 const userException = await UserModel.loadByVideoId(this.payload.videoId)
38 this.users = this.users.filter(u => u.id !== userException.id)
39 }
40
41 // Don't notify if I mentioned myself
42 this.users = this.users.filter(u => u.Account.id !== this.payload.accountId)
43
44 if (this.users.length === 0) return
45
46 this.serverAccountId = (await getServerActor()).Account.id
47
48 const sourceAccounts = this.users.map(u => u.Account.id).concat([ this.serverAccountId ])
49
50 this.accountMutedHash = await AccountBlocklistModel.isAccountMutedByMulti(sourceAccounts, this.payload.accountId)
51 this.instanceMutedHash = await ServerBlocklistModel.isServerMutedByMulti(sourceAccounts, this.payload.Account.Actor.serverId)
52 }
53
54 log () {
55 logger.info('Notifying %d users of new comment %s.', this.users.length, this.payload.url)
56 }
57
58 getSetting (user: MUserNotifSettingAccount) {
59 const accountId = user.Account.id
60 if (
61 this.accountMutedHash[accountId] === true || this.instanceMutedHash[accountId] === true ||
62 this.accountMutedHash[this.serverAccountId] === true || this.instanceMutedHash[this.serverAccountId] === true
63 ) {
64 return UserNotificationSettingValue.NONE
65 }
66
67 return user.NotificationSetting.commentMention
68 }
69
70 getTargetUsers () {
71 return this.users
72 }
73
74 async createNotification (user: MUserWithNotificationSetting) {
75 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
76 type: UserNotificationType.COMMENT_MENTION,
77 userId: user.id,
78 commentId: this.payload.id
79 })
80 notification.Comment = this.payload
81
82 return notification
83 }
84
85 createEmail (to: string) {
86 const comment = this.payload
87
88 const accountName = comment.Account.getDisplayName()
89 const video = comment.Video
90 const videoUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath()
91 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
92 const commentHtml = toSafeHtml(comment.text)
93
94 return {
95 template: 'video-comment-mention',
96 to,
97 subject: 'Mention on video ' + video.name,
98 locals: {
99 comment,
100 commentHtml,
101 video,
102 videoUrl,
103 accountName,
104 action: {
105 text: 'View comment',
106 url: commentUrl
107 }
108 }
109 }
110 }
111}
diff --git a/server/lib/notifier/shared/comment/index.ts b/server/lib/notifier/shared/comment/index.ts
new file mode 100644
index 000000000..ae01a9646
--- /dev/null
+++ b/server/lib/notifier/shared/comment/index.ts
@@ -0,0 +1,2 @@
1export * from './comment-mention'
2export * from './new-comment-for-video-owner'
diff --git a/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts b/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts
new file mode 100644
index 000000000..b76fc15bf
--- /dev/null
+++ b/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts
@@ -0,0 +1,76 @@
1import { logger } from '@server/helpers/logger'
2import { toSafeHtml } from '@server/helpers/markdown'
3import { WEBSERVER } from '@server/initializers/constants'
4import { isBlockedByServerOrAccount } from '@server/lib/blocklist'
5import { UserModel } from '@server/models/user/user'
6import { UserNotificationModel } from '@server/models/user/user-notification'
7import { MCommentOwnerVideo, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
8import { UserNotificationType } from '@shared/models'
9import { AbstractNotification } from '../common/abstract-notification'
10
11export class NewCommentForVideoOwner extends AbstractNotification <MCommentOwnerVideo> {
12 private user: MUserDefault
13
14 async prepare () {
15 this.user = await UserModel.loadByVideoId(this.payload.videoId)
16 }
17
18 log () {
19 logger.info('Notifying owner of a video %s of new comment %s.', this.user.username, this.payload.url)
20 }
21
22 isDisabled () {
23 if (this.payload.Video.isOwned() === false) return true
24
25 // Not our user or user comments its own video
26 if (!this.user || this.payload.Account.userId === this.user.id) return true
27
28 return isBlockedByServerOrAccount(this.payload.Account, this.user.Account)
29 }
30
31 getSetting (user: MUserWithNotificationSetting) {
32 return user.NotificationSetting.newCommentOnMyVideo
33 }
34
35 getTargetUsers () {
36 if (!this.user) return []
37
38 return [ this.user ]
39 }
40
41 async createNotification (user: MUserWithNotificationSetting) {
42 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
43 type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO,
44 userId: user.id,
45 commentId: this.payload.id
46 })
47 notification.Comment = this.payload
48
49 return notification
50 }
51
52 createEmail (to: string) {
53 const video = this.payload.Video
54 const videoUrl = WEBSERVER.URL + this.payload.Video.getWatchStaticPath()
55 const commentUrl = WEBSERVER.URL + this.payload.getCommentStaticPath()
56 const commentHtml = toSafeHtml(this.payload.text)
57
58 return {
59 template: 'video-comment-new',
60 to,
61 subject: 'New comment on your video ' + video.name,
62 locals: {
63 accountName: this.payload.Account.getDisplayName(),
64 accountUrl: this.payload.Account.Actor.url,
65 comment: this.payload,
66 commentHtml,
67 video,
68 videoUrl,
69 action: {
70 text: 'View comment',
71 url: commentUrl
72 }
73 }
74 }
75 }
76}
diff --git a/server/lib/notifier/shared/common/abstract-notification.ts b/server/lib/notifier/shared/common/abstract-notification.ts
new file mode 100644
index 000000000..53e2e02d5
--- /dev/null
+++ b/server/lib/notifier/shared/common/abstract-notification.ts
@@ -0,0 +1,23 @@
1import { MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
2import { EmailPayload, UserNotificationSettingValue } from '@shared/models'
3
4export abstract class AbstractNotification <T, U = MUserWithNotificationSetting> {
5
6 constructor (protected readonly payload: T) {
7
8 }
9
10 abstract prepare (): Promise<void>
11 abstract log (): void
12
13 abstract getSetting (user: U): UserNotificationSettingValue
14 abstract getTargetUsers (): U[]
15
16 abstract createNotification (user: U): Promise<UserNotificationModelForApi>
17 abstract createEmail (to: string): EmailPayload | Promise<EmailPayload>
18
19 isDisabled (): boolean | Promise<boolean> {
20 return false
21 }
22
23}
diff --git a/server/lib/notifier/shared/common/index.ts b/server/lib/notifier/shared/common/index.ts
new file mode 100644
index 000000000..0b2570278
--- /dev/null
+++ b/server/lib/notifier/shared/common/index.ts
@@ -0,0 +1 @@
export * from './abstract-notification'
diff --git a/server/lib/notifier/shared/follow/auto-follow-for-instance.ts b/server/lib/notifier/shared/follow/auto-follow-for-instance.ts
new file mode 100644
index 000000000..16cc62984
--- /dev/null
+++ b/server/lib/notifier/shared/follow/auto-follow-for-instance.ts
@@ -0,0 +1,51 @@
1import { logger } from '@server/helpers/logger'
2import { UserModel } from '@server/models/user/user'
3import { UserNotificationModel } from '@server/models/user/user-notification'
4import { MActorFollowFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
5import { UserNotificationType, UserRight } from '@shared/models'
6import { AbstractNotification } from '../common/abstract-notification'
7
8export class AutoFollowForInstance extends AbstractNotification <MActorFollowFull> {
9 private admins: MUserDefault[]
10
11 async prepare () {
12 this.admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
13 }
14
15 log () {
16 logger.info('Notifying %d administrators of auto instance following: %s.', this.admins.length, this.actorFollow.ActorFollowing.url)
17 }
18
19 getSetting (user: MUserWithNotificationSetting) {
20 return user.NotificationSetting.autoInstanceFollowing
21 }
22
23 getTargetUsers () {
24 return this.admins
25 }
26
27 async createNotification (user: MUserWithNotificationSetting) {
28 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
29 type: UserNotificationType.AUTO_INSTANCE_FOLLOWING,
30 userId: user.id,
31 actorFollowId: this.actorFollow.id
32 })
33 notification.ActorFollow = this.actorFollow
34
35 return notification
36 }
37
38 async createEmail (to: string) {
39 const instanceUrl = this.actorFollow.ActorFollowing.url
40
41 return {
42 to,
43 subject: 'Auto instance following',
44 text: `Your instance automatically followed a new instance: <a href="${instanceUrl}">${instanceUrl}</a>.`
45 }
46 }
47
48 private get actorFollow () {
49 return this.payload
50 }
51}
diff --git a/server/lib/notifier/shared/follow/follow-for-instance.ts b/server/lib/notifier/shared/follow/follow-for-instance.ts
new file mode 100644
index 000000000..9ab269cf1
--- /dev/null
+++ b/server/lib/notifier/shared/follow/follow-for-instance.ts
@@ -0,0 +1,68 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { isBlockedByServerOrAccount } from '@server/lib/blocklist'
4import { UserModel } from '@server/models/user/user'
5import { UserNotificationModel } from '@server/models/user/user-notification'
6import { MActorFollowFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
7import { UserNotificationType, UserRight } from '@shared/models'
8import { AbstractNotification } from '../common/abstract-notification'
9
10export class FollowForInstance extends AbstractNotification <MActorFollowFull> {
11 private admins: MUserDefault[]
12
13 async prepare () {
14 this.admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
15 }
16
17 isDisabled () {
18 const follower = Object.assign(this.actorFollow.ActorFollower.Account, { Actor: this.actorFollow.ActorFollower })
19
20 return isBlockedByServerOrAccount(follower)
21 }
22
23 log () {
24 logger.info('Notifying %d administrators of new instance follower: %s.', this.admins.length, this.actorFollow.ActorFollower.url)
25 }
26
27 getSetting (user: MUserWithNotificationSetting) {
28 return user.NotificationSetting.newInstanceFollower
29 }
30
31 getTargetUsers () {
32 return this.admins
33 }
34
35 async createNotification (user: MUserWithNotificationSetting) {
36 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
37 type: UserNotificationType.NEW_INSTANCE_FOLLOWER,
38 userId: user.id,
39 actorFollowId: this.actorFollow.id
40 })
41 notification.ActorFollow = this.actorFollow
42
43 return notification
44 }
45
46 async createEmail (to: string) {
47 const awaitingApproval = this.actorFollow.state === 'pending'
48 ? ' awaiting manual approval.'
49 : ''
50
51 return {
52 to,
53 subject: 'New instance follower',
54 text: `Your instance has a new follower: ${this.actorFollow.ActorFollower.url}${awaitingApproval}.`,
55 locals: {
56 title: 'New instance follower',
57 action: {
58 text: 'Review followers',
59 url: WEBSERVER.URL + '/admin/follows/followers-list'
60 }
61 }
62 }
63 }
64
65 private get actorFollow () {
66 return this.payload
67 }
68}
diff --git a/server/lib/notifier/shared/follow/follow-for-user.ts b/server/lib/notifier/shared/follow/follow-for-user.ts
new file mode 100644
index 000000000..2d0f675a8
--- /dev/null
+++ b/server/lib/notifier/shared/follow/follow-for-user.ts
@@ -0,0 +1,82 @@
1import { logger } from '@server/helpers/logger'
2import { isBlockedByServerOrAccount } from '@server/lib/blocklist'
3import { UserModel } from '@server/models/user/user'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { MActorFollowFull, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
6import { UserNotificationType } from '@shared/models'
7import { AbstractNotification } from '../common/abstract-notification'
8
9export class FollowForUser extends AbstractNotification <MActorFollowFull> {
10 private followType: 'account' | 'channel'
11 private user: MUserDefault
12
13 async prepare () {
14 // Account follows one of our account?
15 this.followType = 'channel'
16 this.user = await UserModel.loadByChannelActorId(this.actorFollow.ActorFollowing.id)
17
18 // Account follows one of our channel?
19 if (!this.user) {
20 this.user = await UserModel.loadByAccountActorId(this.actorFollow.ActorFollowing.id)
21 this.followType = 'account'
22 }
23 }
24
25 async isDisabled () {
26 if (this.payload.ActorFollowing.isOwned() === false) return true
27
28 const followerAccount = this.actorFollow.ActorFollower.Account
29 const followerAccountWithActor = Object.assign(followerAccount, { Actor: this.actorFollow.ActorFollower })
30
31 return isBlockedByServerOrAccount(followerAccountWithActor, this.user.Account)
32 }
33
34 log () {
35 logger.info('Notifying user %s of new follower: %s.', this.user.username, this.actorFollow.ActorFollower.Account.getDisplayName())
36 }
37
38 getSetting (user: MUserWithNotificationSetting) {
39 return user.NotificationSetting.newFollow
40 }
41
42 getTargetUsers () {
43 if (!this.user) return []
44
45 return [ this.user ]
46 }
47
48 async createNotification (user: MUserWithNotificationSetting) {
49 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
50 type: UserNotificationType.NEW_FOLLOW,
51 userId: user.id,
52 actorFollowId: this.actorFollow.id
53 })
54 notification.ActorFollow = this.actorFollow
55
56 return notification
57 }
58
59 async createEmail (to: string) {
60 const following = this.actorFollow.ActorFollowing
61 const follower = this.actorFollow.ActorFollower
62
63 const followingName = (following.VideoChannel || following.Account).getDisplayName()
64
65 return {
66 template: 'follower-on-channel',
67 to,
68 subject: `New follower on your channel ${followingName}`,
69 locals: {
70 followerName: follower.Account.getDisplayName(),
71 followerUrl: follower.url,
72 followingName,
73 followingUrl: following.url,
74 followType: this.followType
75 }
76 }
77 }
78
79 private get actorFollow () {
80 return this.payload
81 }
82}
diff --git a/server/lib/notifier/shared/follow/index.ts b/server/lib/notifier/shared/follow/index.ts
new file mode 100644
index 000000000..27f5289d9
--- /dev/null
+++ b/server/lib/notifier/shared/follow/index.ts
@@ -0,0 +1,3 @@
1export * from './auto-follow-for-instance'
2export * from './follow-for-instance'
3export * from './follow-for-user'
diff --git a/server/lib/notifier/shared/index.ts b/server/lib/notifier/shared/index.ts
new file mode 100644
index 000000000..cc3ce8c7c
--- /dev/null
+++ b/server/lib/notifier/shared/index.ts
@@ -0,0 +1,7 @@
1export * from './abuse'
2export * from './blacklist'
3export * from './comment'
4export * from './common'
5export * from './follow'
6export * from './instance'
7export * from './video-publication'
diff --git a/server/lib/notifier/shared/instance/index.ts b/server/lib/notifier/shared/instance/index.ts
new file mode 100644
index 000000000..c3bb22aec
--- /dev/null
+++ b/server/lib/notifier/shared/instance/index.ts
@@ -0,0 +1,3 @@
1export * from './new-peertube-version-for-admins'
2export * from './new-plugin-version-for-admins'
3export * from './registration-for-moderators'
diff --git a/server/lib/notifier/shared/instance/new-peertube-version-for-admins.ts b/server/lib/notifier/shared/instance/new-peertube-version-for-admins.ts
new file mode 100644
index 000000000..ab5bfb1ac
--- /dev/null
+++ b/server/lib/notifier/shared/instance/new-peertube-version-for-admins.ts
@@ -0,0 +1,54 @@
1import { logger } from '@server/helpers/logger'
2import { UserModel } from '@server/models/user/user'
3import { UserNotificationModel } from '@server/models/user/user-notification'
4import { MApplication, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
5import { UserNotificationType, UserRight } from '@shared/models'
6import { AbstractNotification } from '../common/abstract-notification'
7
8export type NewPeerTubeVersionForAdminsPayload = {
9 application: MApplication
10 latestVersion: string
11}
12
13export class NewPeerTubeVersionForAdmins extends AbstractNotification <NewPeerTubeVersionForAdminsPayload> {
14 private admins: MUserDefault[]
15
16 async prepare () {
17 // Use the debug right to know who is an administrator
18 this.admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG)
19 }
20
21 log () {
22 logger.info('Notifying %s admins of new PeerTube version %s.', this.admins.length, this.payload.latestVersion)
23 }
24
25 getSetting (user: MUserWithNotificationSetting) {
26 return user.NotificationSetting.newPeerTubeVersion
27 }
28
29 getTargetUsers () {
30 return this.admins
31 }
32
33 async createNotification (user: MUserWithNotificationSetting) {
34 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
35 type: UserNotificationType.NEW_PEERTUBE_VERSION,
36 userId: user.id,
37 applicationId: this.payload.application.id
38 })
39 notification.Application = this.payload.application
40
41 return notification
42 }
43
44 async createEmail (to: string) {
45 return {
46 to,
47 template: 'peertube-version-new',
48 subject: `A new PeerTube version is available: ${this.payload.latestVersion}`,
49 locals: {
50 latestVersion: this.payload.latestVersion
51 }
52 }
53 }
54}
diff --git a/server/lib/notifier/shared/instance/new-plugin-version-for-admins.ts b/server/lib/notifier/shared/instance/new-plugin-version-for-admins.ts
new file mode 100644
index 000000000..e5e456a70
--- /dev/null
+++ b/server/lib/notifier/shared/instance/new-plugin-version-for-admins.ts
@@ -0,0 +1,58 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { UserModel } from '@server/models/user/user'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { MPlugin, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
6import { UserNotificationType, UserRight } from '@shared/models'
7import { AbstractNotification } from '../common/abstract-notification'
8
9export class NewPluginVersionForAdmins extends AbstractNotification <MPlugin> {
10 private admins: MUserDefault[]
11
12 async prepare () {
13 // Use the debug right to know who is an administrator
14 this.admins = await UserModel.listWithRight(UserRight.MANAGE_DEBUG)
15 }
16
17 log () {
18 logger.info('Notifying %s admins of new PeerTube version %s.', this.admins.length, this.payload.latestVersion)
19 }
20
21 getSetting (user: MUserWithNotificationSetting) {
22 return user.NotificationSetting.newPluginVersion
23 }
24
25 getTargetUsers () {
26 return this.admins
27 }
28
29 async createNotification (user: MUserWithNotificationSetting) {
30 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
31 type: UserNotificationType.NEW_PLUGIN_VERSION,
32 userId: user.id,
33 pluginId: this.plugin.id
34 })
35 notification.Plugin = this.plugin
36
37 return notification
38 }
39
40 async createEmail (to: string) {
41 const pluginUrl = WEBSERVER.URL + '/admin/plugins/list-installed?pluginType=' + this.plugin.type
42
43 return {
44 to,
45 template: 'plugin-version-new',
46 subject: `A new plugin/theme version is available: ${this.plugin.name}@${this.plugin.latestVersion}`,
47 locals: {
48 pluginName: this.plugin.name,
49 latestVersion: this.plugin.latestVersion,
50 pluginUrl
51 }
52 }
53 }
54
55 private get plugin () {
56 return this.payload
57 }
58}
diff --git a/server/lib/notifier/shared/instance/registration-for-moderators.ts b/server/lib/notifier/shared/instance/registration-for-moderators.ts
new file mode 100644
index 000000000..4deb5a2cc
--- /dev/null
+++ b/server/lib/notifier/shared/instance/registration-for-moderators.ts
@@ -0,0 +1,49 @@
1import { logger } from '@server/helpers/logger'
2import { CONFIG } from '@server/initializers/config'
3import { UserModel } from '@server/models/user/user'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models'
6import { UserNotificationType, UserRight } from '@shared/models'
7import { AbstractNotification } from '../common/abstract-notification'
8
9export class RegistrationForModerators extends AbstractNotification <MUserDefault> {
10 private moderators: MUserDefault[]
11
12 async prepare () {
13 this.moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS)
14 }
15
16 log () {
17 logger.info('Notifying %s moderators of new user registration of %s.', this.moderators.length, this.payload.username)
18 }
19
20 getSetting (user: MUserWithNotificationSetting) {
21 return user.NotificationSetting.newUserRegistration
22 }
23
24 getTargetUsers () {
25 return this.moderators
26 }
27
28 async createNotification (user: MUserWithNotificationSetting) {
29 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
30 type: UserNotificationType.NEW_USER_REGISTRATION,
31 userId: user.id,
32 accountId: this.payload.Account.id
33 })
34 notification.Account = this.payload.Account
35
36 return notification
37 }
38
39 async createEmail (to: string) {
40 return {
41 template: 'user-registered',
42 to,
43 subject: `a new user registered on ${CONFIG.INSTANCE.NAME}: ${this.payload.username}`,
44 locals: {
45 user: this.payload
46 }
47 }
48 }
49}
diff --git a/server/lib/notifier/shared/video-publication/abstract-owned-video-publication.ts b/server/lib/notifier/shared/video-publication/abstract-owned-video-publication.ts
new file mode 100644
index 000000000..fd06e080d
--- /dev/null
+++ b/server/lib/notifier/shared/video-publication/abstract-owned-video-publication.ts
@@ -0,0 +1,57 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { UserModel } from '@server/models/user/user'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { MUserDefault, MUserWithNotificationSetting, MVideoFullLight, UserNotificationModelForApi } from '@server/types/models'
6import { UserNotificationType } from '@shared/models'
7import { AbstractNotification } from '../common/abstract-notification'
8
9export abstract class AbstractOwnedVideoPublication extends AbstractNotification <MVideoFullLight> {
10 protected user: MUserDefault
11
12 async prepare () {
13 this.user = await UserModel.loadByVideoId(this.payload.id)
14 }
15
16 log () {
17 logger.info('Notifying user %s of the publication of its video %s.', this.user.username, this.payload.url)
18 }
19
20 getSetting (user: MUserWithNotificationSetting) {
21 return user.NotificationSetting.myVideoPublished
22 }
23
24 getTargetUsers () {
25 if (!this.user) return []
26
27 return [ this.user ]
28 }
29
30 async createNotification (user: MUserWithNotificationSetting) {
31 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
32 type: UserNotificationType.MY_VIDEO_PUBLISHED,
33 userId: user.id,
34 videoId: this.payload.id
35 })
36 notification.Video = this.payload
37
38 return notification
39 }
40
41 createEmail (to: string) {
42 const videoUrl = WEBSERVER.URL + this.payload.getWatchStaticPath()
43
44 return {
45 to,
46 subject: `Your video ${this.payload.name} has been published`,
47 text: `Your video "${this.payload.name}" has been published.`,
48 locals: {
49 title: 'You video is live',
50 action: {
51 text: 'View video',
52 url: videoUrl
53 }
54 }
55 }
56 }
57}
diff --git a/server/lib/notifier/shared/video-publication/import-finished-for-owner.ts b/server/lib/notifier/shared/video-publication/import-finished-for-owner.ts
new file mode 100644
index 000000000..9f374b6f9
--- /dev/null
+++ b/server/lib/notifier/shared/video-publication/import-finished-for-owner.ts
@@ -0,0 +1,97 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { UserModel } from '@server/models/user/user'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { MUserDefault, MUserWithNotificationSetting, MVideoImportVideo, UserNotificationModelForApi } from '@server/types/models'
6import { UserNotificationType } from '@shared/models'
7import { AbstractNotification } from '../common/abstract-notification'
8
9export type ImportFinishedForOwnerPayload = {
10 videoImport: MVideoImportVideo
11 success: boolean
12}
13
14export class ImportFinishedForOwner extends AbstractNotification <ImportFinishedForOwnerPayload> {
15 private user: MUserDefault
16
17 async prepare () {
18 this.user = await UserModel.loadByVideoImportId(this.videoImport.id)
19 }
20
21 log () {
22 logger.info('Notifying user %s its video import %s is finished.', this.user.username, this.videoImport.getTargetIdentifier())
23 }
24
25 getSetting (user: MUserWithNotificationSetting) {
26 return user.NotificationSetting.myVideoImportFinished
27 }
28
29 getTargetUsers () {
30 if (!this.user) return []
31
32 return [ this.user ]
33 }
34
35 async createNotification (user: MUserWithNotificationSetting) {
36 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
37 type: this.payload.success
38 ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS
39 : UserNotificationType.MY_VIDEO_IMPORT_ERROR,
40
41 userId: user.id,
42 videoImportId: this.videoImport.id
43 })
44 notification.VideoImport = this.videoImport
45
46 return notification
47 }
48
49 createEmail (to: string) {
50 if (this.payload.success) return this.createSuccessEmail(to)
51
52 return this.createFailEmail(to)
53 }
54
55 private createSuccessEmail (to: string) {
56 const videoUrl = WEBSERVER.URL + this.videoImport.Video.getWatchStaticPath()
57
58 return {
59 to,
60 subject: `Your video import ${this.videoImport.getTargetIdentifier()} is complete`,
61 text: `Your video "${this.videoImport.getTargetIdentifier()}" just finished importing.`,
62 locals: {
63 title: 'Import complete',
64 action: {
65 text: 'View video',
66 url: videoUrl
67 }
68 }
69 }
70 }
71
72 private createFailEmail (to: string) {
73 const importUrl = WEBSERVER.URL + '/my-library/video-imports'
74
75 const text =
76 `Your video import "${this.videoImport.getTargetIdentifier()}" encountered an error.` +
77 '\n\n' +
78 `See your videos import dashboard for more information: <a href="${importUrl}">${importUrl}</a>.`
79
80 return {
81 to,
82 subject: `Your video import "${this.videoImport.getTargetIdentifier()}" encountered an error`,
83 text,
84 locals: {
85 title: 'Import failed',
86 action: {
87 text: 'Review imports',
88 url: importUrl
89 }
90 }
91 }
92 }
93
94 private get videoImport () {
95 return this.payload.videoImport
96 }
97}
diff --git a/server/lib/notifier/shared/video-publication/index.ts b/server/lib/notifier/shared/video-publication/index.ts
new file mode 100644
index 000000000..940774504
--- /dev/null
+++ b/server/lib/notifier/shared/video-publication/index.ts
@@ -0,0 +1,5 @@
1export * from './new-video-for-subscribers'
2export * from './import-finished-for-owner'
3export * from './owned-publication-after-auto-unblacklist'
4export * from './owned-publication-after-schedule-update'
5export * from './owned-publication-after-transcoding'
diff --git a/server/lib/notifier/shared/video-publication/new-video-for-subscribers.ts b/server/lib/notifier/shared/video-publication/new-video-for-subscribers.ts
new file mode 100644
index 000000000..4253a0930
--- /dev/null
+++ b/server/lib/notifier/shared/video-publication/new-video-for-subscribers.ts
@@ -0,0 +1,61 @@
1import { logger } from '@server/helpers/logger'
2import { WEBSERVER } from '@server/initializers/constants'
3import { UserModel } from '@server/models/user/user'
4import { UserNotificationModel } from '@server/models/user/user-notification'
5import { MUserWithNotificationSetting, MVideoAccountLight, UserNotificationModelForApi } from '@server/types/models'
6import { UserNotificationType, VideoPrivacy, VideoState } from '@shared/models'
7import { AbstractNotification } from '../common/abstract-notification'
8
9export class NewVideoForSubscribers extends AbstractNotification <MVideoAccountLight> {
10 private users: MUserWithNotificationSetting[]
11
12 async prepare () {
13 // List all followers that are users
14 this.users = await UserModel.listUserSubscribersOf(this.payload.VideoChannel.actorId)
15 }
16
17 log () {
18 logger.info('Notifying %d users of new video %s.', this.users.length, this.payload.url)
19 }
20
21 isDisabled () {
22 return this.payload.privacy !== VideoPrivacy.PUBLIC || this.payload.state !== VideoState.PUBLISHED || this.payload.isBlacklisted()
23 }
24
25 getSetting (user: MUserWithNotificationSetting) {
26 return user.NotificationSetting.newVideoFromSubscription
27 }
28
29 getTargetUsers () {
30 return this.users
31 }
32
33 async createNotification (user: MUserWithNotificationSetting) {
34 const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
35 type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION,
36 userId: user.id,
37 videoId: this.payload.id
38 })
39 notification.Video = this.payload
40
41 return notification
42 }
43
44 createEmail (to: string) {
45 const channelName = this.payload.VideoChannel.getDisplayName()
46 const videoUrl = WEBSERVER.URL + this.payload.getWatchStaticPath()
47
48 return {
49 to,
50 subject: channelName + ' just published a new video',
51 text: `Your subscription ${channelName} just published a new video: "${this.payload.name}".`,
52 locals: {
53 title: 'New content ',
54 action: {
55 text: 'View video',
56 url: videoUrl
57 }
58 }
59 }
60 }
61}
diff --git a/server/lib/notifier/shared/video-publication/owned-publication-after-auto-unblacklist.ts b/server/lib/notifier/shared/video-publication/owned-publication-after-auto-unblacklist.ts
new file mode 100644
index 000000000..27d89a5c7
--- /dev/null
+++ b/server/lib/notifier/shared/video-publication/owned-publication-after-auto-unblacklist.ts
@@ -0,0 +1,11 @@
1
2import { VideoState } from '@shared/models'
3import { AbstractOwnedVideoPublication } from './abstract-owned-video-publication'
4
5export class OwnedPublicationAfterAutoUnblacklist extends AbstractOwnedVideoPublication {
6
7 isDisabled () {
8 // Don't notify if video is still waiting for transcoding or scheduled update
9 return !!this.payload.ScheduleVideoUpdate || (this.payload.waitTranscoding && this.payload.state !== VideoState.PUBLISHED)
10 }
11}
diff --git a/server/lib/notifier/shared/video-publication/owned-publication-after-schedule-update.ts b/server/lib/notifier/shared/video-publication/owned-publication-after-schedule-update.ts
new file mode 100644
index 000000000..2e253b358
--- /dev/null
+++ b/server/lib/notifier/shared/video-publication/owned-publication-after-schedule-update.ts
@@ -0,0 +1,10 @@
1import { VideoState } from '@shared/models'
2import { AbstractOwnedVideoPublication } from './abstract-owned-video-publication'
3
4export class OwnedPublicationAfterScheduleUpdate extends AbstractOwnedVideoPublication {
5
6 isDisabled () {
7 // Don't notify if video is still blacklisted or waiting for transcoding
8 return !!this.payload.VideoBlacklist || (this.payload.waitTranscoding && this.payload.state !== VideoState.PUBLISHED)
9 }
10}
diff --git a/server/lib/notifier/shared/video-publication/owned-publication-after-transcoding.ts b/server/lib/notifier/shared/video-publication/owned-publication-after-transcoding.ts
new file mode 100644
index 000000000..4fab1090f
--- /dev/null
+++ b/server/lib/notifier/shared/video-publication/owned-publication-after-transcoding.ts
@@ -0,0 +1,9 @@
1import { AbstractOwnedVideoPublication } from './abstract-owned-video-publication'
2
3export class OwnedPublicationAfterTranscoding extends AbstractOwnedVideoPublication {
4
5 isDisabled () {
6 // Don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update
7 return !this.payload.waitTranscoding || !!this.payload.VideoBlacklist || !!this.payload.ScheduleVideoUpdate
8 }
9}
diff --git a/server/lib/plugins/register-helpers.ts b/server/lib/plugins/register-helpers.ts
index 09275f9ba..af533effd 100644
--- a/server/lib/plugins/register-helpers.ts
+++ b/server/lib/plugins/register-helpers.ts
@@ -1,13 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { logger } from '@server/helpers/logger' 2import { logger } from '@server/helpers/logger'
3import {
4 VIDEO_CATEGORIES,
5 VIDEO_LANGUAGES,
6 VIDEO_LICENCES,
7 VIDEO_PLAYLIST_PRIVACIES,
8 VIDEO_PRIVACIES
9} from '@server/initializers/constants'
10import { onExternalUserAuthenticated } from '@server/lib/auth/external-auth' 3import { onExternalUserAuthenticated } from '@server/lib/auth/external-auth'
4import { VideoConstantManagerFactory } from '@server/lib/plugins/video-constant-manager-factory'
11import { PluginModel } from '@server/models/server/plugin' 5import { PluginModel } from '@server/models/server/plugin'
12import { 6import {
13 RegisterServerAuthExternalOptions, 7 RegisterServerAuthExternalOptions,
@@ -18,41 +12,18 @@ import {
18} from '@server/types/plugins' 12} from '@server/types/plugins'
19import { 13import {
20 EncoderOptionsBuilder, 14 EncoderOptionsBuilder,
21 PluginPlaylistPrivacyManager,
22 PluginSettingsManager, 15 PluginSettingsManager,
23 PluginStorageManager, 16 PluginStorageManager,
24 PluginVideoCategoryManager,
25 PluginVideoLanguageManager,
26 PluginVideoLicenceManager,
27 PluginVideoPrivacyManager,
28 RegisterServerHookOptions, 17 RegisterServerHookOptions,
29 RegisterServerSettingOptions, 18 RegisterServerSettingOptions,
30 serverHookObject 19 serverHookObject,
20 VideoPlaylistPrivacy,
21 VideoPrivacy
31} from '@shared/models' 22} from '@shared/models'
32import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles' 23import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles'
33import { buildPluginHelpers } from './plugin-helpers-builder' 24import { buildPluginHelpers } from './plugin-helpers-builder'
34 25
35type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
36type VideoConstant = { [key in number | string]: string }
37
38type UpdatedVideoConstant = {
39 [name in AlterableVideoConstant]: {
40 [ npmName: string]: {
41 added: { key: number | string, label: string }[]
42 deleted: { key: number | string, label: string }[]
43 }
44 }
45}
46
47export class RegisterHelpers { 26export class RegisterHelpers {
48 private readonly updatedVideoConstants: UpdatedVideoConstant = {
49 playlistPrivacy: { },
50 privacy: { },
51 language: { },
52 licence: { },
53 category: { }
54 }
55
56 private readonly transcodingProfiles: { 27 private readonly transcodingProfiles: {
57 [ npmName: string ]: { 28 [ npmName: string ]: {
58 type: 'vod' | 'live' 29 type: 'vod' | 'live'
@@ -78,6 +49,7 @@ export class RegisterHelpers {
78 private readonly onSettingsChangeCallbacks: ((settings: any) => Promise<any>)[] = [] 49 private readonly onSettingsChangeCallbacks: ((settings: any) => Promise<any>)[] = []
79 50
80 private readonly router: express.Router 51 private readonly router: express.Router
52 private readonly videoConstantManagerFactory: VideoConstantManagerFactory
81 53
82 constructor ( 54 constructor (
83 private readonly npmName: string, 55 private readonly npmName: string,
@@ -85,6 +57,7 @@ export class RegisterHelpers {
85 private readonly onHookAdded: (options: RegisterServerHookOptions) => void 57 private readonly onHookAdded: (options: RegisterServerHookOptions) => void
86 ) { 58 ) {
87 this.router = express.Router() 59 this.router = express.Router()
60 this.videoConstantManagerFactory = new VideoConstantManagerFactory(this.npmName)
88 } 61 }
89 62
90 buildRegisterHelpers (): RegisterServerOptions { 63 buildRegisterHelpers (): RegisterServerOptions {
@@ -96,13 +69,13 @@ export class RegisterHelpers {
96 const settingsManager = this.buildSettingsManager() 69 const settingsManager = this.buildSettingsManager()
97 const storageManager = this.buildStorageManager() 70 const storageManager = this.buildStorageManager()
98 71
99 const videoLanguageManager = this.buildVideoLanguageManager() 72 const videoLanguageManager = this.videoConstantManagerFactory.createVideoConstantManager<string>('language')
100 73
101 const videoLicenceManager = this.buildVideoLicenceManager() 74 const videoLicenceManager = this.videoConstantManagerFactory.createVideoConstantManager<number>('licence')
102 const videoCategoryManager = this.buildVideoCategoryManager() 75 const videoCategoryManager = this.videoConstantManagerFactory.createVideoConstantManager<number>('category')
103 76
104 const videoPrivacyManager = this.buildVideoPrivacyManager() 77 const videoPrivacyManager = this.videoConstantManagerFactory.createVideoConstantManager<VideoPrivacy>('privacy')
105 const playlistPrivacyManager = this.buildPlaylistPrivacyManager() 78 const playlistPrivacyManager = this.videoConstantManagerFactory.createVideoConstantManager<VideoPlaylistPrivacy>('playlistPrivacy')
106 79
107 const transcodingManager = this.buildTranscodingManager() 80 const transcodingManager = this.buildTranscodingManager()
108 81
@@ -122,12 +95,38 @@ export class RegisterHelpers {
122 settingsManager, 95 settingsManager,
123 storageManager, 96 storageManager,
124 97
125 videoLanguageManager, 98 videoLanguageManager: {
126 videoCategoryManager, 99 ...videoLanguageManager,
127 videoLicenceManager, 100 /** @deprecated use `addConstant` instead **/
101 addLanguage: videoLanguageManager.addConstant,
102 /** @deprecated use `deleteConstant` instead **/
103 deleteLanguage: videoLanguageManager.deleteConstant
104 },
105 videoCategoryManager: {
106 ...videoCategoryManager,
107 /** @deprecated use `addConstant` instead **/
108 addCategory: videoCategoryManager.addConstant,
109 /** @deprecated use `deleteConstant` instead **/
110 deleteCategory: videoCategoryManager.deleteConstant
111 },
112 videoLicenceManager: {
113 ...videoLicenceManager,
114 /** @deprecated use `addConstant` instead **/
115 addLicence: videoLicenceManager.addConstant,
116 /** @deprecated use `deleteConstant` instead **/
117 deleteLicence: videoLicenceManager.deleteConstant
118 },
128 119
129 videoPrivacyManager, 120 videoPrivacyManager: {
130 playlistPrivacyManager, 121 ...videoPrivacyManager,
122 /** @deprecated use `deleteConstant` instead **/
123 deletePrivacy: videoPrivacyManager.deleteConstant
124 },
125 playlistPrivacyManager: {
126 ...playlistPrivacyManager,
127 /** @deprecated use `deleteConstant` instead **/
128 deletePlaylistPrivacy: playlistPrivacyManager.deleteConstant
129 },
131 130
132 transcodingManager, 131 transcodingManager,
133 132
@@ -141,29 +140,7 @@ export class RegisterHelpers {
141 } 140 }
142 141
143 reinitVideoConstants (npmName: string) { 142 reinitVideoConstants (npmName: string) {
144 const hash = { 143 this.videoConstantManagerFactory.resetVideoConstants(npmName)
145 language: VIDEO_LANGUAGES,
146 licence: VIDEO_LICENCES,
147 category: VIDEO_CATEGORIES,
148 privacy: VIDEO_PRIVACIES,
149 playlistPrivacy: VIDEO_PLAYLIST_PRIVACIES
150 }
151 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category', 'privacy', 'playlistPrivacy' ]
152
153 for (const type of types) {
154 const updatedConstants = this.updatedVideoConstants[type][npmName]
155 if (!updatedConstants) continue
156
157 for (const added of updatedConstants.added) {
158 delete hash[type][added.key]
159 }
160
161 for (const deleted of updatedConstants.deleted) {
162 hash[type][deleted.key] = deleted.label
163 }
164
165 delete this.updatedVideoConstants[type][npmName]
166 }
167 } 144 }
168 145
169 reinitTranscodingProfilesAndEncoders (npmName: string) { 146 reinitTranscodingProfilesAndEncoders (npmName: string) {
@@ -291,119 +268,6 @@ export class RegisterHelpers {
291 } 268 }
292 } 269 }
293 270
294 private buildVideoLanguageManager (): PluginVideoLanguageManager {
295 return {
296 addLanguage: (key: string, label: string) => {
297 return this.addConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key, label })
298 },
299
300 deleteLanguage: (key: string) => {
301 return this.deleteConstant({ npmName: this.npmName, type: 'language', obj: VIDEO_LANGUAGES, key })
302 }
303 }
304 }
305
306 private buildVideoCategoryManager (): PluginVideoCategoryManager {
307 return {
308 addCategory: (key: number, label: string) => {
309 return this.addConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key, label })
310 },
311
312 deleteCategory: (key: number) => {
313 return this.deleteConstant({ npmName: this.npmName, type: 'category', obj: VIDEO_CATEGORIES, key })
314 }
315 }
316 }
317
318 private buildVideoPrivacyManager (): PluginVideoPrivacyManager {
319 return {
320 deletePrivacy: (key: number) => {
321 return this.deleteConstant({ npmName: this.npmName, type: 'privacy', obj: VIDEO_PRIVACIES, key })
322 }
323 }
324 }
325
326 private buildPlaylistPrivacyManager (): PluginPlaylistPrivacyManager {
327 return {
328 deletePlaylistPrivacy: (key: number) => {
329 return this.deleteConstant({ npmName: this.npmName, type: 'playlistPrivacy', obj: VIDEO_PLAYLIST_PRIVACIES, key })
330 }
331 }
332 }
333
334 private buildVideoLicenceManager (): PluginVideoLicenceManager {
335 return {
336 addLicence: (key: number, label: string) => {
337 return this.addConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key, label })
338 },
339
340 deleteLicence: (key: number) => {
341 return this.deleteConstant({ npmName: this.npmName, type: 'licence', obj: VIDEO_LICENCES, key })
342 }
343 }
344 }
345
346 private addConstant<T extends string | number> (parameters: {
347 npmName: string
348 type: AlterableVideoConstant
349 obj: VideoConstant
350 key: T
351 label: string
352 }) {
353 const { npmName, type, obj, key, label } = parameters
354
355 if (obj[key]) {
356 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
357 return false
358 }
359
360 if (!this.updatedVideoConstants[type][npmName]) {
361 this.updatedVideoConstants[type][npmName] = {
362 added: [],
363 deleted: []
364 }
365 }
366
367 this.updatedVideoConstants[type][npmName].added.push({ key, label })
368 obj[key] = label
369
370 return true
371 }
372
373 private deleteConstant<T extends string | number> (parameters: {
374 npmName: string
375 type: AlterableVideoConstant
376 obj: VideoConstant
377 key: T
378 }) {
379 const { npmName, type, obj, key } = parameters
380
381 if (!obj[key]) {
382 logger.warn('Cannot delete %s by plugin %s: key %s does not exist.', type, npmName, key)
383 return false
384 }
385
386 if (!this.updatedVideoConstants[type][npmName]) {
387 this.updatedVideoConstants[type][npmName] = {
388 added: [],
389 deleted: []
390 }
391 }
392
393 const updatedConstants = this.updatedVideoConstants[type][npmName]
394
395 const alreadyAdded = updatedConstants.added.find(a => a.key === key)
396 if (alreadyAdded) {
397 updatedConstants.added.filter(a => a.key !== key)
398 } else if (obj[key]) {
399 updatedConstants.deleted.push({ key, label: obj[key] })
400 }
401
402 delete obj[key]
403
404 return true
405 }
406
407 private buildTranscodingManager () { 271 private buildTranscodingManager () {
408 const self = this 272 const self = this
409 273
diff --git a/server/lib/plugins/video-constant-manager-factory.ts b/server/lib/plugins/video-constant-manager-factory.ts
new file mode 100644
index 000000000..f04dde29f
--- /dev/null
+++ b/server/lib/plugins/video-constant-manager-factory.ts
@@ -0,0 +1,139 @@
1import { logger } from '@server/helpers/logger'
2import {
3 VIDEO_CATEGORIES,
4 VIDEO_LANGUAGES,
5 VIDEO_LICENCES,
6 VIDEO_PLAYLIST_PRIVACIES,
7 VIDEO_PRIVACIES
8} from '@server/initializers/constants'
9import { ConstantManager } from '@shared/models/plugins/server/plugin-constant-manager.model'
10
11type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
12type VideoConstant = Record<number | string, string>
13
14type UpdatedVideoConstant = {
15 [name in AlterableVideoConstant]: {
16 [ npmName: string]: {
17 added: VideoConstant[]
18 deleted: VideoConstant[]
19 }
20 }
21}
22
23const constantsHash: { [key in AlterableVideoConstant]: VideoConstant } = {
24 language: VIDEO_LANGUAGES,
25 licence: VIDEO_LICENCES,
26 category: VIDEO_CATEGORIES,
27 privacy: VIDEO_PRIVACIES,
28 playlistPrivacy: VIDEO_PLAYLIST_PRIVACIES
29}
30
31export class VideoConstantManagerFactory {
32 private readonly updatedVideoConstants: UpdatedVideoConstant = {
33 playlistPrivacy: { },
34 privacy: { },
35 language: { },
36 licence: { },
37 category: { }
38 }
39
40 constructor (
41 private readonly npmName: string
42 ) {}
43
44 public resetVideoConstants (npmName: string) {
45 const types: AlterableVideoConstant[] = [ 'language', 'licence', 'category', 'privacy', 'playlistPrivacy' ]
46 for (const type of types) {
47 this.resetConstants({ npmName, type })
48 }
49 }
50
51 private resetConstants (parameters: { npmName: string, type: AlterableVideoConstant }) {
52 const { npmName, type } = parameters
53 const updatedConstants = this.updatedVideoConstants[type][npmName]
54
55 if (!updatedConstants) return
56
57 for (const added of updatedConstants.added) {
58 delete constantsHash[type][added.key]
59 }
60
61 for (const deleted of updatedConstants.deleted) {
62 constantsHash[type][deleted.key] = deleted.label
63 }
64
65 delete this.updatedVideoConstants[type][npmName]
66 }
67
68 public createVideoConstantManager<K extends number | string>(type: AlterableVideoConstant): ConstantManager<K> {
69 const { npmName } = this
70 return {
71 addConstant: (key: K, label: string) => this.addConstant({ npmName, type, key, label }),
72 deleteConstant: (key: K) => this.deleteConstant({ npmName, type, key }),
73 getConstantValue: (key: K) => constantsHash[type][key],
74 getConstants: () => constantsHash[type] as Record<K, string>,
75 resetConstants: () => this.resetConstants({ npmName, type })
76 }
77 }
78
79 private addConstant<T extends string | number> (parameters: {
80 npmName: string
81 type: AlterableVideoConstant
82 key: T
83 label: string
84 }) {
85 const { npmName, type, key, label } = parameters
86 const obj = constantsHash[type]
87
88 if (obj[key]) {
89 logger.warn('Cannot add %s %s by plugin %s: key already exists.', type, npmName, key)
90 return false
91 }
92
93 if (!this.updatedVideoConstants[type][npmName]) {
94 this.updatedVideoConstants[type][npmName] = {
95 added: [],
96 deleted: []
97 }
98 }
99
100 this.updatedVideoConstants[type][npmName].added.push({ key: key, label } as VideoConstant)
101 obj[key] = label
102
103 return true
104 }
105
106 private deleteConstant<T extends string | number> (parameters: {
107 npmName: string
108 type: AlterableVideoConstant
109 key: T
110 }) {
111 const { npmName, type, key } = parameters
112 const obj = constantsHash[type]
113
114 if (!obj[key]) {
115 logger.warn('Cannot delete %s by plugin %s: key %s does not exist.', type, npmName, key)
116 return false
117 }
118
119 if (!this.updatedVideoConstants[type][npmName]) {
120 this.updatedVideoConstants[type][npmName] = {
121 added: [],
122 deleted: []
123 }
124 }
125
126 const updatedConstants = this.updatedVideoConstants[type][npmName]
127
128 const alreadyAdded = updatedConstants.added.find(a => a.key === key)
129 if (alreadyAdded) {
130 updatedConstants.added.filter(a => a.key !== key)
131 } else if (obj[key]) {
132 updatedConstants.deleted.push({ key, label: obj[key] } as VideoConstant)
133 }
134
135 delete obj[key]
136
137 return true
138 }
139}
diff --git a/server/lib/schedulers/plugins-check-scheduler.ts b/server/lib/schedulers/plugins-check-scheduler.ts
index 9a1ae3ec5..c95e109b0 100644
--- a/server/lib/schedulers/plugins-check-scheduler.ts
+++ b/server/lib/schedulers/plugins-check-scheduler.ts
@@ -1,12 +1,12 @@
1import { chunk } from 'lodash'
2import { compareSemVer } from '@shared/core-utils'
1import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
2import { AbstractScheduler } from './abstract-scheduler'
3import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
4import { CONFIG } from '../../initializers/config' 4import { CONFIG } from '../../initializers/config'
5import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
5import { PluginModel } from '../../models/server/plugin' 6import { PluginModel } from '../../models/server/plugin'
6import { chunk } from 'lodash'
7import { getLatestPluginsVersion } from '../plugins/plugin-index'
8import { compareSemVer } from '../../../shared/core-utils/miscs/miscs'
9import { Notifier } from '../notifier' 7import { Notifier } from '../notifier'
8import { getLatestPluginsVersion } from '../plugins/plugin-index'
9import { AbstractScheduler } from './abstract-scheduler'
10 10
11export class PluginsCheckScheduler extends AbstractScheduler { 11export class PluginsCheckScheduler extends AbstractScheduler {
12 12
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index b5a5eb697..103ab1fab 100644
--- a/server/lib/schedulers/videos-redundancy-scheduler.ts
+++ b/server/lib/schedulers/videos-redundancy-scheduler.ts
@@ -267,7 +267,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
267 logger.info('Duplicating %s streaming playlist in videos redundancy with "%s" strategy.', video.url, strategy) 267 logger.info('Duplicating %s streaming playlist in videos redundancy with "%s" strategy.', video.url, strategy)
268 268
269 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) 269 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid)
270 await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) 270 const masterPlaylistUrl = playlist.getMasterPlaylistUrl(video)
271 await downloadPlaylistSegments(masterPlaylistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT)
271 272
272 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ 273 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
273 expiresOn, 274 expiresOn,
@@ -282,7 +283,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
282 283
283 await sendCreateCacheFile(serverActor, video, createdModel) 284 await sendCreateCacheFile(serverActor, video, createdModel)
284 285
285 logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) 286 logger.info('Duplicated playlist %s -> %s.', masterPlaylistUrl, createdModel.url)
286 } 287 }
287 288
288 private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) { 289 private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) {
@@ -330,7 +331,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
330 private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) { 331 private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) {
331 if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` 332 if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
332 333
333 return `${object.VideoStreamingPlaylist.playlistUrl}` 334 return `${object.VideoStreamingPlaylist.getMasterPlaylistUrl(object.VideoStreamingPlaylist.Video)}`
334 } 335 }
335 336
336 private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylistFiles[]) { 337 private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylistFiles[]) {
diff --git a/server/lib/transcoding/video-transcoding.ts b/server/lib/transcoding/video-transcoding.ts
index 1ad63baf3..d2a556360 100644
--- a/server/lib/transcoding/video-transcoding.ts
+++ b/server/lib/transcoding/video-transcoding.ts
@@ -10,11 +10,18 @@ import { transcode, TranscodeOptions, TranscodeOptionsType } from '../../helpers
10import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils' 10import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils'
11import { logger } from '../../helpers/logger' 11import { logger } from '../../helpers/logger'
12import { CONFIG } from '../../initializers/config' 12import { CONFIG } from '../../initializers/config'
13import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../../initializers/constants' 13import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants'
14import { VideoFileModel } from '../../models/video/video-file' 14import { VideoFileModel } from '../../models/video/video-file'
15import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' 15import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
16import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls' 16import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls'
17import { generateVideoFilename, generateVideoStreamingPlaylistName, getVideoFilePath } from '../video-paths' 17import {
18 generateHLSMasterPlaylistFilename,
19 generateHlsSha256SegmentsFilename,
20 generateHLSVideoFilename,
21 generateWebTorrentVideoFilename,
22 getHlsResolutionPlaylistFilename,
23 getVideoFilePath
24} from '../video-paths'
18import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' 25import { VideoTranscodingProfilesManager } from './video-transcoding-profiles'
19 26
20/** 27/**
@@ -60,7 +67,7 @@ async function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile
60 67
61 // Important to do this before getVideoFilename() to take in account the new filename 68 // Important to do this before getVideoFilename() to take in account the new filename
62 inputVideoFile.extname = newExtname 69 inputVideoFile.extname = newExtname
63 inputVideoFile.filename = generateVideoFilename(video, false, resolution, newExtname) 70 inputVideoFile.filename = generateWebTorrentVideoFilename(resolution, newExtname)
64 71
65 const videoOutputPath = getVideoFilePath(video, inputVideoFile) 72 const videoOutputPath = getVideoFilePath(video, inputVideoFile)
66 73
@@ -86,7 +93,7 @@ async function transcodeNewWebTorrentResolution (video: MVideoFullLight, resolut
86 const newVideoFile = new VideoFileModel({ 93 const newVideoFile = new VideoFileModel({
87 resolution, 94 resolution,
88 extname, 95 extname,
89 filename: generateVideoFilename(video, false, resolution, extname), 96 filename: generateWebTorrentVideoFilename(resolution, extname),
90 size: 0, 97 size: 0,
91 videoId: video.id 98 videoId: video.id
92 }) 99 })
@@ -169,7 +176,7 @@ async function mergeAudioVideofile (video: MVideoFullLight, resolution: VideoRes
169 176
170 // Important to do this before getVideoFilename() to take in account the new file extension 177 // Important to do this before getVideoFilename() to take in account the new file extension
171 inputVideoFile.extname = newExtname 178 inputVideoFile.extname = newExtname
172 inputVideoFile.filename = generateVideoFilename(video, false, inputVideoFile.resolution, newExtname) 179 inputVideoFile.filename = generateWebTorrentVideoFilename(inputVideoFile.resolution, newExtname)
173 180
174 const videoOutputPath = getVideoFilePath(video, inputVideoFile) 181 const videoOutputPath = getVideoFilePath(video, inputVideoFile)
175 // ffmpeg generated a new video file, so update the video duration 182 // ffmpeg generated a new video file, so update the video duration
@@ -271,15 +278,15 @@ async function generateHlsPlaylistCommon (options: {
271 const videoTranscodedBasePath = join(transcodeDirectory, type) 278 const videoTranscodedBasePath = join(transcodeDirectory, type)
272 await ensureDir(videoTranscodedBasePath) 279 await ensureDir(videoTranscodedBasePath)
273 280
274 const videoFilename = generateVideoStreamingPlaylistName(video.uuid, resolution) 281 const videoFilename = generateHLSVideoFilename(resolution)
275 const playlistFilename = VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution) 282 const resolutionPlaylistFilename = getHlsResolutionPlaylistFilename(videoFilename)
276 const playlistFileTranscodePath = join(videoTranscodedBasePath, playlistFilename) 283 const resolutionPlaylistFileTranscodePath = join(videoTranscodedBasePath, resolutionPlaylistFilename)
277 284
278 const transcodeOptions = { 285 const transcodeOptions = {
279 type, 286 type,
280 287
281 inputPath, 288 inputPath,
282 outputPath: playlistFileTranscodePath, 289 outputPath: resolutionPlaylistFileTranscodePath,
283 290
284 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), 291 availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(),
285 profile: CONFIG.TRANSCODING.PROFILE, 292 profile: CONFIG.TRANSCODING.PROFILE,
@@ -299,19 +306,23 @@ async function generateHlsPlaylistCommon (options: {
299 306
300 await transcode(transcodeOptions) 307 await transcode(transcodeOptions)
301 308
302 const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid)
303
304 // Create or update the playlist 309 // Create or update the playlist
305 const [ videoStreamingPlaylist ] = await VideoStreamingPlaylistModel.upsert({ 310 const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video)
306 videoId: video.id, 311
307 playlistUrl, 312 if (!playlist.playlistFilename) {
308 segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid, video.isLive), 313 playlist.playlistFilename = generateHLSMasterPlaylistFilename(video.isLive)
309 p2pMediaLoaderInfohashes: [], 314 }
310 p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, 315
316 if (!playlist.segmentsSha256Filename) {
317 playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(video.isLive)
318 }
319
320 playlist.p2pMediaLoaderInfohashes = []
321 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
311 322
312 type: VideoStreamingPlaylistType.HLS 323 playlist.type = VideoStreamingPlaylistType.HLS
313 }, { returning: true }) as [ MStreamingPlaylistFilesVideo, boolean ] 324
314 videoStreamingPlaylist.Video = video 325 await playlist.save()
315 326
316 // Build the new playlist file 327 // Build the new playlist file
317 const extname = extnameUtil(videoFilename) 328 const extname = extnameUtil(videoFilename)
@@ -319,20 +330,20 @@ async function generateHlsPlaylistCommon (options: {
319 resolution, 330 resolution,
320 extname, 331 extname,
321 size: 0, 332 size: 0,
322 filename: generateVideoFilename(video, true, resolution, extname), 333 filename: videoFilename,
323 fps: -1, 334 fps: -1,
324 videoStreamingPlaylistId: videoStreamingPlaylist.id 335 videoStreamingPlaylistId: playlist.id
325 }) 336 })
326 337
327 const videoFilePath = getVideoFilePath(videoStreamingPlaylist, newVideoFile) 338 const videoFilePath = getVideoFilePath(playlist, newVideoFile)
328 339
329 // Move files from tmp transcoded directory to the appropriate place 340 // Move files from tmp transcoded directory to the appropriate place
330 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 341 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
331 await ensureDir(baseHlsDirectory) 342 await ensureDir(baseHlsDirectory)
332 343
333 // Move playlist file 344 // Move playlist file
334 const playlistPath = join(baseHlsDirectory, playlistFilename) 345 const resolutionPlaylistPath = join(baseHlsDirectory, resolutionPlaylistFilename)
335 await move(playlistFileTranscodePath, playlistPath, { overwrite: true }) 346 await move(resolutionPlaylistFileTranscodePath, resolutionPlaylistPath, { overwrite: true })
336 // Move video file 347 // Move video file
337 await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true }) 348 await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true })
338 349
@@ -342,20 +353,20 @@ async function generateHlsPlaylistCommon (options: {
342 newVideoFile.fps = await getVideoFileFPS(videoFilePath) 353 newVideoFile.fps = await getVideoFileFPS(videoFilePath)
343 newVideoFile.metadata = await getMetadataFromFile(videoFilePath) 354 newVideoFile.metadata = await getMetadataFromFile(videoFilePath)
344 355
345 await createTorrentAndSetInfoHash(videoStreamingPlaylist, newVideoFile) 356 await createTorrentAndSetInfoHash(playlist, newVideoFile)
346 357
347 await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined) 358 await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined)
348 videoStreamingPlaylist.VideoFiles = await videoStreamingPlaylist.$get('VideoFiles')
349 359
350 videoStreamingPlaylist.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes( 360 const playlistWithFiles = playlist as MStreamingPlaylistFilesVideo
351 playlistUrl, videoStreamingPlaylist.VideoFiles 361 playlistWithFiles.VideoFiles = await playlist.$get('VideoFiles')
352 ) 362 playlist.assignP2PMediaLoaderInfoHashes(video, playlistWithFiles.VideoFiles)
353 await videoStreamingPlaylist.save() 363
364 await playlist.save()
354 365
355 video.setHLSPlaylist(videoStreamingPlaylist) 366 video.setHLSPlaylist(playlist)
356 367
357 await updateMasterHLSPlaylist(video) 368 await updateMasterHLSPlaylist(video, playlistWithFiles)
358 await updateSha256VODSegments(video) 369 await updateSha256VODSegments(video, playlistWithFiles)
359 370
360 return playlistPath 371 return resolutionPlaylistPath
361} 372}
diff --git a/server/lib/video-paths.ts b/server/lib/video-paths.ts
index 1708c479a..1e4382108 100644
--- a/server/lib/video-paths.ts
+++ b/server/lib/video-paths.ts
@@ -3,29 +3,17 @@ import { extractVideo } from '@server/helpers/video'
3import { CONFIG } from '@server/initializers/config' 3import { CONFIG } from '@server/initializers/config'
4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants' 4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants'
5import { isStreamingPlaylist, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models' 5import { isStreamingPlaylist, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models'
6import { buildUUID } from '@server/helpers/uuid'
7import { removeFragmentedMP4Ext } from '@shared/core-utils'
6 8
7// ################## Video file name ################## 9// ################## Video file name ##################
8 10
9function generateVideoFilename (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, isHls: boolean, resolution: number, extname: string) { 11function generateWebTorrentVideoFilename (resolution: number, extname: string) {
10 const video = extractVideo(videoOrPlaylist) 12 return buildUUID() + '-' + resolution + extname
11
12 // FIXME: use a generated uuid instead, that will break compatibility with PeerTube < 3.1
13 // const uuid = uuidv4()
14 const uuid = video.uuid
15
16 if (isHls) {
17 return generateVideoStreamingPlaylistName(uuid, resolution)
18 }
19
20 return generateWebTorrentVideoName(uuid, resolution, extname)
21} 13}
22 14
23function generateVideoStreamingPlaylistName (uuid: string, resolution: number) { 15function generateHLSVideoFilename (resolution: number) {
24 return `${uuid}-${resolution}-fragmented.mp4` 16 return `${buildUUID()}-${resolution}-fragmented.mp4`
25}
26
27function generateWebTorrentVideoName (uuid: string, resolution: number, extname: string) {
28 return uuid + '-' + resolution + extname
29} 17}
30 18
31function getVideoFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile, isRedundancy = false) { 19function getVideoFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile, isRedundancy = false) {
@@ -63,15 +51,28 @@ function getHLSDirectory (video: MVideoUUID, isRedundancy = false) {
63 return join(baseDir, video.uuid) 51 return join(baseDir, video.uuid)
64} 52}
65 53
54function getHlsResolutionPlaylistFilename (videoFilename: string) {
55 // Video file name already contain resolution
56 return removeFragmentedMP4Ext(videoFilename) + '.m3u8'
57}
58
59function generateHLSMasterPlaylistFilename (isLive = false) {
60 if (isLive) return 'master.m3u8'
61
62 return buildUUID() + '-master.m3u8'
63}
64
65function generateHlsSha256SegmentsFilename (isLive = false) {
66 if (isLive) return 'segments-sha256.json'
67
68 return buildUUID() + '-segments-sha256.json'
69}
70
66// ################## Torrents ################## 71// ################## Torrents ##################
67 72
68function generateTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, resolution: number) { 73function generateTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, resolution: number) {
69 const video = extractVideo(videoOrPlaylist)
70 const extension = '.torrent' 74 const extension = '.torrent'
71 75 const uuid = buildUUID()
72 // FIXME: use a generated uuid instead, that will break compatibility with PeerTube < 3.1
73 // const uuid = uuidv4()
74 const uuid = video.uuid
75 76
76 if (isStreamingPlaylist(videoOrPlaylist)) { 77 if (isStreamingPlaylist(videoOrPlaylist)) {
77 return `${uuid}-${resolution}-${videoOrPlaylist.getStringType()}${extension}` 78 return `${uuid}-${resolution}-${videoOrPlaylist.getStringType()}${extension}`
@@ -95,15 +96,18 @@ function getLocalVideoFileMetadataUrl (video: MVideoUUID, videoFile: MVideoFile)
95// --------------------------------------------------------------------------- 96// ---------------------------------------------------------------------------
96 97
97export { 98export {
98 generateVideoStreamingPlaylistName, 99 generateHLSVideoFilename,
99 generateWebTorrentVideoName, 100 generateWebTorrentVideoFilename,
100 generateVideoFilename, 101
101 getVideoFilePath, 102 getVideoFilePath,
102 103
103 generateTorrentFileName, 104 generateTorrentFileName,
104 getTorrentFilePath, 105 getTorrentFilePath,
105 106
106 getHLSDirectory, 107 getHLSDirectory,
108 generateHLSMasterPlaylistFilename,
109 generateHlsSha256SegmentsFilename,
110 getHlsResolutionPlaylistFilename,
107 111
108 getLocalVideoFileMetadataUrl, 112 getLocalVideoFileMetadataUrl,
109 113
diff --git a/server/lib/video.ts b/server/lib/video.ts
index daf998704..61fee4949 100644
--- a/server/lib/video.ts
+++ b/server/lib/video.ts
@@ -5,7 +5,7 @@ import { sequelizeTypescript } from '@server/initializers/database'
5import { TagModel } from '@server/models/video/tag' 5import { TagModel } from '@server/models/video/tag'
6import { VideoModel } from '@server/models/video/video' 6import { VideoModel } from '@server/models/video/video'
7import { FilteredModelAttributes } from '@server/types' 7import { FilteredModelAttributes } from '@server/types'
8import { MThumbnail, MUserId, MVideo, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models' 8import { MThumbnail, MUserId, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models'
9import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models' 9import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models'
10import { federateVideoIfNeeded } from './activitypub/videos' 10import { federateVideoIfNeeded } from './activitypub/videos'
11import { JobQueue } from './job-queue/job-queue' 11import { JobQueue } from './job-queue/job-queue'
@@ -105,7 +105,7 @@ async function publishAndFederateIfNeeded (video: MVideoUUID, wasLive = false) {
105 } 105 }
106} 106}
107 107
108async function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile, user: MUserId) { 108async function addOptimizeOrMergeAudioJob (video: MVideoUUID, videoFile: MVideoFile, user: MUserId) {
109 let dataInput: VideoTranscodingPayload 109 let dataInput: VideoTranscodingPayload
110 110
111 if (videoFile.isAudio()) { 111 if (videoFile.isAudio()) {
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts
index 6b43b7764..6ef90b275 100644
--- a/server/middlewares/activitypub.ts
+++ b/server/middlewares/activitypub.ts
@@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express'
2import { getAPId } from '@server/helpers/activitypub' 2import { getAPId } from '@server/helpers/activitypub'
3import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor' 3import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor'
4import { ActivityDelete, ActivityPubSignature } from '../../shared' 4import { ActivityDelete, ActivityPubSignature } from '../../shared'
5import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
6import { logger } from '../helpers/logger' 6import { logger } from '../helpers/logger'
7import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto' 7import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto'
8import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants' 8import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants'
diff --git a/server/middlewares/auth.ts b/server/middlewares/auth.ts
index 176461cc2..9e6327b23 100644
--- a/server/middlewares/auth.ts
+++ b/server/middlewares/auth.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { Socket } from 'socket.io' 2import { Socket } from 'socket.io'
3import { getAccessToken } from '@server/lib/auth/oauth-model' 3import { getAccessToken } from '@server/lib/auth/oauth-model'
4import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { handleOAuthAuthenticate } from '../lib/auth/oauth' 6import { handleOAuthAuthenticate } from '../lib/auth/oauth'
7 7
diff --git a/server/middlewares/cache.ts b/server/middlewares/cache.ts
deleted file mode 100644
index 0708ee8e8..000000000
--- a/server/middlewares/cache.ts
+++ /dev/null
@@ -1,28 +0,0 @@
1import { Redis } from '../lib/redis'
2import * as apicache from 'apicache'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
4
5// Ensure Redis is initialized
6Redis.Instance.init()
7
8const defaultOptions = {
9 redisClient: Redis.Instance.getClient(),
10 appendKey: () => Redis.Instance.getPrefix(),
11 statusCodes: {
12 exclude: [
13 HttpStatusCode.FORBIDDEN_403,
14 HttpStatusCode.NOT_FOUND_404
15 ]
16 }
17}
18
19const cacheRoute = (extraOptions = {}) => apicache.options({
20 ...defaultOptions,
21 ...extraOptions
22}).middleware
23
24// ---------------------------------------------------------------------------
25
26export {
27 cacheRoute
28}
diff --git a/server/middlewares/cache/cache.ts b/server/middlewares/cache/cache.ts
new file mode 100644
index 000000000..48162a0ae
--- /dev/null
+++ b/server/middlewares/cache/cache.ts
@@ -0,0 +1,32 @@
1import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
2import { Redis } from '../../lib/redis'
3import { ApiCache, APICacheOptions } from './shared'
4
5// Ensure Redis is initialized
6Redis.Instance.init()
7
8const defaultOptions: APICacheOptions = {
9 excludeStatus: [
10 HttpStatusCode.FORBIDDEN_403,
11 HttpStatusCode.NOT_FOUND_404
12 ]
13}
14
15function cacheRoute (duration: string) {
16 const instance = new ApiCache(defaultOptions)
17
18 return instance.buildMiddleware(duration)
19}
20
21function cacheRouteFactory (options: APICacheOptions) {
22 const instance = new ApiCache({ ...defaultOptions, ...options })
23
24 return instance.buildMiddleware.bind(instance)
25}
26
27// ---------------------------------------------------------------------------
28
29export {
30 cacheRoute,
31 cacheRouteFactory
32}
diff --git a/server/middlewares/cache/index.ts b/server/middlewares/cache/index.ts
new file mode 100644
index 000000000..79b512828
--- /dev/null
+++ b/server/middlewares/cache/index.ts
@@ -0,0 +1 @@
export * from './cache'
diff --git a/server/middlewares/cache/shared/api-cache.ts b/server/middlewares/cache/shared/api-cache.ts
new file mode 100644
index 000000000..f9f7b1b67
--- /dev/null
+++ b/server/middlewares/cache/shared/api-cache.ts
@@ -0,0 +1,269 @@
1// Thanks: https://github.com/kwhitley/apicache
2// We duplicated the library because it is unmaintened and prevent us to upgrade to recent NodeJS versions
3
4import * as express from 'express'
5import { OutgoingHttpHeaders } from 'http'
6import { isTestInstance, parseDurationToMs } from '@server/helpers/core-utils'
7import { logger } from '@server/helpers/logger'
8import { Redis } from '@server/lib/redis'
9import { HttpStatusCode } from '@shared/models'
10
11export interface APICacheOptions {
12 headerBlacklist?: string[]
13 excludeStatus?: HttpStatusCode[]
14}
15
16interface CacheObject {
17 status: number
18 headers: OutgoingHttpHeaders
19 data: any
20 encoding: BufferEncoding
21 timestamp: number
22}
23
24export class ApiCache {
25
26 private readonly options: APICacheOptions
27 private readonly timers: { [ id: string ]: NodeJS.Timeout } = {}
28
29 private index: { all: string[] } = { all: [] }
30
31 constructor (options: APICacheOptions) {
32 this.options = {
33 headerBlacklist: [],
34 excludeStatus: [],
35
36 ...options
37 }
38 }
39
40 buildMiddleware (strDuration: string) {
41 const duration = parseDurationToMs(strDuration)
42
43 return (req: express.Request, res: express.Response, next: express.NextFunction) => {
44 const key = Redis.Instance.getPrefix() + 'api-cache-' + req.originalUrl
45 const redis = Redis.Instance.getClient()
46
47 if (!redis.connected) return this.makeResponseCacheable(res, next, key, duration)
48
49 try {
50 redis.hgetall(key, (err, obj) => {
51 if (!err && obj && obj.response) {
52 return this.sendCachedResponse(req, res, JSON.parse(obj.response), duration)
53 }
54
55 return this.makeResponseCacheable(res, next, key, duration)
56 })
57 } catch (err) {
58 return this.makeResponseCacheable(res, next, key, duration)
59 }
60 }
61 }
62
63 private shouldCacheResponse (response: express.Response) {
64 if (!response) return false
65 if (this.options.excludeStatus.includes(response.statusCode)) return false
66
67 return true
68 }
69
70 private addIndexEntries (key: string) {
71 this.index.all.unshift(key)
72 }
73
74 private filterBlacklistedHeaders (headers: OutgoingHttpHeaders) {
75 return Object.keys(headers)
76 .filter(key => !this.options.headerBlacklist.includes(key))
77 .reduce((acc, header) => {
78 acc[header] = headers[header]
79
80 return acc
81 }, {})
82 }
83
84 private createCacheObject (status: number, headers: OutgoingHttpHeaders, data: any, encoding: BufferEncoding) {
85 return {
86 status,
87 headers: this.filterBlacklistedHeaders(headers),
88 data,
89 encoding,
90
91 // Seconds since epoch, used to properly decrement max-age headers in cached responses.
92 timestamp: new Date().getTime() / 1000
93 } as CacheObject
94 }
95
96 private cacheResponse (key: string, value: object, duration: number) {
97 const redis = Redis.Instance.getClient()
98
99 if (redis.connected) {
100 try {
101 redis.hset(key, 'response', JSON.stringify(value))
102 redis.hset(key, 'duration', duration + '')
103 redis.expire(key, duration / 1000)
104 } catch (err) {
105 logger.error('Cannot set cache in redis.', { err })
106 }
107 }
108
109 // add automatic cache clearing from duration, includes max limit on setTimeout
110 this.timers[key] = setTimeout(() => this.clear(key), Math.min(duration, 2147483647))
111 }
112
113 private accumulateContent (res: express.Response, content: any) {
114 if (!content) return
115
116 if (typeof content === 'string') {
117 res.locals.apicache.content = (res.locals.apicache.content || '') + content
118 return
119 }
120
121 if (Buffer.isBuffer(content)) {
122 let oldContent = res.locals.apicache.content
123
124 if (typeof oldContent === 'string') {
125 oldContent = Buffer.from(oldContent)
126 }
127
128 if (!oldContent) {
129 oldContent = Buffer.alloc(0)
130 }
131
132 res.locals.apicache.content = Buffer.concat(
133 [ oldContent, content ],
134 oldContent.length + content.length
135 )
136
137 return
138 }
139
140 res.locals.apicache.content = content
141 }
142
143 private makeResponseCacheable (res: express.Response, next: express.NextFunction, key: string, duration: number) {
144 const self = this
145
146 res.locals.apicache = {
147 write: res.write,
148 writeHead: res.writeHead,
149 end: res.end,
150 cacheable: true,
151 content: undefined,
152 headers: {}
153 }
154
155 // Patch express
156 res.writeHead = function () {
157 if (self.shouldCacheResponse(res)) {
158 res.setHeader('cache-control', 'max-age=' + (duration / 1000).toFixed(0))
159 } else {
160 res.setHeader('cache-control', 'no-cache, no-store, must-revalidate')
161 }
162
163 res.locals.apicache.headers = Object.assign({}, res.getHeaders())
164 return res.locals.apicache.writeHead.apply(this, arguments as any)
165 }
166
167 res.write = function (chunk: any) {
168 self.accumulateContent(res, chunk)
169 return res.locals.apicache.write.apply(this, arguments as any)
170 }
171
172 res.end = function (content: any, encoding: BufferEncoding) {
173 if (self.shouldCacheResponse(res)) {
174 self.accumulateContent(res, content)
175
176 if (res.locals.apicache.cacheable && res.locals.apicache.content) {
177 self.addIndexEntries(key)
178
179 const headers = res.locals.apicache.headers || res.getHeaders()
180 const cacheObject = self.createCacheObject(
181 res.statusCode,
182 headers,
183 res.locals.apicache.content,
184 encoding
185 )
186 self.cacheResponse(key, cacheObject, duration)
187 }
188 }
189
190 res.locals.apicache.end.apply(this, arguments as any)
191 } as any
192
193 next()
194 }
195
196 private sendCachedResponse (request: express.Request, response: express.Response, cacheObject: CacheObject, duration: number) {
197 const headers = response.getHeaders()
198
199 if (isTestInstance()) {
200 Object.assign(headers, {
201 'x-api-cache-cached': 'true'
202 })
203 }
204
205 Object.assign(headers, this.filterBlacklistedHeaders(cacheObject.headers || {}), {
206 // Set properly decremented max-age header
207 // This ensures that max-age is in sync with the cache expiration
208 'cache-control':
209 'max-age=' +
210 Math.max(
211 0,
212 (duration / 1000 - (new Date().getTime() / 1000 - cacheObject.timestamp))
213 ).toFixed(0)
214 })
215
216 // unstringify buffers
217 let data = cacheObject.data
218 if (data && data.type === 'Buffer') {
219 data = typeof data.data === 'number'
220 ? Buffer.alloc(data.data)
221 : Buffer.from(data.data)
222 }
223
224 // Test Etag against If-None-Match for 304
225 const cachedEtag = cacheObject.headers.etag
226 const requestEtag = request.headers['if-none-match']
227
228 if (requestEtag && cachedEtag === requestEtag) {
229 response.writeHead(304, headers)
230 return response.end()
231 }
232
233 response.writeHead(cacheObject.status || 200, headers)
234
235 return response.end(data, cacheObject.encoding)
236 }
237
238 private clear (target: string) {
239 const redis = Redis.Instance.getClient()
240
241 if (target) {
242 clearTimeout(this.timers[target])
243 delete this.timers[target]
244
245 try {
246 redis.del(target)
247 } catch (err) {
248 logger.error('Cannot delete %s in redis cache.', target, { err })
249 }
250
251 this.index.all = this.index.all.filter(key => key !== target)
252 } else {
253 for (const key of this.index.all) {
254 clearTimeout(this.timers[key])
255 delete this.timers[key]
256
257 try {
258 redis.del(key)
259 } catch (err) {
260 logger.error('Cannot delete %s in redis cache.', key, { err })
261 }
262 }
263
264 this.index.all = []
265 }
266
267 return this.index
268 }
269}
diff --git a/server/middlewares/cache/shared/index.ts b/server/middlewares/cache/shared/index.ts
new file mode 100644
index 000000000..c707eaf7a
--- /dev/null
+++ b/server/middlewares/cache/shared/index.ts
@@ -0,0 +1 @@
export * from './api-cache'
diff --git a/server/middlewares/error.ts b/server/middlewares/error.ts
index e3eb1c8f5..af5a9c29a 100644
--- a/server/middlewares/error.ts
+++ b/server/middlewares/error.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details' 2import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5function apiFailMiddleware (req: express.Request, res: express.Response, next: express.NextFunction) { 5function apiFailMiddleware (req: express.Request, res: express.Response, next: express.NextFunction) {
6 res.fail = options => { 6 res.fail = options => {
diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts
index 413653dac..a0035f623 100644
--- a/server/middlewares/index.ts
+++ b/server/middlewares/index.ts
@@ -1,4 +1,5 @@
1export * from './validators' 1export * from './validators'
2export * from './cache'
2export * from './activitypub' 3export * from './activitypub'
3export * from './async' 4export * from './async'
4export * from './auth' 5export * from './auth'
diff --git a/server/middlewares/servers.ts b/server/middlewares/servers.ts
index 9aa56bc93..cf70d901e 100644
--- a/server/middlewares/servers.ts
+++ b/server/middlewares/servers.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
2import { getHostWithPort } from '../helpers/express-utils' 3import { getHostWithPort } from '../helpers/express-utils'
3import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
4 4
5function setBodyHostsPort (req: express.Request, res: express.Response, next: express.NextFunction) { 5function setBodyHostsPort (req: express.Request, res: express.Response, next: express.NextFunction) {
6 if (!req.body.hosts) return next() 6 if (!req.body.hosts) return next()
diff --git a/server/middlewares/user-right.ts b/server/middlewares/user-right.ts
index d1888c2d3..c8c694f05 100644
--- a/server/middlewares/user-right.ts
+++ b/server/middlewares/user-right.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight } from '../../shared' 2import { UserRight } from '../../shared'
3import { HttpStatusCode } from '../../shared/models/http/http-error-codes'
3import { logger } from '../helpers/logger' 4import { logger } from '../helpers/logger'
4import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
5 5
6function ensureUserHasRight (userRight: UserRight) { 6function ensureUserHasRight (userRight: UserRight) {
7 return function (req: express.Request, res: express.Response, next: express.NextFunction) { 7 return function (req: express.Request, res: express.Response, next: express.NextFunction) {
diff --git a/server/middlewares/validators/abuse.ts b/server/middlewares/validators/abuse.ts
index c048bc6af..f4d9c3af2 100644
--- a/server/middlewares/validators/abuse.ts
+++ b/server/middlewares/validators/abuse.ts
@@ -16,7 +16,7 @@ import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID, toIntOrNull } from
16import { logger } from '@server/helpers/logger' 16import { logger } from '@server/helpers/logger'
17import { AbuseMessageModel } from '@server/models/abuse/abuse-message' 17import { AbuseMessageModel } from '@server/models/abuse/abuse-message'
18import { AbuseCreate, UserRight } from '@shared/models' 18import { AbuseCreate, UserRight } from '@shared/models'
19import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 19import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
20import { areValidationErrors, doesAbuseExist, doesAccountIdExist, doesCommentIdExist, doesVideoExist } from './shared' 20import { areValidationErrors, doesAbuseExist, doesAccountIdExist, doesCommentIdExist, doesVideoExist } from './shared'
21 21
22const abuseReportValidator = [ 22const abuseReportValidator = [
diff --git a/server/middlewares/validators/activitypub/activity.ts b/server/middlewares/validators/activitypub/activity.ts
index cc6acd4b1..d24e4427b 100644
--- a/server/middlewares/validators/activitypub/activity.ts
+++ b/server/middlewares/validators/activitypub/activity.ts
@@ -1,8 +1,8 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getServerActor } from '@server/models/application/application'
3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
2import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub/activity' 4import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub/activity'
3import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
4import { getServerActor } from '@server/models/application/application'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6 6
7async function activityPubValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 7async function activityPubValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
8 logger.debug('Checking activity pub parameters') 8 logger.debug('Checking activity pub parameters')
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts
index 826b16fc8..f15b293e9 100644
--- a/server/middlewares/validators/blocklist.ts
+++ b/server/middlewares/validators/blocklist.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator' 2import { body, param } from 'express-validator'
3import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { isHostValid } from '../../helpers/custom-validators/servers' 5import { isHostValid } from '../../helpers/custom-validators/servers'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { WEBSERVER } from '../../initializers/constants' 7import { WEBSERVER } from '../../initializers/constants'
diff --git a/server/middlewares/validators/bulk.ts b/server/middlewares/validators/bulk.ts
index 9bb95f5b7..6fec58149 100644
--- a/server/middlewares/validators/bulk.ts
+++ b/server/middlewares/validators/bulk.ts
@@ -1,8 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { isBulkRemoveCommentsOfScopeValid } from '@server/helpers/custom-validators/bulk' 3import { isBulkRemoveCommentsOfScopeValid } from '@server/helpers/custom-validators/bulk'
4import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode, UserRight } from '@shared/models'
5import { UserRight } from '@shared/models'
6import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model' 5import { BulkRemoveCommentsOfBody } from '@shared/models/bulk/bulk-remove-comments-of-body.model'
7import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
8import { areValidationErrors, doesAccountNameWithHostExist } from './shared' 7import { areValidationErrors, doesAccountNameWithHostExist } from './shared'
diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts
index 51b8fdd19..d29bebf64 100644
--- a/server/middlewares/validators/feeds.ts
+++ b/server/middlewares/validators/feeds.ts
@@ -1,7 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param, query } from 'express-validator' 2import { param, query } from 'express-validator'
3 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
5import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' 4import { isValidRSSFeed } from '../../helpers/custom-validators/feeds'
6import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc' 5import { exists, isIdOrUUIDValid, isIdValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
7import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index 205baca48..16abdd096 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -1,18 +1,20 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { isFollowStateValid } from '@server/helpers/custom-validators/follows' 3import { isEachUniqueHandleValid, isFollowStateValid, isRemoteHandleValid } from '@server/helpers/custom-validators/follows'
4import { loadActorUrlOrGetFromWebfinger } from '@server/lib/activitypub/actors' 4import { loadActorUrlOrGetFromWebfinger } from '@server/lib/activitypub/actors'
5import { getRemoteNameAndHost } from '@server/lib/activitypub/follow'
5import { getServerActor } from '@server/models/application/application' 6import { getServerActor } from '@server/models/application/application'
6import { MActorFollowActorsDefault } from '@server/types/models' 7import { MActorFollowActorsDefault } from '@server/types/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
8import { isTestInstance } from '../../helpers/core-utils' 9import { isTestInstance } from '../../helpers/core-utils'
9import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 10import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
10import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' 11import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
11import { logger } from '../../helpers/logger' 12import { logger } from '../../helpers/logger'
12import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' 13import { WEBSERVER } from '../../initializers/constants'
13import { ActorModel } from '../../models/actor/actor' 14import { ActorModel } from '../../models/actor/actor'
14import { ActorFollowModel } from '../../models/actor/actor-follow' 15import { ActorFollowModel } from '../../models/actor/actor-follow'
15import { areValidationErrors } from './shared' 16import { areValidationErrors } from './shared'
17import { ServerFollowCreate } from '@shared/models'
16 18
17const listFollowsValidator = [ 19const listFollowsValidator = [
18 query('state') 20 query('state')
@@ -30,29 +32,46 @@ const listFollowsValidator = [
30] 32]
31 33
32const followValidator = [ 34const followValidator = [
33 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), 35 body('hosts')
36 .toArray()
37 .custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
38
39 body('handles')
40 .toArray()
41 .custom(isEachUniqueHandleValid).withMessage('Should have an array of handles'),
34 42
35 (req: express.Request, res: express.Response, next: express.NextFunction) => { 43 (req: express.Request, res: express.Response, next: express.NextFunction) => {
36 // Force https if the administrator wants to make friends 44 // Force https if the administrator wants to follow remote actors
37 if (isTestInstance() === false && WEBSERVER.SCHEME === 'http') { 45 if (isTestInstance() === false && WEBSERVER.SCHEME === 'http') {
38 return res 46 return res
39 .status(HttpStatusCode.INTERNAL_SERVER_ERROR_500) 47 .status(HttpStatusCode.INTERNAL_SERVER_ERROR_500)
40 .json({ 48 .json({
41 error: 'Cannot follow on a non HTTPS web server.' 49 error: 'Cannot follow on a non HTTPS web server.'
42 }) 50 })
43 .end()
44 } 51 }
45 52
46 logger.debug('Checking follow parameters', { parameters: req.body }) 53 logger.debug('Checking follow parameters', { parameters: req.body })
47 54
48 if (areValidationErrors(req, res)) return 55 if (areValidationErrors(req, res)) return
49 56
57 const body: ServerFollowCreate = req.body
58 if (body.hosts.length === 0 && body.handles.length === 0) {
59
60 return res
61 .status(HttpStatusCode.BAD_REQUEST_400)
62 .json({
63 error: 'You must provide at least one handle or one host.'
64 })
65 }
66
50 return next() 67 return next()
51 } 68 }
52] 69]
53 70
54const removeFollowingValidator = [ 71const removeFollowingValidator = [
55 param('host').custom(isHostValid).withMessage('Should have a valid host'), 72 param('hostOrHandle')
73 .custom(value => isHostValid(value) || isRemoteHandleValid(value))
74 .withMessage('Should have a valid host/handle'),
56 75
57 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 76 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
58 logger.debug('Checking unfollowing parameters', { parameters: req.params }) 77 logger.debug('Checking unfollowing parameters', { parameters: req.params })
@@ -60,12 +79,14 @@ const removeFollowingValidator = [
60 if (areValidationErrors(req, res)) return 79 if (areValidationErrors(req, res)) return
61 80
62 const serverActor = await getServerActor() 81 const serverActor = await getServerActor()
63 const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(serverActor.id, SERVER_ACTOR_NAME, req.params.host) 82
83 const { name, host } = getRemoteNameAndHost(req.params.hostOrHandle)
84 const follow = await ActorFollowModel.loadByActorAndTargetNameAndHostForAPI(serverActor.id, name, host)
64 85
65 if (!follow) { 86 if (!follow) {
66 return res.fail({ 87 return res.fail({
67 status: HttpStatusCode.NOT_FOUND_404, 88 status: HttpStatusCode.NOT_FOUND_404,
68 message: `Following ${req.params.host} not found.` 89 message: `Follow ${req.params.hostOrHandle} not found.`
69 }) 90 })
70 } 91 }
71 92
diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts
index 0a82e6932..e5fc0c277 100644
--- a/server/middlewares/validators/oembed.ts
+++ b/server/middlewares/validators/oembed.ts
@@ -4,7 +4,7 @@ import { join } from 'path'
4import { loadVideo } from '@server/lib/model-loaders' 4import { loadVideo } from '@server/lib/model-loaders'
5import { VideoPlaylistModel } from '@server/models/video/video-playlist' 5import { VideoPlaylistModel } from '@server/models/video/video-playlist'
6import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' 6import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 7import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
8import { isTestInstance } from '../../helpers/core-utils' 8import { isTestInstance } from '../../helpers/core-utils'
9import { isIdOrUUIDValid, toCompleteUUID } from '../../helpers/custom-validators/misc' 9import { isIdOrUUIDValid, toCompleteUUID } from '../../helpers/custom-validators/misc'
10import { logger } from '../../helpers/logger' 10import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts
index 8c76d2e36..3fb2176b9 100644
--- a/server/middlewares/validators/plugins.ts
+++ b/server/middlewares/validators/plugins.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query, ValidationChain } from 'express-validator' 2import { body, param, query, ValidationChain } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { PluginType } from '../../../shared/models/plugins/plugin.type' 4import { PluginType } from '../../../shared/models/plugins/plugin.type'
5import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/server/api/install-plugin.model' 5import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/server/api/install-plugin.model'
6import { exists, isBooleanValid, isSafePath, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 6import { exists, isBooleanValid, isSafePath, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
diff --git a/server/middlewares/validators/redundancy.ts b/server/middlewares/validators/redundancy.ts
index 116c8c611..f1b2ff5cd 100644
--- a/server/middlewares/validators/redundancy.ts
+++ b/server/middlewares/validators/redundancy.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies' 3import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies'
4import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 4import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
5import { 5import {
6 exists, 6 exists,
7 isBooleanValid, 7 isBooleanValid,
diff --git a/server/middlewares/validators/search.ts b/server/middlewares/validators/search.ts
index 7bbf81048..27d0e541d 100644
--- a/server/middlewares/validators/search.ts
+++ b/server/middlewares/validators/search.ts
@@ -1,13 +1,18 @@
1import * as express from 'express' 1import * as express from 'express'
2import { query } from 'express-validator' 2import { query } from 'express-validator'
3import { isSearchTargetValid } from '@server/helpers/custom-validators/search' 3import { isSearchTargetValid } from '@server/helpers/custom-validators/search'
4import { isDateValid } from '../../helpers/custom-validators/misc' 4import { isHostValid } from '@server/helpers/custom-validators/servers'
5import { areUUIDsValid, isDateValid, isNotEmptyStringArray, toCompleteUUIDs } from '../../helpers/custom-validators/misc'
5import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
6import { areValidationErrors } from './shared' 7import { areValidationErrors } from './shared'
7 8
8const videosSearchValidator = [ 9const videosSearchValidator = [
9 query('search').optional().not().isEmpty().withMessage('Should have a valid search'), 10 query('search').optional().not().isEmpty().withMessage('Should have a valid search'),
10 11
12 query('host')
13 .optional()
14 .custom(isHostValid).withMessage('Should have a valid host'),
15
11 query('startDate') 16 query('startDate')
12 .optional() 17 .optional()
13 .custom(isDateValid).withMessage('Should have a start date that conforms to ISO 8601'), 18 .custom(isDateValid).withMessage('Should have a start date that conforms to ISO 8601'),
@@ -22,8 +27,18 @@ const videosSearchValidator = [
22 .optional() 27 .optional()
23 .custom(isDateValid).withMessage('Should have a published end date that conforms to ISO 8601'), 28 .custom(isDateValid).withMessage('Should have a published end date that conforms to ISO 8601'),
24 29
25 query('durationMin').optional().isInt().withMessage('Should have a valid min duration'), 30 query('durationMin')
26 query('durationMax').optional().isInt().withMessage('Should have a valid max duration'), 31 .optional()
32 .isInt().withMessage('Should have a valid min duration'),
33 query('durationMax')
34 .optional()
35 .isInt().withMessage('Should have a valid max duration'),
36
37 query('uuids')
38 .optional()
39 .toArray()
40 .customSanitizer(toCompleteUUIDs)
41 .custom(areUUIDsValid).withMessage('Should have valid uuids'),
27 42
28 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), 43 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'),
29 44
@@ -37,8 +52,22 @@ const videosSearchValidator = [
37] 52]
38 53
39const videoChannelsListSearchValidator = [ 54const videoChannelsListSearchValidator = [
40 query('search').not().isEmpty().withMessage('Should have a valid search'), 55 query('search')
41 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), 56 .optional()
57 .not().isEmpty().withMessage('Should have a valid search'),
58
59 query('host')
60 .optional()
61 .custom(isHostValid).withMessage('Should have a valid host'),
62
63 query('searchTarget')
64 .optional()
65 .custom(isSearchTargetValid).withMessage('Should have a valid search target'),
66
67 query('handles')
68 .optional()
69 .toArray()
70 .custom(isNotEmptyStringArray).withMessage('Should have valid handles'),
42 71
43 (req: express.Request, res: express.Response, next: express.NextFunction) => { 72 (req: express.Request, res: express.Response, next: express.NextFunction) => {
44 logger.debug('Checking video channels search query', { parameters: req.query }) 73 logger.debug('Checking video channels search query', { parameters: req.query })
@@ -50,8 +79,23 @@ const videoChannelsListSearchValidator = [
50] 79]
51 80
52const videoPlaylistsListSearchValidator = [ 81const videoPlaylistsListSearchValidator = [
53 query('search').not().isEmpty().withMessage('Should have a valid search'), 82 query('search')
54 query('searchTarget').optional().custom(isSearchTargetValid).withMessage('Should have a valid search target'), 83 .optional()
84 .not().isEmpty().withMessage('Should have a valid search'),
85
86 query('host')
87 .optional()
88 .custom(isHostValid).withMessage('Should have a valid host'),
89
90 query('searchTarget')
91 .optional()
92 .custom(isSearchTargetValid).withMessage('Should have a valid search target'),
93
94 query('uuids')
95 .optional()
96 .toArray()
97 .customSanitizer(toCompleteUUIDs)
98 .custom(areUUIDsValid).withMessage('Should have valid uuids'),
55 99
56 (req: express.Request, res: express.Response, next: express.NextFunction) => { 100 (req: express.Request, res: express.Response, next: express.NextFunction) => {
57 logger.debug('Checking video playlists search query', { parameters: req.query }) 101 logger.debug('Checking video playlists search query', { parameters: req.query })
diff --git a/server/middlewares/validators/server.ts b/server/middlewares/validators/server.ts
index fc7239b25..29fdc13d2 100644
--- a/server/middlewares/validators/server.ts
+++ b/server/middlewares/validators/server.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { isHostValid, isValidContactBody } from '../../helpers/custom-validators/servers' 4import { isHostValid, isValidContactBody } from '../../helpers/custom-validators/servers'
5import { isUserDisplayNameValid } from '../../helpers/custom-validators/users' 5import { isUserDisplayNameValid } from '../../helpers/custom-validators/users'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/shared/abuses.ts b/server/middlewares/validators/shared/abuses.ts
index 4a20a55fa..2b8d86ba5 100644
--- a/server/middlewares/validators/shared/abuses.ts
+++ b/server/middlewares/validators/shared/abuses.ts
@@ -1,6 +1,6 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { AbuseModel } from '@server/models/abuse/abuse' 2import { AbuseModel } from '@server/models/abuse/abuse'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesAbuseExist (abuseId: number | string, res: Response) { 5async function doesAbuseExist (abuseId: number | string, res: Response) {
6 const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10)) 6 const abuse = await AbuseModel.loadByIdWithReporter(parseInt(abuseId + '', 10))
diff --git a/server/middlewares/validators/shared/accounts.ts b/server/middlewares/validators/shared/accounts.ts
index 04da15441..fe4f83aa0 100644
--- a/server/middlewares/validators/shared/accounts.ts
+++ b/server/middlewares/validators/shared/accounts.ts
@@ -2,7 +2,7 @@ import { Response } from 'express'
2import { AccountModel } from '@server/models/account/account' 2import { AccountModel } from '@server/models/account/account'
3import { UserModel } from '@server/models/user/user' 3import { UserModel } from '@server/models/user/user'
4import { MAccountDefault } from '@server/types/models' 4import { MAccountDefault } from '@server/types/models'
5import { HttpStatusCode } from '@shared/core-utils' 5import { HttpStatusCode } from '@shared/models'
6 6
7function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) { 7function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) {
8 const promise = AccountModel.load(parseInt(id + '', 10)) 8 const promise = AccountModel.load(parseInt(id + '', 10))
diff --git a/server/middlewares/validators/shared/video-blacklists.ts b/server/middlewares/validators/shared/video-blacklists.ts
index 01491c10f..f85b39b23 100644
--- a/server/middlewares/validators/shared/video-blacklists.ts
+++ b/server/middlewares/validators/shared/video-blacklists.ts
@@ -1,6 +1,6 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { VideoBlacklistModel } from '@server/models/video/video-blacklist' 2import { VideoBlacklistModel } from '@server/models/video/video-blacklist'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesVideoBlacklistExist (videoId: number, res: Response) { 5async function doesVideoBlacklistExist (videoId: number, res: Response) {
6 const videoBlacklist = await VideoBlacklistModel.loadByVideoId(videoId) 6 const videoBlacklist = await VideoBlacklistModel.loadByVideoId(videoId)
diff --git a/server/middlewares/validators/shared/video-captions.ts b/server/middlewares/validators/shared/video-captions.ts
index 80f6c5a52..831b366ea 100644
--- a/server/middlewares/validators/shared/video-captions.ts
+++ b/server/middlewares/validators/shared/video-captions.ts
@@ -1,7 +1,7 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { VideoCaptionModel } from '@server/models/video/video-caption' 2import { VideoCaptionModel } from '@server/models/video/video-caption'
3import { MVideoId } from '@server/types/models' 3import { MVideoId } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) { 6async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) {
7 const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) 7 const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language)
diff --git a/server/middlewares/validators/shared/video-channels.ts b/server/middlewares/validators/shared/video-channels.ts
index fe2e663b7..3fc3d012a 100644
--- a/server/middlewares/validators/shared/video-channels.ts
+++ b/server/middlewares/validators/shared/video-channels.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoChannelModel } from '@server/models/video/video-channel' 2import { VideoChannelModel } from '@server/models/video/video-channel'
3import { MChannelBannerAccountDefault } from '@server/types/models' 3import { MChannelBannerAccountDefault } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6async function doesLocalVideoChannelNameExist (name: string, res: express.Response) { 6async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
7 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) 7 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
diff --git a/server/middlewares/validators/shared/video-comments.ts b/server/middlewares/validators/shared/video-comments.ts
index 83ea15c98..60132fb6e 100644
--- a/server/middlewares/validators/shared/video-comments.ts
+++ b/server/middlewares/validators/shared/video-comments.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoCommentModel } from '@server/models/video/video-comment' 2import { VideoCommentModel } from '@server/models/video/video-comment'
3import { MVideoId } from '@server/types/models' 3import { MVideoId } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) { 6async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) {
7 const id = parseInt(idArg + '', 10) 7 const id = parseInt(idArg + '', 10)
diff --git a/server/middlewares/validators/shared/video-imports.ts b/server/middlewares/validators/shared/video-imports.ts
index 0f984bc17..50b49ffcb 100644
--- a/server/middlewares/validators/shared/video-imports.ts
+++ b/server/middlewares/validators/shared/video-imports.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoImportModel } from '@server/models/video/video-import' 2import { VideoImportModel } from '@server/models/video/video-import'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesVideoImportExist (id: number, res: express.Response) { 5async function doesVideoImportExist (id: number, res: express.Response) {
6 const videoImport = await VideoImportModel.loadAndPopulateVideo(id) 6 const videoImport = await VideoImportModel.loadAndPopulateVideo(id)
diff --git a/server/middlewares/validators/shared/video-ownerships.ts b/server/middlewares/validators/shared/video-ownerships.ts
index fc27006ce..93a23ef40 100644
--- a/server/middlewares/validators/shared/video-ownerships.ts
+++ b/server/middlewares/validators/shared/video-ownerships.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership' 2import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership'
3import { HttpStatusCode } from '@shared/core-utils' 3import { HttpStatusCode } from '@shared/models'
4 4
5async function doesChangeVideoOwnershipExist (idArg: number | string, res: express.Response) { 5async function doesChangeVideoOwnershipExist (idArg: number | string, res: express.Response) {
6 const id = parseInt(idArg + '', 10) 6 const id = parseInt(idArg + '', 10)
diff --git a/server/middlewares/validators/shared/video-playlists.ts b/server/middlewares/validators/shared/video-playlists.ts
index d762859a8..3f6768179 100644
--- a/server/middlewares/validators/shared/video-playlists.ts
+++ b/server/middlewares/validators/shared/video-playlists.ts
@@ -1,7 +1,7 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoPlaylistModel } from '@server/models/video/video-playlist' 2import { VideoPlaylistModel } from '@server/models/video/video-playlist'
3import { MVideoPlaylist } from '@server/types/models' 3import { MVideoPlaylist } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 4import { HttpStatusCode } from '@shared/models'
5 5
6export type VideoPlaylistFetchType = 'summary' | 'all' 6export type VideoPlaylistFetchType = 'summary' | 'all'
7async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') { 7async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') {
diff --git a/server/middlewares/validators/shared/videos.ts b/server/middlewares/validators/shared/videos.ts
index 2c66c1a3a..71b81654f 100644
--- a/server/middlewares/validators/shared/videos.ts
+++ b/server/middlewares/validators/shared/videos.ts
@@ -12,8 +12,7 @@ import {
12 MVideoImmutable, 12 MVideoImmutable,
13 MVideoThumbnail 13 MVideoThumbnail
14} from '@server/types/models' 14} from '@server/types/models'
15import { HttpStatusCode } from '@shared/core-utils' 15import { HttpStatusCode, UserRight } from '@shared/models'
16import { UserRight } from '@shared/models'
17 16
18async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') { 17async function doesVideoExist (id: number | string, res: Response, fetchType: VideoLoadType = 'all') {
19 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined 18 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
diff --git a/server/middlewares/validators/themes.ts b/server/middlewares/validators/themes.ts
index d4716257f..2953b9505 100644
--- a/server/middlewares/validators/themes.ts
+++ b/server/middlewares/validators/themes.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param } from 'express-validator' 2import { param } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { isSafePath } from '../../helpers/custom-validators/misc' 4import { isSafePath } from '../../helpers/custom-validators/misc'
5import { isPluginNameValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' 5import { isPluginNameValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts
index ab7962923..df5777771 100644
--- a/server/middlewares/validators/user-subscriptions.ts
+++ b/server/middlewares/validators/user-subscriptions.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 4import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
5import { toArray } from '../../helpers/custom-validators/misc' 5import { toArray } from '../../helpers/custom-validators/misc'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 698d7d814..bc8607523 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -3,10 +3,9 @@ import { body, param, query } from 'express-validator'
3import { omit } from 'lodash' 3import { omit } from 'lodash'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { MUserDefault } from '@server/types/models' 5import { MUserDefault } from '@server/types/models'
6import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
7import { UserRole } from '../../../shared/models/users' 7import { UserRole } from '../../../shared/models/users'
8import { UserRegister } from '../../../shared/models/users/user-register.model' 8import { UserRegister } from '../../../shared/models/users/user-register.model'
9import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor'
10import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 9import { toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc'
11import { isThemeNameValid } from '../../helpers/custom-validators/plugins' 10import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
12import { 11import {
@@ -28,7 +27,7 @@ import {
28 isUserVideoQuotaValid, 27 isUserVideoQuotaValid,
29 isUserVideosHistoryEnabledValid 28 isUserVideosHistoryEnabledValid
30} from '../../helpers/custom-validators/users' 29} from '../../helpers/custom-validators/users'
31import { isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' 30import { isVideoChannelDisplayNameValid, isVideoChannelUsernameValid } from '../../helpers/custom-validators/video-channels'
32import { logger } from '../../helpers/logger' 31import { logger } from '../../helpers/logger'
33import { isThemeRegistered } from '../../lib/plugins/theme-utils' 32import { isThemeRegistered } from '../../lib/plugins/theme-utils'
34import { Redis } from '../../lib/redis' 33import { Redis } from '../../lib/redis'
@@ -56,9 +55,12 @@ const usersAddValidator = [
56 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), 55 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
57 body('password').custom(isUserPasswordValidOrEmpty).withMessage('Should have a valid password'), 56 body('password').custom(isUserPasswordValidOrEmpty).withMessage('Should have a valid password'),
58 body('email').isEmail().withMessage('Should have a valid email'), 57 body('email').isEmail().withMessage('Should have a valid email'),
59 body('channelName').optional().custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), 58
59 body('channelName').optional().custom(isVideoChannelUsernameValid).withMessage('Should have a valid channel name'),
60
60 body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'), 61 body('videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid user quota'),
61 body('videoQuotaDaily').custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'), 62 body('videoQuotaDaily').custom(isUserVideoQuotaDailyValid).withMessage('Should have a valid daily user quota'),
63
62 body('role') 64 body('role')
63 .customSanitizer(toIntOrNull) 65 .customSanitizer(toIntOrNull)
64 .custom(isUserRoleValid).withMessage('Should have a valid role'), 66 .custom(isUserRoleValid).withMessage('Should have a valid role'),
@@ -106,10 +108,10 @@ const usersRegisterValidator = [
106 108
107 body('channel.name') 109 body('channel.name')
108 .optional() 110 .optional()
109 .custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), 111 .custom(isVideoChannelUsernameValid).withMessage('Should have a valid channel name'),
110 body('channel.displayName') 112 body('channel.displayName')
111 .optional() 113 .optional()
112 .custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), 114 .custom(isVideoChannelDisplayNameValid).withMessage('Should have a valid display name'),
113 115
114 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 116 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
115 logger.debug('Checking usersRegister parameters', { parameters: omit(req.body, 'password') }) 117 logger.debug('Checking usersRegister parameters', { parameters: omit(req.body, 'password') })
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index 21141d84d..3a4937b7b 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, query } from 'express-validator' 2import { body, query } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { isBooleanValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' 4import { isBooleanValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc'
5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist' 5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../../helpers/custom-validators/video-blacklist'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index e7df185e4..3b5693d23 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -3,13 +3,13 @@ import { body, param, query } from 'express-validator'
3import { VIDEO_CHANNELS } from '@server/initializers/constants' 3import { VIDEO_CHANNELS } from '@server/initializers/constants'
4import { MChannelAccountDefault, MUser } from '@server/types/models' 4import { MChannelAccountDefault, MUser } from '@server/types/models'
5import { UserRight } from '../../../../shared' 5import { UserRight } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
7import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
8import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' 7import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
9import { 8import {
10 isVideoChannelDescriptionValid, 9 isVideoChannelDescriptionValid,
11 isVideoChannelNameValid, 10 isVideoChannelDisplayNameValid,
12 isVideoChannelSupportValid 11 isVideoChannelSupportValid,
12 isVideoChannelUsernameValid
13} from '../../../helpers/custom-validators/video-channels' 13} from '../../../helpers/custom-validators/video-channels'
14import { logger } from '../../../helpers/logger' 14import { logger } from '../../../helpers/logger'
15import { ActorModel } from '../../../models/actor/actor' 15import { ActorModel } from '../../../models/actor/actor'
@@ -17,8 +17,8 @@ import { VideoChannelModel } from '../../../models/video/video-channel'
17import { areValidationErrors, doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../shared' 17import { areValidationErrors, doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../shared'
18 18
19const videoChannelsAddValidator = [ 19const videoChannelsAddValidator = [
20 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), 20 body('name').custom(isVideoChannelUsernameValid).withMessage('Should have a valid channel name'),
21 body('displayName').custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), 21 body('displayName').custom(isVideoChannelDisplayNameValid).withMessage('Should have a valid display name'),
22 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 22 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
23 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), 23 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
24 24
@@ -50,7 +50,7 @@ const videoChannelsUpdateValidator = [
50 param('nameWithHost').exists().withMessage('Should have an video channel name with host'), 50 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
51 body('displayName') 51 body('displayName')
52 .optional() 52 .optional()
53 .custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), 53 .custom(isVideoChannelDisplayNameValid).withMessage('Should have a valid display name'),
54 body('description') 54 body('description')
55 .optional() 55 .optional()
56 .custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 56 .custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
@@ -117,7 +117,7 @@ const videoChannelsNameWithHostValidator = [
117] 117]
118 118
119const localVideoChannelValidator = [ 119const localVideoChannelValidator = [
120 param('name').custom(isVideoChannelNameValid).withMessage('Should have a valid video channel name'), 120 param('name').custom(isVideoChannelDisplayNameValid).withMessage('Should have a valid video channel name'),
121 121
122 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 122 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
123 logger.debug('Checking localVideoChannelValidator parameters', { parameters: req.params }) 123 logger.debug('Checking localVideoChannelValidator parameters', { parameters: req.params })
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 885506ebe..61c2ed92f 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { MUserAccountUrl } from '@server/types/models' 3import { MUserAccountUrl } from '@server/types/models'
4import { UserRight } from '../../../../shared' 4import { UserRight } from '../../../../shared'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
6import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' 6import { exists, isBooleanValid, isIdValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc'
7import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' 7import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
8import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts
index 85dc647ce..52b839e56 100644
--- a/server/middlewares/validators/videos/video-imports.ts
+++ b/server/middlewares/validators/videos/video-imports.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { isPreImportVideoAccepted } from '@server/lib/moderation' 3import { isPreImportVideoAccepted } from '@server/lib/moderation'
4import { Hooks } from '@server/lib/plugins/hooks' 4import { Hooks } from '@server/lib/plugins/hooks'
5import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '@shared/models'
6import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model' 6import { VideoImportCreate } from '@shared/models/videos/import/video-import-create.model'
7import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc' 7import { isIdValid, toIntOrNull } from '../../../helpers/custom-validators/misc'
8import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports' 8import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../../helpers/custom-validators/video-imports'
diff --git a/server/middlewares/validators/videos/video-live.ts b/server/middlewares/validators/videos/video-live.ts
index 7cfb935e3..97e8b4510 100644
--- a/server/middlewares/validators/videos/video-live.ts
+++ b/server/middlewares/validators/videos/video-live.ts
@@ -5,8 +5,7 @@ import { isLocalLiveVideoAccepted } from '@server/lib/moderation'
5import { Hooks } from '@server/lib/plugins/hooks' 5import { Hooks } from '@server/lib/plugins/hooks'
6import { VideoModel } from '@server/models/video/video' 6import { VideoModel } from '@server/models/video/video'
7import { VideoLiveModel } from '@server/models/video/video-live' 7import { VideoLiveModel } from '@server/models/video/video-live'
8import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 8import { HttpStatusCode, ServerErrorCode, UserRight, VideoState } from '@shared/models'
9import { ServerErrorCode, UserRight, VideoState } from '@shared/models'
10import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc' 9import { isBooleanValid, isIdValid, toBooleanOrNull, toIntOrNull } from '../../../helpers/custom-validators/misc'
11import { isVideoNameValid } from '../../../helpers/custom-validators/videos' 10import { isVideoNameValid } from '../../../helpers/custom-validators/videos'
12import { cleanUpReqFiles } from '../../../helpers/express-utils' 11import { cleanUpReqFiles } from '../../../helpers/express-utils'
diff --git a/server/middlewares/validators/videos/video-ownership-changes.ts b/server/middlewares/validators/videos/video-ownership-changes.ts
index 54ac46c99..a7f0b72c3 100644
--- a/server/middlewares/validators/videos/video-ownership-changes.ts
+++ b/server/middlewares/validators/videos/video-ownership-changes.ts
@@ -6,8 +6,14 @@ import { logger } from '@server/helpers/logger'
6import { isAbleToUploadVideo } from '@server/lib/user' 6import { isAbleToUploadVideo } from '@server/lib/user'
7import { AccountModel } from '@server/models/account/account' 7import { AccountModel } from '@server/models/account/account'
8import { MVideoWithAllFiles } from '@server/types/models' 8import { MVideoWithAllFiles } from '@server/types/models'
9import { HttpStatusCode } from '@shared/core-utils' 9import {
10import { ServerErrorCode, UserRight, VideoChangeOwnershipAccept, VideoChangeOwnershipStatus, VideoState } from '@shared/models' 10 HttpStatusCode,
11 ServerErrorCode,
12 UserRight,
13 VideoChangeOwnershipAccept,
14 VideoChangeOwnershipStatus,
15 VideoState
16} from '@shared/models'
11import { 17import {
12 areValidationErrors, 18 areValidationErrors,
13 checkUserCanManageVideo, 19 checkUserCanManageVideo,
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index 5ee7ee0ce..ab84b4814 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -3,7 +3,7 @@ import { body, param, query, ValidationChain } from 'express-validator'
3import { ExpressPromiseHandler } from '@server/types/express' 3import { ExpressPromiseHandler } from '@server/types/express'
4import { MUserAccountId } from '@server/types/models' 4import { MUserAccountId } from '@server/types/models'
5import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' 5import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
7import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 7import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
8import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' 8import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
9import { 9import {
diff --git a/server/middlewares/validators/videos/video-rates.ts b/server/middlewares/validators/videos/video-rates.ts
index 5d5dfb222..5fe78b39e 100644
--- a/server/middlewares/validators/videos/video-rates.ts
+++ b/server/middlewares/validators/videos/video-rates.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param, query } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { VideoRateType } from '../../../../shared/models/videos' 4import { VideoRateType } from '../../../../shared/models/videos'
5import { isAccountNameValid } from '../../../helpers/custom-validators/accounts' 5import { isAccountNameValid } from '../../../helpers/custom-validators/accounts'
6import { isIdValid } from '../../../helpers/custom-validators/misc' 6import { isIdValid } from '../../../helpers/custom-validators/misc'
diff --git a/server/middlewares/validators/videos/video-shares.ts b/server/middlewares/validators/videos/video-shares.ts
index 7e54b6fc0..3b8d61768 100644
--- a/server/middlewares/validators/videos/video-shares.ts
+++ b/server/middlewares/validators/videos/video-shares.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { param } from 'express-validator' 2import { param } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { isIdValid } from '../../../helpers/custom-validators/misc' 4import { isIdValid } from '../../../helpers/custom-validators/misc'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
diff --git a/server/middlewares/validators/videos/video-watch.ts b/server/middlewares/validators/videos/video-watch.ts
index 43306f7cd..431515eb1 100644
--- a/server/middlewares/validators/videos/video-watch.ts
+++ b/server/middlewares/validators/videos/video-watch.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body } from 'express-validator' 2import { body } from 'express-validator'
3import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
4import { toIntOrNull } from '../../../helpers/custom-validators/misc' 4import { toIntOrNull } from '../../../helpers/custom-validators/misc'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared' 6import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 49e10e2b5..13187f398 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -6,7 +6,7 @@ import { getServerActor } from '@server/models/application/application'
6import { ExpressPromiseHandler } from '@server/types/express' 6import { ExpressPromiseHandler } from '@server/types/express'
7import { MUserAccountId, MVideoFullLight } from '@server/types/models' 7import { MUserAccountId, MVideoFullLight } from '@server/types/models'
8import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared' 8import { ServerErrorCode, UserRight, VideoPrivacy } from '../../../../shared'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
10import { 10import {
11 exists, 11 exists,
12 isBooleanValid, 12 isBooleanValid,
@@ -188,7 +188,7 @@ const videosAddResumableInitValidator = getCommonVideoEditAttributes().concat([
188 // multer required unsetting the Content-Type, now we can set it for node-uploadx 188 // multer required unsetting the Content-Type, now we can set it for node-uploadx
189 req.headers['content-type'] = 'application/json; charset=utf-8' 189 req.headers['content-type'] = 'application/json; charset=utf-8'
190 // place previewfile in metadata so that uploadx saves it in .META 190 // place previewfile in metadata so that uploadx saves it in .META
191 if (req.files['previewfile']) req.body.previewfile = req.files['previewfile'] 191 if (req.files?.['previewfile']) req.body.previewfile = req.files['previewfile']
192 192
193 return next() 193 return next()
194 } 194 }
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts
index bcdd136c6..131360820 100644
--- a/server/middlewares/validators/webfinger.ts
+++ b/server/middlewares/validators/webfinger.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { query } from 'express-validator' 2import { query } from 'express-validator'
3import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 3import { HttpStatusCode } from '../../../shared/models/http/http-error-codes'
4import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' 4import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger'
5import { getHostWithPort } from '../../helpers/express-utils' 5import { getHostWithPort } from '../../helpers/express-utils'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 665ecd595..37194a119 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -52,6 +52,7 @@ export enum ScopeNames {
52export type SummaryOptions = { 52export type SummaryOptions = {
53 actorRequired?: boolean // Default: true 53 actorRequired?: boolean // Default: true
54 whereActor?: WhereOptions 54 whereActor?: WhereOptions
55 whereServer?: WhereOptions
55 withAccountBlockerIds?: number[] 56 withAccountBlockerIds?: number[]
56} 57}
57 58
@@ -65,12 +66,11 @@ export type SummaryOptions = {
65})) 66}))
66@Scopes(() => ({ 67@Scopes(() => ({
67 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { 68 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
68 const whereActor = options.whereActor || undefined
69
70 const serverInclude: IncludeOptions = { 69 const serverInclude: IncludeOptions = {
71 attributes: [ 'host' ], 70 attributes: [ 'host' ],
72 model: ServerModel.unscoped(), 71 model: ServerModel.unscoped(),
73 required: false 72 required: !!options.whereServer,
73 where: options.whereServer
74 } 74 }
75 75
76 const queryInclude: Includeable[] = [ 76 const queryInclude: Includeable[] = [
@@ -78,7 +78,7 @@ export type SummaryOptions = {
78 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], 78 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
79 model: ActorModel.unscoped(), 79 model: ActorModel.unscoped(),
80 required: options.actorRequired ?? true, 80 required: options.actorRequired ?? true,
81 where: whereActor, 81 where: options.whereActor,
82 include: [ 82 include: [
83 serverInclude, 83 serverInclude,
84 84
diff --git a/server/models/actor/actor-follow.ts b/server/models/actor/actor-follow.ts
index 3a09e51d6..283856d3f 100644
--- a/server/models/actor/actor-follow.ts
+++ b/server/models/actor/actor-follow.ts
@@ -20,7 +20,6 @@ import {
20} from 'sequelize-typescript' 20} from 'sequelize-typescript'
21import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc' 21import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
22import { getServerActor } from '@server/models/application/application' 22import { getServerActor } from '@server/models/application/application'
23import { VideoModel } from '@server/models/video/video'
24import { 23import {
25 MActorFollowActorsDefault, 24 MActorFollowActorsDefault,
26 MActorFollowActorsDefaultSubscription, 25 MActorFollowActorsDefaultSubscription,
@@ -36,6 +35,7 @@ import { logger } from '../../helpers/logger'
36import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants' 35import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
37import { AccountModel } from '../account/account' 36import { AccountModel } from '../account/account'
38import { ServerModel } from '../server/server' 37import { ServerModel } from '../server/server'
38import { doesExist } from '../shared/query'
39import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils' 39import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils'
40import { VideoChannelModel } from '../video/video-channel' 40import { VideoChannelModel } from '../video/video-channel'
41import { ActorModel, unusedActorAttributesForAPI } from './actor' 41import { ActorModel, unusedActorAttributesForAPI } from './actor'
@@ -166,14 +166,8 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
166 166
167 static isFollowedBy (actorId: number, followerActorId: number) { 167 static isFollowedBy (actorId: number, followerActorId: number) {
168 const query = 'SELECT 1 FROM "actorFollow" WHERE "actorId" = $followerActorId AND "targetActorId" = $actorId LIMIT 1' 168 const query = 'SELECT 1 FROM "actorFollow" WHERE "actorId" = $followerActorId AND "targetActorId" = $actorId LIMIT 1'
169 const options = {
170 type: QueryTypes.SELECT as QueryTypes.SELECT,
171 bind: { actorId, followerActorId },
172 raw: true
173 }
174 169
175 return VideoModel.sequelize.query(query, options) 170 return doesExist(query, { actorId, followerActorId })
176 .then(results => results.length === 1)
177 } 171 }
178 172
179 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Promise<MActorFollowActorsDefault> { 173 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Promise<MActorFollowActorsDefault> {
@@ -324,13 +318,13 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
324 318
325 const followWhere = state ? { state } : {} 319 const followWhere = state ? { state } : {}
326 const followingWhere: WhereOptions = {} 320 const followingWhere: WhereOptions = {}
327 const followingServerWhere: WhereOptions = {}
328 321
329 if (search) { 322 if (search) {
330 Object.assign(followingServerWhere, { 323 Object.assign(followWhere, {
331 host: { 324 [Op.or]: [
332 [Op.iLike]: '%' + search + '%' 325 searchAttribute(options.search, '$ActorFollowing.preferredUsername$'),
333 } 326 searchAttribute(options.search, '$ActorFollowing.Server.host$')
327 ]
334 }) 328 })
335 } 329 }
336 330
@@ -361,8 +355,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
361 include: [ 355 include: [
362 { 356 {
363 model: ServerModel, 357 model: ServerModel,
364 required: true, 358 required: true
365 where: followingServerWhere
366 } 359 }
367 ] 360 ]
368 } 361 }
@@ -391,13 +384,13 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
391 384
392 const followWhere = state ? { state } : {} 385 const followWhere = state ? { state } : {}
393 const followerWhere: WhereOptions = {} 386 const followerWhere: WhereOptions = {}
394 const followerServerWhere: WhereOptions = {}
395 387
396 if (search) { 388 if (search) {
397 Object.assign(followerServerWhere, { 389 Object.assign(followWhere, {
398 host: { 390 [Op.or]: [
399 [Op.iLike]: '%' + search + '%' 391 searchAttribute(search, '$ActorFollower.preferredUsername$'),
400 } 392 searchAttribute(search, '$ActorFollower.Server.host$')
393 ]
401 }) 394 })
402 } 395 }
403 396
@@ -420,8 +413,7 @@ export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowMo
420 include: [ 413 include: [
421 { 414 {
422 model: ServerModel, 415 model: ServerModel,
423 required: true, 416 required: true
424 where: followerServerWhere
425 } 417 }
426 ] 418 ]
427 }, 419 },
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index ccda023e0..d645be248 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -160,8 +160,8 @@ export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedu
160 const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}` 160 const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}`
161 logger.info('Removing duplicated video file %s.', logIdentifier) 161 logger.info('Removing duplicated video file %s.', logIdentifier)
162 162
163 videoFile.Video.removeFile(videoFile, true) 163 videoFile.Video.removeFileAndTorrent(videoFile, true)
164 .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err })) 164 .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err }))
165 } 165 }
166 166
167 if (instance.videoStreamingPlaylistId) { 167 if (instance.videoStreamingPlaylistId) {
diff --git a/server/models/shared/index.ts b/server/models/shared/index.ts
new file mode 100644
index 000000000..5b97510e0
--- /dev/null
+++ b/server/models/shared/index.ts
@@ -0,0 +1,2 @@
1export * from './query'
2export * from './update'
diff --git a/server/models/shared/query.ts b/server/models/shared/query.ts
new file mode 100644
index 000000000..036cc13c6
--- /dev/null
+++ b/server/models/shared/query.ts
@@ -0,0 +1,17 @@
1import { BindOrReplacements, QueryTypes } from 'sequelize'
2import { sequelizeTypescript } from '@server/initializers/database'
3
4function doesExist (query: string, bind?: BindOrReplacements) {
5 const options = {
6 type: QueryTypes.SELECT as QueryTypes.SELECT,
7 bind,
8 raw: true
9 }
10
11 return sequelizeTypescript.query(query, options)
12 .then(results => results.length === 1)
13}
14
15export {
16 doesExist
17}
diff --git a/server/models/shared/update.ts b/server/models/shared/update.ts
new file mode 100644
index 000000000..d338211e3
--- /dev/null
+++ b/server/models/shared/update.ts
@@ -0,0 +1,18 @@
1import { QueryTypes, Transaction } from 'sequelize'
2import { sequelizeTypescript } from '@server/initializers/database'
3
4// Sequelize always skip the update if we only update updatedAt field
5function setAsUpdated (table: string, id: number, transaction?: Transaction) {
6 return sequelizeTypescript.query(
7 `UPDATE "${table}" SET "updatedAt" = :updatedAt WHERE id = :id`,
8 {
9 replacements: { table, id, updatedAt: new Date() },
10 type: QueryTypes.UPDATE,
11 transaction
12 }
13 )
14}
15
16export {
17 setAsUpdated
18}
diff --git a/server/models/user/user-notification.ts b/server/models/user/user-notification.ts
index a7f84e9ca..04c5513a9 100644
--- a/server/models/user/user-notification.ts
+++ b/server/models/user/user-notification.ts
@@ -1,5 +1,6 @@
1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' 1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { uuidToShort } from '@server/helpers/uuid'
3import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' 4import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
4import { AttributesOnly } from '@shared/core-utils' 5import { AttributesOnly } from '@shared/core-utils'
5import { UserNotification, UserNotificationType } from '../../../shared' 6import { UserNotification, UserNotificationType } from '../../../shared'
@@ -615,6 +616,7 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
615 return { 616 return {
616 id: video.id, 617 id: video.id,
617 uuid: video.uuid, 618 uuid: video.uuid,
619 shortUUID: uuidToShort(video.uuid),
618 name: video.name 620 name: video.name
619 } 621 }
620 } 622 }
@@ -628,6 +630,7 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
628 ? { 630 ? {
629 id: abuse.VideoCommentAbuse.VideoComment.Video.id, 631 id: abuse.VideoCommentAbuse.VideoComment.Video.id,
630 name: abuse.VideoCommentAbuse.VideoComment.Video.name, 632 name: abuse.VideoCommentAbuse.VideoComment.Video.name,
633 shortUUID: uuidToShort(abuse.VideoCommentAbuse.VideoComment.Video.uuid),
631 uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid 634 uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid
632 } 635 }
633 : undefined 636 : undefined
diff --git a/server/models/video/formatter/video-format-utils.ts b/server/models/video/formatter/video-format-utils.ts
index ab4cf53a8..8a54de3b0 100644
--- a/server/models/video/formatter/video-format-utils.ts
+++ b/server/models/video/formatter/video-format-utils.ts
@@ -182,8 +182,8 @@ function streamingPlaylistsModelToFormattedJSON (
182 return { 182 return {
183 id: playlist.id, 183 id: playlist.id,
184 type: playlist.type, 184 type: playlist.type,
185 playlistUrl: playlist.playlistUrl, 185 playlistUrl: playlist.getMasterPlaylistUrl(video),
186 segmentsSha256Url: playlist.segmentsSha256Url, 186 segmentsSha256Url: playlist.getSha256SegmentsUrl(video),
187 redundancies, 187 redundancies,
188 files 188 files
189 } 189 }
@@ -331,7 +331,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject {
331 type: 'Link', 331 type: 'Link',
332 name: 'sha256', 332 name: 'sha256',
333 mediaType: 'application/json' as 'application/json', 333 mediaType: 'application/json' as 'application/json',
334 href: playlist.segmentsSha256Url 334 href: playlist.getSha256SegmentsUrl(video)
335 }) 335 })
336 336
337 addVideoFilesInAPAcc(tag, video, playlist.VideoFiles || []) 337 addVideoFilesInAPAcc(tag, video, playlist.VideoFiles || [])
@@ -339,7 +339,7 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject {
339 url.push({ 339 url.push({
340 type: 'Link', 340 type: 'Link',
341 mediaType: 'application/x-mpegURL' as 'application/x-mpegURL', 341 mediaType: 'application/x-mpegURL' as 'application/x-mpegURL',
342 href: playlist.playlistUrl, 342 href: playlist.getMasterPlaylistUrl(video),
343 tag 343 tag
344 }) 344 })
345 } 345 }
diff --git a/server/models/video/sql/shared/video-tables.ts b/server/models/video/sql/shared/video-tables.ts
index abdd22188..742d19099 100644
--- a/server/models/video/sql/shared/video-tables.ts
+++ b/server/models/video/sql/shared/video-tables.ts
@@ -92,12 +92,13 @@ export class VideoTables {
92 } 92 }
93 93
94 getStreamingPlaylistAttributes () { 94 getStreamingPlaylistAttributes () {
95 let playlistKeys = [ 'id', 'playlistUrl', 'type' ] 95 let playlistKeys = [ 'id', 'playlistUrl', 'playlistFilename', 'type' ]
96 96
97 if (this.mode === 'get') { 97 if (this.mode === 'get') {
98 playlistKeys = playlistKeys.concat([ 98 playlistKeys = playlistKeys.concat([
99 'p2pMediaLoaderInfohashes', 99 'p2pMediaLoaderInfohashes',
100 'p2pMediaLoaderPeerVersion', 100 'p2pMediaLoaderPeerVersion',
101 'segmentsSha256Filename',
101 'segmentsSha256Url', 102 'segmentsSha256Url',
102 'videoId', 103 'videoId',
103 'createdAt', 104 'createdAt',
diff --git a/server/models/video/sql/videos-id-list-query-builder.ts b/server/models/video/sql/videos-id-list-query-builder.ts
index 30b251f0f..7625c003d 100644
--- a/server/models/video/sql/videos-id-list-query-builder.ts
+++ b/server/models/video/sql/videos-id-list-query-builder.ts
@@ -1,6 +1,7 @@
1import { Sequelize } from 'sequelize' 1import { Sequelize } from 'sequelize'
2import validator from 'validator' 2import validator from 'validator'
3import { exists } from '@server/helpers/custom-validators/misc' 3import { exists } from '@server/helpers/custom-validators/misc'
4import { WEBSERVER } from '@server/initializers/constants'
4import { buildDirectionAndField, createSafeIn } from '@server/models/utils' 5import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
5import { MUserAccountId, MUserId } from '@server/types/models' 6import { MUserAccountId, MUserId } from '@server/types/models'
6import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' 7import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
@@ -25,6 +26,7 @@ export type BuildVideosListQueryOptions = {
25 26
26 nsfw?: boolean 27 nsfw?: boolean
27 filter?: VideoFilter 28 filter?: VideoFilter
29 host?: string
28 isLive?: boolean 30 isLive?: boolean
29 31
30 categoryOneOf?: number[] 32 categoryOneOf?: number[]
@@ -33,6 +35,8 @@ export type BuildVideosListQueryOptions = {
33 tagsOneOf?: string[] 35 tagsOneOf?: string[]
34 tagsAllOf?: string[] 36 tagsAllOf?: string[]
35 37
38 uuids?: string[]
39
36 withFiles?: boolean 40 withFiles?: boolean
37 41
38 accountId?: number 42 accountId?: number
@@ -131,6 +135,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
131 this.whereOnlyLocal() 135 this.whereOnlyLocal()
132 } 136 }
133 137
138 if (options.host) {
139 this.whereHost(options.host)
140 }
141
134 if (options.accountId) { 142 if (options.accountId) {
135 this.whereAccountId(options.accountId) 143 this.whereAccountId(options.accountId)
136 } 144 }
@@ -155,6 +163,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
155 this.whereTagsAllOf(options.tagsAllOf) 163 this.whereTagsAllOf(options.tagsAllOf)
156 } 164 }
157 165
166 if (options.uuids) {
167 this.whereUUIDs(options.uuids)
168 }
169
158 if (options.nsfw === true) { 170 if (options.nsfw === true) {
159 this.whereNSFW() 171 this.whereNSFW()
160 } else if (options.nsfw === false) { 172 } else if (options.nsfw === false) {
@@ -291,6 +303,19 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
291 this.and.push('"video"."remote" IS FALSE') 303 this.and.push('"video"."remote" IS FALSE')
292 } 304 }
293 305
306 private whereHost (host: string) {
307 // Local instance
308 if (host === WEBSERVER.HOST) {
309 this.and.push('"accountActor"."serverId" IS NULL')
310 return
311 }
312
313 this.joins.push('INNER JOIN "server" ON "server"."id" = "accountActor"."serverId"')
314
315 this.and.push('"server"."host" = :host')
316 this.replacements.host = host
317 }
318
294 private whereAccountId (accountId: number) { 319 private whereAccountId (accountId: number) {
295 this.and.push('"account"."id" = :accountId') 320 this.and.push('"account"."id" = :accountId')
296 this.replacements.accountId = accountId 321 this.replacements.accountId = accountId
@@ -304,16 +329,16 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
304 private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) { 329 private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) {
305 let query = 330 let query =
306 '(' + 331 '(' +
307 ' EXISTS (' + 332 ' EXISTS (' + // Videos shared by actors we follow
308 ' SELECT 1 FROM "videoShare" ' + 333 ' SELECT 1 FROM "videoShare" ' +
309 ' INNER JOIN "actorFollow" "actorFollowShare" ON "actorFollowShare"."targetActorId" = "videoShare"."actorId" ' + 334 ' INNER JOIN "actorFollow" "actorFollowShare" ON "actorFollowShare"."targetActorId" = "videoShare"."actorId" ' +
310 ' AND "actorFollowShare"."actorId" = :followerActorId AND "actorFollowShare"."state" = \'accepted\' ' + 335 ' AND "actorFollowShare"."actorId" = :followerActorId AND "actorFollowShare"."state" = \'accepted\' ' +
311 ' WHERE "videoShare"."videoId" = "video"."id"' + 336 ' WHERE "videoShare"."videoId" = "video"."id"' +
312 ' )' + 337 ' )' +
313 ' OR' + 338 ' OR' +
314 ' EXISTS (' + 339 ' EXISTS (' + // Videos published by accounts we follow
315 ' SELECT 1 from "actorFollow" ' + 340 ' SELECT 1 from "actorFollow" ' +
316 ' WHERE "actorFollow"."targetActorId" = "videoChannel"."actorId" AND "actorFollow"."actorId" = :followerActorId ' + 341 ' WHERE "actorFollow"."targetActorId" = "account"."actorId" AND "actorFollow"."actorId" = :followerActorId ' +
317 ' AND "actorFollow"."state" = \'accepted\'' + 342 ' AND "actorFollow"."state" = \'accepted\'' +
318 ' )' 343 ' )'
319 344
@@ -367,6 +392,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
367 ) 392 )
368 } 393 }
369 394
395 private whereUUIDs (uuids: string[]) {
396 this.and.push('"video"."uuid" IN (' + createSafeIn(this.sequelize, uuids) + ')')
397 }
398
370 private whereCategoryOneOf (categoryOneOf: number[]) { 399 private whereCategoryOneOf (categoryOneOf: number[]) {
371 this.and.push('"video"."category" IN (:categoryOneOf)') 400 this.and.push('"video"."category" IN (:categoryOneOf)')
372 this.replacements.categoryOneOf = categoryOneOf 401 this.replacements.categoryOneOf = categoryOneOf
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 183e7448c..278149d60 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,4 +1,4 @@
1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction } from 'sequelize' 1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction, WhereOptions } from 'sequelize'
2import { 2import {
3 AllowNull, 3 AllowNull,
4 BeforeDestroy, 4 BeforeDestroy,
@@ -17,14 +17,13 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { setAsUpdated } from '@server/helpers/database-utils'
21import { MAccountActor } from '@server/types/models' 20import { MAccountActor } from '@server/types/models'
22import { AttributesOnly } from '@shared/core-utils' 21import { AttributesOnly, pick } from '@shared/core-utils'
23import { ActivityPubActor } from '../../../shared/models/activitypub' 22import { ActivityPubActor } from '../../../shared/models/activitypub'
24import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' 23import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
25import { 24import {
26 isVideoChannelDescriptionValid, 25 isVideoChannelDescriptionValid,
27 isVideoChannelNameValid, 26 isVideoChannelDisplayNameValid,
28 isVideoChannelSupportValid 27 isVideoChannelSupportValid
29} from '../../helpers/custom-validators/video-channels' 28} from '../../helpers/custom-validators/video-channels'
30import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' 29import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
@@ -41,6 +40,7 @@ import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor'
41import { ActorFollowModel } from '../actor/actor-follow' 40import { ActorFollowModel } from '../actor/actor-follow'
42import { ActorImageModel } from '../actor/actor-image' 41import { ActorImageModel } from '../actor/actor-image'
43import { ServerModel } from '../server/server' 42import { ServerModel } from '../server/server'
43import { setAsUpdated } from '../shared'
44import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' 44import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils'
45import { VideoModel } from './video' 45import { VideoModel } from './video'
46import { VideoPlaylistModel } from './video-playlist' 46import { VideoPlaylistModel } from './video-playlist'
@@ -58,6 +58,8 @@ export enum ScopeNames {
58type AvailableForListOptions = { 58type AvailableForListOptions = {
59 actorId: number 59 actorId: number
60 search?: string 60 search?: string
61 host?: string
62 handles?: string[]
61} 63}
62 64
63type AvailableWithStatsOptions = { 65type AvailableWithStatsOptions = {
@@ -83,7 +85,62 @@ export type SummaryOptions = {
83 // Only list local channels OR channels that are on an instance followed by actorId 85 // Only list local channels OR channels that are on an instance followed by actorId
84 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) 86 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId)
85 87
88 const whereActorAnd: WhereOptions[] = [
89 {
90 [Op.or]: [
91 {
92 serverId: null
93 },
94 {
95 serverId: {
96 [Op.in]: Sequelize.literal(inQueryInstanceFollow)
97 }
98 }
99 ]
100 }
101 ]
102
103 let serverRequired = false
104 let whereServer: WhereOptions
105
106 if (options.host && options.host !== WEBSERVER.HOST) {
107 serverRequired = true
108 whereServer = { host: options.host }
109 }
110
111 if (options.host === WEBSERVER.HOST) {
112 whereActorAnd.push({
113 serverId: null
114 })
115 }
116
117 let rootWhere: WhereOptions
118 if (options.handles) {
119 const or: WhereOptions[] = []
120
121 for (const handle of options.handles || []) {
122 const [ preferredUsername, host ] = handle.split('@')
123
124 if (!host) {
125 or.push({
126 '$Actor.preferredUsername$': preferredUsername,
127 '$Actor.serverId$': null
128 })
129 } else {
130 or.push({
131 '$Actor.preferredUsername$': preferredUsername,
132 '$Actor.Server.host$': host
133 })
134 }
135 }
136
137 rootWhere = {
138 [Op.or]: or
139 }
140 }
141
86 return { 142 return {
143 where: rootWhere,
87 include: [ 144 include: [
88 { 145 {
89 attributes: { 146 attributes: {
@@ -91,19 +148,20 @@ export type SummaryOptions = {
91 }, 148 },
92 model: ActorModel, 149 model: ActorModel,
93 where: { 150 where: {
94 [Op.or]: [ 151 [Op.and]: whereActorAnd
95 {
96 serverId: null
97 },
98 {
99 serverId: {
100 [Op.in]: Sequelize.literal(inQueryInstanceFollow)
101 }
102 }
103 ]
104 }, 152 },
105 include: [ 153 include: [
106 { 154 {
155 model: ServerModel,
156 required: serverRequired,
157 where: whereServer
158 },
159 {
160 model: ActorImageModel,
161 as: 'Avatar',
162 required: false
163 },
164 {
107 model: ActorImageModel, 165 model: ActorImageModel,
108 as: 'Banner', 166 as: 'Banner',
109 required: false 167 required: false
@@ -250,7 +308,7 @@ export type SummaryOptions = {
250export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannelModel>>> { 308export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannelModel>>> {
251 309
252 @AllowNull(false) 310 @AllowNull(false)
253 @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name')) 311 @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelDisplayNameValid, 'name'))
254 @Column 312 @Column
255 name: string 313 name: string
256 314
@@ -380,30 +438,6 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
380 } 438 }
381 } 439 }
382 440
383 static listForApi (parameters: {
384 actorId: number
385 start: number
386 count: number
387 sort: string
388 }) {
389 const { actorId } = parameters
390
391 const query = {
392 offset: parameters.start,
393 limit: parameters.count,
394 order: getSort(parameters.sort)
395 }
396
397 return VideoChannelModel
398 .scope({
399 method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
400 })
401 .findAndCountAll(query)
402 .then(({ rows, count }) => {
403 return { total: count, data: rows }
404 })
405 }
406
407 static listLocalsForSitemap (sort: string): Promise<MChannelActor[]> { 441 static listLocalsForSitemap (sort: string): Promise<MChannelActor[]> {
408 const query = { 442 const query = {
409 attributes: [ ], 443 attributes: [ ],
@@ -425,26 +459,43 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
425 .findAll(query) 459 .findAll(query)
426 } 460 }
427 461
428 static searchForApi (options: { 462 static listForApi (parameters: Pick<AvailableForListOptions, 'actorId'> & {
429 actorId: number
430 search: string
431 start: number 463 start: number
432 count: number 464 count: number
433 sort: string 465 sort: string
434 }) { 466 }) {
435 const attributesInclude = [] 467 const { actorId } = parameters
436 const escapedSearch = VideoChannelModel.sequelize.escape(options.search)
437 const escapedLikeSearch = VideoChannelModel.sequelize.escape('%' + options.search + '%')
438 attributesInclude.push(createSimilarityAttribute('VideoChannelModel.name', options.search))
439 468
440 const query = { 469 const query = {
441 attributes: { 470 offset: parameters.start,
442 include: attributesInclude 471 limit: parameters.count,
443 }, 472 order: getSort(parameters.sort)
444 offset: options.start, 473 }
445 limit: options.count, 474
446 order: getSort(options.sort), 475 return VideoChannelModel
447 where: { 476 .scope({
477 method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
478 })
479 .findAndCountAll(query)
480 .then(({ rows, count }) => {
481 return { total: count, data: rows }
482 })
483 }
484
485 static searchForApi (options: Pick<AvailableForListOptions, 'actorId' | 'search' | 'host' | 'handles'> & {
486 start: number
487 count: number
488 sort: string
489 }) {
490 let attributesInclude: any[] = [ literal('0 as similarity') ]
491 let where: WhereOptions
492
493 if (options.search) {
494 const escapedSearch = VideoChannelModel.sequelize.escape(options.search)
495 const escapedLikeSearch = VideoChannelModel.sequelize.escape('%' + options.search + '%')
496 attributesInclude = [ createSimilarityAttribute('VideoChannelModel.name', options.search) ]
497
498 where = {
448 [Op.or]: [ 499 [Op.or]: [
449 Sequelize.literal( 500 Sequelize.literal(
450 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))' 501 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))'
@@ -456,9 +507,19 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
456 } 507 }
457 } 508 }
458 509
510 const query = {
511 attributes: {
512 include: attributesInclude
513 },
514 offset: options.start,
515 limit: options.count,
516 order: getSort(options.sort),
517 where
518 }
519
459 return VideoChannelModel 520 return VideoChannelModel
460 .scope({ 521 .scope({
461 method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ] 522 method: [ ScopeNames.FOR_API, pick(options, [ 'actorId', 'host', 'handles' ]) as AvailableForListOptions ]
462 }) 523 })
463 .findAndCountAll(query) 524 .findAndCountAll(query)
464 .then(({ rows, count }) => { 525 .then(({ rows, count }) => {
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 22cf63804..09fc5288b 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -1,7 +1,7 @@
1import { remove } from 'fs-extra' 1import { remove } from 'fs-extra'
2import * as memoizee from 'memoizee' 2import * as memoizee from 'memoizee'
3import { join } from 'path' 3import { join } from 'path'
4import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize' 4import { FindOptions, Op, Transaction } from 'sequelize'
5import { 5import {
6 AllowNull, 6 AllowNull,
7 BelongsTo, 7 BelongsTo,
@@ -44,6 +44,7 @@ import {
44} from '../../initializers/constants' 44} from '../../initializers/constants'
45import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file' 45import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file'
46import { VideoRedundancyModel } from '../redundancy/video-redundancy' 46import { VideoRedundancyModel } from '../redundancy/video-redundancy'
47import { doesExist } from '../shared'
47import { parseAggregateResult, throwIfNotValid } from '../utils' 48import { parseAggregateResult, throwIfNotValid } from '../utils'
48import { VideoModel } from './video' 49import { VideoModel } from './video'
49import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 50import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
@@ -250,14 +251,8 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
250 251
251 static doesInfohashExist (infoHash: string) { 252 static doesInfohashExist (infoHash: string) {
252 const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1' 253 const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1'
253 const options = {
254 type: QueryTypes.SELECT as QueryTypes.SELECT,
255 bind: { infoHash },
256 raw: true
257 }
258 254
259 return VideoModel.sequelize.query(query, options) 255 return doesExist(query, { infoHash })
260 .then(results => results.length === 1)
261 } 256 }
262 257
263 static async doesVideoExistForVideoFile (id: number, videoIdOrUUID: number | string) { 258 static async doesVideoExistForVideoFile (id: number, videoIdOrUUID: number | string) {
@@ -266,6 +261,33 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
266 return !!videoFile 261 return !!videoFile
267 } 262 }
268 263
264 static async doesOwnedTorrentFileExist (filename: string) {
265 const query = 'SELECT 1 FROM "videoFile" ' +
266 'LEFT JOIN "video" "webtorrent" ON "webtorrent"."id" = "videoFile"."videoId" AND "webtorrent"."remote" IS FALSE ' +
267 'LEFT JOIN "videoStreamingPlaylist" ON "videoStreamingPlaylist"."id" = "videoFile"."videoStreamingPlaylistId" ' +
268 'LEFT JOIN "video" "hlsVideo" ON "hlsVideo"."id" = "videoStreamingPlaylist"."videoId" AND "hlsVideo"."remote" IS FALSE ' +
269 'WHERE "torrentFilename" = $filename AND ("hlsVideo"."id" IS NOT NULL OR "webtorrent"."id" IS NOT NULL) LIMIT 1'
270
271 return doesExist(query, { filename })
272 }
273
274 static async doesOwnedWebTorrentVideoFileExist (filename: string) {
275 const query = 'SELECT 1 FROM "videoFile" INNER JOIN "video" ON "video"."id" = "videoFile"."videoId" AND "video"."remote" IS FALSE ' +
276 'WHERE "filename" = $filename LIMIT 1'
277
278 return doesExist(query, { filename })
279 }
280
281 static loadByFilename (filename: string) {
282 const query = {
283 where: {
284 filename
285 }
286 }
287
288 return VideoFileModel.findOne(query)
289 }
290
269 static loadWithVideoOrPlaylistByTorrentFilename (filename: string) { 291 static loadWithVideoOrPlaylistByTorrentFilename (filename: string) {
270 const query = { 292 const query = {
271 where: { 293 where: {
@@ -443,10 +465,9 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
443 } 465 }
444 466
445 getFileDownloadUrl (video: MVideoWithHost) { 467 getFileDownloadUrl (video: MVideoWithHost) {
446 const basePath = this.isHLS() 468 const path = this.isHLS()
447 ? STATIC_DOWNLOAD_PATHS.HLS_VIDEOS 469 ? join(STATIC_DOWNLOAD_PATHS.HLS_VIDEOS, `${video.uuid}-${this.resolution}-fragmented${this.extname}`)
448 : STATIC_DOWNLOAD_PATHS.VIDEOS 470 : join(STATIC_DOWNLOAD_PATHS.VIDEOS, `${video.uuid}-${this.resolution}${this.extname}`)
449 const path = join(basePath, this.filename)
450 471
451 if (video.isOwned()) return WEBSERVER.URL + path 472 if (video.isOwned()) return WEBSERVER.URL + path
452 473
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index af81c9906..630684a88 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -17,10 +17,9 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { setAsUpdated } from '@server/helpers/database-utils'
21import { buildUUID, uuidToShort } from '@server/helpers/uuid' 20import { buildUUID, uuidToShort } from '@server/helpers/uuid'
22import { MAccountId, MChannelId } from '@server/types/models' 21import { MAccountId, MChannelId } from '@server/types/models'
23import { AttributesOnly } from '@shared/core-utils' 22import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistWatchPath, pick } from '@shared/core-utils'
24import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 23import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
25import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' 24import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
26import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 25import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
@@ -53,6 +52,7 @@ import {
53} from '../../types/models/video/video-playlist' 52} from '../../types/models/video/video-playlist'
54import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' 53import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account'
55import { ActorModel } from '../actor/actor' 54import { ActorModel } from '../actor/actor'
55import { setAsUpdated } from '../shared'
56import { 56import {
57 buildServerIdsFollowedBy, 57 buildServerIdsFollowedBy,
58 buildTrigramSearchIndex, 58 buildTrigramSearchIndex,
@@ -82,6 +82,8 @@ type AvailableForListOptions = {
82 videoChannelId?: number 82 videoChannelId?: number
83 listMyPlaylists?: boolean 83 listMyPlaylists?: boolean
84 search?: string 84 search?: string
85 host?: string
86 uuids?: string[]
85 withVideos?: boolean 87 withVideos?: boolean
86} 88}
87 89
@@ -141,9 +143,19 @@ function getVideoLengthSelect () {
141 ] 143 ]
142 }, 144 },
143 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { 145 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => {
146 const whereAnd: WhereOptions[] = []
147
148 const whereServer = options.host && options.host !== WEBSERVER.HOST
149 ? { host: options.host }
150 : undefined
151
144 let whereActor: WhereOptions = {} 152 let whereActor: WhereOptions = {}
145 153
146 const whereAnd: WhereOptions[] = [] 154 if (options.host === WEBSERVER.HOST) {
155 whereActor = {
156 [Op.and]: [ { serverId: null } ]
157 }
158 }
147 159
148 if (options.listMyPlaylists !== true) { 160 if (options.listMyPlaylists !== true) {
149 whereAnd.push({ 161 whereAnd.push({
@@ -168,9 +180,7 @@ function getVideoLengthSelect () {
168 }) 180 })
169 } 181 }
170 182
171 whereActor = { 183 Object.assign(whereActor, { [Op.or]: whereActorOr })
172 [Op.or]: whereActorOr
173 }
174 } 184 }
175 185
176 if (options.accountId) { 186 if (options.accountId) {
@@ -191,18 +201,26 @@ function getVideoLengthSelect () {
191 }) 201 })
192 } 202 }
193 203
204 if (options.uuids) {
205 whereAnd.push({
206 uuid: {
207 [Op.in]: options.uuids
208 }
209 })
210 }
211
194 if (options.withVideos === true) { 212 if (options.withVideos === true) {
195 whereAnd.push( 213 whereAnd.push(
196 literal(`(${getVideoLengthSelect()}) != 0`) 214 literal(`(${getVideoLengthSelect()}) != 0`)
197 ) 215 )
198 } 216 }
199 217
200 const attributesInclude = [] 218 let attributesInclude: any[] = [ literal('0 as similarity') ]
201 219
202 if (options.search) { 220 if (options.search) {
203 const escapedSearch = VideoPlaylistModel.sequelize.escape(options.search) 221 const escapedSearch = VideoPlaylistModel.sequelize.escape(options.search)
204 const escapedLikeSearch = VideoPlaylistModel.sequelize.escape('%' + options.search + '%') 222 const escapedLikeSearch = VideoPlaylistModel.sequelize.escape('%' + options.search + '%')
205 attributesInclude.push(createSimilarityAttribute('VideoPlaylistModel.name', options.search)) 223 attributesInclude = [ createSimilarityAttribute('VideoPlaylistModel.name', options.search) ]
206 224
207 whereAnd.push({ 225 whereAnd.push({
208 [Op.or]: [ 226 [Op.or]: [
@@ -228,7 +246,7 @@ function getVideoLengthSelect () {
228 include: [ 246 include: [
229 { 247 {
230 model: AccountModel.scope({ 248 model: AccountModel.scope({
231 method: [ AccountScopeNames.SUMMARY, { whereActor } as SummaryOptions ] 249 method: [ AccountScopeNames.SUMMARY, { whereActor, whereServer } as SummaryOptions ]
232 }), 250 }),
233 required: true 251 required: true
234 }, 252 },
@@ -339,17 +357,10 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
339 }) 357 })
340 Thumbnail: ThumbnailModel 358 Thumbnail: ThumbnailModel
341 359
342 static listForApi (options: { 360 static listForApi (options: AvailableForListOptions & {
343 followerActorId: number
344 start: number 361 start: number
345 count: number 362 count: number
346 sort: string 363 sort: string
347 type?: VideoPlaylistType
348 accountId?: number
349 videoChannelId?: number
350 listMyPlaylists?: boolean
351 search?: string
352 withVideos?: boolean // false by default
353 }) { 364 }) {
354 const query = { 365 const query = {
355 offset: options.start, 366 offset: options.start,
@@ -362,12 +373,8 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
362 method: [ 373 method: [
363 ScopeNames.AVAILABLE_FOR_LIST, 374 ScopeNames.AVAILABLE_FOR_LIST,
364 { 375 {
365 type: options.type, 376 ...pick(options, [ 'type', 'followerActorId', 'accountId', 'videoChannelId', 'listMyPlaylists', 'search', 'host', 'uuids' ]),
366 followerActorId: options.followerActorId, 377
367 accountId: options.accountId,
368 videoChannelId: options.videoChannelId,
369 listMyPlaylists: options.listMyPlaylists,
370 search: options.search,
371 withVideos: options.withVideos || false 378 withVideos: options.withVideos || false
372 } as AvailableForListOptions 379 } as AvailableForListOptions
373 ] 380 ]
@@ -384,15 +391,14 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
384 }) 391 })
385 } 392 }
386 393
387 static searchForApi (options: { 394 static searchForApi (options: Pick<AvailableForListOptions, 'followerActorId' | 'search'| 'host'| 'uuids'> & {
388 followerActorId: number
389 start: number 395 start: number
390 count: number 396 count: number
391 sort: string 397 sort: string
392 search?: string
393 }) { 398 }) {
394 return VideoPlaylistModel.listForApi({ 399 return VideoPlaylistModel.listForApi({
395 ...options, 400 ...options,
401
396 type: VideoPlaylistType.REGULAR, 402 type: VideoPlaylistType.REGULAR,
397 listMyPlaylists: false, 403 listMyPlaylists: false,
398 withVideos: true 404 withVideos: true
@@ -560,12 +566,12 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
560 return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename) 566 return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename)
561 } 567 }
562 568
563 getWatchUrl () { 569 getWatchStaticPath () {
564 return WEBSERVER.URL + '/w/p/' + this.uuid 570 return buildPlaylistWatchPath({ shortUUID: uuidToShort(this.uuid) })
565 } 571 }
566 572
567 getEmbedStaticPath () { 573 getEmbedStaticPath () {
568 return '/video-playlists/embed/' + this.uuid 574 return buildPlaylistEmbedPath(this)
569 } 575 }
570 576
571 static async getStats () { 577 static async getStats () {
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index d627e8c9d..d591a3134 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -1,19 +1,27 @@
1import * as memoizee from 'memoizee' 1import * as memoizee from 'memoizee'
2import { join } from 'path' 2import { join } from 'path'
3import { Op, QueryTypes } from 'sequelize' 3import { Op } from 'sequelize'
4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
5import { VideoFileModel } from '@server/models/video/video-file' 5import { VideoFileModel } from '@server/models/video/video-file'
6import { MStreamingPlaylist } from '@server/types/models' 6import { MStreamingPlaylist, MVideo } from '@server/types/models'
7import { AttributesOnly } from '@shared/core-utils'
7import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 8import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
8import { sha1 } from '../../helpers/core-utils' 9import { sha1 } from '../../helpers/core-utils'
9import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
10import { isArrayOf } from '../../helpers/custom-validators/misc' 11import { isArrayOf } from '../../helpers/custom-validators/misc'
11import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 12import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
12import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants' 13import {
14 CONSTRAINTS_FIELDS,
15 MEMOIZE_LENGTH,
16 MEMOIZE_TTL,
17 P2P_MEDIA_LOADER_PEER_VERSION,
18 STATIC_PATHS,
19 WEBSERVER
20} from '../../initializers/constants'
13import { VideoRedundancyModel } from '../redundancy/video-redundancy' 21import { VideoRedundancyModel } from '../redundancy/video-redundancy'
22import { doesExist } from '../shared'
14import { throwIfNotValid } from '../utils' 23import { throwIfNotValid } from '../utils'
15import { VideoModel } from './video' 24import { VideoModel } from './video'
16import { AttributesOnly } from '@shared/core-utils'
17 25
18@Table({ 26@Table({
19 tableName: 'videoStreamingPlaylist', 27 tableName: 'videoStreamingPlaylist',
@@ -43,7 +51,11 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
43 type: VideoStreamingPlaylistType 51 type: VideoStreamingPlaylistType
44 52
45 @AllowNull(false) 53 @AllowNull(false)
46 @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url')) 54 @Column
55 playlistFilename: string
56
57 @AllowNull(true)
58 @Is('PlaylistUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'playlist url', true))
47 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max)) 59 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
48 playlistUrl: string 60 playlistUrl: string
49 61
@@ -57,7 +69,11 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
57 p2pMediaLoaderPeerVersion: number 69 p2pMediaLoaderPeerVersion: number
58 70
59 @AllowNull(false) 71 @AllowNull(false)
60 @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url')) 72 @Column
73 segmentsSha256Filename: string
74
75 @AllowNull(true)
76 @Is('VideoStreamingSegmentsSha256Url', value => throwIfNotValid(value, isActivityPubUrlValid, 'segments sha256 url', true))
61 @Column 77 @Column
62 segmentsSha256Url: string 78 segmentsSha256Url: string
63 79
@@ -98,14 +114,8 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
98 114
99 static doesInfohashExist (infoHash: string) { 115 static doesInfohashExist (infoHash: string) {
100 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1' 116 const query = 'SELECT 1 FROM "videoStreamingPlaylist" WHERE $infoHash = ANY("p2pMediaLoaderInfohashes") LIMIT 1'
101 const options = {
102 type: QueryTypes.SELECT as QueryTypes.SELECT,
103 bind: { infoHash },
104 raw: true
105 }
106 117
107 return VideoModel.sequelize.query<object>(query, options) 118 return doesExist(query, { infoHash })
108 .then(results => results.length === 1)
109 } 119 }
110 120
111 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, files: unknown[]) { 121 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, files: unknown[]) {
@@ -125,7 +135,13 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
125 p2pMediaLoaderPeerVersion: { 135 p2pMediaLoaderPeerVersion: {
126 [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION 136 [Op.ne]: P2P_MEDIA_LOADER_PEER_VERSION
127 } 137 }
128 } 138 },
139 include: [
140 {
141 model: VideoModel.unscoped(),
142 required: true
143 }
144 ]
129 } 145 }
130 146
131 return VideoStreamingPlaylistModel.findAll(query) 147 return VideoStreamingPlaylistModel.findAll(query)
@@ -144,7 +160,7 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
144 return VideoStreamingPlaylistModel.findByPk(id, options) 160 return VideoStreamingPlaylistModel.findByPk(id, options)
145 } 161 }
146 162
147 static loadHLSPlaylistByVideo (videoId: number) { 163 static loadHLSPlaylistByVideo (videoId: number): Promise<MStreamingPlaylist> {
148 const options = { 164 const options = {
149 where: { 165 where: {
150 type: VideoStreamingPlaylistType.HLS, 166 type: VideoStreamingPlaylistType.HLS,
@@ -155,30 +171,29 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
155 return VideoStreamingPlaylistModel.findOne(options) 171 return VideoStreamingPlaylistModel.findOne(options)
156 } 172 }
157 173
158 static getHlsPlaylistFilename (resolution: number) { 174 static async loadOrGenerate (video: MVideo) {
159 return resolution + '.m3u8' 175 let playlist = await VideoStreamingPlaylistModel.loadHLSPlaylistByVideo(video.id)
160 } 176 if (!playlist) playlist = new VideoStreamingPlaylistModel()
161 177
162 static getMasterHlsPlaylistFilename () { 178 return Object.assign(playlist, { videoId: video.id, Video: video })
163 return 'master.m3u8'
164 } 179 }
165 180
166 static getHlsSha256SegmentsFilename () { 181 assignP2PMediaLoaderInfoHashes (video: MVideo, files: unknown[]) {
167 return 'segments-sha256.json' 182 const masterPlaylistUrl = this.getMasterPlaylistUrl(video)
168 }
169 183
170 static getHlsMasterPlaylistStaticPath (videoUUID: string) { 184 this.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(masterPlaylistUrl, files)
171 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
172 } 185 }
173 186
174 static getHlsPlaylistStaticPath (videoUUID: string, resolution: number) { 187 getMasterPlaylistUrl (video: MVideo) {
175 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) 188 if (video.isOwned()) return WEBSERVER.URL + this.getMasterPlaylistStaticPath(video.uuid)
189
190 return this.playlistUrl
176 } 191 }
177 192
178 static getHlsSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) { 193 getSha256SegmentsUrl (video: MVideo) {
179 if (isLive) return join('/live', 'segments-sha256', videoUUID) 194 if (video.isOwned()) return WEBSERVER.URL + this.getSha256SegmentsStaticPath(video.uuid, video.isLive)
180 195
181 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) 196 return this.segmentsSha256Url
182 } 197 }
183 198
184 getStringType () { 199 getStringType () {
@@ -195,4 +210,14 @@ export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<Vi
195 return this.type === other.type && 210 return this.type === other.type &&
196 this.videoId === other.videoId 211 this.videoId === other.videoId
197 } 212 }
213
214 private getMasterPlaylistStaticPath (videoUUID: string) {
215 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.playlistFilename)
216 }
217
218 private getSha256SegmentsStaticPath (videoUUID: string, isLive: boolean) {
219 if (isLive) return join('/live', 'segments-sha256', videoUUID)
220
221 return join(STATIC_PATHS.STREAMING_PLAYLISTS.HLS, videoUUID, this.segmentsSha256Filename)
222 }
198} 223}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 1e5648a36..56a5b0e18 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -24,14 +24,14 @@ import {
24 Table, 24 Table,
25 UpdatedAt 25 UpdatedAt
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { setAsUpdated } from '@server/helpers/database-utils'
28import { buildNSFWFilter } from '@server/helpers/express-utils' 27import { buildNSFWFilter } from '@server/helpers/express-utils'
28import { uuidToShort } from '@server/helpers/uuid'
29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
30import { LiveManager } from '@server/lib/live/live-manager' 30import { LiveManager } from '@server/lib/live/live-manager'
31import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' 31import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
32import { getServerActor } from '@server/models/application/application' 32import { getServerActor } from '@server/models/application/application'
33import { ModelCache } from '@server/models/model-cache' 33import { ModelCache } from '@server/models/model-cache'
34import { AttributesOnly } from '@shared/core-utils' 34import { AttributesOnly, buildVideoEmbedPath, buildVideoWatchPath, pick } from '@shared/core-utils'
35import { VideoFile } from '@shared/models/videos/video-file.model' 35import { VideoFile } from '@shared/models/videos/video-file.model'
36import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' 36import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
37import { VideoObject } from '../../../shared/models/activitypub/objects' 37import { VideoObject } from '../../../shared/models/activitypub/objects'
@@ -91,6 +91,7 @@ import { VideoRedundancyModel } from '../redundancy/video-redundancy'
91import { ServerModel } from '../server/server' 91import { ServerModel } from '../server/server'
92import { TrackerModel } from '../server/tracker' 92import { TrackerModel } from '../server/tracker'
93import { VideoTrackerModel } from '../server/video-tracker' 93import { VideoTrackerModel } from '../server/video-tracker'
94import { setAsUpdated } from '../shared'
94import { UserModel } from '../user/user' 95import { UserModel } from '../user/user'
95import { UserVideoHistoryModel } from '../user/user-video-history' 96import { UserVideoHistoryModel } from '../user/user-video-history'
96import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' 97import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils'
@@ -762,8 +763,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
762 763
763 // Remove physical files and torrents 764 // Remove physical files and torrents
764 instance.VideoFiles.forEach(file => { 765 instance.VideoFiles.forEach(file => {
765 tasks.push(instance.removeFile(file)) 766 tasks.push(instance.removeFileAndTorrent(file))
766 tasks.push(file.removeTorrent())
767 }) 767 })
768 768
769 // Remove playlists file 769 // Remove playlists file
@@ -1070,7 +1070,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1070 const trendingDays = options.sort.endsWith('trending') 1070 const trendingDays = options.sort.endsWith('trending')
1071 ? CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS 1071 ? CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
1072 : undefined 1072 : undefined
1073 let trendingAlgorithm 1073
1074 let trendingAlgorithm: string
1074 if (options.sort.endsWith('hot')) trendingAlgorithm = 'hot' 1075 if (options.sort.endsWith('hot')) trendingAlgorithm = 'hot'
1075 if (options.sort.endsWith('best')) trendingAlgorithm = 'best' 1076 if (options.sort.endsWith('best')) trendingAlgorithm = 'best'
1076 1077
@@ -1082,40 +1083,44 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1082 : serverActor.id 1083 : serverActor.id
1083 1084
1084 const queryOptions = { 1085 const queryOptions = {
1085 start: options.start, 1086 ...pick(options, [
1086 count: options.count, 1087 'start',
1087 sort: options.sort, 1088 'count',
1089 'sort',
1090 'nsfw',
1091 'isLive',
1092 'categoryOneOf',
1093 'licenceOneOf',
1094 'languageOneOf',
1095 'tagsOneOf',
1096 'tagsAllOf',
1097 'filter',
1098 'withFiles',
1099 'accountId',
1100 'videoChannelId',
1101 'videoPlaylistId',
1102 'includeLocalVideos',
1103 'user',
1104 'historyOfUser',
1105 'search'
1106 ]),
1107
1088 followerActorId, 1108 followerActorId,
1089 serverAccountId: serverActor.Account.id, 1109 serverAccountId: serverActor.Account.id,
1090 nsfw: options.nsfw,
1091 isLive: options.isLive,
1092 categoryOneOf: options.categoryOneOf,
1093 licenceOneOf: options.licenceOneOf,
1094 languageOneOf: options.languageOneOf,
1095 tagsOneOf: options.tagsOneOf,
1096 tagsAllOf: options.tagsAllOf,
1097 filter: options.filter,
1098 withFiles: options.withFiles,
1099 accountId: options.accountId,
1100 videoChannelId: options.videoChannelId,
1101 videoPlaylistId: options.videoPlaylistId,
1102 includeLocalVideos: options.includeLocalVideos,
1103 user: options.user,
1104 historyOfUser: options.historyOfUser,
1105 trendingDays, 1110 trendingDays,
1106 trendingAlgorithm, 1111 trendingAlgorithm
1107 search: options.search
1108 } 1112 }
1109 1113
1110 return VideoModel.getAvailableForApi(queryOptions, options.countVideos) 1114 return VideoModel.getAvailableForApi(queryOptions, options.countVideos)
1111 } 1115 }
1112 1116
1113 static async searchAndPopulateAccountAndServer (options: { 1117 static async searchAndPopulateAccountAndServer (options: {
1118 start: number
1119 count: number
1120 sort: string
1114 includeLocalVideos: boolean 1121 includeLocalVideos: boolean
1115 search?: string 1122 search?: string
1116 start?: number 1123 host?: string
1117 count?: number
1118 sort?: string
1119 startDate?: string // ISO 8601 1124 startDate?: string // ISO 8601
1120 endDate?: string // ISO 8601 1125 endDate?: string // ISO 8601
1121 originallyPublishedStartDate?: string 1126 originallyPublishedStartDate?: string
@@ -1131,41 +1136,38 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1131 durationMax?: number // seconds 1136 durationMax?: number // seconds
1132 user?: MUserAccountId 1137 user?: MUserAccountId
1133 filter?: VideoFilter 1138 filter?: VideoFilter
1139 uuids?: string[]
1134 }) { 1140 }) {
1135 const serverActor = await getServerActor() 1141 const serverActor = await getServerActor()
1136 1142
1137 const queryOptions = { 1143 const queryOptions = {
1138 followerActorId: serverActor.id, 1144 ...pick(options, [
1139 serverAccountId: serverActor.Account.id, 1145 'includeLocalVideos',
1140 1146 'nsfw',
1141 includeLocalVideos: options.includeLocalVideos, 1147 'isLive',
1142 nsfw: options.nsfw, 1148 'categoryOneOf',
1143 isLive: options.isLive, 1149 'licenceOneOf',
1144 1150 'languageOneOf',
1145 categoryOneOf: options.categoryOneOf, 1151 'tagsOneOf',
1146 licenceOneOf: options.licenceOneOf, 1152 'tagsAllOf',
1147 languageOneOf: options.languageOneOf, 1153 'user',
1154 'filter',
1155 'host',
1156 'start',
1157 'count',
1158 'sort',
1159 'startDate',
1160 'endDate',
1161 'originallyPublishedStartDate',
1162 'originallyPublishedEndDate',
1163 'durationMin',
1164 'durationMax',
1165 'uuids',
1166 'search'
1167 ]),
1148 1168
1149 tagsOneOf: options.tagsOneOf, 1169 followerActorId: serverActor.id,
1150 tagsAllOf: options.tagsAllOf, 1170 serverAccountId: serverActor.Account.id
1151
1152 user: options.user,
1153 filter: options.filter,
1154
1155 start: options.start,
1156 count: options.count,
1157 sort: options.sort,
1158
1159 startDate: options.startDate,
1160 endDate: options.endDate,
1161
1162 originallyPublishedStartDate: options.originallyPublishedStartDate,
1163 originallyPublishedEndDate: options.originallyPublishedEndDate,
1164
1165 durationMin: options.durationMin,
1166 durationMax: options.durationMax,
1167
1168 search: options.search
1169 } 1171 }
1170 1172
1171 return VideoModel.getAvailableForApi(queryOptions) 1173 return VideoModel.getAvailableForApi(queryOptions)
@@ -1579,11 +1581,11 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1579 } 1581 }
1580 1582
1581 getWatchStaticPath () { 1583 getWatchStaticPath () {
1582 return '/w/' + this.uuid 1584 return buildVideoWatchPath({ shortUUID: uuidToShort(this.uuid) })
1583 } 1585 }
1584 1586
1585 getEmbedStaticPath () { 1587 getEmbedStaticPath () {
1586 return '/videos/embed/' + this.uuid 1588 return buildVideoEmbedPath(this)
1587 } 1589 }
1588 1590
1589 getMiniatureStaticPath () { 1591 getMiniatureStaticPath () {
@@ -1670,10 +1672,13 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1670 .concat(toAdd) 1672 .concat(toAdd)
1671 } 1673 }
1672 1674
1673 removeFile (videoFile: MVideoFile, isRedundancy = false) { 1675 removeFileAndTorrent (videoFile: MVideoFile, isRedundancy = false) {
1674 const filePath = getVideoFilePath(this, videoFile, isRedundancy) 1676 const filePath = getVideoFilePath(this, videoFile, isRedundancy)
1675 return remove(filePath) 1677
1676 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) 1678 const promises: Promise<any>[] = [ remove(filePath) ]
1679 if (!isRedundancy) promises.push(videoFile.removeTorrent())
1680
1681 return Promise.all(promises)
1677 } 1682 }
1678 1683
1679 async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) { 1684 async removeStreamingPlaylistFiles (streamingPlaylist: MStreamingPlaylist, isRedundancy = false) {
diff --git a/server/tests/api/activitypub/cleaner.ts b/server/tests/api/activitypub/cleaner.ts
index 75ef56ce3..51cf6e599 100644
--- a/server/tests/api/activitypub/cleaner.ts
+++ b/server/tests/api/activitypub/cleaner.ts
@@ -4,24 +4,18 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 closeAllSequelize, 7 createMultipleServers,
8 deleteAll,
9 doubleFollow, 8 doubleFollow,
10 getCount, 9 PeerTubeServer,
11 selectQuery, 10 setAccessTokensToServers,
12 setVideoField, 11 wait,
13 updateQuery, 12 waitJobs
14 wait 13} from '@shared/extra-utils'
15} from '../../../../shared/extra-utils'
16import { flushAndRunMultipleServers, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import { addVideoCommentThread, getVideoCommentThreads } from '../../../../shared/extra-utils/videos/video-comments'
19import { getVideo, rateVideo, uploadVideoAndGetId } from '../../../../shared/extra-utils/videos/videos'
20 14
21const expect = chai.expect 15const expect = chai.expect
22 16
23describe('Test AP cleaner', function () { 17describe('Test AP cleaner', function () {
24 let servers: ServerInfo[] = [] 18 let servers: PeerTubeServer[] = []
25 let videoUUID1: string 19 let videoUUID1: string
26 let videoUUID2: string 20 let videoUUID2: string
27 let videoUUID3: string 21 let videoUUID3: string
@@ -36,7 +30,7 @@ describe('Test AP cleaner', function () {
36 videos: { cleanup_remote_interactions: true } 30 videos: { cleanup_remote_interactions: true }
37 } 31 }
38 } 32 }
39 servers = await flushAndRunMultipleServers(3, config) 33 servers = await createMultipleServers(3, config)
40 34
41 // Get the access tokens 35 // Get the access tokens
42 await setAccessTokensToServers(servers) 36 await setAccessTokensToServers(servers)
@@ -52,9 +46,9 @@ describe('Test AP cleaner', function () {
52 // Create 1 comment per video 46 // Create 1 comment per video
53 // Update 1 remote URL and 1 local URL on 47 // Update 1 remote URL and 1 local URL on
54 48
55 videoUUID1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'server 1' })).uuid 49 videoUUID1 = (await servers[0].videos.quickUpload({ name: 'server 1' })).uuid
56 videoUUID2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' })).uuid 50 videoUUID2 = (await servers[1].videos.quickUpload({ name: 'server 2' })).uuid
57 videoUUID3 = (await uploadVideoAndGetId({ server: servers[2], videoName: 'server 3' })).uuid 51 videoUUID3 = (await servers[2].videos.quickUpload({ name: 'server 3' })).uuid
58 52
59 videoUUIDs = [ videoUUID1, videoUUID2, videoUUID3 ] 53 videoUUIDs = [ videoUUID1, videoUUID2, videoUUID3 ]
60 54
@@ -62,8 +56,8 @@ describe('Test AP cleaner', function () {
62 56
63 for (const server of servers) { 57 for (const server of servers) {
64 for (const uuid of videoUUIDs) { 58 for (const uuid of videoUUIDs) {
65 await rateVideo(server.url, server.accessToken, uuid, 'like') 59 await server.videos.rate({ id: uuid, rating: 'like' })
66 await addVideoCommentThread(server.url, server.accessToken, uuid, 'comment') 60 await server.comments.createThread({ videoId: uuid, text: 'comment' })
67 } 61 }
68 } 62 }
69 63
@@ -73,9 +67,10 @@ describe('Test AP cleaner', function () {
73 it('Should have the correct likes', async function () { 67 it('Should have the correct likes', async function () {
74 for (const server of servers) { 68 for (const server of servers) {
75 for (const uuid of videoUUIDs) { 69 for (const uuid of videoUUIDs) {
76 const res = await getVideo(server.url, uuid) 70 const video = await server.videos.get({ id: uuid })
77 expect(res.body.likes).to.equal(3) 71
78 expect(res.body.dislikes).to.equal(0) 72 expect(video.likes).to.equal(3)
73 expect(video.dislikes).to.equal(0)
79 } 74 }
80 } 75 }
81 }) 76 })
@@ -83,9 +78,9 @@ describe('Test AP cleaner', function () {
83 it('Should destroy server 3 internal likes and correctly clean them', async function () { 78 it('Should destroy server 3 internal likes and correctly clean them', async function () {
84 this.timeout(20000) 79 this.timeout(20000)
85 80
86 await deleteAll(servers[2].internalServerNumber, 'accountVideoRate') 81 await servers[2].sql.deleteAll('accountVideoRate')
87 for (const uuid of videoUUIDs) { 82 for (const uuid of videoUUIDs) {
88 await setVideoField(servers[2].internalServerNumber, uuid, 'likes', '0') 83 await servers[2].sql.setVideoField(uuid, 'likes', '0')
89 } 84 }
90 85
91 await wait(5000) 86 await wait(5000)
@@ -93,16 +88,16 @@ describe('Test AP cleaner', function () {
93 88
94 // Updated rates of my video 89 // Updated rates of my video
95 { 90 {
96 const res = await getVideo(servers[0].url, videoUUID1) 91 const video = await servers[0].videos.get({ id: videoUUID1 })
97 expect(res.body.likes).to.equal(2) 92 expect(video.likes).to.equal(2)
98 expect(res.body.dislikes).to.equal(0) 93 expect(video.dislikes).to.equal(0)
99 } 94 }
100 95
101 // Did not update rates of a remote video 96 // Did not update rates of a remote video
102 { 97 {
103 const res = await getVideo(servers[0].url, videoUUID2) 98 const video = await servers[0].videos.get({ id: videoUUID2 })
104 expect(res.body.likes).to.equal(3) 99 expect(video.likes).to.equal(3)
105 expect(res.body.dislikes).to.equal(0) 100 expect(video.dislikes).to.equal(0)
106 } 101 }
107 }) 102 })
108 103
@@ -111,7 +106,7 @@ describe('Test AP cleaner', function () {
111 106
112 for (const server of servers) { 107 for (const server of servers) {
113 for (const uuid of videoUUIDs) { 108 for (const uuid of videoUUIDs) {
114 await rateVideo(server.url, server.accessToken, uuid, 'dislike') 109 await server.videos.rate({ id: uuid, rating: 'dislike' })
115 } 110 }
116 } 111 }
117 112
@@ -119,9 +114,9 @@ describe('Test AP cleaner', function () {
119 114
120 for (const server of servers) { 115 for (const server of servers) {
121 for (const uuid of videoUUIDs) { 116 for (const uuid of videoUUIDs) {
122 const res = await getVideo(server.url, uuid) 117 const video = await server.videos.get({ id: uuid })
123 expect(res.body.likes).to.equal(0) 118 expect(video.likes).to.equal(0)
124 expect(res.body.dislikes).to.equal(3) 119 expect(video.dislikes).to.equal(3)
125 } 120 }
126 } 121 }
127 }) 122 })
@@ -129,10 +124,10 @@ describe('Test AP cleaner', function () {
129 it('Should destroy server 3 internal dislikes and correctly clean them', async function () { 124 it('Should destroy server 3 internal dislikes and correctly clean them', async function () {
130 this.timeout(20000) 125 this.timeout(20000)
131 126
132 await deleteAll(servers[2].internalServerNumber, 'accountVideoRate') 127 await servers[2].sql.deleteAll('accountVideoRate')
133 128
134 for (const uuid of videoUUIDs) { 129 for (const uuid of videoUUIDs) {
135 await setVideoField(servers[2].internalServerNumber, uuid, 'dislikes', '0') 130 await servers[2].sql.setVideoField(uuid, 'dislikes', '0')
136 } 131 }
137 132
138 await wait(5000) 133 await wait(5000)
@@ -140,31 +135,31 @@ describe('Test AP cleaner', function () {
140 135
141 // Updated rates of my video 136 // Updated rates of my video
142 { 137 {
143 const res = await getVideo(servers[0].url, videoUUID1) 138 const video = await servers[0].videos.get({ id: videoUUID1 })
144 expect(res.body.likes).to.equal(0) 139 expect(video.likes).to.equal(0)
145 expect(res.body.dislikes).to.equal(2) 140 expect(video.dislikes).to.equal(2)
146 } 141 }
147 142
148 // Did not update rates of a remote video 143 // Did not update rates of a remote video
149 { 144 {
150 const res = await getVideo(servers[0].url, videoUUID2) 145 const video = await servers[0].videos.get({ id: videoUUID2 })
151 expect(res.body.likes).to.equal(0) 146 expect(video.likes).to.equal(0)
152 expect(res.body.dislikes).to.equal(3) 147 expect(video.dislikes).to.equal(3)
153 } 148 }
154 }) 149 })
155 150
156 it('Should destroy server 3 internal shares and correctly clean them', async function () { 151 it('Should destroy server 3 internal shares and correctly clean them', async function () {
157 this.timeout(20000) 152 this.timeout(20000)
158 153
159 const preCount = await getCount(servers[0].internalServerNumber, 'videoShare') 154 const preCount = await servers[0].sql.getCount('videoShare')
160 expect(preCount).to.equal(6) 155 expect(preCount).to.equal(6)
161 156
162 await deleteAll(servers[2].internalServerNumber, 'videoShare') 157 await servers[2].sql.deleteAll('videoShare')
163 await wait(5000) 158 await wait(5000)
164 await waitJobs(servers) 159 await waitJobs(servers)
165 160
166 // Still 6 because we don't have remote shares on local videos 161 // Still 6 because we don't have remote shares on local videos
167 const postCount = await getCount(servers[0].internalServerNumber, 'videoShare') 162 const postCount = await servers[0].sql.getCount('videoShare')
168 expect(postCount).to.equal(6) 163 expect(postCount).to.equal(6)
169 }) 164 })
170 165
@@ -172,18 +167,18 @@ describe('Test AP cleaner', function () {
172 this.timeout(20000) 167 this.timeout(20000)
173 168
174 { 169 {
175 const res = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 5) 170 const { total } = await servers[0].comments.listThreads({ videoId: videoUUID1 })
176 expect(res.body.total).to.equal(3) 171 expect(total).to.equal(3)
177 } 172 }
178 173
179 await deleteAll(servers[2].internalServerNumber, 'videoComment') 174 await servers[2].sql.deleteAll('videoComment')
180 175
181 await wait(5000) 176 await wait(5000)
182 await waitJobs(servers) 177 await waitJobs(servers)
183 178
184 { 179 {
185 const res = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 5) 180 const { total } = await servers[0].comments.listThreads({ videoId: videoUUID1 })
186 expect(res.body.total).to.equal(2) 181 expect(total).to.equal(2)
187 } 182 }
188 }) 183 })
189 184
@@ -193,7 +188,7 @@ describe('Test AP cleaner', function () {
193 async function check (like: string, ofServerUrl: string, urlSuffix: string, remote: 'true' | 'false') { 188 async function check (like: string, ofServerUrl: string, urlSuffix: string, remote: 'true' | 'false') {
194 const query = `SELECT "videoId", "accountVideoRate".url FROM "accountVideoRate" ` + 189 const query = `SELECT "videoId", "accountVideoRate".url FROM "accountVideoRate" ` +
195 `INNER JOIN video ON "accountVideoRate"."videoId" = video.id AND remote IS ${remote} WHERE "accountVideoRate"."url" LIKE '${like}'` 190 `INNER JOIN video ON "accountVideoRate"."videoId" = video.id AND remote IS ${remote} WHERE "accountVideoRate"."url" LIKE '${like}'`
196 const res = await selectQuery(servers[0].internalServerNumber, query) 191 const res = await servers[0].sql.selectQuery(query)
197 192
198 for (const rate of res) { 193 for (const rate of res) {
199 const matcher = new RegExp(`^${ofServerUrl}/accounts/root/dislikes/\\d+${urlSuffix}$`) 194 const matcher = new RegExp(`^${ofServerUrl}/accounts/root/dislikes/\\d+${urlSuffix}$`)
@@ -222,7 +217,7 @@ describe('Test AP cleaner', function () {
222 217
223 { 218 {
224 const query = `UPDATE "accountVideoRate" SET url = url || 'stan'` 219 const query = `UPDATE "accountVideoRate" SET url = url || 'stan'`
225 await updateQuery(servers[1].internalServerNumber, query) 220 await servers[1].sql.updateQuery(query)
226 221
227 await wait(5000) 222 await wait(5000)
228 await waitJobs(servers) 223 await waitJobs(servers)
@@ -239,7 +234,7 @@ describe('Test AP cleaner', function () {
239 const query = `SELECT "videoId", "videoComment".url, uuid as "videoUUID" FROM "videoComment" ` + 234 const query = `SELECT "videoId", "videoComment".url, uuid as "videoUUID" FROM "videoComment" ` +
240 `INNER JOIN video ON "videoComment"."videoId" = video.id AND remote IS ${remote} WHERE "videoComment"."url" LIKE '${like}'` 235 `INNER JOIN video ON "videoComment"."videoId" = video.id AND remote IS ${remote} WHERE "videoComment"."url" LIKE '${like}'`
241 236
242 const res = await selectQuery(servers[0].internalServerNumber, query) 237 const res = await servers[0].sql.selectQuery(query)
243 238
244 for (const comment of res) { 239 for (const comment of res) {
245 const matcher = new RegExp(`${ofServerUrl}/videos/watch/${comment.videoUUID}/comments/\\d+${urlSuffix}`) 240 const matcher = new RegExp(`${ofServerUrl}/videos/watch/${comment.videoUUID}/comments/\\d+${urlSuffix}`)
@@ -265,7 +260,7 @@ describe('Test AP cleaner', function () {
265 260
266 { 261 {
267 const query = `UPDATE "videoComment" SET url = url || 'kyle'` 262 const query = `UPDATE "videoComment" SET url = url || 'kyle'`
268 await updateQuery(servers[1].internalServerNumber, query) 263 await servers[1].sql.updateQuery(query)
269 264
270 await wait(5000) 265 await wait(5000)
271 await waitJobs(servers) 266 await waitJobs(servers)
@@ -277,7 +272,5 @@ describe('Test AP cleaner', function () {
277 272
278 after(async function () { 273 after(async function () {
279 await cleanupTests(servers) 274 await cleanupTests(servers)
280
281 await closeAllSequelize(servers)
282 }) 275 })
283}) 276})
diff --git a/server/tests/api/activitypub/client.ts b/server/tests/api/activitypub/client.ts
index be94e219c..c3e4b7f74 100644
--- a/server/tests/api/activitypub/client.ts
+++ b/server/tests/api/activitypub/client.ts
@@ -2,24 +2,21 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoPlaylistPrivacy } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
8 cleanupTests, 6 cleanupTests,
9 createVideoPlaylist, 7 createMultipleServers,
10 doubleFollow, 8 doubleFollow,
11 flushAndRunMultipleServers,
12 makeActivityPubGetRequest, 9 makeActivityPubGetRequest,
13 ServerInfo, 10 PeerTubeServer,
14 setAccessTokensToServers, 11 setAccessTokensToServers,
15 setDefaultVideoChannel, 12 setDefaultVideoChannel
16 uploadVideoAndGetId 13} from '@shared/extra-utils'
17} from '../../../../shared/extra-utils' 14import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
18 15
19const expect = chai.expect 16const expect = chai.expect
20 17
21describe('Test activitypub', function () { 18describe('Test activitypub', function () {
22 let servers: ServerInfo[] = [] 19 let servers: PeerTubeServer[] = []
23 let video: { id: number, uuid: string, shortUUID: string } 20 let video: { id: number, uuid: string, shortUUID: string }
24 let playlist: { id: number, uuid: string, shortUUID: string } 21 let playlist: { id: number, uuid: string, shortUUID: string }
25 22
@@ -64,19 +61,18 @@ describe('Test activitypub', function () {
64 before(async function () { 61 before(async function () {
65 this.timeout(30000) 62 this.timeout(30000)
66 63
67 servers = await flushAndRunMultipleServers(2) 64 servers = await createMultipleServers(2)
68 65
69 await setAccessTokensToServers(servers) 66 await setAccessTokensToServers(servers)
70 await setDefaultVideoChannel(servers) 67 await setDefaultVideoChannel(servers)
71 68
72 { 69 {
73 video = await uploadVideoAndGetId({ server: servers[0], videoName: 'video' }) 70 video = await servers[0].videos.quickUpload({ name: 'video' })
74 } 71 }
75 72
76 { 73 {
77 const playlistAttrs = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].videoChannel.id } 74 const attributes = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].store.channel.id }
78 const resCreate = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) 75 playlist = await servers[0].playlists.create({ attributes })
79 playlist = resCreate.body.videoPlaylist
80 } 76 }
81 77
82 await doubleFollow(servers[0], servers[1]) 78 await doubleFollow(servers[0], servers[1])
diff --git a/server/tests/api/activitypub/fetch.ts b/server/tests/api/activitypub/fetch.ts
index 35fd94eed..422a75d6e 100644
--- a/server/tests/api/activitypub/fetch.ts
+++ b/server/tests/api/activitypub/fetch.ts
@@ -1,61 +1,44 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import {
6 cleanupTests,
7 closeAllSequelize,
8 createUser,
9 doubleFollow,
10 flushAndRunMultipleServers,
11 getVideosListSort,
12 ServerInfo,
13 setAccessTokensToServers,
14 setActorField,
15 setVideoField,
16 uploadVideo,
17 userLogin,
18 waitJobs
19} from '../../../../shared/extra-utils'
20import * as chai from 'chai' 4import * as chai from 'chai'
21import { Video } from '../../../../shared/models/videos' 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
22 6
23const expect = chai.expect 7const expect = chai.expect
24 8
25describe('Test ActivityPub fetcher', function () { 9describe('Test ActivityPub fetcher', function () {
26 let servers: ServerInfo[] 10 let servers: PeerTubeServer[]
27 11
28 // --------------------------------------------------------------- 12 // ---------------------------------------------------------------
29 13
30 before(async function () { 14 before(async function () {
31 this.timeout(60000) 15 this.timeout(60000)
32 16
33 servers = await flushAndRunMultipleServers(3) 17 servers = await createMultipleServers(3)
34 18
35 // Get the access tokens 19 // Get the access tokens
36 await setAccessTokensToServers(servers) 20 await setAccessTokensToServers(servers)
37 21
38 const user = { username: 'user1', password: 'password' } 22 const user = { username: 'user1', password: 'password' }
39 for (const server of servers) { 23 for (const server of servers) {
40 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 24 await server.users.create({ username: user.username, password: user.password })
41 } 25 }
42 26
43 const userAccessToken = await userLogin(servers[0], user) 27 const userAccessToken = await servers[0].login.getAccessToken(user)
44 28
45 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video root' }) 29 await servers[0].videos.upload({ attributes: { name: 'video root' } })
46 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'bad video root' }) 30 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'bad video root' } })
47 const badVideoUUID = res.body.video.uuid 31 await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'video user' } })
48 await uploadVideo(servers[0].url, userAccessToken, { name: 'video user' })
49 32
50 { 33 {
51 const to = 'http://localhost:' + servers[0].port + '/accounts/user1' 34 const to = 'http://localhost:' + servers[0].port + '/accounts/user1'
52 const value = 'http://localhost:' + servers[1].port + '/accounts/user1' 35 const value = 'http://localhost:' + servers[1].port + '/accounts/user1'
53 await setActorField(servers[0].internalServerNumber, to, 'url', value) 36 await servers[0].sql.setActorField(to, 'url', value)
54 } 37 }
55 38
56 { 39 {
57 const value = 'http://localhost:' + servers[2].port + '/videos/watch/' + badVideoUUID 40 const value = 'http://localhost:' + servers[2].port + '/videos/watch/' + uuid
58 await setVideoField(servers[0].internalServerNumber, badVideoUUID, 'url', value) 41 await servers[0].sql.setVideoField(uuid, 'url', value)
59 } 42 }
60 }) 43 })
61 44
@@ -66,20 +49,18 @@ describe('Test ActivityPub fetcher', function () {
66 await waitJobs(servers) 49 await waitJobs(servers)
67 50
68 { 51 {
69 const res = await getVideosListSort(servers[0].url, 'createdAt') 52 const { total, data } = await servers[0].videos.list({ sort: 'createdAt' })
70 expect(res.body.total).to.equal(3)
71 53
72 const data: Video[] = res.body.data 54 expect(total).to.equal(3)
73 expect(data[0].name).to.equal('video root') 55 expect(data[0].name).to.equal('video root')
74 expect(data[1].name).to.equal('bad video root') 56 expect(data[1].name).to.equal('bad video root')
75 expect(data[2].name).to.equal('video user') 57 expect(data[2].name).to.equal('video user')
76 } 58 }
77 59
78 { 60 {
79 const res = await getVideosListSort(servers[1].url, 'createdAt') 61 const { total, data } = await servers[1].videos.list({ sort: 'createdAt' })
80 expect(res.body.total).to.equal(1)
81 62
82 const data: Video[] = res.body.data 63 expect(total).to.equal(1)
83 expect(data[0].name).to.equal('video root') 64 expect(data[0].name).to.equal('video root')
84 } 65 }
85 }) 66 })
@@ -88,7 +69,5 @@ describe('Test ActivityPub fetcher', function () {
88 this.timeout(20000) 69 this.timeout(20000)
89 70
90 await cleanupTests(servers) 71 await cleanupTests(servers)
91
92 await closeAllSequelize(servers)
93 }) 72 })
94}) 73})
diff --git a/server/tests/api/activitypub/helpers.ts b/server/tests/api/activitypub/helpers.ts
index 66d7631b7..57b1cab23 100644
--- a/server/tests/api/activitypub/helpers.ts
+++ b/server/tests/api/activitypub/helpers.ts
@@ -2,11 +2,10 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { buildRequestStub } from '../../../../shared/extra-utils/miscs/stubs'
6import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto'
7import { cloneDeep } from 'lodash' 5import { cloneDeep } from 'lodash'
6import { buildAbsoluteFixturePath, buildRequestStub } from '@shared/extra-utils'
8import { buildSignedActivity } from '../../../helpers/activitypub' 7import { buildSignedActivity } from '../../../helpers/activitypub'
9import { buildAbsoluteFixturePath } from '@shared/extra-utils' 8import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto'
10 9
11describe('Test activity pub helpers', function () { 10describe('Test activity pub helpers', function () {
12 describe('When checking the Linked Signature', function () { 11 describe('When checking the Linked Signature', function () {
diff --git a/server/tests/api/activitypub/refresher.ts b/server/tests/api/activitypub/refresher.ts
index c717f1a30..81fee0044 100644
--- a/server/tests/api/activitypub/refresher.ts
+++ b/server/tests/api/activitypub/refresher.ts
@@ -2,32 +2,20 @@
2 2
3import 'mocha' 3import 'mocha'
4import { 4import {
5 cleanupTests, closeAllSequelize, 5 cleanupTests,
6 createVideoPlaylist, 6 createMultipleServers,
7 doubleFollow, 7 doubleFollow,
8 flushAndRunMultipleServers,
9 generateUserAccessToken,
10 getVideo,
11 getVideoPlaylist,
12 killallServers, 8 killallServers,
13 reRunServer, 9 PeerTubeServer,
14 ServerInfo,
15 setAccessTokensToServers, 10 setAccessTokensToServers,
16 setActorField,
17 setDefaultVideoChannel, 11 setDefaultVideoChannel,
18 setPlaylistField,
19 setVideoField,
20 uploadVideo,
21 uploadVideoAndGetId,
22 wait, 12 wait,
23 waitJobs 13 waitJobs
24} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
25import { getAccount } from '../../../../shared/extra-utils/users/accounts' 15import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
26import { VideoPlaylistPrivacy } from '../../../../shared/models/videos'
27import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
28 16
29describe('Test AP refresher', function () { 17describe('Test AP refresher', function () {
30 let servers: ServerInfo[] = [] 18 let servers: PeerTubeServer[] = []
31 let videoUUID1: string 19 let videoUUID1: string
32 let videoUUID2: string 20 let videoUUID2: string
33 let videoUUID3: string 21 let videoUUID3: string
@@ -37,36 +25,36 @@ describe('Test AP refresher', function () {
37 before(async function () { 25 before(async function () {
38 this.timeout(60000) 26 this.timeout(60000)
39 27
40 servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: false } }) 28 servers = await createMultipleServers(2, { transcoding: { enabled: false } })
41 29
42 // Get the access tokens 30 // Get the access tokens
43 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
44 await setDefaultVideoChannel(servers) 32 await setDefaultVideoChannel(servers)
45 33
46 { 34 {
47 videoUUID1 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video1' })).uuid 35 videoUUID1 = (await servers[1].videos.quickUpload({ name: 'video1' })).uuid
48 videoUUID2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video2' })).uuid 36 videoUUID2 = (await servers[1].videos.quickUpload({ name: 'video2' })).uuid
49 videoUUID3 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video3' })).uuid 37 videoUUID3 = (await servers[1].videos.quickUpload({ name: 'video3' })).uuid
50 } 38 }
51 39
52 { 40 {
53 const a1 = await generateUserAccessToken(servers[1], 'user1') 41 const token1 = await servers[1].users.generateUserAndToken('user1')
54 await uploadVideo(servers[1].url, a1, { name: 'video4' }) 42 await servers[1].videos.upload({ token: token1, attributes: { name: 'video4' } })
55 43
56 const a2 = await generateUserAccessToken(servers[1], 'user2') 44 const token2 = await servers[1].users.generateUserAndToken('user2')
57 await uploadVideo(servers[1].url, a2, { name: 'video5' }) 45 await servers[1].videos.upload({ token: token2, attributes: { name: 'video5' } })
58 } 46 }
59 47
60 { 48 {
61 const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } 49 const attributes = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].store.channel.id }
62 const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) 50 const created = await servers[1].playlists.create({ attributes })
63 playlistUUID1 = res.body.videoPlaylist.uuid 51 playlistUUID1 = created.uuid
64 } 52 }
65 53
66 { 54 {
67 const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } 55 const attributes = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].store.channel.id }
68 const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) 56 const created = await servers[1].playlists.create({ attributes })
69 playlistUUID2 = res.body.videoPlaylist.uuid 57 playlistUUID2 = created.uuid
70 } 58 }
71 59
72 await doubleFollow(servers[0], servers[1]) 60 await doubleFollow(servers[0], servers[1])
@@ -80,34 +68,34 @@ describe('Test AP refresher', function () {
80 await wait(10000) 68 await wait(10000)
81 69
82 // Change UUID so the remote server returns a 404 70 // Change UUID so the remote server returns a 404
83 await setVideoField(servers[1].internalServerNumber, videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f') 71 await servers[1].sql.setVideoField(videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f')
84 72
85 await getVideo(servers[0].url, videoUUID1) 73 await servers[0].videos.get({ id: videoUUID1 })
86 await getVideo(servers[0].url, videoUUID2) 74 await servers[0].videos.get({ id: videoUUID2 })
87 75
88 await waitJobs(servers) 76 await waitJobs(servers)
89 77
90 await getVideo(servers[0].url, videoUUID1, HttpStatusCode.NOT_FOUND_404) 78 await servers[0].videos.get({ id: videoUUID1, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
91 await getVideo(servers[0].url, videoUUID2, HttpStatusCode.OK_200) 79 await servers[0].videos.get({ id: videoUUID2 })
92 }) 80 })
93 81
94 it('Should not update a remote video if the remote instance is down', async function () { 82 it('Should not update a remote video if the remote instance is down', async function () {
95 this.timeout(70000) 83 this.timeout(70000)
96 84
97 killallServers([ servers[1] ]) 85 await killallServers([ servers[1] ])
98 86
99 await setVideoField(servers[1].internalServerNumber, videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e') 87 await servers[1].sql.setVideoField(videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e')
100 88
101 // Video will need a refresh 89 // Video will need a refresh
102 await wait(10000) 90 await wait(10000)
103 91
104 await getVideo(servers[0].url, videoUUID3) 92 await servers[0].videos.get({ id: videoUUID3 })
105 // The refresh should fail 93 // The refresh should fail
106 await waitJobs([ servers[0] ]) 94 await waitJobs([ servers[0] ])
107 95
108 await reRunServer(servers[1]) 96 await servers[1].run()
109 97
110 await getVideo(servers[0].url, videoUUID3, HttpStatusCode.OK_200) 98 await servers[0].videos.get({ id: videoUUID3 })
111 }) 99 })
112 }) 100 })
113 101
@@ -116,19 +104,21 @@ describe('Test AP refresher', function () {
116 it('Should remove a deleted actor', async function () { 104 it('Should remove a deleted actor', async function () {
117 this.timeout(60000) 105 this.timeout(60000)
118 106
107 const command = servers[0].accounts
108
119 await wait(10000) 109 await wait(10000)
120 110
121 // Change actor name so the remote server returns a 404 111 // Change actor name so the remote server returns a 404
122 const to = 'http://localhost:' + servers[1].port + '/accounts/user2' 112 const to = 'http://localhost:' + servers[1].port + '/accounts/user2'
123 await setActorField(servers[1].internalServerNumber, to, 'preferredUsername', 'toto') 113 await servers[1].sql.setActorField(to, 'preferredUsername', 'toto')
124 114
125 await getAccount(servers[0].url, 'user1@localhost:' + servers[1].port) 115 await command.get({ accountName: 'user1@localhost:' + servers[1].port })
126 await getAccount(servers[0].url, 'user2@localhost:' + servers[1].port) 116 await command.get({ accountName: 'user2@localhost:' + servers[1].port })
127 117
128 await waitJobs(servers) 118 await waitJobs(servers)
129 119
130 await getAccount(servers[0].url, 'user1@localhost:' + servers[1].port, HttpStatusCode.OK_200) 120 await command.get({ accountName: 'user1@localhost:' + servers[1].port, expectedStatus: HttpStatusCode.OK_200 })
131 await getAccount(servers[0].url, 'user2@localhost:' + servers[1].port, HttpStatusCode.NOT_FOUND_404) 121 await command.get({ accountName: 'user2@localhost:' + servers[1].port, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
132 }) 122 })
133 }) 123 })
134 124
@@ -140,15 +130,15 @@ describe('Test AP refresher', function () {
140 await wait(10000) 130 await wait(10000)
141 131
142 // Change UUID so the remote server returns a 404 132 // Change UUID so the remote server returns a 404
143 await setPlaylistField(servers[1].internalServerNumber, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e') 133 await servers[1].sql.setPlaylistField(playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e')
144 134
145 await getVideoPlaylist(servers[0].url, playlistUUID1) 135 await servers[0].playlists.get({ playlistId: playlistUUID1 })
146 await getVideoPlaylist(servers[0].url, playlistUUID2) 136 await servers[0].playlists.get({ playlistId: playlistUUID2 })
147 137
148 await waitJobs(servers) 138 await waitJobs(servers)
149 139
150 await getVideoPlaylist(servers[0].url, playlistUUID1, HttpStatusCode.OK_200) 140 await servers[0].playlists.get({ playlistId: playlistUUID1, expectedStatus: HttpStatusCode.OK_200 })
151 await getVideoPlaylist(servers[0].url, playlistUUID2, HttpStatusCode.NOT_FOUND_404) 141 await servers[0].playlists.get({ playlistId: playlistUUID2, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
152 }) 142 })
153 }) 143 })
154 144
@@ -156,7 +146,5 @@ describe('Test AP refresher', function () {
156 this.timeout(10000) 146 this.timeout(10000)
157 147
158 await cleanupTests(servers) 148 await cleanupTests(servers)
159
160 await closeAllSequelize(servers)
161 }) 149 })
162}) 150})
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts
index 61db272f6..94d946563 100644
--- a/server/tests/api/activitypub/security.ts
+++ b/server/tests/api/activitypub/security.ts
@@ -2,45 +2,35 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { activityPubContextify, buildSignedActivity } from '@server/helpers/activitypub'
5import { buildDigest } from '@server/helpers/peertube-crypto' 6import { buildDigest } from '@server/helpers/peertube-crypto'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 7import { HTTP_SIGNATURE } from '@server/initializers/constants'
7import { 8import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
8 buildAbsoluteFixturePath, 9import { buildAbsoluteFixturePath, cleanupTests, createMultipleServers, killallServers, PeerTubeServer, wait } from '@shared/extra-utils'
9 cleanupTests, 10import { makeFollowRequest, makePOSTAPRequest } from '@shared/extra-utils/requests/activitypub'
10 closeAllSequelize, 11import { HttpStatusCode } from '@shared/models'
11 flushAndRunMultipleServers,
12 killallServers,
13 reRunServer,
14 ServerInfo,
15 setActorField,
16 wait
17} from '../../../../shared/extra-utils'
18import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/extra-utils/requests/activitypub'
19import { activityPubContextify, buildSignedActivity } from '../../../helpers/activitypub'
20import { HTTP_SIGNATURE } from '../../../initializers/constants'
21import { buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
22 12
23const expect = chai.expect 13const expect = chai.expect
24 14
25function setKeysOfServer (onServer: ServerInfo, ofServer: ServerInfo, publicKey: string, privateKey: string) { 15function setKeysOfServer (onServer: PeerTubeServer, ofServer: PeerTubeServer, publicKey: string, privateKey: string) {
26 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube' 16 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube'
27 17
28 return Promise.all([ 18 return Promise.all([
29 setActorField(onServer.internalServerNumber, url, 'publicKey', publicKey), 19 onServer.sql.setActorField(url, 'publicKey', publicKey),
30 setActorField(onServer.internalServerNumber, url, 'privateKey', privateKey) 20 onServer.sql.setActorField(url, 'privateKey', privateKey)
31 ]) 21 ])
32} 22}
33 23
34function setUpdatedAtOfServer (onServer: ServerInfo, ofServer: ServerInfo, updatedAt: string) { 24function setUpdatedAtOfServer (onServer: PeerTubeServer, ofServer: PeerTubeServer, updatedAt: string) {
35 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube' 25 const url = 'http://localhost:' + ofServer.port + '/accounts/peertube'
36 26
37 return Promise.all([ 27 return Promise.all([
38 setActorField(onServer.internalServerNumber, url, 'createdAt', updatedAt), 28 onServer.sql.setActorField(url, 'createdAt', updatedAt),
39 setActorField(onServer.internalServerNumber, url, 'updatedAt', updatedAt) 29 onServer.sql.setActorField(url, 'updatedAt', updatedAt)
40 ]) 30 ])
41} 31}
42 32
43function getAnnounceWithoutContext (server: ServerInfo) { 33function getAnnounceWithoutContext (server: PeerTubeServer) {
44 const json = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json')) 34 const json = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json'))
45 const result: typeof json = {} 35 const result: typeof json = {}
46 36
@@ -56,7 +46,7 @@ function getAnnounceWithoutContext (server: ServerInfo) {
56} 46}
57 47
58describe('Test ActivityPub security', function () { 48describe('Test ActivityPub security', function () {
59 let servers: ServerInfo[] 49 let servers: PeerTubeServer[]
60 let url: string 50 let url: string
61 51
62 const keys = require(buildAbsoluteFixturePath('./ap-json/peertube/keys.json')) 52 const keys = require(buildAbsoluteFixturePath('./ap-json/peertube/keys.json'))
@@ -74,7 +64,7 @@ describe('Test ActivityPub security', function () {
74 before(async function () { 64 before(async function () {
75 this.timeout(60000) 65 this.timeout(60000)
76 66
77 servers = await flushAndRunMultipleServers(3) 67 servers = await createMultipleServers(3)
78 68
79 url = servers[0].url + '/inbox' 69 url = servers[0].url + '/inbox'
80 70
@@ -173,8 +163,8 @@ describe('Test ActivityPub security', function () {
173 await setUpdatedAtOfServer(servers[0], servers[1], '2015-07-17 22:00:00+00') 163 await setUpdatedAtOfServer(servers[0], servers[1], '2015-07-17 22:00:00+00')
174 164
175 // Invalid peertube actor cache 165 // Invalid peertube actor cache
176 killallServers([ servers[1] ]) 166 await killallServers([ servers[1] ])
177 await reRunServer(servers[1]) 167 await servers[1].run()
178 168
179 const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) 169 const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
180 const headers = buildGlobalHeaders(body) 170 const headers = buildGlobalHeaders(body)
@@ -294,7 +284,5 @@ describe('Test ActivityPub security', function () {
294 this.timeout(10000) 284 this.timeout(10000)
295 285
296 await cleanupTests(servers) 286 await cleanupTests(servers)
297
298 await closeAllSequelize(servers)
299 }) 287 })
300}) 288})
diff --git a/server/tests/api/check-params/abuses.ts b/server/tests/api/check-params/abuses.ts
index 2054776cc..fb9a5fd8b 100644
--- a/server/tests/api/check-params/abuses.ts
+++ b/server/tests/api/check-params/abuses.ts
@@ -1,66 +1,49 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { AbuseCreate, AbuseState } from '@shared/models'
5import { 4import {
6 addAbuseMessage, 5 AbusesCommand,
6 checkBadCountPagination,
7 checkBadSortPagination,
8 checkBadStartPagination,
7 cleanupTests, 9 cleanupTests,
8 createUser, 10 createSingleServer,
9 deleteAbuse,
10 deleteAbuseMessage,
11 doubleFollow, 11 doubleFollow,
12 flushAndRunServer,
13 generateUserAccessToken,
14 getAdminAbusesList,
15 getVideoIdFromUUID,
16 listAbuseMessages,
17 makeGetRequest, 12 makeGetRequest,
18 makePostBodyRequest, 13 makePostBodyRequest,
19 reportAbuse, 14 PeerTubeServer,
20 ServerInfo,
21 setAccessTokensToServers, 15 setAccessTokensToServers,
22 updateAbuse,
23 uploadVideo,
24 userLogin,
25 waitJobs 16 waitJobs
26} from '../../../../shared/extra-utils' 17} from '@shared/extra-utils'
27import { 18import { AbuseCreate, AbuseState, HttpStatusCode } from '@shared/models'
28 checkBadCountPagination,
29 checkBadSortPagination,
30 checkBadStartPagination
31} from '../../../../shared/extra-utils/requests/check-api-params'
32import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
33 19
34describe('Test abuses API validators', function () { 20describe('Test abuses API validators', function () {
35 const basePath = '/api/v1/abuses/' 21 const basePath = '/api/v1/abuses/'
36 22
37 let server: ServerInfo 23 let server: PeerTubeServer
38 24
39 let userAccessToken = '' 25 let userToken = ''
40 let userAccessToken2 = '' 26 let userToken2 = ''
41 let abuseId: number 27 let abuseId: number
42 let messageId: number 28 let messageId: number
43 29
30 let command: AbusesCommand
31
44 // --------------------------------------------------------------- 32 // ---------------------------------------------------------------
45 33
46 before(async function () { 34 before(async function () {
47 this.timeout(30000) 35 this.timeout(30000)
48 36
49 server = await flushAndRunServer(1) 37 server = await createSingleServer(1)
50 38
51 await setAccessTokensToServers([ server ]) 39 await setAccessTokensToServers([ server ])
52 40
53 const username = 'user1' 41 userToken = await server.users.generateUserAndToken('user_1')
54 const password = 'my super password' 42 userToken2 = await server.users.generateUserAndToken('user_2')
55 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
56 userAccessToken = await userLogin(server, { username, password })
57 43
58 { 44 server.store.videoCreated = await server.videos.upload()
59 userAccessToken2 = await generateUserAccessToken(server, 'user_2')
60 }
61 45
62 const res = await uploadVideo(server.url, server.accessToken, {}) 46 command = server.abuses
63 server.video = res.body.video
64 }) 47 })
65 48
66 describe('When listing abuses for admins', function () { 49 describe('When listing abuses for admins', function () {
@@ -82,7 +65,7 @@ describe('Test abuses API validators', function () {
82 await makeGetRequest({ 65 await makeGetRequest({
83 url: server.url, 66 url: server.url,
84 path, 67 path,
85 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 68 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
86 }) 69 })
87 }) 70 })
88 71
@@ -90,8 +73,8 @@ describe('Test abuses API validators', function () {
90 await makeGetRequest({ 73 await makeGetRequest({
91 url: server.url, 74 url: server.url,
92 path, 75 path,
93 token: userAccessToken, 76 token: userToken,
94 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 77 expectedStatus: HttpStatusCode.FORBIDDEN_403
95 }) 78 })
96 }) 79 })
97 80
@@ -126,7 +109,7 @@ describe('Test abuses API validators', function () {
126 videoIs: 'deleted' 109 videoIs: 'deleted'
127 } 110 }
128 111
129 await makeGetRequest({ url: server.url, path, token: server.accessToken, query, statusCodeExpected: HttpStatusCode.OK_200 }) 112 await makeGetRequest({ url: server.url, path, token: server.accessToken, query, expectedStatus: HttpStatusCode.OK_200 })
130 }) 113 })
131 }) 114 })
132 115
@@ -134,32 +117,32 @@ describe('Test abuses API validators', function () {
134 const path = '/api/v1/users/me/abuses' 117 const path = '/api/v1/users/me/abuses'
135 118
136 it('Should fail with a bad start pagination', async function () { 119 it('Should fail with a bad start pagination', async function () {
137 await checkBadStartPagination(server.url, path, userAccessToken) 120 await checkBadStartPagination(server.url, path, userToken)
138 }) 121 })
139 122
140 it('Should fail with a bad count pagination', async function () { 123 it('Should fail with a bad count pagination', async function () {
141 await checkBadCountPagination(server.url, path, userAccessToken) 124 await checkBadCountPagination(server.url, path, userToken)
142 }) 125 })
143 126
144 it('Should fail with an incorrect sort', async function () { 127 it('Should fail with an incorrect sort', async function () {
145 await checkBadSortPagination(server.url, path, userAccessToken) 128 await checkBadSortPagination(server.url, path, userToken)
146 }) 129 })
147 130
148 it('Should fail with a non authenticated user', async function () { 131 it('Should fail with a non authenticated user', async function () {
149 await makeGetRequest({ 132 await makeGetRequest({
150 url: server.url, 133 url: server.url,
151 path, 134 path,
152 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 135 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
153 }) 136 })
154 }) 137 })
155 138
156 it('Should fail with a bad id filter', async function () { 139 it('Should fail with a bad id filter', async function () {
157 await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { id: 'toto' } }) 140 await makeGetRequest({ url: server.url, path, token: userToken, query: { id: 'toto' } })
158 }) 141 })
159 142
160 it('Should fail with a bad state filter', async function () { 143 it('Should fail with a bad state filter', async function () {
161 await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 'toto' } }) 144 await makeGetRequest({ url: server.url, path, token: userToken, query: { state: 'toto' } })
162 await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 0 } }) 145 await makeGetRequest({ url: server.url, path, token: userToken, query: { state: 0 } })
163 }) 146 })
164 147
165 it('Should succeed with the correct params', async function () { 148 it('Should succeed with the correct params', async function () {
@@ -168,7 +151,7 @@ describe('Test abuses API validators', function () {
168 state: 2 151 state: 2
169 } 152 }
170 153
171 await makeGetRequest({ url: server.url, path, token: userAccessToken, query, statusCodeExpected: HttpStatusCode.OK_200 }) 154 await makeGetRequest({ url: server.url, path, token: userToken, query, expectedStatus: HttpStatusCode.OK_200 })
172 }) 155 })
173 }) 156 })
174 157
@@ -177,12 +160,12 @@ describe('Test abuses API validators', function () {
177 160
178 it('Should fail with nothing', async function () { 161 it('Should fail with nothing', async function () {
179 const fields = {} 162 const fields = {}
180 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 163 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
181 }) 164 })
182 165
183 it('Should fail with a wrong video', async function () { 166 it('Should fail with a wrong video', async function () {
184 const fields = { video: { id: 'blabla' }, reason: 'my super reason' } 167 const fields = { video: { id: 'blabla' }, reason: 'my super reason' }
185 await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) 168 await makePostBodyRequest({ url: server.url, path: path, token: userToken, fields })
186 }) 169 })
187 170
188 it('Should fail with an unknown video', async function () { 171 it('Should fail with an unknown video', async function () {
@@ -190,15 +173,15 @@ describe('Test abuses API validators', function () {
190 await makePostBodyRequest({ 173 await makePostBodyRequest({
191 url: server.url, 174 url: server.url,
192 path, 175 path,
193 token: userAccessToken, 176 token: userToken,
194 fields, 177 fields,
195 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 178 expectedStatus: HttpStatusCode.NOT_FOUND_404
196 }) 179 })
197 }) 180 })
198 181
199 it('Should fail with a wrong comment', async function () { 182 it('Should fail with a wrong comment', async function () {
200 const fields = { comment: { id: 'blabla' }, reason: 'my super reason' } 183 const fields = { comment: { id: 'blabla' }, reason: 'my super reason' }
201 await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) 184 await makePostBodyRequest({ url: server.url, path: path, token: userToken, fields })
202 }) 185 })
203 186
204 it('Should fail with an unknown comment', async function () { 187 it('Should fail with an unknown comment', async function () {
@@ -206,15 +189,15 @@ describe('Test abuses API validators', function () {
206 await makePostBodyRequest({ 189 await makePostBodyRequest({
207 url: server.url, 190 url: server.url,
208 path, 191 path,
209 token: userAccessToken, 192 token: userToken,
210 fields, 193 fields,
211 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 194 expectedStatus: HttpStatusCode.NOT_FOUND_404
212 }) 195 })
213 }) 196 })
214 197
215 it('Should fail with a wrong account', async function () { 198 it('Should fail with a wrong account', async function () {
216 const fields = { account: { id: 'blabla' }, reason: 'my super reason' } 199 const fields = { account: { id: 'blabla' }, reason: 'my super reason' }
217 await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) 200 await makePostBodyRequest({ url: server.url, path: path, token: userToken, fields })
218 }) 201 })
219 202
220 it('Should fail with an unknown account', async function () { 203 it('Should fail with an unknown account', async function () {
@@ -222,9 +205,9 @@ describe('Test abuses API validators', function () {
222 await makePostBodyRequest({ 205 await makePostBodyRequest({
223 url: server.url, 206 url: server.url,
224 path, 207 path,
225 token: userAccessToken, 208 token: userToken,
226 fields, 209 fields,
227 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 210 expectedStatus: HttpStatusCode.NOT_FOUND_404
228 }) 211 })
229 }) 212 })
230 213
@@ -233,65 +216,65 @@ describe('Test abuses API validators', function () {
233 await makePostBodyRequest({ 216 await makePostBodyRequest({
234 url: server.url, 217 url: server.url,
235 path, 218 path,
236 token: userAccessToken, 219 token: userToken,
237 fields, 220 fields,
238 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 221 expectedStatus: HttpStatusCode.BAD_REQUEST_400
239 }) 222 })
240 }) 223 })
241 224
242 it('Should fail with a non authenticated user', async function () { 225 it('Should fail with a non authenticated user', async function () {
243 const fields = { video: { id: server.video.id }, reason: 'my super reason' } 226 const fields = { video: { id: server.store.videoCreated.id }, reason: 'my super reason' }
244 227
245 await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 228 await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
246 }) 229 })
247 230
248 it('Should fail with a reason too short', async function () { 231 it('Should fail with a reason too short', async function () {
249 const fields = { video: { id: server.video.id }, reason: 'h' } 232 const fields = { video: { id: server.store.videoCreated.id }, reason: 'h' }
250 233
251 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 234 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
252 }) 235 })
253 236
254 it('Should fail with a too big reason', async function () { 237 it('Should fail with a too big reason', async function () {
255 const fields = { video: { id: server.video.id }, reason: 'super'.repeat(605) } 238 const fields = { video: { id: server.store.videoCreated.id }, reason: 'super'.repeat(605) }
256 239
257 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 240 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
258 }) 241 })
259 242
260 it('Should succeed with the correct parameters (basic)', async function () { 243 it('Should succeed with the correct parameters (basic)', async function () {
261 const fields: AbuseCreate = { video: { id: server.video.shortUUID }, reason: 'my super reason' } 244 const fields: AbuseCreate = { video: { id: server.store.videoCreated.shortUUID }, reason: 'my super reason' }
262 245
263 const res = await makePostBodyRequest({ 246 const res = await makePostBodyRequest({
264 url: server.url, 247 url: server.url,
265 path, 248 path,
266 token: userAccessToken, 249 token: userToken,
267 fields, 250 fields,
268 statusCodeExpected: HttpStatusCode.OK_200 251 expectedStatus: HttpStatusCode.OK_200
269 }) 252 })
270 abuseId = res.body.abuse.id 253 abuseId = res.body.abuse.id
271 }) 254 })
272 255
273 it('Should fail with a wrong predefined reason', async function () { 256 it('Should fail with a wrong predefined reason', async function () {
274 const fields = { video: { id: server.video.id }, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] } 257 const fields = { video: server.store.videoCreated, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] }
275 258
276 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 259 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
277 }) 260 })
278 261
279 it('Should fail with negative timestamps', async function () { 262 it('Should fail with negative timestamps', async function () {
280 const fields = { video: { id: server.video.id, startAt: -1 }, reason: 'my super reason' } 263 const fields = { video: { id: server.store.videoCreated.id, startAt: -1 }, reason: 'my super reason' }
281 264
282 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 265 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
283 }) 266 })
284 267
285 it('Should fail mith misordered startAt/endAt', async function () { 268 it('Should fail mith misordered startAt/endAt', async function () {
286 const fields = { video: { id: server.video.id, startAt: 5, endAt: 1 }, reason: 'my super reason' } 269 const fields = { video: { id: server.store.videoCreated.id, startAt: 5, endAt: 1 }, reason: 'my super reason' }
287 270
288 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 271 await makePostBodyRequest({ url: server.url, path, token: userToken, fields })
289 }) 272 })
290 273
291 it('Should succeed with the corret parameters (advanced)', async function () { 274 it('Should succeed with the corret parameters (advanced)', async function () {
292 const fields: AbuseCreate = { 275 const fields: AbuseCreate = {
293 video: { 276 video: {
294 id: server.video.id, 277 id: server.store.videoCreated.id,
295 startAt: 1, 278 startAt: 1,
296 endAt: 5 279 endAt: 5
297 }, 280 },
@@ -299,37 +282,37 @@ describe('Test abuses API validators', function () {
299 predefinedReasons: [ 'serverRules' ] 282 predefinedReasons: [ 'serverRules' ]
300 } 283 }
301 284
302 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: HttpStatusCode.OK_200 }) 285 await makePostBodyRequest({ url: server.url, path, token: userToken, fields, expectedStatus: HttpStatusCode.OK_200 })
303 }) 286 })
304 }) 287 })
305 288
306 describe('When updating an abuse', function () { 289 describe('When updating an abuse', function () {
307 290
308 it('Should fail with a non authenticated user', async function () { 291 it('Should fail with a non authenticated user', async function () {
309 await updateAbuse(server.url, 'blabla', abuseId, {}, HttpStatusCode.UNAUTHORIZED_401) 292 await command.update({ token: 'blabla', abuseId, body: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
310 }) 293 })
311 294
312 it('Should fail with a non admin user', async function () { 295 it('Should fail with a non admin user', async function () {
313 await updateAbuse(server.url, userAccessToken, abuseId, {}, HttpStatusCode.FORBIDDEN_403) 296 await command.update({ token: userToken, abuseId, body: {}, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
314 }) 297 })
315 298
316 it('Should fail with a bad abuse id', async function () { 299 it('Should fail with a bad abuse id', async function () {
317 await updateAbuse(server.url, server.accessToken, 45, {}, HttpStatusCode.NOT_FOUND_404) 300 await command.update({ abuseId: 45, body: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
318 }) 301 })
319 302
320 it('Should fail with a bad state', async function () { 303 it('Should fail with a bad state', async function () {
321 const body = { state: 5 } 304 const body = { state: 5 }
322 await updateAbuse(server.url, server.accessToken, abuseId, body, HttpStatusCode.BAD_REQUEST_400) 305 await command.update({ abuseId, body, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
323 }) 306 })
324 307
325 it('Should fail with a bad moderation comment', async function () { 308 it('Should fail with a bad moderation comment', async function () {
326 const body = { moderationComment: 'b'.repeat(3001) } 309 const body = { moderationComment: 'b'.repeat(3001) }
327 await updateAbuse(server.url, server.accessToken, abuseId, body, HttpStatusCode.BAD_REQUEST_400) 310 await command.update({ abuseId, body, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
328 }) 311 })
329 312
330 it('Should succeed with the correct params', async function () { 313 it('Should succeed with the correct params', async function () {
331 const body = { state: AbuseState.ACCEPTED } 314 const body = { state: AbuseState.ACCEPTED }
332 await updateAbuse(server.url, server.accessToken, abuseId, body) 315 await command.update({ abuseId, body })
333 }) 316 })
334 }) 317 })
335 318
@@ -337,23 +320,23 @@ describe('Test abuses API validators', function () {
337 const message = 'my super message' 320 const message = 'my super message'
338 321
339 it('Should fail with an invalid abuse id', async function () { 322 it('Should fail with an invalid abuse id', async function () {
340 await addAbuseMessage(server.url, userAccessToken2, 888, message, HttpStatusCode.NOT_FOUND_404) 323 await command.addMessage({ token: userToken2, abuseId: 888, message, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
341 }) 324 })
342 325
343 it('Should fail with a non authenticated user', async function () { 326 it('Should fail with a non authenticated user', async function () {
344 await addAbuseMessage(server.url, 'fake_token', abuseId, message, HttpStatusCode.UNAUTHORIZED_401) 327 await command.addMessage({ token: 'fake_token', abuseId, message, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
345 }) 328 })
346 329
347 it('Should fail with an invalid logged in user', async function () { 330 it('Should fail with an invalid logged in user', async function () {
348 await addAbuseMessage(server.url, userAccessToken2, abuseId, message, HttpStatusCode.FORBIDDEN_403) 331 await command.addMessage({ token: userToken2, abuseId, message, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
349 }) 332 })
350 333
351 it('Should fail with an invalid message', async function () { 334 it('Should fail with an invalid message', async function () {
352 await addAbuseMessage(server.url, userAccessToken, abuseId, 'a'.repeat(5000), HttpStatusCode.BAD_REQUEST_400) 335 await command.addMessage({ token: userToken, abuseId, message: 'a'.repeat(5000), expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
353 }) 336 })
354 337
355 it('Should suceed with the correct params', async function () { 338 it('Should suceed with the correct params', async function () {
356 const res = await addAbuseMessage(server.url, userAccessToken, abuseId, message) 339 const res = await command.addMessage({ token: userToken, abuseId, message })
357 messageId = res.body.abuseMessage.id 340 messageId = res.body.abuseMessage.id
358 }) 341 })
359 }) 342 })
@@ -361,96 +344,90 @@ describe('Test abuses API validators', function () {
361 describe('When listing abuse messages', function () { 344 describe('When listing abuse messages', function () {
362 345
363 it('Should fail with an invalid abuse id', async function () { 346 it('Should fail with an invalid abuse id', async function () {
364 await listAbuseMessages(server.url, userAccessToken, 888, HttpStatusCode.NOT_FOUND_404) 347 await command.listMessages({ token: userToken, abuseId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
365 }) 348 })
366 349
367 it('Should fail with a non authenticated user', async function () { 350 it('Should fail with a non authenticated user', async function () {
368 await listAbuseMessages(server.url, 'fake_token', abuseId, HttpStatusCode.UNAUTHORIZED_401) 351 await command.listMessages({ token: 'fake_token', abuseId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
369 }) 352 })
370 353
371 it('Should fail with an invalid logged in user', async function () { 354 it('Should fail with an invalid logged in user', async function () {
372 await listAbuseMessages(server.url, userAccessToken2, abuseId, HttpStatusCode.FORBIDDEN_403) 355 await command.listMessages({ token: userToken2, abuseId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
373 }) 356 })
374 357
375 it('Should succeed with the correct params', async function () { 358 it('Should succeed with the correct params', async function () {
376 await listAbuseMessages(server.url, userAccessToken, abuseId) 359 await command.listMessages({ token: userToken, abuseId })
377 }) 360 })
378 }) 361 })
379 362
380 describe('When deleting an abuse message', function () { 363 describe('When deleting an abuse message', function () {
381
382 it('Should fail with an invalid abuse id', async function () { 364 it('Should fail with an invalid abuse id', async function () {
383 await deleteAbuseMessage(server.url, userAccessToken, 888, messageId, HttpStatusCode.NOT_FOUND_404) 365 await command.deleteMessage({ token: userToken, abuseId: 888, messageId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
384 }) 366 })
385 367
386 it('Should fail with an invalid message id', async function () { 368 it('Should fail with an invalid message id', async function () {
387 await deleteAbuseMessage(server.url, userAccessToken, abuseId, 888, HttpStatusCode.NOT_FOUND_404) 369 await command.deleteMessage({ token: userToken, abuseId, messageId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
388 }) 370 })
389 371
390 it('Should fail with a non authenticated user', async function () { 372 it('Should fail with a non authenticated user', async function () {
391 await deleteAbuseMessage(server.url, 'fake_token', abuseId, messageId, HttpStatusCode.UNAUTHORIZED_401) 373 await command.deleteMessage({ token: 'fake_token', abuseId, messageId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
392 }) 374 })
393 375
394 it('Should fail with an invalid logged in user', async function () { 376 it('Should fail with an invalid logged in user', async function () {
395 await deleteAbuseMessage(server.url, userAccessToken2, abuseId, messageId, HttpStatusCode.FORBIDDEN_403) 377 await command.deleteMessage({ token: userToken2, abuseId, messageId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
396 }) 378 })
397 379
398 it('Should succeed with the correct params', async function () { 380 it('Should succeed with the correct params', async function () {
399 await deleteAbuseMessage(server.url, userAccessToken, abuseId, messageId) 381 await command.deleteMessage({ token: userToken, abuseId, messageId })
400 }) 382 })
401 }) 383 })
402 384
403 describe('When deleting a video abuse', function () { 385 describe('When deleting a video abuse', function () {
404 386
405 it('Should fail with a non authenticated user', async function () { 387 it('Should fail with a non authenticated user', async function () {
406 await deleteAbuse(server.url, 'blabla', abuseId, HttpStatusCode.UNAUTHORIZED_401) 388 await command.delete({ token: 'blabla', abuseId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
407 }) 389 })
408 390
409 it('Should fail with a non admin user', async function () { 391 it('Should fail with a non admin user', async function () {
410 await deleteAbuse(server.url, userAccessToken, abuseId, HttpStatusCode.FORBIDDEN_403) 392 await command.delete({ token: userToken, abuseId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
411 }) 393 })
412 394
413 it('Should fail with a bad abuse id', async function () { 395 it('Should fail with a bad abuse id', async function () {
414 await deleteAbuse(server.url, server.accessToken, 45, HttpStatusCode.NOT_FOUND_404) 396 await command.delete({ abuseId: 45, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
415 }) 397 })
416 398
417 it('Should succeed with the correct params', async function () { 399 it('Should succeed with the correct params', async function () {
418 await deleteAbuse(server.url, server.accessToken, abuseId) 400 await command.delete({ abuseId })
419 }) 401 })
420 }) 402 })
421 403
422 describe('When trying to manage messages of a remote abuse', function () { 404 describe('When trying to manage messages of a remote abuse', function () {
423 let remoteAbuseId: number 405 let remoteAbuseId: number
424 let anotherServer: ServerInfo 406 let anotherServer: PeerTubeServer
425 407
426 before(async function () { 408 before(async function () {
427 this.timeout(50000) 409 this.timeout(50000)
428 410
429 anotherServer = await flushAndRunServer(2) 411 anotherServer = await createSingleServer(2)
430 await setAccessTokensToServers([ anotherServer ]) 412 await setAccessTokensToServers([ anotherServer ])
431 413
432 await doubleFollow(anotherServer, server) 414 await doubleFollow(anotherServer, server)
433 415
434 const server2VideoId = await getVideoIdFromUUID(anotherServer.url, server.video.uuid) 416 const server2VideoId = await anotherServer.videos.getId({ uuid: server.store.videoCreated.uuid })
435 await reportAbuse({ 417 await anotherServer.abuses.report({ reason: 'remote server', videoId: server2VideoId })
436 url: anotherServer.url,
437 token: anotherServer.accessToken,
438 reason: 'remote server',
439 videoId: server2VideoId
440 })
441 418
442 await waitJobs([ server, anotherServer ]) 419 await waitJobs([ server, anotherServer ])
443 420
444 const res = await getAdminAbusesList({ url: server.url, token: server.accessToken, sort: '-createdAt' }) 421 const body = await command.getAdminList({ sort: '-createdAt' })
445 remoteAbuseId = res.body.data[0].id 422 remoteAbuseId = body.data[0].id
446 }) 423 })
447 424
448 it('Should fail when listing abuse messages of a remote abuse', async function () { 425 it('Should fail when listing abuse messages of a remote abuse', async function () {
449 await listAbuseMessages(server.url, server.accessToken, remoteAbuseId, HttpStatusCode.BAD_REQUEST_400) 426 await command.listMessages({ abuseId: remoteAbuseId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
450 }) 427 })
451 428
452 it('Should fail when creating abuse message of a remote abuse', async function () { 429 it('Should fail when creating abuse message of a remote abuse', async function () {
453 await addAbuseMessage(server.url, server.accessToken, remoteAbuseId, 'message', HttpStatusCode.BAD_REQUEST_400) 430 await command.addMessage({ abuseId: remoteAbuseId, message: 'message', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
454 }) 431 })
455 432
456 after(async function () { 433 after(async function () {
diff --git a/server/tests/api/check-params/accounts.ts b/server/tests/api/check-params/accounts.ts
index d1712cff6..141d869b7 100644
--- a/server/tests/api/check-params/accounts.ts
+++ b/server/tests/api/check-params/accounts.ts
@@ -1,26 +1,26 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../../shared/extra-utils'
6import { 4import {
7 checkBadCountPagination, 5 checkBadCountPagination,
8 checkBadSortPagination, 6 checkBadSortPagination,
9 checkBadStartPagination 7 checkBadStartPagination,
10} from '../../../../shared/extra-utils/requests/check-api-params' 8 cleanupTests,
11import { getAccount } from '../../../../shared/extra-utils/users/accounts' 9 createSingleServer,
12import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 10 PeerTubeServer
11} from '@shared/extra-utils'
12import { HttpStatusCode } from '@shared/models'
13 13
14describe('Test accounts API validators', function () { 14describe('Test accounts API validators', function () {
15 const path = '/api/v1/accounts/' 15 const path = '/api/v1/accounts/'
16 let server: ServerInfo 16 let server: PeerTubeServer
17 17
18 // --------------------------------------------------------------- 18 // ---------------------------------------------------------------
19 19
20 before(async function () { 20 before(async function () {
21 this.timeout(30000) 21 this.timeout(30000)
22 22
23 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
24 }) 24 })
25 25
26 describe('When listing accounts', function () { 26 describe('When listing accounts', function () {
@@ -38,8 +38,9 @@ describe('Test accounts API validators', function () {
38 }) 38 })
39 39
40 describe('When getting an account', function () { 40 describe('When getting an account', function () {
41
41 it('Should return 404 with a non existing name', async function () { 42 it('Should return 404 with a non existing name', async function () {
42 await getAccount(server.url, 'arfaze', HttpStatusCode.NOT_FOUND_404) 43 await server.accounts.get({ accountName: 'arfaze', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
43 }) 44 })
44 }) 45 })
45 46
diff --git a/server/tests/api/check-params/blocklist.ts b/server/tests/api/check-params/blocklist.ts
index 5ed8810ce..7d5fae5cf 100644
--- a/server/tests/api/check-params/blocklist.ts
+++ b/server/tests/api/check-params/blocklist.ts
@@ -1,43 +1,38 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { 4import {
5 checkBadCountPagination,
6 checkBadSortPagination,
7 checkBadStartPagination,
6 cleanupTests, 8 cleanupTests,
7 createUser, 9 createMultipleServers,
8 doubleFollow, 10 doubleFollow,
9 flushAndRunMultipleServers,
10 makeDeleteRequest, 11 makeDeleteRequest,
11 makeGetRequest, 12 makeGetRequest,
12 makePostBodyRequest, 13 makePostBodyRequest,
13 ServerInfo, 14 PeerTubeServer,
14 setAccessTokensToServers, 15 setAccessTokensToServers
15 userLogin 16} from '@shared/extra-utils'
16} from '../../../../shared/extra-utils' 17import { HttpStatusCode } from '@shared/models'
17import {
18 checkBadCountPagination,
19 checkBadSortPagination,
20 checkBadStartPagination
21} from '../../../../shared/extra-utils/requests/check-api-params'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 18
24describe('Test blocklist API validators', function () { 19describe('Test blocklist API validators', function () {
25 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
26 let server: ServerInfo 21 let server: PeerTubeServer
27 let userAccessToken: string 22 let userAccessToken: string
28 23
29 before(async function () { 24 before(async function () {
30 this.timeout(60000) 25 this.timeout(60000)
31 26
32 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
33 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
34 29
35 server = servers[0] 30 server = servers[0]
36 31
37 const user = { username: 'user1', password: 'password' } 32 const user = { username: 'user1', password: 'password' }
38 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 33 await server.users.create({ username: user.username, password: user.password })
39 34
40 userAccessToken = await userLogin(server, user) 35 userAccessToken = await server.login.getAccessToken(user)
41 36
42 await doubleFollow(servers[0], servers[1]) 37 await doubleFollow(servers[0], servers[1])
43 }) 38 })
@@ -54,7 +49,7 @@ describe('Test blocklist API validators', function () {
54 await makeGetRequest({ 49 await makeGetRequest({
55 url: server.url, 50 url: server.url,
56 path, 51 path,
57 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 52 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
58 }) 53 })
59 }) 54 })
60 55
@@ -77,7 +72,7 @@ describe('Test blocklist API validators', function () {
77 url: server.url, 72 url: server.url,
78 path, 73 path,
79 fields: { accountName: 'user1' }, 74 fields: { accountName: 'user1' },
80 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 75 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
81 }) 76 })
82 }) 77 })
83 78
@@ -87,7 +82,7 @@ describe('Test blocklist API validators', function () {
87 token: server.accessToken, 82 token: server.accessToken,
88 path, 83 path,
89 fields: { accountName: 'user2' }, 84 fields: { accountName: 'user2' },
90 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 85 expectedStatus: HttpStatusCode.NOT_FOUND_404
91 }) 86 })
92 }) 87 })
93 88
@@ -97,7 +92,7 @@ describe('Test blocklist API validators', function () {
97 token: server.accessToken, 92 token: server.accessToken,
98 path, 93 path,
99 fields: { accountName: 'root' }, 94 fields: { accountName: 'root' },
100 statusCodeExpected: HttpStatusCode.CONFLICT_409 95 expectedStatus: HttpStatusCode.CONFLICT_409
101 }) 96 })
102 }) 97 })
103 98
@@ -107,7 +102,7 @@ describe('Test blocklist API validators', function () {
107 token: server.accessToken, 102 token: server.accessToken,
108 path, 103 path,
109 fields: { accountName: 'user1' }, 104 fields: { accountName: 'user1' },
110 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 105 expectedStatus: HttpStatusCode.NO_CONTENT_204
111 }) 106 })
112 }) 107 })
113 }) 108 })
@@ -117,7 +112,7 @@ describe('Test blocklist API validators', function () {
117 await makeDeleteRequest({ 112 await makeDeleteRequest({
118 url: server.url, 113 url: server.url,
119 path: path + '/user1', 114 path: path + '/user1',
120 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 115 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
121 }) 116 })
122 }) 117 })
123 118
@@ -126,7 +121,7 @@ describe('Test blocklist API validators', function () {
126 url: server.url, 121 url: server.url,
127 path: path + '/user2', 122 path: path + '/user2',
128 token: server.accessToken, 123 token: server.accessToken,
129 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 124 expectedStatus: HttpStatusCode.NOT_FOUND_404
130 }) 125 })
131 }) 126 })
132 127
@@ -135,7 +130,7 @@ describe('Test blocklist API validators', function () {
135 url: server.url, 130 url: server.url,
136 path: path + '/user1', 131 path: path + '/user1',
137 token: server.accessToken, 132 token: server.accessToken,
138 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 133 expectedStatus: HttpStatusCode.NO_CONTENT_204
139 }) 134 })
140 }) 135 })
141 }) 136 })
@@ -149,7 +144,7 @@ describe('Test blocklist API validators', function () {
149 await makeGetRequest({ 144 await makeGetRequest({
150 url: server.url, 145 url: server.url,
151 path, 146 path,
152 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 147 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
153 }) 148 })
154 }) 149 })
155 150
@@ -172,7 +167,7 @@ describe('Test blocklist API validators', function () {
172 url: server.url, 167 url: server.url,
173 path, 168 path,
174 fields: { host: 'localhost:9002' }, 169 fields: { host: 'localhost:9002' },
175 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 170 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
176 }) 171 })
177 }) 172 })
178 173
@@ -182,7 +177,7 @@ describe('Test blocklist API validators', function () {
182 token: server.accessToken, 177 token: server.accessToken,
183 path, 178 path,
184 fields: { host: 'localhost:9003' }, 179 fields: { host: 'localhost:9003' },
185 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 180 expectedStatus: HttpStatusCode.NO_CONTENT_204
186 }) 181 })
187 }) 182 })
188 183
@@ -192,7 +187,7 @@ describe('Test blocklist API validators', function () {
192 token: server.accessToken, 187 token: server.accessToken,
193 path, 188 path,
194 fields: { host: 'localhost:' + server.port }, 189 fields: { host: 'localhost:' + server.port },
195 statusCodeExpected: HttpStatusCode.CONFLICT_409 190 expectedStatus: HttpStatusCode.CONFLICT_409
196 }) 191 })
197 }) 192 })
198 193
@@ -202,7 +197,7 @@ describe('Test blocklist API validators', function () {
202 token: server.accessToken, 197 token: server.accessToken,
203 path, 198 path,
204 fields: { host: 'localhost:' + servers[1].port }, 199 fields: { host: 'localhost:' + servers[1].port },
205 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 200 expectedStatus: HttpStatusCode.NO_CONTENT_204
206 }) 201 })
207 }) 202 })
208 }) 203 })
@@ -212,7 +207,7 @@ describe('Test blocklist API validators', function () {
212 await makeDeleteRequest({ 207 await makeDeleteRequest({
213 url: server.url, 208 url: server.url,
214 path: path + '/localhost:' + servers[1].port, 209 path: path + '/localhost:' + servers[1].port,
215 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 210 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
216 }) 211 })
217 }) 212 })
218 213
@@ -221,7 +216,7 @@ describe('Test blocklist API validators', function () {
221 url: server.url, 216 url: server.url,
222 path: path + '/localhost:9004', 217 path: path + '/localhost:9004',
223 token: server.accessToken, 218 token: server.accessToken,
224 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 219 expectedStatus: HttpStatusCode.NOT_FOUND_404
225 }) 220 })
226 }) 221 })
227 222
@@ -230,7 +225,7 @@ describe('Test blocklist API validators', function () {
230 url: server.url, 225 url: server.url,
231 path: path + '/localhost:' + servers[1].port, 226 path: path + '/localhost:' + servers[1].port,
232 token: server.accessToken, 227 token: server.accessToken,
233 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 228 expectedStatus: HttpStatusCode.NO_CONTENT_204
234 }) 229 })
235 }) 230 })
236 }) 231 })
@@ -247,7 +242,7 @@ describe('Test blocklist API validators', function () {
247 await makeGetRequest({ 242 await makeGetRequest({
248 url: server.url, 243 url: server.url,
249 path, 244 path,
250 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 245 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
251 }) 246 })
252 }) 247 })
253 248
@@ -256,7 +251,7 @@ describe('Test blocklist API validators', function () {
256 url: server.url, 251 url: server.url,
257 token: userAccessToken, 252 token: userAccessToken,
258 path, 253 path,
259 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 254 expectedStatus: HttpStatusCode.FORBIDDEN_403
260 }) 255 })
261 }) 256 })
262 257
@@ -279,7 +274,7 @@ describe('Test blocklist API validators', function () {
279 url: server.url, 274 url: server.url,
280 path, 275 path,
281 fields: { accountName: 'user1' }, 276 fields: { accountName: 'user1' },
282 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 277 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
283 }) 278 })
284 }) 279 })
285 280
@@ -289,7 +284,7 @@ describe('Test blocklist API validators', function () {
289 token: userAccessToken, 284 token: userAccessToken,
290 path, 285 path,
291 fields: { accountName: 'user1' }, 286 fields: { accountName: 'user1' },
292 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 287 expectedStatus: HttpStatusCode.FORBIDDEN_403
293 }) 288 })
294 }) 289 })
295 290
@@ -299,7 +294,7 @@ describe('Test blocklist API validators', function () {
299 token: server.accessToken, 294 token: server.accessToken,
300 path, 295 path,
301 fields: { accountName: 'user2' }, 296 fields: { accountName: 'user2' },
302 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 297 expectedStatus: HttpStatusCode.NOT_FOUND_404
303 }) 298 })
304 }) 299 })
305 300
@@ -309,7 +304,7 @@ describe('Test blocklist API validators', function () {
309 token: server.accessToken, 304 token: server.accessToken,
310 path, 305 path,
311 fields: { accountName: 'root' }, 306 fields: { accountName: 'root' },
312 statusCodeExpected: HttpStatusCode.CONFLICT_409 307 expectedStatus: HttpStatusCode.CONFLICT_409
313 }) 308 })
314 }) 309 })
315 310
@@ -319,7 +314,7 @@ describe('Test blocklist API validators', function () {
319 token: server.accessToken, 314 token: server.accessToken,
320 path, 315 path,
321 fields: { accountName: 'user1' }, 316 fields: { accountName: 'user1' },
322 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 317 expectedStatus: HttpStatusCode.NO_CONTENT_204
323 }) 318 })
324 }) 319 })
325 }) 320 })
@@ -329,7 +324,7 @@ describe('Test blocklist API validators', function () {
329 await makeDeleteRequest({ 324 await makeDeleteRequest({
330 url: server.url, 325 url: server.url,
331 path: path + '/user1', 326 path: path + '/user1',
332 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 327 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
333 }) 328 })
334 }) 329 })
335 330
@@ -338,7 +333,7 @@ describe('Test blocklist API validators', function () {
338 url: server.url, 333 url: server.url,
339 path: path + '/user1', 334 path: path + '/user1',
340 token: userAccessToken, 335 token: userAccessToken,
341 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 336 expectedStatus: HttpStatusCode.FORBIDDEN_403
342 }) 337 })
343 }) 338 })
344 339
@@ -347,7 +342,7 @@ describe('Test blocklist API validators', function () {
347 url: server.url, 342 url: server.url,
348 path: path + '/user2', 343 path: path + '/user2',
349 token: server.accessToken, 344 token: server.accessToken,
350 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 345 expectedStatus: HttpStatusCode.NOT_FOUND_404
351 }) 346 })
352 }) 347 })
353 348
@@ -356,7 +351,7 @@ describe('Test blocklist API validators', function () {
356 url: server.url, 351 url: server.url,
357 path: path + '/user1', 352 path: path + '/user1',
358 token: server.accessToken, 353 token: server.accessToken,
359 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 354 expectedStatus: HttpStatusCode.NO_CONTENT_204
360 }) 355 })
361 }) 356 })
362 }) 357 })
@@ -370,7 +365,7 @@ describe('Test blocklist API validators', function () {
370 await makeGetRequest({ 365 await makeGetRequest({
371 url: server.url, 366 url: server.url,
372 path, 367 path,
373 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 368 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
374 }) 369 })
375 }) 370 })
376 371
@@ -379,7 +374,7 @@ describe('Test blocklist API validators', function () {
379 url: server.url, 374 url: server.url,
380 token: userAccessToken, 375 token: userAccessToken,
381 path, 376 path,
382 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 377 expectedStatus: HttpStatusCode.FORBIDDEN_403
383 }) 378 })
384 }) 379 })
385 380
@@ -402,7 +397,7 @@ describe('Test blocklist API validators', function () {
402 url: server.url, 397 url: server.url,
403 path, 398 path,
404 fields: { host: 'localhost:' + servers[1].port }, 399 fields: { host: 'localhost:' + servers[1].port },
405 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 400 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
406 }) 401 })
407 }) 402 })
408 403
@@ -412,7 +407,7 @@ describe('Test blocklist API validators', function () {
412 token: userAccessToken, 407 token: userAccessToken,
413 path, 408 path,
414 fields: { host: 'localhost:' + servers[1].port }, 409 fields: { host: 'localhost:' + servers[1].port },
415 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 410 expectedStatus: HttpStatusCode.FORBIDDEN_403
416 }) 411 })
417 }) 412 })
418 413
@@ -422,7 +417,7 @@ describe('Test blocklist API validators', function () {
422 token: server.accessToken, 417 token: server.accessToken,
423 path, 418 path,
424 fields: { host: 'localhost:9003' }, 419 fields: { host: 'localhost:9003' },
425 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 420 expectedStatus: HttpStatusCode.NO_CONTENT_204
426 }) 421 })
427 }) 422 })
428 423
@@ -432,7 +427,7 @@ describe('Test blocklist API validators', function () {
432 token: server.accessToken, 427 token: server.accessToken,
433 path, 428 path,
434 fields: { host: 'localhost:' + server.port }, 429 fields: { host: 'localhost:' + server.port },
435 statusCodeExpected: HttpStatusCode.CONFLICT_409 430 expectedStatus: HttpStatusCode.CONFLICT_409
436 }) 431 })
437 }) 432 })
438 433
@@ -442,7 +437,7 @@ describe('Test blocklist API validators', function () {
442 token: server.accessToken, 437 token: server.accessToken,
443 path, 438 path,
444 fields: { host: 'localhost:' + servers[1].port }, 439 fields: { host: 'localhost:' + servers[1].port },
445 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 440 expectedStatus: HttpStatusCode.NO_CONTENT_204
446 }) 441 })
447 }) 442 })
448 }) 443 })
@@ -452,7 +447,7 @@ describe('Test blocklist API validators', function () {
452 await makeDeleteRequest({ 447 await makeDeleteRequest({
453 url: server.url, 448 url: server.url,
454 path: path + '/localhost:' + servers[1].port, 449 path: path + '/localhost:' + servers[1].port,
455 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 450 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
456 }) 451 })
457 }) 452 })
458 453
@@ -461,7 +456,7 @@ describe('Test blocklist API validators', function () {
461 url: server.url, 456 url: server.url,
462 path: path + '/localhost:' + servers[1].port, 457 path: path + '/localhost:' + servers[1].port,
463 token: userAccessToken, 458 token: userAccessToken,
464 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 459 expectedStatus: HttpStatusCode.FORBIDDEN_403
465 }) 460 })
466 }) 461 })
467 462
@@ -470,7 +465,7 @@ describe('Test blocklist API validators', function () {
470 url: server.url, 465 url: server.url,
471 path: path + '/localhost:9004', 466 path: path + '/localhost:9004',
472 token: server.accessToken, 467 token: server.accessToken,
473 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 468 expectedStatus: HttpStatusCode.NOT_FOUND_404
474 }) 469 })
475 }) 470 })
476 471
@@ -479,7 +474,7 @@ describe('Test blocklist API validators', function () {
479 url: server.url, 474 url: server.url,
480 path: path + '/localhost:' + servers[1].port, 475 path: path + '/localhost:' + servers[1].port,
481 token: server.accessToken, 476 token: server.accessToken,
482 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 477 expectedStatus: HttpStatusCode.NO_CONTENT_204
483 }) 478 })
484 }) 479 })
485 }) 480 })
diff --git a/server/tests/api/check-params/bulk.ts b/server/tests/api/check-params/bulk.ts
index 07b920ba7..bc9d7784d 100644
--- a/server/tests/api/check-params/bulk.ts
+++ b/server/tests/api/check-params/bulk.ts
@@ -1,19 +1,11 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { 4import { cleanupTests, createSingleServer, makePostBodyRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
5 cleanupTests, 5import { HttpStatusCode } from '@shared/models'
6 createUser,
7 flushAndRunServer,
8 ServerInfo,
9 setAccessTokensToServers,
10 userLogin
11} from '../../../../shared/extra-utils'
12import { makePostBodyRequest } from '../../../../shared/extra-utils/requests/requests'
13import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
14 6
15describe('Test bulk API validators', function () { 7describe('Test bulk API validators', function () {
16 let server: ServerInfo 8 let server: PeerTubeServer
17 let userAccessToken: string 9 let userAccessToken: string
18 10
19 // --------------------------------------------------------------- 11 // ---------------------------------------------------------------
@@ -21,13 +13,13 @@ describe('Test bulk API validators', function () {
21 before(async function () { 13 before(async function () {
22 this.timeout(120000) 14 this.timeout(120000)
23 15
24 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
25 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
26 18
27 const user = { username: 'user1', password: 'password' } 19 const user = { username: 'user1', password: 'password' }
28 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 20 await server.users.create({ username: user.username, password: user.password })
29 21
30 userAccessToken = await userLogin(server, user) 22 userAccessToken = await server.login.getAccessToken(user)
31 }) 23 })
32 24
33 describe('When removing comments of', function () { 25 describe('When removing comments of', function () {
@@ -38,7 +30,7 @@ describe('Test bulk API validators', function () {
38 url: server.url, 30 url: server.url,
39 path, 31 path,
40 fields: { accountName: 'user1', scope: 'my-videos' }, 32 fields: { accountName: 'user1', scope: 'my-videos' },
41 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 33 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
42 }) 34 })
43 }) 35 })
44 36
@@ -48,7 +40,7 @@ describe('Test bulk API validators', function () {
48 token: server.accessToken, 40 token: server.accessToken,
49 path, 41 path,
50 fields: { accountName: 'user2', scope: 'my-videos' }, 42 fields: { accountName: 'user2', scope: 'my-videos' },
51 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 43 expectedStatus: HttpStatusCode.NOT_FOUND_404
52 }) 44 })
53 }) 45 })
54 46
@@ -58,7 +50,7 @@ describe('Test bulk API validators', function () {
58 token: server.accessToken, 50 token: server.accessToken,
59 path, 51 path,
60 fields: { accountName: 'user1', scope: 'my-videoss' }, 52 fields: { accountName: 'user1', scope: 'my-videoss' },
61 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 53 expectedStatus: HttpStatusCode.BAD_REQUEST_400
62 }) 54 })
63 }) 55 })
64 56
@@ -68,7 +60,7 @@ describe('Test bulk API validators', function () {
68 token: userAccessToken, 60 token: userAccessToken,
69 path, 61 path,
70 fields: { accountName: 'user1', scope: 'instance' }, 62 fields: { accountName: 'user1', scope: 'instance' },
71 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 63 expectedStatus: HttpStatusCode.FORBIDDEN_403
72 }) 64 })
73 }) 65 })
74 66
@@ -78,7 +70,7 @@ describe('Test bulk API validators', function () {
78 token: server.accessToken, 70 token: server.accessToken,
79 path, 71 path,
80 fields: { accountName: 'user1', scope: 'instance' }, 72 fields: { accountName: 'user1', scope: 'instance' },
81 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 73 expectedStatus: HttpStatusCode.NO_CONTENT_204
82 }) 74 })
83 }) 75 })
84 }) 76 })
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts
index 9549070ef..87cb2287e 100644
--- a/server/tests/api/check-params/config.ts
+++ b/server/tests/api/check-params/config.ts
@@ -1,26 +1,21 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import { omit } from 'lodash'
4import 'mocha' 3import 'mocha'
5import { CustomConfig } from '../../../../shared/models/server/custom-config.model' 4import { omit } from 'lodash'
6
7import { 5import {
8 cleanupTests, 6 cleanupTests,
9 createUser, 7 createSingleServer,
10 flushAndRunServer,
11 immutableAssign,
12 makeDeleteRequest, 8 makeDeleteRequest,
13 makeGetRequest, 9 makeGetRequest,
14 makePutBodyRequest, 10 makePutBodyRequest,
15 ServerInfo, 11 PeerTubeServer,
16 setAccessTokensToServers, 12 setAccessTokensToServers
17 userLogin 13} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 14import { CustomConfig, HttpStatusCode } from '@shared/models'
19import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
20 15
21describe('Test config API validators', function () { 16describe('Test config API validators', function () {
22 const path = '/api/v1/config/custom' 17 const path = '/api/v1/config/custom'
23 let server: ServerInfo 18 let server: PeerTubeServer
24 let userAccessToken: string 19 let userAccessToken: string
25 const updateParams: CustomConfig = { 20 const updateParams: CustomConfig = {
26 instance: { 21 instance: {
@@ -201,7 +196,7 @@ describe('Test config API validators', function () {
201 before(async function () { 196 before(async function () {
202 this.timeout(30000) 197 this.timeout(30000)
203 198
204 server = await flushAndRunServer(1) 199 server = await createSingleServer(1)
205 200
206 await setAccessTokensToServers([ server ]) 201 await setAccessTokensToServers([ server ])
207 202
@@ -209,8 +204,8 @@ describe('Test config API validators', function () {
209 username: 'user1', 204 username: 'user1',
210 password: 'password' 205 password: 'password'
211 } 206 }
212 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 207 await server.users.create({ username: user.username, password: user.password })
213 userAccessToken = await userLogin(server, user) 208 userAccessToken = await server.login.getAccessToken(user)
214 }) 209 })
215 210
216 describe('When getting the configuration', function () { 211 describe('When getting the configuration', function () {
@@ -218,7 +213,7 @@ describe('Test config API validators', function () {
218 await makeGetRequest({ 213 await makeGetRequest({
219 url: server.url, 214 url: server.url,
220 path, 215 path,
221 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 216 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
222 }) 217 })
223 }) 218 })
224 219
@@ -227,7 +222,7 @@ describe('Test config API validators', function () {
227 url: server.url, 222 url: server.url,
228 path, 223 path,
229 token: userAccessToken, 224 token: userAccessToken,
230 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 225 expectedStatus: HttpStatusCode.FORBIDDEN_403
231 }) 226 })
232 }) 227 })
233 }) 228 })
@@ -238,7 +233,7 @@ describe('Test config API validators', function () {
238 url: server.url, 233 url: server.url,
239 path, 234 path,
240 fields: updateParams, 235 fields: updateParams,
241 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 236 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
242 }) 237 })
243 }) 238 })
244 239
@@ -248,7 +243,7 @@ describe('Test config API validators', function () {
248 path, 243 path,
249 fields: updateParams, 244 fields: updateParams,
250 token: userAccessToken, 245 token: userAccessToken,
251 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 246 expectedStatus: HttpStatusCode.FORBIDDEN_403
252 }) 247 })
253 }) 248 })
254 249
@@ -260,47 +255,53 @@ describe('Test config API validators', function () {
260 path, 255 path,
261 fields: newUpdateParams, 256 fields: newUpdateParams,
262 token: server.accessToken, 257 token: server.accessToken,
263 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 258 expectedStatus: HttpStatusCode.BAD_REQUEST_400
264 }) 259 })
265 }) 260 })
266 261
267 it('Should fail with a bad default NSFW policy', async function () { 262 it('Should fail with a bad default NSFW policy', async function () {
268 const newUpdateParams = immutableAssign(updateParams, { 263 const newUpdateParams = {
264 ...updateParams,
265
269 instance: { 266 instance: {
270 defaultNSFWPolicy: 'hello' 267 defaultNSFWPolicy: 'hello'
271 } 268 }
272 }) 269 }
273 270
274 await makePutBodyRequest({ 271 await makePutBodyRequest({
275 url: server.url, 272 url: server.url,
276 path, 273 path,
277 fields: newUpdateParams, 274 fields: newUpdateParams,
278 token: server.accessToken, 275 token: server.accessToken,
279 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 276 expectedStatus: HttpStatusCode.BAD_REQUEST_400
280 }) 277 })
281 }) 278 })
282 279
283 it('Should fail if email disabled and signup requires email verification', async function () { 280 it('Should fail if email disabled and signup requires email verification', async function () {
284 // opposite scenario - success when enable enabled - covered via tests/api/users/user-verification.ts 281 // opposite scenario - success when enable enabled - covered via tests/api/users/user-verification.ts
285 const newUpdateParams = immutableAssign(updateParams, { 282 const newUpdateParams = {
283 ...updateParams,
284
286 signup: { 285 signup: {
287 enabled: true, 286 enabled: true,
288 limit: 5, 287 limit: 5,
289 requiresEmailVerification: true 288 requiresEmailVerification: true
290 } 289 }
291 }) 290 }
292 291
293 await makePutBodyRequest({ 292 await makePutBodyRequest({
294 url: server.url, 293 url: server.url,
295 path, 294 path,
296 fields: newUpdateParams, 295 fields: newUpdateParams,
297 token: server.accessToken, 296 token: server.accessToken,
298 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 297 expectedStatus: HttpStatusCode.BAD_REQUEST_400
299 }) 298 })
300 }) 299 })
301 300
302 it('Should fail with a disabled webtorrent & hls transcoding', async function () { 301 it('Should fail with a disabled webtorrent & hls transcoding', async function () {
303 const newUpdateParams = immutableAssign(updateParams, { 302 const newUpdateParams = {
303 ...updateParams,
304
304 transcoding: { 305 transcoding: {
305 hls: { 306 hls: {
306 enabled: false 307 enabled: false
@@ -309,14 +310,14 @@ describe('Test config API validators', function () {
309 enabled: false 310 enabled: false
310 } 311 }
311 } 312 }
312 }) 313 }
313 314
314 await makePutBodyRequest({ 315 await makePutBodyRequest({
315 url: server.url, 316 url: server.url,
316 path, 317 path,
317 fields: newUpdateParams, 318 fields: newUpdateParams,
318 token: server.accessToken, 319 token: server.accessToken,
319 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 320 expectedStatus: HttpStatusCode.BAD_REQUEST_400
320 }) 321 })
321 }) 322 })
322 323
@@ -326,7 +327,7 @@ describe('Test config API validators', function () {
326 path, 327 path,
327 fields: updateParams, 328 fields: updateParams,
328 token: server.accessToken, 329 token: server.accessToken,
329 statusCodeExpected: HttpStatusCode.OK_200 330 expectedStatus: HttpStatusCode.OK_200
330 }) 331 })
331 }) 332 })
332 }) 333 })
@@ -336,7 +337,7 @@ describe('Test config API validators', function () {
336 await makeDeleteRequest({ 337 await makeDeleteRequest({
337 url: server.url, 338 url: server.url,
338 path, 339 path,
339 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 340 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
340 }) 341 })
341 }) 342 })
342 343
@@ -345,7 +346,7 @@ describe('Test config API validators', function () {
345 url: server.url, 346 url: server.url,
346 path, 347 path,
347 token: userAccessToken, 348 token: userAccessToken,
348 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 349 expectedStatus: HttpStatusCode.FORBIDDEN_403
349 }) 350 })
350 }) 351 })
351 }) 352 })
diff --git a/server/tests/api/check-params/contact-form.ts b/server/tests/api/check-params/contact-form.ts
index c7f9c1b47..1df9993da 100644
--- a/server/tests/api/check-params/contact-form.ts
+++ b/server/tests/api/check-params/contact-form.ts
@@ -1,14 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { cleanupTests, createSingleServer, killallServers, MockSmtpServer, PeerTubeServer } from '@shared/extra-utils'
5import { cleanupTests, flushAndRunServer, immutableAssign, killallServers, reRunServer, ServerInfo } from '../../../../shared/extra-utils' 5import { ContactFormCommand } from '@shared/extra-utils/server'
6import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form' 6import { HttpStatusCode } from '@shared/models'
7import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
9 7
10describe('Test contact form API validators', function () { 8describe('Test contact form API validators', function () {
11 let server: ServerInfo 9 let server: PeerTubeServer
12 const emails: object[] = [] 10 const emails: object[] = []
13 const defaultBody = { 11 const defaultBody = {
14 fromName: 'super name', 12 fromName: 'super name',
@@ -17,6 +15,7 @@ describe('Test contact form API validators', function () {
17 body: 'Hello, how are you?' 15 body: 'Hello, how are you?'
18 } 16 }
19 let emailPort: number 17 let emailPort: number
18 let command: ContactFormCommand
20 19
21 // --------------------------------------------------------------- 20 // ---------------------------------------------------------------
22 21
@@ -26,86 +25,51 @@ describe('Test contact form API validators', function () {
26 emailPort = await MockSmtpServer.Instance.collectEmails(emails) 25 emailPort = await MockSmtpServer.Instance.collectEmails(emails)
27 26
28 // Email is disabled 27 // Email is disabled
29 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
29 command = server.contactForm
30 }) 30 })
31 31
32 it('Should not accept a contact form if emails are disabled', async function () { 32 it('Should not accept a contact form if emails are disabled', async function () {
33 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: HttpStatusCode.CONFLICT_409 })) 33 await command.send({ ...defaultBody, expectedStatus: HttpStatusCode.CONFLICT_409 })
34 }) 34 })
35 35
36 it('Should not accept a contact form if it is disabled in the configuration', async function () { 36 it('Should not accept a contact form if it is disabled in the configuration', async function () {
37 this.timeout(10000) 37 this.timeout(10000)
38 38
39 killallServers([ server ]) 39 await killallServers([ server ])
40 40
41 // Contact form is disabled 41 // Contact form is disabled
42 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort }, contact_form: { enabled: false } }) 42 await server.run({ smtp: { hostname: 'localhost', port: emailPort }, contact_form: { enabled: false } })
43 await sendContactForm(immutableAssign(defaultBody, { url: server.url, expectedStatus: HttpStatusCode.CONFLICT_409 })) 43 await command.send({ ...defaultBody, expectedStatus: HttpStatusCode.CONFLICT_409 })
44 }) 44 })
45 45
46 it('Should not accept a contact form if from email is invalid', async function () { 46 it('Should not accept a contact form if from email is invalid', async function () {
47 this.timeout(10000) 47 this.timeout(10000)
48 48
49 killallServers([ server ]) 49 await killallServers([ server ])
50 50
51 // Email & contact form enabled 51 // Email & contact form enabled
52 await reRunServer(server, { smtp: { hostname: 'localhost', port: emailPort } }) 52 await server.run({ smtp: { hostname: 'localhost', port: emailPort } })
53 53
54 await sendContactForm(immutableAssign(defaultBody, { 54 await command.send({ ...defaultBody, fromEmail: 'badEmail', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
55 url: server.url, 55 await command.send({ ...defaultBody, fromEmail: 'badEmail@', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
56 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 56 await command.send({ ...defaultBody, fromEmail: undefined, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
57 fromEmail: 'badEmail'
58 }))
59 await sendContactForm(immutableAssign(defaultBody, {
60 url: server.url,
61 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
62 fromEmail: 'badEmail@'
63 }))
64 await sendContactForm(immutableAssign(defaultBody, {
65 url: server.url,
66 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
67 fromEmail: undefined
68 }))
69 }) 57 })
70 58
71 it('Should not accept a contact form if from name is invalid', async function () { 59 it('Should not accept a contact form if from name is invalid', async function () {
72 await sendContactForm(immutableAssign(defaultBody, { 60 await command.send({ ...defaultBody, fromName: 'name'.repeat(100), expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
73 url: server.url, 61 await command.send({ ...defaultBody, fromName: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
74 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 62 await command.send({ ...defaultBody, fromName: undefined, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
75 fromName: 'name'.repeat(100)
76 }))
77 await sendContactForm(immutableAssign(defaultBody, {
78 url: server.url,
79 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
80 fromName: ''
81 }))
82 await sendContactForm(immutableAssign(defaultBody, {
83 url: server.url,
84 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
85 fromName: undefined
86 }))
87 }) 63 })
88 64
89 it('Should not accept a contact form if body is invalid', async function () { 65 it('Should not accept a contact form if body is invalid', async function () {
90 await sendContactForm(immutableAssign(defaultBody, { 66 await command.send({ ...defaultBody, body: 'body'.repeat(5000), expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
91 url: server.url, 67 await command.send({ ...defaultBody, body: 'a', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
92 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 68 await command.send({ ...defaultBody, body: undefined, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
93 body: 'body'.repeat(5000)
94 }))
95 await sendContactForm(immutableAssign(defaultBody, {
96 url: server.url,
97 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
98 body: 'a'
99 }))
100 await sendContactForm(immutableAssign(defaultBody, {
101 url: server.url,
102 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
103 body: undefined
104 }))
105 }) 69 })
106 70
107 it('Should accept a contact form with the correct parameters', async function () { 71 it('Should accept a contact form with the correct parameters', async function () {
108 await sendContactForm(immutableAssign(defaultBody, { url: server.url })) 72 await command.send(defaultBody)
109 }) 73 })
110 74
111 after(async function () { 75 after(async function () {
diff --git a/server/tests/api/check-params/custom-pages.ts b/server/tests/api/check-params/custom-pages.ts
index 74ca3384c..9fbbea315 100644
--- a/server/tests/api/check-params/custom-pages.ts
+++ b/server/tests/api/check-params/custom-pages.ts
@@ -1,21 +1,20 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
5import { 4import {
6 cleanupTests, 5 cleanupTests,
7 createUser, 6 createSingleServer,
8 flushAndRunServer, 7 makeGetRequest,
9 ServerInfo, 8 makePutBodyRequest,
10 setAccessTokensToServers, 9 PeerTubeServer,
11 userLogin 10 setAccessTokensToServers
12} from '../../../../shared/extra-utils' 11} from '@shared/extra-utils'
13import { makeGetRequest, makePutBodyRequest } from '../../../../shared/extra-utils/requests/requests' 12import { HttpStatusCode } from '@shared/models'
14 13
15describe('Test custom pages validators', function () { 14describe('Test custom pages validators', function () {
16 const path = '/api/v1/custom-pages/homepage/instance' 15 const path = '/api/v1/custom-pages/homepage/instance'
17 16
18 let server: ServerInfo 17 let server: PeerTubeServer
19 let userAccessToken: string 18 let userAccessToken: string
20 19
21 // --------------------------------------------------------------- 20 // ---------------------------------------------------------------
@@ -23,13 +22,13 @@ describe('Test custom pages validators', function () {
23 before(async function () { 22 before(async function () {
24 this.timeout(120000) 23 this.timeout(120000)
25 24
26 server = await flushAndRunServer(1) 25 server = await createSingleServer(1)
27 await setAccessTokensToServers([ server ]) 26 await setAccessTokensToServers([ server ])
28 27
29 const user = { username: 'user1', password: 'password' } 28 const user = { username: 'user1', password: 'password' }
30 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 29 await server.users.create({ username: user.username, password: user.password })
31 30
32 userAccessToken = await userLogin(server, user) 31 userAccessToken = await server.login.getAccessToken(user)
33 }) 32 })
34 33
35 describe('When updating instance homepage', function () { 34 describe('When updating instance homepage', function () {
@@ -39,7 +38,7 @@ describe('Test custom pages validators', function () {
39 url: server.url, 38 url: server.url,
40 path, 39 path,
41 fields: { content: 'super content' }, 40 fields: { content: 'super content' },
42 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 41 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
43 }) 42 })
44 }) 43 })
45 44
@@ -49,7 +48,7 @@ describe('Test custom pages validators', function () {
49 path, 48 path,
50 token: userAccessToken, 49 token: userAccessToken,
51 fields: { content: 'super content' }, 50 fields: { content: 'super content' },
52 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 51 expectedStatus: HttpStatusCode.FORBIDDEN_403
53 }) 52 })
54 }) 53 })
55 54
@@ -59,7 +58,7 @@ describe('Test custom pages validators', function () {
59 path, 58 path,
60 token: server.accessToken, 59 token: server.accessToken,
61 fields: { content: 'super content' }, 60 fields: { content: 'super content' },
62 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 61 expectedStatus: HttpStatusCode.NO_CONTENT_204
63 }) 62 })
64 }) 63 })
65 }) 64 })
@@ -70,7 +69,7 @@ describe('Test custom pages validators', function () {
70 await makeGetRequest({ 69 await makeGetRequest({
71 url: server.url, 70 url: server.url,
72 path, 71 path,
73 statusCodeExpected: HttpStatusCode.OK_200 72 expectedStatus: HttpStatusCode.OK_200
74 }) 73 })
75 }) 74 })
76 }) 75 })
diff --git a/server/tests/api/check-params/debug.ts b/server/tests/api/check-params/debug.ts
index 37bf0f99b..a55786359 100644
--- a/server/tests/api/check-params/debug.ts
+++ b/server/tests/api/check-params/debug.ts
@@ -1,21 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
5import { 5import { HttpStatusCode } from '@shared/models'
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 ServerInfo,
10 setAccessTokensToServers,
11 userLogin
12} from '../../../../shared/extra-utils'
13import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15 6
16describe('Test debug API validators', function () { 7describe('Test debug API validators', function () {
17 const path = '/api/v1/server/debug' 8 const path = '/api/v1/server/debug'
18 let server: ServerInfo 9 let server: PeerTubeServer
19 let userAccessToken = '' 10 let userAccessToken = ''
20 11
21 // --------------------------------------------------------------- 12 // ---------------------------------------------------------------
@@ -23,7 +14,7 @@ describe('Test debug API validators', function () {
23 before(async function () { 14 before(async function () {
24 this.timeout(120000) 15 this.timeout(120000)
25 16
26 server = await flushAndRunServer(1) 17 server = await createSingleServer(1)
27 18
28 await setAccessTokensToServers([ server ]) 19 await setAccessTokensToServers([ server ])
29 20
@@ -31,8 +22,8 @@ describe('Test debug API validators', function () {
31 username: 'user1', 22 username: 'user1',
32 password: 'my super password' 23 password: 'my super password'
33 } 24 }
34 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 25 await server.users.create({ username: user.username, password: user.password })
35 userAccessToken = await userLogin(server, user) 26 userAccessToken = await server.login.getAccessToken(user)
36 }) 27 })
37 28
38 describe('When getting debug endpoint', function () { 29 describe('When getting debug endpoint', function () {
@@ -41,7 +32,7 @@ describe('Test debug API validators', function () {
41 await makeGetRequest({ 32 await makeGetRequest({
42 url: server.url, 33 url: server.url,
43 path, 34 path,
44 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 35 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
45 }) 36 })
46 }) 37 })
47 38
@@ -50,7 +41,7 @@ describe('Test debug API validators', function () {
50 url: server.url, 41 url: server.url,
51 path, 42 path,
52 token: userAccessToken, 43 token: userAccessToken,
53 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 44 expectedStatus: HttpStatusCode.FORBIDDEN_403
54 }) 45 })
55 }) 46 })
56 47
@@ -60,7 +51,7 @@ describe('Test debug API validators', function () {
60 path, 51 path,
61 token: server.accessToken, 52 token: server.accessToken,
62 query: { startDate: new Date().toISOString() }, 53 query: { startDate: new Date().toISOString() },
63 statusCodeExpected: HttpStatusCode.OK_200 54 expectedStatus: HttpStatusCode.OK_200
64 }) 55 })
65 }) 56 })
66 }) 57 })
diff --git a/server/tests/api/check-params/follows.ts b/server/tests/api/check-params/follows.ts
index c03dd5c9c..2bc9f6b96 100644
--- a/server/tests/api/check-params/follows.ts
+++ b/server/tests/api/check-params/follows.ts
@@ -1,33 +1,29 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import {
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 makeDeleteRequest, makeGetRequest,
10 makePostBodyRequest,
11 ServerInfo,
12 setAccessTokensToServers,
13 userLogin
14} from '../../../../shared/extra-utils'
15import { 4import {
16 checkBadCountPagination, 5 checkBadCountPagination,
17 checkBadSortPagination, 6 checkBadSortPagination,
18 checkBadStartPagination 7 checkBadStartPagination,
19} from '../../../../shared/extra-utils/requests/check-api-params' 8 cleanupTests,
20import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 9 createSingleServer,
10 makeDeleteRequest,
11 makeGetRequest,
12 makePostBodyRequest,
13 PeerTubeServer,
14 setAccessTokensToServers
15} from '@shared/extra-utils'
16import { HttpStatusCode } from '@shared/models'
21 17
22describe('Test server follows API validators', function () { 18describe('Test server follows API validators', function () {
23 let server: ServerInfo 19 let server: PeerTubeServer
24 20
25 // --------------------------------------------------------------- 21 // ---------------------------------------------------------------
26 22
27 before(async function () { 23 before(async function () {
28 this.timeout(30000) 24 this.timeout(30000)
29 25
30 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
31 27
32 await setAccessTokensToServers([ server ]) 28 await setAccessTokensToServers([ server ])
33 }) 29 })
@@ -36,64 +32,68 @@ describe('Test server follows API validators', function () {
36 let userAccessToken = null 32 let userAccessToken = null
37 33
38 before(async function () { 34 before(async function () {
39 const user = { 35 userAccessToken = await server.users.generateUserAndToken('user1')
40 username: 'user1',
41 password: 'password'
42 }
43
44 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password })
45 userAccessToken = await userLogin(server, user)
46 }) 36 })
47 37
48 describe('When adding follows', function () { 38 describe('When adding follows', function () {
49 const path = '/api/v1/server/following' 39 const path = '/api/v1/server/following'
50 40
51 it('Should fail without hosts', async function () { 41 it('Should fail with nothing', async function () {
52 await makePostBodyRequest({ 42 await makePostBodyRequest({
53 url: server.url, 43 url: server.url,
54 path, 44 path,
55 token: server.accessToken, 45 token: server.accessToken,
56 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 46 expectedStatus: HttpStatusCode.BAD_REQUEST_400
57 }) 47 })
58 }) 48 })
59 49
60 it('Should fail if hosts is not an array', async function () { 50 it('Should fail if hosts is not composed by hosts', async function () {
61 await makePostBodyRequest({ 51 await makePostBodyRequest({
62 url: server.url, 52 url: server.url,
63 path, 53 path,
54 fields: { hosts: [ 'localhost:9002', 'localhost:coucou' ] },
64 token: server.accessToken, 55 token: server.accessToken,
65 fields: { hosts: 'localhost:9002' }, 56 expectedStatus: HttpStatusCode.BAD_REQUEST_400
66 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400
67 }) 57 })
68 }) 58 })
69 59
70 it('Should fail if the array is not composed by hosts', async function () { 60 it('Should fail if hosts is composed with http schemes', async function () {
71 await makePostBodyRequest({ 61 await makePostBodyRequest({
72 url: server.url, 62 url: server.url,
73 path, 63 path,
74 fields: { hosts: [ 'localhost:9002', 'localhost:coucou' ] }, 64 fields: { hosts: [ 'localhost:9002', 'http://localhost:9003' ] },
65 token: server.accessToken,
66 expectedStatus: HttpStatusCode.BAD_REQUEST_400
67 })
68 })
69
70 it('Should fail if hosts are not unique', async function () {
71 await makePostBodyRequest({
72 url: server.url,
73 path,
74 fields: { urls: [ 'localhost:9002', 'localhost:9002' ] },
75 token: server.accessToken, 75 token: server.accessToken,
76 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 76 expectedStatus: HttpStatusCode.BAD_REQUEST_400
77 }) 77 })
78 }) 78 })
79 79
80 it('Should fail if the array is composed with http schemes', async function () { 80 it('Should fail if handles is not composed by handles', async function () {
81 await makePostBodyRequest({ 81 await makePostBodyRequest({
82 url: server.url, 82 url: server.url,
83 path, 83 path,
84 fields: { hosts: [ 'localhost:9002', 'http://localhost:9003' ] }, 84 fields: { handles: [ 'hello@example.com', 'localhost:9001' ] },
85 token: server.accessToken, 85 token: server.accessToken,
86 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 86 expectedStatus: HttpStatusCode.BAD_REQUEST_400
87 }) 87 })
88 }) 88 })
89 89
90 it('Should fail if hosts are not unique', async function () { 90 it('Should fail if handles are not unique', async function () {
91 await makePostBodyRequest({ 91 await makePostBodyRequest({
92 url: server.url, 92 url: server.url,
93 path, 93 path,
94 fields: { urls: [ 'localhost:9002', 'localhost:9002' ] }, 94 fields: { urls: [ 'hello@example.com', 'hello@example.com' ] },
95 token: server.accessToken, 95 token: server.accessToken,
96 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 96 expectedStatus: HttpStatusCode.BAD_REQUEST_400
97 }) 97 })
98 }) 98 })
99 99
@@ -103,7 +103,7 @@ describe('Test server follows API validators', function () {
103 path, 103 path,
104 fields: { hosts: [ 'localhost:9002' ] }, 104 fields: { hosts: [ 'localhost:9002' ] },
105 token: 'fake_token', 105 token: 'fake_token',
106 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 106 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
107 }) 107 })
108 }) 108 })
109 109
@@ -113,7 +113,7 @@ describe('Test server follows API validators', function () {
113 path, 113 path,
114 fields: { hosts: [ 'localhost:9002' ] }, 114 fields: { hosts: [ 'localhost:9002' ] },
115 token: userAccessToken, 115 token: userAccessToken,
116 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 116 expectedStatus: HttpStatusCode.FORBIDDEN_403
117 }) 117 })
118 }) 118 })
119 }) 119 })
@@ -157,7 +157,7 @@ describe('Test server follows API validators', function () {
157 await makeGetRequest({ 157 await makeGetRequest({
158 url: server.url, 158 url: server.url,
159 path, 159 path,
160 statusCodeExpected: HttpStatusCode.OK_200, 160 expectedStatus: HttpStatusCode.OK_200,
161 query: { 161 query: {
162 state: 'accepted', 162 state: 'accepted',
163 actorType: 'Application' 163 actorType: 'Application'
@@ -206,7 +206,7 @@ describe('Test server follows API validators', function () {
206 await makeGetRequest({ 206 await makeGetRequest({
207 url: server.url, 207 url: server.url,
208 path, 208 path,
209 statusCodeExpected: HttpStatusCode.OK_200, 209 expectedStatus: HttpStatusCode.OK_200,
210 query: { 210 query: {
211 state: 'accepted' 211 state: 'accepted'
212 } 212 }
@@ -222,7 +222,7 @@ describe('Test server follows API validators', function () {
222 url: server.url, 222 url: server.url,
223 path: path + '/toto@localhost:9002', 223 path: path + '/toto@localhost:9002',
224 token: 'fake_token', 224 token: 'fake_token',
225 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 225 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
226 }) 226 })
227 }) 227 })
228 228
@@ -231,7 +231,7 @@ describe('Test server follows API validators', function () {
231 url: server.url, 231 url: server.url,
232 path: path + '/toto@localhost:9002', 232 path: path + '/toto@localhost:9002',
233 token: userAccessToken, 233 token: userAccessToken,
234 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 234 expectedStatus: HttpStatusCode.FORBIDDEN_403
235 }) 235 })
236 }) 236 })
237 237
@@ -240,7 +240,7 @@ describe('Test server follows API validators', function () {
240 url: server.url, 240 url: server.url,
241 path: path + '/toto', 241 path: path + '/toto',
242 token: server.accessToken, 242 token: server.accessToken,
243 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 243 expectedStatus: HttpStatusCode.BAD_REQUEST_400
244 }) 244 })
245 }) 245 })
246 246
@@ -249,7 +249,7 @@ describe('Test server follows API validators', function () {
249 url: server.url, 249 url: server.url,
250 path: path + '/toto@localhost:9003', 250 path: path + '/toto@localhost:9003',
251 token: server.accessToken, 251 token: server.accessToken,
252 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 252 expectedStatus: HttpStatusCode.NOT_FOUND_404
253 }) 253 })
254 }) 254 })
255 }) 255 })
@@ -262,7 +262,7 @@ describe('Test server follows API validators', function () {
262 url: server.url, 262 url: server.url,
263 path: path + '/toto@localhost:9002/accept', 263 path: path + '/toto@localhost:9002/accept',
264 token: 'fake_token', 264 token: 'fake_token',
265 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 265 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
266 }) 266 })
267 }) 267 })
268 268
@@ -271,7 +271,7 @@ describe('Test server follows API validators', function () {
271 url: server.url, 271 url: server.url,
272 path: path + '/toto@localhost:9002/accept', 272 path: path + '/toto@localhost:9002/accept',
273 token: userAccessToken, 273 token: userAccessToken,
274 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 274 expectedStatus: HttpStatusCode.FORBIDDEN_403
275 }) 275 })
276 }) 276 })
277 277
@@ -280,7 +280,7 @@ describe('Test server follows API validators', function () {
280 url: server.url, 280 url: server.url,
281 path: path + '/toto/accept', 281 path: path + '/toto/accept',
282 token: server.accessToken, 282 token: server.accessToken,
283 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 283 expectedStatus: HttpStatusCode.BAD_REQUEST_400
284 }) 284 })
285 }) 285 })
286 286
@@ -289,7 +289,7 @@ describe('Test server follows API validators', function () {
289 url: server.url, 289 url: server.url,
290 path: path + '/toto@localhost:9003/accept', 290 path: path + '/toto@localhost:9003/accept',
291 token: server.accessToken, 291 token: server.accessToken,
292 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 292 expectedStatus: HttpStatusCode.NOT_FOUND_404
293 }) 293 })
294 }) 294 })
295 }) 295 })
@@ -302,7 +302,7 @@ describe('Test server follows API validators', function () {
302 url: server.url, 302 url: server.url,
303 path: path + '/toto@localhost:9002/reject', 303 path: path + '/toto@localhost:9002/reject',
304 token: 'fake_token', 304 token: 'fake_token',
305 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 305 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
306 }) 306 })
307 }) 307 })
308 308
@@ -311,7 +311,7 @@ describe('Test server follows API validators', function () {
311 url: server.url, 311 url: server.url,
312 path: path + '/toto@localhost:9002/reject', 312 path: path + '/toto@localhost:9002/reject',
313 token: userAccessToken, 313 token: userAccessToken,
314 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 314 expectedStatus: HttpStatusCode.FORBIDDEN_403
315 }) 315 })
316 }) 316 })
317 317
@@ -320,7 +320,7 @@ describe('Test server follows API validators', function () {
320 url: server.url, 320 url: server.url,
321 path: path + '/toto/reject', 321 path: path + '/toto/reject',
322 token: server.accessToken, 322 token: server.accessToken,
323 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 323 expectedStatus: HttpStatusCode.BAD_REQUEST_400
324 }) 324 })
325 }) 325 })
326 326
@@ -329,7 +329,7 @@ describe('Test server follows API validators', function () {
329 url: server.url, 329 url: server.url,
330 path: path + '/toto@localhost:9003/reject', 330 path: path + '/toto@localhost:9003/reject',
331 token: server.accessToken, 331 token: server.accessToken,
332 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 332 expectedStatus: HttpStatusCode.NOT_FOUND_404
333 }) 333 })
334 }) 334 })
335 }) 335 })
@@ -342,7 +342,7 @@ describe('Test server follows API validators', function () {
342 url: server.url, 342 url: server.url,
343 path: path + '/localhost:9002', 343 path: path + '/localhost:9002',
344 token: 'fake_token', 344 token: 'fake_token',
345 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 345 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
346 }) 346 })
347 }) 347 })
348 348
@@ -351,7 +351,7 @@ describe('Test server follows API validators', function () {
351 url: server.url, 351 url: server.url,
352 path: path + '/localhost:9002', 352 path: path + '/localhost:9002',
353 token: userAccessToken, 353 token: userAccessToken,
354 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 354 expectedStatus: HttpStatusCode.FORBIDDEN_403
355 }) 355 })
356 }) 356 })
357 357
@@ -360,7 +360,7 @@ describe('Test server follows API validators', function () {
360 url: server.url, 360 url: server.url,
361 path: path + '/example.com', 361 path: path + '/example.com',
362 token: server.accessToken, 362 token: server.accessToken,
363 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 363 expectedStatus: HttpStatusCode.NOT_FOUND_404
364 }) 364 })
365 }) 365 })
366 }) 366 })
diff --git a/server/tests/api/check-params/jobs.ts b/server/tests/api/check-params/jobs.ts
index 3c1d2049b..23d95d8e4 100644
--- a/server/tests/api/check-params/jobs.ts
+++ b/server/tests/api/check-params/jobs.ts
@@ -1,26 +1,21 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import {
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 ServerInfo,
10 setAccessTokensToServers,
11 userLogin
12} from '../../../../shared/extra-utils'
13import { 4import {
14 checkBadCountPagination, 5 checkBadCountPagination,
15 checkBadSortPagination, 6 checkBadSortPagination,
16 checkBadStartPagination 7 checkBadStartPagination,
17} from '../../../../shared/extra-utils/requests/check-api-params' 8 cleanupTests,
18import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests' 9 createSingleServer,
19import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 10 makeGetRequest,
11 PeerTubeServer,
12 setAccessTokensToServers
13} from '@shared/extra-utils'
14import { HttpStatusCode } from '@shared/models'
20 15
21describe('Test jobs API validators', function () { 16describe('Test jobs API validators', function () {
22 const path = '/api/v1/jobs/failed' 17 const path = '/api/v1/jobs/failed'
23 let server: ServerInfo 18 let server: PeerTubeServer
24 let userAccessToken = '' 19 let userAccessToken = ''
25 20
26 // --------------------------------------------------------------- 21 // ---------------------------------------------------------------
@@ -28,7 +23,7 @@ describe('Test jobs API validators', function () {
28 before(async function () { 23 before(async function () {
29 this.timeout(120000) 24 this.timeout(120000)
30 25
31 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
32 27
33 await setAccessTokensToServers([ server ]) 28 await setAccessTokensToServers([ server ])
34 29
@@ -36,8 +31,8 @@ describe('Test jobs API validators', function () {
36 username: 'user1', 31 username: 'user1',
37 password: 'my super password' 32 password: 'my super password'
38 } 33 }
39 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 34 await server.users.create({ username: user.username, password: user.password })
40 userAccessToken = await userLogin(server, user) 35 userAccessToken = await server.login.getAccessToken(user)
41 }) 36 })
42 37
43 describe('When listing jobs', function () { 38 describe('When listing jobs', function () {
@@ -77,7 +72,7 @@ describe('Test jobs API validators', function () {
77 await makeGetRequest({ 72 await makeGetRequest({
78 url: server.url, 73 url: server.url,
79 path, 74 path,
80 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 75 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
81 }) 76 })
82 }) 77 })
83 78
@@ -86,7 +81,7 @@ describe('Test jobs API validators', function () {
86 url: server.url, 81 url: server.url,
87 path, 82 path,
88 token: userAccessToken, 83 token: userAccessToken,
89 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 84 expectedStatus: HttpStatusCode.FORBIDDEN_403
90 }) 85 })
91 }) 86 })
92 87
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts
index 933d8abf2..700b4724d 100644
--- a/server/tests/api/check-params/live.ts
+++ b/server/tests/api/check-params/live.ts
@@ -2,69 +2,64 @@
2 2
3import 'mocha' 3import 'mocha'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { LiveVideo, VideoCreateResult, VideoPrivacy } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
8 buildAbsoluteFixturePath, 6 buildAbsoluteFixturePath,
9 cleanupTests, 7 cleanupTests,
10 createUser, 8 createSingleServer,
11 flushAndRunServer, 9 LiveCommand,
12 getLive,
13 getMyUserInformation,
14 immutableAssign,
15 makePostBodyRequest, 10 makePostBodyRequest,
16 makeUploadRequest, 11 makeUploadRequest,
17 runAndTestFfmpegStreamError, 12 PeerTubeServer,
18 sendRTMPStream, 13 sendRTMPStream,
19 ServerInfo,
20 setAccessTokensToServers, 14 setAccessTokensToServers,
21 stopFfmpeg, 15 stopFfmpeg
22 updateCustomSubConfig, 16} from '@shared/extra-utils'
23 updateLive, 17import { HttpStatusCode, VideoCreateResult, VideoPrivacy } from '@shared/models'
24 uploadVideoAndGetId,
25 userLogin,
26 waitUntilLivePublished
27} from '../../../../shared/extra-utils'
28 18
29describe('Test video lives API validator', function () { 19describe('Test video lives API validator', function () {
30 const path = '/api/v1/videos/live' 20 const path = '/api/v1/videos/live'
31 let server: ServerInfo 21 let server: PeerTubeServer
32 let userAccessToken = '' 22 let userAccessToken = ''
33 let channelId: number 23 let channelId: number
34 let video: VideoCreateResult 24 let video: VideoCreateResult
35 let videoIdNotLive: number 25 let videoIdNotLive: number
26 let command: LiveCommand
36 27
37 // --------------------------------------------------------------- 28 // ---------------------------------------------------------------
38 29
39 before(async function () { 30 before(async function () {
40 this.timeout(30000) 31 this.timeout(30000)
41 32
42 server = await flushAndRunServer(1) 33 server = await createSingleServer(1)
43 34
44 await setAccessTokensToServers([ server ]) 35 await setAccessTokensToServers([ server ])
45 36
46 await updateCustomSubConfig(server.url, server.accessToken, { 37 await server.config.updateCustomSubConfig({
47 live: { 38 newConfig: {
48 enabled: true, 39 live: {
49 maxInstanceLives: 20, 40 enabled: true,
50 maxUserLives: 20, 41 maxInstanceLives: 20,
51 allowReplay: true 42 maxUserLives: 20,
43 allowReplay: true
44 }
52 } 45 }
53 }) 46 })
54 47
55 const username = 'user1' 48 const username = 'user1'
56 const password = 'my super password' 49 const password = 'my super password'
57 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 50 await server.users.create({ username: username, password: password })
58 userAccessToken = await userLogin(server, { username, password }) 51 userAccessToken = await server.login.getAccessToken({ username, password })
59 52
60 { 53 {
61 const res = await getMyUserInformation(server.url, server.accessToken) 54 const { videoChannels } = await server.users.getMyInfo()
62 channelId = res.body.videoChannels[0].id 55 channelId = videoChannels[0].id
63 } 56 }
64 57
65 { 58 {
66 videoIdNotLive = (await uploadVideoAndGetId({ server, videoName: 'not live' })).id 59 videoIdNotLive = (await server.videos.quickUpload({ name: 'not live' })).id
67 } 60 }
61
62 command = server.live
68 }) 63 })
69 64
70 describe('When creating a live', function () { 65 describe('When creating a live', function () {
@@ -96,37 +91,37 @@ describe('Test video lives API validator', function () {
96 }) 91 })
97 92
98 it('Should fail with a long name', async function () { 93 it('Should fail with a long name', async function () {
99 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 94 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
100 95
101 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 96 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
102 }) 97 })
103 98
104 it('Should fail with a bad category', async function () { 99 it('Should fail with a bad category', async function () {
105 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 100 const fields = { ...baseCorrectParams, category: 125 }
106 101
107 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 102 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
108 }) 103 })
109 104
110 it('Should fail with a bad licence', async function () { 105 it('Should fail with a bad licence', async function () {
111 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 106 const fields = { ...baseCorrectParams, licence: 125 }
112 107
113 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 108 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
114 }) 109 })
115 110
116 it('Should fail with a bad language', async function () { 111 it('Should fail with a bad language', async function () {
117 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 112 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
118 113
119 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 114 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
120 }) 115 })
121 116
122 it('Should fail with a long description', async function () { 117 it('Should fail with a long description', async function () {
123 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 118 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
124 119
125 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 120 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
126 }) 121 })
127 122
128 it('Should fail with a long support text', async function () { 123 it('Should fail with a long support text', async function () {
129 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 124 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
130 125
131 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 126 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
132 }) 127 })
@@ -138,7 +133,7 @@ describe('Test video lives API validator', function () {
138 }) 133 })
139 134
140 it('Should fail with a bad channel', async function () { 135 it('Should fail with a bad channel', async function () {
141 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 136 const fields = { ...baseCorrectParams, channelId: 545454 }
142 137
143 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 138 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
144 }) 139 })
@@ -148,31 +143,31 @@ describe('Test video lives API validator', function () {
148 username: 'fake', 143 username: 'fake',
149 password: 'fake_password' 144 password: 'fake_password'
150 } 145 }
151 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 146 await server.users.create({ username: user.username, password: user.password })
152 147
153 const accessTokenUser = await userLogin(server, user) 148 const accessTokenUser = await server.login.getAccessToken(user)
154 const res = await getMyUserInformation(server.url, accessTokenUser) 149 const { videoChannels } = await server.users.getMyInfo({ token: accessTokenUser })
155 const customChannelId = res.body.videoChannels[0].id 150 const customChannelId = videoChannels[0].id
156 151
157 const fields = immutableAssign(baseCorrectParams, { channelId: customChannelId }) 152 const fields = { ...baseCorrectParams, channelId: customChannelId }
158 153
159 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 154 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
160 }) 155 })
161 156
162 it('Should fail with too many tags', async function () { 157 it('Should fail with too many tags', async function () {
163 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 158 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
164 159
165 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 160 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
166 }) 161 })
167 162
168 it('Should fail with a tag length too low', async function () { 163 it('Should fail with a tag length too low', async function () {
169 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 164 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
170 165
171 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 166 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
172 }) 167 })
173 168
174 it('Should fail with a tag length too big', async function () { 169 it('Should fail with a tag length too big', async function () {
175 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 170 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
176 171
177 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 172 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
178 }) 173 })
@@ -214,7 +209,7 @@ describe('Test video lives API validator', function () {
214 }) 209 })
215 210
216 it('Should fail with save replay and permanent live set to true', async function () { 211 it('Should fail with save replay and permanent live set to true', async function () {
217 const fields = immutableAssign(baseCorrectParams, { saveReplay: true, permanentLive: true }) 212 const fields = { ...baseCorrectParams, saveReplay: true, permanentLive: true }
218 213
219 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 214 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
220 }) 215 })
@@ -227,16 +222,18 @@ describe('Test video lives API validator', function () {
227 path, 222 path,
228 token: server.accessToken, 223 token: server.accessToken,
229 fields: baseCorrectParams, 224 fields: baseCorrectParams,
230 statusCodeExpected: HttpStatusCode.OK_200 225 expectedStatus: HttpStatusCode.OK_200
231 }) 226 })
232 227
233 video = res.body.video 228 video = res.body.video
234 }) 229 })
235 230
236 it('Should forbid if live is disabled', async function () { 231 it('Should forbid if live is disabled', async function () {
237 await updateCustomSubConfig(server.url, server.accessToken, { 232 await server.config.updateCustomSubConfig({
238 live: { 233 newConfig: {
239 enabled: false 234 live: {
235 enabled: false
236 }
240 } 237 }
241 }) 238 })
242 239
@@ -245,17 +242,19 @@ describe('Test video lives API validator', function () {
245 path, 242 path,
246 token: server.accessToken, 243 token: server.accessToken,
247 fields: baseCorrectParams, 244 fields: baseCorrectParams,
248 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 245 expectedStatus: HttpStatusCode.FORBIDDEN_403
249 }) 246 })
250 }) 247 })
251 248
252 it('Should forbid to save replay if not enabled by the admin', async function () { 249 it('Should forbid to save replay if not enabled by the admin', async function () {
253 const fields = immutableAssign(baseCorrectParams, { saveReplay: true }) 250 const fields = { ...baseCorrectParams, saveReplay: true }
254 251
255 await updateCustomSubConfig(server.url, server.accessToken, { 252 await server.config.updateCustomSubConfig({
256 live: { 253 newConfig: {
257 enabled: true, 254 live: {
258 allowReplay: false 255 enabled: true,
256 allowReplay: false
257 }
259 } 258 }
260 }) 259 })
261 260
@@ -264,17 +263,19 @@ describe('Test video lives API validator', function () {
264 path, 263 path,
265 token: server.accessToken, 264 token: server.accessToken,
266 fields, 265 fields,
267 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 266 expectedStatus: HttpStatusCode.FORBIDDEN_403
268 }) 267 })
269 }) 268 })
270 269
271 it('Should allow to save replay if enabled by the admin', async function () { 270 it('Should allow to save replay if enabled by the admin', async function () {
272 const fields = immutableAssign(baseCorrectParams, { saveReplay: true }) 271 const fields = { ...baseCorrectParams, saveReplay: true }
273 272
274 await updateCustomSubConfig(server.url, server.accessToken, { 273 await server.config.updateCustomSubConfig({
275 live: { 274 newConfig: {
276 enabled: true, 275 live: {
277 allowReplay: true 276 enabled: true,
277 allowReplay: true
278 }
278 } 279 }
279 }) 280 })
280 281
@@ -283,15 +284,17 @@ describe('Test video lives API validator', function () {
283 path, 284 path,
284 token: server.accessToken, 285 token: server.accessToken,
285 fields, 286 fields,
286 statusCodeExpected: HttpStatusCode.OK_200 287 expectedStatus: HttpStatusCode.OK_200
287 }) 288 })
288 }) 289 })
289 290
290 it('Should not allow live if max instance lives is reached', async function () { 291 it('Should not allow live if max instance lives is reached', async function () {
291 await updateCustomSubConfig(server.url, server.accessToken, { 292 await server.config.updateCustomSubConfig({
292 live: { 293 newConfig: {
293 enabled: true, 294 live: {
294 maxInstanceLives: 1 295 enabled: true,
296 maxInstanceLives: 1
297 }
295 } 298 }
296 }) 299 })
297 300
@@ -300,16 +303,18 @@ describe('Test video lives API validator', function () {
300 path, 303 path,
301 token: server.accessToken, 304 token: server.accessToken,
302 fields: baseCorrectParams, 305 fields: baseCorrectParams,
303 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 306 expectedStatus: HttpStatusCode.FORBIDDEN_403
304 }) 307 })
305 }) 308 })
306 309
307 it('Should not allow live if max user lives is reached', async function () { 310 it('Should not allow live if max user lives is reached', async function () {
308 await updateCustomSubConfig(server.url, server.accessToken, { 311 await server.config.updateCustomSubConfig({
309 live: { 312 newConfig: {
310 enabled: true, 313 live: {
311 maxInstanceLives: 20, 314 enabled: true,
312 maxUserLives: 1 315 maxInstanceLives: 20,
316 maxUserLives: 1
317 }
313 } 318 }
314 }) 319 })
315 320
@@ -318,7 +323,7 @@ describe('Test video lives API validator', function () {
318 path, 323 path,
319 token: server.accessToken, 324 token: server.accessToken,
320 fields: baseCorrectParams, 325 fields: baseCorrectParams,
321 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 326 expectedStatus: HttpStatusCode.FORBIDDEN_403
322 }) 327 })
323 }) 328 })
324 }) 329 })
@@ -326,110 +331,112 @@ describe('Test video lives API validator', function () {
326 describe('When getting live information', function () { 331 describe('When getting live information', function () {
327 332
328 it('Should fail without access token', async function () { 333 it('Should fail without access token', async function () {
329 await getLive(server.url, '', video.id, HttpStatusCode.UNAUTHORIZED_401) 334 await command.get({ token: '', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
330 }) 335 })
331 336
332 it('Should fail with a bad access token', async function () { 337 it('Should fail with a bad access token', async function () {
333 await getLive(server.url, 'toto', video.id, HttpStatusCode.UNAUTHORIZED_401) 338 await command.get({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
334 }) 339 })
335 340
336 it('Should fail with access token of another user', async function () { 341 it('Should fail with access token of another user', async function () {
337 await getLive(server.url, userAccessToken, video.id, HttpStatusCode.FORBIDDEN_403) 342 await command.get({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
338 }) 343 })
339 344
340 it('Should fail with a bad video id', async function () { 345 it('Should fail with a bad video id', async function () {
341 await getLive(server.url, server.accessToken, 'toto', HttpStatusCode.BAD_REQUEST_400) 346 await command.get({ videoId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
342 }) 347 })
343 348
344 it('Should fail with an unknown video id', async function () { 349 it('Should fail with an unknown video id', async function () {
345 await getLive(server.url, server.accessToken, 454555, HttpStatusCode.NOT_FOUND_404) 350 await command.get({ videoId: 454555, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
346 }) 351 })
347 352
348 it('Should fail with a non live video', async function () { 353 it('Should fail with a non live video', async function () {
349 await getLive(server.url, server.accessToken, videoIdNotLive, HttpStatusCode.NOT_FOUND_404) 354 await command.get({ videoId: videoIdNotLive, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
350 }) 355 })
351 356
352 it('Should succeed with the correct params', async function () { 357 it('Should succeed with the correct params', async function () {
353 await getLive(server.url, server.accessToken, video.id) 358 await command.get({ videoId: video.id })
354 await getLive(server.url, server.accessToken, video.shortUUID) 359 await command.get({ videoId: video.uuid })
360 await command.get({ videoId: video.shortUUID })
355 }) 361 })
356 }) 362 })
357 363
358 describe('When updating live information', async function () { 364 describe('When updating live information', async function () {
359 365
360 it('Should fail without access token', async function () { 366 it('Should fail without access token', async function () {
361 await updateLive(server.url, '', video.id, {}, HttpStatusCode.UNAUTHORIZED_401) 367 await command.update({ token: '', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
362 }) 368 })
363 369
364 it('Should fail with a bad access token', async function () { 370 it('Should fail with a bad access token', async function () {
365 await updateLive(server.url, 'toto', video.id, {}, HttpStatusCode.UNAUTHORIZED_401) 371 await command.update({ token: 'toto', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
366 }) 372 })
367 373
368 it('Should fail with access token of another user', async function () { 374 it('Should fail with access token of another user', async function () {
369 await updateLive(server.url, userAccessToken, video.id, {}, HttpStatusCode.FORBIDDEN_403) 375 await command.update({ token: userAccessToken, videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
370 }) 376 })
371 377
372 it('Should fail with a bad video id', async function () { 378 it('Should fail with a bad video id', async function () {
373 await updateLive(server.url, server.accessToken, 'toto', {}, HttpStatusCode.BAD_REQUEST_400) 379 await command.update({ videoId: 'toto', fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
374 }) 380 })
375 381
376 it('Should fail with an unknown video id', async function () { 382 it('Should fail with an unknown video id', async function () {
377 await updateLive(server.url, server.accessToken, 454555, {}, HttpStatusCode.NOT_FOUND_404) 383 await command.update({ videoId: 454555, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
378 }) 384 })
379 385
380 it('Should fail with a non live video', async function () { 386 it('Should fail with a non live video', async function () {
381 await updateLive(server.url, server.accessToken, videoIdNotLive, {}, HttpStatusCode.NOT_FOUND_404) 387 await command.update({ videoId: videoIdNotLive, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
382 }) 388 })
383 389
384 it('Should fail with save replay and permanent live set to true', async function () { 390 it('Should fail with save replay and permanent live set to true', async function () {
385 const fields = { saveReplay: true, permanentLive: true } 391 const fields = { saveReplay: true, permanentLive: true }
386 392
387 await updateLive(server.url, server.accessToken, video.id, fields, HttpStatusCode.BAD_REQUEST_400) 393 await command.update({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
388 }) 394 })
389 395
390 it('Should succeed with the correct params', async function () { 396 it('Should succeed with the correct params', async function () {
391 await updateLive(server.url, server.accessToken, video.id, { saveReplay: false }) 397 await command.update({ videoId: video.id, fields: { saveReplay: false } })
392 await updateLive(server.url, server.accessToken, video.shortUUID, { saveReplay: false }) 398 await command.update({ videoId: video.uuid, fields: { saveReplay: false } })
399 await command.update({ videoId: video.shortUUID, fields: { saveReplay: false } })
393 }) 400 })
394 401
395 it('Should fail to update replay status if replay is not allowed on the instance', async function () { 402 it('Should fail to update replay status if replay is not allowed on the instance', async function () {
396 await updateCustomSubConfig(server.url, server.accessToken, { 403 await server.config.updateCustomSubConfig({
397 live: { 404 newConfig: {
398 enabled: true, 405 live: {
399 allowReplay: false 406 enabled: true,
407 allowReplay: false
408 }
400 } 409 }
401 }) 410 })
402 411
403 await updateLive(server.url, server.accessToken, video.id, { saveReplay: true }, HttpStatusCode.FORBIDDEN_403) 412 await command.update({ videoId: video.id, fields: { saveReplay: true }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
404 }) 413 })
405 414
406 it('Should fail to update a live if it has already started', async function () { 415 it('Should fail to update a live if it has already started', async function () {
407 this.timeout(40000) 416 this.timeout(40000)
408 417
409 const resLive = await getLive(server.url, server.accessToken, video.id) 418 const live = await command.get({ videoId: video.id })
410 const live: LiveVideo = resLive.body
411 419
412 const command = sendRTMPStream(live.rtmpUrl, live.streamKey) 420 const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
413 421
414 await waitUntilLivePublished(server.url, server.accessToken, video.id) 422 await command.waitUntilPublished({ videoId: video.id })
415 await updateLive(server.url, server.accessToken, video.id, {}, HttpStatusCode.BAD_REQUEST_400) 423 await command.update({ videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
416 424
417 await stopFfmpeg(command) 425 await stopFfmpeg(ffmpegCommand)
418 }) 426 })
419 427
420 it('Should fail to stream twice in the save live', async function () { 428 it('Should fail to stream twice in the save live', async function () {
421 this.timeout(40000) 429 this.timeout(40000)
422 430
423 const resLive = await getLive(server.url, server.accessToken, video.id) 431 const live = await command.get({ videoId: video.id })
424 const live: LiveVideo = resLive.body
425 432
426 const command = sendRTMPStream(live.rtmpUrl, live.streamKey) 433 const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
427 434
428 await waitUntilLivePublished(server.url, server.accessToken, video.id) 435 await command.waitUntilPublished({ videoId: video.id })
429 436
430 await runAndTestFfmpegStreamError(server.url, server.accessToken, video.id, true) 437 await command.runAndTestStreamError({ videoId: video.id, shouldHaveError: true })
431 438
432 await stopFfmpeg(command) 439 await stopFfmpeg(ffmpegCommand)
433 }) 440 })
434 }) 441 })
435 442
diff --git a/server/tests/api/check-params/logs.ts b/server/tests/api/check-params/logs.ts
index dac1e6b98..05372257a 100644
--- a/server/tests/api/check-params/logs.ts
+++ b/server/tests/api/check-params/logs.ts
@@ -1,21 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
5import { 5import { HttpStatusCode } from '@shared/models'
6 cleanupTests,
7 createUser,
8 flushAndRunServer,
9 ServerInfo,
10 setAccessTokensToServers,
11 userLogin
12} from '../../../../shared/extra-utils'
13import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15 6
16describe('Test logs API validators', function () { 7describe('Test logs API validators', function () {
17 const path = '/api/v1/server/logs' 8 const path = '/api/v1/server/logs'
18 let server: ServerInfo 9 let server: PeerTubeServer
19 let userAccessToken = '' 10 let userAccessToken = ''
20 11
21 // --------------------------------------------------------------- 12 // ---------------------------------------------------------------
@@ -23,7 +14,7 @@ describe('Test logs API validators', function () {
23 before(async function () { 14 before(async function () {
24 this.timeout(120000) 15 this.timeout(120000)
25 16
26 server = await flushAndRunServer(1) 17 server = await createSingleServer(1)
27 18
28 await setAccessTokensToServers([ server ]) 19 await setAccessTokensToServers([ server ])
29 20
@@ -31,8 +22,8 @@ describe('Test logs API validators', function () {
31 username: 'user1', 22 username: 'user1',
32 password: 'my super password' 23 password: 'my super password'
33 } 24 }
34 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 25 await server.users.create({ username: user.username, password: user.password })
35 userAccessToken = await userLogin(server, user) 26 userAccessToken = await server.login.getAccessToken(user)
36 }) 27 })
37 28
38 describe('When getting logs', function () { 29 describe('When getting logs', function () {
@@ -41,7 +32,7 @@ describe('Test logs API validators', function () {
41 await makeGetRequest({ 32 await makeGetRequest({
42 url: server.url, 33 url: server.url,
43 path, 34 path,
44 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 35 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
45 }) 36 })
46 }) 37 })
47 38
@@ -50,7 +41,7 @@ describe('Test logs API validators', function () {
50 url: server.url, 41 url: server.url,
51 path, 42 path,
52 token: userAccessToken, 43 token: userAccessToken,
53 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 44 expectedStatus: HttpStatusCode.FORBIDDEN_403
54 }) 45 })
55 }) 46 })
56 47
@@ -59,7 +50,7 @@ describe('Test logs API validators', function () {
59 url: server.url, 50 url: server.url,
60 path, 51 path,
61 token: server.accessToken, 52 token: server.accessToken,
62 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 53 expectedStatus: HttpStatusCode.BAD_REQUEST_400
63 }) 54 })
64 }) 55 })
65 56
@@ -69,7 +60,7 @@ describe('Test logs API validators', function () {
69 path, 60 path,
70 token: server.accessToken, 61 token: server.accessToken,
71 query: { startDate: 'toto' }, 62 query: { startDate: 'toto' },
72 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 63 expectedStatus: HttpStatusCode.BAD_REQUEST_400
73 }) 64 })
74 }) 65 })
75 66
@@ -79,7 +70,7 @@ describe('Test logs API validators', function () {
79 path, 70 path,
80 token: server.accessToken, 71 token: server.accessToken,
81 query: { startDate: new Date().toISOString(), endDate: 'toto' }, 72 query: { startDate: new Date().toISOString(), endDate: 'toto' },
82 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 73 expectedStatus: HttpStatusCode.BAD_REQUEST_400
83 }) 74 })
84 }) 75 })
85 76
@@ -89,7 +80,7 @@ describe('Test logs API validators', function () {
89 path, 80 path,
90 token: server.accessToken, 81 token: server.accessToken,
91 query: { startDate: new Date().toISOString(), level: 'toto' }, 82 query: { startDate: new Date().toISOString(), level: 'toto' },
92 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 83 expectedStatus: HttpStatusCode.BAD_REQUEST_400
93 }) 84 })
94 }) 85 })
95 86
@@ -99,7 +90,7 @@ describe('Test logs API validators', function () {
99 path, 90 path,
100 token: server.accessToken, 91 token: server.accessToken,
101 query: { startDate: new Date().toISOString() }, 92 query: { startDate: new Date().toISOString() },
102 statusCodeExpected: HttpStatusCode.OK_200 93 expectedStatus: HttpStatusCode.OK_200
103 }) 94 })
104 }) 95 })
105 }) 96 })
diff --git a/server/tests/api/check-params/plugins.ts b/server/tests/api/check-params/plugins.ts
index a833fe6ff..33f84ecbc 100644
--- a/server/tests/api/check-params/plugins.ts
+++ b/server/tests/api/check-params/plugins.ts
@@ -1,27 +1,22 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { HttpStatusCode } from '@shared/core-utils'
5import { 4import {
6 checkBadCountPagination, 5 checkBadCountPagination,
7 checkBadSortPagination, 6 checkBadSortPagination,
8 checkBadStartPagination, 7 checkBadStartPagination,
9 cleanupTests, 8 cleanupTests,
10 createUser, 9 createSingleServer,
11 flushAndRunServer,
12 immutableAssign,
13 installPlugin,
14 makeGetRequest, 10 makeGetRequest,
15 makePostBodyRequest, 11 makePostBodyRequest,
16 makePutBodyRequest, 12 makePutBodyRequest,
17 ServerInfo, 13 PeerTubeServer,
18 setAccessTokensToServers, 14 setAccessTokensToServers
19 userLogin
20} from '@shared/extra-utils' 15} from '@shared/extra-utils'
21import { PeerTubePlugin, PluginType } from '@shared/models' 16import { HttpStatusCode, PeerTubePlugin, PluginType } from '@shared/models'
22 17
23describe('Test server plugins API validators', function () { 18describe('Test server plugins API validators', function () {
24 let server: ServerInfo 19 let server: PeerTubeServer
25 let userAccessToken = null 20 let userAccessToken = null
26 21
27 const npmPlugin = 'peertube-plugin-hello-world' 22 const npmPlugin = 'peertube-plugin-hello-world'
@@ -37,7 +32,7 @@ describe('Test server plugins API validators', function () {
37 before(async function () { 32 before(async function () {
38 this.timeout(30000) 33 this.timeout(30000)
39 34
40 server = await flushAndRunServer(1) 35 server = await createSingleServer(1)
41 36
42 await setAccessTokensToServers([ server ]) 37 await setAccessTokensToServers([ server ])
43 38
@@ -46,17 +41,17 @@ describe('Test server plugins API validators', function () {
46 password: 'password' 41 password: 'password'
47 } 42 }
48 43
49 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 44 await server.users.create({ username: user.username, password: user.password })
50 userAccessToken = await userLogin(server, user) 45 userAccessToken = await server.login.getAccessToken(user)
51 46
52 { 47 {
53 const res = await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: npmPlugin }) 48 const res = await server.plugins.install({ npmName: npmPlugin })
54 const plugin = res.body as PeerTubePlugin 49 const plugin = res.body as PeerTubePlugin
55 npmVersion = plugin.version 50 npmVersion = plugin.version
56 } 51 }
57 52
58 { 53 {
59 const res = await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: themePlugin }) 54 const res = await server.plugins.install({ npmName: themePlugin })
60 const plugin = res.body as PeerTubePlugin 55 const plugin = res.body as PeerTubePlugin
61 themeVersion = plugin.version 56 themeVersion = plugin.version
62 } 57 }
@@ -74,7 +69,7 @@ describe('Test server plugins API validators', function () {
74 ] 69 ]
75 70
76 for (const p of paths) { 71 for (const p of paths) {
77 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 72 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
78 } 73 }
79 }) 74 })
80 75
@@ -82,7 +77,7 @@ describe('Test server plugins API validators', function () {
82 await makeGetRequest({ 77 await makeGetRequest({
83 url: server.url, 78 url: server.url,
84 path: '/themes/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png', 79 path: '/themes/' + pluginName + '/' + npmVersion + '/static/images/chocobo.png',
85 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 80 expectedStatus: HttpStatusCode.NOT_FOUND_404
86 }) 81 })
87 }) 82 })
88 83
@@ -97,7 +92,7 @@ describe('Test server plugins API validators', function () {
97 ] 92 ]
98 93
99 for (const p of paths) { 94 for (const p of paths) {
100 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 95 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
101 } 96 }
102 }) 97 })
103 98
@@ -111,14 +106,14 @@ describe('Test server plugins API validators', function () {
111 ] 106 ]
112 107
113 for (const p of paths) { 108 for (const p of paths) {
114 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 109 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
115 } 110 }
116 }) 111 })
117 112
118 it('Should fail with an unknown auth name', async function () { 113 it('Should fail with an unknown auth name', async function () {
119 const path = '/plugins/' + pluginName + '/' + npmVersion + '/auth/bad-auth' 114 const path = '/plugins/' + pluginName + '/' + npmVersion + '/auth/bad-auth'
120 115
121 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 116 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
122 }) 117 })
123 118
124 it('Should fail with an unknown static file', async function () { 119 it('Should fail with an unknown static file', async function () {
@@ -130,7 +125,7 @@ describe('Test server plugins API validators', function () {
130 ] 125 ]
131 126
132 for (const p of paths) { 127 for (const p of paths) {
133 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 128 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
134 } 129 }
135 }) 130 })
136 131
@@ -138,7 +133,7 @@ describe('Test server plugins API validators', function () {
138 await makeGetRequest({ 133 await makeGetRequest({
139 url: server.url, 134 url: server.url,
140 path: '/themes/' + themeName + '/' + themeVersion + '/css/assets/fake.css', 135 path: '/themes/' + themeName + '/' + themeVersion + '/css/assets/fake.css',
141 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 136 expectedStatus: HttpStatusCode.NOT_FOUND_404
142 }) 137 })
143 }) 138 })
144 139
@@ -152,11 +147,11 @@ describe('Test server plugins API validators', function () {
152 ] 147 ]
153 148
154 for (const p of paths) { 149 for (const p of paths) {
155 await makeGetRequest({ url: server.url, path: p, statusCodeExpected: HttpStatusCode.OK_200 }) 150 await makeGetRequest({ url: server.url, path: p, expectedStatus: HttpStatusCode.OK_200 })
156 } 151 }
157 152
158 const authPath = '/plugins/' + pluginName + '/' + npmVersion + '/auth/fake-auth' 153 const authPath = '/plugins/' + pluginName + '/' + npmVersion + '/auth/fake-auth'
159 await makeGetRequest({ url: server.url, path: authPath, statusCodeExpected: HttpStatusCode.FOUND_302 }) 154 await makeGetRequest({ url: server.url, path: authPath, expectedStatus: HttpStatusCode.FOUND_302 })
160 }) 155 })
161 }) 156 })
162 157
@@ -174,7 +169,7 @@ describe('Test server plugins API validators', function () {
174 path, 169 path,
175 token: 'fake_token', 170 token: 'fake_token',
176 query: baseQuery, 171 query: baseQuery,
177 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 172 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
178 }) 173 })
179 }) 174 })
180 175
@@ -184,7 +179,7 @@ describe('Test server plugins API validators', function () {
184 path, 179 path,
185 token: userAccessToken, 180 token: userAccessToken,
186 query: baseQuery, 181 query: baseQuery,
187 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 182 expectedStatus: HttpStatusCode.FORBIDDEN_403
188 }) 183 })
189 }) 184 })
190 185
@@ -201,7 +196,7 @@ describe('Test server plugins API validators', function () {
201 }) 196 })
202 197
203 it('Should fail with an invalid plugin type', async function () { 198 it('Should fail with an invalid plugin type', async function () {
204 const query = immutableAssign(baseQuery, { pluginType: 5 }) 199 const query = { ...baseQuery, pluginType: 5 }
205 200
206 await makeGetRequest({ 201 await makeGetRequest({
207 url: server.url, 202 url: server.url,
@@ -212,7 +207,7 @@ describe('Test server plugins API validators', function () {
212 }) 207 })
213 208
214 it('Should fail with an invalid current peertube engine', async function () { 209 it('Should fail with an invalid current peertube engine', async function () {
215 const query = immutableAssign(baseQuery, { currentPeerTubeEngine: '1.0' }) 210 const query = { ...baseQuery, currentPeerTubeEngine: '1.0' }
216 211
217 await makeGetRequest({ 212 await makeGetRequest({
218 url: server.url, 213 url: server.url,
@@ -228,7 +223,7 @@ describe('Test server plugins API validators', function () {
228 path, 223 path,
229 token: server.accessToken, 224 token: server.accessToken,
230 query: baseQuery, 225 query: baseQuery,
231 statusCodeExpected: HttpStatusCode.OK_200 226 expectedStatus: HttpStatusCode.OK_200
232 }) 227 })
233 }) 228 })
234 }) 229 })
@@ -245,7 +240,7 @@ describe('Test server plugins API validators', function () {
245 path, 240 path,
246 token: 'fake_token', 241 token: 'fake_token',
247 query: baseQuery, 242 query: baseQuery,
248 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 243 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
249 }) 244 })
250 }) 245 })
251 246
@@ -255,7 +250,7 @@ describe('Test server plugins API validators', function () {
255 path, 250 path,
256 token: userAccessToken, 251 token: userAccessToken,
257 query: baseQuery, 252 query: baseQuery,
258 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 253 expectedStatus: HttpStatusCode.FORBIDDEN_403
259 }) 254 })
260 }) 255 })
261 256
@@ -272,7 +267,7 @@ describe('Test server plugins API validators', function () {
272 }) 267 })
273 268
274 it('Should fail with an invalid plugin type', async function () { 269 it('Should fail with an invalid plugin type', async function () {
275 const query = immutableAssign(baseQuery, { pluginType: 5 }) 270 const query = { ...baseQuery, pluginType: 5 }
276 271
277 await makeGetRequest({ 272 await makeGetRequest({
278 url: server.url, 273 url: server.url,
@@ -288,7 +283,7 @@ describe('Test server plugins API validators', function () {
288 path, 283 path,
289 token: server.accessToken, 284 token: server.accessToken,
290 query: baseQuery, 285 query: baseQuery,
291 statusCodeExpected: HttpStatusCode.OK_200 286 expectedStatus: HttpStatusCode.OK_200
292 }) 287 })
293 }) 288 })
294 }) 289 })
@@ -302,7 +297,7 @@ describe('Test server plugins API validators', function () {
302 url: server.url, 297 url: server.url,
303 path: path + suffix, 298 path: path + suffix,
304 token: 'fake_token', 299 token: 'fake_token',
305 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 300 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
306 }) 301 })
307 } 302 }
308 }) 303 })
@@ -313,7 +308,7 @@ describe('Test server plugins API validators', function () {
313 url: server.url, 308 url: server.url,
314 path: path + suffix, 309 path: path + suffix,
315 token: userAccessToken, 310 token: userAccessToken,
316 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 311 expectedStatus: HttpStatusCode.FORBIDDEN_403
317 }) 312 })
318 } 313 }
319 }) 314 })
@@ -324,7 +319,7 @@ describe('Test server plugins API validators', function () {
324 url: server.url, 319 url: server.url,
325 path: path + suffix, 320 path: path + suffix,
326 token: server.accessToken, 321 token: server.accessToken,
327 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 322 expectedStatus: HttpStatusCode.BAD_REQUEST_400
328 }) 323 })
329 } 324 }
330 325
@@ -333,7 +328,7 @@ describe('Test server plugins API validators', function () {
333 url: server.url, 328 url: server.url,
334 path: path + suffix, 329 path: path + suffix,
335 token: server.accessToken, 330 token: server.accessToken,
336 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 331 expectedStatus: HttpStatusCode.BAD_REQUEST_400
337 }) 332 })
338 } 333 }
339 }) 334 })
@@ -344,7 +339,7 @@ describe('Test server plugins API validators', function () {
344 url: server.url, 339 url: server.url,
345 path: path + suffix, 340 path: path + suffix,
346 token: server.accessToken, 341 token: server.accessToken,
347 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 342 expectedStatus: HttpStatusCode.NOT_FOUND_404
348 }) 343 })
349 } 344 }
350 }) 345 })
@@ -355,7 +350,7 @@ describe('Test server plugins API validators', function () {
355 url: server.url, 350 url: server.url,
356 path: path + suffix, 351 path: path + suffix,
357 token: server.accessToken, 352 token: server.accessToken,
358 statusCodeExpected: HttpStatusCode.OK_200 353 expectedStatus: HttpStatusCode.OK_200
359 }) 354 })
360 } 355 }
361 }) 356 })
@@ -371,7 +366,7 @@ describe('Test server plugins API validators', function () {
371 path: path + npmPlugin + '/settings', 366 path: path + npmPlugin + '/settings',
372 fields: { settings }, 367 fields: { settings },
373 token: 'fake_token', 368 token: 'fake_token',
374 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 369 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
375 }) 370 })
376 }) 371 })
377 372
@@ -381,7 +376,7 @@ describe('Test server plugins API validators', function () {
381 path: path + npmPlugin + '/settings', 376 path: path + npmPlugin + '/settings',
382 fields: { settings }, 377 fields: { settings },
383 token: userAccessToken, 378 token: userAccessToken,
384 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 379 expectedStatus: HttpStatusCode.FORBIDDEN_403
385 }) 380 })
386 }) 381 })
387 382
@@ -391,7 +386,7 @@ describe('Test server plugins API validators', function () {
391 path: path + 'toto/settings', 386 path: path + 'toto/settings',
392 fields: { settings }, 387 fields: { settings },
393 token: server.accessToken, 388 token: server.accessToken,
394 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 389 expectedStatus: HttpStatusCode.BAD_REQUEST_400
395 }) 390 })
396 391
397 await makePutBodyRequest({ 392 await makePutBodyRequest({
@@ -399,7 +394,7 @@ describe('Test server plugins API validators', function () {
399 path: path + 'peertube-plugin-TOTO/settings', 394 path: path + 'peertube-plugin-TOTO/settings',
400 fields: { settings }, 395 fields: { settings },
401 token: server.accessToken, 396 token: server.accessToken,
402 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 397 expectedStatus: HttpStatusCode.BAD_REQUEST_400
403 }) 398 })
404 }) 399 })
405 400
@@ -409,7 +404,7 @@ describe('Test server plugins API validators', function () {
409 path: path + 'peertube-plugin-toto/settings', 404 path: path + 'peertube-plugin-toto/settings',
410 fields: { settings }, 405 fields: { settings },
411 token: server.accessToken, 406 token: server.accessToken,
412 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 407 expectedStatus: HttpStatusCode.NOT_FOUND_404
413 }) 408 })
414 }) 409 })
415 410
@@ -419,7 +414,7 @@ describe('Test server plugins API validators', function () {
419 path: path + npmPlugin + '/settings', 414 path: path + npmPlugin + '/settings',
420 fields: { settings }, 415 fields: { settings },
421 token: server.accessToken, 416 token: server.accessToken,
422 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 417 expectedStatus: HttpStatusCode.NO_CONTENT_204
423 }) 418 })
424 }) 419 })
425 }) 420 })
@@ -434,7 +429,7 @@ describe('Test server plugins API validators', function () {
434 path: path + suffix, 429 path: path + suffix,
435 fields: { npmName: npmPlugin }, 430 fields: { npmName: npmPlugin },
436 token: 'fake_token', 431 token: 'fake_token',
437 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 432 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
438 }) 433 })
439 } 434 }
440 }) 435 })
@@ -446,7 +441,7 @@ describe('Test server plugins API validators', function () {
446 path: path + suffix, 441 path: path + suffix,
447 fields: { npmName: npmPlugin }, 442 fields: { npmName: npmPlugin },
448 token: userAccessToken, 443 token: userAccessToken,
449 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 444 expectedStatus: HttpStatusCode.FORBIDDEN_403
450 }) 445 })
451 } 446 }
452 }) 447 })
@@ -458,7 +453,7 @@ describe('Test server plugins API validators', function () {
458 path: path + suffix, 453 path: path + suffix,
459 fields: { npmName: 'toto' }, 454 fields: { npmName: 'toto' },
460 token: server.accessToken, 455 token: server.accessToken,
461 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 456 expectedStatus: HttpStatusCode.BAD_REQUEST_400
462 }) 457 })
463 } 458 }
464 459
@@ -468,7 +463,7 @@ describe('Test server plugins API validators', function () {
468 path: path + suffix, 463 path: path + suffix,
469 fields: { npmName: 'peertube-plugin-TOTO' }, 464 fields: { npmName: 'peertube-plugin-TOTO' },
470 token: server.accessToken, 465 token: server.accessToken,
471 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 466 expectedStatus: HttpStatusCode.BAD_REQUEST_400
472 }) 467 })
473 } 468 }
474 }) 469 })
@@ -488,7 +483,7 @@ describe('Test server plugins API validators', function () {
488 path: path + obj.suffix, 483 path: path + obj.suffix,
489 fields: { npmName: npmPlugin }, 484 fields: { npmName: npmPlugin },
490 token: server.accessToken, 485 token: server.accessToken,
491 statusCodeExpected: obj.status 486 expectedStatus: obj.status
492 }) 487 })
493 } 488 }
494 }) 489 })
diff --git a/server/tests/api/check-params/redundancy.ts b/server/tests/api/check-params/redundancy.ts
index dac6938de..d9f905549 100644
--- a/server/tests/api/check-params/redundancy.ts
+++ b/server/tests/api/check-params/redundancy.ts
@@ -1,30 +1,25 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { VideoCreateResult } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 4import {
7 checkBadCountPagination, 5 checkBadCountPagination,
8 checkBadSortPagination, 6 checkBadSortPagination,
9 checkBadStartPagination, 7 checkBadStartPagination,
10 cleanupTests, 8 cleanupTests,
11 createUser, 9 createMultipleServers,
12 doubleFollow, 10 doubleFollow,
13 flushAndRunMultipleServers,
14 getVideo,
15 makeDeleteRequest, 11 makeDeleteRequest,
16 makeGetRequest, 12 makeGetRequest,
17 makePostBodyRequest, 13 makePostBodyRequest,
18 makePutBodyRequest, 14 makePutBodyRequest,
19 ServerInfo, 15 PeerTubeServer,
20 setAccessTokensToServers, 16 setAccessTokensToServers,
21 uploadVideoAndGetId,
22 userLogin,
23 waitJobs 17 waitJobs
24} from '../../../../shared/extra-utils' 18} from '@shared/extra-utils'
19import { HttpStatusCode, VideoCreateResult } from '@shared/models'
25 20
26describe('Test server redundancy API validators', function () { 21describe('Test server redundancy API validators', function () {
27 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
28 let userAccessToken = null 23 let userAccessToken = null
29 let videoIdLocal: number 24 let videoIdLocal: number
30 let videoRemote: VideoCreateResult 25 let videoRemote: VideoCreateResult
@@ -34,7 +29,7 @@ describe('Test server redundancy API validators', function () {
34 before(async function () { 29 before(async function () {
35 this.timeout(80000) 30 this.timeout(80000)
36 31
37 servers = await flushAndRunMultipleServers(2) 32 servers = await createMultipleServers(2)
38 33
39 await setAccessTokensToServers(servers) 34 await setAccessTokensToServers(servers)
40 await doubleFollow(servers[0], servers[1]) 35 await doubleFollow(servers[0], servers[1])
@@ -44,17 +39,16 @@ describe('Test server redundancy API validators', function () {
44 password: 'password' 39 password: 'password'
45 } 40 }
46 41
47 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 42 await servers[0].users.create({ username: user.username, password: user.password })
48 userAccessToken = await userLogin(servers[0], user) 43 userAccessToken = await servers[0].login.getAccessToken(user)
49 44
50 videoIdLocal = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video' })).id 45 videoIdLocal = (await servers[0].videos.quickUpload({ name: 'video' })).id
51 46
52 const remoteUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video' })).uuid 47 const remoteUUID = (await servers[1].videos.quickUpload({ name: 'video' })).uuid
53 48
54 await waitJobs(servers) 49 await waitJobs(servers)
55 50
56 const resVideo = await getVideo(servers[0].url, remoteUUID) 51 videoRemote = await servers[0].videos.get({ id: remoteUUID })
57 videoRemote = resVideo.body
58 }) 52 })
59 53
60 describe('When listing redundancies', function () { 54 describe('When listing redundancies', function () {
@@ -69,11 +63,11 @@ describe('Test server redundancy API validators', function () {
69 }) 63 })
70 64
71 it('Should fail with an invalid token', async function () { 65 it('Should fail with an invalid token', async function () {
72 await makeGetRequest({ url, path, token: 'fake_token', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 66 await makeGetRequest({ url, path, token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
73 }) 67 })
74 68
75 it('Should fail if the user is not an administrator', async function () { 69 it('Should fail if the user is not an administrator', async function () {
76 await makeGetRequest({ url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 70 await makeGetRequest({ url, path, token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
77 }) 71 })
78 72
79 it('Should fail with a bad start pagination', async function () { 73 it('Should fail with a bad start pagination', async function () {
@@ -97,7 +91,7 @@ describe('Test server redundancy API validators', function () {
97 }) 91 })
98 92
99 it('Should succeed with the correct params', async function () { 93 it('Should succeed with the correct params', async function () {
100 await makeGetRequest({ url, path, token, query: { target: 'my-videos' }, statusCodeExpected: HttpStatusCode.OK_200 }) 94 await makeGetRequest({ url, path, token, query: { target: 'my-videos' }, expectedStatus: HttpStatusCode.OK_200 })
101 }) 95 })
102 }) 96 })
103 97
@@ -113,11 +107,11 @@ describe('Test server redundancy API validators', function () {
113 }) 107 })
114 108
115 it('Should fail with an invalid token', async function () { 109 it('Should fail with an invalid token', async function () {
116 await makePostBodyRequest({ url, path, token: 'fake_token', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 110 await makePostBodyRequest({ url, path, token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
117 }) 111 })
118 112
119 it('Should fail if the user is not an administrator', async function () { 113 it('Should fail if the user is not an administrator', async function () {
120 await makePostBodyRequest({ url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 114 await makePostBodyRequest({ url, path, token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
121 }) 115 })
122 116
123 it('Should fail without a video id', async function () { 117 it('Should fail without a video id', async function () {
@@ -129,7 +123,7 @@ describe('Test server redundancy API validators', function () {
129 }) 123 })
130 124
131 it('Should fail with a not found video id', async function () { 125 it('Should fail with a not found video id', async function () {
132 await makePostBodyRequest({ url, path, token, fields: { videoId: 6565 }, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 126 await makePostBodyRequest({ url, path, token, fields: { videoId: 6565 }, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
133 }) 127 })
134 128
135 it('Should fail with a local a video id', async function () { 129 it('Should fail with a local a video id', async function () {
@@ -142,7 +136,7 @@ describe('Test server redundancy API validators', function () {
142 path, 136 path,
143 token, 137 token,
144 fields: { videoId: videoRemote.shortUUID }, 138 fields: { videoId: videoRemote.shortUUID },
145 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 139 expectedStatus: HttpStatusCode.NO_CONTENT_204
146 }) 140 })
147 }) 141 })
148 142
@@ -156,7 +150,7 @@ describe('Test server redundancy API validators', function () {
156 path, 150 path,
157 token, 151 token,
158 fields: { videoId: videoRemote.uuid }, 152 fields: { videoId: videoRemote.uuid },
159 statusCodeExpected: HttpStatusCode.CONFLICT_409 153 expectedStatus: HttpStatusCode.CONFLICT_409
160 }) 154 })
161 }) 155 })
162 }) 156 })
@@ -173,11 +167,11 @@ describe('Test server redundancy API validators', function () {
173 }) 167 })
174 168
175 it('Should fail with an invalid token', async function () { 169 it('Should fail with an invalid token', async function () {
176 await makeDeleteRequest({ url, path: path + '1', token: 'fake_token', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 170 await makeDeleteRequest({ url, path: path + '1', token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
177 }) 171 })
178 172
179 it('Should fail if the user is not an administrator', async function () { 173 it('Should fail if the user is not an administrator', async function () {
180 await makeDeleteRequest({ url, path: path + '1', token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 174 await makeDeleteRequest({ url, path: path + '1', token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
181 }) 175 })
182 176
183 it('Should fail with an incorrect video id', async function () { 177 it('Should fail with an incorrect video id', async function () {
@@ -185,7 +179,7 @@ describe('Test server redundancy API validators', function () {
185 }) 179 })
186 180
187 it('Should fail with a not found video redundancy', async function () { 181 it('Should fail with a not found video redundancy', async function () {
188 await makeDeleteRequest({ url, path: path + '454545', token, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 182 await makeDeleteRequest({ url, path: path + '454545', token, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
189 }) 183 })
190 }) 184 })
191 185
@@ -198,7 +192,7 @@ describe('Test server redundancy API validators', function () {
198 path: path + '/localhost:' + servers[1].port, 192 path: path + '/localhost:' + servers[1].port,
199 fields: { redundancyAllowed: true }, 193 fields: { redundancyAllowed: true },
200 token: 'fake_token', 194 token: 'fake_token',
201 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 195 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
202 }) 196 })
203 }) 197 })
204 198
@@ -208,7 +202,7 @@ describe('Test server redundancy API validators', function () {
208 path: path + '/localhost:' + servers[1].port, 202 path: path + '/localhost:' + servers[1].port,
209 fields: { redundancyAllowed: true }, 203 fields: { redundancyAllowed: true },
210 token: userAccessToken, 204 token: userAccessToken,
211 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 205 expectedStatus: HttpStatusCode.FORBIDDEN_403
212 }) 206 })
213 }) 207 })
214 208
@@ -218,7 +212,7 @@ describe('Test server redundancy API validators', function () {
218 path: path + '/example.com', 212 path: path + '/example.com',
219 fields: { redundancyAllowed: true }, 213 fields: { redundancyAllowed: true },
220 token: servers[0].accessToken, 214 token: servers[0].accessToken,
221 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 215 expectedStatus: HttpStatusCode.NOT_FOUND_404
222 }) 216 })
223 }) 217 })
224 218
@@ -228,7 +222,7 @@ describe('Test server redundancy API validators', function () {
228 path: path + '/localhost:' + servers[1].port, 222 path: path + '/localhost:' + servers[1].port,
229 fields: { blabla: true }, 223 fields: { blabla: true },
230 token: servers[0].accessToken, 224 token: servers[0].accessToken,
231 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 225 expectedStatus: HttpStatusCode.BAD_REQUEST_400
232 }) 226 })
233 }) 227 })
234 228
@@ -238,7 +232,7 @@ describe('Test server redundancy API validators', function () {
238 path: path + '/localhost:' + servers[1].port, 232 path: path + '/localhost:' + servers[1].port,
239 fields: { redundancyAllowed: true }, 233 fields: { redundancyAllowed: true },
240 token: servers[0].accessToken, 234 token: servers[0].accessToken,
241 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 235 expectedStatus: HttpStatusCode.NO_CONTENT_204
242 }) 236 })
243 }) 237 })
244 }) 238 })
diff --git a/server/tests/api/check-params/search.ts b/server/tests/api/check-params/search.ts
index 20ad46cff..cc15d2593 100644
--- a/server/tests/api/check-params/search.ts
+++ b/server/tests/api/check-params/search.ts
@@ -2,41 +2,39 @@
2 2
3import 'mocha' 3import 'mocha'
4import { 4import {
5 checkBadCountPagination,
6 checkBadSortPagination,
7 checkBadStartPagination,
5 cleanupTests, 8 cleanupTests,
6 flushAndRunServer, 9 createSingleServer,
7 immutableAssign,
8 makeGetRequest, 10 makeGetRequest,
9 ServerInfo, 11 PeerTubeServer,
10 updateCustomSubConfig,
11 setAccessTokensToServers 12 setAccessTokensToServers
12} from '../../../../shared/extra-utils' 13} from '@shared/extra-utils'
13import { 14import { HttpStatusCode } from '@shared/models'
14 checkBadCountPagination, 15
15 checkBadSortPagination, 16function updateSearchIndex (server: PeerTubeServer, enabled: boolean, disableLocalSearch = false) {
16 checkBadStartPagination 17 return server.config.updateCustomSubConfig({
17} from '../../../../shared/extra-utils/requests/check-api-params' 18 newConfig: {
18import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 19 search: {
19 20 searchIndex: {
20function updateSearchIndex (server: ServerInfo, enabled: boolean, disableLocalSearch = false) { 21 enabled,
21 return updateCustomSubConfig(server.url, server.accessToken, { 22 disableLocalSearch
22 search: { 23 }
23 searchIndex: {
24 enabled,
25 disableLocalSearch
26 } 24 }
27 } 25 }
28 }) 26 })
29} 27}
30 28
31describe('Test videos API validator', function () { 29describe('Test videos API validator', function () {
32 let server: ServerInfo 30 let server: PeerTubeServer
33 31
34 // --------------------------------------------------------------- 32 // ---------------------------------------------------------------
35 33
36 before(async function () { 34 before(async function () {
37 this.timeout(30000) 35 this.timeout(30000)
38 36
39 server = await flushAndRunServer(1) 37 server = await createSingleServer(1)
40 await setAccessTokensToServers([ server ]) 38 await setAccessTokensToServers([ server ])
41 }) 39 })
42 40
@@ -59,84 +57,104 @@ describe('Test videos API validator', function () {
59 await checkBadSortPagination(server.url, path, null, query) 57 await checkBadSortPagination(server.url, path, null, query)
60 }) 58 })
61 59
62 it('Should success with the correct parameters', async function () { 60 it('Should succeed with the correct parameters', async function () {
63 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 61 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
64 }) 62 })
65 63
66 it('Should fail with an invalid category', async function () { 64 it('Should fail with an invalid category', async function () {
67 const customQuery1 = immutableAssign(query, { categoryOneOf: [ 'aa', 'b' ] }) 65 const customQuery1 = { ...query, categoryOneOf: [ 'aa', 'b' ] }
68 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 66 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
69 67
70 const customQuery2 = immutableAssign(query, { categoryOneOf: 'a' }) 68 const customQuery2 = { ...query, categoryOneOf: 'a' }
71 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 69 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
72 }) 70 })
73 71
74 it('Should succeed with a valid category', async function () { 72 it('Should succeed with a valid category', async function () {
75 const customQuery1 = immutableAssign(query, { categoryOneOf: [ 1, 7 ] }) 73 const customQuery1 = { ...query, categoryOneOf: [ 1, 7 ] }
76 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 74 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
77 75
78 const customQuery2 = immutableAssign(query, { categoryOneOf: 1 }) 76 const customQuery2 = { ...query, categoryOneOf: 1 }
79 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 77 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
80 }) 78 })
81 79
82 it('Should fail with an invalid licence', async function () { 80 it('Should fail with an invalid licence', async function () {
83 const customQuery1 = immutableAssign(query, { licenceOneOf: [ 'aa', 'b' ] }) 81 const customQuery1 = { ...query, licenceOneOf: [ 'aa', 'b' ] }
84 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 82 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
85 83
86 const customQuery2 = immutableAssign(query, { licenceOneOf: 'a' }) 84 const customQuery2 = { ...query, licenceOneOf: 'a' }
87 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 85 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
88 }) 86 })
89 87
90 it('Should succeed with a valid licence', async function () { 88 it('Should succeed with a valid licence', async function () {
91 const customQuery1 = immutableAssign(query, { licenceOneOf: [ 1, 2 ] }) 89 const customQuery1 = { ...query, licenceOneOf: [ 1, 2 ] }
92 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 90 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
93 91
94 const customQuery2 = immutableAssign(query, { licenceOneOf: 1 }) 92 const customQuery2 = { ...query, licenceOneOf: 1 }
95 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 93 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
96 }) 94 })
97 95
98 it('Should succeed with a valid language', async function () { 96 it('Should succeed with a valid language', async function () {
99 const customQuery1 = immutableAssign(query, { languageOneOf: [ 'fr', 'en' ] }) 97 const customQuery1 = { ...query, languageOneOf: [ 'fr', 'en' ] }
100 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 98 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
101 99
102 const customQuery2 = immutableAssign(query, { languageOneOf: 'fr' }) 100 const customQuery2 = { ...query, languageOneOf: 'fr' }
103 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 101 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
104 }) 102 })
105 103
106 it('Should succeed with valid tags', async function () { 104 it('Should succeed with valid tags', async function () {
107 const customQuery1 = immutableAssign(query, { tagsOneOf: [ 'tag1', 'tag2' ] }) 105 const customQuery1 = { ...query, tagsOneOf: [ 'tag1', 'tag2' ] }
108 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.OK_200 }) 106 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.OK_200 })
109 107
110 const customQuery2 = immutableAssign(query, { tagsOneOf: 'tag1' }) 108 const customQuery2 = { ...query, tagsOneOf: 'tag1' }
111 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.OK_200 }) 109 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.OK_200 })
112 110
113 const customQuery3 = immutableAssign(query, { tagsAllOf: [ 'tag1', 'tag2' ] }) 111 const customQuery3 = { ...query, tagsAllOf: [ 'tag1', 'tag2' ] }
114 await makeGetRequest({ url: server.url, path, query: customQuery3, statusCodeExpected: HttpStatusCode.OK_200 }) 112 await makeGetRequest({ url: server.url, path, query: customQuery3, expectedStatus: HttpStatusCode.OK_200 })
115 113
116 const customQuery4 = immutableAssign(query, { tagsAllOf: 'tag1' }) 114 const customQuery4 = { ...query, tagsAllOf: 'tag1' }
117 await makeGetRequest({ url: server.url, path, query: customQuery4, statusCodeExpected: HttpStatusCode.OK_200 }) 115 await makeGetRequest({ url: server.url, path, query: customQuery4, expectedStatus: HttpStatusCode.OK_200 })
118 }) 116 })
119 117
120 it('Should fail with invalid durations', async function () { 118 it('Should fail with invalid durations', async function () {
121 const customQuery1 = immutableAssign(query, { durationMin: 'hello' }) 119 const customQuery1 = { ...query, durationMin: 'hello' }
122 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 120 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
123 121
124 const customQuery2 = immutableAssign(query, { durationMax: 'hello' }) 122 const customQuery2 = { ...query, durationMax: 'hello' }
125 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 123 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
126 }) 124 })
127 125
128 it('Should fail with invalid dates', async function () { 126 it('Should fail with invalid dates', async function () {
129 const customQuery1 = immutableAssign(query, { startDate: 'hello' }) 127 const customQuery1 = { ...query, startDate: 'hello' }
130 await makeGetRequest({ url: server.url, path, query: customQuery1, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 128 await makeGetRequest({ url: server.url, path, query: customQuery1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
129
130 const customQuery2 = { ...query, endDate: 'hello' }
131 await makeGetRequest({ url: server.url, path, query: customQuery2, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
132
133 const customQuery3 = { ...query, originallyPublishedStartDate: 'hello' }
134 await makeGetRequest({ url: server.url, path, query: customQuery3, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
135
136 const customQuery4 = { ...query, originallyPublishedEndDate: 'hello' }
137 await makeGetRequest({ url: server.url, path, query: customQuery4, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
138 })
139
140 it('Should fail with an invalid host', async function () {
141 const customQuery = { ...query, host: '6565' }
142 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
143 })
131 144
132 const customQuery2 = immutableAssign(query, { endDate: 'hello' }) 145 it('Should succeed with a host', async function () {
133 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 146 const customQuery = { ...query, host: 'example.com' }
147 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
148 })
134 149
135 const customQuery3 = immutableAssign(query, { originallyPublishedStartDate: 'hello' }) 150 it('Should fail with invalid uuids', async function () {
136 await makeGetRequest({ url: server.url, path, query: customQuery3, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 151 const customQuery = { ...query, uuids: [ '6565', 'dfd70b83-639f-4980-94af-304a56ab4b35' ] }
152 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
153 })
137 154
138 const customQuery4 = immutableAssign(query, { originallyPublishedEndDate: 'hello' }) 155 it('Should succeed with valid uuids', async function () {
139 await makeGetRequest({ url: server.url, path, query: customQuery4, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 156 const customQuery = { ...query, uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] }
157 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
140 }) 158 })
141 }) 159 })
142 160
@@ -144,7 +162,8 @@ describe('Test videos API validator', function () {
144 const path = '/api/v1/search/video-playlists/' 162 const path = '/api/v1/search/video-playlists/'
145 163
146 const query = { 164 const query = {
147 search: 'coucou' 165 search: 'coucou',
166 host: 'example.com'
148 } 167 }
149 168
150 it('Should fail with a bad start pagination', async function () { 169 it('Should fail with a bad start pagination', async function () {
@@ -159,8 +178,17 @@ describe('Test videos API validator', function () {
159 await checkBadSortPagination(server.url, path, null, query) 178 await checkBadSortPagination(server.url, path, null, query)
160 }) 179 })
161 180
162 it('Should success with the correct parameters', async function () { 181 it('Should fail with an invalid host', async function () {
163 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 182 await makeGetRequest({ url: server.url, path, query: { ...query, host: '6565' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
183 })
184
185 it('Should fail with invalid uuids', async function () {
186 const customQuery = { ...query, uuids: [ '6565', 'dfd70b83-639f-4980-94af-304a56ab4b35' ] }
187 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
188 })
189
190 it('Should succeed with the correct parameters', async function () {
191 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
164 }) 192 })
165 }) 193 })
166 194
@@ -168,7 +196,8 @@ describe('Test videos API validator', function () {
168 const path = '/api/v1/search/video-channels/' 196 const path = '/api/v1/search/video-channels/'
169 197
170 const query = { 198 const query = {
171 search: 'coucou' 199 search: 'coucou',
200 host: 'example.com'
172 } 201 }
173 202
174 it('Should fail with a bad start pagination', async function () { 203 it('Should fail with a bad start pagination', async function () {
@@ -183,8 +212,16 @@ describe('Test videos API validator', function () {
183 await checkBadSortPagination(server.url, path, null, query) 212 await checkBadSortPagination(server.url, path, null, query)
184 }) 213 })
185 214
186 it('Should success with the correct parameters', async function () { 215 it('Should fail with an invalid host', async function () {
187 await makeGetRequest({ url: server.url, path, query, statusCodeExpected: HttpStatusCode.OK_200 }) 216 await makeGetRequest({ url: server.url, path, query: { ...query, host: '6565' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
217 })
218
219 it('Should fail with invalid handles', async function () {
220 await makeGetRequest({ url: server.url, path, query: { ...query, handles: [ '' ] }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
221 })
222
223 it('Should succeed with the correct parameters', async function () {
224 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
188 }) 225 })
189 }) 226 })
190 227
@@ -202,42 +239,42 @@ describe('Test videos API validator', function () {
202 239
203 for (const path of paths) { 240 for (const path of paths) {
204 { 241 {
205 const customQuery = immutableAssign(query, { searchTarget: 'hello' }) 242 const customQuery = { ...query, searchTarget: 'hello' }
206 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 243 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
207 } 244 }
208 245
209 { 246 {
210 const customQuery = immutableAssign(query, { searchTarget: undefined }) 247 const customQuery = { ...query, searchTarget: undefined }
211 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 248 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
212 } 249 }
213 250
214 { 251 {
215 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 252 const customQuery = { ...query, searchTarget: 'local' }
216 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 253 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
217 } 254 }
218 255
219 { 256 {
220 const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) 257 const customQuery = { ...query, searchTarget: 'search-index' }
221 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 258 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
222 } 259 }
223 260
224 await updateSearchIndex(server, true, true) 261 await updateSearchIndex(server, true, true)
225 262
226 { 263 {
227 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 264 const customQuery = { ...query, searchTarget: 'local' }
228 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 }) 265 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
229 } 266 }
230 267
231 { 268 {
232 const customQuery = immutableAssign(query, { searchTarget: 'search-index' }) 269 const customQuery = { ...query, searchTarget: 'search-index' }
233 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 270 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
234 } 271 }
235 272
236 await updateSearchIndex(server, true, false) 273 await updateSearchIndex(server, true, false)
237 274
238 { 275 {
239 const customQuery = immutableAssign(query, { searchTarget: 'local' }) 276 const customQuery = { ...query, searchTarget: 'local' }
240 await makeGetRequest({ url: server.url, path, query: customQuery, statusCodeExpected: HttpStatusCode.OK_200 }) 277 await makeGetRequest({ url: server.url, path, query: customQuery, expectedStatus: HttpStatusCode.OK_200 })
241 } 278 }
242 279
243 await updateSearchIndex(server, false, false) 280 await updateSearchIndex(server, false, false)
diff --git a/server/tests/api/check-params/services.ts b/server/tests/api/check-params/services.ts
index 514e3da70..8d795fabc 100644
--- a/server/tests/api/check-params/services.ts
+++ b/server/tests/api/check-params/services.ts
@@ -1,22 +1,18 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { 4import {
6 cleanupTests, 5 cleanupTests,
7 flushAndRunServer, 6 createSingleServer,
8 makeGetRequest, 7 makeGetRequest,
9 ServerInfo, 8 PeerTubeServer,
10 setAccessTokensToServers, 9 setAccessTokensToServers,
11 uploadVideo,
12 createVideoPlaylist,
13 setDefaultVideoChannel 10 setDefaultVideoChannel
14} from '../../../../shared/extra-utils' 11} from '@shared/extra-utils'
15import { VideoPlaylistPrivacy } from '@shared/models' 12import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
16import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
17 13
18describe('Test services API validators', function () { 14describe('Test services API validators', function () {
19 let server: ServerInfo 15 let server: PeerTubeServer
20 let playlistUUID: string 16 let playlistUUID: string
21 17
22 // --------------------------------------------------------------- 18 // ---------------------------------------------------------------
@@ -24,27 +20,22 @@ describe('Test services API validators', function () {
24 before(async function () { 20 before(async function () {
25 this.timeout(60000) 21 this.timeout(60000)
26 22
27 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
28 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
29 await setDefaultVideoChannel([ server ]) 25 await setDefaultVideoChannel([ server ])
30 26
31 { 27 server.store.videoCreated = await server.videos.upload({ attributes: { name: 'my super name' } })
32 const res = await uploadVideo(server.url, server.accessToken, { name: 'my super name' })
33 server.video = res.body.video
34 }
35 28
36 { 29 {
37 const res = await createVideoPlaylist({ 30 const created = await server.playlists.create({
38 url: server.url, 31 attributes: {
39 token: server.accessToken,
40 playlistAttrs: {
41 displayName: 'super playlist', 32 displayName: 'super playlist',
42 privacy: VideoPlaylistPrivacy.PUBLIC, 33 privacy: VideoPlaylistPrivacy.PUBLIC,
43 videoChannelId: server.videoChannel.id 34 videoChannelId: server.store.channel.id
44 } 35 }
45 }) 36 })
46 37
47 playlistUUID = res.body.videoPlaylist.uuid 38 playlistUUID = created.uuid
48 } 39 }
49 }) 40 })
50 41
@@ -56,7 +47,7 @@ describe('Test services API validators', function () {
56 }) 47 })
57 48
58 it('Should fail with an invalid host', async function () { 49 it('Should fail with an invalid host', async function () {
59 const embedUrl = 'http://hello.com/videos/watch/' + server.video.uuid 50 const embedUrl = 'http://hello.com/videos/watch/' + server.store.videoCreated.uuid
60 await checkParamEmbed(server, embedUrl) 51 await checkParamEmbed(server, embedUrl)
61 }) 52 })
62 53
@@ -71,37 +62,37 @@ describe('Test services API validators', function () {
71 }) 62 })
72 63
73 it('Should fail with an invalid path', async function () { 64 it('Should fail with an invalid path', async function () {
74 const embedUrl = `http://localhost:${server.port}/videos/watchs/${server.video.uuid}` 65 const embedUrl = `http://localhost:${server.port}/videos/watchs/${server.store.videoCreated.uuid}`
75 66
76 await checkParamEmbed(server, embedUrl) 67 await checkParamEmbed(server, embedUrl)
77 }) 68 })
78 69
79 it('Should fail with an invalid max height', async function () { 70 it('Should fail with an invalid max height', async function () {
80 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 71 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
81 72
82 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxheight: 'hello' }) 73 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxheight: 'hello' })
83 }) 74 })
84 75
85 it('Should fail with an invalid max width', async function () { 76 it('Should fail with an invalid max width', async function () {
86 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 77 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
87 78
88 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxwidth: 'hello' }) 79 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { maxwidth: 'hello' })
89 }) 80 })
90 81
91 it('Should fail with an invalid format', async function () { 82 it('Should fail with an invalid format', async function () {
92 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 83 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
93 84
94 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { format: 'blabla' }) 85 await checkParamEmbed(server, embedUrl, HttpStatusCode.BAD_REQUEST_400, { format: 'blabla' })
95 }) 86 })
96 87
97 it('Should fail with a non supported format', async function () { 88 it('Should fail with a non supported format', async function () {
98 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 89 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
99 90
100 await checkParamEmbed(server, embedUrl, HttpStatusCode.NOT_IMPLEMENTED_501, { format: 'xml' }) 91 await checkParamEmbed(server, embedUrl, HttpStatusCode.NOT_IMPLEMENTED_501, { format: 'xml' })
101 }) 92 })
102 93
103 it('Should succeed with the correct params with a video', async function () { 94 it('Should succeed with the correct params with a video', async function () {
104 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.video.uuid}` 95 const embedUrl = `http://localhost:${server.port}/videos/watch/${server.store.videoCreated.uuid}`
105 const query = { 96 const query = {
106 format: 'json', 97 format: 'json',
107 maxheight: 400, 98 maxheight: 400,
@@ -128,13 +119,13 @@ describe('Test services API validators', function () {
128 }) 119 })
129}) 120})
130 121
131function checkParamEmbed (server: ServerInfo, embedUrl: string, statusCodeExpected = HttpStatusCode.BAD_REQUEST_400, query = {}) { 122function checkParamEmbed (server: PeerTubeServer, embedUrl: string, expectedStatus = HttpStatusCode.BAD_REQUEST_400, query = {}) {
132 const path = '/services/oembed' 123 const path = '/services/oembed'
133 124
134 return makeGetRequest({ 125 return makeGetRequest({
135 url: server.url, 126 url: server.url,
136 path, 127 path,
137 query: Object.assign(query, { url: embedUrl }), 128 query: Object.assign(query, { url: embedUrl }),
138 statusCodeExpected 129 expectedStatus
139 }) 130 })
140} 131}
diff --git a/server/tests/api/check-params/upload-quota.ts b/server/tests/api/check-params/upload-quota.ts
index d0fbec415..322e93d0d 100644
--- a/server/tests/api/check-params/upload-quota.ts
+++ b/server/tests/api/check-params/upload-quota.ts
@@ -2,46 +2,39 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { HttpStatusCode, randomInt } from '@shared/core-utils' 5import { randomInt } from '@shared/core-utils'
6import { getGoodVideoUrl, getMagnetURI, getMyVideoImports, importVideo } from '@shared/extra-utils/videos/video-imports'
7import { MyUser, VideoImport, VideoImportState, VideoPrivacy } from '@shared/models'
8import { 6import {
9 cleanupTests, 7 cleanupTests,
10 flushAndRunServer, 8 createSingleServer,
11 getMyUserInformation, 9 FIXTURE_URLS,
12 immutableAssign, 10 PeerTubeServer,
13 registerUser,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 setDefaultVideoChannel, 12 setDefaultVideoChannel,
17 updateUser, 13 VideosCommand,
18 uploadVideo,
19 userLogin,
20 waitJobs 14 waitJobs
21} from '../../../../shared/extra-utils' 15} from '@shared/extra-utils'
16import { HttpStatusCode, VideoImportState, VideoPrivacy } from '@shared/models'
22 17
23describe('Test upload quota', function () { 18describe('Test upload quota', function () {
24 let server: ServerInfo 19 let server: PeerTubeServer
25 let rootId: number 20 let rootId: number
21 let command: VideosCommand
26 22
27 // --------------------------------------------------------------- 23 // ---------------------------------------------------------------
28 24
29 before(async function () { 25 before(async function () {
30 this.timeout(30000) 26 this.timeout(30000)
31 27
32 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
33 await setAccessTokensToServers([ server ]) 29 await setAccessTokensToServers([ server ])
34 await setDefaultVideoChannel([ server ]) 30 await setDefaultVideoChannel([ server ])
35 31
36 const res = await getMyUserInformation(server.url, server.accessToken) 32 const user = await server.users.getMyInfo()
37 rootId = (res.body as MyUser).id 33 rootId = user.id
38 34
39 await updateUser({ 35 await server.users.update({ userId: rootId, videoQuota: 42 })
40 url: server.url, 36
41 userId: rootId, 37 command = server.videos
42 accessToken: server.accessToken,
43 videoQuota: 42
44 })
45 }) 38 })
46 39
47 describe('When having a video quota', function () { 40 describe('When having a video quota', function () {
@@ -50,49 +43,48 @@ describe('Test upload quota', function () {
50 this.timeout(30000) 43 this.timeout(30000)
51 44
52 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' } 45 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' }
53 await registerUser(server.url, user.username, user.password) 46 await server.users.register(user)
54 const userAccessToken = await userLogin(server, user) 47 const userToken = await server.login.getAccessToken(user)
55 48
56 const videoAttributes = { fixture: 'video_short2.webm' } 49 const attributes = { fixture: 'video_short2.webm' }
57 for (let i = 0; i < 5; i++) { 50 for (let i = 0; i < 5; i++) {
58 await uploadVideo(server.url, userAccessToken, videoAttributes) 51 await command.upload({ token: userToken, attributes })
59 } 52 }
60 53
61 await uploadVideo(server.url, userAccessToken, videoAttributes, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 54 await command.upload({ token: userToken, attributes, expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
62 }) 55 })
63 56
64 it('Should fail with a registered user having too many videos with resumable upload', async function () { 57 it('Should fail with a registered user having too many videos with resumable upload', async function () {
65 this.timeout(30000) 58 this.timeout(30000)
66 59
67 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' } 60 const user = { username: 'registered' + randomInt(1, 1500), password: 'password' }
68 await registerUser(server.url, user.username, user.password) 61 await server.users.register(user)
69 const userAccessToken = await userLogin(server, user) 62 const userToken = await server.login.getAccessToken(user)
70 63
71 const videoAttributes = { fixture: 'video_short2.webm' } 64 const attributes = { fixture: 'video_short2.webm' }
72 for (let i = 0; i < 5; i++) { 65 for (let i = 0; i < 5; i++) {
73 await uploadVideo(server.url, userAccessToken, videoAttributes) 66 await command.upload({ token: userToken, attributes })
74 } 67 }
75 68
76 await uploadVideo(server.url, userAccessToken, videoAttributes, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 69 await command.upload({ token: userToken, attributes, expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
77 }) 70 })
78 71
79 it('Should fail to import with HTTP/Torrent/magnet', async function () { 72 it('Should fail to import with HTTP/Torrent/magnet', async function () {
80 this.timeout(120000) 73 this.timeout(120000)
81 74
82 const baseAttributes = { 75 const baseAttributes = {
83 channelId: server.videoChannel.id, 76 channelId: server.store.channel.id,
84 privacy: VideoPrivacy.PUBLIC 77 privacy: VideoPrivacy.PUBLIC
85 } 78 }
86 await importVideo(server.url, server.accessToken, immutableAssign(baseAttributes, { targetUrl: getGoodVideoUrl() })) 79 await server.imports.importVideo({ attributes: { ...baseAttributes, targetUrl: FIXTURE_URLS.goodVideo } })
87 await importVideo(server.url, server.accessToken, immutableAssign(baseAttributes, { magnetUri: getMagnetURI() })) 80 await server.imports.importVideo({ attributes: { ...baseAttributes, magnetUri: FIXTURE_URLS.magnet } })
88 await importVideo(server.url, server.accessToken, immutableAssign(baseAttributes, { torrentfile: 'video-720p.torrent' as any })) 81 await server.imports.importVideo({ attributes: { ...baseAttributes, torrentfile: 'video-720p.torrent' as any } })
89 82
90 await waitJobs([ server ]) 83 await waitJobs([ server ])
91 84
92 const res = await getMyVideoImports(server.url, server.accessToken) 85 const { total, data: videoImports } = await server.imports.getMyVideoImports()
86 expect(total).to.equal(3)
93 87
94 expect(res.body.total).to.equal(3)
95 const videoImports: VideoImport[] = res.body.data
96 expect(videoImports).to.have.lengthOf(3) 88 expect(videoImports).to.have.lengthOf(3)
97 89
98 for (const videoImport of videoImports) { 90 for (const videoImport of videoImports) {
@@ -106,43 +98,34 @@ describe('Test upload quota', function () {
106 describe('When having a daily video quota', function () { 98 describe('When having a daily video quota', function () {
107 99
108 it('Should fail with a user having too many videos daily', async function () { 100 it('Should fail with a user having too many videos daily', async function () {
109 await updateUser({ 101 await server.users.update({ userId: rootId, videoQuotaDaily: 42 })
110 url: server.url,
111 userId: rootId,
112 accessToken: server.accessToken,
113 videoQuotaDaily: 42
114 })
115 102
116 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 103 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
117 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 104 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
118 }) 105 })
119 }) 106 })
120 107
121 describe('When having an absolute and daily video quota', function () { 108 describe('When having an absolute and daily video quota', function () {
122 it('Should fail if exceeding total quota', async function () { 109 it('Should fail if exceeding total quota', async function () {
123 await updateUser({ 110 await server.users.update({
124 url: server.url,
125 userId: rootId, 111 userId: rootId,
126 accessToken: server.accessToken,
127 videoQuota: 42, 112 videoQuota: 42,
128 videoQuotaDaily: 1024 * 1024 * 1024 113 videoQuotaDaily: 1024 * 1024 * 1024
129 }) 114 })
130 115
131 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 116 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
132 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 117 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
133 }) 118 })
134 119
135 it('Should fail if exceeding daily quota', async function () { 120 it('Should fail if exceeding daily quota', async function () {
136 await updateUser({ 121 await server.users.update({
137 url: server.url,
138 userId: rootId, 122 userId: rootId,
139 accessToken: server.accessToken,
140 videoQuota: 1024 * 1024 * 1024, 123 videoQuota: 1024 * 1024 * 1024,
141 videoQuotaDaily: 42 124 videoQuotaDaily: 42
142 }) 125 })
143 126
144 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'legacy') 127 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'legacy' })
145 await uploadVideo(server.url, server.accessToken, {}, HttpStatusCode.PAYLOAD_TOO_LARGE_413, 'resumable') 128 await command.upload({ expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413, mode: 'resumable' })
146 }) 129 })
147 }) 130 })
148 131
diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts
index 26d4423f9..17edf5aa1 100644
--- a/server/tests/api/check-params/user-notifications.ts
+++ b/server/tests/api/check-params/user-notifications.ts
@@ -2,35 +2,30 @@
2 2
3import 'mocha' 3import 'mocha'
4import { io } from 'socket.io-client' 4import { io } from 'socket.io-client'
5
6import { 5import {
6 checkBadCountPagination,
7 checkBadSortPagination,
8 checkBadStartPagination,
7 cleanupTests, 9 cleanupTests,
8 flushAndRunServer, 10 createSingleServer,
9 immutableAssign,
10 makeGetRequest, 11 makeGetRequest,
11 makePostBodyRequest, 12 makePostBodyRequest,
12 makePutBodyRequest, 13 makePutBodyRequest,
13 ServerInfo, 14 PeerTubeServer,
14 setAccessTokensToServers, 15 setAccessTokensToServers,
15 wait 16 wait
16} from '../../../../shared/extra-utils' 17} from '@shared/extra-utils'
17import { 18import { HttpStatusCode, UserNotificationSetting, UserNotificationSettingValue } from '@shared/models'
18 checkBadCountPagination,
19 checkBadSortPagination,
20 checkBadStartPagination
21} from '../../../../shared/extra-utils/requests/check-api-params'
22import { UserNotificationSetting, UserNotificationSettingValue } from '../../../../shared/models/users'
23import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
24 19
25describe('Test user notifications API validators', function () { 20describe('Test user notifications API validators', function () {
26 let server: ServerInfo 21 let server: PeerTubeServer
27 22
28 // --------------------------------------------------------------- 23 // ---------------------------------------------------------------
29 24
30 before(async function () { 25 before(async function () {
31 this.timeout(30000) 26 this.timeout(30000)
32 27
33 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
34 29
35 await setAccessTokensToServers([ server ]) 30 await setAccessTokensToServers([ server ])
36 }) 31 })
@@ -58,7 +53,7 @@ describe('Test user notifications API validators', function () {
58 unread: 'toto' 53 unread: 'toto'
59 }, 54 },
60 token: server.accessToken, 55 token: server.accessToken,
61 statusCodeExpected: HttpStatusCode.OK_200 56 expectedStatus: HttpStatusCode.OK_200
62 }) 57 })
63 }) 58 })
64 59
@@ -66,7 +61,7 @@ describe('Test user notifications API validators', function () {
66 await makeGetRequest({ 61 await makeGetRequest({
67 url: server.url, 62 url: server.url,
68 path, 63 path,
69 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 64 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
70 }) 65 })
71 }) 66 })
72 67
@@ -75,7 +70,7 @@ describe('Test user notifications API validators', function () {
75 url: server.url, 70 url: server.url,
76 path, 71 path,
77 token: server.accessToken, 72 token: server.accessToken,
78 statusCodeExpected: HttpStatusCode.OK_200 73 expectedStatus: HttpStatusCode.OK_200
79 }) 74 })
80 }) 75 })
81 }) 76 })
@@ -91,7 +86,7 @@ describe('Test user notifications API validators', function () {
91 ids: [ 'hello' ] 86 ids: [ 'hello' ]
92 }, 87 },
93 token: server.accessToken, 88 token: server.accessToken,
94 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 89 expectedStatus: HttpStatusCode.BAD_REQUEST_400
95 }) 90 })
96 91
97 await makePostBodyRequest({ 92 await makePostBodyRequest({
@@ -101,7 +96,7 @@ describe('Test user notifications API validators', function () {
101 ids: [ ] 96 ids: [ ]
102 }, 97 },
103 token: server.accessToken, 98 token: server.accessToken,
104 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 99 expectedStatus: HttpStatusCode.BAD_REQUEST_400
105 }) 100 })
106 101
107 await makePostBodyRequest({ 102 await makePostBodyRequest({
@@ -111,7 +106,7 @@ describe('Test user notifications API validators', function () {
111 ids: 5 106 ids: 5
112 }, 107 },
113 token: server.accessToken, 108 token: server.accessToken,
114 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 109 expectedStatus: HttpStatusCode.BAD_REQUEST_400
115 }) 110 })
116 }) 111 })
117 112
@@ -122,7 +117,7 @@ describe('Test user notifications API validators', function () {
122 fields: { 117 fields: {
123 ids: [ 5 ] 118 ids: [ 5 ]
124 }, 119 },
125 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 120 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
126 }) 121 })
127 }) 122 })
128 123
@@ -134,7 +129,7 @@ describe('Test user notifications API validators', function () {
134 ids: [ 5 ] 129 ids: [ 5 ]
135 }, 130 },
136 token: server.accessToken, 131 token: server.accessToken,
137 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 132 expectedStatus: HttpStatusCode.NO_CONTENT_204
138 }) 133 })
139 }) 134 })
140 }) 135 })
@@ -146,7 +141,7 @@ describe('Test user notifications API validators', function () {
146 await makePostBodyRequest({ 141 await makePostBodyRequest({
147 url: server.url, 142 url: server.url,
148 path, 143 path,
149 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 144 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
150 }) 145 })
151 }) 146 })
152 147
@@ -155,7 +150,7 @@ describe('Test user notifications API validators', function () {
155 url: server.url, 150 url: server.url,
156 path, 151 path,
157 token: server.accessToken, 152 token: server.accessToken,
158 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 153 expectedStatus: HttpStatusCode.NO_CONTENT_204
159 }) 154 })
160 }) 155 })
161 }) 156 })
@@ -187,32 +182,32 @@ describe('Test user notifications API validators', function () {
187 path, 182 path,
188 token: server.accessToken, 183 token: server.accessToken,
189 fields: { newVideoFromSubscription: UserNotificationSettingValue.WEB }, 184 fields: { newVideoFromSubscription: UserNotificationSettingValue.WEB },
190 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 185 expectedStatus: HttpStatusCode.BAD_REQUEST_400
191 }) 186 })
192 }) 187 })
193 188
194 it('Should fail with incorrect field values', async function () { 189 it('Should fail with incorrect field values', async function () {
195 { 190 {
196 const fields = immutableAssign(correctFields, { newCommentOnMyVideo: 15 }) 191 const fields = { ...correctFields, newCommentOnMyVideo: 15 }
197 192
198 await makePutBodyRequest({ 193 await makePutBodyRequest({
199 url: server.url, 194 url: server.url,
200 path, 195 path,
201 token: server.accessToken, 196 token: server.accessToken,
202 fields, 197 fields,
203 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 198 expectedStatus: HttpStatusCode.BAD_REQUEST_400
204 }) 199 })
205 } 200 }
206 201
207 { 202 {
208 const fields = immutableAssign(correctFields, { newCommentOnMyVideo: 'toto' }) 203 const fields = { ...correctFields, newCommentOnMyVideo: 'toto' }
209 204
210 await makePutBodyRequest({ 205 await makePutBodyRequest({
211 url: server.url, 206 url: server.url,
212 path, 207 path,
213 fields, 208 fields,
214 token: server.accessToken, 209 token: server.accessToken,
215 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 210 expectedStatus: HttpStatusCode.BAD_REQUEST_400
216 }) 211 })
217 } 212 }
218 }) 213 })
@@ -222,7 +217,7 @@ describe('Test user notifications API validators', function () {
222 url: server.url, 217 url: server.url,
223 path, 218 path,
224 fields: correctFields, 219 fields: correctFields,
225 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 220 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
226 }) 221 })
227 }) 222 })
228 223
@@ -232,7 +227,7 @@ describe('Test user notifications API validators', function () {
232 path, 227 path,
233 token: server.accessToken, 228 token: server.accessToken,
234 fields: correctFields, 229 fields: correctFields,
235 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 230 expectedStatus: HttpStatusCode.NO_CONTENT_204
236 }) 231 })
237 }) 232 })
238 }) 233 })
diff --git a/server/tests/api/check-params/user-subscriptions.ts b/server/tests/api/check-params/user-subscriptions.ts
index 538201647..624069c80 100644
--- a/server/tests/api/check-params/user-subscriptions.ts
+++ b/server/tests/api/check-params/user-subscriptions.ts
@@ -1,30 +1,24 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4
5import { 4import {
5 checkBadCountPagination,
6 checkBadSortPagination,
7 checkBadStartPagination,
6 cleanupTests, 8 cleanupTests,
7 createUser, 9 createSingleServer,
8 flushAndRunServer,
9 makeDeleteRequest, 10 makeDeleteRequest,
10 makeGetRequest, 11 makeGetRequest,
11 makePostBodyRequest, 12 makePostBodyRequest,
12 ServerInfo, 13 PeerTubeServer,
13 setAccessTokensToServers, 14 setAccessTokensToServers,
14 userLogin 15 waitJobs
15} from '../../../../shared/extra-utils' 16} from '@shared/extra-utils'
16 17import { HttpStatusCode } from '@shared/models'
17import {
18 checkBadCountPagination,
19 checkBadSortPagination,
20 checkBadStartPagination
21} from '../../../../shared/extra-utils/requests/check-api-params'
22import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
24 18
25describe('Test user subscriptions API validators', function () { 19describe('Test user subscriptions API validators', function () {
26 const path = '/api/v1/users/me/subscriptions' 20 const path = '/api/v1/users/me/subscriptions'
27 let server: ServerInfo 21 let server: PeerTubeServer
28 let userAccessToken = '' 22 let userAccessToken = ''
29 23
30 // --------------------------------------------------------------- 24 // ---------------------------------------------------------------
@@ -32,7 +26,7 @@ describe('Test user subscriptions API validators', function () {
32 before(async function () { 26 before(async function () {
33 this.timeout(30000) 27 this.timeout(30000)
34 28
35 server = await flushAndRunServer(1) 29 server = await createSingleServer(1)
36 30
37 await setAccessTokensToServers([ server ]) 31 await setAccessTokensToServers([ server ])
38 32
@@ -40,8 +34,8 @@ describe('Test user subscriptions API validators', function () {
40 username: 'user1', 34 username: 'user1',
41 password: 'my super password' 35 password: 'my super password'
42 } 36 }
43 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 37 await server.users.create({ username: user.username, password: user.password })
44 userAccessToken = await userLogin(server, user) 38 userAccessToken = await server.login.getAccessToken(user)
45 }) 39 })
46 40
47 describe('When listing my subscriptions', function () { 41 describe('When listing my subscriptions', function () {
@@ -61,7 +55,7 @@ describe('Test user subscriptions API validators', function () {
61 await makeGetRequest({ 55 await makeGetRequest({
62 url: server.url, 56 url: server.url,
63 path, 57 path,
64 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 58 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
65 }) 59 })
66 }) 60 })
67 61
@@ -70,7 +64,7 @@ describe('Test user subscriptions API validators', function () {
70 url: server.url, 64 url: server.url,
71 path, 65 path,
72 token: userAccessToken, 66 token: userAccessToken,
73 statusCodeExpected: HttpStatusCode.OK_200 67 expectedStatus: HttpStatusCode.OK_200
74 }) 68 })
75 }) 69 })
76 }) 70 })
@@ -94,7 +88,7 @@ describe('Test user subscriptions API validators', function () {
94 await makeGetRequest({ 88 await makeGetRequest({
95 url: server.url, 89 url: server.url,
96 path, 90 path,
97 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 91 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
98 }) 92 })
99 }) 93 })
100 94
@@ -103,7 +97,7 @@ describe('Test user subscriptions API validators', function () {
103 url: server.url, 97 url: server.url,
104 path, 98 path,
105 token: userAccessToken, 99 token: userAccessToken,
106 statusCodeExpected: HttpStatusCode.OK_200 100 expectedStatus: HttpStatusCode.OK_200
107 }) 101 })
108 }) 102 })
109 }) 103 })
@@ -114,7 +108,7 @@ describe('Test user subscriptions API validators', function () {
114 url: server.url, 108 url: server.url,
115 path, 109 path,
116 fields: { uri: 'user1_channel@localhost:' + server.port }, 110 fields: { uri: 'user1_channel@localhost:' + server.port },
117 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 111 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
118 }) 112 })
119 }) 113 })
120 114
@@ -124,7 +118,7 @@ describe('Test user subscriptions API validators', function () {
124 path, 118 path,
125 token: server.accessToken, 119 token: server.accessToken,
126 fields: { uri: 'root' }, 120 fields: { uri: 'root' },
127 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 121 expectedStatus: HttpStatusCode.BAD_REQUEST_400
128 }) 122 })
129 123
130 await makePostBodyRequest({ 124 await makePostBodyRequest({
@@ -132,7 +126,7 @@ describe('Test user subscriptions API validators', function () {
132 path, 126 path,
133 token: server.accessToken, 127 token: server.accessToken,
134 fields: { uri: 'root@' }, 128 fields: { uri: 'root@' },
135 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 129 expectedStatus: HttpStatusCode.BAD_REQUEST_400
136 }) 130 })
137 131
138 await makePostBodyRequest({ 132 await makePostBodyRequest({
@@ -140,7 +134,7 @@ describe('Test user subscriptions API validators', function () {
140 path, 134 path,
141 token: server.accessToken, 135 token: server.accessToken,
142 fields: { uri: 'root@hello@' }, 136 fields: { uri: 'root@hello@' },
143 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 137 expectedStatus: HttpStatusCode.BAD_REQUEST_400
144 }) 138 })
145 }) 139 })
146 140
@@ -152,7 +146,7 @@ describe('Test user subscriptions API validators', function () {
152 path, 146 path,
153 token: server.accessToken, 147 token: server.accessToken,
154 fields: { uri: 'user1_channel@localhost:' + server.port }, 148 fields: { uri: 'user1_channel@localhost:' + server.port },
155 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 149 expectedStatus: HttpStatusCode.NO_CONTENT_204
156 }) 150 })
157 151
158 await waitJobs([ server ]) 152 await waitJobs([ server ])
@@ -164,7 +158,7 @@ describe('Test user subscriptions API validators', function () {
164 await makeGetRequest({ 158 await makeGetRequest({
165 url: server.url, 159 url: server.url,
166 path: path + '/user1_channel@localhost:' + server.port, 160 path: path + '/user1_channel@localhost:' + server.port,
167 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 161 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
168 }) 162 })
169 }) 163 })
170 164
@@ -173,21 +167,21 @@ describe('Test user subscriptions API validators', function () {
173 url: server.url, 167 url: server.url,
174 path: path + '/root', 168 path: path + '/root',
175 token: server.accessToken, 169 token: server.accessToken,
176 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 170 expectedStatus: HttpStatusCode.BAD_REQUEST_400
177 }) 171 })
178 172
179 await makeGetRequest({ 173 await makeGetRequest({
180 url: server.url, 174 url: server.url,
181 path: path + '/root@', 175 path: path + '/root@',
182 token: server.accessToken, 176 token: server.accessToken,
183 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 177 expectedStatus: HttpStatusCode.BAD_REQUEST_400
184 }) 178 })
185 179
186 await makeGetRequest({ 180 await makeGetRequest({
187 url: server.url, 181 url: server.url,
188 path: path + '/root@hello@', 182 path: path + '/root@hello@',
189 token: server.accessToken, 183 token: server.accessToken,
190 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 184 expectedStatus: HttpStatusCode.BAD_REQUEST_400
191 }) 185 })
192 }) 186 })
193 187
@@ -196,7 +190,7 @@ describe('Test user subscriptions API validators', function () {
196 url: server.url, 190 url: server.url,
197 path: path + '/root1@localhost:' + server.port, 191 path: path + '/root1@localhost:' + server.port,
198 token: server.accessToken, 192 token: server.accessToken,
199 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 193 expectedStatus: HttpStatusCode.NOT_FOUND_404
200 }) 194 })
201 }) 195 })
202 196
@@ -205,7 +199,7 @@ describe('Test user subscriptions API validators', function () {
205 url: server.url, 199 url: server.url,
206 path: path + '/user1_channel@localhost:' + server.port, 200 path: path + '/user1_channel@localhost:' + server.port,
207 token: server.accessToken, 201 token: server.accessToken,
208 statusCodeExpected: HttpStatusCode.OK_200 202 expectedStatus: HttpStatusCode.OK_200
209 }) 203 })
210 }) 204 })
211 }) 205 })
@@ -217,7 +211,7 @@ describe('Test user subscriptions API validators', function () {
217 await makeGetRequest({ 211 await makeGetRequest({
218 url: server.url, 212 url: server.url,
219 path: existPath, 213 path: existPath,
220 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 214 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
221 }) 215 })
222 }) 216 })
223 217
@@ -227,7 +221,7 @@ describe('Test user subscriptions API validators', function () {
227 path: existPath, 221 path: existPath,
228 query: { uris: 'toto' }, 222 query: { uris: 'toto' },
229 token: server.accessToken, 223 token: server.accessToken,
230 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 224 expectedStatus: HttpStatusCode.BAD_REQUEST_400
231 }) 225 })
232 226
233 await makeGetRequest({ 227 await makeGetRequest({
@@ -235,7 +229,7 @@ describe('Test user subscriptions API validators', function () {
235 path: existPath, 229 path: existPath,
236 query: { 'uris[]': 1 }, 230 query: { 'uris[]': 1 },
237 token: server.accessToken, 231 token: server.accessToken,
238 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 232 expectedStatus: HttpStatusCode.BAD_REQUEST_400
239 }) 233 })
240 }) 234 })
241 235
@@ -245,7 +239,7 @@ describe('Test user subscriptions API validators', function () {
245 path: existPath, 239 path: existPath,
246 query: { 'uris[]': 'coucou@localhost:' + server.port }, 240 query: { 'uris[]': 'coucou@localhost:' + server.port },
247 token: server.accessToken, 241 token: server.accessToken,
248 statusCodeExpected: HttpStatusCode.OK_200 242 expectedStatus: HttpStatusCode.OK_200
249 }) 243 })
250 }) 244 })
251 }) 245 })
@@ -255,7 +249,7 @@ describe('Test user subscriptions API validators', function () {
255 await makeDeleteRequest({ 249 await makeDeleteRequest({
256 url: server.url, 250 url: server.url,
257 path: path + '/user1_channel@localhost:' + server.port, 251 path: path + '/user1_channel@localhost:' + server.port,
258 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 252 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
259 }) 253 })
260 }) 254 })
261 255
@@ -264,21 +258,21 @@ describe('Test user subscriptions API validators', function () {
264 url: server.url, 258 url: server.url,
265 path: path + '/root', 259 path: path + '/root',
266 token: server.accessToken, 260 token: server.accessToken,
267 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 261 expectedStatus: HttpStatusCode.BAD_REQUEST_400
268 }) 262 })
269 263
270 await makeDeleteRequest({ 264 await makeDeleteRequest({
271 url: server.url, 265 url: server.url,
272 path: path + '/root@', 266 path: path + '/root@',
273 token: server.accessToken, 267 token: server.accessToken,
274 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 268 expectedStatus: HttpStatusCode.BAD_REQUEST_400
275 }) 269 })
276 270
277 await makeDeleteRequest({ 271 await makeDeleteRequest({
278 url: server.url, 272 url: server.url,
279 path: path + '/root@hello@', 273 path: path + '/root@hello@',
280 token: server.accessToken, 274 token: server.accessToken,
281 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 275 expectedStatus: HttpStatusCode.BAD_REQUEST_400
282 }) 276 })
283 }) 277 })
284 278
@@ -287,7 +281,7 @@ describe('Test user subscriptions API validators', function () {
287 url: server.url, 281 url: server.url,
288 path: path + '/root1@localhost:' + server.port, 282 path: path + '/root1@localhost:' + server.port,
289 token: server.accessToken, 283 token: server.accessToken,
290 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 284 expectedStatus: HttpStatusCode.NOT_FOUND_404
291 }) 285 })
292 }) 286 })
293 287
@@ -296,7 +290,7 @@ describe('Test user subscriptions API validators', function () {
296 url: server.url, 290 url: server.url,
297 path: path + '/user1_channel@localhost:' + server.port, 291 path: path + '/user1_channel@localhost:' + server.port,
298 token: server.accessToken, 292 token: server.accessToken,
299 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 293 expectedStatus: HttpStatusCode.NO_CONTENT_204
300 }) 294 })
301 }) 295 })
302 }) 296 })
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 70a872ce5..9d8f933db 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -2,43 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { User, UserRole, VideoCreateResult } from '../../../../shared'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
8 addVideoChannel,
9 blockUser,
10 buildAbsoluteFixturePath, 6 buildAbsoluteFixturePath,
7 checkBadCountPagination,
8 checkBadSortPagination,
9 checkBadStartPagination,
11 cleanupTests, 10 cleanupTests,
12 createUser, 11 createSingleServer,
13 deleteMe,
14 flushAndRunServer,
15 getMyUserInformation,
16 getMyUserVideoRating,
17 getUserScopedTokens,
18 getUsersList,
19 immutableAssign,
20 killallServers, 12 killallServers,
21 makeGetRequest, 13 makeGetRequest,
22 makePostBodyRequest, 14 makePostBodyRequest,
23 makePutBodyRequest, 15 makePutBodyRequest,
24 makeUploadRequest, 16 makeUploadRequest,
25 registerUser, 17 MockSmtpServer,
26 removeUser, 18 PeerTubeServer,
27 renewUserScopedTokens,
28 reRunServer,
29 ServerInfo,
30 setAccessTokensToServers, 19 setAccessTokensToServers,
31 unblockUser, 20 UsersCommand
32 uploadVideo, 21} from '@shared/extra-utils'
33 userLogin 22import { HttpStatusCode, UserAdminFlag, UserRole, VideoCreateResult } from '@shared/models'
34} from '../../../../shared/extra-utils'
35import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
36import {
37 checkBadCountPagination,
38 checkBadSortPagination,
39 checkBadStartPagination
40} from '../../../../shared/extra-utils/requests/check-api-params'
41import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
42 23
43describe('Test users API validators', function () { 24describe('Test users API validators', function () {
44 const path = '/api/v1/users/' 25 const path = '/api/v1/users/'
@@ -46,10 +27,10 @@ describe('Test users API validators', function () {
46 let rootId: number 27 let rootId: number
47 let moderatorId: number 28 let moderatorId: number
48 let video: VideoCreateResult 29 let video: VideoCreateResult
49 let server: ServerInfo 30 let server: PeerTubeServer
50 let serverWithRegistrationDisabled: ServerInfo 31 let serverWithRegistrationDisabled: PeerTubeServer
51 let userAccessToken = '' 32 let userToken = ''
52 let moderatorAccessToken = '' 33 let moderatorToken = ''
53 let emailPort: number 34 let emailPort: number
54 let overrideConfig: Object 35 let overrideConfig: Object
55 36
@@ -65,8 +46,8 @@ describe('Test users API validators', function () {
65 46
66 { 47 {
67 const res = await Promise.all([ 48 const res = await Promise.all([
68 flushAndRunServer(1, overrideConfig), 49 createSingleServer(1, overrideConfig),
69 flushAndRunServer(2) 50 createSingleServer(2)
70 ]) 51 ])
71 52
72 server = res[0] 53 server = res[0]
@@ -76,66 +57,31 @@ describe('Test users API validators', function () {
76 } 57 }
77 58
78 { 59 {
79 const user = { 60 const user = { username: 'user1' }
80 username: 'user1', 61 await server.users.create({ ...user })
81 password: 'my super password' 62 userToken = await server.login.getAccessToken(user)
82 }
83
84 const videoQuota = 42000000
85 await createUser({
86 url: server.url,
87 accessToken: server.accessToken,
88 username: user.username,
89 password: user.password,
90 videoQuota: videoQuota
91 })
92 userAccessToken = await userLogin(server, user)
93 } 63 }
94 64
95 { 65 {
96 const moderator = { 66 const moderator = { username: 'moderator1' }
97 username: 'moderator1', 67 await server.users.create({ ...moderator, role: UserRole.MODERATOR })
98 password: 'super password' 68 moderatorToken = await server.login.getAccessToken(moderator)
99 }
100
101 await createUser({
102 url: server.url,
103 accessToken: server.accessToken,
104 username: moderator.username,
105 password: moderator.password,
106 role: UserRole.MODERATOR
107 })
108
109 moderatorAccessToken = await userLogin(server, moderator)
110 } 69 }
111 70
112 { 71 {
113 const moderator = { 72 const moderator = { username: 'moderator2' }
114 username: 'moderator2', 73 await server.users.create({ ...moderator, role: UserRole.MODERATOR })
115 password: 'super password'
116 }
117
118 await createUser({
119 url: server.url,
120 accessToken: server.accessToken,
121 username: moderator.username,
122 password: moderator.password,
123 role: UserRole.MODERATOR
124 })
125 } 74 }
126 75
127 { 76 {
128 const res = await uploadVideo(server.url, server.accessToken, {}) 77 video = await server.videos.upload()
129 video = res.body.video
130 } 78 }
131 79
132 { 80 {
133 const res = await getUsersList(server.url, server.accessToken) 81 const { data } = await server.users.list()
134 const users: User[] = res.body.data 82 userId = data.find(u => u.username === 'user1').id
135 83 rootId = data.find(u => u.username === 'root').id
136 userId = users.find(u => u.username === 'user1').id 84 moderatorId = data.find(u => u.username === 'moderator2').id
137 rootId = users.find(u => u.username === 'root').id
138 moderatorId = users.find(u => u.username === 'moderator2').id
139 } 85 }
140 }) 86 })
141 87
@@ -156,7 +102,7 @@ describe('Test users API validators', function () {
156 await makeGetRequest({ 102 await makeGetRequest({
157 url: server.url, 103 url: server.url,
158 path, 104 path,
159 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 105 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
160 }) 106 })
161 }) 107 })
162 108
@@ -164,8 +110,8 @@ describe('Test users API validators', function () {
164 await makeGetRequest({ 110 await makeGetRequest({
165 url: server.url, 111 url: server.url,
166 path, 112 path,
167 token: userAccessToken, 113 token: userToken,
168 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 114 expectedStatus: HttpStatusCode.FORBIDDEN_403
169 }) 115 })
170 }) 116 })
171 }) 117 })
@@ -182,25 +128,25 @@ describe('Test users API validators', function () {
182 } 128 }
183 129
184 it('Should fail with a too small username', async function () { 130 it('Should fail with a too small username', async function () {
185 const fields = immutableAssign(baseCorrectParams, { username: '' }) 131 const fields = { ...baseCorrectParams, username: '' }
186 132
187 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 133 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
188 }) 134 })
189 135
190 it('Should fail with a too long username', async function () { 136 it('Should fail with a too long username', async function () {
191 const fields = immutableAssign(baseCorrectParams, { username: 'super'.repeat(50) }) 137 const fields = { ...baseCorrectParams, username: 'super'.repeat(50) }
192 138
193 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 139 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
194 }) 140 })
195 141
196 it('Should fail with a not lowercase username', async function () { 142 it('Should fail with a not lowercase username', async function () {
197 const fields = immutableAssign(baseCorrectParams, { username: 'Toto' }) 143 const fields = { ...baseCorrectParams, username: 'Toto' }
198 144
199 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 145 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
200 }) 146 })
201 147
202 it('Should fail with an incorrect username', async function () { 148 it('Should fail with an incorrect username', async function () {
203 const fields = immutableAssign(baseCorrectParams, { username: 'my username' }) 149 const fields = { ...baseCorrectParams, username: 'my username' }
204 150
205 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 151 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
206 }) 152 })
@@ -212,25 +158,25 @@ describe('Test users API validators', function () {
212 }) 158 })
213 159
214 it('Should fail with an invalid email', async function () { 160 it('Should fail with an invalid email', async function () {
215 const fields = immutableAssign(baseCorrectParams, { email: 'test_example.com' }) 161 const fields = { ...baseCorrectParams, email: 'test_example.com' }
216 162
217 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 163 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
218 }) 164 })
219 165
220 it('Should fail with a too small password', async function () { 166 it('Should fail with a too small password', async function () {
221 const fields = immutableAssign(baseCorrectParams, { password: 'bla' }) 167 const fields = { ...baseCorrectParams, password: 'bla' }
222 168
223 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 169 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
224 }) 170 })
225 171
226 it('Should fail with a too long password', async function () { 172 it('Should fail with a too long password', async function () {
227 const fields = immutableAssign(baseCorrectParams, { password: 'super'.repeat(61) }) 173 const fields = { ...baseCorrectParams, password: 'super'.repeat(61) }
228 174
229 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 175 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
230 }) 176 })
231 177
232 it('Should fail with empty password and no smtp configured', async function () { 178 it('Should fail with empty password and no smtp configured', async function () {
233 const fields = immutableAssign(baseCorrectParams, { password: '' }) 179 const fields = { ...baseCorrectParams, password: '' }
234 180
235 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 181 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
236 }) 182 })
@@ -238,33 +184,37 @@ describe('Test users API validators', function () {
238 it('Should succeed with no password on a server with smtp enabled', async function () { 184 it('Should succeed with no password on a server with smtp enabled', async function () {
239 this.timeout(20000) 185 this.timeout(20000)
240 186
241 killallServers([ server ]) 187 await killallServers([ server ])
188
189 const config = {
190 ...overrideConfig,
242 191
243 const config = immutableAssign(overrideConfig, {
244 smtp: { 192 smtp: {
245 hostname: 'localhost', 193 hostname: 'localhost',
246 port: emailPort 194 port: emailPort
247 } 195 }
248 }) 196 }
249 await reRunServer(server, config) 197 await server.run(config)
198
199 const fields = {
200 ...baseCorrectParams,
250 201
251 const fields = immutableAssign(baseCorrectParams, {
252 password: '', 202 password: '',
253 username: 'create_password', 203 username: 'create_password',
254 email: 'create_password@example.com' 204 email: 'create_password@example.com'
255 }) 205 }
256 206
257 await makePostBodyRequest({ 207 await makePostBodyRequest({
258 url: server.url, 208 url: server.url,
259 path: path, 209 path: path,
260 token: server.accessToken, 210 token: server.accessToken,
261 fields, 211 fields,
262 statusCodeExpected: HttpStatusCode.OK_200 212 expectedStatus: HttpStatusCode.OK_200
263 }) 213 })
264 }) 214 })
265 215
266 it('Should fail with invalid admin flags', async function () { 216 it('Should fail with invalid admin flags', async function () {
267 const fields = immutableAssign(baseCorrectParams, { adminFlags: 'toto' }) 217 const fields = { ...baseCorrectParams, adminFlags: 'toto' }
268 218
269 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 219 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
270 }) 220 })
@@ -275,31 +225,31 @@ describe('Test users API validators', function () {
275 path, 225 path,
276 token: 'super token', 226 token: 'super token',
277 fields: baseCorrectParams, 227 fields: baseCorrectParams,
278 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 228 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
279 }) 229 })
280 }) 230 })
281 231
282 it('Should fail if we add a user with the same username', async function () { 232 it('Should fail if we add a user with the same username', async function () {
283 const fields = immutableAssign(baseCorrectParams, { username: 'user1' }) 233 const fields = { ...baseCorrectParams, username: 'user1' }
284 234
285 await makePostBodyRequest({ 235 await makePostBodyRequest({
286 url: server.url, 236 url: server.url,
287 path, 237 path,
288 token: server.accessToken, 238 token: server.accessToken,
289 fields, 239 fields,
290 statusCodeExpected: HttpStatusCode.CONFLICT_409 240 expectedStatus: HttpStatusCode.CONFLICT_409
291 }) 241 })
292 }) 242 })
293 243
294 it('Should fail if we add a user with the same email', async function () { 244 it('Should fail if we add a user with the same email', async function () {
295 const fields = immutableAssign(baseCorrectParams, { email: 'user1@example.com' }) 245 const fields = { ...baseCorrectParams, email: 'user1@example.com' }
296 246
297 await makePostBodyRequest({ 247 await makePostBodyRequest({
298 url: server.url, 248 url: server.url,
299 path, 249 path,
300 token: server.accessToken, 250 token: server.accessToken,
301 fields, 251 fields,
302 statusCodeExpected: HttpStatusCode.CONFLICT_409 252 expectedStatus: HttpStatusCode.CONFLICT_409
303 }) 253 })
304 }) 254 })
305 255
@@ -316,13 +266,13 @@ describe('Test users API validators', function () {
316 }) 266 })
317 267
318 it('Should fail with an invalid videoQuota', async function () { 268 it('Should fail with an invalid videoQuota', async function () {
319 const fields = immutableAssign(baseCorrectParams, { videoQuota: -5 }) 269 const fields = { ...baseCorrectParams, videoQuota: -5 }
320 270
321 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 271 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
322 }) 272 })
323 273
324 it('Should fail with an invalid videoQuotaDaily', async function () { 274 it('Should fail with an invalid videoQuotaDaily', async function () {
325 const fields = immutableAssign(baseCorrectParams, { videoQuotaDaily: -7 }) 275 const fields = { ...baseCorrectParams, videoQuotaDaily: -7 }
326 276
327 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 277 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
328 }) 278 })
@@ -334,46 +284,46 @@ describe('Test users API validators', function () {
334 }) 284 })
335 285
336 it('Should fail with an invalid user role', async function () { 286 it('Should fail with an invalid user role', async function () {
337 const fields = immutableAssign(baseCorrectParams, { role: 88989 }) 287 const fields = { ...baseCorrectParams, role: 88989 }
338 288
339 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 289 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
340 }) 290 })
341 291
342 it('Should fail with a "peertube" username', async function () { 292 it('Should fail with a "peertube" username', async function () {
343 const fields = immutableAssign(baseCorrectParams, { username: 'peertube' }) 293 const fields = { ...baseCorrectParams, username: 'peertube' }
344 294
345 await makePostBodyRequest({ 295 await makePostBodyRequest({
346 url: server.url, 296 url: server.url,
347 path, 297 path,
348 token: server.accessToken, 298 token: server.accessToken,
349 fields, 299 fields,
350 statusCodeExpected: HttpStatusCode.CONFLICT_409 300 expectedStatus: HttpStatusCode.CONFLICT_409
351 }) 301 })
352 }) 302 })
353 303
354 it('Should fail to create a moderator or an admin with a moderator', async function () { 304 it('Should fail to create a moderator or an admin with a moderator', async function () {
355 for (const role of [ UserRole.MODERATOR, UserRole.ADMINISTRATOR ]) { 305 for (const role of [ UserRole.MODERATOR, UserRole.ADMINISTRATOR ]) {
356 const fields = immutableAssign(baseCorrectParams, { role }) 306 const fields = { ...baseCorrectParams, role }
357 307
358 await makePostBodyRequest({ 308 await makePostBodyRequest({
359 url: server.url, 309 url: server.url,
360 path, 310 path,
361 token: moderatorAccessToken, 311 token: moderatorToken,
362 fields, 312 fields,
363 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 313 expectedStatus: HttpStatusCode.FORBIDDEN_403
364 }) 314 })
365 } 315 }
366 }) 316 })
367 317
368 it('Should succeed to create a user with a moderator', async function () { 318 it('Should succeed to create a user with a moderator', async function () {
369 const fields = immutableAssign(baseCorrectParams, { username: 'a4656', email: 'a4656@example.com', role: UserRole.USER }) 319 const fields = { ...baseCorrectParams, username: 'a4656', email: 'a4656@example.com', role: UserRole.USER }
370 320
371 await makePostBodyRequest({ 321 await makePostBodyRequest({
372 url: server.url, 322 url: server.url,
373 path, 323 path,
374 token: moderatorAccessToken, 324 token: moderatorToken,
375 fields, 325 fields,
376 statusCodeExpected: HttpStatusCode.OK_200 326 expectedStatus: HttpStatusCode.OK_200
377 }) 327 })
378 }) 328 })
379 329
@@ -383,16 +333,13 @@ describe('Test users API validators', function () {
383 path, 333 path,
384 token: server.accessToken, 334 token: server.accessToken,
385 fields: baseCorrectParams, 335 fields: baseCorrectParams,
386 statusCodeExpected: HttpStatusCode.OK_200 336 expectedStatus: HttpStatusCode.OK_200
387 }) 337 })
388 }) 338 })
389 339
390 it('Should fail with a non admin user', async function () { 340 it('Should fail with a non admin user', async function () {
391 const user = { 341 const user = { username: 'user1' }
392 username: 'user1', 342 userToken = await server.login.getAccessToken(user)
393 password: 'my super password'
394 }
395 userAccessToken = await userLogin(server, user)
396 343
397 const fields = { 344 const fields = {
398 username: 'user3', 345 username: 'user3',
@@ -400,11 +347,12 @@ describe('Test users API validators', function () {
400 password: 'my super password', 347 password: 'my super password',
401 videoQuota: 42000000 348 videoQuota: 42000000
402 } 349 }
403 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 350 await makePostBodyRequest({ url: server.url, path, token: userToken, fields, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
404 }) 351 })
405 }) 352 })
406 353
407 describe('When updating my account', function () { 354 describe('When updating my account', function () {
355
408 it('Should fail with an invalid email attribute', async function () { 356 it('Should fail with an invalid email attribute', async function () {
409 const fields = { 357 const fields = {
410 email: 'blabla' 358 email: 'blabla'
@@ -415,29 +363,29 @@ describe('Test users API validators', function () {
415 363
416 it('Should fail with a too small password', async function () { 364 it('Should fail with a too small password', async function () {
417 const fields = { 365 const fields = {
418 currentPassword: 'my super password', 366 currentPassword: 'password',
419 password: 'bla' 367 password: 'bla'
420 } 368 }
421 369
422 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 370 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
423 }) 371 })
424 372
425 it('Should fail with a too long password', async function () { 373 it('Should fail with a too long password', async function () {
426 const fields = { 374 const fields = {
427 currentPassword: 'my super password', 375 currentPassword: 'password',
428 password: 'super'.repeat(61) 376 password: 'super'.repeat(61)
429 } 377 }
430 378
431 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 379 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
432 }) 380 })
433 381
434 it('Should fail without the current password', async function () { 382 it('Should fail without the current password', async function () {
435 const fields = { 383 const fields = {
436 currentPassword: 'my super password', 384 currentPassword: 'password',
437 password: 'super'.repeat(61) 385 password: 'super'.repeat(61)
438 } 386 }
439 387
440 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 388 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
441 }) 389 })
442 390
443 it('Should fail with an invalid current password', async function () { 391 it('Should fail with an invalid current password', async function () {
@@ -449,9 +397,9 @@ describe('Test users API validators', function () {
449 await makePutBodyRequest({ 397 await makePutBodyRequest({
450 url: server.url, 398 url: server.url,
451 path: path + 'me', 399 path: path + 'me',
452 token: userAccessToken, 400 token: userToken,
453 fields, 401 fields,
454 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 402 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
455 }) 403 })
456 }) 404 })
457 405
@@ -460,7 +408,7 @@ describe('Test users API validators', function () {
460 nsfwPolicy: 'hello' 408 nsfwPolicy: 'hello'
461 } 409 }
462 410
463 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 411 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
464 }) 412 })
465 413
466 it('Should fail with an invalid autoPlayVideo attribute', async function () { 414 it('Should fail with an invalid autoPlayVideo attribute', async function () {
@@ -468,7 +416,7 @@ describe('Test users API validators', function () {
468 autoPlayVideo: -1 416 autoPlayVideo: -1
469 } 417 }
470 418
471 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 419 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
472 }) 420 })
473 421
474 it('Should fail with an invalid autoPlayNextVideo attribute', async function () { 422 it('Should fail with an invalid autoPlayNextVideo attribute', async function () {
@@ -476,7 +424,7 @@ describe('Test users API validators', function () {
476 autoPlayNextVideo: -1 424 autoPlayNextVideo: -1
477 } 425 }
478 426
479 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 427 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
480 }) 428 })
481 429
482 it('Should fail with an invalid videosHistoryEnabled attribute', async function () { 430 it('Should fail with an invalid videosHistoryEnabled attribute', async function () {
@@ -484,12 +432,12 @@ describe('Test users API validators', function () {
484 videosHistoryEnabled: -1 432 videosHistoryEnabled: -1
485 } 433 }
486 434
487 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 435 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
488 }) 436 })
489 437
490 it('Should fail with an non authenticated user', async function () { 438 it('Should fail with an non authenticated user', async function () {
491 const fields = { 439 const fields = {
492 currentPassword: 'my super password', 440 currentPassword: 'password',
493 password: 'my super password' 441 password: 'my super password'
494 } 442 }
495 443
@@ -498,7 +446,7 @@ describe('Test users API validators', function () {
498 path: path + 'me', 446 path: path + 'me',
499 token: 'super token', 447 token: 'super token',
500 fields, 448 fields,
501 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 449 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
502 }) 450 })
503 }) 451 })
504 452
@@ -507,7 +455,7 @@ describe('Test users API validators', function () {
507 description: 'super'.repeat(201) 455 description: 'super'.repeat(201)
508 } 456 }
509 457
510 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 458 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
511 }) 459 })
512 460
513 it('Should fail with an invalid videoLanguages attribute', async function () { 461 it('Should fail with an invalid videoLanguages attribute', async function () {
@@ -516,7 +464,7 @@ describe('Test users API validators', function () {
516 videoLanguages: 'toto' 464 videoLanguages: 'toto'
517 } 465 }
518 466
519 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 467 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
520 } 468 }
521 469
522 { 470 {
@@ -529,18 +477,18 @@ describe('Test users API validators', function () {
529 videoLanguages: languages 477 videoLanguages: languages
530 } 478 }
531 479
532 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 480 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
533 } 481 }
534 }) 482 })
535 483
536 it('Should fail with an invalid theme', async function () { 484 it('Should fail with an invalid theme', async function () {
537 const fields = { theme: 'invalid' } 485 const fields = { theme: 'invalid' }
538 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 486 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
539 }) 487 })
540 488
541 it('Should fail with an unknown theme', async function () { 489 it('Should fail with an unknown theme', async function () {
542 const fields = { theme: 'peertube-theme-unknown' } 490 const fields = { theme: 'peertube-theme-unknown' }
543 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 491 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
544 }) 492 })
545 493
546 it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () { 494 it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () {
@@ -548,7 +496,7 @@ describe('Test users API validators', function () {
548 noInstanceConfigWarningModal: -1 496 noInstanceConfigWarningModal: -1
549 } 497 }
550 498
551 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 499 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
552 }) 500 })
553 501
554 it('Should fail with an invalid noWelcomeModal attribute', async function () { 502 it('Should fail with an invalid noWelcomeModal attribute', async function () {
@@ -556,12 +504,12 @@ describe('Test users API validators', function () {
556 noWelcomeModal: -1 504 noWelcomeModal: -1
557 } 505 }
558 506
559 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) 507 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userToken, fields })
560 }) 508 })
561 509
562 it('Should succeed to change password with the correct params', async function () { 510 it('Should succeed to change password with the correct params', async function () {
563 const fields = { 511 const fields = {
564 currentPassword: 'my super password', 512 currentPassword: 'password',
565 password: 'my super password', 513 password: 'my super password',
566 nsfwPolicy: 'blur', 514 nsfwPolicy: 'blur',
567 autoPlayVideo: false, 515 autoPlayVideo: false,
@@ -574,9 +522,9 @@ describe('Test users API validators', function () {
574 await makePutBodyRequest({ 522 await makePutBodyRequest({
575 url: server.url, 523 url: server.url,
576 path: path + 'me', 524 path: path + 'me',
577 token: userAccessToken, 525 token: userToken,
578 fields, 526 fields,
579 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 527 expectedStatus: HttpStatusCode.NO_CONTENT_204
580 }) 528 })
581 }) 529 })
582 530
@@ -589,9 +537,9 @@ describe('Test users API validators', function () {
589 await makePutBodyRequest({ 537 await makePutBodyRequest({
590 url: server.url, 538 url: server.url,
591 path: path + 'me', 539 path: path + 'me',
592 token: userAccessToken, 540 token: userToken,
593 fields, 541 fields,
594 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 542 expectedStatus: HttpStatusCode.NO_CONTENT_204
595 }) 543 })
596 }) 544 })
597 }) 545 })
@@ -623,7 +571,7 @@ describe('Test users API validators', function () {
623 path: path + '/me/avatar/pick', 571 path: path + '/me/avatar/pick',
624 fields, 572 fields,
625 attaches, 573 attaches,
626 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 574 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
627 }) 575 })
628 }) 576 })
629 577
@@ -638,7 +586,7 @@ describe('Test users API validators', function () {
638 token: server.accessToken, 586 token: server.accessToken,
639 fields, 587 fields,
640 attaches, 588 attaches,
641 statusCodeExpected: HttpStatusCode.OK_200 589 expectedStatus: HttpStatusCode.OK_200
642 }) 590 })
643 }) 591 })
644 }) 592 })
@@ -646,28 +594,28 @@ describe('Test users API validators', function () {
646 describe('When managing my scoped tokens', function () { 594 describe('When managing my scoped tokens', function () {
647 595
648 it('Should fail to get my scoped tokens with an non authenticated user', async function () { 596 it('Should fail to get my scoped tokens with an non authenticated user', async function () {
649 await getUserScopedTokens(server.url, null, HttpStatusCode.UNAUTHORIZED_401) 597 await server.users.getMyScopedTokens({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
650 }) 598 })
651 599
652 it('Should fail to get my scoped tokens with a bad token', async function () { 600 it('Should fail to get my scoped tokens with a bad token', async function () {
653 await getUserScopedTokens(server.url, 'bad', HttpStatusCode.UNAUTHORIZED_401) 601 await server.users.getMyScopedTokens({ token: 'bad', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
654 602
655 }) 603 })
656 604
657 it('Should succeed to get my scoped tokens', async function () { 605 it('Should succeed to get my scoped tokens', async function () {
658 await getUserScopedTokens(server.url, server.accessToken) 606 await server.users.getMyScopedTokens()
659 }) 607 })
660 608
661 it('Should fail to renew my scoped tokens with an non authenticated user', async function () { 609 it('Should fail to renew my scoped tokens with an non authenticated user', async function () {
662 await renewUserScopedTokens(server.url, null, HttpStatusCode.UNAUTHORIZED_401) 610 await server.users.renewMyScopedTokens({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
663 }) 611 })
664 612
665 it('Should fail to renew my scoped tokens with a bad token', async function () { 613 it('Should fail to renew my scoped tokens with a bad token', async function () {
666 await renewUserScopedTokens(server.url, 'bad', HttpStatusCode.UNAUTHORIZED_401) 614 await server.users.renewMyScopedTokens({ token: 'bad', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
667 }) 615 })
668 616
669 it('Should succeed to renew my scoped tokens', async function () { 617 it('Should succeed to renew my scoped tokens', async function () {
670 await renewUserScopedTokens(server.url, server.accessToken) 618 await server.users.renewMyScopedTokens()
671 }) 619 })
672 }) 620 })
673 621
@@ -678,16 +626,16 @@ describe('Test users API validators', function () {
678 url: server.url, 626 url: server.url,
679 path: path + userId, 627 path: path + userId,
680 token: 'super token', 628 token: 'super token',
681 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 629 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
682 }) 630 })
683 }) 631 })
684 632
685 it('Should fail with a non admin user', async function () { 633 it('Should fail with a non admin user', async function () {
686 await makeGetRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 634 await makeGetRequest({ url: server.url, path, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
687 }) 635 })
688 636
689 it('Should succeed with the correct params', async function () { 637 it('Should succeed with the correct params', async function () {
690 await makeGetRequest({ url: server.url, path: path + userId, token: server.accessToken, statusCodeExpected: HttpStatusCode.OK_200 }) 638 await makeGetRequest({ url: server.url, path: path + userId, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
691 }) 639 })
692 }) 640 })
693 641
@@ -727,7 +675,7 @@ describe('Test users API validators', function () {
727 675
728 it('Should fail with a too small password', async function () { 676 it('Should fail with a too small password', async function () {
729 const fields = { 677 const fields = {
730 currentPassword: 'my super password', 678 currentPassword: 'password',
731 password: 'bla' 679 password: 'bla'
732 } 680 }
733 681
@@ -736,7 +684,7 @@ describe('Test users API validators', function () {
736 684
737 it('Should fail with a too long password', async function () { 685 it('Should fail with a too long password', async function () {
738 const fields = { 686 const fields = {
739 currentPassword: 'my super password', 687 currentPassword: 'password',
740 password: 'super'.repeat(61) 688 password: 'super'.repeat(61)
741 } 689 }
742 690
@@ -753,7 +701,7 @@ describe('Test users API validators', function () {
753 path: path + userId, 701 path: path + userId,
754 token: 'super token', 702 token: 'super token',
755 fields, 703 fields,
756 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 704 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
757 }) 705 })
758 }) 706 })
759 707
@@ -779,9 +727,9 @@ describe('Test users API validators', function () {
779 await makePutBodyRequest({ 727 await makePutBodyRequest({
780 url: server.url, 728 url: server.url,
781 path: path + moderatorId, 729 path: path + moderatorId,
782 token: moderatorAccessToken, 730 token: moderatorToken,
783 fields, 731 fields,
784 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 732 expectedStatus: HttpStatusCode.FORBIDDEN_403
785 }) 733 })
786 }) 734 })
787 735
@@ -793,9 +741,9 @@ describe('Test users API validators', function () {
793 await makePutBodyRequest({ 741 await makePutBodyRequest({
794 url: server.url, 742 url: server.url,
795 path: path + userId, 743 path: path + userId,
796 token: moderatorAccessToken, 744 token: moderatorToken,
797 fields, 745 fields,
798 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 746 expectedStatus: HttpStatusCode.NO_CONTENT_204
799 }) 747 })
800 }) 748 })
801 749
@@ -812,38 +760,44 @@ describe('Test users API validators', function () {
812 path: path + userId, 760 path: path + userId,
813 token: server.accessToken, 761 token: server.accessToken,
814 fields, 762 fields,
815 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 763 expectedStatus: HttpStatusCode.NO_CONTENT_204
816 }) 764 })
817 }) 765 })
818 }) 766 })
819 767
820 describe('When getting my information', function () { 768 describe('When getting my information', function () {
821 it('Should fail with a non authenticated user', async function () { 769 it('Should fail with a non authenticated user', async function () {
822 await getMyUserInformation(server.url, 'fake_token', HttpStatusCode.UNAUTHORIZED_401) 770 await server.users.getMyInfo({ token: 'fake_token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
823 }) 771 })
824 772
825 it('Should success with the correct parameters', async function () { 773 it('Should success with the correct parameters', async function () {
826 await getMyUserInformation(server.url, userAccessToken) 774 await server.users.getMyInfo({ token: userToken })
827 }) 775 })
828 }) 776 })
829 777
830 describe('When getting my video rating', function () { 778 describe('When getting my video rating', function () {
779 let command: UsersCommand
780
781 before(function () {
782 command = server.users
783 })
784
831 it('Should fail with a non authenticated user', async function () { 785 it('Should fail with a non authenticated user', async function () {
832 await getMyUserVideoRating(server.url, 'fake_token', video.id, HttpStatusCode.UNAUTHORIZED_401) 786 await command.getMyRating({ token: 'fake_token', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
833 }) 787 })
834 788
835 it('Should fail with an incorrect video uuid', async function () { 789 it('Should fail with an incorrect video uuid', async function () {
836 await getMyUserVideoRating(server.url, server.accessToken, 'blabla', HttpStatusCode.BAD_REQUEST_400) 790 await command.getMyRating({ videoId: 'blabla', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
837 }) 791 })
838 792
839 it('Should fail with an unknown video', async function () { 793 it('Should fail with an unknown video', async function () {
840 await getMyUserVideoRating(server.url, server.accessToken, '4da6fde3-88f7-4d16-b119-108df5630b06', HttpStatusCode.NOT_FOUND_404) 794 await command.getMyRating({ videoId: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
841 }) 795 })
842 796
843 it('Should succeed with the correct parameters', async function () { 797 it('Should succeed with the correct parameters', async function () {
844 await getMyUserVideoRating(server.url, server.accessToken, video.id) 798 await command.getMyRating({ videoId: video.id })
845 await getMyUserVideoRating(server.url, server.accessToken, video.uuid) 799 await command.getMyRating({ videoId: video.uuid })
846 await getMyUserVideoRating(server.url, server.accessToken, video.shortUUID) 800 await command.getMyRating({ videoId: video.shortUUID })
847 }) 801 })
848 }) 802 })
849 803
@@ -851,80 +805,93 @@ describe('Test users API validators', function () {
851 const path = '/api/v1/accounts/user1/ratings' 805 const path = '/api/v1/accounts/user1/ratings'
852 806
853 it('Should fail with a bad start pagination', async function () { 807 it('Should fail with a bad start pagination', async function () {
854 await checkBadStartPagination(server.url, path, userAccessToken) 808 await checkBadStartPagination(server.url, path, userToken)
855 }) 809 })
856 810
857 it('Should fail with a bad count pagination', async function () { 811 it('Should fail with a bad count pagination', async function () {
858 await checkBadCountPagination(server.url, path, userAccessToken) 812 await checkBadCountPagination(server.url, path, userToken)
859 }) 813 })
860 814
861 it('Should fail with an incorrect sort', async function () { 815 it('Should fail with an incorrect sort', async function () {
862 await checkBadSortPagination(server.url, path, userAccessToken) 816 await checkBadSortPagination(server.url, path, userToken)
863 }) 817 })
864 818
865 it('Should fail with a unauthenticated user', async function () { 819 it('Should fail with a unauthenticated user', async function () {
866 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 820 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
867 }) 821 })
868 822
869 it('Should fail with a another user', async function () { 823 it('Should fail with a another user', async function () {
870 await makeGetRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 824 await makeGetRequest({ url: server.url, path, token: server.accessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
871 }) 825 })
872 826
873 it('Should fail with a bad type', async function () { 827 it('Should fail with a bad type', async function () {
874 await makeGetRequest({ 828 await makeGetRequest({
875 url: server.url, 829 url: server.url,
876 path, 830 path,
877 token: userAccessToken, 831 token: userToken,
878 query: { rating: 'toto ' }, 832 query: { rating: 'toto ' },
879 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 833 expectedStatus: HttpStatusCode.BAD_REQUEST_400
880 }) 834 })
881 }) 835 })
882 836
883 it('Should succeed with the correct params', async function () { 837 it('Should succeed with the correct params', async function () {
884 await makeGetRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.OK_200 }) 838 await makeGetRequest({ url: server.url, path, token: userToken, expectedStatus: HttpStatusCode.OK_200 })
885 }) 839 })
886 }) 840 })
887 841
888 describe('When blocking/unblocking/removing user', function () { 842 describe('When blocking/unblocking/removing user', function () {
843
889 it('Should fail with an incorrect id', async function () { 844 it('Should fail with an incorrect id', async function () {
890 await removeUser(server.url, 'blabla', server.accessToken, HttpStatusCode.BAD_REQUEST_400) 845 const options = { userId: 'blabla' as any, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
891 await blockUser(server.url, 'blabla', server.accessToken, HttpStatusCode.BAD_REQUEST_400) 846
892 await unblockUser(server.url, 'blabla', server.accessToken, HttpStatusCode.BAD_REQUEST_400) 847 await server.users.remove(options)
848 await server.users.banUser({ userId: 'blabla' as any, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
849 await server.users.unbanUser({ userId: 'blabla' as any, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
893 }) 850 })
894 851
895 it('Should fail with the root user', async function () { 852 it('Should fail with the root user', async function () {
896 await removeUser(server.url, rootId, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 853 const options = { userId: rootId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
897 await blockUser(server.url, rootId, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 854
898 await unblockUser(server.url, rootId, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 855 await server.users.remove(options)
856 await server.users.banUser(options)
857 await server.users.unbanUser(options)
899 }) 858 })
900 859
901 it('Should return 404 with a non existing id', async function () { 860 it('Should return 404 with a non existing id', async function () {
902 await removeUser(server.url, 4545454, server.accessToken, HttpStatusCode.NOT_FOUND_404) 861 const options = { userId: 4545454, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
903 await blockUser(server.url, 4545454, server.accessToken, HttpStatusCode.NOT_FOUND_404) 862
904 await unblockUser(server.url, 4545454, server.accessToken, HttpStatusCode.NOT_FOUND_404) 863 await server.users.remove(options)
864 await server.users.banUser(options)
865 await server.users.unbanUser(options)
905 }) 866 })
906 867
907 it('Should fail with a non admin user', async function () { 868 it('Should fail with a non admin user', async function () {
908 await removeUser(server.url, userId, userAccessToken, HttpStatusCode.FORBIDDEN_403) 869 const options = { userId, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }
909 await blockUser(server.url, userId, userAccessToken, HttpStatusCode.FORBIDDEN_403) 870
910 await unblockUser(server.url, userId, userAccessToken, HttpStatusCode.FORBIDDEN_403) 871 await server.users.remove(options)
872 await server.users.banUser(options)
873 await server.users.unbanUser(options)
911 }) 874 })
912 875
913 it('Should fail on a moderator with a moderator', async function () { 876 it('Should fail on a moderator with a moderator', async function () {
914 await removeUser(server.url, moderatorId, moderatorAccessToken, HttpStatusCode.FORBIDDEN_403) 877 const options = { userId: moderatorId, token: moderatorToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }
915 await blockUser(server.url, moderatorId, moderatorAccessToken, HttpStatusCode.FORBIDDEN_403) 878
916 await unblockUser(server.url, moderatorId, moderatorAccessToken, HttpStatusCode.FORBIDDEN_403) 879 await server.users.remove(options)
880 await server.users.banUser(options)
881 await server.users.unbanUser(options)
917 }) 882 })
918 883
919 it('Should succeed on a user with a moderator', async function () { 884 it('Should succeed on a user with a moderator', async function () {
920 await blockUser(server.url, userId, moderatorAccessToken) 885 const options = { userId, token: moderatorToken }
921 await unblockUser(server.url, userId, moderatorAccessToken) 886
887 await server.users.banUser(options)
888 await server.users.unbanUser(options)
922 }) 889 })
923 }) 890 })
924 891
925 describe('When deleting our account', function () { 892 describe('When deleting our account', function () {
926 it('Should fail with with the root account', async function () { 893 it('Should fail with with the root account', async function () {
927 await deleteMe(server.url, server.accessToken, HttpStatusCode.BAD_REQUEST_400) 894 await server.users.deleteMe({ expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
928 }) 895 })
929 }) 896 })
930 897
@@ -938,19 +905,19 @@ describe('Test users API validators', function () {
938 } 905 }
939 906
940 it('Should fail with a too small username', async function () { 907 it('Should fail with a too small username', async function () {
941 const fields = immutableAssign(baseCorrectParams, { username: '' }) 908 const fields = { ...baseCorrectParams, username: '' }
942 909
943 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 910 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
944 }) 911 })
945 912
946 it('Should fail with a too long username', async function () { 913 it('Should fail with a too long username', async function () {
947 const fields = immutableAssign(baseCorrectParams, { username: 'super'.repeat(50) }) 914 const fields = { ...baseCorrectParams, username: 'super'.repeat(50) }
948 915
949 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 916 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
950 }) 917 })
951 918
952 it('Should fail with an incorrect username', async function () { 919 it('Should fail with an incorrect username', async function () {
953 const fields = immutableAssign(baseCorrectParams, { username: 'my username' }) 920 const fields = { ...baseCorrectParams, username: 'my username' }
954 921
955 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 922 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
956 }) 923 })
@@ -962,108 +929,108 @@ describe('Test users API validators', function () {
962 }) 929 })
963 930
964 it('Should fail with an invalid email', async function () { 931 it('Should fail with an invalid email', async function () {
965 const fields = immutableAssign(baseCorrectParams, { email: 'test_example.com' }) 932 const fields = { ...baseCorrectParams, email: 'test_example.com' }
966 933
967 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 934 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
968 }) 935 })
969 936
970 it('Should fail with a too small password', async function () { 937 it('Should fail with a too small password', async function () {
971 const fields = immutableAssign(baseCorrectParams, { password: 'bla' }) 938 const fields = { ...baseCorrectParams, password: 'bla' }
972 939
973 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 940 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
974 }) 941 })
975 942
976 it('Should fail with a too long password', async function () { 943 it('Should fail with a too long password', async function () {
977 const fields = immutableAssign(baseCorrectParams, { password: 'super'.repeat(61) }) 944 const fields = { ...baseCorrectParams, password: 'super'.repeat(61) }
978 945
979 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 946 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
980 }) 947 })
981 948
982 it('Should fail if we register a user with the same username', async function () { 949 it('Should fail if we register a user with the same username', async function () {
983 const fields = immutableAssign(baseCorrectParams, { username: 'root' }) 950 const fields = { ...baseCorrectParams, username: 'root' }
984 951
985 await makePostBodyRequest({ 952 await makePostBodyRequest({
986 url: server.url, 953 url: server.url,
987 path: registrationPath, 954 path: registrationPath,
988 token: server.accessToken, 955 token: server.accessToken,
989 fields, 956 fields,
990 statusCodeExpected: HttpStatusCode.CONFLICT_409 957 expectedStatus: HttpStatusCode.CONFLICT_409
991 }) 958 })
992 }) 959 })
993 960
994 it('Should fail with a "peertube" username', async function () { 961 it('Should fail with a "peertube" username', async function () {
995 const fields = immutableAssign(baseCorrectParams, { username: 'peertube' }) 962 const fields = { ...baseCorrectParams, username: 'peertube' }
996 963
997 await makePostBodyRequest({ 964 await makePostBodyRequest({
998 url: server.url, 965 url: server.url,
999 path: registrationPath, 966 path: registrationPath,
1000 token: server.accessToken, 967 token: server.accessToken,
1001 fields, 968 fields,
1002 statusCodeExpected: HttpStatusCode.CONFLICT_409 969 expectedStatus: HttpStatusCode.CONFLICT_409
1003 }) 970 })
1004 }) 971 })
1005 972
1006 it('Should fail if we register a user with the same email', async function () { 973 it('Should fail if we register a user with the same email', async function () {
1007 const fields = immutableAssign(baseCorrectParams, { email: 'admin' + server.internalServerNumber + '@example.com' }) 974 const fields = { ...baseCorrectParams, email: 'admin' + server.internalServerNumber + '@example.com' }
1008 975
1009 await makePostBodyRequest({ 976 await makePostBodyRequest({
1010 url: server.url, 977 url: server.url,
1011 path: registrationPath, 978 path: registrationPath,
1012 token: server.accessToken, 979 token: server.accessToken,
1013 fields, 980 fields,
1014 statusCodeExpected: HttpStatusCode.CONFLICT_409 981 expectedStatus: HttpStatusCode.CONFLICT_409
1015 }) 982 })
1016 }) 983 })
1017 984
1018 it('Should fail with a bad display name', async function () { 985 it('Should fail with a bad display name', async function () {
1019 const fields = immutableAssign(baseCorrectParams, { displayName: 'a'.repeat(150) }) 986 const fields = { ...baseCorrectParams, displayName: 'a'.repeat(150) }
1020 987
1021 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 988 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1022 }) 989 })
1023 990
1024 it('Should fail with a bad channel name', async function () { 991 it('Should fail with a bad channel name', async function () {
1025 const fields = immutableAssign(baseCorrectParams, { channel: { name: '[]azf', displayName: 'toto' } }) 992 const fields = { ...baseCorrectParams, channel: { name: '[]azf', displayName: 'toto' } }
1026 993
1027 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 994 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1028 }) 995 })
1029 996
1030 it('Should fail with a bad channel display name', async function () { 997 it('Should fail with a bad channel display name', async function () {
1031 const fields = immutableAssign(baseCorrectParams, { channel: { name: 'toto', displayName: '' } }) 998 const fields = { ...baseCorrectParams, channel: { name: 'toto', displayName: '' } }
1032 999
1033 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 1000 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1034 }) 1001 })
1035 1002
1036 it('Should fail with a channel name that is the same as username', async function () { 1003 it('Should fail with a channel name that is the same as username', async function () {
1037 const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } } 1004 const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } }
1038 const fields = immutableAssign(baseCorrectParams, source) 1005 const fields = { ...baseCorrectParams, ...source }
1039 1006
1040 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) 1007 await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields })
1041 }) 1008 })
1042 1009
1043 it('Should fail with an existing channel', async function () { 1010 it('Should fail with an existing channel', async function () {
1044 const videoChannelAttributesArg = { name: 'existing_channel', displayName: 'hello', description: 'super description' } 1011 const attributes = { name: 'existing_channel', displayName: 'hello', description: 'super description' }
1045 await addVideoChannel(server.url, server.accessToken, videoChannelAttributesArg) 1012 await server.channels.create({ attributes })
1046 1013
1047 const fields = immutableAssign(baseCorrectParams, { channel: { name: 'existing_channel', displayName: 'toto' } }) 1014 const fields = { ...baseCorrectParams, channel: { name: 'existing_channel', displayName: 'toto' } }
1048 1015
1049 await makePostBodyRequest({ 1016 await makePostBodyRequest({
1050 url: server.url, 1017 url: server.url,
1051 path: registrationPath, 1018 path: registrationPath,
1052 token: server.accessToken, 1019 token: server.accessToken,
1053 fields, 1020 fields,
1054 statusCodeExpected: HttpStatusCode.CONFLICT_409 1021 expectedStatus: HttpStatusCode.CONFLICT_409
1055 }) 1022 })
1056 }) 1023 })
1057 1024
1058 it('Should succeed with the correct params', async function () { 1025 it('Should succeed with the correct params', async function () {
1059 const fields = immutableAssign(baseCorrectParams, { channel: { name: 'super_channel', displayName: 'toto' } }) 1026 const fields = { ...baseCorrectParams, channel: { name: 'super_channel', displayName: 'toto' } }
1060 1027
1061 await makePostBodyRequest({ 1028 await makePostBodyRequest({
1062 url: server.url, 1029 url: server.url,
1063 path: registrationPath, 1030 path: registrationPath,
1064 token: server.accessToken, 1031 token: server.accessToken,
1065 fields: fields, 1032 fields: fields,
1066 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 1033 expectedStatus: HttpStatusCode.NO_CONTENT_204
1067 }) 1034 })
1068 }) 1035 })
1069 1036
@@ -1079,14 +1046,14 @@ describe('Test users API validators', function () {
1079 path: registrationPath, 1046 path: registrationPath,
1080 token: serverWithRegistrationDisabled.accessToken, 1047 token: serverWithRegistrationDisabled.accessToken,
1081 fields, 1048 fields,
1082 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 1049 expectedStatus: HttpStatusCode.FORBIDDEN_403
1083 }) 1050 })
1084 }) 1051 })
1085 }) 1052 })
1086 1053
1087 describe('When registering multiple users on a server with users limit', function () { 1054 describe('When registering multiple users on a server with users limit', function () {
1088 it('Should fail when after 3 registrations', async function () { 1055 it('Should fail when after 3 registrations', async function () {
1089 await registerUser(server.url, 'user42', 'super password', HttpStatusCode.FORBIDDEN_403) 1056 await server.users.register({ username: 'user42', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
1090 }) 1057 })
1091 }) 1058 })
1092 1059
@@ -1113,7 +1080,7 @@ describe('Test users API validators', function () {
1113 path, 1080 path,
1114 token: server.accessToken, 1081 token: server.accessToken,
1115 fields, 1082 fields,
1116 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 1083 expectedStatus: HttpStatusCode.NO_CONTENT_204
1117 }) 1084 })
1118 }) 1085 })
1119 }) 1086 })
@@ -1141,7 +1108,7 @@ describe('Test users API validators', function () {
1141 path, 1108 path,
1142 token: server.accessToken, 1109 token: server.accessToken,
1143 fields, 1110 fields,
1144 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 1111 expectedStatus: HttpStatusCode.NO_CONTENT_204
1145 }) 1112 })
1146 }) 1113 })
1147 }) 1114 })
diff --git a/server/tests/api/check-params/video-blacklist.ts b/server/tests/api/check-params/video-blacklist.ts
index ce7f5fa17..1f926d227 100644
--- a/server/tests/api/check-params/video-blacklist.ts
+++ b/server/tests/api/check-params/video-blacklist.ts
@@ -1,46 +1,37 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4 4import { expect } from 'chai'
5import { 5import {
6 BlacklistCommand,
7 checkBadCountPagination,
8 checkBadSortPagination,
9 checkBadStartPagination,
6 cleanupTests, 10 cleanupTests,
7 createUser, 11 createMultipleServers,
8 doubleFollow, 12 doubleFollow,
9 flushAndRunMultipleServers,
10 getBlacklistedVideosList,
11 getVideo,
12 getVideoWithToken,
13 makePostBodyRequest, 13 makePostBodyRequest,
14 makePutBodyRequest, 14 makePutBodyRequest,
15 removeVideoFromBlacklist, 15 PeerTubeServer,
16 ServerInfo,
17 setAccessTokensToServers, 16 setAccessTokensToServers,
18 uploadVideo,
19 userLogin,
20 waitJobs 17 waitJobs
21} from '../../../../shared/extra-utils' 18} from '@shared/extra-utils'
22import { 19import { HttpStatusCode, VideoBlacklistType } from '@shared/models'
23 checkBadCountPagination,
24 checkBadSortPagination,
25 checkBadStartPagination
26} from '../../../../shared/extra-utils/requests/check-api-params'
27import { VideoBlacklistType, VideoDetails } from '../../../../shared/models/videos'
28import { expect } from 'chai'
29import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
30 20
31describe('Test video blacklist API validators', function () { 21describe('Test video blacklist API validators', function () {
32 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
33 let notBlacklistedVideoId: number 23 let notBlacklistedVideoId: string
34 let remoteVideoUUID: string 24 let remoteVideoUUID: string
35 let userAccessToken1 = '' 25 let userAccessToken1 = ''
36 let userAccessToken2 = '' 26 let userAccessToken2 = ''
27 let command: BlacklistCommand
37 28
38 // --------------------------------------------------------------- 29 // ---------------------------------------------------------------
39 30
40 before(async function () { 31 before(async function () {
41 this.timeout(120000) 32 this.timeout(120000)
42 33
43 servers = await flushAndRunMultipleServers(2) 34 servers = await createMultipleServers(2)
44 35
45 await setAccessTokensToServers(servers) 36 await setAccessTokensToServers(servers)
46 await doubleFollow(servers[0], servers[1]) 37 await doubleFollow(servers[0], servers[1])
@@ -48,40 +39,41 @@ describe('Test video blacklist API validators', function () {
48 { 39 {
49 const username = 'user1' 40 const username = 'user1'
50 const password = 'my super password' 41 const password = 'my super password'
51 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: username, password: password }) 42 await servers[0].users.create({ username: username, password: password })
52 userAccessToken1 = await userLogin(servers[0], { username, password }) 43 userAccessToken1 = await servers[0].login.getAccessToken({ username, password })
53 } 44 }
54 45
55 { 46 {
56 const username = 'user2' 47 const username = 'user2'
57 const password = 'my super password' 48 const password = 'my super password'
58 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: username, password: password }) 49 await servers[0].users.create({ username: username, password: password })
59 userAccessToken2 = await userLogin(servers[0], { username, password }) 50 userAccessToken2 = await servers[0].login.getAccessToken({ username, password })
60 } 51 }
61 52
62 { 53 {
63 const res = await uploadVideo(servers[0].url, userAccessToken1, {}) 54 servers[0].store.videoCreated = await servers[0].videos.upload({ token: userAccessToken1 })
64 servers[0].video = res.body.video
65 } 55 }
66 56
67 { 57 {
68 const res = await uploadVideo(servers[0].url, servers[0].accessToken, {}) 58 const { uuid } = await servers[0].videos.upload()
69 notBlacklistedVideoId = res.body.video.uuid 59 notBlacklistedVideoId = uuid
70 } 60 }
71 61
72 { 62 {
73 const res = await uploadVideo(servers[1].url, servers[1].accessToken, {}) 63 const { uuid } = await servers[1].videos.upload()
74 remoteVideoUUID = res.body.video.uuid 64 remoteVideoUUID = uuid
75 } 65 }
76 66
77 await waitJobs(servers) 67 await waitJobs(servers)
68
69 command = servers[0].blacklist
78 }) 70 })
79 71
80 describe('When adding a video in blacklist', function () { 72 describe('When adding a video in blacklist', function () {
81 const basePath = '/api/v1/videos/' 73 const basePath = '/api/v1/videos/'
82 74
83 it('Should fail with nothing', async function () { 75 it('Should fail with nothing', async function () {
84 const path = basePath + servers[0].video + '/blacklist' 76 const path = basePath + servers[0].store.videoCreated + '/blacklist'
85 const fields = {} 77 const fields = {}
86 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) 78 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
87 }) 79 })
@@ -93,25 +85,25 @@ describe('Test video blacklist API validators', function () {
93 }) 85 })
94 86
95 it('Should fail with a non authenticated user', async function () { 87 it('Should fail with a non authenticated user', async function () {
96 const path = basePath + servers[0].video + '/blacklist' 88 const path = basePath + servers[0].store.videoCreated + '/blacklist'
97 const fields = {} 89 const fields = {}
98 await makePostBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 90 await makePostBodyRequest({ url: servers[0].url, path, token: 'hello', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
99 }) 91 })
100 92
101 it('Should fail with a non admin user', async function () { 93 it('Should fail with a non admin user', async function () {
102 const path = basePath + servers[0].video + '/blacklist' 94 const path = basePath + servers[0].store.videoCreated + '/blacklist'
103 const fields = {} 95 const fields = {}
104 await makePostBodyRequest({ 96 await makePostBodyRequest({
105 url: servers[0].url, 97 url: servers[0].url,
106 path, 98 path,
107 token: userAccessToken2, 99 token: userAccessToken2,
108 fields, 100 fields,
109 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 101 expectedStatus: HttpStatusCode.FORBIDDEN_403
110 }) 102 })
111 }) 103 })
112 104
113 it('Should fail with an invalid reason', async function () { 105 it('Should fail with an invalid reason', async function () {
114 const path = basePath + servers[0].video.uuid + '/blacklist' 106 const path = basePath + servers[0].store.videoCreated.uuid + '/blacklist'
115 const fields = { reason: 'a'.repeat(305) } 107 const fields = { reason: 'a'.repeat(305) }
116 108
117 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) 109 await makePostBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
@@ -126,12 +118,12 @@ describe('Test video blacklist API validators', function () {
126 path, 118 path,
127 token: servers[0].accessToken, 119 token: servers[0].accessToken,
128 fields, 120 fields,
129 statusCodeExpected: HttpStatusCode.CONFLICT_409 121 expectedStatus: HttpStatusCode.CONFLICT_409
130 }) 122 })
131 }) 123 })
132 124
133 it('Should succeed with the correct params', async function () { 125 it('Should succeed with the correct params', async function () {
134 const path = basePath + servers[0].video.uuid + '/blacklist' 126 const path = basePath + servers[0].store.videoCreated.uuid + '/blacklist'
135 const fields = {} 127 const fields = {}
136 128
137 await makePostBodyRequest({ 129 await makePostBodyRequest({
@@ -139,7 +131,7 @@ describe('Test video blacklist API validators', function () {
139 path, 131 path,
140 token: servers[0].accessToken, 132 token: servers[0].accessToken,
141 fields, 133 fields,
142 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 134 expectedStatus: HttpStatusCode.NO_CONTENT_204
143 }) 135 })
144 }) 136 })
145 }) 137 })
@@ -161,37 +153,37 @@ describe('Test video blacklist API validators', function () {
161 path, 153 path,
162 token: servers[0].accessToken, 154 token: servers[0].accessToken,
163 fields, 155 fields,
164 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 156 expectedStatus: HttpStatusCode.NOT_FOUND_404
165 }) 157 })
166 }) 158 })
167 159
168 it('Should fail with a non authenticated user', async function () { 160 it('Should fail with a non authenticated user', async function () {
169 const path = basePath + servers[0].video + '/blacklist' 161 const path = basePath + servers[0].store.videoCreated + '/blacklist'
170 const fields = {} 162 const fields = {}
171 await makePutBodyRequest({ url: servers[0].url, path, token: 'hello', fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 163 await makePutBodyRequest({ url: servers[0].url, path, token: 'hello', fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
172 }) 164 })
173 165
174 it('Should fail with a non admin user', async function () { 166 it('Should fail with a non admin user', async function () {
175 const path = basePath + servers[0].video + '/blacklist' 167 const path = basePath + servers[0].store.videoCreated + '/blacklist'
176 const fields = {} 168 const fields = {}
177 await makePutBodyRequest({ 169 await makePutBodyRequest({
178 url: servers[0].url, 170 url: servers[0].url,
179 path, 171 path,
180 token: userAccessToken2, 172 token: userAccessToken2,
181 fields, 173 fields,
182 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 174 expectedStatus: HttpStatusCode.FORBIDDEN_403
183 }) 175 })
184 }) 176 })
185 177
186 it('Should fail with an invalid reason', async function () { 178 it('Should fail with an invalid reason', async function () {
187 const path = basePath + servers[0].video.uuid + '/blacklist' 179 const path = basePath + servers[0].store.videoCreated.uuid + '/blacklist'
188 const fields = { reason: 'a'.repeat(305) } 180 const fields = { reason: 'a'.repeat(305) }
189 181
190 await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields }) 182 await makePutBodyRequest({ url: servers[0].url, path, token: servers[0].accessToken, fields })
191 }) 183 })
192 184
193 it('Should succeed with the correct params', async function () { 185 it('Should succeed with the correct params', async function () {
194 const path = basePath + servers[0].video.shortUUID + '/blacklist' 186 const path = basePath + servers[0].store.videoCreated.shortUUID + '/blacklist'
195 const fields = { reason: 'hello' } 187 const fields = { reason: 'hello' }
196 188
197 await makePutBodyRequest({ 189 await makePutBodyRequest({
@@ -199,7 +191,7 @@ describe('Test video blacklist API validators', function () {
199 path, 191 path,
200 token: servers[0].accessToken, 192 token: servers[0].accessToken,
201 fields, 193 fields,
202 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 194 expectedStatus: HttpStatusCode.NO_CONTENT_204
203 }) 195 })
204 }) 196 })
205 }) 197 })
@@ -207,52 +199,61 @@ describe('Test video blacklist API validators', function () {
207 describe('When getting blacklisted video', function () { 199 describe('When getting blacklisted video', function () {
208 200
209 it('Should fail with a non authenticated user', async function () { 201 it('Should fail with a non authenticated user', async function () {
210 await getVideo(servers[0].url, servers[0].video.uuid, HttpStatusCode.UNAUTHORIZED_401) 202 await servers[0].videos.get({ id: servers[0].store.videoCreated.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
211 }) 203 })
212 204
213 it('Should fail with another user', async function () { 205 it('Should fail with another user', async function () {
214 await getVideoWithToken(servers[0].url, userAccessToken2, servers[0].video.uuid, HttpStatusCode.FORBIDDEN_403) 206 await servers[0].videos.getWithToken({
207 token: userAccessToken2,
208 id: servers[0].store.videoCreated.uuid,
209 expectedStatus: HttpStatusCode.FORBIDDEN_403
210 })
215 }) 211 })
216 212
217 it('Should succeed with the owner authenticated user', async function () { 213 it('Should succeed with the owner authenticated user', async function () {
218 const res = await getVideoWithToken(servers[0].url, userAccessToken1, servers[0].video.uuid, HttpStatusCode.OK_200) 214 const video = await servers[0].videos.getWithToken({ token: userAccessToken1, id: servers[0].store.videoCreated.uuid })
219 const video: VideoDetails = res.body
220
221 expect(video.blacklisted).to.be.true 215 expect(video.blacklisted).to.be.true
222 }) 216 })
223 217
224 it('Should succeed with an admin', async function () { 218 it('Should succeed with an admin', async function () {
225 const video = servers[0].video 219 const video = servers[0].store.videoCreated
226 220
227 for (const id of [ video.id, video.uuid, video.shortUUID ]) { 221 for (const id of [ video.id, video.uuid, video.shortUUID ]) {
228 const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, id, HttpStatusCode.OK_200) 222 const video = await servers[0].videos.getWithToken({ id, expectedStatus: HttpStatusCode.OK_200 })
229 const video: VideoDetails = res.body
230
231 expect(video.blacklisted).to.be.true 223 expect(video.blacklisted).to.be.true
232 } 224 }
233 }) 225 })
234 }) 226 })
235 227
236 describe('When removing a video in blacklist', function () { 228 describe('When removing a video in blacklist', function () {
229
237 it('Should fail with a non authenticated user', async function () { 230 it('Should fail with a non authenticated user', async function () {
238 await removeVideoFromBlacklist(servers[0].url, 'fake token', servers[0].video.uuid, HttpStatusCode.UNAUTHORIZED_401) 231 await command.remove({
232 token: 'fake token',
233 videoId: servers[0].store.videoCreated.uuid,
234 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
235 })
239 }) 236 })
240 237
241 it('Should fail with a non admin user', async function () { 238 it('Should fail with a non admin user', async function () {
242 await removeVideoFromBlacklist(servers[0].url, userAccessToken2, servers[0].video.uuid, HttpStatusCode.FORBIDDEN_403) 239 await command.remove({
240 token: userAccessToken2,
241 videoId: servers[0].store.videoCreated.uuid,
242 expectedStatus: HttpStatusCode.FORBIDDEN_403
243 })
243 }) 244 })
244 245
245 it('Should fail with an incorrect id', async function () { 246 it('Should fail with an incorrect id', async function () {
246 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, 'hello', HttpStatusCode.BAD_REQUEST_400) 247 await command.remove({ videoId: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
247 }) 248 })
248 249
249 it('Should fail with a not blacklisted video', async function () { 250 it('Should fail with a not blacklisted video', async function () {
250 // The video was not added to the blacklist so it should fail 251 // The video was not added to the blacklist so it should fail
251 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, notBlacklistedVideoId, HttpStatusCode.NOT_FOUND_404) 252 await command.remove({ videoId: notBlacklistedVideoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
252 }) 253 })
253 254
254 it('Should succeed with the correct params', async function () { 255 it('Should succeed with the correct params', async function () {
255 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, servers[0].video.uuid, HttpStatusCode.NO_CONTENT_204) 256 await command.remove({ videoId: servers[0].store.videoCreated.uuid, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
256 }) 257 })
257 }) 258 })
258 259
@@ -260,11 +261,11 @@ describe('Test video blacklist API validators', function () {
260 const basePath = '/api/v1/videos/blacklist/' 261 const basePath = '/api/v1/videos/blacklist/'
261 262
262 it('Should fail with a non authenticated user', async function () { 263 it('Should fail with a non authenticated user', async function () {
263 await getBlacklistedVideosList({ url: servers[0].url, token: 'fake token', specialStatus: HttpStatusCode.UNAUTHORIZED_401 }) 264 await servers[0].blacklist.list({ token: 'fake token', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
264 }) 265 })
265 266
266 it('Should fail with a non admin user', async function () { 267 it('Should fail with a non admin user', async function () {
267 await getBlacklistedVideosList({ url: servers[0].url, token: userAccessToken2, specialStatus: HttpStatusCode.FORBIDDEN_403 }) 268 await servers[0].blacklist.list({ token: userAccessToken2, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
268 }) 269 })
269 270
270 it('Should fail with a bad start pagination', async function () { 271 it('Should fail with a bad start pagination', async function () {
@@ -280,16 +281,11 @@ describe('Test video blacklist API validators', function () {
280 }) 281 })
281 282
282 it('Should fail with an invalid type', async function () { 283 it('Should fail with an invalid type', async function () {
283 await getBlacklistedVideosList({ 284 await servers[0].blacklist.list({ type: 0, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
284 url: servers[0].url,
285 token: servers[0].accessToken,
286 type: 0,
287 specialStatus: HttpStatusCode.BAD_REQUEST_400
288 })
289 }) 285 })
290 286
291 it('Should succeed with the correct parameters', async function () { 287 it('Should succeed with the correct parameters', async function () {
292 await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, type: VideoBlacklistType.MANUAL }) 288 await servers[0].blacklist.list({ type: VideoBlacklistType.MANUAL })
293 }) 289 })
294 }) 290 })
295 291
diff --git a/server/tests/api/check-params/video-captions.ts b/server/tests/api/check-params/video-captions.ts
index c0595c04d..90f429314 100644
--- a/server/tests/api/check-params/video-captions.ts
+++ b/server/tests/api/check-params/video-captions.ts
@@ -1,27 +1,22 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { VideoCreateResult } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 4import {
7 buildAbsoluteFixturePath, 5 buildAbsoluteFixturePath,
8 cleanupTests, 6 cleanupTests,
9 createUser, 7 createSingleServer,
10 flushAndRunServer,
11 makeDeleteRequest, 8 makeDeleteRequest,
12 makeGetRequest, 9 makeGetRequest,
13 makeUploadRequest, 10 makeUploadRequest,
14 ServerInfo, 11 PeerTubeServer,
15 setAccessTokensToServers, 12 setAccessTokensToServers
16 uploadVideo, 13} from '@shared/extra-utils'
17 userLogin 14import { HttpStatusCode, VideoCreateResult } from '@shared/models'
18} from '../../../../shared/extra-utils'
19import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
20 15
21describe('Test video captions API validator', function () { 16describe('Test video captions API validator', function () {
22 const path = '/api/v1/videos/' 17 const path = '/api/v1/videos/'
23 18
24 let server: ServerInfo 19 let server: PeerTubeServer
25 let userAccessToken: string 20 let userAccessToken: string
26 let video: VideoCreateResult 21 let video: VideoCreateResult
27 22
@@ -30,22 +25,19 @@ describe('Test video captions API validator', function () {
30 before(async function () { 25 before(async function () {
31 this.timeout(30000) 26 this.timeout(30000)
32 27
33 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
34 29
35 await setAccessTokensToServers([ server ]) 30 await setAccessTokensToServers([ server ])
36 31
37 { 32 video = await server.videos.upload()
38 const res = await uploadVideo(server.url, server.accessToken, {})
39 video = res.body.video
40 }
41 33
42 { 34 {
43 const user = { 35 const user = {
44 username: 'user1', 36 username: 'user1',
45 password: 'my super password' 37 password: 'my super password'
46 } 38 }
47 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 39 await server.users.create({ username: user.username, password: user.password })
48 userAccessToken = await userLogin(server, user) 40 userAccessToken = await server.login.getAccessToken(user)
49 } 41 }
50 }) 42 })
51 43
@@ -74,7 +66,7 @@ describe('Test video captions API validator', function () {
74 token: server.accessToken, 66 token: server.accessToken,
75 fields, 67 fields,
76 attaches, 68 attaches,
77 statusCodeExpected: 404 69 expectedStatus: 404
78 }) 70 })
79 }) 71 })
80 72
@@ -110,7 +102,7 @@ describe('Test video captions API validator', function () {
110 path: captionPath, 102 path: captionPath,
111 fields, 103 fields,
112 attaches, 104 attaches,
113 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 105 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
114 }) 106 })
115 }) 107 })
116 108
@@ -123,7 +115,7 @@ describe('Test video captions API validator', function () {
123 token: 'blabla', 115 token: 'blabla',
124 fields, 116 fields,
125 attaches, 117 attaches,
126 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 118 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
127 }) 119 })
128 }) 120 })
129 121
@@ -141,7 +133,7 @@ describe('Test video captions API validator', function () {
141 // token: server.accessToken, 133 // token: server.accessToken,
142 // fields, 134 // fields,
143 // attaches, 135 // attaches,
144 // statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 136 // expectedStatus: HttpStatusCode.BAD_REQUEST_400
145 // }) 137 // })
146 // }) 138 // })
147 139
@@ -154,14 +146,12 @@ describe('Test video captions API validator', function () {
154 // videoId: video.uuid, 146 // videoId: video.uuid,
155 // fixture: 'subtitle-bad.txt', 147 // fixture: 'subtitle-bad.txt',
156 // mimeType: 'application/octet-stream', 148 // mimeType: 'application/octet-stream',
157 // statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 149 // expectedStatus: HttpStatusCode.BAD_REQUEST_400
158 // }) 150 // })
159 // }) 151 // })
160 152
161 it('Should succeed with a valid captionfile extension and octet-stream mime type', async function () { 153 it('Should succeed with a valid captionfile extension and octet-stream mime type', async function () {
162 await createVideoCaption({ 154 await server.captions.add({
163 url: server.url,
164 accessToken: server.accessToken,
165 language: 'zh', 155 language: 'zh',
166 videoId: video.uuid, 156 videoId: video.uuid,
167 fixture: 'subtitle-good.srt', 157 fixture: 'subtitle-good.srt',
@@ -183,7 +173,7 @@ describe('Test video captions API validator', function () {
183 // token: server.accessToken, 173 // token: server.accessToken,
184 // fields, 174 // fields,
185 // attaches, 175 // attaches,
186 // statusCodeExpected: HttpStatusCode.INTERNAL_SERVER_ERROR_500 176 // expectedStatus: HttpStatusCode.INTERNAL_SERVER_ERROR_500
187 // }) 177 // })
188 // }) 178 // })
189 179
@@ -196,7 +186,7 @@ describe('Test video captions API validator', function () {
196 token: server.accessToken, 186 token: server.accessToken,
197 fields, 187 fields,
198 attaches, 188 attaches,
199 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 189 expectedStatus: HttpStatusCode.NO_CONTENT_204
200 }) 190 })
201 }) 191 })
202 }) 192 })
@@ -210,12 +200,12 @@ describe('Test video captions API validator', function () {
210 await makeGetRequest({ 200 await makeGetRequest({
211 url: server.url, 201 url: server.url,
212 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions', 202 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions',
213 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 203 expectedStatus: HttpStatusCode.NOT_FOUND_404
214 }) 204 })
215 }) 205 })
216 206
217 it('Should success with the correct parameters', async function () { 207 it('Should success with the correct parameters', async function () {
218 await makeGetRequest({ url: server.url, path: path + video.shortUUID + '/captions', statusCodeExpected: HttpStatusCode.OK_200 }) 208 await makeGetRequest({ url: server.url, path: path + video.shortUUID + '/captions', expectedStatus: HttpStatusCode.OK_200 })
219 }) 209 })
220 }) 210 })
221 211
@@ -233,7 +223,7 @@ describe('Test video captions API validator', function () {
233 url: server.url, 223 url: server.url,
234 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions/fr', 224 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/captions/fr',
235 token: server.accessToken, 225 token: server.accessToken,
236 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 226 expectedStatus: HttpStatusCode.NOT_FOUND_404
237 }) 227 })
238 }) 228 })
239 229
@@ -257,12 +247,12 @@ describe('Test video captions API validator', function () {
257 247
258 it('Should fail without access token', async function () { 248 it('Should fail without access token', async function () {
259 const captionPath = path + video.shortUUID + '/captions/fr' 249 const captionPath = path + video.shortUUID + '/captions/fr'
260 await makeDeleteRequest({ url: server.url, path: captionPath, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 250 await makeDeleteRequest({ url: server.url, path: captionPath, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
261 }) 251 })
262 252
263 it('Should fail with a bad access token', async function () { 253 it('Should fail with a bad access token', async function () {
264 const captionPath = path + video.shortUUID + '/captions/fr' 254 const captionPath = path + video.shortUUID + '/captions/fr'
265 await makeDeleteRequest({ url: server.url, path: captionPath, token: 'coucou', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 255 await makeDeleteRequest({ url: server.url, path: captionPath, token: 'coucou', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
266 }) 256 })
267 257
268 it('Should fail with another user', async function () { 258 it('Should fail with another user', async function () {
@@ -271,7 +261,7 @@ describe('Test video captions API validator', function () {
271 url: server.url, 261 url: server.url,
272 path: captionPath, 262 path: captionPath,
273 token: userAccessToken, 263 token: userAccessToken,
274 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 264 expectedStatus: HttpStatusCode.FORBIDDEN_403
275 }) 265 })
276 }) 266 })
277 267
@@ -281,7 +271,7 @@ describe('Test video captions API validator', function () {
281 url: server.url, 271 url: server.url,
282 path: captionPath, 272 path: captionPath,
283 token: server.accessToken, 273 token: server.accessToken,
284 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 274 expectedStatus: HttpStatusCode.NO_CONTENT_204
285 }) 275 })
286 }) 276 })
287 }) 277 })
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index 5c02afd31..2e63916d4 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -3,43 +3,37 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 6import {
8 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
8 ChannelsCommand,
9 checkBadCountPagination,
10 checkBadSortPagination,
11 checkBadStartPagination,
9 cleanupTests, 12 cleanupTests,
10 createUser, 13 createSingleServer,
11 deleteVideoChannel,
12 flushAndRunServer,
13 getAccountVideoChannelsList,
14 immutableAssign,
15 makeGetRequest, 14 makeGetRequest,
16 makePostBodyRequest, 15 makePostBodyRequest,
17 makePutBodyRequest, 16 makePutBodyRequest,
18 makeUploadRequest, 17 makeUploadRequest,
19 ServerInfo, 18 PeerTubeServer,
20 setAccessTokensToServers, 19 setAccessTokensToServers
21 userLogin 20} from '@shared/extra-utils'
22} from '../../../../shared/extra-utils' 21import { HttpStatusCode, VideoChannelUpdate } from '@shared/models'
23import {
24 checkBadCountPagination,
25 checkBadSortPagination,
26 checkBadStartPagination
27} from '../../../../shared/extra-utils/requests/check-api-params'
28import { VideoChannelUpdate } from '../../../../shared/models/videos'
29 22
30const expect = chai.expect 23const expect = chai.expect
31 24
32describe('Test video channels API validator', function () { 25describe('Test video channels API validator', function () {
33 const videoChannelPath = '/api/v1/video-channels' 26 const videoChannelPath = '/api/v1/video-channels'
34 let server: ServerInfo 27 let server: PeerTubeServer
35 let accessTokenUser: string 28 let accessTokenUser: string
29 let command: ChannelsCommand
36 30
37 // --------------------------------------------------------------- 31 // ---------------------------------------------------------------
38 32
39 before(async function () { 33 before(async function () {
40 this.timeout(30000) 34 this.timeout(30000)
41 35
42 server = await flushAndRunServer(1) 36 server = await createSingleServer(1)
43 37
44 await setAccessTokensToServers([ server ]) 38 await setAccessTokensToServers([ server ])
45 39
@@ -49,9 +43,11 @@ describe('Test video channels API validator', function () {
49 } 43 }
50 44
51 { 45 {
52 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 46 await server.users.create({ username: user.username, password: user.password })
53 accessTokenUser = await userLogin(server, user) 47 accessTokenUser = await server.login.getAccessToken(user)
54 } 48 }
49
50 command = server.channels
55 }) 51 })
56 52
57 describe('When listing a video channels', function () { 53 describe('When listing a video channels', function () {
@@ -84,14 +80,14 @@ describe('Test video channels API validator', function () {
84 }) 80 })
85 81
86 it('Should fail with a unknown account', async function () { 82 it('Should fail with a unknown account', async function () {
87 await getAccountVideoChannelsList({ url: server.url, accountName: 'unknown', specialStatus: HttpStatusCode.NOT_FOUND_404 }) 83 await server.channels.listByAccount({ accountName: 'unknown', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
88 }) 84 })
89 85
90 it('Should succeed with the correct parameters', async function () { 86 it('Should succeed with the correct parameters', async function () {
91 await makeGetRequest({ 87 await makeGetRequest({
92 url: server.url, 88 url: server.url,
93 path: accountChannelPath, 89 path: accountChannelPath,
94 statusCodeExpected: HttpStatusCode.OK_200 90 expectedStatus: HttpStatusCode.OK_200
95 }) 91 })
96 }) 92 })
97 }) 93 })
@@ -110,7 +106,7 @@ describe('Test video channels API validator', function () {
110 path: videoChannelPath, 106 path: videoChannelPath,
111 token: 'none', 107 token: 'none',
112 fields: baseCorrectParams, 108 fields: baseCorrectParams,
113 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 109 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
114 }) 110 })
115 }) 111 })
116 112
@@ -125,7 +121,7 @@ describe('Test video channels API validator', function () {
125 }) 121 })
126 122
127 it('Should fail with a bad name', async function () { 123 it('Should fail with a bad name', async function () {
128 const fields = immutableAssign(baseCorrectParams, { name: 'super name' }) 124 const fields = { ...baseCorrectParams, name: 'super name' }
129 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 125 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
130 }) 126 })
131 127
@@ -135,17 +131,17 @@ describe('Test video channels API validator', function () {
135 }) 131 })
136 132
137 it('Should fail with a long name', async function () { 133 it('Should fail with a long name', async function () {
138 const fields = immutableAssign(baseCorrectParams, { displayName: 'super'.repeat(25) }) 134 const fields = { ...baseCorrectParams, displayName: 'super'.repeat(25) }
139 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 135 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
140 }) 136 })
141 137
142 it('Should fail with a long description', async function () { 138 it('Should fail with a long description', async function () {
143 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(201) }) 139 const fields = { ...baseCorrectParams, description: 'super'.repeat(201) }
144 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 140 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
145 }) 141 })
146 142
147 it('Should fail with a long support text', async function () { 143 it('Should fail with a long support text', async function () {
148 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 144 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
149 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields }) 145 await makePostBodyRequest({ url: server.url, path: videoChannelPath, token: server.accessToken, fields })
150 }) 146 })
151 147
@@ -155,7 +151,7 @@ describe('Test video channels API validator', function () {
155 path: videoChannelPath, 151 path: videoChannelPath,
156 token: server.accessToken, 152 token: server.accessToken,
157 fields: baseCorrectParams, 153 fields: baseCorrectParams,
158 statusCodeExpected: HttpStatusCode.OK_200 154 expectedStatus: HttpStatusCode.OK_200
159 }) 155 })
160 }) 156 })
161 157
@@ -165,7 +161,7 @@ describe('Test video channels API validator', function () {
165 path: videoChannelPath, 161 path: videoChannelPath,
166 token: server.accessToken, 162 token: server.accessToken,
167 fields: baseCorrectParams, 163 fields: baseCorrectParams,
168 statusCodeExpected: HttpStatusCode.CONFLICT_409 164 expectedStatus: HttpStatusCode.CONFLICT_409
169 }) 165 })
170 }) 166 })
171 }) 167 })
@@ -189,7 +185,7 @@ describe('Test video channels API validator', function () {
189 path, 185 path,
190 token: 'hi', 186 token: 'hi',
191 fields: baseCorrectParams, 187 fields: baseCorrectParams,
192 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 188 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
193 }) 189 })
194 }) 190 })
195 191
@@ -199,27 +195,27 @@ describe('Test video channels API validator', function () {
199 path, 195 path,
200 token: accessTokenUser, 196 token: accessTokenUser,
201 fields: baseCorrectParams, 197 fields: baseCorrectParams,
202 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 198 expectedStatus: HttpStatusCode.FORBIDDEN_403
203 }) 199 })
204 }) 200 })
205 201
206 it('Should fail with a long name', async function () { 202 it('Should fail with a long name', async function () {
207 const fields = immutableAssign(baseCorrectParams, { displayName: 'super'.repeat(25) }) 203 const fields = { ...baseCorrectParams, displayName: 'super'.repeat(25) }
208 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 204 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
209 }) 205 })
210 206
211 it('Should fail with a long description', async function () { 207 it('Should fail with a long description', async function () {
212 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(201) }) 208 const fields = { ...baseCorrectParams, description: 'super'.repeat(201) }
213 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 209 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
214 }) 210 })
215 211
216 it('Should fail with a long support text', async function () { 212 it('Should fail with a long support text', async function () {
217 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 213 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
218 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 214 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
219 }) 215 })
220 216
221 it('Should fail with a bad bulkVideosSupportUpdate field', async function () { 217 it('Should fail with a bad bulkVideosSupportUpdate field', async function () {
222 const fields = immutableAssign(baseCorrectParams, { bulkVideosSupportUpdate: 'super' }) 218 const fields = { ...baseCorrectParams, bulkVideosSupportUpdate: 'super' }
223 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 219 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
224 }) 220 })
225 221
@@ -229,7 +225,7 @@ describe('Test video channels API validator', function () {
229 path, 225 path,
230 token: server.accessToken, 226 token: server.accessToken,
231 fields: baseCorrectParams, 227 fields: baseCorrectParams,
232 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 228 expectedStatus: HttpStatusCode.NO_CONTENT_204
233 }) 229 })
234 }) 230 })
235 }) 231 })
@@ -274,7 +270,7 @@ describe('Test video channels API validator', function () {
274 path: `${path}/${type}/pick`, 270 path: `${path}/${type}/pick`,
275 fields, 271 fields,
276 attaches, 272 attaches,
277 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 273 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
278 }) 274 })
279 } 275 }
280 }) 276 })
@@ -291,7 +287,7 @@ describe('Test video channels API validator', function () {
291 token: server.accessToken, 287 token: server.accessToken,
292 fields, 288 fields,
293 attaches, 289 attaches,
294 statusCodeExpected: HttpStatusCode.OK_200 290 expectedStatus: HttpStatusCode.OK_200
295 }) 291 })
296 } 292 }
297 }) 293 })
@@ -302,7 +298,7 @@ describe('Test video channels API validator', function () {
302 const res = await makeGetRequest({ 298 const res = await makeGetRequest({
303 url: server.url, 299 url: server.url,
304 path: videoChannelPath, 300 path: videoChannelPath,
305 statusCodeExpected: HttpStatusCode.OK_200 301 expectedStatus: HttpStatusCode.OK_200
306 }) 302 })
307 303
308 expect(res.body.data).to.be.an('array') 304 expect(res.body.data).to.be.an('array')
@@ -312,7 +308,7 @@ describe('Test video channels API validator', function () {
312 await makeGetRequest({ 308 await makeGetRequest({
313 url: server.url, 309 url: server.url,
314 path: videoChannelPath + '/super_channel2', 310 path: videoChannelPath + '/super_channel2',
315 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 311 expectedStatus: HttpStatusCode.NOT_FOUND_404
316 }) 312 })
317 }) 313 })
318 314
@@ -320,30 +316,30 @@ describe('Test video channels API validator', function () {
320 await makeGetRequest({ 316 await makeGetRequest({
321 url: server.url, 317 url: server.url,
322 path: videoChannelPath + '/super_channel', 318 path: videoChannelPath + '/super_channel',
323 statusCodeExpected: HttpStatusCode.OK_200 319 expectedStatus: HttpStatusCode.OK_200
324 }) 320 })
325 }) 321 })
326 }) 322 })
327 323
328 describe('When deleting a video channel', function () { 324 describe('When deleting a video channel', function () {
329 it('Should fail with a non authenticated user', async function () { 325 it('Should fail with a non authenticated user', async function () {
330 await deleteVideoChannel(server.url, 'coucou', 'super_channel', HttpStatusCode.UNAUTHORIZED_401) 326 await command.delete({ token: 'coucou', channelName: 'super_channel', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
331 }) 327 })
332 328
333 it('Should fail with another authenticated user', async function () { 329 it('Should fail with another authenticated user', async function () {
334 await deleteVideoChannel(server.url, accessTokenUser, 'super_channel', HttpStatusCode.FORBIDDEN_403) 330 await command.delete({ token: accessTokenUser, channelName: 'super_channel', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
335 }) 331 })
336 332
337 it('Should fail with an unknown video channel id', async function () { 333 it('Should fail with an unknown video channel id', async function () {
338 await deleteVideoChannel(server.url, server.accessToken, 'super_channel2', HttpStatusCode.NOT_FOUND_404) 334 await command.delete({ channelName: 'super_channel2', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
339 }) 335 })
340 336
341 it('Should succeed with the correct parameters', async function () { 337 it('Should succeed with the correct parameters', async function () {
342 await deleteVideoChannel(server.url, server.accessToken, 'super_channel') 338 await command.delete({ channelName: 'super_channel' })
343 }) 339 })
344 340
345 it('Should fail to delete the last user video channel', async function () { 341 it('Should fail to delete the last user video channel', async function () {
346 await deleteVideoChannel(server.url, server.accessToken, 'root_channel', HttpStatusCode.CONFLICT_409) 342 await command.delete({ channelName: 'root_channel', expectedStatus: HttpStatusCode.CONFLICT_409 })
347 }) 343 })
348 }) 344 })
349 345
diff --git a/server/tests/api/check-params/video-comments.ts b/server/tests/api/check-params/video-comments.ts
index a38420851..2d9ee1e0d 100644
--- a/server/tests/api/check-params/video-comments.ts
+++ b/server/tests/api/check-params/video-comments.ts
@@ -2,33 +2,26 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoCreateResult } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 5import {
6 checkBadCountPagination,
7 checkBadSortPagination,
8 checkBadStartPagination,
8 cleanupTests, 9 cleanupTests,
9 createUser, 10 createSingleServer,
10 flushAndRunServer,
11 makeDeleteRequest, 11 makeDeleteRequest,
12 makeGetRequest, 12 makeGetRequest,
13 makePostBodyRequest, 13 makePostBodyRequest,
14 ServerInfo, 14 PeerTubeServer,
15 setAccessTokensToServers, 15 setAccessTokensToServers
16 uploadVideo, 16} from '@shared/extra-utils'
17 userLogin 17import { HttpStatusCode, VideoCreateResult } from '@shared/models'
18} from '../../../../shared/extra-utils'
19import {
20 checkBadCountPagination,
21 checkBadSortPagination,
22 checkBadStartPagination
23} from '../../../../shared/extra-utils/requests/check-api-params'
24import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
25 18
26const expect = chai.expect 19const expect = chai.expect
27 20
28describe('Test video comments API validator', function () { 21describe('Test video comments API validator', function () {
29 let pathThread: string 22 let pathThread: string
30 let pathComment: string 23 let pathComment: string
31 let server: ServerInfo 24 let server: PeerTubeServer
32 let video: VideoCreateResult 25 let video: VideoCreateResult
33 let userAccessToken: string 26 let userAccessToken: string
34 let userAccessToken2: string 27 let userAccessToken2: string
@@ -39,32 +32,31 @@ describe('Test video comments API validator', function () {
39 before(async function () { 32 before(async function () {
40 this.timeout(30000) 33 this.timeout(30000)
41 34
42 server = await flushAndRunServer(1) 35 server = await createSingleServer(1)
43 36
44 await setAccessTokensToServers([ server ]) 37 await setAccessTokensToServers([ server ])
45 38
46 { 39 {
47 const res = await uploadVideo(server.url, server.accessToken, {}) 40 video = await server.videos.upload({ attributes: {} })
48 video = res.body.video
49 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads' 41 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads'
50 } 42 }
51 43
52 { 44 {
53 const res = await addVideoCommentThread(server.url, server.accessToken, video.uuid, 'coucou') 45 const created = await server.comments.createThread({ videoId: video.uuid, text: 'coucou' })
54 commentId = res.body.comment.id 46 commentId = created.id
55 pathComment = '/api/v1/videos/' + video.uuid + '/comments/' + commentId 47 pathComment = '/api/v1/videos/' + video.uuid + '/comments/' + commentId
56 } 48 }
57 49
58 { 50 {
59 const user = { username: 'user1', password: 'my super password' } 51 const user = { username: 'user1', password: 'my super password' }
60 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 52 await server.users.create({ username: user.username, password: user.password })
61 userAccessToken = await userLogin(server, user) 53 userAccessToken = await server.login.getAccessToken(user)
62 } 54 }
63 55
64 { 56 {
65 const user = { username: 'user2', password: 'my super password' } 57 const user = { username: 'user2', password: 'my super password' }
66 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 58 await server.users.create({ username: user.username, password: user.password })
67 userAccessToken2 = await userLogin(server, user) 59 userAccessToken2 = await server.login.getAccessToken(user)
68 } 60 }
69 }) 61 })
70 62
@@ -85,7 +77,7 @@ describe('Test video comments API validator', function () {
85 await makeGetRequest({ 77 await makeGetRequest({
86 url: server.url, 78 url: server.url,
87 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads', 79 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads',
88 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 80 expectedStatus: HttpStatusCode.NOT_FOUND_404
89 }) 81 })
90 }) 82 })
91 }) 83 })
@@ -95,7 +87,7 @@ describe('Test video comments API validator', function () {
95 await makeGetRequest({ 87 await makeGetRequest({
96 url: server.url, 88 url: server.url,
97 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads/' + commentId, 89 path: '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comment-threads/' + commentId,
98 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 90 expectedStatus: HttpStatusCode.NOT_FOUND_404
99 }) 91 })
100 }) 92 })
101 93
@@ -103,7 +95,7 @@ describe('Test video comments API validator', function () {
103 await makeGetRequest({ 95 await makeGetRequest({
104 url: server.url, 96 url: server.url,
105 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/156', 97 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/156',
106 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 98 expectedStatus: HttpStatusCode.NOT_FOUND_404
107 }) 99 })
108 }) 100 })
109 101
@@ -111,7 +103,7 @@ describe('Test video comments API validator', function () {
111 await makeGetRequest({ 103 await makeGetRequest({
112 url: server.url, 104 url: server.url,
113 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/' + commentId, 105 path: '/api/v1/videos/' + video.shortUUID + '/comment-threads/' + commentId,
114 statusCodeExpected: HttpStatusCode.OK_200 106 expectedStatus: HttpStatusCode.OK_200
115 }) 107 })
116 }) 108 })
117 }) 109 })
@@ -127,7 +119,7 @@ describe('Test video comments API validator', function () {
127 path: pathThread, 119 path: pathThread,
128 token: 'none', 120 token: 'none',
129 fields, 121 fields,
130 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 122 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
131 }) 123 })
132 }) 124 })
133 125
@@ -160,7 +152,7 @@ describe('Test video comments API validator', function () {
160 path, 152 path,
161 token: server.accessToken, 153 token: server.accessToken,
162 fields, 154 fields,
163 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 155 expectedStatus: HttpStatusCode.NOT_FOUND_404
164 }) 156 })
165 }) 157 })
166 158
@@ -173,7 +165,7 @@ describe('Test video comments API validator', function () {
173 path: pathThread, 165 path: pathThread,
174 token: server.accessToken, 166 token: server.accessToken,
175 fields, 167 fields,
176 statusCodeExpected: HttpStatusCode.OK_200 168 expectedStatus: HttpStatusCode.OK_200
177 }) 169 })
178 }) 170 })
179 }) 171 })
@@ -188,7 +180,7 @@ describe('Test video comments API validator', function () {
188 path: pathComment, 180 path: pathComment,
189 token: 'none', 181 token: 'none',
190 fields, 182 fields,
191 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 183 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
192 }) 184 })
193 }) 185 })
194 186
@@ -221,7 +213,7 @@ describe('Test video comments API validator', function () {
221 path, 213 path,
222 token: server.accessToken, 214 token: server.accessToken,
223 fields, 215 fields,
224 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 216 expectedStatus: HttpStatusCode.NOT_FOUND_404
225 }) 217 })
226 }) 218 })
227 219
@@ -235,7 +227,7 @@ describe('Test video comments API validator', function () {
235 path, 227 path,
236 token: server.accessToken, 228 token: server.accessToken,
237 fields, 229 fields,
238 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 230 expectedStatus: HttpStatusCode.NOT_FOUND_404
239 }) 231 })
240 }) 232 })
241 233
@@ -248,14 +240,14 @@ describe('Test video comments API validator', function () {
248 path: pathComment, 240 path: pathComment,
249 token: server.accessToken, 241 token: server.accessToken,
250 fields, 242 fields,
251 statusCodeExpected: HttpStatusCode.OK_200 243 expectedStatus: HttpStatusCode.OK_200
252 }) 244 })
253 }) 245 })
254 }) 246 })
255 247
256 describe('When removing video comments', function () { 248 describe('When removing video comments', function () {
257 it('Should fail with a non authenticated user', async function () { 249 it('Should fail with a non authenticated user', async function () {
258 await makeDeleteRequest({ url: server.url, path: pathComment, token: 'none', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 250 await makeDeleteRequest({ url: server.url, path: pathComment, token: 'none', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
259 }) 251 })
260 252
261 it('Should fail with another user', async function () { 253 it('Should fail with another user', async function () {
@@ -263,32 +255,32 @@ describe('Test video comments API validator', function () {
263 url: server.url, 255 url: server.url,
264 path: pathComment, 256 path: pathComment,
265 token: userAccessToken, 257 token: userAccessToken,
266 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 258 expectedStatus: HttpStatusCode.FORBIDDEN_403
267 }) 259 })
268 }) 260 })
269 261
270 it('Should fail with an incorrect video', async function () { 262 it('Should fail with an incorrect video', async function () {
271 const path = '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comments/' + commentId 263 const path = '/api/v1/videos/ba708d62-e3d7-45d9-9d73-41b9097cc02d/comments/' + commentId
272 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 264 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
273 }) 265 })
274 266
275 it('Should fail with an incorrect comment', async function () { 267 it('Should fail with an incorrect comment', async function () {
276 const path = '/api/v1/videos/' + video.uuid + '/comments/124' 268 const path = '/api/v1/videos/' + video.uuid + '/comments/124'
277 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, statusCodeExpected: HttpStatusCode.NOT_FOUND_404 }) 269 await makeDeleteRequest({ url: server.url, path, token: server.accessToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
278 }) 270 })
279 271
280 it('Should succeed with the same user', async function () { 272 it('Should succeed with the same user', async function () {
281 let commentToDelete: number 273 let commentToDelete: number
282 274
283 { 275 {
284 const res = await addVideoCommentThread(server.url, userAccessToken, video.uuid, 'hello') 276 const created = await server.comments.createThread({ videoId: video.uuid, token: userAccessToken, text: 'hello' })
285 commentToDelete = res.body.comment.id 277 commentToDelete = created.id
286 } 278 }
287 279
288 const path = '/api/v1/videos/' + video.uuid + '/comments/' + commentToDelete 280 const path = '/api/v1/videos/' + video.uuid + '/comments/' + commentToDelete
289 281
290 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 282 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
291 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.NO_CONTENT_204 }) 283 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
292 }) 284 })
293 285
294 it('Should succeed with the owner of the video', async function () { 286 it('Should succeed with the owner of the video', async function () {
@@ -296,19 +288,19 @@ describe('Test video comments API validator', function () {
296 let anotherVideoUUID: string 288 let anotherVideoUUID: string
297 289
298 { 290 {
299 const res = await uploadVideo(server.url, userAccessToken, { name: 'video' }) 291 const { uuid } = await server.videos.upload({ token: userAccessToken, attributes: { name: 'video' } })
300 anotherVideoUUID = res.body.video.uuid 292 anotherVideoUUID = uuid
301 } 293 }
302 294
303 { 295 {
304 const res = await addVideoCommentThread(server.url, server.accessToken, anotherVideoUUID, 'hello') 296 const created = await server.comments.createThread({ videoId: anotherVideoUUID, text: 'hello' })
305 commentToDelete = res.body.comment.id 297 commentToDelete = created.id
306 } 298 }
307 299
308 const path = '/api/v1/videos/' + anotherVideoUUID + '/comments/' + commentToDelete 300 const path = '/api/v1/videos/' + anotherVideoUUID + '/comments/' + commentToDelete
309 301
310 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, statusCodeExpected: HttpStatusCode.FORBIDDEN_403 }) 302 await makeDeleteRequest({ url: server.url, path, token: userAccessToken2, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
311 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, statusCodeExpected: HttpStatusCode.NO_CONTENT_204 }) 303 await makeDeleteRequest({ url: server.url, path, token: userAccessToken, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
312 }) 304 })
313 305
314 it('Should succeed with the correct parameters', async function () { 306 it('Should succeed with the correct parameters', async function () {
@@ -316,15 +308,14 @@ describe('Test video comments API validator', function () {
316 url: server.url, 308 url: server.url,
317 path: pathComment, 309 path: pathComment,
318 token: server.accessToken, 310 token: server.accessToken,
319 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 311 expectedStatus: HttpStatusCode.NO_CONTENT_204
320 }) 312 })
321 }) 313 })
322 }) 314 })
323 315
324 describe('When a video has comments disabled', function () { 316 describe('When a video has comments disabled', function () {
325 before(async function () { 317 before(async function () {
326 const res = await uploadVideo(server.url, server.accessToken, { commentsEnabled: false }) 318 video = await server.videos.upload({ attributes: { commentsEnabled: false } })
327 video = res.body.video
328 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads' 319 pathThread = '/api/v1/videos/' + video.uuid + '/comment-threads'
329 }) 320 })
330 321
@@ -332,7 +323,7 @@ describe('Test video comments API validator', function () {
332 const res = await makeGetRequest({ 323 const res = await makeGetRequest({
333 url: server.url, 324 url: server.url,
334 path: pathThread, 325 path: pathThread,
335 statusCodeExpected: HttpStatusCode.OK_200 326 expectedStatus: HttpStatusCode.OK_200
336 }) 327 })
337 expect(res.body.total).to.equal(0) 328 expect(res.body.total).to.equal(0)
338 expect(res.body.data).to.have.lengthOf(0) 329 expect(res.body.data).to.have.lengthOf(0)
@@ -349,7 +340,7 @@ describe('Test video comments API validator', function () {
349 path: pathThread, 340 path: pathThread,
350 token: server.accessToken, 341 token: server.accessToken,
351 fields, 342 fields,
352 statusCodeExpected: HttpStatusCode.CONFLICT_409 343 expectedStatus: HttpStatusCode.CONFLICT_409
353 }) 344 })
354 }) 345 })
355 346
@@ -375,7 +366,7 @@ describe('Test video comments API validator', function () {
375 await makeGetRequest({ 366 await makeGetRequest({
376 url: server.url, 367 url: server.url,
377 path, 368 path,
378 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 369 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
379 }) 370 })
380 }) 371 })
381 372
@@ -384,7 +375,7 @@ describe('Test video comments API validator', function () {
384 url: server.url, 375 url: server.url,
385 path, 376 path,
386 token: userAccessToken, 377 token: userAccessToken,
387 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 378 expectedStatus: HttpStatusCode.FORBIDDEN_403
388 }) 379 })
389 }) 380 })
390 381
@@ -399,7 +390,7 @@ describe('Test video comments API validator', function () {
399 searchAccount: 'toto', 390 searchAccount: 'toto',
400 searchVideo: 'toto' 391 searchVideo: 'toto'
401 }, 392 },
402 statusCodeExpected: HttpStatusCode.OK_200 393 expectedStatus: HttpStatusCode.OK_200
403 }) 394 })
404 }) 395 })
405 }) 396 })
diff --git a/server/tests/api/check-params/video-imports.ts b/server/tests/api/check-params/video-imports.ts
index a27b624d0..d6d745488 100644
--- a/server/tests/api/check-params/video-imports.ts
+++ b/server/tests/api/check-params/video-imports.ts
@@ -2,33 +2,25 @@
2 2
3import 'mocha' 3import 'mocha'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 5import {
7 buildAbsoluteFixturePath, 6 buildAbsoluteFixturePath,
7 checkBadCountPagination,
8 checkBadSortPagination,
9 checkBadStartPagination,
8 cleanupTests, 10 cleanupTests,
9 createUser, 11 createSingleServer,
10 flushAndRunServer, 12 FIXTURE_URLS,
11 getMyUserInformation,
12 immutableAssign,
13 makeGetRequest, 13 makeGetRequest,
14 makePostBodyRequest, 14 makePostBodyRequest,
15 makeUploadRequest, 15 makeUploadRequest,
16 ServerInfo, 16 PeerTubeServer,
17 setAccessTokensToServers, 17 setAccessTokensToServers
18 updateCustomSubConfig, 18} from '@shared/extra-utils'
19 userLogin 19import { HttpStatusCode, VideoPrivacy } from '@shared/models'
20} from '../../../../shared/extra-utils'
21import {
22 checkBadCountPagination,
23 checkBadSortPagination,
24 checkBadStartPagination
25} from '../../../../shared/extra-utils/requests/check-api-params'
26import { getGoodVideoUrl, getMagnetURI } from '../../../../shared/extra-utils/videos/video-imports'
27import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
28 20
29describe('Test video imports API validator', function () { 21describe('Test video imports API validator', function () {
30 const path = '/api/v1/videos/imports' 22 const path = '/api/v1/videos/imports'
31 let server: ServerInfo 23 let server: PeerTubeServer
32 let userAccessToken = '' 24 let userAccessToken = ''
33 let channelId: number 25 let channelId: number
34 26
@@ -37,18 +29,18 @@ describe('Test video imports API validator', function () {
37 before(async function () { 29 before(async function () {
38 this.timeout(30000) 30 this.timeout(30000)
39 31
40 server = await flushAndRunServer(1) 32 server = await createSingleServer(1)
41 33
42 await setAccessTokensToServers([ server ]) 34 await setAccessTokensToServers([ server ])
43 35
44 const username = 'user1' 36 const username = 'user1'
45 const password = 'my super password' 37 const password = 'my super password'
46 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 38 await server.users.create({ username: username, password: password })
47 userAccessToken = await userLogin(server, { username, password }) 39 userAccessToken = await server.login.getAccessToken({ username, password })
48 40
49 { 41 {
50 const res = await getMyUserInformation(server.url, server.accessToken) 42 const { videoChannels } = await server.users.getMyInfo()
51 channelId = res.body.videoChannels[0].id 43 channelId = videoChannels[0].id
52 } 44 }
53 }) 45 })
54 46
@@ -68,7 +60,7 @@ describe('Test video imports API validator', function () {
68 }) 60 })
69 61
70 it('Should success with the correct parameters', async function () { 62 it('Should success with the correct parameters', async function () {
71 await makeGetRequest({ url: server.url, path: myPath, statusCodeExpected: HttpStatusCode.OK_200, token: server.accessToken }) 63 await makeGetRequest({ url: server.url, path: myPath, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken })
72 }) 64 })
73 }) 65 })
74 66
@@ -77,7 +69,7 @@ describe('Test video imports API validator', function () {
77 69
78 before(function () { 70 before(function () {
79 baseCorrectParams = { 71 baseCorrectParams = {
80 targetUrl: getGoodVideoUrl(), 72 targetUrl: FIXTURE_URLS.goodVideo,
81 name: 'my super name', 73 name: 'my super name',
82 category: 5, 74 category: 5,
83 licence: 1, 75 licence: 1,
@@ -106,48 +98,48 @@ describe('Test video imports API validator', function () {
106 path, 98 path,
107 token: server.accessToken, 99 token: server.accessToken,
108 fields, 100 fields,
109 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 101 expectedStatus: HttpStatusCode.BAD_REQUEST_400
110 }) 102 })
111 }) 103 })
112 104
113 it('Should fail with a bad target url', async function () { 105 it('Should fail with a bad target url', async function () {
114 const fields = immutableAssign(baseCorrectParams, { targetUrl: 'htt://hello' }) 106 const fields = { ...baseCorrectParams, targetUrl: 'htt://hello' }
115 107
116 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 108 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
117 }) 109 })
118 110
119 it('Should fail with a long name', async function () { 111 it('Should fail with a long name', async function () {
120 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 112 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
121 113
122 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 114 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
123 }) 115 })
124 116
125 it('Should fail with a bad category', async function () { 117 it('Should fail with a bad category', async function () {
126 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 118 const fields = { ...baseCorrectParams, category: 125 }
127 119
128 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 120 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
129 }) 121 })
130 122
131 it('Should fail with a bad licence', async function () { 123 it('Should fail with a bad licence', async function () {
132 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 124 const fields = { ...baseCorrectParams, licence: 125 }
133 125
134 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 126 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
135 }) 127 })
136 128
137 it('Should fail with a bad language', async function () { 129 it('Should fail with a bad language', async function () {
138 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 130 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
139 131
140 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 132 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
141 }) 133 })
142 134
143 it('Should fail with a long description', async function () { 135 it('Should fail with a long description', async function () {
144 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 136 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
145 137
146 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 138 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
147 }) 139 })
148 140
149 it('Should fail with a long support text', async function () { 141 it('Should fail with a long support text', async function () {
150 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 142 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
151 143
152 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 144 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
153 }) 145 })
@@ -159,7 +151,7 @@ describe('Test video imports API validator', function () {
159 }) 151 })
160 152
161 it('Should fail with a bad channel', async function () { 153 it('Should fail with a bad channel', async function () {
162 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 154 const fields = { ...baseCorrectParams, channelId: 545454 }
163 155
164 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 156 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
165 }) 157 })
@@ -169,31 +161,31 @@ describe('Test video imports API validator', function () {
169 username: 'fake', 161 username: 'fake',
170 password: 'fake_password' 162 password: 'fake_password'
171 } 163 }
172 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 164 await server.users.create({ username: user.username, password: user.password })
173 165
174 const accessTokenUser = await userLogin(server, user) 166 const accessTokenUser = await server.login.getAccessToken(user)
175 const res = await getMyUserInformation(server.url, accessTokenUser) 167 const { videoChannels } = await server.users.getMyInfo({ token: accessTokenUser })
176 const customChannelId = res.body.videoChannels[0].id 168 const customChannelId = videoChannels[0].id
177 169
178 const fields = immutableAssign(baseCorrectParams, { channelId: customChannelId }) 170 const fields = { ...baseCorrectParams, channelId: customChannelId }
179 171
180 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) 172 await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields })
181 }) 173 })
182 174
183 it('Should fail with too many tags', async function () { 175 it('Should fail with too many tags', async function () {
184 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 176 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
185 177
186 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 178 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
187 }) 179 })
188 180
189 it('Should fail with a tag length too low', async function () { 181 it('Should fail with a tag length too low', async function () {
190 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 182 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
191 183
192 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 184 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
193 }) 185 })
194 186
195 it('Should fail with a tag length too big', async function () { 187 it('Should fail with a tag length too big', async function () {
196 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 188 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
197 189
198 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 190 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
199 }) 191 })
@@ -245,7 +237,7 @@ describe('Test video imports API validator', function () {
245 237
246 it('Should fail with an invalid magnet URI', async function () { 238 it('Should fail with an invalid magnet URI', async function () {
247 let fields = omit(baseCorrectParams, 'targetUrl') 239 let fields = omit(baseCorrectParams, 'targetUrl')
248 fields = immutableAssign(fields, { magnetUri: 'blabla' }) 240 fields = { ...fields, magnetUri: 'blabla' }
249 241
250 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 242 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
251 }) 243 })
@@ -258,19 +250,21 @@ describe('Test video imports API validator', function () {
258 path, 250 path,
259 token: server.accessToken, 251 token: server.accessToken,
260 fields: baseCorrectParams, 252 fields: baseCorrectParams,
261 statusCodeExpected: HttpStatusCode.OK_200 253 expectedStatus: HttpStatusCode.OK_200
262 }) 254 })
263 }) 255 })
264 256
265 it('Should forbid to import http videos', async function () { 257 it('Should forbid to import http videos', async function () {
266 await updateCustomSubConfig(server.url, server.accessToken, { 258 await server.config.updateCustomSubConfig({
267 import: { 259 newConfig: {
268 videos: { 260 import: {
269 http: { 261 videos: {
270 enabled: false 262 http: {
271 }, 263 enabled: false
272 torrent: { 264 },
273 enabled: true 265 torrent: {
266 enabled: true
267 }
274 } 268 }
275 } 269 }
276 } 270 }
@@ -281,33 +275,35 @@ describe('Test video imports API validator', function () {
281 path, 275 path,
282 token: server.accessToken, 276 token: server.accessToken,
283 fields: baseCorrectParams, 277 fields: baseCorrectParams,
284 statusCodeExpected: HttpStatusCode.CONFLICT_409 278 expectedStatus: HttpStatusCode.CONFLICT_409
285 }) 279 })
286 }) 280 })
287 281
288 it('Should forbid to import torrent videos', async function () { 282 it('Should forbid to import torrent videos', async function () {
289 await updateCustomSubConfig(server.url, server.accessToken, { 283 await server.config.updateCustomSubConfig({
290 import: { 284 newConfig: {
291 videos: { 285 import: {
292 http: { 286 videos: {
293 enabled: true 287 http: {
294 }, 288 enabled: true
295 torrent: { 289 },
296 enabled: false 290 torrent: {
291 enabled: false
292 }
297 } 293 }
298 } 294 }
299 } 295 }
300 }) 296 })
301 297
302 let fields = omit(baseCorrectParams, 'targetUrl') 298 let fields = omit(baseCorrectParams, 'targetUrl')
303 fields = immutableAssign(fields, { magnetUri: getMagnetURI() }) 299 fields = { ...fields, magnetUri: FIXTURE_URLS.magnet }
304 300
305 await makePostBodyRequest({ 301 await makePostBodyRequest({
306 url: server.url, 302 url: server.url,
307 path, 303 path,
308 token: server.accessToken, 304 token: server.accessToken,
309 fields, 305 fields,
310 statusCodeExpected: HttpStatusCode.CONFLICT_409 306 expectedStatus: HttpStatusCode.CONFLICT_409
311 }) 307 })
312 308
313 fields = omit(fields, 'magnetUri') 309 fields = omit(fields, 'magnetUri')
@@ -321,7 +317,7 @@ describe('Test video imports API validator', function () {
321 token: server.accessToken, 317 token: server.accessToken,
322 fields, 318 fields,
323 attaches, 319 attaches,
324 statusCodeExpected: HttpStatusCode.CONFLICT_409 320 expectedStatus: HttpStatusCode.CONFLICT_409
325 }) 321 })
326 }) 322 })
327 }) 323 })
diff --git a/server/tests/api/check-params/video-playlists.ts b/server/tests/api/check-params/video-playlists.ts
index 18253d11a..e4d541b48 100644
--- a/server/tests/api/check-params/video-playlists.ts
+++ b/server/tests/api/check-params/video-playlists.ts
@@ -1,34 +1,31 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { VideoPlaylistCreateResult, VideoPlaylistPrivacy, VideoPlaylistType } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 4import {
7 addVideoInPlaylist,
8 checkBadCountPagination, 5 checkBadCountPagination,
9 checkBadSortPagination, 6 checkBadSortPagination,
10 checkBadStartPagination, 7 checkBadStartPagination,
11 cleanupTests, 8 cleanupTests,
12 createVideoPlaylist, 9 createSingleServer,
13 deleteVideoPlaylist,
14 flushAndRunServer,
15 generateUserAccessToken,
16 getAccountPlaylistsListWithToken,
17 getVideoPlaylist,
18 immutableAssign,
19 makeGetRequest, 10 makeGetRequest,
20 removeVideoFromPlaylist, 11 PeerTubeServer,
21 reorderVideosPlaylist, 12 PlaylistsCommand,
22 ServerInfo,
23 setAccessTokensToServers, 13 setAccessTokensToServers,
24 setDefaultVideoChannel, 14 setDefaultVideoChannel
25 updateVideoPlaylist, 15} from '@shared/extra-utils'
26 updateVideoPlaylistElement, 16import {
27 uploadVideoAndGetId 17 HttpStatusCode,
28} from '../../../../shared/extra-utils' 18 VideoPlaylistCreate,
19 VideoPlaylistCreateResult,
20 VideoPlaylistElementCreate,
21 VideoPlaylistElementUpdate,
22 VideoPlaylistPrivacy,
23 VideoPlaylistReorder,
24 VideoPlaylistType
25} from '@shared/models'
29 26
30describe('Test video playlists API validator', function () { 27describe('Test video playlists API validator', function () {
31 let server: ServerInfo 28 let server: PeerTubeServer
32 let userAccessToken: string 29 let userAccessToken: string
33 30
34 let playlist: VideoPlaylistCreateResult 31 let playlist: VideoPlaylistCreateResult
@@ -36,49 +33,54 @@ describe('Test video playlists API validator', function () {
36 33
37 let watchLaterPlaylistId: number 34 let watchLaterPlaylistId: number
38 let videoId: number 35 let videoId: number
39 let playlistElementId: number 36 let elementId: number
37
38 let command: PlaylistsCommand
40 39
41 // --------------------------------------------------------------- 40 // ---------------------------------------------------------------
42 41
43 before(async function () { 42 before(async function () {
44 this.timeout(30000) 43 this.timeout(30000)
45 44
46 server = await flushAndRunServer(1) 45 server = await createSingleServer(1)
47 46
48 await setAccessTokensToServers([ server ]) 47 await setAccessTokensToServers([ server ])
49 await setDefaultVideoChannel([ server ]) 48 await setDefaultVideoChannel([ server ])
50 49
51 userAccessToken = await generateUserAccessToken(server, 'user1') 50 userAccessToken = await server.users.generateUserAndToken('user1')
52 videoId = (await uploadVideoAndGetId({ server, videoName: 'video 1' })).id 51 videoId = (await server.videos.quickUpload({ name: 'video 1' })).id
52
53 command = server.playlists
53 54
54 { 55 {
55 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER) 56 const { data } = await command.listByAccount({
56 watchLaterPlaylistId = res.body.data[0].id 57 token: server.accessToken,
58 handle: 'root',
59 start: 0,
60 count: 5,
61 playlistType: VideoPlaylistType.WATCH_LATER
62 })
63 watchLaterPlaylistId = data[0].id
57 } 64 }
58 65
59 { 66 {
60 const res = await createVideoPlaylist({ 67 playlist = await command.create({
61 url: server.url, 68 attributes: {
62 token: server.accessToken,
63 playlistAttrs: {
64 displayName: 'super playlist', 69 displayName: 'super playlist',
65 privacy: VideoPlaylistPrivacy.PUBLIC, 70 privacy: VideoPlaylistPrivacy.PUBLIC,
66 videoChannelId: server.videoChannel.id 71 videoChannelId: server.store.channel.id
67 } 72 }
68 }) 73 })
69 playlist = res.body.videoPlaylist
70 } 74 }
71 75
72 { 76 {
73 const res = await createVideoPlaylist({ 77 const created = await command.create({
74 url: server.url, 78 attributes: {
75 token: server.accessToken,
76 playlistAttrs: {
77 displayName: 'private', 79 displayName: 'private',
78 privacy: VideoPlaylistPrivacy.PRIVATE 80 privacy: VideoPlaylistPrivacy.PRIVATE
79 } 81 }
80 }) 82 })
81 privatePlaylistUUID = res.body.videoPlaylist.uuid 83 privatePlaylistUUID = created.uuid
82 } 84 }
83 }) 85 })
84 86
@@ -117,7 +119,7 @@ describe('Test video playlists API validator', function () {
117 await makeGetRequest({ 119 await makeGetRequest({
118 url: server.url, 120 url: server.url,
119 path: accountPath, 121 path: accountPath,
120 statusCodeExpected: HttpStatusCode.NOT_FOUND_404, 122 expectedStatus: HttpStatusCode.NOT_FOUND_404,
121 token: server.accessToken 123 token: server.accessToken
122 }) 124 })
123 }) 125 })
@@ -128,18 +130,18 @@ describe('Test video playlists API validator', function () {
128 await makeGetRequest({ 130 await makeGetRequest({
129 url: server.url, 131 url: server.url,
130 path: accountPath, 132 path: accountPath,
131 statusCodeExpected: HttpStatusCode.NOT_FOUND_404, 133 expectedStatus: HttpStatusCode.NOT_FOUND_404,
132 token: server.accessToken 134 token: server.accessToken
133 }) 135 })
134 }) 136 })
135 137
136 it('Should success with the correct parameters', async function () { 138 it('Should success with the correct parameters', async function () {
137 await makeGetRequest({ url: server.url, path: globalPath, statusCodeExpected: HttpStatusCode.OK_200, token: server.accessToken }) 139 await makeGetRequest({ url: server.url, path: globalPath, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken })
138 await makeGetRequest({ url: server.url, path: accountPath, statusCodeExpected: HttpStatusCode.OK_200, token: server.accessToken }) 140 await makeGetRequest({ url: server.url, path: accountPath, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken })
139 await makeGetRequest({ 141 await makeGetRequest({
140 url: server.url, 142 url: server.url,
141 path: videoChannelPath, 143 path: videoChannelPath,
142 statusCodeExpected: HttpStatusCode.OK_200, 144 expectedStatus: HttpStatusCode.OK_200,
143 token: server.accessToken 145 token: server.accessToken
144 }) 146 })
145 }) 147 })
@@ -157,141 +159,144 @@ describe('Test video playlists API validator', function () {
157 }) 159 })
158 160
159 it('Should success with the correct parameters', async function () { 161 it('Should success with the correct parameters', async function () {
160 await makeGetRequest({ url: server.url, path: path + playlist.shortUUID + '/videos', statusCodeExpected: HttpStatusCode.OK_200 }) 162 await makeGetRequest({ url: server.url, path: path + playlist.shortUUID + '/videos', expectedStatus: HttpStatusCode.OK_200 })
161 }) 163 })
162 }) 164 })
163 165
164 describe('When getting a video playlist', function () { 166 describe('When getting a video playlist', function () {
165 it('Should fail with a bad id or uuid', async function () { 167 it('Should fail with a bad id or uuid', async function () {
166 await getVideoPlaylist(server.url, 'toto', HttpStatusCode.BAD_REQUEST_400) 168 await command.get({ playlistId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
167 }) 169 })
168 170
169 it('Should fail with an unknown playlist', async function () { 171 it('Should fail with an unknown playlist', async function () {
170 await getVideoPlaylist(server.url, 42, HttpStatusCode.NOT_FOUND_404) 172 await command.get({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
171 }) 173 })
172 174
173 it('Should fail to get an unlisted playlist with the number id', async function () { 175 it('Should fail to get an unlisted playlist with the number id', async function () {
174 const res = await createVideoPlaylist({ 176 const playlist = await command.create({
175 url: server.url, 177 attributes: {
176 token: server.accessToken,
177 playlistAttrs: {
178 displayName: 'super playlist', 178 displayName: 'super playlist',
179 videoChannelId: server.videoChannel.id, 179 videoChannelId: server.store.channel.id,
180 privacy: VideoPlaylistPrivacy.UNLISTED 180 privacy: VideoPlaylistPrivacy.UNLISTED
181 } 181 }
182 }) 182 })
183 const playlist = res.body.videoPlaylist
184 183
185 await getVideoPlaylist(server.url, playlist.id, HttpStatusCode.NOT_FOUND_404) 184 await command.get({ playlistId: playlist.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
186 await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200) 185 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
187 }) 186 })
188 187
189 it('Should succeed with the correct params', async function () { 188 it('Should succeed with the correct params', async function () {
190 await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200) 189 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
191 }) 190 })
192 }) 191 })
193 192
194 describe('When creating/updating a video playlist', function () { 193 describe('When creating/updating a video playlist', function () {
195 const getBase = (playlistAttrs: any = {}, wrapper: any = {}) => { 194 const getBase = (
196 return Object.assign({ 195 attributes?: Partial<VideoPlaylistCreate>,
197 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 196 wrapper?: Partial<Parameters<PlaylistsCommand['create']>[0]>
198 url: server.url, 197 ) => {
199 token: server.accessToken, 198 return {
200 playlistAttrs: Object.assign({ 199 attributes: {
201 displayName: 'display name', 200 displayName: 'display name',
202 privacy: VideoPlaylistPrivacy.UNLISTED, 201 privacy: VideoPlaylistPrivacy.UNLISTED,
203 thumbnailfile: 'thumbnail.jpg', 202 thumbnailfile: 'thumbnail.jpg',
204 videoChannelId: server.videoChannel.id 203 videoChannelId: server.store.channel.id,
205 }, playlistAttrs) 204
206 }, wrapper) 205 ...attributes
206 },
207
208 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
209
210 ...wrapper
211 }
207 } 212 }
208 const getUpdate = (params: any, playlistId: number | string) => { 213 const getUpdate = (params: any, playlistId: number | string) => {
209 return immutableAssign(params, { playlistId: playlistId }) 214 return { ...params, playlistId: playlistId }
210 } 215 }
211 216
212 it('Should fail with an unauthenticated user', async function () { 217 it('Should fail with an unauthenticated user', async function () {
213 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 218 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
214 219
215 await createVideoPlaylist(params) 220 await command.create(params)
216 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 221 await command.update(getUpdate(params, playlist.shortUUID))
217 }) 222 })
218 223
219 it('Should fail without displayName', async function () { 224 it('Should fail without displayName', async function () {
220 const params = getBase({ displayName: undefined }) 225 const params = getBase({ displayName: undefined })
221 226
222 await createVideoPlaylist(params) 227 await command.create(params)
223 }) 228 })
224 229
225 it('Should fail with an incorrect display name', async function () { 230 it('Should fail with an incorrect display name', async function () {
226 const params = getBase({ displayName: 's'.repeat(300) }) 231 const params = getBase({ displayName: 's'.repeat(300) })
227 232
228 await createVideoPlaylist(params) 233 await command.create(params)
229 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 234 await command.update(getUpdate(params, playlist.shortUUID))
230 }) 235 })
231 236
232 it('Should fail with an incorrect description', async function () { 237 it('Should fail with an incorrect description', async function () {
233 const params = getBase({ description: 't' }) 238 const params = getBase({ description: 't' })
234 239
235 await createVideoPlaylist(params) 240 await command.create(params)
236 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 241 await command.update(getUpdate(params, playlist.shortUUID))
237 }) 242 })
238 243
239 it('Should fail with an incorrect privacy', async function () { 244 it('Should fail with an incorrect privacy', async function () {
240 const params = getBase({ privacy: 45 }) 245 const params = getBase({ privacy: 45 })
241 246
242 await createVideoPlaylist(params) 247 await command.create(params)
243 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 248 await command.update(getUpdate(params, playlist.shortUUID))
244 }) 249 })
245 250
246 it('Should fail with an unknown video channel id', async function () { 251 it('Should fail with an unknown video channel id', async function () {
247 const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 252 const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
248 253
249 await createVideoPlaylist(params) 254 await command.create(params)
250 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 255 await command.update(getUpdate(params, playlist.shortUUID))
251 }) 256 })
252 257
253 it('Should fail with an incorrect thumbnail file', async function () { 258 it('Should fail with an incorrect thumbnail file', async function () {
254 const params = getBase({ thumbnailfile: 'video_short.mp4' }) 259 const params = getBase({ thumbnailfile: 'video_short.mp4' })
255 260
256 await createVideoPlaylist(params) 261 await command.create(params)
257 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 262 await command.update(getUpdate(params, playlist.shortUUID))
258 }) 263 })
259 264
260 it('Should fail with a thumbnail file too big', async function () { 265 it('Should fail with a thumbnail file too big', async function () {
261 const params = getBase({ thumbnailfile: 'preview-big.png' }) 266 const params = getBase({ thumbnailfile: 'preview-big.png' })
262 267
263 await createVideoPlaylist(params) 268 await command.create(params)
264 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 269 await command.update(getUpdate(params, playlist.shortUUID))
265 }) 270 })
266 271
267 it('Should fail to set "public" a playlist not assigned to a channel', async function () { 272 it('Should fail to set "public" a playlist not assigned to a channel', async function () {
268 const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined }) 273 const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined })
269 const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' }) 274 const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' as any })
270 const params3 = getBase({ privacy: undefined, videoChannelId: 'null' }) 275 const params3 = getBase({ privacy: undefined, videoChannelId: 'null' as any })
271 276
272 await createVideoPlaylist(params) 277 await command.create(params)
273 await createVideoPlaylist(params2) 278 await command.create(params2)
274 await updateVideoPlaylist(getUpdate(params, privatePlaylistUUID)) 279 await command.update(getUpdate(params, privatePlaylistUUID))
275 await updateVideoPlaylist(getUpdate(params2, playlist.shortUUID)) 280 await command.update(getUpdate(params2, playlist.shortUUID))
276 await updateVideoPlaylist(getUpdate(params3, playlist.shortUUID)) 281 await command.update(getUpdate(params3, playlist.shortUUID))
277 }) 282 })
278 283
279 it('Should fail with an unknown playlist to update', async function () { 284 it('Should fail with an unknown playlist to update', async function () {
280 await updateVideoPlaylist(getUpdate( 285 await command.update(getUpdate(
281 getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }), 286 getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }),
282 42 287 42
283 )) 288 ))
284 }) 289 })
285 290
286 it('Should fail to update a playlist of another user', async function () { 291 it('Should fail to update a playlist of another user', async function () {
287 await updateVideoPlaylist(getUpdate( 292 await command.update(getUpdate(
288 getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }), 293 getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }),
289 playlist.shortUUID 294 playlist.shortUUID
290 )) 295 ))
291 }) 296 })
292 297
293 it('Should fail to update the watch later playlist', async function () { 298 it('Should fail to update the watch later playlist', async function () {
294 await updateVideoPlaylist(getUpdate( 299 await command.update(getUpdate(
295 getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }), 300 getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
296 watchLaterPlaylistId 301 watchLaterPlaylistId
297 )) 302 ))
@@ -300,146 +305,158 @@ describe('Test video playlists API validator', function () {
300 it('Should succeed with the correct params', async function () { 305 it('Should succeed with the correct params', async function () {
301 { 306 {
302 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 }) 307 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
303 await createVideoPlaylist(params) 308 await command.create(params)
304 } 309 }
305 310
306 { 311 {
307 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 312 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
308 await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) 313 await command.update(getUpdate(params, playlist.shortUUID))
309 } 314 }
310 }) 315 })
311 }) 316 })
312 317
313 describe('When adding an element in a playlist', function () { 318 describe('When adding an element in a playlist', function () {
314 const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { 319 const getBase = (
315 return Object.assign({ 320 attributes?: Partial<VideoPlaylistElementCreate>,
316 expectedStatus: HttpStatusCode.BAD_REQUEST_400, 321 wrapper?: Partial<Parameters<PlaylistsCommand['addElement']>[0]>
317 url: server.url, 322 ) => {
318 token: server.accessToken, 323 return {
319 playlistId: playlist.id, 324 attributes: {
320 elementAttrs: Object.assign({
321 videoId, 325 videoId,
322 startTimestamp: 2, 326 startTimestamp: 2,
323 stopTimestamp: 3 327 stopTimestamp: 3,
324 }, elementAttrs) 328
325 }, wrapper) 329 ...attributes
330 },
331
332 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
333 playlistId: playlist.id,
334
335 ...wrapper
336 }
326 } 337 }
327 338
328 it('Should fail with an unauthenticated user', async function () { 339 it('Should fail with an unauthenticated user', async function () {
329 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 340 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
330 await addVideoInPlaylist(params) 341 await command.addElement(params)
331 }) 342 })
332 343
333 it('Should fail with the playlist of another user', async function () { 344 it('Should fail with the playlist of another user', async function () {
334 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 345 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
335 await addVideoInPlaylist(params) 346 await command.addElement(params)
336 }) 347 })
337 348
338 it('Should fail with an unknown or incorrect playlist id', async function () { 349 it('Should fail with an unknown or incorrect playlist id', async function () {
339 { 350 {
340 const params = getBase({}, { playlistId: 'toto' }) 351 const params = getBase({}, { playlistId: 'toto' })
341 await addVideoInPlaylist(params) 352 await command.addElement(params)
342 } 353 }
343 354
344 { 355 {
345 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 356 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
346 await addVideoInPlaylist(params) 357 await command.addElement(params)
347 } 358 }
348 }) 359 })
349 360
350 it('Should fail with an unknown or incorrect video id', async function () { 361 it('Should fail with an unknown or incorrect video id', async function () {
351 const params = getBase({ videoId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 362 const params = getBase({ videoId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
352 await addVideoInPlaylist(params) 363 await command.addElement(params)
353 }) 364 })
354 365
355 it('Should fail with a bad start/stop timestamp', async function () { 366 it('Should fail with a bad start/stop timestamp', async function () {
356 { 367 {
357 const params = getBase({ startTimestamp: -42 }) 368 const params = getBase({ startTimestamp: -42 })
358 await addVideoInPlaylist(params) 369 await command.addElement(params)
359 } 370 }
360 371
361 { 372 {
362 const params = getBase({ stopTimestamp: 'toto' as any }) 373 const params = getBase({ stopTimestamp: 'toto' as any })
363 await addVideoInPlaylist(params) 374 await command.addElement(params)
364 } 375 }
365 }) 376 })
366 377
367 it('Succeed with the correct params', async function () { 378 it('Succeed with the correct params', async function () {
368 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 }) 379 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
369 const res = await addVideoInPlaylist(params) 380 const created = await command.addElement(params)
370 playlistElementId = res.body.videoPlaylistElement.id 381 elementId = created.id
371 }) 382 })
372 }) 383 })
373 384
374 describe('When updating an element in a playlist', function () { 385 describe('When updating an element in a playlist', function () {
375 const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { 386 const getBase = (
376 return Object.assign({ 387 attributes?: Partial<VideoPlaylistElementUpdate>,
377 url: server.url, 388 wrapper?: Partial<Parameters<PlaylistsCommand['updateElement']>[0]>
378 token: server.accessToken, 389 ) => {
379 elementAttrs: Object.assign({ 390 return {
391 attributes: {
380 startTimestamp: 1, 392 startTimestamp: 1,
381 stopTimestamp: 2 393 stopTimestamp: 2,
382 }, elementAttrs), 394
383 playlistElementId, 395 ...attributes
396 },
397
398 elementId,
384 playlistId: playlist.id, 399 playlistId: playlist.id,
385 expectedStatus: HttpStatusCode.BAD_REQUEST_400 400 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
386 }, wrapper) 401
402 ...wrapper
403 }
387 } 404 }
388 405
389 it('Should fail with an unauthenticated user', async function () { 406 it('Should fail with an unauthenticated user', async function () {
390 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 407 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
391 await updateVideoPlaylistElement(params) 408 await command.updateElement(params)
392 }) 409 })
393 410
394 it('Should fail with the playlist of another user', async function () { 411 it('Should fail with the playlist of another user', async function () {
395 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 412 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
396 await updateVideoPlaylistElement(params) 413 await command.updateElement(params)
397 }) 414 })
398 415
399 it('Should fail with an unknown or incorrect playlist id', async function () { 416 it('Should fail with an unknown or incorrect playlist id', async function () {
400 { 417 {
401 const params = getBase({}, { playlistId: 'toto' }) 418 const params = getBase({}, { playlistId: 'toto' })
402 await updateVideoPlaylistElement(params) 419 await command.updateElement(params)
403 } 420 }
404 421
405 { 422 {
406 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 423 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
407 await updateVideoPlaylistElement(params) 424 await command.updateElement(params)
408 } 425 }
409 }) 426 })
410 427
411 it('Should fail with an unknown or incorrect playlistElement id', async function () { 428 it('Should fail with an unknown or incorrect playlistElement id', async function () {
412 { 429 {
413 const params = getBase({}, { playlistElementId: 'toto' }) 430 const params = getBase({}, { elementId: 'toto' })
414 await updateVideoPlaylistElement(params) 431 await command.updateElement(params)
415 } 432 }
416 433
417 { 434 {
418 const params = getBase({}, { playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 435 const params = getBase({}, { elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
419 await updateVideoPlaylistElement(params) 436 await command.updateElement(params)
420 } 437 }
421 }) 438 })
422 439
423 it('Should fail with a bad start/stop timestamp', async function () { 440 it('Should fail with a bad start/stop timestamp', async function () {
424 { 441 {
425 const params = getBase({ startTimestamp: 'toto' as any }) 442 const params = getBase({ startTimestamp: 'toto' as any })
426 await updateVideoPlaylistElement(params) 443 await command.updateElement(params)
427 } 444 }
428 445
429 { 446 {
430 const params = getBase({ stopTimestamp: -42 }) 447 const params = getBase({ stopTimestamp: -42 })
431 await updateVideoPlaylistElement(params) 448 await command.updateElement(params)
432 } 449 }
433 }) 450 })
434 451
435 it('Should fail with an unknown element', async function () { 452 it('Should fail with an unknown element', async function () {
436 const params = getBase({}, { playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 453 const params = getBase({}, { elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
437 await updateVideoPlaylistElement(params) 454 await command.updateElement(params)
438 }) 455 })
439 456
440 it('Succeed with the correct params', async function () { 457 it('Succeed with the correct params', async function () {
441 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 458 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
442 await updateVideoPlaylistElement(params) 459 await command.updateElement(params)
443 }) 460 })
444 }) 461 })
445 462
@@ -447,110 +464,111 @@ describe('Test video playlists API validator', function () {
447 let videoId3: number 464 let videoId3: number
448 let videoId4: number 465 let videoId4: number
449 466
450 const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { 467 const getBase = (
451 return Object.assign({ 468 attributes?: Partial<VideoPlaylistReorder>,
452 url: server.url, 469 wrapper?: Partial<Parameters<PlaylistsCommand['reorderElements']>[0]>
453 token: server.accessToken, 470 ) => {
454 playlistId: playlist.shortUUID, 471 return {
455 elementAttrs: Object.assign({ 472 attributes: {
456 startPosition: 1, 473 startPosition: 1,
457 insertAfterPosition: 2, 474 insertAfterPosition: 2,
458 reorderLength: 3 475 reorderLength: 3,
459 }, elementAttrs), 476
460 expectedStatus: HttpStatusCode.BAD_REQUEST_400 477 ...attributes
461 }, wrapper) 478 },
479
480 playlistId: playlist.shortUUID,
481 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
482
483 ...wrapper
484 }
462 } 485 }
463 486
464 before(async function () { 487 before(async function () {
465 videoId3 = (await uploadVideoAndGetId({ server, videoName: 'video 3' })).id 488 videoId3 = (await server.videos.quickUpload({ name: 'video 3' })).id
466 videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id 489 videoId4 = (await server.videos.quickUpload({ name: 'video 4' })).id
467 490
468 for (const id of [ videoId3, videoId4 ]) { 491 for (const id of [ videoId3, videoId4 ]) {
469 await addVideoInPlaylist({ 492 await command.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: id } })
470 url: server.url,
471 token: server.accessToken,
472 playlistId: playlist.shortUUID,
473 elementAttrs: { videoId: id }
474 })
475 } 493 }
476 }) 494 })
477 495
478 it('Should fail with an unauthenticated user', async function () { 496 it('Should fail with an unauthenticated user', async function () {
479 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 497 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
480 await reorderVideosPlaylist(params) 498 await command.reorderElements(params)
481 }) 499 })
482 500
483 it('Should fail with the playlist of another user', async function () { 501 it('Should fail with the playlist of another user', async function () {
484 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 502 const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
485 await reorderVideosPlaylist(params) 503 await command.reorderElements(params)
486 }) 504 })
487 505
488 it('Should fail with an invalid playlist', async function () { 506 it('Should fail with an invalid playlist', async function () {
489 { 507 {
490 const params = getBase({}, { playlistId: 'toto' }) 508 const params = getBase({}, { playlistId: 'toto' })
491 await reorderVideosPlaylist(params) 509 await command.reorderElements(params)
492 } 510 }
493 511
494 { 512 {
495 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 513 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
496 await reorderVideosPlaylist(params) 514 await command.reorderElements(params)
497 } 515 }
498 }) 516 })
499 517
500 it('Should fail with an invalid start position', async function () { 518 it('Should fail with an invalid start position', async function () {
501 { 519 {
502 const params = getBase({ startPosition: -1 }) 520 const params = getBase({ startPosition: -1 })
503 await reorderVideosPlaylist(params) 521 await command.reorderElements(params)
504 } 522 }
505 523
506 { 524 {
507 const params = getBase({ startPosition: 'toto' as any }) 525 const params = getBase({ startPosition: 'toto' as any })
508 await reorderVideosPlaylist(params) 526 await command.reorderElements(params)
509 } 527 }
510 528
511 { 529 {
512 const params = getBase({ startPosition: 42 }) 530 const params = getBase({ startPosition: 42 })
513 await reorderVideosPlaylist(params) 531 await command.reorderElements(params)
514 } 532 }
515 }) 533 })
516 534
517 it('Should fail with an invalid insert after position', async function () { 535 it('Should fail with an invalid insert after position', async function () {
518 { 536 {
519 const params = getBase({ insertAfterPosition: 'toto' as any }) 537 const params = getBase({ insertAfterPosition: 'toto' as any })
520 await reorderVideosPlaylist(params) 538 await command.reorderElements(params)
521 } 539 }
522 540
523 { 541 {
524 const params = getBase({ insertAfterPosition: -2 }) 542 const params = getBase({ insertAfterPosition: -2 })
525 await reorderVideosPlaylist(params) 543 await command.reorderElements(params)
526 } 544 }
527 545
528 { 546 {
529 const params = getBase({ insertAfterPosition: 42 }) 547 const params = getBase({ insertAfterPosition: 42 })
530 await reorderVideosPlaylist(params) 548 await command.reorderElements(params)
531 } 549 }
532 }) 550 })
533 551
534 it('Should fail with an invalid reorder length', async function () { 552 it('Should fail with an invalid reorder length', async function () {
535 { 553 {
536 const params = getBase({ reorderLength: 'toto' as any }) 554 const params = getBase({ reorderLength: 'toto' as any })
537 await reorderVideosPlaylist(params) 555 await command.reorderElements(params)
538 } 556 }
539 557
540 { 558 {
541 const params = getBase({ reorderLength: -2 }) 559 const params = getBase({ reorderLength: -2 })
542 await reorderVideosPlaylist(params) 560 await command.reorderElements(params)
543 } 561 }
544 562
545 { 563 {
546 const params = getBase({ reorderLength: 42 }) 564 const params = getBase({ reorderLength: 42 })
547 await reorderVideosPlaylist(params) 565 await command.reorderElements(params)
548 } 566 }
549 }) 567 })
550 568
551 it('Succeed with the correct params', async function () { 569 it('Succeed with the correct params', async function () {
552 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 570 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
553 await reorderVideosPlaylist(params) 571 await command.reorderElements(params)
554 }) 572 })
555 }) 573 })
556 574
@@ -562,7 +580,7 @@ describe('Test video playlists API validator', function () {
562 url: server.url, 580 url: server.url,
563 path, 581 path,
564 query: { videoIds: [ 1, 2 ] }, 582 query: { videoIds: [ 1, 2 ] },
565 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 583 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
566 }) 584 })
567 }) 585 })
568 586
@@ -595,82 +613,82 @@ describe('Test video playlists API validator', function () {
595 token: server.accessToken, 613 token: server.accessToken,
596 path, 614 path,
597 query: { videoIds: [ 1, 2 ] }, 615 query: { videoIds: [ 1, 2 ] },
598 statusCodeExpected: HttpStatusCode.OK_200 616 expectedStatus: HttpStatusCode.OK_200
599 }) 617 })
600 }) 618 })
601 }) 619 })
602 620
603 describe('When deleting an element in a playlist', function () { 621 describe('When deleting an element in a playlist', function () {
604 const getBase = (wrapper: any = {}) => { 622 const getBase = (wrapper: Partial<Parameters<PlaylistsCommand['removeElement']>[0]>) => {
605 return Object.assign({ 623 return {
606 url: server.url, 624 elementId,
607 token: server.accessToken,
608 playlistElementId,
609 playlistId: playlist.uuid, 625 playlistId: playlist.uuid,
610 expectedStatus: HttpStatusCode.BAD_REQUEST_400 626 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
611 }, wrapper) 627
628 ...wrapper
629 }
612 } 630 }
613 631
614 it('Should fail with an unauthenticated user', async function () { 632 it('Should fail with an unauthenticated user', async function () {
615 const params = getBase({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 633 const params = getBase({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
616 await removeVideoFromPlaylist(params) 634 await command.removeElement(params)
617 }) 635 })
618 636
619 it('Should fail with the playlist of another user', async function () { 637 it('Should fail with the playlist of another user', async function () {
620 const params = getBase({ token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 638 const params = getBase({ token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
621 await removeVideoFromPlaylist(params) 639 await command.removeElement(params)
622 }) 640 })
623 641
624 it('Should fail with an unknown or incorrect playlist id', async function () { 642 it('Should fail with an unknown or incorrect playlist id', async function () {
625 { 643 {
626 const params = getBase({ playlistId: 'toto' }) 644 const params = getBase({ playlistId: 'toto' })
627 await removeVideoFromPlaylist(params) 645 await command.removeElement(params)
628 } 646 }
629 647
630 { 648 {
631 const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 649 const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
632 await removeVideoFromPlaylist(params) 650 await command.removeElement(params)
633 } 651 }
634 }) 652 })
635 653
636 it('Should fail with an unknown or incorrect video id', async function () { 654 it('Should fail with an unknown or incorrect video id', async function () {
637 { 655 {
638 const params = getBase({ playlistElementId: 'toto' }) 656 const params = getBase({ elementId: 'toto' as any })
639 await removeVideoFromPlaylist(params) 657 await command.removeElement(params)
640 } 658 }
641 659
642 { 660 {
643 const params = getBase({ playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 661 const params = getBase({ elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
644 await removeVideoFromPlaylist(params) 662 await command.removeElement(params)
645 } 663 }
646 }) 664 })
647 665
648 it('Should fail with an unknown element', async function () { 666 it('Should fail with an unknown element', async function () {
649 const params = getBase({ playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 667 const params = getBase({ elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
650 await removeVideoFromPlaylist(params) 668 await command.removeElement(params)
651 }) 669 })
652 670
653 it('Succeed with the correct params', async function () { 671 it('Succeed with the correct params', async function () {
654 const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 }) 672 const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 })
655 await removeVideoFromPlaylist(params) 673 await command.removeElement(params)
656 }) 674 })
657 }) 675 })
658 676
659 describe('When deleting a playlist', function () { 677 describe('When deleting a playlist', function () {
660 it('Should fail with an unknown playlist', async function () { 678 it('Should fail with an unknown playlist', async function () {
661 await deleteVideoPlaylist(server.url, server.accessToken, 42, HttpStatusCode.NOT_FOUND_404) 679 await command.delete({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
662 }) 680 })
663 681
664 it('Should fail with a playlist of another user', async function () { 682 it('Should fail with a playlist of another user', async function () {
665 await deleteVideoPlaylist(server.url, userAccessToken, playlist.uuid, HttpStatusCode.FORBIDDEN_403) 683 await command.delete({ token: userAccessToken, playlistId: playlist.uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
666 }) 684 })
667 685
668 it('Should fail with the watch later playlist', async function () { 686 it('Should fail with the watch later playlist', async function () {
669 await deleteVideoPlaylist(server.url, server.accessToken, watchLaterPlaylistId, HttpStatusCode.BAD_REQUEST_400) 687 await command.delete({ playlistId: watchLaterPlaylistId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
670 }) 688 })
671 689
672 it('Should succeed with the correct params', async function () { 690 it('Should succeed with the correct params', async function () {
673 await deleteVideoPlaylist(server.url, server.accessToken, playlist.uuid) 691 await command.delete({ playlistId: playlist.uuid })
674 }) 692 })
675 }) 693 })
676 694
diff --git a/server/tests/api/check-params/videos-filter.ts b/server/tests/api/check-params/videos-filter.ts
index 4d54a4fd0..d08570bbe 100644
--- a/server/tests/api/check-params/videos-filter.ts
+++ b/server/tests/api/check-params/videos-filter.ts
@@ -3,18 +3,15 @@
3import 'mocha' 3import 'mocha'
4import { 4import {
5 cleanupTests, 5 cleanupTests,
6 createUser, 6 createSingleServer,
7 flushAndRunServer,
8 makeGetRequest, 7 makeGetRequest,
9 ServerInfo, 8 PeerTubeServer,
10 setAccessTokensToServers, 9 setAccessTokensToServers,
11 setDefaultVideoChannel, 10 setDefaultVideoChannel
12 userLogin 11} from '@shared/extra-utils'
13} from '../../../../shared/extra-utils' 12import { HttpStatusCode, UserRole } from '@shared/models'
14import { UserRole } from '../../../../shared/models/users'
15import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
16 13
17async function testEndpoints (server: ServerInfo, token: string, filter: string, statusCodeExpected: HttpStatusCode) { 14async function testEndpoints (server: PeerTubeServer, token: string, filter: string, expectedStatus: HttpStatusCode) {
18 const paths = [ 15 const paths = [
19 '/api/v1/video-channels/root_channel/videos', 16 '/api/v1/video-channels/root_channel/videos',
20 '/api/v1/accounts/root/videos', 17 '/api/v1/accounts/root/videos',
@@ -30,13 +27,13 @@ async function testEndpoints (server: ServerInfo, token: string, filter: string,
30 query: { 27 query: {
31 filter 28 filter
32 }, 29 },
33 statusCodeExpected 30 expectedStatus
34 }) 31 })
35 } 32 }
36} 33}
37 34
38describe('Test video filters validators', function () { 35describe('Test video filters validators', function () {
39 let server: ServerInfo 36 let server: PeerTubeServer
40 let userAccessToken: string 37 let userAccessToken: string
41 let moderatorAccessToken: string 38 let moderatorAccessToken: string
42 39
@@ -45,28 +42,19 @@ describe('Test video filters validators', function () {
45 before(async function () { 42 before(async function () {
46 this.timeout(30000) 43 this.timeout(30000)
47 44
48 server = await flushAndRunServer(1) 45 server = await createSingleServer(1)
49 46
50 await setAccessTokensToServers([ server ]) 47 await setAccessTokensToServers([ server ])
51 await setDefaultVideoChannel([ server ]) 48 await setDefaultVideoChannel([ server ])
52 49
53 const user = { username: 'user1', password: 'my super password' } 50 const user = { username: 'user1', password: 'my super password' }
54 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 51 await server.users.create({ username: user.username, password: user.password })
55 userAccessToken = await userLogin(server, user) 52 userAccessToken = await server.login.getAccessToken(user)
56 53
57 const moderator = { username: 'moderator', password: 'my super password' } 54 const moderator = { username: 'moderator', password: 'my super password' }
58 await createUser( 55 await server.users.create({ username: moderator.username, password: moderator.password, role: UserRole.MODERATOR })
59 { 56
60 url: server.url, 57 moderatorAccessToken = await server.login.getAccessToken(moderator)
61 accessToken: server.accessToken,
62 username: moderator.username,
63 password: moderator.password,
64 videoQuota: undefined,
65 videoQuotaDaily: undefined,
66 role: UserRole.MODERATOR
67 }
68 )
69 moderatorAccessToken = await userLogin(server, moderator)
70 }) 58 })
71 59
72 describe('When setting a video filter', function () { 60 describe('When setting a video filter', function () {
@@ -100,7 +88,7 @@ describe('Test video filters validators', function () {
100 await makeGetRequest({ 88 await makeGetRequest({
101 url: server.url, 89 url: server.url,
102 path: '/feeds/videos.json', 90 path: '/feeds/videos.json',
103 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401, 91 expectedStatus: HttpStatusCode.UNAUTHORIZED_401,
104 query: { 92 query: {
105 filter 93 filter
106 } 94 }
@@ -112,7 +100,7 @@ describe('Test video filters validators', function () {
112 await makeGetRequest({ 100 await makeGetRequest({
113 url: server.url, 101 url: server.url,
114 path: '/feeds/videos.json', 102 path: '/feeds/videos.json',
115 statusCodeExpected: HttpStatusCode.OK_200, 103 expectedStatus: HttpStatusCode.OK_200,
116 query: { 104 query: {
117 filter: 'local' 105 filter: 'local'
118 } 106 }
diff --git a/server/tests/api/check-params/videos-history.ts b/server/tests/api/check-params/videos-history.ts
index 0e91fe0a8..c3c309ed2 100644
--- a/server/tests/api/check-params/videos-history.ts
+++ b/server/tests/api/check-params/videos-history.ts
@@ -5,42 +5,39 @@ import {
5 checkBadCountPagination, 5 checkBadCountPagination,
6 checkBadStartPagination, 6 checkBadStartPagination,
7 cleanupTests, 7 cleanupTests,
8 flushAndRunServer, 8 createSingleServer,
9 makeGetRequest, 9 makeGetRequest,
10 makePostBodyRequest, 10 makePostBodyRequest,
11 makePutBodyRequest, 11 makePutBodyRequest,
12 ServerInfo, 12 PeerTubeServer,
13 setAccessTokensToServers, 13 setAccessTokensToServers
14 uploadVideo 14} from '@shared/extra-utils'
15} from '../../../../shared/extra-utils' 15import { HttpStatusCode } from '@shared/models'
16import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
17 16
18describe('Test videos history API validator', function () { 17describe('Test videos history API validator', function () {
19 const myHistoryPath = '/api/v1/users/me/history/videos' 18 const myHistoryPath = '/api/v1/users/me/history/videos'
20 const myHistoryRemove = myHistoryPath + '/remove' 19 const myHistoryRemove = myHistoryPath + '/remove'
21 let watchingPath: string 20 let watchingPath: string
22 let server: ServerInfo 21 let server: PeerTubeServer
23 22
24 // --------------------------------------------------------------- 23 // ---------------------------------------------------------------
25 24
26 before(async function () { 25 before(async function () {
27 this.timeout(30000) 26 this.timeout(30000)
28 27
29 server = await flushAndRunServer(1) 28 server = await createSingleServer(1)
30 29
31 await setAccessTokensToServers([ server ]) 30 await setAccessTokensToServers([ server ])
32 31
33 const res = await uploadVideo(server.url, server.accessToken, {}) 32 const { uuid } = await server.videos.upload()
34 const videoUUID = res.body.video.uuid 33 watchingPath = '/api/v1/videos/' + uuid + '/watching'
35
36 watchingPath = '/api/v1/videos/' + videoUUID + '/watching'
37 }) 34 })
38 35
39 describe('When notifying a user is watching a video', function () { 36 describe('When notifying a user is watching a video', function () {
40 37
41 it('Should fail with an unauthenticated user', async function () { 38 it('Should fail with an unauthenticated user', async function () {
42 const fields = { currentTime: 5 } 39 const fields = { currentTime: 5 }
43 await makePutBodyRequest({ url: server.url, path: watchingPath, fields, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 40 await makePutBodyRequest({ url: server.url, path: watchingPath, fields, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
44 }) 41 })
45 42
46 it('Should fail with an incorrect video id', async function () { 43 it('Should fail with an incorrect video id', async function () {
@@ -51,7 +48,7 @@ describe('Test videos history API validator', function () {
51 path, 48 path,
52 fields, 49 fields,
53 token: server.accessToken, 50 token: server.accessToken,
54 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 51 expectedStatus: HttpStatusCode.BAD_REQUEST_400
55 }) 52 })
56 }) 53 })
57 54
@@ -64,7 +61,7 @@ describe('Test videos history API validator', function () {
64 path, 61 path,
65 fields, 62 fields,
66 token: server.accessToken, 63 token: server.accessToken,
67 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 64 expectedStatus: HttpStatusCode.NOT_FOUND_404
68 }) 65 })
69 }) 66 })
70 67
@@ -75,7 +72,7 @@ describe('Test videos history API validator', function () {
75 path: watchingPath, 72 path: watchingPath,
76 fields, 73 fields,
77 token: server.accessToken, 74 token: server.accessToken,
78 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 75 expectedStatus: HttpStatusCode.BAD_REQUEST_400
79 }) 76 })
80 }) 77 })
81 78
@@ -87,7 +84,7 @@ describe('Test videos history API validator', function () {
87 path: watchingPath, 84 path: watchingPath,
88 fields, 85 fields,
89 token: server.accessToken, 86 token: server.accessToken,
90 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 87 expectedStatus: HttpStatusCode.NO_CONTENT_204
91 }) 88 })
92 }) 89 })
93 }) 90 })
@@ -102,17 +99,17 @@ describe('Test videos history API validator', function () {
102 }) 99 })
103 100
104 it('Should fail with an unauthenticated user', async function () { 101 it('Should fail with an unauthenticated user', async function () {
105 await makeGetRequest({ url: server.url, path: myHistoryPath, statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 102 await makeGetRequest({ url: server.url, path: myHistoryPath, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
106 }) 103 })
107 104
108 it('Should succeed with the correct params', async function () { 105 it('Should succeed with the correct params', async function () {
109 await makeGetRequest({ url: server.url, token: server.accessToken, path: myHistoryPath, statusCodeExpected: HttpStatusCode.OK_200 }) 106 await makeGetRequest({ url: server.url, token: server.accessToken, path: myHistoryPath, expectedStatus: HttpStatusCode.OK_200 })
110 }) 107 })
111 }) 108 })
112 109
113 describe('When removing user videos history', function () { 110 describe('When removing user videos history', function () {
114 it('Should fail with an unauthenticated user', async function () { 111 it('Should fail with an unauthenticated user', async function () {
115 await makePostBodyRequest({ url: server.url, path: myHistoryPath + '/remove', statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 }) 112 await makePostBodyRequest({ url: server.url, path: myHistoryPath + '/remove', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
116 }) 113 })
117 114
118 it('Should fail with a bad beforeDate parameter', async function () { 115 it('Should fail with a bad beforeDate parameter', async function () {
@@ -122,7 +119,7 @@ describe('Test videos history API validator', function () {
122 token: server.accessToken, 119 token: server.accessToken,
123 path: myHistoryRemove, 120 path: myHistoryRemove,
124 fields: body, 121 fields: body,
125 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 122 expectedStatus: HttpStatusCode.BAD_REQUEST_400
126 }) 123 })
127 }) 124 })
128 125
@@ -133,7 +130,7 @@ describe('Test videos history API validator', function () {
133 token: server.accessToken, 130 token: server.accessToken,
134 path: myHistoryRemove, 131 path: myHistoryRemove,
135 fields: body, 132 fields: body,
136 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 133 expectedStatus: HttpStatusCode.NO_CONTENT_204
137 }) 134 })
138 }) 135 })
139 136
@@ -142,7 +139,7 @@ describe('Test videos history API validator', function () {
142 url: server.url, 139 url: server.url,
143 token: server.accessToken, 140 token: server.accessToken,
144 path: myHistoryRemove, 141 path: myHistoryRemove,
145 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 142 expectedStatus: HttpStatusCode.NO_CONTENT_204
146 }) 143 })
147 }) 144 })
148 }) 145 })
diff --git a/server/tests/api/check-params/videos-overviews.ts b/server/tests/api/check-params/videos-overviews.ts
index 69d7fc471..c2139d74b 100644
--- a/server/tests/api/check-params/videos-overviews.ts
+++ b/server/tests/api/check-params/videos-overviews.ts
@@ -1,29 +1,28 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../../shared/extra-utils' 4import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/extra-utils'
5import { getVideosOverview } from '@shared/extra-utils/overviews/overviews'
6 5
7describe('Test videos overview', function () { 6describe('Test videos overview', function () {
8 let server: ServerInfo 7 let server: PeerTubeServer
9 8
10 // --------------------------------------------------------------- 9 // ---------------------------------------------------------------
11 10
12 before(async function () { 11 before(async function () {
13 this.timeout(30000) 12 this.timeout(30000)
14 13
15 server = await flushAndRunServer(1) 14 server = await createSingleServer(1)
16 }) 15 })
17 16
18 describe('When getting videos overview', function () { 17 describe('When getting videos overview', function () {
19 18
20 it('Should fail with a bad pagination', async function () { 19 it('Should fail with a bad pagination', async function () {
21 await getVideosOverview(server.url, 0, 400) 20 await server.overviews.getVideos({ page: 0, expectedStatus: 400 })
22 await getVideosOverview(server.url, 100, 400) 21 await server.overviews.getVideos({ page: 100, expectedStatus: 400 })
23 }) 22 })
24 23
25 it('Should succeed with a good pagination', async function () { 24 it('Should succeed with a good pagination', async function () {
26 await getVideosOverview(server.url, 1) 25 await server.overviews.getVideos({ page: 1 })
27 }) 26 })
28 }) 27 })
29 28
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index 4d7a9a23b..e11ca0c82 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -5,39 +5,28 @@ import * as chai from 'chai'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { join } from 'path' 6import { join } from 'path'
7import { randomInt } from '@shared/core-utils' 7import { randomInt } from '@shared/core-utils'
8import { PeerTubeProblemDocument, VideoCreateResult } from '@shared/models'
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
10import { 8import {
9 checkBadCountPagination,
10 checkBadSortPagination,
11 checkBadStartPagination,
11 checkUploadVideoParam, 12 checkUploadVideoParam,
12 cleanupTests, 13 cleanupTests,
13 createUser, 14 createSingleServer,
14 flushAndRunServer,
15 getMyUserInformation,
16 getVideo,
17 getVideosList,
18 immutableAssign,
19 makeDeleteRequest, 15 makeDeleteRequest,
20 makeGetRequest, 16 makeGetRequest,
21 makePutBodyRequest, 17 makePutBodyRequest,
22 makeUploadRequest, 18 makeUploadRequest,
23 removeVideo, 19 PeerTubeServer,
24 root, 20 root,
25 ServerInfo, 21 setAccessTokensToServers
26 setAccessTokensToServers, 22} from '@shared/extra-utils'
27 userLogin 23import { HttpStatusCode, PeerTubeProblemDocument, VideoCreateResult, VideoPrivacy } from '@shared/models'
28} from '../../../../shared/extra-utils'
29import {
30 checkBadCountPagination,
31 checkBadSortPagination,
32 checkBadStartPagination
33} from '../../../../shared/extra-utils/requests/check-api-params'
34import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
35 24
36const expect = chai.expect 25const expect = chai.expect
37 26
38describe('Test videos API validator', function () { 27describe('Test videos API validator', function () {
39 const path = '/api/v1/videos/' 28 const path = '/api/v1/videos/'
40 let server: ServerInfo 29 let server: PeerTubeServer
41 let userAccessToken = '' 30 let userAccessToken = ''
42 let accountName: string 31 let accountName: string
43 let channelId: number 32 let channelId: number
@@ -49,20 +38,20 @@ describe('Test videos API validator', function () {
49 before(async function () { 38 before(async function () {
50 this.timeout(30000) 39 this.timeout(30000)
51 40
52 server = await flushAndRunServer(1) 41 server = await createSingleServer(1)
53 42
54 await setAccessTokensToServers([ server ]) 43 await setAccessTokensToServers([ server ])
55 44
56 const username = 'user1' 45 const username = 'user1'
57 const password = 'my super password' 46 const password = 'my super password'
58 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 47 await server.users.create({ username: username, password: password })
59 userAccessToken = await userLogin(server, { username, password }) 48 userAccessToken = await server.login.getAccessToken({ username, password })
60 49
61 { 50 {
62 const res = await getMyUserInformation(server.url, server.accessToken) 51 const body = await server.users.getMyInfo()
63 channelId = res.body.videoChannels[0].id 52 channelId = body.videoChannels[0].id
64 channelName = res.body.videoChannels[0].name 53 channelName = body.videoChannels[0].name
65 accountName = res.body.account.name + '@' + res.body.account.host 54 accountName = body.account.name + '@' + body.account.host
66 } 55 }
67 }) 56 })
68 57
@@ -80,11 +69,11 @@ describe('Test videos API validator', function () {
80 }) 69 })
81 70
82 it('Should fail with a bad skipVideos query', async function () { 71 it('Should fail with a bad skipVideos query', async function () {
83 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200, query: { skipCount: 'toto' } }) 72 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200, query: { skipCount: 'toto' } })
84 }) 73 })
85 74
86 it('Should success with the correct parameters', async function () { 75 it('Should success with the correct parameters', async function () {
87 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200, query: { skipCount: false } }) 76 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200, query: { skipCount: false } })
88 }) 77 })
89 }) 78 })
90 79
@@ -94,7 +83,7 @@ describe('Test videos API validator', function () {
94 await makeGetRequest({ 83 await makeGetRequest({
95 url: server.url, 84 url: server.url,
96 path: join(path, 'search'), 85 path: join(path, 'search'),
97 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 86 expectedStatus: HttpStatusCode.BAD_REQUEST_400
98 }) 87 })
99 }) 88 })
100 89
@@ -111,7 +100,7 @@ describe('Test videos API validator', function () {
111 }) 100 })
112 101
113 it('Should success with the correct parameters', async function () { 102 it('Should success with the correct parameters', async function () {
114 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200 }) 103 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200 })
115 }) 104 })
116 }) 105 })
117 106
@@ -131,7 +120,7 @@ describe('Test videos API validator', function () {
131 }) 120 })
132 121
133 it('Should success with the correct parameters', async function () { 122 it('Should success with the correct parameters', async function () {
134 await makeGetRequest({ url: server.url, token: server.accessToken, path, statusCodeExpected: HttpStatusCode.OK_200 }) 123 await makeGetRequest({ url: server.url, token: server.accessToken, path, expectedStatus: HttpStatusCode.OK_200 })
135 }) 124 })
136 }) 125 })
137 126
@@ -155,7 +144,7 @@ describe('Test videos API validator', function () {
155 }) 144 })
156 145
157 it('Should success with the correct parameters', async function () { 146 it('Should success with the correct parameters', async function () {
158 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200 }) 147 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200 })
159 }) 148 })
160 }) 149 })
161 150
@@ -179,7 +168,7 @@ describe('Test videos API validator', function () {
179 }) 168 })
180 169
181 it('Should success with the correct parameters', async function () { 170 it('Should success with the correct parameters', async function () {
182 await makeGetRequest({ url: server.url, path, statusCodeExpected: HttpStatusCode.OK_200 }) 171 await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200 })
183 }) 172 })
184 }) 173 })
185 174
@@ -214,70 +203,70 @@ describe('Test videos API validator', function () {
214 it('Should fail with nothing', async function () { 203 it('Should fail with nothing', async function () {
215 const fields = {} 204 const fields = {}
216 const attaches = {} 205 const attaches = {}
217 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 206 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
218 }) 207 })
219 208
220 it('Should fail without name', async function () { 209 it('Should fail without name', async function () {
221 const fields = omit(baseCorrectParams, 'name') 210 const fields = omit(baseCorrectParams, 'name')
222 const attaches = baseCorrectAttaches 211 const attaches = baseCorrectAttaches
223 212
224 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 213 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
225 }) 214 })
226 215
227 it('Should fail with a long name', async function () { 216 it('Should fail with a long name', async function () {
228 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 217 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
229 const attaches = baseCorrectAttaches 218 const attaches = baseCorrectAttaches
230 219
231 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 220 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
232 }) 221 })
233 222
234 it('Should fail with a bad category', async function () { 223 it('Should fail with a bad category', async function () {
235 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 224 const fields = { ...baseCorrectParams, category: 125 }
236 const attaches = baseCorrectAttaches 225 const attaches = baseCorrectAttaches
237 226
238 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 227 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
239 }) 228 })
240 229
241 it('Should fail with a bad licence', async function () { 230 it('Should fail with a bad licence', async function () {
242 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 231 const fields = { ...baseCorrectParams, licence: 125 }
243 const attaches = baseCorrectAttaches 232 const attaches = baseCorrectAttaches
244 233
245 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 234 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
246 }) 235 })
247 236
248 it('Should fail with a bad language', async function () { 237 it('Should fail with a bad language', async function () {
249 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 238 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
250 const attaches = baseCorrectAttaches 239 const attaches = baseCorrectAttaches
251 240
252 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 241 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
253 }) 242 })
254 243
255 it('Should fail with a long description', async function () { 244 it('Should fail with a long description', async function () {
256 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 245 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
257 const attaches = baseCorrectAttaches 246 const attaches = baseCorrectAttaches
258 247
259 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 248 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
260 }) 249 })
261 250
262 it('Should fail with a long support text', async function () { 251 it('Should fail with a long support text', async function () {
263 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 252 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
264 const attaches = baseCorrectAttaches 253 const attaches = baseCorrectAttaches
265 254
266 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 255 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
267 }) 256 })
268 257
269 it('Should fail without a channel', async function () { 258 it('Should fail without a channel', async function () {
270 const fields = omit(baseCorrectParams, 'channelId') 259 const fields = omit(baseCorrectParams, 'channelId')
271 const attaches = baseCorrectAttaches 260 const attaches = baseCorrectAttaches
272 261
273 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 262 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
274 }) 263 })
275 264
276 it('Should fail with a bad channel', async function () { 265 it('Should fail with a bad channel', async function () {
277 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 266 const fields = { ...baseCorrectParams, channelId: 545454 }
278 const attaches = baseCorrectAttaches 267 const attaches = baseCorrectAttaches
279 268
280 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 269 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
281 }) 270 })
282 271
283 it('Should fail with another user channel', async function () { 272 it('Should fail with another user channel', async function () {
@@ -285,69 +274,71 @@ describe('Test videos API validator', function () {
285 username: 'fake' + randomInt(0, 1500), 274 username: 'fake' + randomInt(0, 1500),
286 password: 'fake_password' 275 password: 'fake_password'
287 } 276 }
288 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 277 await server.users.create({ username: user.username, password: user.password })
289 278
290 const accessTokenUser = await userLogin(server, user) 279 const accessTokenUser = await server.login.getAccessToken(user)
291 const res = await getMyUserInformation(server.url, accessTokenUser) 280 const { videoChannels } = await server.users.getMyInfo({ token: accessTokenUser })
292 const customChannelId = res.body.videoChannels[0].id 281 const customChannelId = videoChannels[0].id
293 282
294 const fields = immutableAssign(baseCorrectParams, { channelId: customChannelId }) 283 const fields = { ...baseCorrectParams, channelId: customChannelId }
295 const attaches = baseCorrectAttaches 284 const attaches = baseCorrectAttaches
296 285
297 await checkUploadVideoParam(server.url, userAccessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 286 await checkUploadVideoParam(server, userAccessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
298 }) 287 })
299 288
300 it('Should fail with too many tags', async function () { 289 it('Should fail with too many tags', async function () {
301 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 290 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
302 const attaches = baseCorrectAttaches 291 const attaches = baseCorrectAttaches
303 292
304 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 293 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
305 }) 294 })
306 295
307 it('Should fail with a tag length too low', async function () { 296 it('Should fail with a tag length too low', async function () {
308 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 297 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
309 const attaches = baseCorrectAttaches 298 const attaches = baseCorrectAttaches
310 299
311 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 300 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
312 }) 301 })
313 302
314 it('Should fail with a tag length too big', async function () { 303 it('Should fail with a tag length too big', async function () {
315 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 304 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
316 const attaches = baseCorrectAttaches 305 const attaches = baseCorrectAttaches
317 306
318 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 307 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
319 }) 308 })
320 309
321 it('Should fail with a bad schedule update (miss updateAt)', async function () { 310 it('Should fail with a bad schedule update (miss updateAt)', async function () {
322 const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }) 311 const fields = { ...baseCorrectParams, scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }
323 const attaches = baseCorrectAttaches 312 const attaches = baseCorrectAttaches
324 313
325 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 314 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
326 }) 315 })
327 316
328 it('Should fail with a bad schedule update (wrong updateAt)', async function () { 317 it('Should fail with a bad schedule update (wrong updateAt)', async function () {
329 const fields = immutableAssign(baseCorrectParams, { 318 const fields = {
319 ...baseCorrectParams,
320
330 scheduleUpdate: { 321 scheduleUpdate: {
331 privacy: VideoPrivacy.PUBLIC, 322 privacy: VideoPrivacy.PUBLIC,
332 updateAt: 'toto' 323 updateAt: 'toto'
333 } 324 }
334 }) 325 }
335 const attaches = baseCorrectAttaches 326 const attaches = baseCorrectAttaches
336 327
337 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 328 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
338 }) 329 })
339 330
340 it('Should fail with a bad originally published at attribute', async function () { 331 it('Should fail with a bad originally published at attribute', async function () {
341 const fields = immutableAssign(baseCorrectParams, { originallyPublishedAt: 'toto' }) 332 const fields = { ...baseCorrectParams, originallyPublishedAt: 'toto' }
342 const attaches = baseCorrectAttaches 333 const attaches = baseCorrectAttaches
343 334
344 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 335 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
345 }) 336 })
346 337
347 it('Should fail without an input file', async function () { 338 it('Should fail without an input file', async function () {
348 const fields = baseCorrectParams 339 const fields = baseCorrectParams
349 const attaches = {} 340 const attaches = {}
350 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 341 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
351 }) 342 })
352 343
353 it('Should fail with an incorrect input file', async function () { 344 it('Should fail with an incorrect input file', async function () {
@@ -355,7 +346,7 @@ describe('Test videos API validator', function () {
355 let attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short_fake.webm') } 346 let attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short_fake.webm') }
356 347
357 await checkUploadVideoParam( 348 await checkUploadVideoParam(
358 server.url, 349 server,
359 server.accessToken, 350 server.accessToken,
360 { ...fields, ...attaches }, 351 { ...fields, ...attaches },
361 HttpStatusCode.UNPROCESSABLE_ENTITY_422, 352 HttpStatusCode.UNPROCESSABLE_ENTITY_422,
@@ -364,7 +355,7 @@ describe('Test videos API validator', function () {
364 355
365 attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mkv') } 356 attaches = { fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mkv') }
366 await checkUploadVideoParam( 357 await checkUploadVideoParam(
367 server.url, 358 server,
368 server.accessToken, 359 server.accessToken,
369 { ...fields, ...attaches }, 360 { ...fields, ...attaches },
370 HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415, 361 HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415,
@@ -379,7 +370,7 @@ describe('Test videos API validator', function () {
379 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 370 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
380 } 371 }
381 372
382 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 373 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
383 }) 374 })
384 375
385 it('Should fail with a big thumbnail file', async function () { 376 it('Should fail with a big thumbnail file', async function () {
@@ -389,7 +380,7 @@ describe('Test videos API validator', function () {
389 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 380 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
390 } 381 }
391 382
392 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 383 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
393 }) 384 })
394 385
395 it('Should fail with an incorrect preview file', async function () { 386 it('Should fail with an incorrect preview file', async function () {
@@ -399,7 +390,7 @@ describe('Test videos API validator', function () {
399 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 390 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
400 } 391 }
401 392
402 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 393 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
403 }) 394 })
404 395
405 it('Should fail with a big preview file', async function () { 396 it('Should fail with a big preview file', async function () {
@@ -409,17 +400,17 @@ describe('Test videos API validator', function () {
409 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 400 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
410 } 401 }
411 402
412 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode) 403 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.BAD_REQUEST_400, mode)
413 }) 404 })
414 405
415 it('Should report the appropriate error', async function () { 406 it('Should report the appropriate error', async function () {
416 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 407 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
417 const attaches = baseCorrectAttaches 408 const attaches = baseCorrectAttaches
418 409
419 const attributes = { ...fields, ...attaches } 410 const attributes = { ...fields, ...attaches }
420 const res = await checkUploadVideoParam(server.url, server.accessToken, attributes, HttpStatusCode.BAD_REQUEST_400, mode) 411 const body = await checkUploadVideoParam(server, server.accessToken, attributes, HttpStatusCode.BAD_REQUEST_400, mode)
421 412
422 const error = res.body as PeerTubeProblemDocument 413 const error = body as unknown as PeerTubeProblemDocument
423 414
424 if (mode === 'legacy') { 415 if (mode === 'legacy') {
425 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadLegacy') 416 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/uploadLegacy')
@@ -444,23 +435,27 @@ describe('Test videos API validator', function () {
444 435
445 { 436 {
446 const attaches = baseCorrectAttaches 437 const attaches = baseCorrectAttaches
447 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode) 438 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode)
448 } 439 }
449 440
450 { 441 {
451 const attaches = immutableAssign(baseCorrectAttaches, { 442 const attaches = {
443 ...baseCorrectAttaches,
444
452 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 445 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
453 }) 446 }
454 447
455 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode) 448 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode)
456 } 449 }
457 450
458 { 451 {
459 const attaches = immutableAssign(baseCorrectAttaches, { 452 const attaches = {
453 ...baseCorrectAttaches,
454
460 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.ogv') 455 videofile: join(root(), 'server', 'tests', 'fixtures', 'video_short.ogv')
461 }) 456 }
462 457
463 await checkUploadVideoParam(server.url, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode) 458 await checkUploadVideoParam(server, server.accessToken, { ...fields, ...attaches }, HttpStatusCode.OK_200, mode)
464 } 459 }
465 }) 460 })
466 } 461 }
@@ -489,8 +484,8 @@ describe('Test videos API validator', function () {
489 } 484 }
490 485
491 before(async function () { 486 before(async function () {
492 const res = await getVideosList(server.url) 487 const { data } = await server.videos.list()
493 video = res.body.data[0] 488 video = data[0]
494 }) 489 })
495 490
496 it('Should fail with nothing', async function () { 491 it('Should fail with nothing', async function () {
@@ -511,84 +506,84 @@ describe('Test videos API validator', function () {
511 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06', 506 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06',
512 token: server.accessToken, 507 token: server.accessToken,
513 fields, 508 fields,
514 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 509 expectedStatus: HttpStatusCode.NOT_FOUND_404
515 }) 510 })
516 }) 511 })
517 512
518 it('Should fail with a long name', async function () { 513 it('Should fail with a long name', async function () {
519 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(65) }) 514 const fields = { ...baseCorrectParams, name: 'super'.repeat(65) }
520 515
521 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 516 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
522 }) 517 })
523 518
524 it('Should fail with a bad category', async function () { 519 it('Should fail with a bad category', async function () {
525 const fields = immutableAssign(baseCorrectParams, { category: 125 }) 520 const fields = { ...baseCorrectParams, category: 125 }
526 521
527 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 522 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
528 }) 523 })
529 524
530 it('Should fail with a bad licence', async function () { 525 it('Should fail with a bad licence', async function () {
531 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 526 const fields = { ...baseCorrectParams, licence: 125 }
532 527
533 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 528 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
534 }) 529 })
535 530
536 it('Should fail with a bad language', async function () { 531 it('Should fail with a bad language', async function () {
537 const fields = immutableAssign(baseCorrectParams, { language: 'a'.repeat(15) }) 532 const fields = { ...baseCorrectParams, language: 'a'.repeat(15) }
538 533
539 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 534 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
540 }) 535 })
541 536
542 it('Should fail with a long description', async function () { 537 it('Should fail with a long description', async function () {
543 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) }) 538 const fields = { ...baseCorrectParams, description: 'super'.repeat(2500) }
544 539
545 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 540 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
546 }) 541 })
547 542
548 it('Should fail with a long support text', async function () { 543 it('Should fail with a long support text', async function () {
549 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(201) }) 544 const fields = { ...baseCorrectParams, support: 'super'.repeat(201) }
550 545
551 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 546 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
552 }) 547 })
553 548
554 it('Should fail with a bad channel', async function () { 549 it('Should fail with a bad channel', async function () {
555 const fields = immutableAssign(baseCorrectParams, { channelId: 545454 }) 550 const fields = { ...baseCorrectParams, channelId: 545454 }
556 551
557 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 552 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
558 }) 553 })
559 554
560 it('Should fail with too many tags', async function () { 555 it('Should fail with too many tags', async function () {
561 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }) 556 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6' ] }
562 557
563 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 558 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
564 }) 559 })
565 560
566 it('Should fail with a tag length too low', async function () { 561 it('Should fail with a tag length too low', async function () {
567 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 't' ] }) 562 const fields = { ...baseCorrectParams, tags: [ 'tag1', 't' ] }
568 563
569 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 564 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
570 }) 565 })
571 566
572 it('Should fail with a tag length too big', async function () { 567 it('Should fail with a tag length too big', async function () {
573 const fields = immutableAssign(baseCorrectParams, { tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }) 568 const fields = { ...baseCorrectParams, tags: [ 'tag1', 'my_super_tag_too_long_long_long_long_long_long' ] }
574 569
575 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 570 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
576 }) 571 })
577 572
578 it('Should fail with a bad schedule update (miss updateAt)', async function () { 573 it('Should fail with a bad schedule update (miss updateAt)', async function () {
579 const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }) 574 const fields = { ...baseCorrectParams, scheduleUpdate: { privacy: VideoPrivacy.PUBLIC } }
580 575
581 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 576 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
582 }) 577 })
583 578
584 it('Should fail with a bad schedule update (wrong updateAt)', async function () { 579 it('Should fail with a bad schedule update (wrong updateAt)', async function () {
585 const fields = immutableAssign(baseCorrectParams, { scheduleUpdate: { updateAt: 'toto', privacy: VideoPrivacy.PUBLIC } }) 580 const fields = { ...baseCorrectParams, scheduleUpdate: { updateAt: 'toto', privacy: VideoPrivacy.PUBLIC } }
586 581
587 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 582 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
588 }) 583 })
589 584
590 it('Should fail with a bad originally published at param', async function () { 585 it('Should fail with a bad originally published at param', async function () {
591 const fields = immutableAssign(baseCorrectParams, { originallyPublishedAt: 'toto' }) 586 const fields = { ...baseCorrectParams, originallyPublishedAt: 'toto' }
592 587
593 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 588 await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
594 }) 589 })
@@ -665,14 +660,14 @@ describe('Test videos API validator', function () {
665 path: path + video.shortUUID, 660 path: path + video.shortUUID,
666 token: userAccessToken, 661 token: userAccessToken,
667 fields, 662 fields,
668 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 663 expectedStatus: HttpStatusCode.FORBIDDEN_403
669 }) 664 })
670 }) 665 })
671 666
672 it('Should fail with a video of another server') 667 it('Should fail with a video of another server')
673 668
674 it('Shoud report the appropriate error', async function () { 669 it('Shoud report the appropriate error', async function () {
675 const fields = immutableAssign(baseCorrectParams, { licence: 125 }) 670 const fields = { ...baseCorrectParams, licence: 125 }
676 671
677 const res = await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields }) 672 const res = await makePutBodyRequest({ url: server.url, path: path + video.shortUUID, token: server.accessToken, fields })
678 const error = res.body as PeerTubeProblemDocument 673 const error = res.body as PeerTubeProblemDocument
@@ -697,7 +692,7 @@ describe('Test videos API validator', function () {
697 path: path + video.shortUUID, 692 path: path + video.shortUUID,
698 token: server.accessToken, 693 token: server.accessToken,
699 fields, 694 fields,
700 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 695 expectedStatus: HttpStatusCode.NO_CONTENT_204
701 }) 696 })
702 }) 697 })
703 }) 698 })
@@ -707,7 +702,7 @@ describe('Test videos API validator', function () {
707 const res = await makeGetRequest({ 702 const res = await makeGetRequest({
708 url: server.url, 703 url: server.url,
709 path, 704 path,
710 statusCodeExpected: HttpStatusCode.OK_200 705 expectedStatus: HttpStatusCode.OK_200
711 }) 706 })
712 707
713 expect(res.body.data).to.be.an('array') 708 expect(res.body.data).to.be.an('array')
@@ -715,16 +710,16 @@ describe('Test videos API validator', function () {
715 }) 710 })
716 711
717 it('Should fail without a correct uuid', async function () { 712 it('Should fail without a correct uuid', async function () {
718 await getVideo(server.url, 'coucou', HttpStatusCode.BAD_REQUEST_400) 713 await server.videos.get({ id: 'coucou', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
719 }) 714 })
720 715
721 it('Should return 404 with an incorrect video', async function () { 716 it('Should return 404 with an incorrect video', async function () {
722 await getVideo(server.url, '4da6fde3-88f7-4d16-b119-108df5630b06', HttpStatusCode.NOT_FOUND_404) 717 await server.videos.get({ id: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
723 }) 718 })
724 719
725 it('Shoud report the appropriate error', async function () { 720 it('Shoud report the appropriate error', async function () {
726 const res = await getVideo(server.url, 'hi', HttpStatusCode.BAD_REQUEST_400) 721 const body = await server.videos.get({ id: 'hi', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
727 const error = res.body as PeerTubeProblemDocument 722 const error = body as unknown as PeerTubeProblemDocument
728 723
729 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo') 724 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo')
730 725
@@ -739,16 +734,16 @@ describe('Test videos API validator', function () {
739 }) 734 })
740 735
741 it('Should succeed with the correct parameters', async function () { 736 it('Should succeed with the correct parameters', async function () {
742 await getVideo(server.url, video.shortUUID) 737 await server.videos.get({ id: video.shortUUID })
743 }) 738 })
744 }) 739 })
745 740
746 describe('When rating a video', function () { 741 describe('When rating a video', function () {
747 let videoId 742 let videoId: number
748 743
749 before(async function () { 744 before(async function () {
750 const res = await getVideosList(server.url) 745 const { data } = await server.videos.list()
751 videoId = res.body.data[0].id 746 videoId = data[0].id
752 }) 747 })
753 748
754 it('Should fail without a valid uuid', async function () { 749 it('Should fail without a valid uuid', async function () {
@@ -767,7 +762,7 @@ describe('Test videos API validator', function () {
767 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/rate', 762 path: path + '4da6fde3-88f7-4d16-b119-108df5630b06/rate',
768 token: server.accessToken, 763 token: server.accessToken,
769 fields, 764 fields,
770 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 765 expectedStatus: HttpStatusCode.NOT_FOUND_404
771 }) 766 })
772 }) 767 })
773 768
@@ -787,7 +782,7 @@ describe('Test videos API validator', function () {
787 path: path + videoId + '/rate', 782 path: path + videoId + '/rate',
788 token: server.accessToken, 783 token: server.accessToken,
789 fields, 784 fields,
790 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 785 expectedStatus: HttpStatusCode.NO_CONTENT_204
791 }) 786 })
792 }) 787 })
793 }) 788 })
@@ -797,27 +792,27 @@ describe('Test videos API validator', function () {
797 await makeDeleteRequest({ 792 await makeDeleteRequest({
798 url: server.url, 793 url: server.url,
799 path, 794 path,
800 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 795 expectedStatus: HttpStatusCode.BAD_REQUEST_400
801 }) 796 })
802 }) 797 })
803 798
804 it('Should fail without a correct uuid', async function () { 799 it('Should fail without a correct uuid', async function () {
805 await removeVideo(server.url, server.accessToken, 'hello', HttpStatusCode.BAD_REQUEST_400) 800 await server.videos.remove({ id: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
806 }) 801 })
807 802
808 it('Should fail with a video which does not exist', async function () { 803 it('Should fail with a video which does not exist', async function () {
809 await removeVideo(server.url, server.accessToken, '4da6fde3-88f7-4d16-b119-108df5630b06', HttpStatusCode.NOT_FOUND_404) 804 await server.videos.remove({ id: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
810 }) 805 })
811 806
812 it('Should fail with a video of another user without the appropriate right', async function () { 807 it('Should fail with a video of another user without the appropriate right', async function () {
813 await removeVideo(server.url, userAccessToken, video.uuid, HttpStatusCode.FORBIDDEN_403) 808 await server.videos.remove({ token: userAccessToken, id: video.uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
814 }) 809 })
815 810
816 it('Should fail with a video of another server') 811 it('Should fail with a video of another server')
817 812
818 it('Shoud report the appropriate error', async function () { 813 it('Shoud report the appropriate error', async function () {
819 const res = await removeVideo(server.url, server.accessToken, 'hello', HttpStatusCode.BAD_REQUEST_400) 814 const body = await server.videos.remove({ id: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
820 const error = res.body as PeerTubeProblemDocument 815 const error = body as PeerTubeProblemDocument
821 816
822 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/delVideo') 817 expect(error.docs).to.equal('https://docs.joinpeertube.org/api-rest-reference.html#operation/delVideo')
823 818
@@ -832,7 +827,7 @@ describe('Test videos API validator', function () {
832 }) 827 })
833 828
834 it('Should succeed with the correct parameters', async function () { 829 it('Should succeed with the correct parameters', async function () {
835 await removeVideo(server.url, server.accessToken, video.uuid) 830 await server.videos.remove({ id: video.uuid })
836 }) 831 })
837 }) 832 })
838 833
diff --git a/server/tests/api/live/live-constraints.ts b/server/tests/api/live/live-constraints.ts
index cc635de33..4acde3cc5 100644
--- a/server/tests/api/live/live-constraints.ts
+++ b/server/tests/api/live/live-constraints.ts
@@ -2,31 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoDetails, VideoPrivacy } from '@shared/models' 5import { VideoPrivacy } from '@shared/models'
6import { 6import {
7 checkLiveCleanup, 7 checkLiveCleanupAfterSave,
8 cleanupTests, 8 cleanupTests,
9 createLive, 9 ConfigCommand,
10 createMultipleServers,
10 doubleFollow, 11 doubleFollow,
11 flushAndRunMultipleServers, 12 PeerTubeServer,
12 generateUser,
13 getCustomConfigResolutions,
14 getVideo,
15 runAndTestFfmpegStreamError,
16 ServerInfo,
17 setAccessTokensToServers, 13 setAccessTokensToServers,
18 setDefaultVideoChannel, 14 setDefaultVideoChannel,
19 updateCustomSubConfig,
20 updateUser,
21 wait, 15 wait,
22 waitJobs, 16 waitJobs
23 waitUntilLivePublished
24} from '../../../../shared/extra-utils' 17} from '../../../../shared/extra-utils'
25 18
26const expect = chai.expect 19const expect = chai.expect
27 20
28describe('Test live constraints', function () { 21describe('Test live constraints', function () {
29 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
30 let userId: number 23 let userId: number
31 let userAccessToken: string 24 let userAccessToken: string
32 let userChannelId: number 25 let userChannelId: number
@@ -39,32 +32,28 @@ describe('Test live constraints', function () {
39 saveReplay 32 saveReplay
40 } 33 }
41 34
42 const res = await createLive(servers[0].url, userAccessToken, liveAttributes) 35 const { uuid } = await servers[0].live.create({ token: userAccessToken, fields: liveAttributes })
43 return res.body.video.uuid as string 36 return uuid
44 } 37 }
45 38
46 async function checkSaveReplay (videoId: string, resolutions = [ 720 ]) { 39 async function checkSaveReplay (videoId: string, resolutions = [ 720 ]) {
47 for (const server of servers) { 40 for (const server of servers) {
48 const res = await getVideo(server.url, videoId) 41 const video = await server.videos.get({ id: videoId })
49
50 const video: VideoDetails = res.body
51 expect(video.isLive).to.be.false 42 expect(video.isLive).to.be.false
52 expect(video.duration).to.be.greaterThan(0) 43 expect(video.duration).to.be.greaterThan(0)
53 } 44 }
54 45
55 await checkLiveCleanup(servers[0], videoId, resolutions) 46 await checkLiveCleanupAfterSave(servers[0], videoId, resolutions)
56 } 47 }
57 48
58 async function waitUntilLivePublishedOnAllServers (videoId: string) { 49 async function waitUntilLivePublishedOnAllServers (videoId: string) {
59 for (const server of servers) { 50 for (const server of servers) {
60 await waitUntilLivePublished(server.url, server.accessToken, videoId) 51 await server.live.waitUntilPublished({ videoId })
61 } 52 }
62 } 53 }
63 54
64 function updateQuota (options: { total: number, daily: number }) { 55 function updateQuota (options: { total: number, daily: number }) {
65 return updateUser({ 56 return servers[0].users.update({
66 url: servers[0].url,
67 accessToken: servers[0].accessToken,
68 userId, 57 userId,
69 videoQuota: options.total, 58 videoQuota: options.total,
70 videoQuotaDaily: options.daily 59 videoQuotaDaily: options.daily
@@ -74,24 +63,26 @@ describe('Test live constraints', function () {
74 before(async function () { 63 before(async function () {
75 this.timeout(120000) 64 this.timeout(120000)
76 65
77 servers = await flushAndRunMultipleServers(2) 66 servers = await createMultipleServers(2)
78 67
79 // Get the access tokens 68 // Get the access tokens
80 await setAccessTokensToServers(servers) 69 await setAccessTokensToServers(servers)
81 await setDefaultVideoChannel(servers) 70 await setDefaultVideoChannel(servers)
82 71
83 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 72 await servers[0].config.updateCustomSubConfig({
84 live: { 73 newConfig: {
85 enabled: true, 74 live: {
86 allowReplay: true, 75 enabled: true,
87 transcoding: { 76 allowReplay: true,
88 enabled: false 77 transcoding: {
78 enabled: false
79 }
89 } 80 }
90 } 81 }
91 }) 82 })
92 83
93 { 84 {
94 const res = await generateUser(servers[0], 'user1') 85 const res = await servers[0].users.generate('user1')
95 userId = res.userId 86 userId = res.userId
96 userChannelId = res.userChannelId 87 userChannelId = res.userChannelId
97 userAccessToken = res.token 88 userAccessToken = res.token
@@ -107,7 +98,7 @@ describe('Test live constraints', function () {
107 this.timeout(60000) 98 this.timeout(60000)
108 99
109 const userVideoLiveoId = await createLiveWrapper(false) 100 const userVideoLiveoId = await createLiveWrapper(false)
110 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) 101 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
111 }) 102 })
112 103
113 it('Should have size limit depending on user global quota if save replay is enabled', async function () { 104 it('Should have size limit depending on user global quota if save replay is enabled', async function () {
@@ -117,7 +108,7 @@ describe('Test live constraints', function () {
117 await wait(5000) 108 await wait(5000)
118 109
119 const userVideoLiveoId = await createLiveWrapper(true) 110 const userVideoLiveoId = await createLiveWrapper(true)
120 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 111 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
121 112
122 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 113 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
123 await waitJobs(servers) 114 await waitJobs(servers)
@@ -134,7 +125,7 @@ describe('Test live constraints', function () {
134 await updateQuota({ total: -1, daily: 1 }) 125 await updateQuota({ total: -1, daily: 1 })
135 126
136 const userVideoLiveoId = await createLiveWrapper(true) 127 const userVideoLiveoId = await createLiveWrapper(true)
137 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 128 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
138 129
139 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 130 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
140 await waitJobs(servers) 131 await waitJobs(servers)
@@ -151,26 +142,28 @@ describe('Test live constraints', function () {
151 await updateQuota({ total: 10 * 1000 * 1000, daily: -1 }) 142 await updateQuota({ total: 10 * 1000 * 1000, daily: -1 })
152 143
153 const userVideoLiveoId = await createLiveWrapper(true) 144 const userVideoLiveoId = await createLiveWrapper(true)
154 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) 145 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
155 }) 146 })
156 147
157 it('Should have max duration limit', async function () { 148 it('Should have max duration limit', async function () {
158 this.timeout(60000) 149 this.timeout(60000)
159 150
160 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 151 await servers[0].config.updateCustomSubConfig({
161 live: { 152 newConfig: {
162 enabled: true, 153 live: {
163 allowReplay: true,
164 maxDuration: 1,
165 transcoding: {
166 enabled: true, 154 enabled: true,
167 resolutions: getCustomConfigResolutions(true) 155 allowReplay: true,
156 maxDuration: 1,
157 transcoding: {
158 enabled: true,
159 resolutions: ConfigCommand.getCustomConfigResolutions(true)
160 }
168 } 161 }
169 } 162 }
170 }) 163 })
171 164
172 const userVideoLiveoId = await createLiveWrapper(true) 165 const userVideoLiveoId = await createLiveWrapper(true)
173 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 166 await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
174 167
175 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 168 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
176 await waitJobs(servers) 169 await waitJobs(servers)
diff --git a/server/tests/api/live/live-permanent.ts b/server/tests/api/live/live-permanent.ts
index 71b7d28a8..f07d4cfec 100644
--- a/server/tests/api/live/live-permanent.ts
+++ b/server/tests/api/live/live-permanent.ts
@@ -2,59 +2,50 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { LiveVideoCreate, VideoDetails, VideoPrivacy, VideoState } from '@shared/models' 5import { LiveVideoCreate, VideoPrivacy, VideoState } from '@shared/models'
6import { 6import {
7 cleanupTests, 7 cleanupTests,
8 createLive, 8 ConfigCommand,
9 createMultipleServers,
9 doubleFollow, 10 doubleFollow,
10 flushAndRunMultipleServers, 11 PeerTubeServer,
11 getCustomConfigResolutions,
12 getLive,
13 getPlaylistsCount,
14 getVideo,
15 sendRTMPStreamInVideo,
16 ServerInfo,
17 setAccessTokensToServers, 12 setAccessTokensToServers,
18 setDefaultVideoChannel, 13 setDefaultVideoChannel,
19 stopFfmpeg, 14 stopFfmpeg,
20 updateCustomSubConfig,
21 updateLive,
22 wait, 15 wait,
23 waitJobs, 16 waitJobs
24 waitUntilLivePublished,
25 waitUntilLiveWaiting
26} from '../../../../shared/extra-utils' 17} from '../../../../shared/extra-utils'
27 18
28const expect = chai.expect 19const expect = chai.expect
29 20
30describe('Permanent live', function () { 21describe('Permanent live', function () {
31 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
32 let videoUUID: string 23 let videoUUID: string
33 24
34 async function createLiveWrapper (permanentLive: boolean) { 25 async function createLiveWrapper (permanentLive: boolean) {
35 const attributes: LiveVideoCreate = { 26 const attributes: LiveVideoCreate = {
36 channelId: servers[0].videoChannel.id, 27 channelId: servers[0].store.channel.id,
37 privacy: VideoPrivacy.PUBLIC, 28 privacy: VideoPrivacy.PUBLIC,
38 name: 'my super live', 29 name: 'my super live',
39 saveReplay: false, 30 saveReplay: false,
40 permanentLive 31 permanentLive
41 } 32 }
42 33
43 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 34 const { uuid } = await servers[0].live.create({ fields: attributes })
44 return res.body.video.uuid 35 return uuid
45 } 36 }
46 37
47 async function checkVideoState (videoId: string, state: VideoState) { 38 async function checkVideoState (videoId: string, state: VideoState) {
48 for (const server of servers) { 39 for (const server of servers) {
49 const res = await getVideo(server.url, videoId) 40 const video = await server.videos.get({ id: videoId })
50 expect((res.body as VideoDetails).state.id).to.equal(state) 41 expect(video.state.id).to.equal(state)
51 } 42 }
52 } 43 }
53 44
54 before(async function () { 45 before(async function () {
55 this.timeout(120000) 46 this.timeout(120000)
56 47
57 servers = await flushAndRunMultipleServers(2) 48 servers = await createMultipleServers(2)
58 49
59 // Get the access tokens 50 // Get the access tokens
60 await setAccessTokensToServers(servers) 51 await setAccessTokensToServers(servers)
@@ -63,14 +54,16 @@ describe('Permanent live', function () {
63 // Server 1 and server 2 follow each other 54 // Server 1 and server 2 follow each other
64 await doubleFollow(servers[0], servers[1]) 55 await doubleFollow(servers[0], servers[1])
65 56
66 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 57 await servers[0].config.updateCustomSubConfig({
67 live: { 58 newConfig: {
68 enabled: true, 59 live: {
69 allowReplay: true,
70 maxDuration: -1,
71 transcoding: {
72 enabled: true, 60 enabled: true,
73 resolutions: getCustomConfigResolutions(true) 61 allowReplay: true,
62 maxDuration: -1,
63 transcoding: {
64 enabled: true,
65 resolutions: ConfigCommand.getCustomConfigResolutions(true)
66 }
74 } 67 }
75 } 68 }
76 }) 69 })
@@ -82,15 +75,15 @@ describe('Permanent live', function () {
82 const videoUUID = await createLiveWrapper(false) 75 const videoUUID = await createLiveWrapper(false)
83 76
84 { 77 {
85 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 78 const live = await servers[0].live.get({ videoId: videoUUID })
86 expect(res.body.permanentLive).to.be.false 79 expect(live.permanentLive).to.be.false
87 } 80 }
88 81
89 await updateLive(servers[0].url, servers[0].accessToken, videoUUID, { permanentLive: true }) 82 await servers[0].live.update({ videoId: videoUUID, fields: { permanentLive: true } })
90 83
91 { 84 {
92 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 85 const live = await servers[0].live.get({ videoId: videoUUID })
93 expect(res.body.permanentLive).to.be.true 86 expect(live.permanentLive).to.be.true
94 } 87 }
95 }) 88 })
96 89
@@ -99,8 +92,8 @@ describe('Permanent live', function () {
99 92
100 videoUUID = await createLiveWrapper(true) 93 videoUUID = await createLiveWrapper(true)
101 94
102 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 95 const live = await servers[0].live.get({ videoId: videoUUID })
103 expect(res.body.permanentLive).to.be.true 96 expect(live.permanentLive).to.be.true
104 97
105 await waitJobs(servers) 98 await waitJobs(servers)
106 }) 99 })
@@ -108,16 +101,16 @@ describe('Permanent live', function () {
108 it('Should stream into this permanent live', async function () { 101 it('Should stream into this permanent live', async function () {
109 this.timeout(120000) 102 this.timeout(120000)
110 103
111 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, videoUUID) 104 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUID })
112 105
113 for (const server of servers) { 106 for (const server of servers) {
114 await waitUntilLivePublished(server.url, server.accessToken, videoUUID) 107 await server.live.waitUntilPublished({ videoId: videoUUID })
115 } 108 }
116 109
117 await checkVideoState(videoUUID, VideoState.PUBLISHED) 110 await checkVideoState(videoUUID, VideoState.PUBLISHED)
118 111
119 await stopFfmpeg(command) 112 await stopFfmpeg(ffmpegCommand)
120 await waitUntilLiveWaiting(servers[0].url, servers[0].accessToken, videoUUID) 113 await servers[0].live.waitUntilWaiting({ videoId: videoUUID })
121 114
122 await waitJobs(servers) 115 await waitJobs(servers)
123 }) 116 })
@@ -129,9 +122,7 @@ describe('Permanent live', function () {
129 await waitJobs(servers) 122 await waitJobs(servers)
130 123
131 for (const server of servers) { 124 for (const server of servers) {
132 const res = await getVideo(server.url, videoUUID) 125 const videoDetails = await server.videos.get({ id: videoUUID })
133
134 const videoDetails = res.body as VideoDetails
135 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) 126 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
136 } 127 }
137 }) 128 })
@@ -145,31 +136,33 @@ describe('Permanent live', function () {
145 it('Should be able to stream again in the permanent live', async function () { 136 it('Should be able to stream again in the permanent live', async function () {
146 this.timeout(20000) 137 this.timeout(20000)
147 138
148 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 139 await servers[0].config.updateCustomSubConfig({
149 live: { 140 newConfig: {
150 enabled: true, 141 live: {
151 allowReplay: true,
152 maxDuration: -1,
153 transcoding: {
154 enabled: true, 142 enabled: true,
155 resolutions: getCustomConfigResolutions(false) 143 allowReplay: true,
144 maxDuration: -1,
145 transcoding: {
146 enabled: true,
147 resolutions: ConfigCommand.getCustomConfigResolutions(false)
148 }
156 } 149 }
157 } 150 }
158 }) 151 })
159 152
160 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, videoUUID) 153 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUID })
161 154
162 for (const server of servers) { 155 for (const server of servers) {
163 await waitUntilLivePublished(server.url, server.accessToken, videoUUID) 156 await server.live.waitUntilPublished({ videoId: videoUUID })
164 } 157 }
165 158
166 await checkVideoState(videoUUID, VideoState.PUBLISHED) 159 await checkVideoState(videoUUID, VideoState.PUBLISHED)
167 160
168 const count = await getPlaylistsCount(servers[0], videoUUID) 161 const count = await servers[0].live.countPlaylists({ videoUUID })
169 // master playlist and 720p playlist 162 // master playlist and 720p playlist
170 expect(count).to.equal(2) 163 expect(count).to.equal(2)
171 164
172 await stopFfmpeg(command) 165 await stopFfmpeg(ffmpegCommand)
173 }) 166 })
174 167
175 after(async function () { 168 after(async function () {
diff --git a/server/tests/api/live/live-save-replay.ts b/server/tests/api/live/live-save-replay.ts
index 3d4736c8f..8f1fb78a5 100644
--- a/server/tests/api/live/live-save-replay.ts
+++ b/server/tests/api/live/live-save-replay.ts
@@ -3,97 +3,85 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { FfmpegCommand } from 'fluent-ffmpeg' 5import { FfmpegCommand } from 'fluent-ffmpeg'
6import { LiveVideoCreate, VideoDetails, VideoPrivacy, VideoState } from '@shared/models'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
8import { 6import {
9 addVideoToBlacklist, 7 checkLiveCleanupAfterSave,
10 checkLiveCleanup,
11 cleanupTests, 8 cleanupTests,
12 createLive, 9 ConfigCommand,
10 createMultipleServers,
13 doubleFollow, 11 doubleFollow,
14 flushAndRunMultipleServers, 12 PeerTubeServer,
15 getCustomConfigResolutions,
16 getVideo,
17 getVideosList,
18 removeVideo,
19 sendRTMPStreamInVideo,
20 ServerInfo,
21 setAccessTokensToServers, 13 setAccessTokensToServers,
22 setDefaultVideoChannel, 14 setDefaultVideoChannel,
23 stopFfmpeg, 15 stopFfmpeg,
24 testFfmpegStreamError, 16 testFfmpegStreamError,
25 updateCustomSubConfig,
26 updateVideo,
27 wait, 17 wait,
28 waitJobs, 18 waitJobs
29 waitUntilLiveEnded, 19} from '@shared/extra-utils'
30 waitUntilLivePublished, 20import { HttpStatusCode, LiveVideoCreate, VideoPrivacy, VideoState } from '@shared/models'
31 waitUntilLiveSaved
32} from '../../../../shared/extra-utils'
33 21
34const expect = chai.expect 22const expect = chai.expect
35 23
36describe('Save replay setting', function () { 24describe('Save replay setting', function () {
37 let servers: ServerInfo[] = [] 25 let servers: PeerTubeServer[] = []
38 let liveVideoUUID: string 26 let liveVideoUUID: string
39 let ffmpegCommand: FfmpegCommand 27 let ffmpegCommand: FfmpegCommand
40 28
41 async function createLiveWrapper (saveReplay: boolean) { 29 async function createLiveWrapper (saveReplay: boolean) {
42 if (liveVideoUUID) { 30 if (liveVideoUUID) {
43 try { 31 try {
44 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 32 await servers[0].videos.remove({ id: liveVideoUUID })
45 await waitJobs(servers) 33 await waitJobs(servers)
46 } catch {} 34 } catch {}
47 } 35 }
48 36
49 const attributes: LiveVideoCreate = { 37 const attributes: LiveVideoCreate = {
50 channelId: servers[0].videoChannel.id, 38 channelId: servers[0].store.channel.id,
51 privacy: VideoPrivacy.PUBLIC, 39 privacy: VideoPrivacy.PUBLIC,
52 name: 'my super live', 40 name: 'my super live',
53 saveReplay 41 saveReplay
54 } 42 }
55 43
56 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 44 const { uuid } = await servers[0].live.create({ fields: attributes })
57 return res.body.video.uuid 45 return uuid
58 } 46 }
59 47
60 async function checkVideosExist (videoId: string, existsInList: boolean, getStatus?: number) { 48 async function checkVideosExist (videoId: string, existsInList: boolean, expectedStatus?: number) {
61 for (const server of servers) { 49 for (const server of servers) {
62 const length = existsInList ? 1 : 0 50 const length = existsInList ? 1 : 0
63 51
64 const resVideos = await getVideosList(server.url) 52 const { data, total } = await server.videos.list()
65 expect(resVideos.body.data).to.have.lengthOf(length) 53 expect(data).to.have.lengthOf(length)
66 expect(resVideos.body.total).to.equal(length) 54 expect(total).to.equal(length)
67 55
68 if (getStatus) { 56 if (expectedStatus) {
69 await getVideo(server.url, videoId, getStatus) 57 await server.videos.get({ id: videoId, expectedStatus })
70 } 58 }
71 } 59 }
72 } 60 }
73 61
74 async function checkVideoState (videoId: string, state: VideoState) { 62 async function checkVideoState (videoId: string, state: VideoState) {
75 for (const server of servers) { 63 for (const server of servers) {
76 const res = await getVideo(server.url, videoId) 64 const video = await server.videos.get({ id: videoId })
77 expect((res.body as VideoDetails).state.id).to.equal(state) 65 expect(video.state.id).to.equal(state)
78 } 66 }
79 } 67 }
80 68
81 async function waitUntilLivePublishedOnAllServers (videoId: string) { 69 async function waitUntilLivePublishedOnAllServers (videoId: string) {
82 for (const server of servers) { 70 for (const server of servers) {
83 await waitUntilLivePublished(server.url, server.accessToken, videoId) 71 await server.live.waitUntilPublished({ videoId })
84 } 72 }
85 } 73 }
86 74
87 async function waitUntilLiveSavedOnAllServers (videoId: string) { 75 async function waitUntilLiveSavedOnAllServers (videoId: string) {
88 for (const server of servers) { 76 for (const server of servers) {
89 await waitUntilLiveSaved(server.url, server.accessToken, videoId) 77 await server.live.waitUntilSaved({ videoId })
90 } 78 }
91 } 79 }
92 80
93 before(async function () { 81 before(async function () {
94 this.timeout(120000) 82 this.timeout(120000)
95 83
96 servers = await flushAndRunMultipleServers(2) 84 servers = await createMultipleServers(2)
97 85
98 // Get the access tokens 86 // Get the access tokens
99 await setAccessTokensToServers(servers) 87 await setAccessTokensToServers(servers)
@@ -102,14 +90,16 @@ describe('Save replay setting', function () {
102 // Server 1 and server 2 follow each other 90 // Server 1 and server 2 follow each other
103 await doubleFollow(servers[0], servers[1]) 91 await doubleFollow(servers[0], servers[1])
104 92
105 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 93 await servers[0].config.updateCustomSubConfig({
106 live: { 94 newConfig: {
107 enabled: true, 95 live: {
108 allowReplay: true, 96 enabled: true,
109 maxDuration: -1, 97 allowReplay: true,
110 transcoding: { 98 maxDuration: -1,
111 enabled: false, 99 transcoding: {
112 resolutions: getCustomConfigResolutions(true) 100 enabled: false,
101 resolutions: ConfigCommand.getCustomConfigResolutions(true)
102 }
113 } 103 }
114 } 104 }
115 }) 105 })
@@ -135,7 +125,7 @@ describe('Save replay setting', function () {
135 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 125 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
136 this.timeout(30000) 126 this.timeout(30000)
137 127
138 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 128 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
139 129
140 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 130 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
141 131
@@ -151,7 +141,7 @@ describe('Save replay setting', function () {
151 await stopFfmpeg(ffmpegCommand) 141 await stopFfmpeg(ffmpegCommand)
152 142
153 for (const server of servers) { 143 for (const server of servers) {
154 await waitUntilLiveEnded(server.url, server.accessToken, liveVideoUUID) 144 await server.live.waitUntilEnded({ videoId: liveVideoUUID })
155 } 145 }
156 await waitJobs(servers) 146 await waitJobs(servers)
157 147
@@ -160,7 +150,7 @@ describe('Save replay setting', function () {
160 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED) 150 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED)
161 151
162 // No resolutions saved since we did not save replay 152 // No resolutions saved since we did not save replay
163 await checkLiveCleanup(servers[0], liveVideoUUID, []) 153 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
164 }) 154 })
165 155
166 it('Should correctly terminate the stream on blacklist and delete the live', async function () { 156 it('Should correctly terminate the stream on blacklist and delete the live', async function () {
@@ -168,7 +158,7 @@ describe('Save replay setting', function () {
168 158
169 liveVideoUUID = await createLiveWrapper(false) 159 liveVideoUUID = await createLiveWrapper(false)
170 160
171 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 161 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
172 162
173 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 163 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
174 164
@@ -176,7 +166,7 @@ describe('Save replay setting', function () {
176 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200) 166 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
177 167
178 await Promise.all([ 168 await Promise.all([
179 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), 169 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
180 testFfmpegStreamError(ffmpegCommand, true) 170 testFfmpegStreamError(ffmpegCommand, true)
181 ]) 171 ])
182 172
@@ -184,12 +174,12 @@ describe('Save replay setting', function () {
184 174
185 await checkVideosExist(liveVideoUUID, false) 175 await checkVideosExist(liveVideoUUID, false)
186 176
187 await getVideo(servers[0].url, liveVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 177 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
188 await getVideo(servers[1].url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 178 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
189 179
190 await wait(5000) 180 await wait(5000)
191 await waitJobs(servers) 181 await waitJobs(servers)
192 await checkLiveCleanup(servers[0], liveVideoUUID, []) 182 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
193 }) 183 })
194 184
195 it('Should correctly terminate the stream on delete and delete the video', async function () { 185 it('Should correctly terminate the stream on delete and delete the video', async function () {
@@ -197,7 +187,7 @@ describe('Save replay setting', function () {
197 187
198 liveVideoUUID = await createLiveWrapper(false) 188 liveVideoUUID = await createLiveWrapper(false)
199 189
200 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 190 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
201 191
202 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 192 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
203 193
@@ -206,14 +196,14 @@ describe('Save replay setting', function () {
206 196
207 await Promise.all([ 197 await Promise.all([
208 testFfmpegStreamError(ffmpegCommand, true), 198 testFfmpegStreamError(ffmpegCommand, true),
209 removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 199 servers[0].videos.remove({ id: liveVideoUUID })
210 ]) 200 ])
211 201
212 await wait(5000) 202 await wait(5000)
213 await waitJobs(servers) 203 await waitJobs(servers)
214 204
215 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404) 205 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
216 await checkLiveCleanup(servers[0], liveVideoUUID, []) 206 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
217 }) 207 })
218 }) 208 })
219 209
@@ -233,7 +223,7 @@ describe('Save replay setting', function () {
233 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 223 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
234 this.timeout(20000) 224 this.timeout(20000)
235 225
236 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 226 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
237 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 227 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
238 228
239 await waitJobs(servers) 229 await waitJobs(servers)
@@ -258,18 +248,18 @@ describe('Save replay setting', function () {
258 it('Should update the saved live and correctly federate the updated attributes', async function () { 248 it('Should update the saved live and correctly federate the updated attributes', async function () {
259 this.timeout(30000) 249 this.timeout(30000)
260 250
261 await updateVideo(servers[0].url, servers[0].accessToken, liveVideoUUID, { name: 'video updated' }) 251 await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated' } })
262 await waitJobs(servers) 252 await waitJobs(servers)
263 253
264 for (const server of servers) { 254 for (const server of servers) {
265 const res = await getVideo(server.url, liveVideoUUID) 255 const video = await server.videos.get({ id: liveVideoUUID })
266 expect(res.body.name).to.equal('video updated') 256 expect(video.name).to.equal('video updated')
267 expect(res.body.isLive).to.be.false 257 expect(video.isLive).to.be.false
268 } 258 }
269 }) 259 })
270 260
271 it('Should have cleaned up the live files', async function () { 261 it('Should have cleaned up the live files', async function () {
272 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ]) 262 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [ 720 ])
273 }) 263 })
274 264
275 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () { 265 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
@@ -277,14 +267,14 @@ describe('Save replay setting', function () {
277 267
278 liveVideoUUID = await createLiveWrapper(true) 268 liveVideoUUID = await createLiveWrapper(true)
279 269
280 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 270 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
281 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 271 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
282 272
283 await waitJobs(servers) 273 await waitJobs(servers)
284 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200) 274 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
285 275
286 await Promise.all([ 276 await Promise.all([
287 addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideoUUID, 'bad live', true), 277 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
288 testFfmpegStreamError(ffmpegCommand, true) 278 testFfmpegStreamError(ffmpegCommand, true)
289 ]) 279 ])
290 280
@@ -292,12 +282,12 @@ describe('Save replay setting', function () {
292 282
293 await checkVideosExist(liveVideoUUID, false) 283 await checkVideosExist(liveVideoUUID, false)
294 284
295 await getVideo(servers[0].url, liveVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 285 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
296 await getVideo(servers[1].url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 286 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
297 287
298 await wait(5000) 288 await wait(5000)
299 await waitJobs(servers) 289 await waitJobs(servers)
300 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ]) 290 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [ 720 ])
301 }) 291 })
302 292
303 it('Should correctly terminate the stream on delete and delete the video', async function () { 293 it('Should correctly terminate the stream on delete and delete the video', async function () {
@@ -305,14 +295,14 @@ describe('Save replay setting', function () {
305 295
306 liveVideoUUID = await createLiveWrapper(true) 296 liveVideoUUID = await createLiveWrapper(true)
307 297
308 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 298 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
309 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 299 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
310 300
311 await waitJobs(servers) 301 await waitJobs(servers)
312 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200) 302 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
313 303
314 await Promise.all([ 304 await Promise.all([
315 removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID), 305 servers[0].videos.remove({ id: liveVideoUUID }),
316 testFfmpegStreamError(ffmpegCommand, true) 306 testFfmpegStreamError(ffmpegCommand, true)
317 ]) 307 ])
318 308
@@ -320,7 +310,7 @@ describe('Save replay setting', function () {
320 await waitJobs(servers) 310 await waitJobs(servers)
321 311
322 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404) 312 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
323 await checkLiveCleanup(servers[0], liveVideoUUID, []) 313 await checkLiveCleanupAfterSave(servers[0], liveVideoUUID, [])
324 }) 314 })
325 }) 315 })
326 316
diff --git a/server/tests/api/live/live-socket-messages.ts b/server/tests/api/live/live-socket-messages.ts
index e00909ade..2a1f9f108 100644
--- a/server/tests/api/live/live-socket-messages.ts
+++ b/server/tests/api/live/live-socket-messages.ts
@@ -2,47 +2,42 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { getLiveNotificationSocket } from '@shared/extra-utils/socket/socket-io'
6import { VideoPrivacy, VideoState } from '@shared/models' 5import { VideoPrivacy, VideoState } from '@shared/models'
7import { 6import {
8 cleanupTests, 7 cleanupTests,
9 createLive, 8 createMultipleServers,
10 doubleFollow, 9 doubleFollow,
11 flushAndRunMultipleServers, 10 PeerTubeServer,
12 getVideoIdFromUUID,
13 sendRTMPStreamInVideo,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 setDefaultVideoChannel, 12 setDefaultVideoChannel,
17 stopFfmpeg, 13 stopFfmpeg,
18 updateCustomSubConfig,
19 viewVideo,
20 wait, 14 wait,
21 waitJobs, 15 waitJobs,
22 waitUntilLiveEnded,
23 waitUntilLivePublishedOnAllServers 16 waitUntilLivePublishedOnAllServers
24} from '../../../../shared/extra-utils' 17} from '../../../../shared/extra-utils'
25 18
26const expect = chai.expect 19const expect = chai.expect
27 20
28describe('Test live', function () { 21describe('Test live', function () {
29 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
30 23
31 before(async function () { 24 before(async function () {
32 this.timeout(120000) 25 this.timeout(120000)
33 26
34 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
35 28
36 // Get the access tokens 29 // Get the access tokens
37 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
38 await setDefaultVideoChannel(servers) 31 await setDefaultVideoChannel(servers)
39 32
40 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 33 await servers[0].config.updateCustomSubConfig({
41 live: { 34 newConfig: {
42 enabled: true, 35 live: {
43 allowReplay: true, 36 enabled: true,
44 transcoding: { 37 allowReplay: true,
45 enabled: false 38 transcoding: {
39 enabled: false
40 }
46 } 41 }
47 } 42 }
48 }) 43 })
@@ -56,12 +51,12 @@ describe('Test live', function () {
56 async function createLiveWrapper () { 51 async function createLiveWrapper () {
57 const liveAttributes = { 52 const liveAttributes = {
58 name: 'live video', 53 name: 'live video',
59 channelId: servers[0].videoChannel.id, 54 channelId: servers[0].store.channel.id,
60 privacy: VideoPrivacy.PUBLIC 55 privacy: VideoPrivacy.PUBLIC
61 } 56 }
62 57
63 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 58 const { uuid } = await servers[0].live.create({ fields: liveAttributes })
64 return res.body.video.uuid 59 return uuid
65 } 60 }
66 61
67 it('Should correctly send a message when the live starts and ends', async function () { 62 it('Should correctly send a message when the live starts and ends', async function () {
@@ -74,22 +69,22 @@ describe('Test live', function () {
74 await waitJobs(servers) 69 await waitJobs(servers)
75 70
76 { 71 {
77 const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID) 72 const videoId = await servers[0].videos.getId({ uuid: liveVideoUUID })
78 73
79 const localSocket = getLiveNotificationSocket(servers[0].url) 74 const localSocket = servers[0].socketIO.getLiveNotificationSocket()
80 localSocket.on('state-change', data => localStateChanges.push(data.state)) 75 localSocket.on('state-change', data => localStateChanges.push(data.state))
81 localSocket.emit('subscribe', { videoId }) 76 localSocket.emit('subscribe', { videoId })
82 } 77 }
83 78
84 { 79 {
85 const videoId = await getVideoIdFromUUID(servers[1].url, liveVideoUUID) 80 const videoId = await servers[1].videos.getId({ uuid: liveVideoUUID })
86 81
87 const remoteSocket = getLiveNotificationSocket(servers[1].url) 82 const remoteSocket = servers[1].socketIO.getLiveNotificationSocket()
88 remoteSocket.on('state-change', data => remoteStateChanges.push(data.state)) 83 remoteSocket.on('state-change', data => remoteStateChanges.push(data.state))
89 remoteSocket.emit('subscribe', { videoId }) 84 remoteSocket.emit('subscribe', { videoId })
90 } 85 }
91 86
92 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 87 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
93 88
94 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 89 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
95 await waitJobs(servers) 90 await waitJobs(servers)
@@ -99,10 +94,10 @@ describe('Test live', function () {
99 expect(stateChanges[stateChanges.length - 1]).to.equal(VideoState.PUBLISHED) 94 expect(stateChanges[stateChanges.length - 1]).to.equal(VideoState.PUBLISHED)
100 } 95 }
101 96
102 await stopFfmpeg(command) 97 await stopFfmpeg(ffmpegCommand)
103 98
104 for (const server of servers) { 99 for (const server of servers) {
105 await waitUntilLiveEnded(server.url, server.accessToken, liveVideoUUID) 100 await server.live.waitUntilEnded({ videoId: liveVideoUUID })
106 } 101 }
107 await waitJobs(servers) 102 await waitJobs(servers)
108 103
@@ -122,22 +117,22 @@ describe('Test live', function () {
122 await waitJobs(servers) 117 await waitJobs(servers)
123 118
124 { 119 {
125 const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID) 120 const videoId = await servers[0].videos.getId({ uuid: liveVideoUUID })
126 121
127 const localSocket = getLiveNotificationSocket(servers[0].url) 122 const localSocket = servers[0].socketIO.getLiveNotificationSocket()
128 localSocket.on('views-change', data => { localLastVideoViews = data.views }) 123 localSocket.on('views-change', data => { localLastVideoViews = data.views })
129 localSocket.emit('subscribe', { videoId }) 124 localSocket.emit('subscribe', { videoId })
130 } 125 }
131 126
132 { 127 {
133 const videoId = await getVideoIdFromUUID(servers[1].url, liveVideoUUID) 128 const videoId = await servers[1].videos.getId({ uuid: liveVideoUUID })
134 129
135 const remoteSocket = getLiveNotificationSocket(servers[1].url) 130 const remoteSocket = servers[1].socketIO.getLiveNotificationSocket()
136 remoteSocket.on('views-change', data => { remoteLastVideoViews = data.views }) 131 remoteSocket.on('views-change', data => { remoteLastVideoViews = data.views })
137 remoteSocket.emit('subscribe', { videoId }) 132 remoteSocket.emit('subscribe', { videoId })
138 } 133 }
139 134
140 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 135 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
141 136
142 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 137 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
143 await waitJobs(servers) 138 await waitJobs(servers)
@@ -145,8 +140,8 @@ describe('Test live', function () {
145 expect(localLastVideoViews).to.equal(0) 140 expect(localLastVideoViews).to.equal(0)
146 expect(remoteLastVideoViews).to.equal(0) 141 expect(remoteLastVideoViews).to.equal(0)
147 142
148 await viewVideo(servers[0].url, liveVideoUUID) 143 await servers[0].videos.view({ id: liveVideoUUID })
149 await viewVideo(servers[1].url, liveVideoUUID) 144 await servers[1].videos.view({ id: liveVideoUUID })
150 145
151 await waitJobs(servers) 146 await waitJobs(servers)
152 await wait(5000) 147 await wait(5000)
@@ -155,7 +150,7 @@ describe('Test live', function () {
155 expect(localLastVideoViews).to.equal(2) 150 expect(localLastVideoViews).to.equal(2)
156 expect(remoteLastVideoViews).to.equal(2) 151 expect(remoteLastVideoViews).to.equal(2)
157 152
158 await stopFfmpeg(command) 153 await stopFfmpeg(ffmpegCommand)
159 }) 154 })
160 155
161 it('Should not receive a notification after unsubscribe', async function () { 156 it('Should not receive a notification after unsubscribe', async function () {
@@ -166,13 +161,13 @@ describe('Test live', function () {
166 const liveVideoUUID = await createLiveWrapper() 161 const liveVideoUUID = await createLiveWrapper()
167 await waitJobs(servers) 162 await waitJobs(servers)
168 163
169 const videoId = await getVideoIdFromUUID(servers[0].url, liveVideoUUID) 164 const videoId = await servers[0].videos.getId({ uuid: liveVideoUUID })
170 165
171 const socket = getLiveNotificationSocket(servers[0].url) 166 const socket = servers[0].socketIO.getLiveNotificationSocket()
172 socket.on('state-change', data => stateChanges.push(data.state)) 167 socket.on('state-change', data => stateChanges.push(data.state))
173 socket.emit('subscribe', { videoId }) 168 socket.emit('subscribe', { videoId })
174 169
175 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 170 const command = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
176 171
177 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 172 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
178 await waitJobs(servers) 173 await waitJobs(servers)
diff --git a/server/tests/api/live/live-views.ts b/server/tests/api/live/live-views.ts
index a44d21ffa..5e3a79c64 100644
--- a/server/tests/api/live/live-views.ts
+++ b/server/tests/api/live/live-views.ts
@@ -3,20 +3,15 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { FfmpegCommand } from 'fluent-ffmpeg' 5import { FfmpegCommand } from 'fluent-ffmpeg'
6import { VideoDetails, VideoPrivacy } from '@shared/models' 6import { VideoPrivacy } from '@shared/models'
7import { 7import {
8 cleanupTests, 8 cleanupTests,
9 createLive, 9 createMultipleServers,
10 doubleFollow, 10 doubleFollow,
11 flushAndRunMultipleServers, 11 PeerTubeServer,
12 getVideo,
13 sendRTMPStreamInVideo,
14 ServerInfo,
15 setAccessTokensToServers, 12 setAccessTokensToServers,
16 setDefaultVideoChannel, 13 setDefaultVideoChannel,
17 stopFfmpeg, 14 stopFfmpeg,
18 updateCustomSubConfig,
19 viewVideo,
20 wait, 15 wait,
21 waitJobs, 16 waitJobs,
22 waitUntilLivePublishedOnAllServers 17 waitUntilLivePublishedOnAllServers
@@ -25,23 +20,25 @@ import {
25const expect = chai.expect 20const expect = chai.expect
26 21
27describe('Test live', function () { 22describe('Test live', function () {
28 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
29 24
30 before(async function () { 25 before(async function () {
31 this.timeout(120000) 26 this.timeout(120000)
32 27
33 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
34 29
35 // Get the access tokens 30 // Get the access tokens
36 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
37 await setDefaultVideoChannel(servers) 32 await setDefaultVideoChannel(servers)
38 33
39 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 34 await servers[0].config.updateCustomSubConfig({
40 live: { 35 newConfig: {
41 enabled: true, 36 live: {
42 allowReplay: true, 37 enabled: true,
43 transcoding: { 38 allowReplay: true,
44 enabled: false 39 transcoding: {
40 enabled: false
41 }
45 } 42 }
46 } 43 }
47 }) 44 })
@@ -56,9 +53,7 @@ describe('Test live', function () {
56 53
57 async function countViews (expected: number) { 54 async function countViews (expected: number) {
58 for (const server of servers) { 55 for (const server of servers) {
59 const res = await getVideo(server.url, liveVideoId) 56 const video = await server.videos.get({ id: liveVideoId })
60 const video: VideoDetails = res.body
61
62 expect(video.views).to.equal(expected) 57 expect(video.views).to.equal(expected)
63 } 58 }
64 } 59 }
@@ -68,14 +63,14 @@ describe('Test live', function () {
68 63
69 const liveAttributes = { 64 const liveAttributes = {
70 name: 'live video', 65 name: 'live video',
71 channelId: servers[0].videoChannel.id, 66 channelId: servers[0].store.channel.id,
72 privacy: VideoPrivacy.PUBLIC 67 privacy: VideoPrivacy.PUBLIC
73 } 68 }
74 69
75 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 70 const live = await servers[0].live.create({ fields: liveAttributes })
76 liveVideoId = res.body.video.uuid 71 liveVideoId = live.uuid
77 72
78 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 73 command = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoId })
79 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 74 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
80 await waitJobs(servers) 75 await waitJobs(servers)
81 }) 76 })
@@ -87,8 +82,8 @@ describe('Test live', function () {
87 it('Should view a live twice and display 1 view', async function () { 82 it('Should view a live twice and display 1 view', async function () {
88 this.timeout(30000) 83 this.timeout(30000)
89 84
90 await viewVideo(servers[0].url, liveVideoId) 85 await servers[0].videos.view({ id: liveVideoId })
91 await viewVideo(servers[0].url, liveVideoId) 86 await servers[0].videos.view({ id: liveVideoId })
92 87
93 await wait(7000) 88 await wait(7000)
94 89
@@ -109,9 +104,9 @@ describe('Test live', function () {
109 it('Should view a live on a remote and on local and display 2 views', async function () { 104 it('Should view a live on a remote and on local and display 2 views', async function () {
110 this.timeout(30000) 105 this.timeout(30000)
111 106
112 await viewVideo(servers[0].url, liveVideoId) 107 await servers[0].videos.view({ id: liveVideoId })
113 await viewVideo(servers[1].url, liveVideoId) 108 await servers[1].videos.view({ id: liveVideoId })
114 await viewVideo(servers[1].url, liveVideoId) 109 await servers[1].videos.view({ id: liveVideoId })
115 110
116 await wait(7000) 111 await wait(7000)
117 await waitJobs(servers) 112 await waitJobs(servers)
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 50397924e..d555cff19 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -2,75 +2,70 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path' 5import { basename, join } from 'path'
6import { ffprobePromise, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' 6import { ffprobePromise, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils'
7import { LiveVideo, LiveVideoCreate, Video, VideoDetails, VideoPrivacy, VideoState, VideoStreamingPlaylistType } from '@shared/models'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
9import { 7import {
10 addVideoToBlacklist, 8 checkLiveCleanupAfterSave,
11 buildServerDirectory,
12 checkLiveCleanup,
13 checkLiveSegmentHash, 9 checkLiveSegmentHash,
14 checkResolutionsInMasterPlaylist, 10 checkResolutionsInMasterPlaylist,
15 cleanupTests, 11 cleanupTests,
16 createLive, 12 createMultipleServers,
17 doubleFollow, 13 doubleFollow,
18 flushAndRunMultipleServers,
19 getLive,
20 getMyVideosWithFilter,
21 getPlaylist,
22 getVideo,
23 getVideosList,
24 getVideosWithFilters,
25 killallServers, 14 killallServers,
15 LiveCommand,
26 makeRawRequest, 16 makeRawRequest,
27 removeVideo, 17 PeerTubeServer,
28 reRunServer,
29 sendRTMPStream, 18 sendRTMPStream,
30 sendRTMPStreamInVideo,
31 ServerInfo,
32 setAccessTokensToServers, 19 setAccessTokensToServers,
33 setDefaultVideoChannel, 20 setDefaultVideoChannel,
34 stopFfmpeg, 21 stopFfmpeg,
35 testFfmpegStreamError, 22 testFfmpegStreamError,
36 testImage, 23 testImage,
37 updateCustomSubConfig,
38 updateLive,
39 uploadVideoAndGetId,
40 wait, 24 wait,
41 waitJobs, 25 waitJobs,
42 waitUntilLiveEnded, 26 waitUntilLivePublishedOnAllServers
43 waitUntilLivePublished, 27} from '@shared/extra-utils'
44 waitUntilLivePublishedOnAllServers, 28import {
45 waitUntilLiveSegmentGeneration 29 HttpStatusCode,
46} from '../../../../shared/extra-utils' 30 LiveVideo,
31 LiveVideoCreate,
32 VideoDetails,
33 VideoPrivacy,
34 VideoState,
35 VideoStreamingPlaylistType
36} from '@shared/models'
47 37
48const expect = chai.expect 38const expect = chai.expect
49 39
50describe('Test live', function () { 40describe('Test live', function () {
51 let servers: ServerInfo[] = [] 41 let servers: PeerTubeServer[] = []
42 let commands: LiveCommand[]
52 43
53 before(async function () { 44 before(async function () {
54 this.timeout(120000) 45 this.timeout(120000)
55 46
56 servers = await flushAndRunMultipleServers(2) 47 servers = await createMultipleServers(2)
57 48
58 // Get the access tokens 49 // Get the access tokens
59 await setAccessTokensToServers(servers) 50 await setAccessTokensToServers(servers)
60 await setDefaultVideoChannel(servers) 51 await setDefaultVideoChannel(servers)
61 52
62 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 53 await servers[0].config.updateCustomSubConfig({
63 live: { 54 newConfig: {
64 enabled: true, 55 live: {
65 allowReplay: true, 56 enabled: true,
66 transcoding: { 57 allowReplay: true,
67 enabled: false 58 transcoding: {
59 enabled: false
60 }
68 } 61 }
69 } 62 }
70 }) 63 })
71 64
72 // Server 1 and server 2 follow each other 65 // Server 1 and server 2 follow each other
73 await doubleFollow(servers[0], servers[1]) 66 await doubleFollow(servers[0], servers[1])
67
68 commands = servers.map(s => s.live)
74 }) 69 })
75 70
76 describe('Live creation, update and delete', function () { 71 describe('Live creation, update and delete', function () {
@@ -85,7 +80,7 @@ describe('Test live', function () {
85 language: 'fr', 80 language: 'fr',
86 description: 'super live description', 81 description: 'super live description',
87 support: 'support field', 82 support: 'support field',
88 channelId: servers[0].videoChannel.id, 83 channelId: servers[0].store.channel.id,
89 nsfw: false, 84 nsfw: false,
90 waitTranscoding: false, 85 waitTranscoding: false,
91 name: 'my super live', 86 name: 'my super live',
@@ -98,14 +93,13 @@ describe('Test live', function () {
98 thumbnailfile: 'video_short1.webm.jpg' 93 thumbnailfile: 'video_short1.webm.jpg'
99 } 94 }
100 95
101 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 96 const live = await commands[0].create({ fields: attributes })
102 liveVideoUUID = res.body.video.uuid 97 liveVideoUUID = live.uuid
103 98
104 await waitJobs(servers) 99 await waitJobs(servers)
105 100
106 for (const server of servers) { 101 for (const server of servers) {
107 const resVideo = await getVideo(server.url, liveVideoUUID) 102 const video = await server.videos.get({ id: liveVideoUUID })
108 const video: VideoDetails = resVideo.body
109 103
110 expect(video.category.id).to.equal(1) 104 expect(video.category.id).to.equal(1)
111 expect(video.licence.id).to.equal(2) 105 expect(video.licence.id).to.equal(2)
@@ -113,8 +107,8 @@ describe('Test live', function () {
113 expect(video.description).to.equal('super live description') 107 expect(video.description).to.equal('super live description')
114 expect(video.support).to.equal('support field') 108 expect(video.support).to.equal('support field')
115 109
116 expect(video.channel.name).to.equal(servers[0].videoChannel.name) 110 expect(video.channel.name).to.equal(servers[0].store.channel.name)
117 expect(video.channel.host).to.equal(servers[0].videoChannel.host) 111 expect(video.channel.host).to.equal(servers[0].store.channel.host)
118 112
119 expect(video.isLive).to.be.true 113 expect(video.isLive).to.be.true
120 114
@@ -129,8 +123,7 @@ describe('Test live', function () {
129 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 123 await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
130 await testImage(server.url, 'video_short1.webm', video.thumbnailPath) 124 await testImage(server.url, 'video_short1.webm', video.thumbnailPath)
131 125
132 const resLive = await getLive(server.url, server.accessToken, liveVideoUUID) 126 const live = await server.live.get({ videoId: liveVideoUUID })
133 const live: LiveVideo = resLive.body
134 127
135 if (server.url === servers[0].url) { 128 if (server.url === servers[0].url) {
136 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live') 129 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
@@ -149,20 +142,18 @@ describe('Test live', function () {
149 142
150 const attributes: LiveVideoCreate = { 143 const attributes: LiveVideoCreate = {
151 name: 'default live thumbnail', 144 name: 'default live thumbnail',
152 channelId: servers[0].videoChannel.id, 145 channelId: servers[0].store.channel.id,
153 privacy: VideoPrivacy.UNLISTED, 146 privacy: VideoPrivacy.UNLISTED,
154 nsfw: true 147 nsfw: true
155 } 148 }
156 149
157 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 150 const live = await commands[0].create({ fields: attributes })
158 const videoId = res.body.video.uuid 151 const videoId = live.uuid
159 152
160 await waitJobs(servers) 153 await waitJobs(servers)
161 154
162 for (const server of servers) { 155 for (const server of servers) {
163 const resVideo = await getVideo(server.url, videoId) 156 const video = await server.videos.get({ id: videoId })
164 const video: VideoDetails = resVideo.body
165
166 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED) 157 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
167 expect(video.nsfw).to.be.true 158 expect(video.nsfw).to.be.true
168 159
@@ -173,28 +164,27 @@ describe('Test live', function () {
173 164
174 it('Should not have the live listed since nobody streams into', async function () { 165 it('Should not have the live listed since nobody streams into', async function () {
175 for (const server of servers) { 166 for (const server of servers) {
176 const res = await getVideosList(server.url) 167 const { total, data } = await server.videos.list()
177 168
178 expect(res.body.total).to.equal(0) 169 expect(total).to.equal(0)
179 expect(res.body.data).to.have.lengthOf(0) 170 expect(data).to.have.lengthOf(0)
180 } 171 }
181 }) 172 })
182 173
183 it('Should not be able to update a live of another server', async function () { 174 it('Should not be able to update a live of another server', async function () {
184 await updateLive(servers[1].url, servers[1].accessToken, liveVideoUUID, { saveReplay: false }, HttpStatusCode.FORBIDDEN_403) 175 await commands[1].update({ videoId: liveVideoUUID, fields: { saveReplay: false }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
185 }) 176 })
186 177
187 it('Should update the live', async function () { 178 it('Should update the live', async function () {
188 this.timeout(10000) 179 this.timeout(10000)
189 180
190 await updateLive(servers[0].url, servers[0].accessToken, liveVideoUUID, { saveReplay: false }) 181 await commands[0].update({ videoId: liveVideoUUID, fields: { saveReplay: false } })
191 await waitJobs(servers) 182 await waitJobs(servers)
192 }) 183 })
193 184
194 it('Have the live updated', async function () { 185 it('Have the live updated', async function () {
195 for (const server of servers) { 186 for (const server of servers) {
196 const res = await getLive(server.url, server.accessToken, liveVideoUUID) 187 const live = await server.live.get({ videoId: liveVideoUUID })
197 const live: LiveVideo = res.body
198 188
199 if (server.url === servers[0].url) { 189 if (server.url === servers[0].url) {
200 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live') 190 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
@@ -211,77 +201,75 @@ describe('Test live', function () {
211 it('Delete the live', async function () { 201 it('Delete the live', async function () {
212 this.timeout(10000) 202 this.timeout(10000)
213 203
214 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 204 await servers[0].videos.remove({ id: liveVideoUUID })
215 await waitJobs(servers) 205 await waitJobs(servers)
216 }) 206 })
217 207
218 it('Should have the live deleted', async function () { 208 it('Should have the live deleted', async function () {
219 for (const server of servers) { 209 for (const server of servers) {
220 await getVideo(server.url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 210 await server.videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
221 await getLive(server.url, server.accessToken, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 211 await server.live.get({ videoId: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
222 } 212 }
223 }) 213 })
224 }) 214 })
225 215
226 describe('Live filters', function () { 216 describe('Live filters', function () {
227 let command: any 217 let ffmpegCommand: any
228 let liveVideoId: string 218 let liveVideoId: string
229 let vodVideoId: string 219 let vodVideoId: string
230 220
231 before(async function () { 221 before(async function () {
232 this.timeout(120000) 222 this.timeout(120000)
233 223
234 vodVideoId = (await uploadVideoAndGetId({ server: servers[0], videoName: 'vod video' })).uuid 224 vodVideoId = (await servers[0].videos.quickUpload({ name: 'vod video' })).uuid
235 225
236 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].videoChannel.id } 226 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].store.channel.id }
237 const resLive = await createLive(servers[0].url, servers[0].accessToken, liveOptions) 227 const live = await commands[0].create({ fields: liveOptions })
238 liveVideoId = resLive.body.video.uuid 228 liveVideoId = live.uuid
239 229
240 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 230 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoId })
241 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 231 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
242 await waitJobs(servers) 232 await waitJobs(servers)
243 }) 233 })
244 234
245 it('Should only display lives', async function () { 235 it('Should only display lives', async function () {
246 const res = await getVideosWithFilters(servers[0].url, { isLive: true }) 236 const { data, total } = await servers[0].videos.list({ isLive: true })
247 237
248 expect(res.body.total).to.equal(1) 238 expect(total).to.equal(1)
249 expect(res.body.data).to.have.lengthOf(1) 239 expect(data).to.have.lengthOf(1)
250 expect(res.body.data[0].name).to.equal('live') 240 expect(data[0].name).to.equal('live')
251 }) 241 })
252 242
253 it('Should not display lives', async function () { 243 it('Should not display lives', async function () {
254 const res = await getVideosWithFilters(servers[0].url, { isLive: false }) 244 const { data, total } = await servers[0].videos.list({ isLive: false })
255 245
256 expect(res.body.total).to.equal(1) 246 expect(total).to.equal(1)
257 expect(res.body.data).to.have.lengthOf(1) 247 expect(data).to.have.lengthOf(1)
258 expect(res.body.data[0].name).to.equal('vod video') 248 expect(data[0].name).to.equal('vod video')
259 }) 249 })
260 250
261 it('Should display my lives', async function () { 251 it('Should display my lives', async function () {
262 this.timeout(60000) 252 this.timeout(60000)
263 253
264 await stopFfmpeg(command) 254 await stopFfmpeg(ffmpegCommand)
265 await waitJobs(servers) 255 await waitJobs(servers)
266 256
267 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: true }) 257 const { data } = await servers[0].videos.listMyVideos({ isLive: true })
268 const videos = res.body.data as Video[]
269 258
270 const result = videos.every(v => v.isLive) 259 const result = data.every(v => v.isLive)
271 expect(result).to.be.true 260 expect(result).to.be.true
272 }) 261 })
273 262
274 it('Should not display my lives', async function () { 263 it('Should not display my lives', async function () {
275 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: false }) 264 const { data } = await servers[0].videos.listMyVideos({ isLive: false })
276 const videos = res.body.data as Video[]
277 265
278 const result = videos.every(v => !v.isLive) 266 const result = data.every(v => !v.isLive)
279 expect(result).to.be.true 267 expect(result).to.be.true
280 }) 268 })
281 269
282 after(async function () { 270 after(async function () {
283 await removeVideo(servers[0].url, servers[0].accessToken, vodVideoId) 271 await servers[0].videos.remove({ id: vodVideoId })
284 await removeVideo(servers[0].url, servers[0].accessToken, liveVideoId) 272 await servers[0].videos.remove({ id: liveVideoId })
285 }) 273 })
286 }) 274 })
287 275
@@ -296,18 +284,17 @@ describe('Test live', function () {
296 async function createLiveWrapper () { 284 async function createLiveWrapper () {
297 const liveAttributes = { 285 const liveAttributes = {
298 name: 'user live', 286 name: 'user live',
299 channelId: servers[0].videoChannel.id, 287 channelId: servers[0].store.channel.id,
300 privacy: VideoPrivacy.PUBLIC, 288 privacy: VideoPrivacy.PUBLIC,
301 saveReplay: false 289 saveReplay: false
302 } 290 }
303 291
304 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 292 const { uuid } = await commands[0].create({ fields: liveAttributes })
305 const uuid = res.body.video.uuid
306 293
307 const resLive = await getLive(servers[0].url, servers[0].accessToken, uuid) 294 const live = await commands[0].get({ videoId: uuid })
308 const resVideo = await getVideo(servers[0].url, uuid) 295 const video = await servers[0].videos.get({ id: uuid })
309 296
310 return Object.assign(resVideo.body, resLive.body) as LiveVideo & VideoDetails 297 return Object.assign(video, live)
311 } 298 }
312 299
313 it('Should not allow a stream without the appropriate path', async function () { 300 it('Should not allow a stream without the appropriate path', async function () {
@@ -335,13 +322,12 @@ describe('Test live', function () {
335 322
336 it('Should list this live now someone stream into it', async function () { 323 it('Should list this live now someone stream into it', async function () {
337 for (const server of servers) { 324 for (const server of servers) {
338 const res = await getVideosList(server.url) 325 const { total, data } = await server.videos.list()
339 326
340 expect(res.body.total).to.equal(1) 327 expect(total).to.equal(1)
341 expect(res.body.data).to.have.lengthOf(1) 328 expect(data).to.have.lengthOf(1)
342
343 const video: Video = res.body.data[0]
344 329
330 const video = data[0]
345 expect(video.name).to.equal('user live') 331 expect(video.name).to.equal('user live')
346 expect(video.isLive).to.be.true 332 expect(video.isLive).to.be.true
347 } 333 }
@@ -352,7 +338,7 @@ describe('Test live', function () {
352 338
353 liveVideo = await createLiveWrapper() 339 liveVideo = await createLiveWrapper()
354 340
355 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, liveVideo.uuid) 341 await servers[0].blacklist.add({ videoId: liveVideo.uuid })
356 342
357 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) 343 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
358 await testFfmpegStreamError(command, true) 344 await testFfmpegStreamError(command, true)
@@ -363,7 +349,7 @@ describe('Test live', function () {
363 349
364 liveVideo = await createLiveWrapper() 350 liveVideo = await createLiveWrapper()
365 351
366 await removeVideo(servers[0].url, servers[0].accessToken, liveVideo.uuid) 352 await servers[0].videos.remove({ id: liveVideo.uuid })
367 353
368 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey) 354 const command = sendRTMPStream(rtmpUrl + '/live', liveVideo.streamKey)
369 await testFfmpegStreamError(command, true) 355 await testFfmpegStreamError(command, true)
@@ -376,24 +362,21 @@ describe('Test live', function () {
376 async function createLiveWrapper (saveReplay: boolean) { 362 async function createLiveWrapper (saveReplay: boolean) {
377 const liveAttributes = { 363 const liveAttributes = {
378 name: 'live video', 364 name: 'live video',
379 channelId: servers[0].videoChannel.id, 365 channelId: servers[0].store.channel.id,
380 privacy: VideoPrivacy.PUBLIC, 366 privacy: VideoPrivacy.PUBLIC,
381 saveReplay 367 saveReplay
382 } 368 }
383 369
384 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 370 const { uuid } = await commands[0].create({ fields: liveAttributes })
385 return res.body.video.uuid 371 return uuid
386 } 372 }
387 373
388 async function testVideoResolutions (liveVideoId: string, resolutions: number[]) { 374 async function testVideoResolutions (liveVideoId: string, resolutions: number[]) {
389 for (const server of servers) { 375 for (const server of servers) {
390 const resList = await getVideosList(server.url) 376 const { data } = await server.videos.list()
391 const videos: Video[] = resList.body.data 377 expect(data.find(v => v.uuid === liveVideoId)).to.exist
392
393 expect(videos.find(v => v.uuid === liveVideoId)).to.exist
394 378
395 const resVideo = await getVideo(server.url, liveVideoId) 379 const video = await server.videos.get({ id: liveVideoId })
396 const video: VideoDetails = resVideo.body
397 380
398 expect(video.streamingPlaylists).to.have.lengthOf(1) 381 expect(video.streamingPlaylists).to.have.lengthOf(1)
399 382
@@ -403,39 +386,48 @@ describe('Test live', function () {
403 // Only finite files are displayed 386 // Only finite files are displayed
404 expect(hlsPlaylist.files).to.have.lengthOf(0) 387 expect(hlsPlaylist.files).to.have.lengthOf(0)
405 388
406 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions) 389 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
407 390
408 for (let i = 0; i < resolutions.length; i++) { 391 for (let i = 0; i < resolutions.length; i++) {
409 const segmentNum = 3 392 const segmentNum = 3
410 const segmentName = `${i}-00000${segmentNum}.ts` 393 const segmentName = `${i}-00000${segmentNum}.ts`
411 await waitUntilLiveSegmentGeneration(servers[0], video.uuid, i, segmentNum) 394 await commands[0].waitUntilSegmentGeneration({ videoUUID: video.uuid, resolution: i, segment: segmentNum })
412 395
413 const res = await getPlaylist(`${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`) 396 const subPlaylist = await servers[0].streamingPlaylists.get({
414 const subPlaylist = res.text 397 url: `${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`
398 })
415 399
416 expect(subPlaylist).to.contain(segmentName) 400 expect(subPlaylist).to.contain(segmentName)
417 401
418 const baseUrlAndPath = servers[0].url + '/static/streaming-playlists/hls' 402 const baseUrlAndPath = servers[0].url + '/static/streaming-playlists/hls'
419 await checkLiveSegmentHash(baseUrlAndPath, video.uuid, segmentName, hlsPlaylist) 403 await checkLiveSegmentHash({
404 server,
405 baseUrlSegment: baseUrlAndPath,
406 videoUUID: video.uuid,
407 segmentName,
408 hlsPlaylist
409 })
420 } 410 }
421 } 411 }
422 } 412 }
423 413
424 function updateConf (resolutions: number[]) { 414 function updateConf (resolutions: number[]) {
425 return updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 415 return servers[0].config.updateCustomSubConfig({
426 live: { 416 newConfig: {
427 enabled: true, 417 live: {
428 allowReplay: true,
429 maxDuration: -1,
430 transcoding: {
431 enabled: true, 418 enabled: true,
432 resolutions: { 419 allowReplay: true,
433 '240p': resolutions.includes(240), 420 maxDuration: -1,
434 '360p': resolutions.includes(360), 421 transcoding: {
435 '480p': resolutions.includes(480), 422 enabled: true,
436 '720p': resolutions.includes(720), 423 resolutions: {
437 '1080p': resolutions.includes(1080), 424 '240p': resolutions.includes(240),
438 '2160p': resolutions.includes(2160) 425 '360p': resolutions.includes(360),
426 '480p': resolutions.includes(480),
427 '720p': resolutions.includes(720),
428 '1080p': resolutions.includes(1080),
429 '2160p': resolutions.includes(2160)
430 }
439 } 431 }
440 } 432 }
441 } 433 }
@@ -451,13 +443,13 @@ describe('Test live', function () {
451 443
452 liveVideoId = await createLiveWrapper(false) 444 liveVideoId = await createLiveWrapper(false)
453 445
454 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 446 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId })
455 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 447 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
456 await waitJobs(servers) 448 await waitJobs(servers)
457 449
458 await testVideoResolutions(liveVideoId, [ 720 ]) 450 await testVideoResolutions(liveVideoId, [ 720 ])
459 451
460 await stopFfmpeg(command) 452 await stopFfmpeg(ffmpegCommand)
461 }) 453 })
462 454
463 it('Should enable transcoding with some resolutions', async function () { 455 it('Should enable transcoding with some resolutions', async function () {
@@ -467,13 +459,13 @@ describe('Test live', function () {
467 await updateConf(resolutions) 459 await updateConf(resolutions)
468 liveVideoId = await createLiveWrapper(false) 460 liveVideoId = await createLiveWrapper(false)
469 461
470 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 462 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId })
471 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 463 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
472 await waitJobs(servers) 464 await waitJobs(servers)
473 465
474 await testVideoResolutions(liveVideoId, resolutions) 466 await testVideoResolutions(liveVideoId, resolutions)
475 467
476 await stopFfmpeg(command) 468 await stopFfmpeg(ffmpegCommand)
477 }) 469 })
478 470
479 it('Should enable transcoding with some resolutions and correctly save them', async function () { 471 it('Should enable transcoding with some resolutions and correctly save them', async function () {
@@ -484,14 +476,14 @@ describe('Test live', function () {
484 await updateConf(resolutions) 476 await updateConf(resolutions)
485 liveVideoId = await createLiveWrapper(true) 477 liveVideoId = await createLiveWrapper(true)
486 478
487 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId, 'video_short2.webm') 479 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
488 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 480 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
489 await waitJobs(servers) 481 await waitJobs(servers)
490 482
491 await testVideoResolutions(liveVideoId, resolutions) 483 await testVideoResolutions(liveVideoId, resolutions)
492 484
493 await stopFfmpeg(command) 485 await stopFfmpeg(ffmpegCommand)
494 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId) 486 await commands[0].waitUntilEnded({ videoId: liveVideoId })
495 487
496 await waitJobs(servers) 488 await waitJobs(servers)
497 489
@@ -504,8 +496,7 @@ describe('Test live', function () {
504 } 496 }
505 497
506 for (const server of servers) { 498 for (const server of servers) {
507 const resVideo = await getVideo(server.url, liveVideoId) 499 const video = await server.videos.get({ id: liveVideoId })
508 const video: VideoDetails = resVideo.body
509 500
510 expect(video.state.id).to.equal(VideoState.PUBLISHED) 501 expect(video.state.id).to.equal(VideoState.PUBLISHED)
511 expect(video.duration).to.be.greaterThan(1) 502 expect(video.duration).to.be.greaterThan(1)
@@ -515,6 +506,10 @@ describe('Test live', function () {
515 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200) 506 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200)
516 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200) 507 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200)
517 508
509 // We should have generated random filenames
510 expect(basename(hlsPlaylist.playlistUrl)).to.not.equal('master.m3u8')
511 expect(basename(hlsPlaylist.segmentsSha256Url)).to.not.equal('segments-sha256.json')
512
518 expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length) 513 expect(hlsPlaylist.files).to.have.lengthOf(resolutions.length)
519 514
520 for (const resolution of resolutions) { 515 for (const resolution of resolutions) {
@@ -529,8 +524,10 @@ describe('Test live', function () {
529 expect(file.fps).to.be.approximately(30, 2) 524 expect(file.fps).to.be.approximately(30, 2)
530 } 525 }
531 526
532 const filename = `${video.uuid}-${resolution}-fragmented.mp4` 527 const filename = basename(file.fileUrl)
533 const segmentPath = buildServerDirectory(servers[0], join('streaming-playlists', 'hls', video.uuid, filename)) 528 expect(filename).to.not.contain(video.uuid)
529
530 const segmentPath = servers[0].servers.buildDirectory(join('streaming-playlists', 'hls', video.uuid, filename))
534 531
535 const probe = await ffprobePromise(segmentPath) 532 const probe = await ffprobePromise(segmentPath)
536 const videoStream = await getVideoStreamFromFile(segmentPath, probe) 533 const videoStream = await getVideoStreamFromFile(segmentPath, probe)
@@ -546,7 +543,7 @@ describe('Test live', function () {
546 it('Should correctly have cleaned up the live files', async function () { 543 it('Should correctly have cleaned up the live files', async function () {
547 this.timeout(30000) 544 this.timeout(30000)
548 545
549 await checkLiveCleanup(servers[0], liveVideoId, [ 240, 360, 720 ]) 546 await checkLiveCleanupAfterSave(servers[0], liveVideoId, [ 240, 360, 720 ])
550 }) 547 })
551 }) 548 })
552 549
@@ -557,13 +554,13 @@ describe('Test live', function () {
557 async function createLiveWrapper (saveReplay: boolean) { 554 async function createLiveWrapper (saveReplay: boolean) {
558 const liveAttributes = { 555 const liveAttributes = {
559 name: 'live video', 556 name: 'live video',
560 channelId: servers[0].videoChannel.id, 557 channelId: servers[0].store.channel.id,
561 privacy: VideoPrivacy.PUBLIC, 558 privacy: VideoPrivacy.PUBLIC,
562 saveReplay 559 saveReplay
563 } 560 }
564 561
565 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 562 const { uuid } = await commands[0].create({ fields: liveAttributes })
566 return res.body.video.uuid 563 return uuid
567 } 564 }
568 565
569 before(async function () { 566 before(async function () {
@@ -573,20 +570,20 @@ describe('Test live', function () {
573 liveVideoReplayId = await createLiveWrapper(true) 570 liveVideoReplayId = await createLiveWrapper(true)
574 571
575 await Promise.all([ 572 await Promise.all([
576 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId), 573 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId }),
577 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoReplayId) 574 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoReplayId })
578 ]) 575 ])
579 576
580 await Promise.all([ 577 await Promise.all([
581 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId), 578 commands[0].waitUntilPublished({ videoId: liveVideoId }),
582 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 579 commands[0].waitUntilPublished({ videoId: liveVideoReplayId })
583 ]) 580 ])
584 581
585 await waitUntilLiveSegmentGeneration(servers[0], liveVideoId, 0, 2) 582 await commands[0].waitUntilSegmentGeneration({ videoUUID: liveVideoId, resolution: 0, segment: 2 })
586 await waitUntilLiveSegmentGeneration(servers[0], liveVideoReplayId, 0, 2) 583 await commands[0].waitUntilSegmentGeneration({ videoUUID: liveVideoReplayId, resolution: 0, segment: 2 })
587 584
588 await killallServers([ servers[0] ]) 585 await killallServers([ servers[0] ])
589 await reRunServer(servers[0]) 586 await servers[0].run()
590 587
591 await wait(5000) 588 await wait(5000)
592 }) 589 })
@@ -594,13 +591,13 @@ describe('Test live', function () {
594 it('Should cleanup lives', async function () { 591 it('Should cleanup lives', async function () {
595 this.timeout(60000) 592 this.timeout(60000)
596 593
597 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId) 594 await commands[0].waitUntilEnded({ videoId: liveVideoId })
598 }) 595 })
599 596
600 it('Should save a live replay', async function () { 597 it('Should save a live replay', async function () {
601 this.timeout(120000) 598 this.timeout(120000)
602 599
603 await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 600 await commands[0].waitUntilPublished({ videoId: liveVideoReplayId })
604 }) 601 })
605 }) 602 })
606 603
diff --git a/server/tests/api/moderation/abuses.ts b/server/tests/api/moderation/abuses.ts
index fb765e7e3..c258414ce 100644
--- a/server/tests/api/moderation/abuses.ts
+++ b/server/tests/api/moderation/abuses.ts
@@ -3,70 +3,37 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 AbuseFilter, 6 AbusesCommand,
7 AbuseMessage,
8 AbusePredefinedReasonsString,
9 AbuseState,
10 Account,
11 AdminAbuse,
12 UserAbuse,
13 VideoComment
14} from '@shared/models'
15import {
16 addAbuseMessage,
17 addVideoCommentThread,
18 cleanupTests, 7 cleanupTests,
19 createUser, 8 createMultipleServers,
20 deleteAbuse, 9 doubleFollow,
21 deleteAbuseMessage, 10 PeerTubeServer,
22 deleteVideoComment,
23 flushAndRunMultipleServers,
24 generateUserAccessToken,
25 getAccount,
26 getAdminAbusesList,
27 getUserAbusesList,
28 getVideoCommentThreads,
29 getVideoIdFromUUID,
30 getVideosList,
31 immutableAssign,
32 listAbuseMessages,
33 removeUser,
34 removeVideo,
35 reportAbuse,
36 ServerInfo,
37 setAccessTokensToServers, 11 setAccessTokensToServers,
38 updateAbuse, 12 waitJobs
39 uploadVideo, 13} from '@shared/extra-utils'
40 uploadVideoAndGetId, 14import { AbuseMessage, AbusePredefinedReasonsString, AbuseState, AdminAbuse, UserAbuse } from '@shared/models'
41 userLogin
42} from '../../../../shared/extra-utils/index'
43import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
44import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
45import {
46 addAccountToServerBlocklist,
47 addServerToServerBlocklist,
48 removeAccountFromServerBlocklist,
49 removeServerFromServerBlocklist
50} from '../../../../shared/extra-utils/users/blocklist'
51 15
52const expect = chai.expect 16const expect = chai.expect
53 17
54describe('Test abuses', function () { 18describe('Test abuses', function () {
55 let servers: ServerInfo[] = [] 19 let servers: PeerTubeServer[] = []
56 let abuseServer1: AdminAbuse 20 let abuseServer1: AdminAbuse
57 let abuseServer2: AdminAbuse 21 let abuseServer2: AdminAbuse
22 let commands: AbusesCommand[]
58 23
59 before(async function () { 24 before(async function () {
60 this.timeout(50000) 25 this.timeout(50000)
61 26
62 // Run servers 27 // Run servers
63 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
64 29
65 // Get the access tokens 30 // Get the access tokens
66 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
67 32
68 // Server 1 and server 2 follow each other 33 // Server 1 and server 2 follow each other
69 await doubleFollow(servers[0], servers[1]) 34 await doubleFollow(servers[0], servers[1])
35
36 commands = servers.map(s => s.abuses)
70 }) 37 })
71 38
72 describe('Video abuses', function () { 39 describe('Video abuses', function () {
@@ -75,179 +42,189 @@ describe('Test abuses', function () {
75 this.timeout(50000) 42 this.timeout(50000)
76 43
77 // Upload some videos on each servers 44 // Upload some videos on each servers
78 const video1Attributes = { 45 {
79 name: 'my super name for server 1', 46 const attributes = {
80 description: 'my super description for server 1' 47 name: 'my super name for server 1',
48 description: 'my super description for server 1'
49 }
50 await servers[0].videos.upload({ attributes })
81 } 51 }
82 await uploadVideo(servers[0].url, servers[0].accessToken, video1Attributes)
83 52
84 const video2Attributes = { 53 {
85 name: 'my super name for server 2', 54 const attributes = {
86 description: 'my super description for server 2' 55 name: 'my super name for server 2',
56 description: 'my super description for server 2'
57 }
58 await servers[1].videos.upload({ attributes })
87 } 59 }
88 await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes)
89 60
90 // Wait videos propagation, server 2 has transcoding enabled 61 // Wait videos propagation, server 2 has transcoding enabled
91 await waitJobs(servers) 62 await waitJobs(servers)
92 63
93 const res = await getVideosList(servers[0].url) 64 const { data } = await servers[0].videos.list()
94 const videos = res.body.data 65 expect(data.length).to.equal(2)
95 66
96 expect(videos.length).to.equal(2) 67 servers[0].store.videoCreated = data.find(video => video.name === 'my super name for server 1')
97 68 servers[1].store.videoCreated = data.find(video => video.name === 'my super name for server 2')
98 servers[0].video = videos.find(video => video.name === 'my super name for server 1')
99 servers[1].video = videos.find(video => video.name === 'my super name for server 2')
100 }) 69 })
101 70
102 it('Should not have abuses', async function () { 71 it('Should not have abuses', async function () {
103 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 72 const body = await commands[0].getAdminList()
104 73
105 expect(res.body.total).to.equal(0) 74 expect(body.total).to.equal(0)
106 expect(res.body.data).to.be.an('array') 75 expect(body.data).to.be.an('array')
107 expect(res.body.data.length).to.equal(0) 76 expect(body.data.length).to.equal(0)
108 }) 77 })
109 78
110 it('Should report abuse on a local video', async function () { 79 it('Should report abuse on a local video', async function () {
111 this.timeout(15000) 80 this.timeout(15000)
112 81
113 const reason = 'my super bad reason' 82 const reason = 'my super bad reason'
114 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: servers[0].video.id, reason }) 83 await commands[0].report({ videoId: servers[0].store.videoCreated.id, reason })
115 84
116 // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2 85 // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2
117 await waitJobs(servers) 86 await waitJobs(servers)
118 }) 87 })
119 88
120 it('Should have 1 video abuses on server 1 and 0 on server 2', async function () { 89 it('Should have 1 video abuses on server 1 and 0 on server 2', async function () {
121 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 90 {
91 const body = await commands[0].getAdminList()
122 92
123 expect(res1.body.total).to.equal(1) 93 expect(body.total).to.equal(1)
124 expect(res1.body.data).to.be.an('array') 94 expect(body.data).to.be.an('array')
125 expect(res1.body.data.length).to.equal(1) 95 expect(body.data.length).to.equal(1)
126 96
127 const abuse: AdminAbuse = res1.body.data[0] 97 const abuse = body.data[0]
128 expect(abuse.reason).to.equal('my super bad reason') 98 expect(abuse.reason).to.equal('my super bad reason')
129 99
130 expect(abuse.reporterAccount.name).to.equal('root') 100 expect(abuse.reporterAccount.name).to.equal('root')
131 expect(abuse.reporterAccount.host).to.equal(servers[0].host) 101 expect(abuse.reporterAccount.host).to.equal(servers[0].host)
132 102
133 expect(abuse.video.id).to.equal(servers[0].video.id) 103 expect(abuse.video.id).to.equal(servers[0].store.videoCreated.id)
134 expect(abuse.video.channel).to.exist 104 expect(abuse.video.channel).to.exist
135 105
136 expect(abuse.comment).to.be.null 106 expect(abuse.comment).to.be.null
137 107
138 expect(abuse.flaggedAccount.name).to.equal('root') 108 expect(abuse.flaggedAccount.name).to.equal('root')
139 expect(abuse.flaggedAccount.host).to.equal(servers[0].host) 109 expect(abuse.flaggedAccount.host).to.equal(servers[0].host)
140 110
141 expect(abuse.video.countReports).to.equal(1) 111 expect(abuse.video.countReports).to.equal(1)
142 expect(abuse.video.nthReport).to.equal(1) 112 expect(abuse.video.nthReport).to.equal(1)
143 113
144 expect(abuse.countReportsForReporter).to.equal(1) 114 expect(abuse.countReportsForReporter).to.equal(1)
145 expect(abuse.countReportsForReportee).to.equal(1) 115 expect(abuse.countReportsForReportee).to.equal(1)
116 }
146 117
147 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 118 {
148 expect(res2.body.total).to.equal(0) 119 const body = await commands[1].getAdminList()
149 expect(res2.body.data).to.be.an('array') 120 expect(body.total).to.equal(0)
150 expect(res2.body.data.length).to.equal(0) 121 expect(body.data).to.be.an('array')
122 expect(body.data.length).to.equal(0)
123 }
151 }) 124 })
152 125
153 it('Should report abuse on a remote video', async function () { 126 it('Should report abuse on a remote video', async function () {
154 this.timeout(10000) 127 this.timeout(10000)
155 128
156 const reason = 'my super bad reason 2' 129 const reason = 'my super bad reason 2'
157 const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid) 130 const videoId = await servers[0].videos.getId({ uuid: servers[1].store.videoCreated.uuid })
158 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId, reason }) 131 await commands[0].report({ videoId, reason })
159 132
160 // We wait requests propagation 133 // We wait requests propagation
161 await waitJobs(servers) 134 await waitJobs(servers)
162 }) 135 })
163 136
164 it('Should have 2 video abuses on server 1 and 1 on server 2', async function () { 137 it('Should have 2 video abuses on server 1 and 1 on server 2', async function () {
165 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 138 {
139 const body = await commands[0].getAdminList()
166 140
167 expect(res1.body.total).to.equal(2) 141 expect(body.total).to.equal(2)
168 expect(res1.body.data.length).to.equal(2) 142 expect(body.data.length).to.equal(2)
169 143
170 const abuse1: AdminAbuse = res1.body.data[0] 144 const abuse1 = body.data[0]
171 expect(abuse1.reason).to.equal('my super bad reason') 145 expect(abuse1.reason).to.equal('my super bad reason')
172 expect(abuse1.reporterAccount.name).to.equal('root') 146 expect(abuse1.reporterAccount.name).to.equal('root')
173 expect(abuse1.reporterAccount.host).to.equal(servers[0].host) 147 expect(abuse1.reporterAccount.host).to.equal(servers[0].host)
174 148
175 expect(abuse1.video.id).to.equal(servers[0].video.id) 149 expect(abuse1.video.id).to.equal(servers[0].store.videoCreated.id)
176 expect(abuse1.video.countReports).to.equal(1) 150 expect(abuse1.video.countReports).to.equal(1)
177 expect(abuse1.video.nthReport).to.equal(1) 151 expect(abuse1.video.nthReport).to.equal(1)
178 152
179 expect(abuse1.comment).to.be.null 153 expect(abuse1.comment).to.be.null
180 154
181 expect(abuse1.flaggedAccount.name).to.equal('root') 155 expect(abuse1.flaggedAccount.name).to.equal('root')
182 expect(abuse1.flaggedAccount.host).to.equal(servers[0].host) 156 expect(abuse1.flaggedAccount.host).to.equal(servers[0].host)
183 157
184 expect(abuse1.state.id).to.equal(AbuseState.PENDING) 158 expect(abuse1.state.id).to.equal(AbuseState.PENDING)
185 expect(abuse1.state.label).to.equal('Pending') 159 expect(abuse1.state.label).to.equal('Pending')
186 expect(abuse1.moderationComment).to.be.null 160 expect(abuse1.moderationComment).to.be.null
187 161
188 const abuse2: AdminAbuse = res1.body.data[1] 162 const abuse2 = body.data[1]
189 expect(abuse2.reason).to.equal('my super bad reason 2') 163 expect(abuse2.reason).to.equal('my super bad reason 2')
190 164
191 expect(abuse2.reporterAccount.name).to.equal('root') 165 expect(abuse2.reporterAccount.name).to.equal('root')
192 expect(abuse2.reporterAccount.host).to.equal(servers[0].host) 166 expect(abuse2.reporterAccount.host).to.equal(servers[0].host)
193 167
194 expect(abuse2.video.id).to.equal(servers[1].video.id) 168 expect(abuse2.video.id).to.equal(servers[1].store.videoCreated.id)
195 169
196 expect(abuse2.comment).to.be.null 170 expect(abuse2.comment).to.be.null
197 171
198 expect(abuse2.flaggedAccount.name).to.equal('root') 172 expect(abuse2.flaggedAccount.name).to.equal('root')
199 expect(abuse2.flaggedAccount.host).to.equal(servers[1].host) 173 expect(abuse2.flaggedAccount.host).to.equal(servers[1].host)
200 174
201 expect(abuse2.state.id).to.equal(AbuseState.PENDING) 175 expect(abuse2.state.id).to.equal(AbuseState.PENDING)
202 expect(abuse2.state.label).to.equal('Pending') 176 expect(abuse2.state.label).to.equal('Pending')
203 expect(abuse2.moderationComment).to.be.null 177 expect(abuse2.moderationComment).to.be.null
178 }
204 179
205 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 180 {
206 expect(res2.body.total).to.equal(1) 181 const body = await commands[1].getAdminList()
207 expect(res2.body.data.length).to.equal(1) 182 expect(body.total).to.equal(1)
183 expect(body.data.length).to.equal(1)
208 184
209 abuseServer2 = res2.body.data[0] 185 abuseServer2 = body.data[0]
210 expect(abuseServer2.reason).to.equal('my super bad reason 2') 186 expect(abuseServer2.reason).to.equal('my super bad reason 2')
211 expect(abuseServer2.reporterAccount.name).to.equal('root') 187 expect(abuseServer2.reporterAccount.name).to.equal('root')
212 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host) 188 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host)
213 189
214 expect(abuse2.flaggedAccount.name).to.equal('root') 190 expect(abuseServer2.flaggedAccount.name).to.equal('root')
215 expect(abuse2.flaggedAccount.host).to.equal(servers[1].host) 191 expect(abuseServer2.flaggedAccount.host).to.equal(servers[1].host)
216 192
217 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING) 193 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING)
218 expect(abuseServer2.state.label).to.equal('Pending') 194 expect(abuseServer2.state.label).to.equal('Pending')
219 expect(abuseServer2.moderationComment).to.be.null 195 expect(abuseServer2.moderationComment).to.be.null
196 }
220 }) 197 })
221 198
222 it('Should hide video abuses from blocked accounts', async function () { 199 it('Should hide video abuses from blocked accounts', async function () {
223 this.timeout(10000) 200 this.timeout(10000)
224 201
225 { 202 {
226 const videoId = await getVideoIdFromUUID(servers[1].url, servers[0].video.uuid) 203 const videoId = await servers[1].videos.getId({ uuid: servers[0].store.videoCreated.uuid })
227 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'will mute this' }) 204 await commands[1].report({ videoId, reason: 'will mute this' })
228 await waitJobs(servers) 205 await waitJobs(servers)
229 206
230 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 207 const body = await commands[0].getAdminList()
231 expect(res.body.total).to.equal(3) 208 expect(body.total).to.equal(3)
232 } 209 }
233 210
234 const accountToBlock = 'root@' + servers[1].host 211 const accountToBlock = 'root@' + servers[1].host
235 212
236 { 213 {
237 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) 214 await servers[0].blocklist.addToServerBlocklist({ account: accountToBlock })
238 215
239 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 216 const body = await commands[0].getAdminList()
240 expect(res.body.total).to.equal(2) 217 expect(body.total).to.equal(2)
241 218
242 const abuse = res.body.data.find(a => a.reason === 'will mute this') 219 const abuse = body.data.find(a => a.reason === 'will mute this')
243 expect(abuse).to.be.undefined 220 expect(abuse).to.be.undefined
244 } 221 }
245 222
246 { 223 {
247 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) 224 await servers[0].blocklist.removeFromServerBlocklist({ account: accountToBlock })
248 225
249 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 226 const body = await commands[0].getAdminList()
250 expect(res.body.total).to.equal(3) 227 expect(body.total).to.equal(3)
251 } 228 }
252 }) 229 })
253 230
@@ -255,35 +232,35 @@ describe('Test abuses', function () {
255 const serverToBlock = servers[1].host 232 const serverToBlock = servers[1].host
256 233
257 { 234 {
258 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host) 235 await servers[0].blocklist.addToServerBlocklist({ server: serverToBlock })
259 236
260 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 237 const body = await commands[0].getAdminList()
261 expect(res.body.total).to.equal(2) 238 expect(body.total).to.equal(2)
262 239
263 const abuse = res.body.data.find(a => a.reason === 'will mute this') 240 const abuse = body.data.find(a => a.reason === 'will mute this')
264 expect(abuse).to.be.undefined 241 expect(abuse).to.be.undefined
265 } 242 }
266 243
267 { 244 {
268 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, serverToBlock) 245 await servers[0].blocklist.removeFromServerBlocklist({ server: serverToBlock })
269 246
270 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 247 const body = await commands[0].getAdminList()
271 expect(res.body.total).to.equal(3) 248 expect(body.total).to.equal(3)
272 } 249 }
273 }) 250 })
274 251
275 it('Should keep the video abuse when deleting the video', async function () { 252 it('Should keep the video abuse when deleting the video', async function () {
276 this.timeout(10000) 253 this.timeout(10000)
277 254
278 await removeVideo(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid) 255 await servers[1].videos.remove({ id: abuseServer2.video.uuid })
279 256
280 await waitJobs(servers) 257 await waitJobs(servers)
281 258
282 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 259 const body = await commands[1].getAdminList()
283 expect(res.body.total).to.equal(2, "wrong number of videos returned") 260 expect(body.total).to.equal(2, "wrong number of videos returned")
284 expect(res.body.data).to.have.lengthOf(2, "wrong number of videos returned") 261 expect(body.data).to.have.lengthOf(2, "wrong number of videos returned")
285 262
286 const abuse: AdminAbuse = res.body.data[0] 263 const abuse = body.data[0]
287 expect(abuse.id).to.equal(abuseServer2.id, "wrong origin server id for first video") 264 expect(abuse.id).to.equal(abuseServer2.id, "wrong origin server id for first video")
288 expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id") 265 expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id")
289 expect(abuse.video.channel).to.exist 266 expect(abuse.video.channel).to.exist
@@ -295,39 +272,36 @@ describe('Test abuses', function () {
295 272
296 // register a second user to have two reporters/reportees 273 // register a second user to have two reporters/reportees
297 const user = { username: 'user2', password: 'password' } 274 const user = { username: 'user2', password: 'password' }
298 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, ...user }) 275 await servers[0].users.create({ ...user })
299 const userAccessToken = await userLogin(servers[0], user) 276 const userAccessToken = await servers[0].login.getAccessToken(user)
300 277
301 // upload a third video via this user 278 // upload a third video via this user
302 const video3Attributes = { 279 const attributes = {
303 name: 'my second super name for server 1', 280 name: 'my second super name for server 1',
304 description: 'my second super description for server 1' 281 description: 'my second super description for server 1'
305 } 282 }
306 await uploadVideo(servers[0].url, userAccessToken, video3Attributes) 283 const { id } = await servers[0].videos.upload({ token: userAccessToken, attributes })
307 284 const video3Id = id
308 const res1 = await getVideosList(servers[0].url)
309 const videos = res1.body.data
310 const video3 = videos.find(video => video.name === 'my second super name for server 1')
311 285
312 // resume with the test 286 // resume with the test
313 const reason3 = 'my super bad reason 3' 287 const reason3 = 'my super bad reason 3'
314 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: video3.id, reason: reason3 }) 288 await commands[0].report({ videoId: video3Id, reason: reason3 })
315 289
316 const reason4 = 'my super bad reason 4' 290 const reason4 = 'my super bad reason 4'
317 await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: reason4 }) 291 await commands[0].report({ token: userAccessToken, videoId: servers[0].store.videoCreated.id, reason: reason4 })
318 292
319 { 293 {
320 const res2 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 294 const body = await commands[0].getAdminList()
321 const abuses = res2.body.data as AdminAbuse[] 295 const abuses = body.data
322 296
323 const abuseVideo3 = res2.body.data.find(a => a.video.id === video3.id) 297 const abuseVideo3 = body.data.find(a => a.video.id === video3Id)
324 expect(abuseVideo3).to.not.be.undefined 298 expect(abuseVideo3).to.not.be.undefined
325 expect(abuseVideo3.video.countReports).to.equal(1, "wrong reports count for video 3") 299 expect(abuseVideo3.video.countReports).to.equal(1, "wrong reports count for video 3")
326 expect(abuseVideo3.video.nthReport).to.equal(1, "wrong report position in report list for video 3") 300 expect(abuseVideo3.video.nthReport).to.equal(1, "wrong report position in report list for video 3")
327 expect(abuseVideo3.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse") 301 expect(abuseVideo3.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse")
328 expect(abuseVideo3.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse") 302 expect(abuseVideo3.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse")
329 303
330 const abuseServer1 = abuses.find(a => a.video.id === servers[0].video.id) 304 const abuseServer1 = abuses.find(a => a.video.id === servers[0].store.videoCreated.id)
331 expect(abuseServer1.countReportsForReportee).to.equal(3, "wrong reports count for reporter on video 1 abuse") 305 expect(abuseServer1.countReportsForReportee).to.equal(3, "wrong reports count for reporter on video 1 abuse")
332 } 306 }
333 }) 307 })
@@ -337,20 +311,18 @@ describe('Test abuses', function () {
337 311
338 const reason5 = 'my super bad reason 5' 312 const reason5 = 'my super bad reason 5'
339 const predefinedReasons5: AbusePredefinedReasonsString[] = [ 'violentOrRepulsive', 'captions' ] 313 const predefinedReasons5: AbusePredefinedReasonsString[] = [ 'violentOrRepulsive', 'captions' ]
340 const createdAbuse = (await reportAbuse({ 314 const createRes = await commands[0].report({
341 url: servers[0].url, 315 videoId: servers[0].store.videoCreated.id,
342 token: servers[0].accessToken,
343 videoId: servers[0].video.id,
344 reason: reason5, 316 reason: reason5,
345 predefinedReasons: predefinedReasons5, 317 predefinedReasons: predefinedReasons5,
346 startAt: 1, 318 startAt: 1,
347 endAt: 5 319 endAt: 5
348 })).body.abuse 320 })
349 321
350 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 322 const body = await commands[0].getAdminList()
351 323
352 { 324 {
353 const abuse = (res.body.data as AdminAbuse[]).find(a => a.id === createdAbuse.id) 325 const abuse = body.data.find(a => a.id === createRes.abuse.id)
354 expect(abuse.reason).to.equals(reason5) 326 expect(abuse.reason).to.equals(reason5)
355 expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported") 327 expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported")
356 expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported") 328 expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported")
@@ -361,37 +333,30 @@ describe('Test abuses', function () {
361 it('Should delete the video abuse', async function () { 333 it('Should delete the video abuse', async function () {
362 this.timeout(10000) 334 this.timeout(10000)
363 335
364 await deleteAbuse(servers[1].url, servers[1].accessToken, abuseServer2.id) 336 await commands[1].delete({ abuseId: abuseServer2.id })
365 337
366 await waitJobs(servers) 338 await waitJobs(servers)
367 339
368 { 340 {
369 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) 341 const body = await commands[1].getAdminList()
370 expect(res.body.total).to.equal(1) 342 expect(body.total).to.equal(1)
371 expect(res.body.data.length).to.equal(1) 343 expect(body.data.length).to.equal(1)
372 expect(res.body.data[0].id).to.not.equal(abuseServer2.id) 344 expect(body.data[0].id).to.not.equal(abuseServer2.id)
373 } 345 }
374 346
375 { 347 {
376 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) 348 const body = await commands[0].getAdminList()
377 expect(res.body.total).to.equal(6) 349 expect(body.total).to.equal(6)
378 } 350 }
379 }) 351 })
380 352
381 it('Should list and filter video abuses', async function () { 353 it('Should list and filter video abuses', async function () {
382 this.timeout(10000) 354 this.timeout(10000)
383 355
384 async function list (query: Omit<Parameters<typeof getAdminAbusesList>[0], 'url' | 'token'>) { 356 async function list (query: Parameters<AbusesCommand['getAdminList']>[0]) {
385 const options = { 357 const body = await commands[0].getAdminList(query)
386 url: servers[0].url,
387 token: servers[0].accessToken
388 }
389
390 Object.assign(options, query)
391 358
392 const res = await getAdminAbusesList(options) 359 return body.data
393
394 return res.body.data as AdminAbuse[]
395 } 360 }
396 361
397 expect(await list({ id: 56 })).to.have.lengthOf(0) 362 expect(await list({ id: 56 })).to.have.lengthOf(0)
@@ -424,24 +389,24 @@ describe('Test abuses', function () {
424 389
425 describe('Comment abuses', function () { 390 describe('Comment abuses', function () {
426 391
427 async function getComment (url: string, videoIdArg: number | string) { 392 async function getComment (server: PeerTubeServer, videoIdArg: number | string) {
428 const videoId = typeof videoIdArg === 'string' 393 const videoId = typeof videoIdArg === 'string'
429 ? await getVideoIdFromUUID(url, videoIdArg) 394 ? await server.videos.getId({ uuid: videoIdArg })
430 : videoIdArg 395 : videoIdArg
431 396
432 const res = await getVideoCommentThreads(url, videoId, 0, 5) 397 const { data } = await server.comments.listThreads({ videoId })
433 398
434 return res.body.data[0] as VideoComment 399 return data[0]
435 } 400 }
436 401
437 before(async function () { 402 before(async function () {
438 this.timeout(50000) 403 this.timeout(50000)
439 404
440 servers[0].video = await uploadVideoAndGetId({ server: servers[0], videoName: 'server 1' }) 405 servers[0].store.videoCreated = await servers[0].videos.quickUpload({ name: 'server 1' })
441 servers[1].video = await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' }) 406 servers[1].store.videoCreated = await servers[1].videos.quickUpload({ name: 'server 2' })
442 407
443 await addVideoCommentThread(servers[0].url, servers[0].accessToken, servers[0].video.id, 'comment server 1') 408 await servers[0].comments.createThread({ videoId: servers[0].store.videoCreated.id, text: 'comment server 1' })
444 await addVideoCommentThread(servers[1].url, servers[1].accessToken, servers[1].video.id, 'comment server 2') 409 await servers[1].comments.createThread({ videoId: servers[1].store.videoCreated.id, text: 'comment server 2' })
445 410
446 await waitJobs(servers) 411 await waitJobs(servers)
447 }) 412 })
@@ -449,23 +414,23 @@ describe('Test abuses', function () {
449 it('Should report abuse on a comment', async function () { 414 it('Should report abuse on a comment', async function () {
450 this.timeout(15000) 415 this.timeout(15000)
451 416
452 const comment = await getComment(servers[0].url, servers[0].video.id) 417 const comment = await getComment(servers[0], servers[0].store.videoCreated.id)
453 418
454 const reason = 'it is a bad comment' 419 const reason = 'it is a bad comment'
455 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, commentId: comment.id, reason }) 420 await commands[0].report({ commentId: comment.id, reason })
456 421
457 await waitJobs(servers) 422 await waitJobs(servers)
458 }) 423 })
459 424
460 it('Should have 1 comment abuse on server 1 and 0 on server 2', async function () { 425 it('Should have 1 comment abuse on server 1 and 0 on server 2', async function () {
461 { 426 {
462 const comment = await getComment(servers[0].url, servers[0].video.id) 427 const comment = await getComment(servers[0], servers[0].store.videoCreated.id)
463 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 428 const body = await commands[0].getAdminList({ filter: 'comment' })
464 429
465 expect(res.body.total).to.equal(1) 430 expect(body.total).to.equal(1)
466 expect(res.body.data).to.have.lengthOf(1) 431 expect(body.data).to.have.lengthOf(1)
467 432
468 const abuse: AdminAbuse = res.body.data[0] 433 const abuse = body.data[0]
469 expect(abuse.reason).to.equal('it is a bad comment') 434 expect(abuse.reason).to.equal('it is a bad comment')
470 435
471 expect(abuse.reporterAccount.name).to.equal('root') 436 expect(abuse.reporterAccount.name).to.equal('root')
@@ -477,98 +442,102 @@ describe('Test abuses', function () {
477 expect(abuse.comment.id).to.equal(comment.id) 442 expect(abuse.comment.id).to.equal(comment.id)
478 expect(abuse.comment.text).to.equal(comment.text) 443 expect(abuse.comment.text).to.equal(comment.text)
479 expect(abuse.comment.video.name).to.equal('server 1') 444 expect(abuse.comment.video.name).to.equal('server 1')
480 expect(abuse.comment.video.id).to.equal(servers[0].video.id) 445 expect(abuse.comment.video.id).to.equal(servers[0].store.videoCreated.id)
481 expect(abuse.comment.video.uuid).to.equal(servers[0].video.uuid) 446 expect(abuse.comment.video.uuid).to.equal(servers[0].store.videoCreated.uuid)
482 447
483 expect(abuse.countReportsForReporter).to.equal(5) 448 expect(abuse.countReportsForReporter).to.equal(5)
484 expect(abuse.countReportsForReportee).to.equal(5) 449 expect(abuse.countReportsForReportee).to.equal(5)
485 } 450 }
486 451
487 { 452 {
488 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 453 const body = await commands[1].getAdminList({ filter: 'comment' })
489 expect(res.body.total).to.equal(0) 454 expect(body.total).to.equal(0)
490 expect(res.body.data.length).to.equal(0) 455 expect(body.data.length).to.equal(0)
491 } 456 }
492 }) 457 })
493 458
494 it('Should report abuse on a remote comment', async function () { 459 it('Should report abuse on a remote comment', async function () {
495 this.timeout(10000) 460 this.timeout(10000)
496 461
497 const comment = await getComment(servers[0].url, servers[1].video.uuid) 462 const comment = await getComment(servers[0], servers[1].store.videoCreated.uuid)
498 463
499 const reason = 'it is a really bad comment' 464 const reason = 'it is a really bad comment'
500 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, commentId: comment.id, reason }) 465 await commands[0].report({ commentId: comment.id, reason })
501 466
502 await waitJobs(servers) 467 await waitJobs(servers)
503 }) 468 })
504 469
505 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { 470 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () {
506 const commentServer2 = await getComment(servers[0].url, servers[1].video.id) 471 const commentServer2 = await getComment(servers[0], servers[1].store.videoCreated.id)
507 472
508 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 473 {
509 expect(res1.body.total).to.equal(2) 474 const body = await commands[0].getAdminList({ filter: 'comment' })
510 expect(res1.body.data.length).to.equal(2) 475 expect(body.total).to.equal(2)
476 expect(body.data.length).to.equal(2)
511 477
512 const abuse: AdminAbuse = res1.body.data[0] 478 const abuse = body.data[0]
513 expect(abuse.reason).to.equal('it is a bad comment') 479 expect(abuse.reason).to.equal('it is a bad comment')
514 expect(abuse.countReportsForReporter).to.equal(6) 480 expect(abuse.countReportsForReporter).to.equal(6)
515 expect(abuse.countReportsForReportee).to.equal(5) 481 expect(abuse.countReportsForReportee).to.equal(5)
516 482
517 const abuse2: AdminAbuse = res1.body.data[1] 483 const abuse2 = body.data[1]
518 484
519 expect(abuse2.reason).to.equal('it is a really bad comment') 485 expect(abuse2.reason).to.equal('it is a really bad comment')
520 486
521 expect(abuse2.reporterAccount.name).to.equal('root') 487 expect(abuse2.reporterAccount.name).to.equal('root')
522 expect(abuse2.reporterAccount.host).to.equal(servers[0].host) 488 expect(abuse2.reporterAccount.host).to.equal(servers[0].host)
523 489
524 expect(abuse2.video).to.be.null 490 expect(abuse2.video).to.be.null
525 491
526 expect(abuse2.comment.deleted).to.be.false 492 expect(abuse2.comment.deleted).to.be.false
527 expect(abuse2.comment.id).to.equal(commentServer2.id) 493 expect(abuse2.comment.id).to.equal(commentServer2.id)
528 expect(abuse2.comment.text).to.equal(commentServer2.text) 494 expect(abuse2.comment.text).to.equal(commentServer2.text)
529 expect(abuse2.comment.video.name).to.equal('server 2') 495 expect(abuse2.comment.video.name).to.equal('server 2')
530 expect(abuse2.comment.video.uuid).to.equal(servers[1].video.uuid) 496 expect(abuse2.comment.video.uuid).to.equal(servers[1].store.videoCreated.uuid)
531 497
532 expect(abuse2.state.id).to.equal(AbuseState.PENDING) 498 expect(abuse2.state.id).to.equal(AbuseState.PENDING)
533 expect(abuse2.state.label).to.equal('Pending') 499 expect(abuse2.state.label).to.equal('Pending')
534 500
535 expect(abuse2.moderationComment).to.be.null 501 expect(abuse2.moderationComment).to.be.null
536 502
537 expect(abuse2.countReportsForReporter).to.equal(6) 503 expect(abuse2.countReportsForReporter).to.equal(6)
538 expect(abuse2.countReportsForReportee).to.equal(2) 504 expect(abuse2.countReportsForReportee).to.equal(2)
505 }
539 506
540 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 507 {
541 expect(res2.body.total).to.equal(1) 508 const body = await commands[1].getAdminList({ filter: 'comment' })
542 expect(res2.body.data.length).to.equal(1) 509 expect(body.total).to.equal(1)
510 expect(body.data.length).to.equal(1)
543 511
544 abuseServer2 = res2.body.data[0] 512 abuseServer2 = body.data[0]
545 expect(abuseServer2.reason).to.equal('it is a really bad comment') 513 expect(abuseServer2.reason).to.equal('it is a really bad comment')
546 expect(abuseServer2.reporterAccount.name).to.equal('root') 514 expect(abuseServer2.reporterAccount.name).to.equal('root')
547 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host) 515 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host)
548 516
549 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING) 517 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING)
550 expect(abuseServer2.state.label).to.equal('Pending') 518 expect(abuseServer2.state.label).to.equal('Pending')
551 519
552 expect(abuseServer2.moderationComment).to.be.null 520 expect(abuseServer2.moderationComment).to.be.null
553 521
554 expect(abuseServer2.countReportsForReporter).to.equal(1) 522 expect(abuseServer2.countReportsForReporter).to.equal(1)
555 expect(abuseServer2.countReportsForReportee).to.equal(1) 523 expect(abuseServer2.countReportsForReportee).to.equal(1)
524 }
556 }) 525 })
557 526
558 it('Should keep the comment abuse when deleting the comment', async function () { 527 it('Should keep the comment abuse when deleting the comment', async function () {
559 this.timeout(10000) 528 this.timeout(10000)
560 529
561 const commentServer2 = await getComment(servers[0].url, servers[1].video.id) 530 const commentServer2 = await getComment(servers[0], servers[1].store.videoCreated.id)
562 531
563 await deleteVideoComment(servers[0].url, servers[0].accessToken, servers[1].video.uuid, commentServer2.id) 532 await servers[0].comments.delete({ videoId: servers[1].store.videoCreated.uuid, commentId: commentServer2.id })
564 533
565 await waitJobs(servers) 534 await waitJobs(servers)
566 535
567 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 536 const body = await commands[0].getAdminList({ filter: 'comment' })
568 expect(res.body.total).to.equal(2) 537 expect(body.total).to.equal(2)
569 expect(res.body.data).to.have.lengthOf(2) 538 expect(body.data).to.have.lengthOf(2)
570 539
571 const abuse = (res.body.data as AdminAbuse[]).find(a => a.comment?.id === commentServer2.id) 540 const abuse = body.data.find(a => a.comment?.id === commentServer2.id)
572 expect(abuse).to.not.be.undefined 541 expect(abuse).to.not.be.undefined
573 542
574 expect(abuse.comment.text).to.be.empty 543 expect(abuse.comment.text).to.be.empty
@@ -579,72 +548,60 @@ describe('Test abuses', function () {
579 it('Should delete the comment abuse', async function () { 548 it('Should delete the comment abuse', async function () {
580 this.timeout(10000) 549 this.timeout(10000)
581 550
582 await deleteAbuse(servers[1].url, servers[1].accessToken, abuseServer2.id) 551 await commands[1].delete({ abuseId: abuseServer2.id })
583 552
584 await waitJobs(servers) 553 await waitJobs(servers)
585 554
586 { 555 {
587 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 556 const body = await commands[1].getAdminList({ filter: 'comment' })
588 expect(res.body.total).to.equal(0) 557 expect(body.total).to.equal(0)
589 expect(res.body.data.length).to.equal(0) 558 expect(body.data.length).to.equal(0)
590 } 559 }
591 560
592 { 561 {
593 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) 562 const body = await commands[0].getAdminList({ filter: 'comment' })
594 expect(res.body.total).to.equal(2) 563 expect(body.total).to.equal(2)
595 } 564 }
596 }) 565 })
597 566
598 it('Should list and filter video abuses', async function () { 567 it('Should list and filter video abuses', async function () {
599 { 568 {
600 const res = await getAdminAbusesList({ 569 const body = await commands[0].getAdminList({ filter: 'comment', searchReportee: 'foo' })
601 url: servers[0].url, 570 expect(body.total).to.equal(0)
602 token: servers[0].accessToken,
603 filter: 'comment',
604 searchReportee: 'foo'
605 })
606 expect(res.body.total).to.equal(0)
607 } 571 }
608 572
609 { 573 {
610 const res = await getAdminAbusesList({ 574 const body = await commands[0].getAdminList({ filter: 'comment', searchReportee: 'ot' })
611 url: servers[0].url, 575 expect(body.total).to.equal(2)
612 token: servers[0].accessToken,
613 filter: 'comment',
614 searchReportee: 'ot'
615 })
616 expect(res.body.total).to.equal(2)
617 } 576 }
618 577
619 { 578 {
620 const baseParams = { url: servers[0].url, token: servers[0].accessToken, filter: 'comment' as AbuseFilter, start: 1, count: 1 } 579 const body = await commands[0].getAdminList({ filter: 'comment', start: 1, count: 1, sort: 'createdAt' })
621 580 expect(body.data).to.have.lengthOf(1)
622 const res1 = await getAdminAbusesList(immutableAssign(baseParams, { sort: 'createdAt' })) 581 expect(body.data[0].comment.text).to.be.empty
623 expect(res1.body.data).to.have.lengthOf(1) 582 }
624 expect(res1.body.data[0].comment.text).to.be.empty
625 583
626 const res2 = await getAdminAbusesList(immutableAssign(baseParams, { sort: '-createdAt' })) 584 {
627 expect(res2.body.data).to.have.lengthOf(1) 585 const body = await commands[0].getAdminList({ filter: 'comment', start: 1, count: 1, sort: '-createdAt' })
628 expect(res2.body.data[0].comment.text).to.equal('comment server 1') 586 expect(body.data).to.have.lengthOf(1)
587 expect(body.data[0].comment.text).to.equal('comment server 1')
629 } 588 }
630 }) 589 })
631 }) 590 })
632 591
633 describe('Account abuses', function () { 592 describe('Account abuses', function () {
634 593
635 async function getAccountFromServer (url: string, name: string, server: ServerInfo) { 594 function getAccountFromServer (server: PeerTubeServer, targetName: string, targetServer: PeerTubeServer) {
636 const res = await getAccount(url, name + '@' + server.host) 595 return server.accounts.get({ accountName: targetName + '@' + targetServer.host })
637
638 return res.body as Account
639 } 596 }
640 597
641 before(async function () { 598 before(async function () {
642 this.timeout(50000) 599 this.timeout(50000)
643 600
644 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: 'user_1', password: 'donald' }) 601 await servers[0].users.create({ username: 'user_1', password: 'donald' })
645 602
646 const token = await generateUserAccessToken(servers[1], 'user_2') 603 const token = await servers[1].users.generateUserAndToken('user_2')
647 await uploadVideo(servers[1].url, token, { name: 'super video' }) 604 await servers[1].videos.upload({ token, attributes: { name: 'super video' } })
648 605
649 await waitJobs(servers) 606 await waitJobs(servers)
650 }) 607 })
@@ -652,22 +609,22 @@ describe('Test abuses', function () {
652 it('Should report abuse on an account', async function () { 609 it('Should report abuse on an account', async function () {
653 this.timeout(15000) 610 this.timeout(15000)
654 611
655 const account = await getAccountFromServer(servers[0].url, 'user_1', servers[0]) 612 const account = await getAccountFromServer(servers[0], 'user_1', servers[0])
656 613
657 const reason = 'it is a bad account' 614 const reason = 'it is a bad account'
658 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, accountId: account.id, reason }) 615 await commands[0].report({ accountId: account.id, reason })
659 616
660 await waitJobs(servers) 617 await waitJobs(servers)
661 }) 618 })
662 619
663 it('Should have 1 account abuse on server 1 and 0 on server 2', async function () { 620 it('Should have 1 account abuse on server 1 and 0 on server 2', async function () {
664 { 621 {
665 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 622 const body = await commands[0].getAdminList({ filter: 'account' })
666 623
667 expect(res.body.total).to.equal(1) 624 expect(body.total).to.equal(1)
668 expect(res.body.data).to.have.lengthOf(1) 625 expect(body.data).to.have.lengthOf(1)
669 626
670 const abuse: AdminAbuse = res.body.data[0] 627 const abuse = body.data[0]
671 expect(abuse.reason).to.equal('it is a bad account') 628 expect(abuse.reason).to.equal('it is a bad account')
672 629
673 expect(abuse.reporterAccount.name).to.equal('root') 630 expect(abuse.reporterAccount.name).to.equal('root')
@@ -681,96 +638,100 @@ describe('Test abuses', function () {
681 } 638 }
682 639
683 { 640 {
684 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) 641 const body = await commands[1].getAdminList({ filter: 'comment' })
685 expect(res.body.total).to.equal(0) 642 expect(body.total).to.equal(0)
686 expect(res.body.data.length).to.equal(0) 643 expect(body.data.length).to.equal(0)
687 } 644 }
688 }) 645 })
689 646
690 it('Should report abuse on a remote account', async function () { 647 it('Should report abuse on a remote account', async function () {
691 this.timeout(10000) 648 this.timeout(10000)
692 649
693 const account = await getAccountFromServer(servers[0].url, 'user_2', servers[1]) 650 const account = await getAccountFromServer(servers[0], 'user_2', servers[1])
694 651
695 const reason = 'it is a really bad account' 652 const reason = 'it is a really bad account'
696 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, accountId: account.id, reason }) 653 await commands[0].report({ accountId: account.id, reason })
697 654
698 await waitJobs(servers) 655 await waitJobs(servers)
699 }) 656 })
700 657
701 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { 658 it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () {
702 const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 659 {
703 expect(res1.body.total).to.equal(2) 660 const body = await commands[0].getAdminList({ filter: 'account' })
704 expect(res1.body.data.length).to.equal(2) 661 expect(body.total).to.equal(2)
662 expect(body.data.length).to.equal(2)
705 663
706 const abuse: AdminAbuse = res1.body.data[0] 664 const abuse: AdminAbuse = body.data[0]
707 expect(abuse.reason).to.equal('it is a bad account') 665 expect(abuse.reason).to.equal('it is a bad account')
708 666
709 const abuse2: AdminAbuse = res1.body.data[1] 667 const abuse2: AdminAbuse = body.data[1]
710 expect(abuse2.reason).to.equal('it is a really bad account') 668 expect(abuse2.reason).to.equal('it is a really bad account')
711 669
712 expect(abuse2.reporterAccount.name).to.equal('root') 670 expect(abuse2.reporterAccount.name).to.equal('root')
713 expect(abuse2.reporterAccount.host).to.equal(servers[0].host) 671 expect(abuse2.reporterAccount.host).to.equal(servers[0].host)
714 672
715 expect(abuse2.video).to.be.null 673 expect(abuse2.video).to.be.null
716 expect(abuse2.comment).to.be.null 674 expect(abuse2.comment).to.be.null
717 675
718 expect(abuse2.state.id).to.equal(AbuseState.PENDING) 676 expect(abuse2.state.id).to.equal(AbuseState.PENDING)
719 expect(abuse2.state.label).to.equal('Pending') 677 expect(abuse2.state.label).to.equal('Pending')
720 678
721 expect(abuse2.moderationComment).to.be.null 679 expect(abuse2.moderationComment).to.be.null
680 }
722 681
723 const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) 682 {
724 expect(res2.body.total).to.equal(1) 683 const body = await commands[1].getAdminList({ filter: 'account' })
725 expect(res2.body.data.length).to.equal(1) 684 expect(body.total).to.equal(1)
685 expect(body.data.length).to.equal(1)
726 686
727 abuseServer2 = res2.body.data[0] 687 abuseServer2 = body.data[0]
728 688
729 expect(abuseServer2.reason).to.equal('it is a really bad account') 689 expect(abuseServer2.reason).to.equal('it is a really bad account')
730 690
731 expect(abuseServer2.reporterAccount.name).to.equal('root') 691 expect(abuseServer2.reporterAccount.name).to.equal('root')
732 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host) 692 expect(abuseServer2.reporterAccount.host).to.equal(servers[0].host)
733 693
734 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING) 694 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING)
735 expect(abuseServer2.state.label).to.equal('Pending') 695 expect(abuseServer2.state.label).to.equal('Pending')
736 696
737 expect(abuseServer2.moderationComment).to.be.null 697 expect(abuseServer2.moderationComment).to.be.null
698 }
738 }) 699 })
739 700
740 it('Should keep the account abuse when deleting the account', async function () { 701 it('Should keep the account abuse when deleting the account', async function () {
741 this.timeout(10000) 702 this.timeout(10000)
742 703
743 const account = await getAccountFromServer(servers[1].url, 'user_2', servers[1]) 704 const account = await getAccountFromServer(servers[1], 'user_2', servers[1])
744 await removeUser(servers[1].url, account.userId, servers[1].accessToken) 705 await servers[1].users.remove({ userId: account.userId })
745 706
746 await waitJobs(servers) 707 await waitJobs(servers)
747 708
748 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 709 const body = await commands[0].getAdminList({ filter: 'account' })
749 expect(res.body.total).to.equal(2) 710 expect(body.total).to.equal(2)
750 expect(res.body.data).to.have.lengthOf(2) 711 expect(body.data).to.have.lengthOf(2)
751 712
752 const abuse = (res.body.data as AdminAbuse[]).find(a => a.reason === 'it is a really bad account') 713 const abuse = body.data.find(a => a.reason === 'it is a really bad account')
753 expect(abuse).to.not.be.undefined 714 expect(abuse).to.not.be.undefined
754 }) 715 })
755 716
756 it('Should delete the account abuse', async function () { 717 it('Should delete the account abuse', async function () {
757 this.timeout(10000) 718 this.timeout(10000)
758 719
759 await deleteAbuse(servers[1].url, servers[1].accessToken, abuseServer2.id) 720 await commands[1].delete({ abuseId: abuseServer2.id })
760 721
761 await waitJobs(servers) 722 await waitJobs(servers)
762 723
763 { 724 {
764 const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) 725 const body = await commands[1].getAdminList({ filter: 'account' })
765 expect(res.body.total).to.equal(0) 726 expect(body.total).to.equal(0)
766 expect(res.body.data.length).to.equal(0) 727 expect(body.data.length).to.equal(0)
767 } 728 }
768 729
769 { 730 {
770 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) 731 const body = await commands[0].getAdminList({ filter: 'account' })
771 expect(res.body.total).to.equal(2) 732 expect(body.total).to.equal(2)
772 733
773 abuseServer1 = res.body.data[0] 734 abuseServer1 = body.data[0]
774 } 735 }
775 }) 736 })
776 }) 737 })
@@ -778,20 +739,18 @@ describe('Test abuses', function () {
778 describe('Common actions on abuses', function () { 739 describe('Common actions on abuses', function () {
779 740
780 it('Should update the state of an abuse', async function () { 741 it('Should update the state of an abuse', async function () {
781 const body = { state: AbuseState.REJECTED } 742 await commands[0].update({ abuseId: abuseServer1.id, body: { state: AbuseState.REJECTED } })
782 await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body)
783 743
784 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) 744 const body = await commands[0].getAdminList({ id: abuseServer1.id })
785 expect(res.body.data[0].state.id).to.equal(AbuseState.REJECTED) 745 expect(body.data[0].state.id).to.equal(AbuseState.REJECTED)
786 }) 746 })
787 747
788 it('Should add a moderation comment', async function () { 748 it('Should add a moderation comment', async function () {
789 const body = { state: AbuseState.ACCEPTED, moderationComment: 'It is valid' } 749 await commands[0].update({ abuseId: abuseServer1.id, body: { state: AbuseState.ACCEPTED, moderationComment: 'Valid' } })
790 await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body)
791 750
792 const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) 751 const body = await commands[0].getAdminList({ id: abuseServer1.id })
793 expect(res.body.data[0].state.id).to.equal(AbuseState.ACCEPTED) 752 expect(body.data[0].state.id).to.equal(AbuseState.ACCEPTED)
794 expect(res.body.data[0].moderationComment).to.equal('It is valid') 753 expect(body.data[0].moderationComment).to.equal('Valid')
795 }) 754 })
796 }) 755 })
797 756
@@ -800,20 +759,20 @@ describe('Test abuses', function () {
800 let userAccessToken: string 759 let userAccessToken: string
801 760
802 before(async function () { 761 before(async function () {
803 userAccessToken = await generateUserAccessToken(servers[0], 'user_42') 762 userAccessToken = await servers[0].users.generateUserAndToken('user_42')
804 763
805 await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: 'user reason 1' }) 764 await commands[0].report({ token: userAccessToken, videoId: servers[0].store.videoCreated.id, reason: 'user reason 1' })
806 765
807 const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid) 766 const videoId = await servers[0].videos.getId({ uuid: servers[1].store.videoCreated.uuid })
808 await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId, reason: 'user reason 2' }) 767 await commands[0].report({ token: userAccessToken, videoId, reason: 'user reason 2' })
809 }) 768 })
810 769
811 it('Should correctly list my abuses', async function () { 770 it('Should correctly list my abuses', async function () {
812 { 771 {
813 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 5, sort: 'createdAt' }) 772 const body = await commands[0].getUserList({ token: userAccessToken, start: 0, count: 5, sort: 'createdAt' })
814 expect(res.body.total).to.equal(2) 773 expect(body.total).to.equal(2)
815 774
816 const abuses: UserAbuse[] = res.body.data 775 const abuses = body.data
817 expect(abuses[0].reason).to.equal('user reason 1') 776 expect(abuses[0].reason).to.equal('user reason 1')
818 expect(abuses[1].reason).to.equal('user reason 2') 777 expect(abuses[1].reason).to.equal('user reason 2')
819 778
@@ -821,95 +780,77 @@ describe('Test abuses', function () {
821 } 780 }
822 781
823 { 782 {
824 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: 'createdAt' }) 783 const body = await commands[0].getUserList({ token: userAccessToken, start: 1, count: 1, sort: 'createdAt' })
825 expect(res.body.total).to.equal(2) 784 expect(body.total).to.equal(2)
826 785
827 const abuses: UserAbuse[] = res.body.data 786 const abuses: UserAbuse[] = body.data
828 expect(abuses[0].reason).to.equal('user reason 2') 787 expect(abuses[0].reason).to.equal('user reason 2')
829 } 788 }
830 789
831 { 790 {
832 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: '-createdAt' }) 791 const body = await commands[0].getUserList({ token: userAccessToken, start: 1, count: 1, sort: '-createdAt' })
833 expect(res.body.total).to.equal(2) 792 expect(body.total).to.equal(2)
834 793
835 const abuses: UserAbuse[] = res.body.data 794 const abuses: UserAbuse[] = body.data
836 expect(abuses[0].reason).to.equal('user reason 1') 795 expect(abuses[0].reason).to.equal('user reason 1')
837 } 796 }
838 }) 797 })
839 798
840 it('Should correctly filter my abuses by id', async function () { 799 it('Should correctly filter my abuses by id', async function () {
841 const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, id: abuseId1 }) 800 const body = await commands[0].getUserList({ token: userAccessToken, id: abuseId1 })
801 expect(body.total).to.equal(1)
842 802
843 expect(res.body.total).to.equal(1) 803 const abuses: UserAbuse[] = body.data
844
845 const abuses: UserAbuse[] = res.body.data
846 expect(abuses[0].reason).to.equal('user reason 1') 804 expect(abuses[0].reason).to.equal('user reason 1')
847 }) 805 })
848 806
849 it('Should correctly filter my abuses by search', async function () { 807 it('Should correctly filter my abuses by search', async function () {
850 const res = await getUserAbusesList({ 808 const body = await commands[0].getUserList({ token: userAccessToken, search: 'server 2' })
851 url: servers[0].url, 809 expect(body.total).to.equal(1)
852 token: userAccessToken,
853 search: 'server 2'
854 })
855
856 expect(res.body.total).to.equal(1)
857 810
858 const abuses: UserAbuse[] = res.body.data 811 const abuses: UserAbuse[] = body.data
859 expect(abuses[0].reason).to.equal('user reason 2') 812 expect(abuses[0].reason).to.equal('user reason 2')
860 }) 813 })
861 814
862 it('Should correctly filter my abuses by state', async function () { 815 it('Should correctly filter my abuses by state', async function () {
863 const body = { state: AbuseState.REJECTED } 816 await commands[0].update({ abuseId: abuseId1, body: { state: AbuseState.REJECTED } })
864 await updateAbuse(servers[0].url, servers[0].accessToken, abuseId1, body)
865 817
866 const res = await getUserAbusesList({ 818 const body = await commands[0].getUserList({ token: userAccessToken, state: AbuseState.REJECTED })
867 url: servers[0].url, 819 expect(body.total).to.equal(1)
868 token: userAccessToken,
869 state: AbuseState.REJECTED
870 })
871
872 expect(res.body.total).to.equal(1)
873 820
874 const abuses: UserAbuse[] = res.body.data 821 const abuses: UserAbuse[] = body.data
875 expect(abuses[0].reason).to.equal('user reason 1') 822 expect(abuses[0].reason).to.equal('user reason 1')
876 }) 823 })
877 }) 824 })
878 825
879 describe('Abuse messages', async function () { 826 describe('Abuse messages', async function () {
880 let abuseId: number 827 let abuseId: number
881 let userAccessToken: string 828 let userToken: string
882 let abuseMessageUserId: number 829 let abuseMessageUserId: number
883 let abuseMessageModerationId: number 830 let abuseMessageModerationId: number
884 831
885 before(async function () { 832 before(async function () {
886 userAccessToken = await generateUserAccessToken(servers[0], 'user_43') 833 userToken = await servers[0].users.generateUserAndToken('user_43')
887 834
888 const res = await reportAbuse({ 835 const body = await commands[0].report({ token: userToken, videoId: servers[0].store.videoCreated.id, reason: 'user 43 reason 1' })
889 url: servers[0].url, 836 abuseId = body.abuse.id
890 token: userAccessToken,
891 videoId: servers[0].video.id,
892 reason: 'user 43 reason 1'
893 })
894
895 abuseId = res.body.abuse.id
896 }) 837 })
897 838
898 it('Should create some messages on the abuse', async function () { 839 it('Should create some messages on the abuse', async function () {
899 await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 1') 840 await commands[0].addMessage({ token: userToken, abuseId, message: 'message 1' })
900 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 2') 841 await commands[0].addMessage({ abuseId, message: 'message 2' })
901 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 3') 842 await commands[0].addMessage({ abuseId, message: 'message 3' })
902 await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 4') 843 await commands[0].addMessage({ token: userToken, abuseId, message: 'message 4' })
903 }) 844 })
904 845
905 it('Should have the correct messages count when listing abuses', async function () { 846 it('Should have the correct messages count when listing abuses', async function () {
906 const results = await Promise.all([ 847 const results = await Promise.all([
907 getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, start: 0, count: 50 }), 848 commands[0].getAdminList({ start: 0, count: 50 }),
908 getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 50 }) 849 commands[0].getUserList({ token: userToken, start: 0, count: 50 })
909 ]) 850 ])
910 851
911 for (const res of results) { 852 for (const body of results) {
912 const abuses: AdminAbuse[] = res.body.data 853 const abuses = body.data
913 const abuse = abuses.find(a => a.id === abuseId) 854 const abuse = abuses.find(a => a.id === abuseId)
914 expect(abuse.countMessages).to.equal(4) 855 expect(abuse.countMessages).to.equal(4)
915 } 856 }
@@ -917,14 +858,14 @@ describe('Test abuses', function () {
917 858
918 it('Should correctly list messages of this abuse', async function () { 859 it('Should correctly list messages of this abuse', async function () {
919 const results = await Promise.all([ 860 const results = await Promise.all([
920 listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId), 861 commands[0].listMessages({ abuseId }),
921 listAbuseMessages(servers[0].url, userAccessToken, abuseId) 862 commands[0].listMessages({ token: userToken, abuseId })
922 ]) 863 ])
923 864
924 for (const res of results) { 865 for (const body of results) {
925 expect(res.body.total).to.equal(4) 866 expect(body.total).to.equal(4)
926 867
927 const abuseMessages: AbuseMessage[] = res.body.data 868 const abuseMessages: AbuseMessage[] = body.data
928 869
929 expect(abuseMessages[0].message).to.equal('message 1') 870 expect(abuseMessages[0].message).to.equal('message 1')
930 expect(abuseMessages[0].byModerator).to.be.false 871 expect(abuseMessages[0].byModerator).to.be.false
@@ -948,19 +889,18 @@ describe('Test abuses', function () {
948 }) 889 })
949 890
950 it('Should delete messages', async function () { 891 it('Should delete messages', async function () {
951 await deleteAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, abuseMessageModerationId) 892 await commands[0].deleteMessage({ abuseId, messageId: abuseMessageModerationId })
952 await deleteAbuseMessage(servers[0].url, userAccessToken, abuseId, abuseMessageUserId) 893 await commands[0].deleteMessage({ token: userToken, abuseId, messageId: abuseMessageUserId })
953 894
954 const results = await Promise.all([ 895 const results = await Promise.all([
955 listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId), 896 commands[0].listMessages({ abuseId }),
956 listAbuseMessages(servers[0].url, userAccessToken, abuseId) 897 commands[0].listMessages({ token: userToken, abuseId })
957 ]) 898 ])
958 899
959 for (const res of results) { 900 for (const body of results) {
960 expect(res.body.total).to.equal(2) 901 expect(body.total).to.equal(2)
961
962 const abuseMessages: AbuseMessage[] = res.body.data
963 902
903 const abuseMessages: AbuseMessage[] = body.data
964 expect(abuseMessages[0].message).to.equal('message 2') 904 expect(abuseMessages[0].message).to.equal('message 2')
965 expect(abuseMessages[1].message).to.equal('message 4') 905 expect(abuseMessages[1].message).to.equal('message 4')
966 } 906 }
diff --git a/server/tests/api/moderation/blocklist-notification.ts b/server/tests/api/moderation/blocklist-notification.ts
index 4fb3c95f2..75b15c298 100644
--- a/server/tests/api/moderation/blocklist-notification.ts
+++ b/server/tests/api/moderation/blocklist-notification.ts
@@ -2,47 +2,22 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { getUserNotifications, markAsReadAllNotifications } from '@shared/extra-utils/users/user-notifications' 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { addUserSubscription, removeUserSubscription } from '@shared/extra-utils/users/user-subscriptions' 6import { UserNotificationType } from '@shared/models'
7import { UserNotification, UserNotificationType } from '@shared/models'
8import {
9 cleanupTests,
10 createUser,
11 doubleFollow,
12 flushAndRunMultipleServers,
13 ServerInfo,
14 uploadVideo,
15 userLogin
16} from '../../../../shared/extra-utils/index'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import {
19 addAccountToAccountBlocklist,
20 addAccountToServerBlocklist,
21 addServerToAccountBlocklist,
22 addServerToServerBlocklist,
23 removeAccountFromAccountBlocklist,
24 removeAccountFromServerBlocklist,
25 removeServerFromAccountBlocklist
26} from '../../../../shared/extra-utils/users/blocklist'
27import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
28import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
29 7
30const expect = chai.expect 8const expect = chai.expect
31 9
32async function checkNotifications (url: string, token: string, expected: UserNotificationType[]) { 10async function checkNotifications (server: PeerTubeServer, token: string, expected: UserNotificationType[]) {
33 const res = await getUserNotifications(url, token, 0, 10, true) 11 const { data } = await server.notifications.list({ token, start: 0, count: 10, unread: true })
34 12 expect(data).to.have.lengthOf(expected.length)
35 const notifications: UserNotification[] = res.body.data
36
37 expect(notifications).to.have.lengthOf(expected.length)
38 13
39 for (const type of expected) { 14 for (const type of expected) {
40 expect(notifications.find(n => n.type === type)).to.exist 15 expect(data.find(n => n.type === type)).to.exist
41 } 16 }
42} 17}
43 18
44describe('Test blocklist', function () { 19describe('Test blocklist', function () {
45 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
46 let videoUUID: string 21 let videoUUID: string
47 22
48 let userToken1: string 23 let userToken1: string
@@ -51,30 +26,34 @@ describe('Test blocklist', function () {
51 26
52 async function resetState () { 27 async function resetState () {
53 try { 28 try {
54 await removeUserSubscription(servers[1].url, remoteUserToken, 'user1_channel@' + servers[0].host) 29 await servers[1].subscriptions.remove({ token: remoteUserToken, uri: 'user1_channel@' + servers[0].host })
55 await removeUserSubscription(servers[1].url, remoteUserToken, 'user2_channel@' + servers[0].host) 30 await servers[1].subscriptions.remove({ token: remoteUserToken, uri: 'user2_channel@' + servers[0].host })
56 } catch {} 31 } catch {}
57 32
58 await waitJobs(servers) 33 await waitJobs(servers)
59 34
60 await markAsReadAllNotifications(servers[0].url, userToken1) 35 await servers[0].notifications.markAsReadAll({ token: userToken1 })
61 await markAsReadAllNotifications(servers[0].url, userToken2) 36 await servers[0].notifications.markAsReadAll({ token: userToken2 })
62 37
63 { 38 {
64 const res = await uploadVideo(servers[0].url, userToken1, { name: 'video' }) 39 const { uuid } = await servers[0].videos.upload({ token: userToken1, attributes: { name: 'video' } })
65 videoUUID = res.body.video.uuid 40 videoUUID = uuid
66 41
67 await waitJobs(servers) 42 await waitJobs(servers)
68 } 43 }
69 44
70 { 45 {
71 await addVideoCommentThread(servers[1].url, remoteUserToken, videoUUID, '@user2@' + servers[0].host + ' hello') 46 await servers[1].comments.createThread({
47 token: remoteUserToken,
48 videoId: videoUUID,
49 text: '@user2@' + servers[0].host + ' hello'
50 })
72 } 51 }
73 52
74 { 53 {
75 54
76 await addUserSubscription(servers[1].url, remoteUserToken, 'user1_channel@' + servers[0].host) 55 await servers[1].subscriptions.add({ token: remoteUserToken, targetUri: 'user1_channel@' + servers[0].host })
77 await addUserSubscription(servers[1].url, remoteUserToken, 'user2_channel@' + servers[0].host) 56 await servers[1].subscriptions.add({ token: remoteUserToken, targetUri: 'user2_channel@' + servers[0].host })
78 } 57 }
79 58
80 await waitJobs(servers) 59 await waitJobs(servers)
@@ -83,36 +62,34 @@ describe('Test blocklist', function () {
83 before(async function () { 62 before(async function () {
84 this.timeout(60000) 63 this.timeout(60000)
85 64
86 servers = await flushAndRunMultipleServers(2) 65 servers = await createMultipleServers(2)
87 await setAccessTokensToServers(servers) 66 await setAccessTokensToServers(servers)
88 67
89 { 68 {
90 const user = { username: 'user1', password: 'password' } 69 const user = { username: 'user1', password: 'password' }
91 await createUser({ 70 await servers[0].users.create({
92 url: servers[0].url,
93 accessToken: servers[0].accessToken,
94 username: user.username, 71 username: user.username,
95 password: user.password, 72 password: user.password,
96 videoQuota: -1, 73 videoQuota: -1,
97 videoQuotaDaily: -1 74 videoQuotaDaily: -1
98 }) 75 })
99 76
100 userToken1 = await userLogin(servers[0], user) 77 userToken1 = await servers[0].login.getAccessToken(user)
101 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' }) 78 await servers[0].videos.upload({ token: userToken1, attributes: { name: 'video user 1' } })
102 } 79 }
103 80
104 { 81 {
105 const user = { username: 'user2', password: 'password' } 82 const user = { username: 'user2', password: 'password' }
106 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 83 await servers[0].users.create({ username: user.username, password: user.password })
107 84
108 userToken2 = await userLogin(servers[0], user) 85 userToken2 = await servers[0].login.getAccessToken(user)
109 } 86 }
110 87
111 { 88 {
112 const user = { username: 'user3', password: 'password' } 89 const user = { username: 'user3', password: 'password' }
113 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 90 await servers[1].users.create({ username: user.username, password: user.password })
114 91
115 remoteUserToken = await userLogin(servers[1], user) 92 remoteUserToken = await servers[1].login.getAccessToken(user)
116 } 93 }
117 94
118 await doubleFollow(servers[0], servers[1]) 95 await doubleFollow(servers[0], servers[1])
@@ -128,26 +105,26 @@ describe('Test blocklist', function () {
128 105
129 it('Should have appropriate notifications', async function () { 106 it('Should have appropriate notifications', async function () {
130 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 107 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
131 await checkNotifications(servers[0].url, userToken1, notifs) 108 await checkNotifications(servers[0], userToken1, notifs)
132 }) 109 })
133 110
134 it('Should block an account', async function () { 111 it('Should block an account', async function () {
135 this.timeout(10000) 112 this.timeout(10000)
136 113
137 await addAccountToAccountBlocklist(servers[0].url, userToken1, 'user3@' + servers[1].host) 114 await servers[0].blocklist.addToMyBlocklist({ token: userToken1, account: 'user3@' + servers[1].host })
138 await waitJobs(servers) 115 await waitJobs(servers)
139 }) 116 })
140 117
141 it('Should not have notifications from this account', async function () { 118 it('Should not have notifications from this account', async function () {
142 await checkNotifications(servers[0].url, userToken1, []) 119 await checkNotifications(servers[0], userToken1, [])
143 }) 120 })
144 121
145 it('Should have notifications of this account on user 2', async function () { 122 it('Should have notifications of this account on user 2', async function () {
146 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 123 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
147 124
148 await checkNotifications(servers[0].url, userToken2, notifs) 125 await checkNotifications(servers[0], userToken2, notifs)
149 126
150 await removeAccountFromAccountBlocklist(servers[0].url, userToken1, 'user3@' + servers[1].host) 127 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken1, account: 'user3@' + servers[1].host })
151 }) 128 })
152 }) 129 })
153 130
@@ -161,26 +138,26 @@ describe('Test blocklist', function () {
161 138
162 it('Should have appropriate notifications', async function () { 139 it('Should have appropriate notifications', async function () {
163 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 140 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
164 await checkNotifications(servers[0].url, userToken1, notifs) 141 await checkNotifications(servers[0], userToken1, notifs)
165 }) 142 })
166 143
167 it('Should block an account', async function () { 144 it('Should block an account', async function () {
168 this.timeout(10000) 145 this.timeout(10000)
169 146
170 await addServerToAccountBlocklist(servers[0].url, userToken1, servers[1].host) 147 await servers[0].blocklist.addToMyBlocklist({ token: userToken1, server: servers[1].host })
171 await waitJobs(servers) 148 await waitJobs(servers)
172 }) 149 })
173 150
174 it('Should not have notifications from this account', async function () { 151 it('Should not have notifications from this account', async function () {
175 await checkNotifications(servers[0].url, userToken1, []) 152 await checkNotifications(servers[0], userToken1, [])
176 }) 153 })
177 154
178 it('Should have notifications of this account on user 2', async function () { 155 it('Should have notifications of this account on user 2', async function () {
179 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 156 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
180 157
181 await checkNotifications(servers[0].url, userToken2, notifs) 158 await checkNotifications(servers[0], userToken2, notifs)
182 159
183 await removeServerFromAccountBlocklist(servers[0].url, userToken1, servers[1].host) 160 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken1, server: servers[1].host })
184 }) 161 })
185 }) 162 })
186 163
@@ -195,27 +172,27 @@ describe('Test blocklist', function () {
195 it('Should have appropriate notifications', async function () { 172 it('Should have appropriate notifications', async function () {
196 { 173 {
197 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 174 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
198 await checkNotifications(servers[0].url, userToken1, notifs) 175 await checkNotifications(servers[0], userToken1, notifs)
199 } 176 }
200 177
201 { 178 {
202 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 179 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
203 await checkNotifications(servers[0].url, userToken2, notifs) 180 await checkNotifications(servers[0], userToken2, notifs)
204 } 181 }
205 }) 182 })
206 183
207 it('Should block an account', async function () { 184 it('Should block an account', async function () {
208 this.timeout(10000) 185 this.timeout(10000)
209 186
210 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user3@' + servers[1].host) 187 await servers[0].blocklist.addToServerBlocklist({ account: 'user3@' + servers[1].host })
211 await waitJobs(servers) 188 await waitJobs(servers)
212 }) 189 })
213 190
214 it('Should not have notifications from this account', async function () { 191 it('Should not have notifications from this account', async function () {
215 await checkNotifications(servers[0].url, userToken1, []) 192 await checkNotifications(servers[0], userToken1, [])
216 await checkNotifications(servers[0].url, userToken2, []) 193 await checkNotifications(servers[0], userToken2, [])
217 194
218 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user3@' + servers[1].host) 195 await servers[0].blocklist.removeFromServerBlocklist({ account: 'user3@' + servers[1].host })
219 }) 196 })
220 }) 197 })
221 198
@@ -230,25 +207,25 @@ describe('Test blocklist', function () {
230 it('Should have appropriate notifications', async function () { 207 it('Should have appropriate notifications', async function () {
231 { 208 {
232 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ] 209 const notifs = [ UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, UserNotificationType.NEW_FOLLOW ]
233 await checkNotifications(servers[0].url, userToken1, notifs) 210 await checkNotifications(servers[0], userToken1, notifs)
234 } 211 }
235 212
236 { 213 {
237 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ] 214 const notifs = [ UserNotificationType.COMMENT_MENTION, UserNotificationType.NEW_FOLLOW ]
238 await checkNotifications(servers[0].url, userToken2, notifs) 215 await checkNotifications(servers[0], userToken2, notifs)
239 } 216 }
240 }) 217 })
241 218
242 it('Should block an account', async function () { 219 it('Should block an account', async function () {
243 this.timeout(10000) 220 this.timeout(10000)
244 221
245 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host) 222 await servers[0].blocklist.addToServerBlocklist({ server: servers[1].host })
246 await waitJobs(servers) 223 await waitJobs(servers)
247 }) 224 })
248 225
249 it('Should not have notifications from this account', async function () { 226 it('Should not have notifications from this account', async function () {
250 await checkNotifications(servers[0].url, userToken1, []) 227 await checkNotifications(servers[0], userToken1, [])
251 await checkNotifications(servers[0].url, userToken2, []) 228 await checkNotifications(servers[0], userToken2, [])
252 }) 229 })
253 }) 230 })
254 231
diff --git a/server/tests/api/moderation/blocklist.ts b/server/tests/api/moderation/blocklist.ts
index 793abbcb4..089af8b15 100644
--- a/server/tests/api/moderation/blocklist.ts
+++ b/server/tests/api/moderation/blocklist.ts
@@ -3,106 +3,67 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addAccountToAccountBlocklist, 6 BlocklistCommand,
7 addAccountToServerBlocklist,
8 addServerToAccountBlocklist,
9 addServerToServerBlocklist,
10 addVideoCommentReply,
11 addVideoCommentThread,
12 cleanupTests, 7 cleanupTests,
13 createUser, 8 CommentsCommand,
14 deleteVideoComment, 9 createMultipleServers,
15 doubleFollow, 10 doubleFollow,
16 findCommentId, 11 PeerTubeServer,
17 flushAndRunMultipleServers,
18 follow,
19 getAccountBlocklistByAccount,
20 getAccountBlocklistByServer,
21 getServerBlocklistByAccount,
22 getServerBlocklistByServer,
23 getUserNotifications,
24 getVideoCommentThreads,
25 getVideosList,
26 getVideosListWithToken,
27 getVideoThreadComments,
28 removeAccountFromAccountBlocklist,
29 removeAccountFromServerBlocklist,
30 removeServerFromAccountBlocklist,
31 removeServerFromServerBlocklist,
32 ServerInfo,
33 setAccessTokensToServers, 12 setAccessTokensToServers,
34 unfollow,
35 uploadVideo,
36 userLogin,
37 waitJobs 13 waitJobs
38} from '@shared/extra-utils' 14} from '@shared/extra-utils'
39import { 15import { UserNotificationType } from '@shared/models'
40 AccountBlock,
41 ServerBlock,
42 UserNotification,
43 UserNotificationType,
44 Video,
45 VideoComment,
46 VideoCommentThreadTree
47} from '@shared/models'
48 16
49const expect = chai.expect 17const expect = chai.expect
50 18
51async function checkAllVideos (url: string, token: string) { 19async function checkAllVideos (server: PeerTubeServer, token: string) {
52 { 20 {
53 const res = await getVideosListWithToken(url, token) 21 const { data } = await server.videos.listWithToken({ token })
54 22 expect(data).to.have.lengthOf(5)
55 expect(res.body.data).to.have.lengthOf(5)
56 } 23 }
57 24
58 { 25 {
59 const res = await getVideosList(url) 26 const { data } = await server.videos.list()
60 27 expect(data).to.have.lengthOf(5)
61 expect(res.body.data).to.have.lengthOf(5)
62 } 28 }
63} 29}
64 30
65async function checkAllComments (url: string, token: string, videoUUID: string) { 31async function checkAllComments (server: PeerTubeServer, token: string, videoUUID: string) {
66 const resThreads = await getVideoCommentThreads(url, videoUUID, 0, 25, '-createdAt', token) 32 const { data } = await server.comments.listThreads({ videoId: videoUUID, start: 0, count: 25, sort: '-createdAt', token })
67 33
68 const allThreads: VideoComment[] = resThreads.body.data 34 const threads = data.filter(t => t.isDeleted === false)
69 const threads = allThreads.filter(t => t.isDeleted === false)
70 expect(threads).to.have.lengthOf(2) 35 expect(threads).to.have.lengthOf(2)
71 36
72 for (const thread of threads) { 37 for (const thread of threads) {
73 const res = await getVideoThreadComments(url, videoUUID, thread.id, token) 38 const tree = await server.comments.getThread({ videoId: videoUUID, threadId: thread.id, token })
74
75 const tree: VideoCommentThreadTree = res.body
76 expect(tree.children).to.have.lengthOf(1) 39 expect(tree.children).to.have.lengthOf(1)
77 } 40 }
78} 41}
79 42
80async function checkCommentNotification ( 43async function checkCommentNotification (
81 mainServer: ServerInfo, 44 mainServer: PeerTubeServer,
82 comment: { server: ServerInfo, token: string, videoUUID: string, text: string }, 45 comment: { server: PeerTubeServer, token: string, videoUUID: string, text: string },
83 check: 'presence' | 'absence' 46 check: 'presence' | 'absence'
84) { 47) {
85 const resComment = await addVideoCommentThread(comment.server.url, comment.token, comment.videoUUID, comment.text) 48 const command = comment.server.comments
86 const created = resComment.body.comment as VideoComment 49
87 const threadId = created.id 50 const { threadId, createdAt } = await command.createThread({ token: comment.token, videoId: comment.videoUUID, text: comment.text })
88 const createdAt = created.createdAt
89 51
90 await waitJobs([ mainServer, comment.server ]) 52 await waitJobs([ mainServer, comment.server ])
91 53
92 const res = await getUserNotifications(mainServer.url, mainServer.accessToken, 0, 30) 54 const { data } = await mainServer.notifications.list({ start: 0, count: 30 })
93 const commentNotifications = (res.body.data as UserNotification[]) 55 const commentNotifications = data.filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt)
94 .filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt)
95 56
96 if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1) 57 if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1)
97 else expect(commentNotifications).to.have.lengthOf(0) 58 else expect(commentNotifications).to.have.lengthOf(0)
98 59
99 await deleteVideoComment(comment.server.url, comment.token, comment.videoUUID, threadId) 60 await command.delete({ token: comment.token, videoId: comment.videoUUID, commentId: threadId })
100 61
101 await waitJobs([ mainServer, comment.server ]) 62 await waitJobs([ mainServer, comment.server ])
102} 63}
103 64
104describe('Test blocklist', function () { 65describe('Test blocklist', function () {
105 let servers: ServerInfo[] 66 let servers: PeerTubeServer[]
106 let videoUUID1: string 67 let videoUUID1: string
107 let videoUUID2: string 68 let videoUUID2: string
108 let videoUUID3: string 69 let videoUUID3: string
@@ -110,62 +71,73 @@ describe('Test blocklist', function () {
110 let userModeratorToken: string 71 let userModeratorToken: string
111 let userToken2: string 72 let userToken2: string
112 73
74 let command: BlocklistCommand
75 let commentsCommand: CommentsCommand[]
76
113 before(async function () { 77 before(async function () {
114 this.timeout(120000) 78 this.timeout(120000)
115 79
116 servers = await flushAndRunMultipleServers(3) 80 servers = await createMultipleServers(3)
117 await setAccessTokensToServers(servers) 81 await setAccessTokensToServers(servers)
118 82
83 command = servers[0].blocklist
84 commentsCommand = servers.map(s => s.comments)
85
119 { 86 {
120 const user = { username: 'user1', password: 'password' } 87 const user = { username: 'user1', password: 'password' }
121 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 88 await servers[0].users.create({ username: user.username, password: user.password })
122 89
123 userToken1 = await userLogin(servers[0], user) 90 userToken1 = await servers[0].login.getAccessToken(user)
124 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' }) 91 await servers[0].videos.upload({ token: userToken1, attributes: { name: 'video user 1' } })
125 } 92 }
126 93
127 { 94 {
128 const user = { username: 'moderator', password: 'password' } 95 const user = { username: 'moderator', password: 'password' }
129 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 96 await servers[0].users.create({ username: user.username, password: user.password })
130 97
131 userModeratorToken = await userLogin(servers[0], user) 98 userModeratorToken = await servers[0].login.getAccessToken(user)
132 } 99 }
133 100
134 { 101 {
135 const user = { username: 'user2', password: 'password' } 102 const user = { username: 'user2', password: 'password' }
136 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 103 await servers[1].users.create({ username: user.username, password: user.password })
137 104
138 userToken2 = await userLogin(servers[1], user) 105 userToken2 = await servers[1].login.getAccessToken(user)
139 await uploadVideo(servers[1].url, userToken2, { name: 'video user 2' }) 106 await servers[1].videos.upload({ token: userToken2, attributes: { name: 'video user 2' } })
140 } 107 }
141 108
142 { 109 {
143 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' }) 110 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video server 1' } })
144 videoUUID1 = res.body.video.uuid 111 videoUUID1 = uuid
145 } 112 }
146 113
147 { 114 {
148 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' }) 115 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video server 2' } })
149 videoUUID2 = res.body.video.uuid 116 videoUUID2 = uuid
150 } 117 }
151 118
152 { 119 {
153 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 2 server 1' }) 120 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 2 server 1' } })
154 videoUUID3 = res.body.video.uuid 121 videoUUID3 = uuid
155 } 122 }
156 123
157 await doubleFollow(servers[0], servers[1]) 124 await doubleFollow(servers[0], servers[1])
158 await doubleFollow(servers[0], servers[2]) 125 await doubleFollow(servers[0], servers[2])
159 126
160 { 127 {
161 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID1, 'comment root 1') 128 const created = await commentsCommand[0].createThread({ videoId: videoUUID1, text: 'comment root 1' })
162 const resReply = await addVideoCommentReply(servers[0].url, userToken1, videoUUID1, resComment.body.comment.id, 'comment user 1') 129 const reply = await commentsCommand[0].addReply({
163 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resReply.body.comment.id, 'comment root 1') 130 token: userToken1,
131 videoId: videoUUID1,
132 toCommentId: created.id,
133 text: 'comment user 1'
134 })
135 await commentsCommand[0].addReply({ videoId: videoUUID1, toCommentId: reply.id, text: 'comment root 1' })
164 } 136 }
165 137
166 { 138 {
167 const resComment = await addVideoCommentThread(servers[0].url, userToken1, videoUUID1, 'comment user 1') 139 const created = await commentsCommand[0].createThread({ token: userToken1, videoId: videoUUID1, text: 'comment user 1' })
168 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resComment.body.comment.id, 'comment root 1') 140 await commentsCommand[0].addReply({ videoId: videoUUID1, toCommentId: created.id, text: 'comment root 1' })
169 } 141 }
170 142
171 await waitJobs(servers) 143 await waitJobs(servers)
@@ -175,55 +147,60 @@ describe('Test blocklist', function () {
175 147
176 describe('When managing account blocklist', function () { 148 describe('When managing account blocklist', function () {
177 it('Should list all videos', function () { 149 it('Should list all videos', function () {
178 return checkAllVideos(servers[0].url, servers[0].accessToken) 150 return checkAllVideos(servers[0], servers[0].accessToken)
179 }) 151 })
180 152
181 it('Should list the comments', function () { 153 it('Should list the comments', function () {
182 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 154 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
183 }) 155 })
184 156
185 it('Should block a remote account', async function () { 157 it('Should block a remote account', async function () {
186 await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 158 await command.addToMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
187 }) 159 })
188 160
189 it('Should hide its videos', async function () { 161 it('Should hide its videos', async function () {
190 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 162 const { data } = await servers[0].videos.listWithToken()
191 163
192 const videos: Video[] = res.body.data 164 expect(data).to.have.lengthOf(4)
193 expect(videos).to.have.lengthOf(4)
194 165
195 const v = videos.find(v => v.name === 'video user 2') 166 const v = data.find(v => v.name === 'video user 2')
196 expect(v).to.be.undefined 167 expect(v).to.be.undefined
197 }) 168 })
198 169
199 it('Should block a local account', async function () { 170 it('Should block a local account', async function () {
200 await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1') 171 await command.addToMyBlocklist({ account: 'user1' })
201 }) 172 })
202 173
203 it('Should hide its videos', async function () { 174 it('Should hide its videos', async function () {
204 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 175 const { data } = await servers[0].videos.listWithToken()
205 176
206 const videos: Video[] = res.body.data 177 expect(data).to.have.lengthOf(3)
207 expect(videos).to.have.lengthOf(3)
208 178
209 const v = videos.find(v => v.name === 'video user 1') 179 const v = data.find(v => v.name === 'video user 1')
210 expect(v).to.be.undefined 180 expect(v).to.be.undefined
211 }) 181 })
212 182
213 it('Should hide its comments', async function () { 183 it('Should hide its comments', async function () {
214 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 25, '-createdAt', servers[0].accessToken) 184 const { data } = await commentsCommand[0].listThreads({
215 185 token: servers[0].accessToken,
216 const threads: VideoComment[] = resThreads.body.data 186 videoId: videoUUID1,
217 expect(threads).to.have.lengthOf(1) 187 start: 0,
218 expect(threads[0].totalReplies).to.equal(1) 188 count: 25,
219 189 sort: '-createdAt'
220 const t = threads.find(t => t.text === 'comment user 1') 190 })
191
192 expect(data).to.have.lengthOf(1)
193 expect(data[0].totalReplies).to.equal(1)
194
195 const t = data.find(t => t.text === 'comment user 1')
221 expect(t).to.be.undefined 196 expect(t).to.be.undefined
222 197
223 for (const thread of threads) { 198 for (const thread of data) {
224 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, servers[0].accessToken) 199 const tree = await commentsCommand[0].getThread({
225 200 videoId: videoUUID1,
226 const tree: VideoCommentThreadTree = res.body 201 threadId: thread.id,
202 token: servers[0].accessToken
203 })
227 expect(tree.children).to.have.lengthOf(0) 204 expect(tree.children).to.have.lengthOf(0)
228 } 205 }
229 }) 206 })
@@ -248,17 +225,15 @@ describe('Test blocklist', function () {
248 }) 225 })
249 226
250 it('Should list all the videos with another user', async function () { 227 it('Should list all the videos with another user', async function () {
251 return checkAllVideos(servers[0].url, userToken1) 228 return checkAllVideos(servers[0], userToken1)
252 }) 229 })
253 230
254 it('Should list blocked accounts', async function () { 231 it('Should list blocked accounts', async function () {
255 { 232 {
256 const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 233 const body = await command.listMyAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
257 const blocks: AccountBlock[] = res.body.data 234 expect(body.total).to.equal(2)
258 235
259 expect(res.body.total).to.equal(2) 236 const block = body.data[0]
260
261 const block = blocks[0]
262 expect(block.byAccount.displayName).to.equal('root') 237 expect(block.byAccount.displayName).to.equal('root')
263 expect(block.byAccount.name).to.equal('root') 238 expect(block.byAccount.name).to.equal('root')
264 expect(block.blockedAccount.displayName).to.equal('user2') 239 expect(block.blockedAccount.displayName).to.equal('user2')
@@ -267,12 +242,10 @@ describe('Test blocklist', function () {
267 } 242 }
268 243
269 { 244 {
270 const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 1, 2, 'createdAt') 245 const body = await command.listMyAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
271 const blocks: AccountBlock[] = res.body.data 246 expect(body.total).to.equal(2)
272
273 expect(res.body.total).to.equal(2)
274 247
275 const block = blocks[0] 248 const block = body.data[0]
276 expect(block.byAccount.displayName).to.equal('root') 249 expect(block.byAccount.displayName).to.equal('root')
277 expect(block.byAccount.name).to.equal('root') 250 expect(block.byAccount.name).to.equal('root')
278 expect(block.blockedAccount.displayName).to.equal('user1') 251 expect(block.blockedAccount.displayName).to.equal('user1')
@@ -285,32 +258,29 @@ describe('Test blocklist', function () {
285 this.timeout(60000) 258 this.timeout(60000)
286 259
287 { 260 {
288 await addVideoCommentThread(servers[1].url, userToken2, videoUUID3, 'comment user 2') 261 await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID3, text: 'comment user 2' })
289 await waitJobs(servers) 262 await waitJobs(servers)
290 263
291 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID3, 'uploader') 264 await commentsCommand[0].createThread({ token: servers[0].accessToken, videoId: videoUUID3, text: 'uploader' })
292 await waitJobs(servers) 265 await waitJobs(servers)
293 266
294 const commentId = await findCommentId(servers[1].url, videoUUID3, 'uploader') 267 const commentId = await commentsCommand[1].findCommentId({ videoId: videoUUID3, text: 'uploader' })
295 const message = 'reply by user 2' 268 const message = 'reply by user 2'
296 const resReply = await addVideoCommentReply(servers[1].url, userToken2, videoUUID3, commentId, message) 269 const reply = await commentsCommand[1].addReply({ token: userToken2, videoId: videoUUID3, toCommentId: commentId, text: message })
297 await addVideoCommentReply(servers[1].url, servers[1].accessToken, videoUUID3, resReply.body.comment.id, 'another reply') 270 await commentsCommand[1].addReply({ videoId: videoUUID3, toCommentId: reply.id, text: 'another reply' })
298 271
299 await waitJobs(servers) 272 await waitJobs(servers)
300 } 273 }
301 274
302 // Server 2 has all the comments 275 // Server 2 has all the comments
303 { 276 {
304 const resThreads = await getVideoCommentThreads(servers[1].url, videoUUID3, 0, 25, '-createdAt') 277 const { data } = await commentsCommand[1].listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
305 const threads: VideoComment[] = resThreads.body.data
306
307 expect(threads).to.have.lengthOf(2)
308 expect(threads[0].text).to.equal('uploader')
309 expect(threads[1].text).to.equal('comment user 2')
310 278
311 const resReplies = await getVideoThreadComments(servers[1].url, videoUUID3, threads[0].id) 279 expect(data).to.have.lengthOf(2)
280 expect(data[0].text).to.equal('uploader')
281 expect(data[1].text).to.equal('comment user 2')
312 282
313 const tree: VideoCommentThreadTree = resReplies.body 283 const tree = await commentsCommand[1].getThread({ videoId: videoUUID3, threadId: data[0].id })
314 expect(tree.children).to.have.lengthOf(1) 284 expect(tree.children).to.have.lengthOf(1)
315 expect(tree.children[0].comment.text).to.equal('reply by user 2') 285 expect(tree.children[0].comment.text).to.equal('reply by user 2')
316 expect(tree.children[0].children).to.have.lengthOf(1) 286 expect(tree.children[0].children).to.have.lengthOf(1)
@@ -319,55 +289,45 @@ describe('Test blocklist', function () {
319 289
320 // Server 1 and 3 should only have uploader comments 290 // Server 1 and 3 should only have uploader comments
321 for (const server of [ servers[0], servers[2] ]) { 291 for (const server of [ servers[0], servers[2] ]) {
322 const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt') 292 const { data } = await server.comments.listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
323 const threads: VideoComment[] = resThreads.body.data
324 293
325 expect(threads).to.have.lengthOf(1) 294 expect(data).to.have.lengthOf(1)
326 expect(threads[0].text).to.equal('uploader') 295 expect(data[0].text).to.equal('uploader')
327 296
328 const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id) 297 const tree = await server.comments.getThread({ videoId: videoUUID3, threadId: data[0].id })
329 298
330 const tree: VideoCommentThreadTree = resReplies.body 299 if (server.serverNumber === 1) expect(tree.children).to.have.lengthOf(0)
331 if (server.serverNumber === 1) { 300 else expect(tree.children).to.have.lengthOf(1)
332 expect(tree.children).to.have.lengthOf(0)
333 } else {
334 expect(tree.children).to.have.lengthOf(1)
335 }
336 } 301 }
337 }) 302 })
338 303
339 it('Should unblock the remote account', async function () { 304 it('Should unblock the remote account', async function () {
340 await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 305 await command.removeFromMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
341 }) 306 })
342 307
343 it('Should display its videos', async function () { 308 it('Should display its videos', async function () {
344 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 309 const { data } = await servers[0].videos.listWithToken()
345 310 expect(data).to.have.lengthOf(4)
346 const videos: Video[] = res.body.data
347 expect(videos).to.have.lengthOf(4)
348 311
349 const v = videos.find(v => v.name === 'video user 2') 312 const v = data.find(v => v.name === 'video user 2')
350 expect(v).not.to.be.undefined 313 expect(v).not.to.be.undefined
351 }) 314 })
352 315
353 it('Should display its comments on my video', async function () { 316 it('Should display its comments on my video', async function () {
354 for (const server of servers) { 317 for (const server of servers) {
355 const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt') 318 const { data } = await server.comments.listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
356 const threads: VideoComment[] = resThreads.body.data
357 319
358 // Server 3 should not have 2 comment threads, because server 1 did not forward the server 2 comment 320 // Server 3 should not have 2 comment threads, because server 1 did not forward the server 2 comment
359 if (server.serverNumber === 3) { 321 if (server.serverNumber === 3) {
360 expect(threads).to.have.lengthOf(1) 322 expect(data).to.have.lengthOf(1)
361 continue 323 continue
362 } 324 }
363 325
364 expect(threads).to.have.lengthOf(2) 326 expect(data).to.have.lengthOf(2)
365 expect(threads[0].text).to.equal('uploader') 327 expect(data[0].text).to.equal('uploader')
366 expect(threads[1].text).to.equal('comment user 2') 328 expect(data[1].text).to.equal('comment user 2')
367 329
368 const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id) 330 const tree = await server.comments.getThread({ videoId: videoUUID3, threadId: data[0].id })
369
370 const tree: VideoCommentThreadTree = resReplies.body
371 expect(tree.children).to.have.lengthOf(1) 331 expect(tree.children).to.have.lengthOf(1)
372 expect(tree.children[0].comment.text).to.equal('reply by user 2') 332 expect(tree.children[0].comment.text).to.equal('reply by user 2')
373 expect(tree.children[0].children).to.have.lengthOf(1) 333 expect(tree.children[0].children).to.have.lengthOf(1)
@@ -376,11 +336,11 @@ describe('Test blocklist', function () {
376 }) 336 })
377 337
378 it('Should unblock the local account', async function () { 338 it('Should unblock the local account', async function () {
379 await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1') 339 await command.removeFromMyBlocklist({ account: 'user1' })
380 }) 340 })
381 341
382 it('Should display its comments', function () { 342 it('Should display its comments', function () {
383 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 343 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
384 }) 344 })
385 345
386 it('Should have a notification from a non blocked account', async function () { 346 it('Should have a notification from a non blocked account', async function () {
@@ -404,46 +364,45 @@ describe('Test blocklist', function () {
404 }) 364 })
405 365
406 describe('When managing server blocklist', function () { 366 describe('When managing server blocklist', function () {
367
407 it('Should list all videos', function () { 368 it('Should list all videos', function () {
408 return checkAllVideos(servers[0].url, servers[0].accessToken) 369 return checkAllVideos(servers[0], servers[0].accessToken)
409 }) 370 })
410 371
411 it('Should list the comments', function () { 372 it('Should list the comments', function () {
412 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 373 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
413 }) 374 })
414 375
415 it('Should block a remote server', async function () { 376 it('Should block a remote server', async function () {
416 await addServerToAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 377 await command.addToMyBlocklist({ server: 'localhost:' + servers[1].port })
417 }) 378 })
418 379
419 it('Should hide its videos', async function () { 380 it('Should hide its videos', async function () {
420 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 381 const { data } = await servers[0].videos.listWithToken()
421 382
422 const videos: Video[] = res.body.data 383 expect(data).to.have.lengthOf(3)
423 expect(videos).to.have.lengthOf(3)
424 384
425 const v1 = videos.find(v => v.name === 'video user 2') 385 const v1 = data.find(v => v.name === 'video user 2')
426 const v2 = videos.find(v => v.name === 'video server 2') 386 const v2 = data.find(v => v.name === 'video server 2')
427 387
428 expect(v1).to.be.undefined 388 expect(v1).to.be.undefined
429 expect(v2).to.be.undefined 389 expect(v2).to.be.undefined
430 }) 390 })
431 391
432 it('Should list all the videos with another user', async function () { 392 it('Should list all the videos with another user', async function () {
433 return checkAllVideos(servers[0].url, userToken1) 393 return checkAllVideos(servers[0], userToken1)
434 }) 394 })
435 395
436 it('Should hide its comments', async function () { 396 it('Should hide its comments', async function () {
437 this.timeout(10000) 397 this.timeout(10000)
438 398
439 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2') 399 const { id } = await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID1, text: 'hidden comment 2' })
440 const threadId = resThreads.body.comment.id
441 400
442 await waitJobs(servers) 401 await waitJobs(servers)
443 402
444 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 403 await checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
445 404
446 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId) 405 await commentsCommand[1].delete({ token: userToken2, videoId: videoUUID1, commentId: id })
447 }) 406 })
448 407
449 it('Should not have notifications from blocked server', async function () { 408 it('Should not have notifications from blocked server', async function () {
@@ -466,27 +425,25 @@ describe('Test blocklist', function () {
466 }) 425 })
467 426
468 it('Should list blocked servers', async function () { 427 it('Should list blocked servers', async function () {
469 const res = await getServerBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 428 const body = await command.listMyServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
470 const blocks: ServerBlock[] = res.body.data 429 expect(body.total).to.equal(1)
471
472 expect(res.body.total).to.equal(1)
473 430
474 const block = blocks[0] 431 const block = body.data[0]
475 expect(block.byAccount.displayName).to.equal('root') 432 expect(block.byAccount.displayName).to.equal('root')
476 expect(block.byAccount.name).to.equal('root') 433 expect(block.byAccount.name).to.equal('root')
477 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) 434 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
478 }) 435 })
479 436
480 it('Should unblock the remote server', async function () { 437 it('Should unblock the remote server', async function () {
481 await removeServerFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 438 await command.removeFromMyBlocklist({ server: 'localhost:' + servers[1].port })
482 }) 439 })
483 440
484 it('Should display its videos', function () { 441 it('Should display its videos', function () {
485 return checkAllVideos(servers[0].url, servers[0].accessToken) 442 return checkAllVideos(servers[0], servers[0].accessToken)
486 }) 443 })
487 444
488 it('Should display its comments', function () { 445 it('Should display its comments', function () {
489 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 446 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
490 }) 447 })
491 448
492 it('Should have notification from unblocked server', async function () { 449 it('Should have notification from unblocked server', async function () {
@@ -515,54 +472,50 @@ describe('Test blocklist', function () {
515 describe('When managing account blocklist', function () { 472 describe('When managing account blocklist', function () {
516 it('Should list all videos', async function () { 473 it('Should list all videos', async function () {
517 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 474 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
518 await checkAllVideos(servers[0].url, token) 475 await checkAllVideos(servers[0], token)
519 } 476 }
520 }) 477 })
521 478
522 it('Should list the comments', async function () { 479 it('Should list the comments', async function () {
523 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 480 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
524 await checkAllComments(servers[0].url, token, videoUUID1) 481 await checkAllComments(servers[0], token, videoUUID1)
525 } 482 }
526 }) 483 })
527 484
528 it('Should block a remote account', async function () { 485 it('Should block a remote account', async function () {
529 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 486 await command.addToServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
530 }) 487 })
531 488
532 it('Should hide its videos', async function () { 489 it('Should hide its videos', async function () {
533 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 490 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
534 const res = await getVideosListWithToken(servers[0].url, token) 491 const { data } = await servers[0].videos.listWithToken({ token })
535 492
536 const videos: Video[] = res.body.data 493 expect(data).to.have.lengthOf(4)
537 expect(videos).to.have.lengthOf(4)
538 494
539 const v = videos.find(v => v.name === 'video user 2') 495 const v = data.find(v => v.name === 'video user 2')
540 expect(v).to.be.undefined 496 expect(v).to.be.undefined
541 } 497 }
542 }) 498 })
543 499
544 it('Should block a local account', async function () { 500 it('Should block a local account', async function () {
545 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user1') 501 await command.addToServerBlocklist({ account: 'user1' })
546 }) 502 })
547 503
548 it('Should hide its videos', async function () { 504 it('Should hide its videos', async function () {
549 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 505 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
550 const res = await getVideosListWithToken(servers[0].url, token) 506 const { data } = await servers[0].videos.listWithToken({ token })
551 507
552 const videos: Video[] = res.body.data 508 expect(data).to.have.lengthOf(3)
553 expect(videos).to.have.lengthOf(3)
554 509
555 const v = videos.find(v => v.name === 'video user 1') 510 const v = data.find(v => v.name === 'video user 1')
556 expect(v).to.be.undefined 511 expect(v).to.be.undefined
557 } 512 }
558 }) 513 })
559 514
560 it('Should hide its comments', async function () { 515 it('Should hide its comments', async function () {
561 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 516 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
562 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 20, '-createdAt', token) 517 const { data } = await commentsCommand[0].listThreads({ videoId: videoUUID1, count: 20, sort: '-createdAt', token })
563 518 const threads = data.filter(t => t.isDeleted === false)
564 let threads: VideoComment[] = resThreads.body.data
565 threads = threads.filter(t => t.isDeleted === false)
566 519
567 expect(threads).to.have.lengthOf(1) 520 expect(threads).to.have.lengthOf(1)
568 expect(threads[0].totalReplies).to.equal(1) 521 expect(threads[0].totalReplies).to.equal(1)
@@ -571,9 +524,7 @@ describe('Test blocklist', function () {
571 expect(t).to.be.undefined 524 expect(t).to.be.undefined
572 525
573 for (const thread of threads) { 526 for (const thread of threads) {
574 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, token) 527 const tree = await commentsCommand[0].getThread({ videoId: videoUUID1, threadId: thread.id, token })
575
576 const tree: VideoCommentThreadTree = res.body
577 expect(tree.children).to.have.lengthOf(0) 528 expect(tree.children).to.have.lengthOf(0)
578 } 529 }
579 } 530 }
@@ -600,12 +551,10 @@ describe('Test blocklist', function () {
600 551
601 it('Should list blocked accounts', async function () { 552 it('Should list blocked accounts', async function () {
602 { 553 {
603 const res = await getAccountBlocklistByServer(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 554 const body = await command.listServerAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
604 const blocks: AccountBlock[] = res.body.data 555 expect(body.total).to.equal(2)
605
606 expect(res.body.total).to.equal(2)
607 556
608 const block = blocks[0] 557 const block = body.data[0]
609 expect(block.byAccount.displayName).to.equal('peertube') 558 expect(block.byAccount.displayName).to.equal('peertube')
610 expect(block.byAccount.name).to.equal('peertube') 559 expect(block.byAccount.name).to.equal('peertube')
611 expect(block.blockedAccount.displayName).to.equal('user2') 560 expect(block.blockedAccount.displayName).to.equal('user2')
@@ -614,12 +563,10 @@ describe('Test blocklist', function () {
614 } 563 }
615 564
616 { 565 {
617 const res = await getAccountBlocklistByServer(servers[0].url, servers[0].accessToken, 1, 2, 'createdAt') 566 const body = await command.listServerAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
618 const blocks: AccountBlock[] = res.body.data 567 expect(body.total).to.equal(2)
619 568
620 expect(res.body.total).to.equal(2) 569 const block = body.data[0]
621
622 const block = blocks[0]
623 expect(block.byAccount.displayName).to.equal('peertube') 570 expect(block.byAccount.displayName).to.equal('peertube')
624 expect(block.byAccount.name).to.equal('peertube') 571 expect(block.byAccount.name).to.equal('peertube')
625 expect(block.blockedAccount.displayName).to.equal('user1') 572 expect(block.blockedAccount.displayName).to.equal('user1')
@@ -629,28 +576,26 @@ describe('Test blocklist', function () {
629 }) 576 })
630 577
631 it('Should unblock the remote account', async function () { 578 it('Should unblock the remote account', async function () {
632 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) 579 await command.removeFromServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
633 }) 580 })
634 581
635 it('Should display its videos', async function () { 582 it('Should display its videos', async function () {
636 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 583 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
637 const res = await getVideosListWithToken(servers[0].url, token) 584 const { data } = await servers[0].videos.listWithToken({ token })
638 585 expect(data).to.have.lengthOf(4)
639 const videos: Video[] = res.body.data
640 expect(videos).to.have.lengthOf(4)
641 586
642 const v = videos.find(v => v.name === 'video user 2') 587 const v = data.find(v => v.name === 'video user 2')
643 expect(v).not.to.be.undefined 588 expect(v).not.to.be.undefined
644 } 589 }
645 }) 590 })
646 591
647 it('Should unblock the local account', async function () { 592 it('Should unblock the local account', async function () {
648 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user1') 593 await command.removeFromServerBlocklist({ account: 'user1' })
649 }) 594 })
650 595
651 it('Should display its comments', async function () { 596 it('Should display its comments', async function () {
652 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 597 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
653 await checkAllComments(servers[0].url, token, videoUUID1) 598 await checkAllComments(servers[0], token, videoUUID1)
654 } 599 }
655 }) 600 })
656 601
@@ -677,31 +622,33 @@ describe('Test blocklist', function () {
677 describe('When managing server blocklist', function () { 622 describe('When managing server blocklist', function () {
678 it('Should list all videos', async function () { 623 it('Should list all videos', async function () {
679 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 624 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
680 await checkAllVideos(servers[0].url, token) 625 await checkAllVideos(servers[0], token)
681 } 626 }
682 }) 627 })
683 628
684 it('Should list the comments', async function () { 629 it('Should list the comments', async function () {
685 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 630 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
686 await checkAllComments(servers[0].url, token, videoUUID1) 631 await checkAllComments(servers[0], token, videoUUID1)
687 } 632 }
688 }) 633 })
689 634
690 it('Should block a remote server', async function () { 635 it('Should block a remote server', async function () {
691 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 636 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
692 }) 637 })
693 638
694 it('Should hide its videos', async function () { 639 it('Should hide its videos', async function () {
695 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 640 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
696 const res1 = await getVideosList(servers[0].url) 641 const requests = [
697 const res2 = await getVideosListWithToken(servers[0].url, token) 642 servers[0].videos.list(),
643 servers[0].videos.listWithToken({ token })
644 ]
698 645
699 for (const res of [ res1, res2 ]) { 646 for (const req of requests) {
700 const videos: Video[] = res.body.data 647 const { data } = await req
701 expect(videos).to.have.lengthOf(3) 648 expect(data).to.have.lengthOf(3)
702 649
703 const v1 = videos.find(v => v.name === 'video user 2') 650 const v1 = data.find(v => v.name === 'video user 2')
704 const v2 = videos.find(v => v.name === 'video server 2') 651 const v2 = data.find(v => v.name === 'video server 2')
705 652
706 expect(v1).to.be.undefined 653 expect(v1).to.be.undefined
707 expect(v2).to.be.undefined 654 expect(v2).to.be.undefined
@@ -712,14 +659,13 @@ describe('Test blocklist', function () {
712 it('Should hide its comments', async function () { 659 it('Should hide its comments', async function () {
713 this.timeout(10000) 660 this.timeout(10000)
714 661
715 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2') 662 const { id } = await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID1, text: 'hidden comment 2' })
716 const threadId = resThreads.body.comment.id
717 663
718 await waitJobs(servers) 664 await waitJobs(servers)
719 665
720 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1) 666 await checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
721 667
722 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId) 668 await commentsCommand[1].delete({ token: userToken2, videoId: videoUUID1, commentId: id })
723 }) 669 })
724 670
725 it('Should not have notification from blocked instances by instance', async function () { 671 it('Should not have notification from blocked instances by instance', async function () {
@@ -742,48 +688,44 @@ describe('Test blocklist', function () {
742 688
743 { 689 {
744 const now = new Date() 690 const now = new Date()
745 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 691 await servers[1].follows.unfollow({ target: servers[0] })
746 await waitJobs(servers) 692 await waitJobs(servers)
747 await follow(servers[1].url, [ servers[0].host ], servers[1].accessToken) 693 await servers[1].follows.follow({ hosts: [ servers[0].host ] })
748 694
749 await waitJobs(servers) 695 await waitJobs(servers)
750 696
751 const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30) 697 const { data } = await servers[0].notifications.list({ start: 0, count: 30 })
752 const commentNotifications = (res.body.data as UserNotification[]) 698 const commentNotifications = data.filter(n => {
753 .filter(n => { 699 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && n.createdAt >= now.toISOString()
754 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && 700 })
755 n.createdAt >= now.toISOString()
756 })
757 701
758 expect(commentNotifications).to.have.lengthOf(0) 702 expect(commentNotifications).to.have.lengthOf(0)
759 } 703 }
760 }) 704 })
761 705
762 it('Should list blocked servers', async function () { 706 it('Should list blocked servers', async function () {
763 const res = await getServerBlocklistByServer(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') 707 const body = await command.listServerServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
764 const blocks: ServerBlock[] = res.body.data 708 expect(body.total).to.equal(1)
765
766 expect(res.body.total).to.equal(1)
767 709
768 const block = blocks[0] 710 const block = body.data[0]
769 expect(block.byAccount.displayName).to.equal('peertube') 711 expect(block.byAccount.displayName).to.equal('peertube')
770 expect(block.byAccount.name).to.equal('peertube') 712 expect(block.byAccount.name).to.equal('peertube')
771 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) 713 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
772 }) 714 })
773 715
774 it('Should unblock the remote server', async function () { 716 it('Should unblock the remote server', async function () {
775 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 717 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
776 }) 718 })
777 719
778 it('Should list all videos', async function () { 720 it('Should list all videos', async function () {
779 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 721 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
780 await checkAllVideos(servers[0].url, token) 722 await checkAllVideos(servers[0], token)
781 } 723 }
782 }) 724 })
783 725
784 it('Should list the comments', async function () { 726 it('Should list the comments', async function () {
785 for (const token of [ userModeratorToken, servers[0].accessToken ]) { 727 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
786 await checkAllComments(servers[0].url, token, videoUUID1) 728 await checkAllComments(servers[0], token, videoUUID1)
787 } 729 }
788 }) 730 })
789 731
@@ -807,18 +749,16 @@ describe('Test blocklist', function () {
807 749
808 { 750 {
809 const now = new Date() 751 const now = new Date()
810 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 752 await servers[1].follows.unfollow({ target: servers[0] })
811 await waitJobs(servers) 753 await waitJobs(servers)
812 await follow(servers[1].url, [ servers[0].host ], servers[1].accessToken) 754 await servers[1].follows.follow({ hosts: [ servers[0].host ] })
813 755
814 await waitJobs(servers) 756 await waitJobs(servers)
815 757
816 const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30) 758 const { data } = await servers[0].notifications.list({ start: 0, count: 30 })
817 const commentNotifications = (res.body.data as UserNotification[]) 759 const commentNotifications = data.filter(n => {
818 .filter(n => { 760 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && n.createdAt >= now.toISOString()
819 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && 761 })
820 n.createdAt >= now.toISOString()
821 })
822 762
823 expect(commentNotifications).to.have.lengthOf(1) 763 expect(commentNotifications).to.have.lengthOf(1)
824 } 764 }
diff --git a/server/tests/api/moderation/video-blacklist.ts b/server/tests/api/moderation/video-blacklist.ts
index 52cac20d9..d5838191a 100644
--- a/server/tests/api/moderation/video-blacklist.ts
+++ b/server/tests/api/moderation/video-blacklist.ts
@@ -4,44 +4,30 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { orderBy } from 'lodash' 5import { orderBy } from 'lodash'
6import { 6import {
7 addVideoToBlacklist, 7 BlacklistCommand,
8 cleanupTests, 8 cleanupTests,
9 createUser, 9 createMultipleServers,
10 flushAndRunMultipleServers, 10 doubleFollow,
11 getBlacklistedVideosList, 11 FIXTURE_URLS,
12 getMyUserInformation,
13 getMyVideos,
14 getVideosList,
15 killallServers, 12 killallServers,
16 removeVideoFromBlacklist, 13 PeerTubeServer,
17 reRunServer,
18 searchVideo,
19 ServerInfo,
20 setAccessTokensToServers, 14 setAccessTokensToServers,
21 updateVideo, 15 waitJobs
22 updateVideoBlacklist, 16} from '@shared/extra-utils'
23 uploadVideo, 17import { UserAdminFlag, UserRole, VideoBlacklist, VideoBlacklistType } from '@shared/models'
24 userLogin
25} from '../../../../shared/extra-utils/index'
26import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
27import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
28import { getGoodVideoUrl, getMagnetURI, importVideo } from '../../../../shared/extra-utils/videos/video-imports'
29import { User, UserRole } from '../../../../shared/models/users'
30import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
31import { VideoBlacklist, VideoBlacklistType } from '../../../../shared/models/videos'
32 18
33const expect = chai.expect 19const expect = chai.expect
34 20
35describe('Test video blacklist', function () { 21describe('Test video blacklist', function () {
36 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
37 let videoId: number 23 let videoId: number
24 let command: BlacklistCommand
38 25
39 async function blacklistVideosOnServer (server: ServerInfo) { 26 async function blacklistVideosOnServer (server: PeerTubeServer) {
40 const res = await getVideosList(server.url) 27 const { data } = await server.videos.list()
41 28
42 const videos = res.body.data 29 for (const video of data) {
43 for (const video of videos) { 30 await server.blacklist.add({ videoId: video.id, reason: 'super reason' })
44 await addVideoToBlacklist(server.url, server.accessToken, video.id, 'super reason')
45 } 31 }
46 } 32 }
47 33
@@ -49,7 +35,7 @@ describe('Test video blacklist', function () {
49 this.timeout(50000) 35 this.timeout(50000)
50 36
51 // Run servers 37 // Run servers
52 servers = await flushAndRunMultipleServers(2) 38 servers = await createMultipleServers(2)
53 39
54 // Get the access tokens 40 // Get the access tokens
55 await setAccessTokensToServers(servers) 41 await setAccessTokensToServers(servers)
@@ -58,12 +44,14 @@ describe('Test video blacklist', function () {
58 await doubleFollow(servers[0], servers[1]) 44 await doubleFollow(servers[0], servers[1])
59 45
60 // Upload 2 videos on server 2 46 // Upload 2 videos on server 2
61 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' }) 47 await servers[1].videos.upload({ attributes: { name: 'My 1st video', description: 'A video on server 2' } })
62 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' }) 48 await servers[1].videos.upload({ attributes: { name: 'My 2nd video', description: 'A video on server 2' } })
63 49
64 // Wait videos propagation, server 2 has transcoding enabled 50 // Wait videos propagation, server 2 has transcoding enabled
65 await waitJobs(servers) 51 await waitJobs(servers)
66 52
53 command = servers[0].blacklist
54
67 // Blacklist the two videos on server 1 55 // Blacklist the two videos on server 1
68 await blacklistVideosOnServer(servers[0]) 56 await blacklistVideosOnServer(servers[0])
69 }) 57 })
@@ -72,48 +60,47 @@ describe('Test video blacklist', function () {
72 60
73 it('Should not have the video blacklisted in videos list/search on server 1', async function () { 61 it('Should not have the video blacklisted in videos list/search on server 1', async function () {
74 { 62 {
75 const res = await getVideosList(servers[0].url) 63 const { total, data } = await servers[0].videos.list()
76 64
77 expect(res.body.total).to.equal(0) 65 expect(total).to.equal(0)
78 expect(res.body.data).to.be.an('array') 66 expect(data).to.be.an('array')
79 expect(res.body.data.length).to.equal(0) 67 expect(data.length).to.equal(0)
80 } 68 }
81 69
82 { 70 {
83 const res = await searchVideo(servers[0].url, 'name') 71 const body = await servers[0].search.searchVideos({ search: 'video' })
84 72
85 expect(res.body.total).to.equal(0) 73 expect(body.total).to.equal(0)
86 expect(res.body.data).to.be.an('array') 74 expect(body.data).to.be.an('array')
87 expect(res.body.data.length).to.equal(0) 75 expect(body.data.length).to.equal(0)
88 } 76 }
89 }) 77 })
90 78
91 it('Should have the blacklisted video in videos list/search on server 2', async function () { 79 it('Should have the blacklisted video in videos list/search on server 2', async function () {
92 { 80 {
93 const res = await getVideosList(servers[1].url) 81 const { total, data } = await servers[1].videos.list()
94 82
95 expect(res.body.total).to.equal(2) 83 expect(total).to.equal(2)
96 expect(res.body.data).to.be.an('array') 84 expect(data).to.be.an('array')
97 expect(res.body.data.length).to.equal(2) 85 expect(data.length).to.equal(2)
98 } 86 }
99 87
100 { 88 {
101 const res = await searchVideo(servers[1].url, 'video') 89 const body = await servers[1].search.searchVideos({ search: 'video' })
102 90
103 expect(res.body.total).to.equal(2) 91 expect(body.total).to.equal(2)
104 expect(res.body.data).to.be.an('array') 92 expect(body.data).to.be.an('array')
105 expect(res.body.data.length).to.equal(2) 93 expect(body.data.length).to.equal(2)
106 } 94 }
107 }) 95 })
108 }) 96 })
109 97
110 describe('When listing manually blacklisted videos', function () { 98 describe('When listing manually blacklisted videos', function () {
111 it('Should display all the blacklisted videos', async function () { 99 it('Should display all the blacklisted videos', async function () {
112 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken }) 100 const body = await command.list()
113 101 expect(body.total).to.equal(2)
114 expect(res.body.total).to.equal(2)
115 102
116 const blacklistedVideos = res.body.data 103 const blacklistedVideos = body.data
117 expect(blacklistedVideos).to.be.an('array') 104 expect(blacklistedVideos).to.be.an('array')
118 expect(blacklistedVideos.length).to.equal(2) 105 expect(blacklistedVideos.length).to.equal(2)
119 106
@@ -124,79 +111,66 @@ describe('Test video blacklist', function () {
124 }) 111 })
125 112
126 it('Should display all the blacklisted videos when applying manual type filter', async function () { 113 it('Should display all the blacklisted videos when applying manual type filter', async function () {
127 const res = await getBlacklistedVideosList({ 114 const body = await command.list({ type: VideoBlacklistType.MANUAL })
128 url: servers[0].url, 115 expect(body.total).to.equal(2)
129 token: servers[0].accessToken,
130 type: VideoBlacklistType.MANUAL
131 })
132 116
133 expect(res.body.total).to.equal(2) 117 const blacklistedVideos = body.data
134
135 const blacklistedVideos = res.body.data
136 expect(blacklistedVideos).to.be.an('array') 118 expect(blacklistedVideos).to.be.an('array')
137 expect(blacklistedVideos.length).to.equal(2) 119 expect(blacklistedVideos.length).to.equal(2)
138 }) 120 })
139 121
140 it('Should display nothing when applying automatic type filter', async function () { 122 it('Should display nothing when applying automatic type filter', async function () {
141 const res = await getBlacklistedVideosList({ 123 const body = await command.list({ type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
142 url: servers[0].url, 124 expect(body.total).to.equal(0)
143 token: servers[0].accessToken,
144 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
145 })
146
147 expect(res.body.total).to.equal(0)
148 125
149 const blacklistedVideos = res.body.data 126 const blacklistedVideos = body.data
150 expect(blacklistedVideos).to.be.an('array') 127 expect(blacklistedVideos).to.be.an('array')
151 expect(blacklistedVideos.length).to.equal(0) 128 expect(blacklistedVideos.length).to.equal(0)
152 }) 129 })
153 130
154 it('Should get the correct sort when sorting by descending id', async function () { 131 it('Should get the correct sort when sorting by descending id', async function () {
155 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-id' }) 132 const body = await command.list({ sort: '-id' })
156 expect(res.body.total).to.equal(2) 133 expect(body.total).to.equal(2)
157 134
158 const blacklistedVideos = res.body.data 135 const blacklistedVideos = body.data
159 expect(blacklistedVideos).to.be.an('array') 136 expect(blacklistedVideos).to.be.an('array')
160 expect(blacklistedVideos.length).to.equal(2) 137 expect(blacklistedVideos.length).to.equal(2)
161 138
162 const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ]) 139 const result = orderBy(body.data, [ 'id' ], [ 'desc' ])
163
164 expect(blacklistedVideos).to.deep.equal(result) 140 expect(blacklistedVideos).to.deep.equal(result)
165 }) 141 })
166 142
167 it('Should get the correct sort when sorting by descending video name', async function () { 143 it('Should get the correct sort when sorting by descending video name', async function () {
168 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 144 const body = await command.list({ sort: '-name' })
169 expect(res.body.total).to.equal(2) 145 expect(body.total).to.equal(2)
170 146
171 const blacklistedVideos = res.body.data 147 const blacklistedVideos = body.data
172 expect(blacklistedVideos).to.be.an('array') 148 expect(blacklistedVideos).to.be.an('array')
173 expect(blacklistedVideos.length).to.equal(2) 149 expect(blacklistedVideos.length).to.equal(2)
174 150
175 const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ]) 151 const result = orderBy(body.data, [ 'name' ], [ 'desc' ])
176
177 expect(blacklistedVideos).to.deep.equal(result) 152 expect(blacklistedVideos).to.deep.equal(result)
178 }) 153 })
179 154
180 it('Should get the correct sort when sorting by ascending creation date', async function () { 155 it('Should get the correct sort when sorting by ascending creation date', async function () {
181 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: 'createdAt' }) 156 const body = await command.list({ sort: 'createdAt' })
182 expect(res.body.total).to.equal(2) 157 expect(body.total).to.equal(2)
183 158
184 const blacklistedVideos = res.body.data 159 const blacklistedVideos = body.data
185 expect(blacklistedVideos).to.be.an('array') 160 expect(blacklistedVideos).to.be.an('array')
186 expect(blacklistedVideos.length).to.equal(2) 161 expect(blacklistedVideos.length).to.equal(2)
187 162
188 const result = orderBy(res.body.data, [ 'createdAt' ]) 163 const result = orderBy(body.data, [ 'createdAt' ])
189
190 expect(blacklistedVideos).to.deep.equal(result) 164 expect(blacklistedVideos).to.deep.equal(result)
191 }) 165 })
192 }) 166 })
193 167
194 describe('When updating blacklisted videos', function () { 168 describe('When updating blacklisted videos', function () {
195 it('Should change the reason', async function () { 169 it('Should change the reason', async function () {
196 await updateVideoBlacklist(servers[0].url, servers[0].accessToken, videoId, 'my super reason updated') 170 await command.update({ videoId, reason: 'my super reason updated' })
197 171
198 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 172 const body = await command.list({ sort: '-name' })
199 const video = res.body.data.find(b => b.video.id === videoId) 173 const video = body.data.find(b => b.video.id === videoId)
200 174
201 expect(video.reason).to.equal('my super reason updated') 175 expect(video.reason).to.equal('my super reason updated')
202 }) 176 })
@@ -206,12 +180,12 @@ describe('Test video blacklist', function () {
206 it('Should display blacklisted videos', async function () { 180 it('Should display blacklisted videos', async function () {
207 await blacklistVideosOnServer(servers[1]) 181 await blacklistVideosOnServer(servers[1])
208 182
209 const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 5) 183 const { total, data } = await servers[1].videos.listMyVideos()
210 184
211 expect(res.body.total).to.equal(2) 185 expect(total).to.equal(2)
212 expect(res.body.data).to.have.lengthOf(2) 186 expect(data).to.have.lengthOf(2)
213 187
214 for (const video of res.body.data) { 188 for (const video of data) {
215 expect(video.blacklisted).to.be.true 189 expect(video.blacklisted).to.be.true
216 expect(video.blacklistedReason).to.equal('super reason') 190 expect(video.blacklistedReason).to.equal('super reason')
217 } 191 }
@@ -223,39 +197,38 @@ describe('Test video blacklist', function () {
223 let blacklist = [] 197 let blacklist = []
224 198
225 it('Should not have any video in videos list on server 1', async function () { 199 it('Should not have any video in videos list on server 1', async function () {
226 const res = await getVideosList(servers[0].url) 200 const { total, data } = await servers[0].videos.list()
227 expect(res.body.total).to.equal(0) 201 expect(total).to.equal(0)
228 expect(res.body.data).to.be.an('array') 202 expect(data).to.be.an('array')
229 expect(res.body.data.length).to.equal(0) 203 expect(data.length).to.equal(0)
230 }) 204 })
231 205
232 it('Should remove a video from the blacklist on server 1', async function () { 206 it('Should remove a video from the blacklist on server 1', async function () {
233 // Get one video in the blacklist 207 // Get one video in the blacklist
234 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 208 const body = await command.list({ sort: '-name' })
235 videoToRemove = res.body.data[0] 209 videoToRemove = body.data[0]
236 blacklist = res.body.data.slice(1) 210 blacklist = body.data.slice(1)
237 211
238 // Remove it 212 // Remove it
239 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.video.id) 213 await command.remove({ videoId: videoToRemove.video.id })
240 }) 214 })
241 215
242 it('Should have the ex-blacklisted video in videos list on server 1', async function () { 216 it('Should have the ex-blacklisted video in videos list on server 1', async function () {
243 const res = await getVideosList(servers[0].url) 217 const { total, data } = await servers[0].videos.list()
244 expect(res.body.total).to.equal(1) 218 expect(total).to.equal(1)
245 219
246 const videos = res.body.data 220 expect(data).to.be.an('array')
247 expect(videos).to.be.an('array') 221 expect(data.length).to.equal(1)
248 expect(videos.length).to.equal(1)
249 222
250 expect(videos[0].name).to.equal(videoToRemove.video.name) 223 expect(data[0].name).to.equal(videoToRemove.video.name)
251 expect(videos[0].id).to.equal(videoToRemove.video.id) 224 expect(data[0].id).to.equal(videoToRemove.video.id)
252 }) 225 })
253 226
254 it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () { 227 it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () {
255 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: '-name' }) 228 const body = await command.list({ sort: '-name' })
256 expect(res.body.total).to.equal(1) 229 expect(body.total).to.equal(1)
257 230
258 const videos = res.body.data 231 const videos = body.data
259 expect(videos).to.be.an('array') 232 expect(videos).to.be.an('array')
260 expect(videos.length).to.equal(1) 233 expect(videos.length).to.equal(1)
261 expect(videos).to.deep.equal(blacklist) 234 expect(videos).to.deep.equal(blacklist)
@@ -270,12 +243,12 @@ describe('Test video blacklist', function () {
270 this.timeout(10000) 243 this.timeout(10000)
271 244
272 { 245 {
273 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'Video 3' }) 246 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'Video 3' } })
274 video3UUID = res.body.video.uuid 247 video3UUID = uuid
275 } 248 }
276 { 249 {
277 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'Video 4' }) 250 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'Video 4' } })
278 video4UUID = res.body.video.uuid 251 video4UUID = uuid
279 } 252 }
280 253
281 await waitJobs(servers) 254 await waitJobs(servers)
@@ -284,51 +257,51 @@ describe('Test video blacklist', function () {
284 it('Should blacklist video 3 and keep it federated', async function () { 257 it('Should blacklist video 3 and keep it federated', async function () {
285 this.timeout(10000) 258 this.timeout(10000)
286 259
287 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video3UUID, 'super reason', false) 260 await command.add({ videoId: video3UUID, reason: 'super reason', unfederate: false })
288 261
289 await waitJobs(servers) 262 await waitJobs(servers)
290 263
291 { 264 {
292 const res = await getVideosList(servers[0].url) 265 const { data } = await servers[0].videos.list()
293 expect(res.body.data.find(v => v.uuid === video3UUID)).to.be.undefined 266 expect(data.find(v => v.uuid === video3UUID)).to.be.undefined
294 } 267 }
295 268
296 { 269 {
297 const res = await getVideosList(servers[1].url) 270 const { data } = await servers[1].videos.list()
298 expect(res.body.data.find(v => v.uuid === video3UUID)).to.not.be.undefined 271 expect(data.find(v => v.uuid === video3UUID)).to.not.be.undefined
299 } 272 }
300 }) 273 })
301 274
302 it('Should unfederate the video', async function () { 275 it('Should unfederate the video', async function () {
303 this.timeout(10000) 276 this.timeout(10000)
304 277
305 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video4UUID, 'super reason', true) 278 await command.add({ videoId: video4UUID, reason: 'super reason', unfederate: true })
306 279
307 await waitJobs(servers) 280 await waitJobs(servers)
308 281
309 for (const server of servers) { 282 for (const server of servers) {
310 const res = await getVideosList(server.url) 283 const { data } = await server.videos.list()
311 expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined 284 expect(data.find(v => v.uuid === video4UUID)).to.be.undefined
312 } 285 }
313 }) 286 })
314 287
315 it('Should have the video unfederated even after an Update AP message', async function () { 288 it('Should have the video unfederated even after an Update AP message', async function () {
316 this.timeout(10000) 289 this.timeout(10000)
317 290
318 await updateVideo(servers[0].url, servers[0].accessToken, video4UUID, { description: 'super description' }) 291 await servers[0].videos.update({ id: video4UUID, attributes: { description: 'super description' } })
319 292
320 await waitJobs(servers) 293 await waitJobs(servers)
321 294
322 for (const server of servers) { 295 for (const server of servers) {
323 const res = await getVideosList(server.url) 296 const { data } = await server.videos.list()
324 expect(res.body.data.find(v => v.uuid === video4UUID)).to.be.undefined 297 expect(data.find(v => v.uuid === video4UUID)).to.be.undefined
325 } 298 }
326 }) 299 })
327 300
328 it('Should have the correct video blacklist unfederate attribute', async function () { 301 it('Should have the correct video blacklist unfederate attribute', async function () {
329 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken, sort: 'createdAt' }) 302 const body = await command.list({ sort: 'createdAt' })
330 303
331 const blacklistedVideos: VideoBlacklist[] = res.body.data 304 const blacklistedVideos = body.data
332 const video3Blacklisted = blacklistedVideos.find(b => b.video.uuid === video3UUID) 305 const video3Blacklisted = blacklistedVideos.find(b => b.video.uuid === video3UUID)
333 const video4Blacklisted = blacklistedVideos.find(b => b.video.uuid === video4UUID) 306 const video4Blacklisted = blacklistedVideos.find(b => b.video.uuid === video4UUID)
334 307
@@ -339,13 +312,13 @@ describe('Test video blacklist', function () {
339 it('Should remove the video from blacklist and refederate the video', async function () { 312 it('Should remove the video from blacklist and refederate the video', async function () {
340 this.timeout(10000) 313 this.timeout(10000)
341 314
342 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video4UUID) 315 await command.remove({ videoId: video4UUID })
343 316
344 await waitJobs(servers) 317 await waitJobs(servers)
345 318
346 for (const server of servers) { 319 for (const server of servers) {
347 const res = await getVideosList(server.url) 320 const { data } = await server.videos.list()
348 expect(res.body.data.find(v => v.uuid === video4UUID)).to.not.be.undefined 321 expect(data.find(v => v.uuid === video4UUID)).to.not.be.undefined
349 } 322 }
350 }) 323 })
351 324
@@ -359,7 +332,7 @@ describe('Test video blacklist', function () {
359 before(async function () { 332 before(async function () {
360 this.timeout(20000) 333 this.timeout(20000)
361 334
362 killallServers([ servers[0] ]) 335 await killallServers([ servers[0] ])
363 336
364 const config = { 337 const config = {
365 auto_blacklist: { 338 auto_blacklist: {
@@ -370,106 +343,79 @@ describe('Test video blacklist', function () {
370 } 343 }
371 } 344 }
372 } 345 }
373 await reRunServer(servers[0], config) 346 await servers[0].run(config)
374 347
375 { 348 {
376 const user = { username: 'user_without_flag', password: 'password' } 349 const user = { username: 'user_without_flag', password: 'password' }
377 await createUser({ 350 await servers[0].users.create({
378 url: servers[0].url,
379 accessToken: servers[0].accessToken,
380 username: user.username, 351 username: user.username,
381 adminFlags: UserAdminFlag.NONE, 352 adminFlags: UserAdminFlag.NONE,
382 password: user.password, 353 password: user.password,
383 role: UserRole.USER 354 role: UserRole.USER
384 }) 355 })
385 356
386 userWithoutFlag = await userLogin(servers[0], user) 357 userWithoutFlag = await servers[0].login.getAccessToken(user)
387 358
388 const res = await getMyUserInformation(servers[0].url, userWithoutFlag) 359 const { videoChannels } = await servers[0].users.getMyInfo({ token: userWithoutFlag })
389 const body: User = res.body 360 channelOfUserWithoutFlag = videoChannels[0].id
390 channelOfUserWithoutFlag = body.videoChannels[0].id
391 } 361 }
392 362
393 { 363 {
394 const user = { username: 'user_with_flag', password: 'password' } 364 const user = { username: 'user_with_flag', password: 'password' }
395 await createUser({ 365 await servers[0].users.create({
396 url: servers[0].url,
397 accessToken: servers[0].accessToken,
398 username: user.username, 366 username: user.username,
399 adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST, 367 adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST,
400 password: user.password, 368 password: user.password,
401 role: UserRole.USER 369 role: UserRole.USER
402 }) 370 })
403 371
404 userWithFlag = await userLogin(servers[0], user) 372 userWithFlag = await servers[0].login.getAccessToken(user)
405 } 373 }
406 374
407 await waitJobs(servers) 375 await waitJobs(servers)
408 }) 376 })
409 377
410 it('Should auto blacklist a video on upload', async function () { 378 it('Should auto blacklist a video on upload', async function () {
411 await uploadVideo(servers[0].url, userWithoutFlag, { name: 'blacklisted' }) 379 await servers[0].videos.upload({ token: userWithoutFlag, attributes: { name: 'blacklisted' } })
412
413 const res = await getBlacklistedVideosList({
414 url: servers[0].url,
415 token: servers[0].accessToken,
416 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
417 })
418 380
419 expect(res.body.total).to.equal(1) 381 const body = await command.list({ type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
420 expect(res.body.data[0].video.name).to.equal('blacklisted') 382 expect(body.total).to.equal(1)
383 expect(body.data[0].video.name).to.equal('blacklisted')
421 }) 384 })
422 385
423 it('Should auto blacklist a video on URL import', async function () { 386 it('Should auto blacklist a video on URL import', async function () {
424 this.timeout(15000) 387 this.timeout(15000)
425 388
426 const attributes = { 389 const attributes = {
427 targetUrl: getGoodVideoUrl(), 390 targetUrl: FIXTURE_URLS.goodVideo,
428 name: 'URL import', 391 name: 'URL import',
429 channelId: channelOfUserWithoutFlag 392 channelId: channelOfUserWithoutFlag
430 } 393 }
431 await importVideo(servers[0].url, userWithoutFlag, attributes) 394 await servers[0].imports.importVideo({ token: userWithoutFlag, attributes })
432 395
433 const res = await getBlacklistedVideosList({ 396 const body = await command.list({ sort: 'createdAt', type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
434 url: servers[0].url, 397 expect(body.total).to.equal(2)
435 token: servers[0].accessToken, 398 expect(body.data[1].video.name).to.equal('URL import')
436 sort: 'createdAt',
437 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
438 })
439
440 expect(res.body.total).to.equal(2)
441 expect(res.body.data[1].video.name).to.equal('URL import')
442 }) 399 })
443 400
444 it('Should auto blacklist a video on torrent import', async function () { 401 it('Should auto blacklist a video on torrent import', async function () {
445 const attributes = { 402 const attributes = {
446 magnetUri: getMagnetURI(), 403 magnetUri: FIXTURE_URLS.magnet,
447 name: 'Torrent import', 404 name: 'Torrent import',
448 channelId: channelOfUserWithoutFlag 405 channelId: channelOfUserWithoutFlag
449 } 406 }
450 await importVideo(servers[0].url, userWithoutFlag, attributes) 407 await servers[0].imports.importVideo({ token: userWithoutFlag, attributes })
451
452 const res = await getBlacklistedVideosList({
453 url: servers[0].url,
454 token: servers[0].accessToken,
455 sort: 'createdAt',
456 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
457 })
458 408
459 expect(res.body.total).to.equal(3) 409 const body = await command.list({ sort: 'createdAt', type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
460 expect(res.body.data[2].video.name).to.equal('Torrent import') 410 expect(body.total).to.equal(3)
411 expect(body.data[2].video.name).to.equal('Torrent import')
461 }) 412 })
462 413
463 it('Should not auto blacklist a video on upload if the user has the bypass blacklist flag', async function () { 414 it('Should not auto blacklist a video on upload if the user has the bypass blacklist flag', async function () {
464 await uploadVideo(servers[0].url, userWithFlag, { name: 'not blacklisted' }) 415 await servers[0].videos.upload({ token: userWithFlag, attributes: { name: 'not blacklisted' } })
465
466 const res = await getBlacklistedVideosList({
467 url: servers[0].url,
468 token: servers[0].accessToken,
469 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
470 })
471 416
472 expect(res.body.total).to.equal(3) 417 const body = await command.list({ type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED })
418 expect(body.total).to.equal(3)
473 }) 419 })
474 }) 420 })
475 421
diff --git a/server/tests/api/notifications/admin-notifications.ts b/server/tests/api/notifications/admin-notifications.ts
index cfe0bd2bb..c00d4e257 100644
--- a/server/tests/api/notifications/admin-notifications.ts
+++ b/server/tests/api/notifications/admin-notifications.ts
@@ -2,21 +2,21 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { MockJoinPeerTubeVersions } from '@shared/extra-utils/mock-servers/joinpeertube-versions'
6import { PluginType } from '@shared/models'
7import { cleanupTests, installPlugin, setPluginLatestVersion, setPluginVersion, wait } from '../../../../shared/extra-utils'
8import { ServerInfo } from '../../../../shared/extra-utils/index'
9import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
10import { 5import {
11 CheckerBaseParams, 6 CheckerBaseParams,
12 checkNewPeerTubeVersion, 7 checkNewPeerTubeVersion,
13 checkNewPluginVersion, 8 checkNewPluginVersion,
14 prepareNotificationsTest 9 cleanupTests,
15} from '../../../../shared/extra-utils/users/user-notifications' 10 MockJoinPeerTubeVersions,
16import { UserNotification, UserNotificationType } from '../../../../shared/models/users' 11 MockSmtpServer,
12 PeerTubeServer,
13 prepareNotificationsTest,
14 wait
15} from '@shared/extra-utils'
16import { PluginType, UserNotification, UserNotificationType } from '@shared/models'
17 17
18describe('Test admin notifications', function () { 18describe('Test admin notifications', function () {
19 let server: ServerInfo 19 let server: PeerTubeServer
20 let userNotifications: UserNotification[] = [] 20 let userNotifications: UserNotification[] = []
21 let adminNotifications: UserNotification[] = [] 21 let adminNotifications: UserNotification[] = []
22 let emails: object[] = [] 22 let emails: object[] = []
@@ -58,17 +58,8 @@ describe('Test admin notifications', function () {
58 token: server.accessToken 58 token: server.accessToken
59 } 59 }
60 60
61 await installPlugin({ 61 await server.plugins.install({ npmName: 'peertube-plugin-hello-world' })
62 url: server.url, 62 await server.plugins.install({ npmName: 'peertube-theme-background-red' })
63 accessToken: server.accessToken,
64 npmName: 'peertube-plugin-hello-world'
65 })
66
67 await installPlugin({
68 url: server.url,
69 accessToken: server.accessToken,
70 npmName: 'peertube-theme-background-red'
71 })
72 }) 63 })
73 64
74 describe('Latest PeerTube version notification', function () { 65 describe('Latest PeerTube version notification', function () {
@@ -79,7 +70,7 @@ describe('Test admin notifications', function () {
79 joinPeerTubeServer.setLatestVersion('1.4.2') 70 joinPeerTubeServer.setLatestVersion('1.4.2')
80 71
81 await wait(3000) 72 await wait(3000)
82 await checkNewPeerTubeVersion(baseParams, '1.4.2', 'absence') 73 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '1.4.2', checkType: 'absence' })
83 }) 74 })
84 75
85 it('Should send a notification to admins on new plugin version', async function () { 76 it('Should send a notification to admins on new plugin version', async function () {
@@ -88,7 +79,7 @@ describe('Test admin notifications', function () {
88 joinPeerTubeServer.setLatestVersion('15.4.2') 79 joinPeerTubeServer.setLatestVersion('15.4.2')
89 80
90 await wait(3000) 81 await wait(3000)
91 await checkNewPeerTubeVersion(baseParams, '15.4.2', 'presence') 82 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '15.4.2', checkType: 'presence' })
92 }) 83 })
93 84
94 it('Should not send the same notification to admins', async function () { 85 it('Should not send the same notification to admins', async function () {
@@ -110,7 +101,7 @@ describe('Test admin notifications', function () {
110 joinPeerTubeServer.setLatestVersion('15.4.3') 101 joinPeerTubeServer.setLatestVersion('15.4.3')
111 102
112 await wait(3000) 103 await wait(3000)
113 await checkNewPeerTubeVersion(baseParams, '15.4.3', 'presence') 104 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '15.4.3', checkType: 'presence' })
114 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2) 105 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
115 }) 106 })
116 }) 107 })
@@ -121,17 +112,17 @@ describe('Test admin notifications', function () {
121 this.timeout(30000) 112 this.timeout(30000)
122 113
123 await wait(6000) 114 await wait(6000)
124 await checkNewPluginVersion(baseParams, PluginType.PLUGIN, 'hello-world', 'absence') 115 await checkNewPluginVersion({ ...baseParams, pluginType: PluginType.PLUGIN, pluginName: 'hello-world', checkType: 'absence' })
125 }) 116 })
126 117
127 it('Should send a notification to admins on new plugin version', async function () { 118 it('Should send a notification to admins on new plugin version', async function () {
128 this.timeout(30000) 119 this.timeout(30000)
129 120
130 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1') 121 await server.sql.setPluginVersion('hello-world', '0.0.1')
131 await setPluginLatestVersion(server.internalServerNumber, 'hello-world', '0.0.1') 122 await server.sql.setPluginLatestVersion('hello-world', '0.0.1')
132 await wait(6000) 123 await wait(6000)
133 124
134 await checkNewPluginVersion(baseParams, PluginType.PLUGIN, 'hello-world', 'presence') 125 await checkNewPluginVersion({ ...baseParams, pluginType: PluginType.PLUGIN, pluginName: 'hello-world', checkType: 'presence' })
135 }) 126 })
136 127
137 it('Should not send the same notification to admins', async function () { 128 it('Should not send the same notification to admins', async function () {
@@ -149,8 +140,8 @@ describe('Test admin notifications', function () {
149 it('Should send a new notification after a new plugin release', async function () { 140 it('Should send a new notification after a new plugin release', async function () {
150 this.timeout(30000) 141 this.timeout(30000)
151 142
152 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1') 143 await server.sql.setPluginVersion('hello-world', '0.0.1')
153 await setPluginLatestVersion(server.internalServerNumber, 'hello-world', '0.0.1') 144 await server.sql.setPluginLatestVersion('hello-world', '0.0.1')
154 await wait(6000) 145 await wait(6000)
155 146
156 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2) 147 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
diff --git a/server/tests/api/notifications/comments-notifications.ts b/server/tests/api/notifications/comments-notifications.ts
index d2badf237..7cbb21397 100644
--- a/server/tests/api/notifications/comments-notifications.ts
+++ b/server/tests/api/notifications/comments-notifications.ts
@@ -3,30 +3,22 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addAccountToAccountBlocklist,
7 addVideoCommentReply,
8 addVideoCommentThread,
9 checkCommentMention, 6 checkCommentMention,
10 CheckerBaseParams, 7 CheckerBaseParams,
11 checkNewCommentOnMyVideo, 8 checkNewCommentOnMyVideo,
12 cleanupTests, 9 cleanupTests,
13 getVideoCommentThreads,
14 getVideoThreadComments,
15 MockSmtpServer, 10 MockSmtpServer,
11 PeerTubeServer,
16 prepareNotificationsTest, 12 prepareNotificationsTest,
17 removeAccountFromAccountBlocklist,
18 ServerInfo,
19 updateMyUser,
20 uploadVideo,
21 waitJobs 13 waitJobs
22} from '@shared/extra-utils' 14} from '@shared/extra-utils'
23import { UserNotification, VideoCommentThreadTree } from '@shared/models' 15import { UserNotification } from '@shared/models'
24 16
25const expect = chai.expect 17const expect = chai.expect
26 18
27describe('Test comments notifications', function () { 19describe('Test comments notifications', function () {
28 let servers: ServerInfo[] = [] 20 let servers: PeerTubeServer[] = []
29 let userAccessToken: string 21 let userToken: string
30 let userNotifications: UserNotification[] = [] 22 let userNotifications: UserNotification[] = []
31 let emails: object[] = [] 23 let emails: object[] = []
32 24
@@ -40,7 +32,7 @@ describe('Test comments notifications', function () {
40 32
41 const res = await prepareNotificationsTest(2) 33 const res = await prepareNotificationsTest(2)
42 emails = res.emails 34 emails = res.emails
43 userAccessToken = res.userAccessToken 35 userToken = res.userAccessToken
44 servers = res.servers 36 servers = res.servers
45 userNotifications = res.userNotifications 37 userNotifications = res.userNotifications
46 }) 38 })
@@ -53,136 +45,125 @@ describe('Test comments notifications', function () {
53 server: servers[0], 45 server: servers[0],
54 emails, 46 emails,
55 socketNotifications: userNotifications, 47 socketNotifications: userNotifications,
56 token: userAccessToken 48 token: userToken
57 } 49 }
58 }) 50 })
59 51
60 it('Should not send a new comment notification after a comment on another video', async function () { 52 it('Should not send a new comment notification after a comment on another video', async function () {
61 this.timeout(20000) 53 this.timeout(20000)
62 54
63 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 55 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
64 const uuid = resVideo.body.video.uuid
65 56
66 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 57 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
67 const commentId = resComment.body.comment.id 58 const commentId = created.id
68 59
69 await waitJobs(servers) 60 await waitJobs(servers)
70 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') 61 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
71 }) 62 })
72 63
73 it('Should not send a new comment notification if I comment my own video', async function () { 64 it('Should not send a new comment notification if I comment my own video', async function () {
74 this.timeout(20000) 65 this.timeout(20000)
75 66
76 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 67 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
77 const uuid = resVideo.body.video.uuid
78 68
79 const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, uuid, 'comment') 69 const created = await servers[0].comments.createThread({ token: userToken, videoId: uuid, text: 'comment' })
80 const commentId = resComment.body.comment.id 70 const commentId = created.id
81 71
82 await waitJobs(servers) 72 await waitJobs(servers)
83 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') 73 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
84 }) 74 })
85 75
86 it('Should not send a new comment notification if the account is muted', async function () { 76 it('Should not send a new comment notification if the account is muted', async function () {
87 this.timeout(20000) 77 this.timeout(20000)
88 78
89 await addAccountToAccountBlocklist(servers[0].url, userAccessToken, 'root') 79 await servers[0].blocklist.addToMyBlocklist({ token: userToken, account: 'root' })
90 80
91 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 81 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
92 const uuid = resVideo.body.video.uuid
93 82
94 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 83 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
95 const commentId = resComment.body.comment.id 84 const commentId = created.id
96 85
97 await waitJobs(servers) 86 await waitJobs(servers)
98 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence') 87 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
99 88
100 await removeAccountFromAccountBlocklist(servers[0].url, userAccessToken, 'root') 89 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken, account: 'root' })
101 }) 90 })
102 91
103 it('Should send a new comment notification after a local comment on my video', async function () { 92 it('Should send a new comment notification after a local comment on my video', async function () {
104 this.timeout(20000) 93 this.timeout(20000)
105 94
106 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 95 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
107 const uuid = resVideo.body.video.uuid
108 96
109 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 97 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
110 const commentId = resComment.body.comment.id 98 const commentId = created.id
111 99
112 await waitJobs(servers) 100 await waitJobs(servers)
113 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'presence') 101 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'presence' })
114 }) 102 })
115 103
116 it('Should send a new comment notification after a remote comment on my video', async function () { 104 it('Should send a new comment notification after a remote comment on my video', async function () {
117 this.timeout(20000) 105 this.timeout(20000)
118 106
119 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 107 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
120 const uuid = resVideo.body.video.uuid
121 108
122 await waitJobs(servers) 109 await waitJobs(servers)
123 110
124 await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'comment') 111 await servers[1].comments.createThread({ videoId: uuid, text: 'comment' })
125 112
126 await waitJobs(servers) 113 await waitJobs(servers)
127 114
128 const resComment = await getVideoCommentThreads(servers[0].url, uuid, 0, 5) 115 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
129 expect(resComment.body.data).to.have.lengthOf(1) 116 expect(data).to.have.lengthOf(1)
130 const commentId = resComment.body.data[0].id
131 117
132 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'presence') 118 const commentId = data[0].id
119 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'presence' })
133 }) 120 })
134 121
135 it('Should send a new comment notification after a local reply on my video', async function () { 122 it('Should send a new comment notification after a local reply on my video', async function () {
136 this.timeout(20000) 123 this.timeout(20000)
137 124
138 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 125 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
139 const uuid = resVideo.body.video.uuid
140 126
141 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment') 127 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
142 const threadId = resThread.body.comment.id
143 128
144 const resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, 'reply') 129 const { id: commentId } = await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'reply' })
145 const commentId = resComment.body.comment.id
146 130
147 await waitJobs(servers) 131 await waitJobs(servers)
148 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, threadId, 'presence') 132 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId, commentId, checkType: 'presence' })
149 }) 133 })
150 134
151 it('Should send a new comment notification after a remote reply on my video', async function () { 135 it('Should send a new comment notification after a remote reply on my video', async function () {
152 this.timeout(20000) 136 this.timeout(20000)
153 137
154 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 138 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
155 const uuid = resVideo.body.video.uuid
156 await waitJobs(servers) 139 await waitJobs(servers)
157 140
158 { 141 {
159 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'comment') 142 const created = await servers[1].comments.createThread({ videoId: uuid, text: 'comment' })
160 const threadId = resThread.body.comment.id 143 const threadId = created.id
161 await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, threadId, 'reply') 144 await servers[1].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'reply' })
162 } 145 }
163 146
164 await waitJobs(servers) 147 await waitJobs(servers)
165 148
166 const resThread = await getVideoCommentThreads(servers[0].url, uuid, 0, 5) 149 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
167 expect(resThread.body.data).to.have.lengthOf(1) 150 expect(data).to.have.lengthOf(1)
168 const threadId = resThread.body.data[0].id
169 151
170 const resComments = await getVideoThreadComments(servers[0].url, uuid, threadId) 152 const threadId = data[0].id
171 const tree = resComments.body as VideoCommentThreadTree 153 const tree = await servers[0].comments.getThread({ videoId: uuid, threadId })
172 154
173 expect(tree.children).to.have.lengthOf(1) 155 expect(tree.children).to.have.lengthOf(1)
174 const commentId = tree.children[0].comment.id 156 const commentId = tree.children[0].comment.id
175 157
176 await checkNewCommentOnMyVideo(baseParams, uuid, commentId, threadId, 'presence') 158 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId, commentId, checkType: 'presence' })
177 }) 159 })
178 160
179 it('Should convert markdown in comment to html', async function () { 161 it('Should convert markdown in comment to html', async function () {
180 this.timeout(20000) 162 this.timeout(20000)
181 163
182 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'cool video' }) 164 const { uuid } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'cool video' } })
183 const uuid = resVideo.body.video.uuid
184 165
185 await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, commentText) 166 await servers[0].comments.createThread({ videoId: uuid, text: commentText })
186 167
187 await waitJobs(servers) 168 await waitJobs(servers)
188 169
@@ -193,147 +174,127 @@ describe('Test comments notifications', function () {
193 174
194 describe('Mention notifications', function () { 175 describe('Mention notifications', function () {
195 let baseParams: CheckerBaseParams 176 let baseParams: CheckerBaseParams
177 const byAccountDisplayName = 'super root name'
196 178
197 before(async () => { 179 before(async () => {
198 baseParams = { 180 baseParams = {
199 server: servers[0], 181 server: servers[0],
200 emails, 182 emails,
201 socketNotifications: userNotifications, 183 socketNotifications: userNotifications,
202 token: userAccessToken 184 token: userToken
203 } 185 }
204 186
205 await updateMyUser({ 187 await servers[0].users.updateMe({ displayName: 'super root name' })
206 url: servers[0].url, 188 await servers[1].users.updateMe({ displayName: 'super root 2 name' })
207 accessToken: servers[0].accessToken,
208 displayName: 'super root name'
209 })
210
211 await updateMyUser({
212 url: servers[1].url,
213 accessToken: servers[1].accessToken,
214 displayName: 'super root 2 name'
215 })
216 }) 189 })
217 190
218 it('Should not send a new mention comment notification if I mention the video owner', async function () { 191 it('Should not send a new mention comment notification if I mention the video owner', async function () {
219 this.timeout(10000) 192 this.timeout(10000)
220 193
221 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' }) 194 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
222 const uuid = resVideo.body.video.uuid
223 195
224 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello') 196 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
225 const commentId = resComment.body.comment.id
226 197
227 await waitJobs(servers) 198 await waitJobs(servers)
228 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 199 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
229 }) 200 })
230 201
231 it('Should not send a new mention comment notification if I mention myself', async function () { 202 it('Should not send a new mention comment notification if I mention myself', async function () {
232 this.timeout(10000) 203 this.timeout(10000)
233 204
234 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 205 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
235 const uuid = resVideo.body.video.uuid
236 206
237 const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, uuid, '@user_1 hello') 207 const { id: commentId } = await servers[0].comments.createThread({ token: userToken, videoId: uuid, text: '@user_1 hello' })
238 const commentId = resComment.body.comment.id
239 208
240 await waitJobs(servers) 209 await waitJobs(servers)
241 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 210 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
242 }) 211 })
243 212
244 it('Should not send a new mention notification if the account is muted', async function () { 213 it('Should not send a new mention notification if the account is muted', async function () {
245 this.timeout(10000) 214 this.timeout(10000)
246 215
247 await addAccountToAccountBlocklist(servers[0].url, userAccessToken, 'root') 216 await servers[0].blocklist.addToMyBlocklist({ token: userToken, account: 'root' })
248 217
249 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 218 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
250 const uuid = resVideo.body.video.uuid
251 219
252 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello') 220 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
253 const commentId = resComment.body.comment.id
254 221
255 await waitJobs(servers) 222 await waitJobs(servers)
256 await checkCommentMention(baseParams, uuid, commentId, commentId, 'super root name', 'absence') 223 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
257 224
258 await removeAccountFromAccountBlocklist(servers[0].url, userAccessToken, 'root') 225 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken, account: 'root' })
259 }) 226 })
260 227
261 it('Should not send a new mention notification if the remote account mention a local account', async function () { 228 it('Should not send a new mention notification if the remote account mention a local account', async function () {
262 this.timeout(20000) 229 this.timeout(20000)
263 230
264 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 231 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
265 const uuid = resVideo.body.video.uuid
266 232
267 await waitJobs(servers) 233 await waitJobs(servers)
268 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, '@user_1 hello') 234 const { id: threadId } = await servers[1].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
269 const threadId = resThread.body.comment.id
270 235
271 await waitJobs(servers) 236 await waitJobs(servers)
272 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root 2 name', 'absence') 237
238 const byAccountDisplayName = 'super root 2 name'
239 await checkCommentMention({ ...baseParams, shortUUID, threadId, commentId: threadId, byAccountDisplayName, checkType: 'absence' })
273 }) 240 })
274 241
275 it('Should send a new mention notification after local comments', async function () { 242 it('Should send a new mention notification after local comments', async function () {
276 this.timeout(10000) 243 this.timeout(10000)
277 244
278 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 245 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
279 const uuid = resVideo.body.video.uuid
280 246
281 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello 1') 247 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hellotext: 1' })
282 const threadId = resThread.body.comment.id
283 248
284 await waitJobs(servers) 249 await waitJobs(servers)
285 await checkCommentMention(baseParams, uuid, threadId, threadId, 'super root name', 'presence') 250 await checkCommentMention({ ...baseParams, shortUUID, threadId, commentId: threadId, byAccountDisplayName, checkType: 'presence' })
286 251
287 const resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, 'hello 2 @user_1') 252 const { id: commentId } = await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'hello 2 @user_1' })
288 const commentId = resComment.body.comment.id
289 253
290 await waitJobs(servers) 254 await waitJobs(servers)
291 await checkCommentMention(baseParams, uuid, commentId, threadId, 'super root name', 'presence') 255 await checkCommentMention({ ...baseParams, shortUUID, commentId, threadId, byAccountDisplayName, checkType: 'presence' })
292 }) 256 })
293 257
294 it('Should send a new mention notification after remote comments', async function () { 258 it('Should send a new mention notification after remote comments', async function () {
295 this.timeout(20000) 259 this.timeout(20000)
296 260
297 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 261 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
298 const uuid = resVideo.body.video.uuid
299 262
300 await waitJobs(servers) 263 await waitJobs(servers)
301 264
302 const text1 = `hello @user_1@localhost:${servers[0].port} 1` 265 const text1 = `hello @user_1@localhost:${servers[0].port} 1`
303 const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, text1) 266 const { id: server2ThreadId } = await servers[1].comments.createThread({ videoId: uuid, text: text1 })
304 const server2ThreadId = resThread.body.comment.id
305 267
306 await waitJobs(servers) 268 await waitJobs(servers)
307 269
308 const resThread2 = await getVideoCommentThreads(servers[0].url, uuid, 0, 5) 270 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
309 expect(resThread2.body.data).to.have.lengthOf(1) 271 expect(data).to.have.lengthOf(1)
310 const server1ThreadId = resThread2.body.data[0].id 272
311 await checkCommentMention(baseParams, uuid, server1ThreadId, server1ThreadId, 'super root 2 name', 'presence') 273 const byAccountDisplayName = 'super root 2 name'
274 const threadId = data[0].id
275 await checkCommentMention({ ...baseParams, shortUUID, commentId: threadId, threadId, byAccountDisplayName, checkType: 'presence' })
312 276
313 const text2 = `@user_1@localhost:${servers[0].port} hello 2 @root@localhost:${servers[0].port}` 277 const text2 = `@user_1@localhost:${servers[0].port} hello 2 @root@localhost:${servers[0].port}`
314 await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, server2ThreadId, text2) 278 await servers[1].comments.addReply({ videoId: uuid, toCommentId: server2ThreadId, text: text2 })
315 279
316 await waitJobs(servers) 280 await waitJobs(servers)
317 281
318 const resComments = await getVideoThreadComments(servers[0].url, uuid, server1ThreadId) 282 const tree = await servers[0].comments.getThread({ videoId: uuid, threadId })
319 const tree = resComments.body as VideoCommentThreadTree
320 283
321 expect(tree.children).to.have.lengthOf(1) 284 expect(tree.children).to.have.lengthOf(1)
322 const commentId = tree.children[0].comment.id 285 const commentId = tree.children[0].comment.id
323 286
324 await checkCommentMention(baseParams, uuid, commentId, server1ThreadId, 'super root 2 name', 'presence') 287 await checkCommentMention({ ...baseParams, shortUUID, commentId, threadId, byAccountDisplayName, checkType: 'presence' })
325 }) 288 })
326 289
327 it('Should convert markdown in comment to html', async function () { 290 it('Should convert markdown in comment to html', async function () {
328 this.timeout(10000) 291 this.timeout(10000)
329 292
330 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' }) 293 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
331 const uuid = resVideo.body.video.uuid
332 294
333 const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, '@user_1 hello 1') 295 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello 1' })
334 const threadId = resThread.body.comment.id
335 296
336 await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, '@user_1 ' + commentText) 297 await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: '@user_1 ' + commentText })
337 298
338 await waitJobs(servers) 299 await waitJobs(servers)
339 300
diff --git a/server/tests/api/notifications/moderation-notifications.ts b/server/tests/api/notifications/moderation-notifications.ts
index 3425480ae..eb3c29fe7 100644
--- a/server/tests/api/notifications/moderation-notifications.ts
+++ b/server/tests/api/notifications/moderation-notifications.ts
@@ -2,33 +2,6 @@
2 2
3import 'mocha' 3import 'mocha'
4import { buildUUID } from '@server/helpers/uuid' 4import { buildUUID } from '@server/helpers/uuid'
5import { AbuseState } from '@shared/models'
6import {
7 addAbuseMessage,
8 addVideoCommentThread,
9 addVideoToBlacklist,
10 cleanupTests,
11 createUser,
12 follow,
13 generateUserAccessToken,
14 getAccount,
15 getCustomConfig,
16 getVideoCommentThreads,
17 getVideoIdFromUUID,
18 immutableAssign,
19 MockInstancesIndex,
20 registerUser,
21 removeVideoFromBlacklist,
22 reportAbuse,
23 unfollow,
24 updateAbuse,
25 updateCustomConfig,
26 updateCustomSubConfig,
27 wait
28} from '../../../../shared/extra-utils'
29import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index'
30import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
31import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
32import { 5import {
33 checkAbuseStateChange, 6 checkAbuseStateChange,
34 checkAutoInstanceFollowing, 7 checkAutoInstanceFollowing,
@@ -43,15 +16,18 @@ import {
43 checkUserRegistered, 16 checkUserRegistered,
44 checkVideoAutoBlacklistForModerators, 17 checkVideoAutoBlacklistForModerators,
45 checkVideoIsPublished, 18 checkVideoIsPublished,
46 prepareNotificationsTest 19 cleanupTests,
47} from '../../../../shared/extra-utils/users/user-notifications' 20 MockInstancesIndex,
48import { addUserSubscription, removeUserSubscription } from '../../../../shared/extra-utils/users/user-subscriptions' 21 MockSmtpServer,
49import { CustomConfig } from '../../../../shared/models/server' 22 PeerTubeServer,
50import { UserNotification } from '../../../../shared/models/users' 23 prepareNotificationsTest,
51import { VideoPrivacy } from '../../../../shared/models/videos' 24 wait,
25 waitJobs
26} from '@shared/extra-utils'
27import { AbuseState, CustomConfig, UserNotification, VideoPrivacy } from '@shared/models'
52 28
53describe('Test moderation notifications', function () { 29describe('Test moderation notifications', function () {
54 let servers: ServerInfo[] = [] 30 let servers: PeerTubeServer[] = []
55 let userAccessToken: string 31 let userAccessToken: string
56 let userNotifications: UserNotification[] = [] 32 let userNotifications: UserNotification[] = []
57 let adminNotifications: UserNotification[] = [] 33 let adminNotifications: UserNotification[] = []
@@ -86,93 +62,97 @@ describe('Test moderation notifications', function () {
86 this.timeout(20000) 62 this.timeout(20000)
87 63
88 const name = 'video for abuse ' + buildUUID() 64 const name = 'video for abuse ' + buildUUID()
89 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 65 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
90 const video = resVideo.body.video
91 66
92 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: video.id, reason: 'super reason' }) 67 await servers[0].abuses.report({ videoId: video.id, reason: 'super reason' })
93 68
94 await waitJobs(servers) 69 await waitJobs(servers)
95 await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence') 70 await checkNewVideoAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
96 }) 71 })
97 72
98 it('Should send a notification to moderators on remote video abuse', async function () { 73 it('Should send a notification to moderators on remote video abuse', async function () {
99 this.timeout(20000) 74 this.timeout(20000)
100 75
101 const name = 'video for abuse ' + buildUUID() 76 const name = 'video for abuse ' + buildUUID()
102 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 77 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
103 const video = resVideo.body.video
104 78
105 await waitJobs(servers) 79 await waitJobs(servers)
106 80
107 const videoId = await getVideoIdFromUUID(servers[1].url, video.uuid) 81 const videoId = await servers[1].videos.getId({ uuid: video.uuid })
108 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'super reason' }) 82 await servers[1].abuses.report({ videoId, reason: 'super reason' })
109 83
110 await waitJobs(servers) 84 await waitJobs(servers)
111 await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence') 85 await checkNewVideoAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
112 }) 86 })
113 87
114 it('Should send a notification to moderators on local comment abuse', async function () { 88 it('Should send a notification to moderators on local comment abuse', async function () {
115 this.timeout(20000) 89 this.timeout(20000)
116 90
117 const name = 'video for abuse ' + buildUUID() 91 const name = 'video for abuse ' + buildUUID()
118 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 92 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
119 const video = resVideo.body.video 93 const comment = await servers[0].comments.createThread({
120 const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + buildUUID()) 94 token: userAccessToken,
121 const comment = resComment.body.comment 95 videoId: video.id,
96 text: 'comment abuse ' + buildUUID()
97 })
122 98
123 await waitJobs(servers) 99 await waitJobs(servers)
124 100
125 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, commentId: comment.id, reason: 'super reason' }) 101 await servers[0].abuses.report({ commentId: comment.id, reason: 'super reason' })
126 102
127 await waitJobs(servers) 103 await waitJobs(servers)
128 await checkNewCommentAbuseForModerators(baseParams, video.uuid, name, 'presence') 104 await checkNewCommentAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
129 }) 105 })
130 106
131 it('Should send a notification to moderators on remote comment abuse', async function () { 107 it('Should send a notification to moderators on remote comment abuse', async function () {
132 this.timeout(20000) 108 this.timeout(20000)
133 109
134 const name = 'video for abuse ' + buildUUID() 110 const name = 'video for abuse ' + buildUUID()
135 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 111 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
136 const video = resVideo.body.video 112
137 await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + buildUUID()) 113 await servers[0].comments.createThread({
114 token: userAccessToken,
115 videoId: video.id,
116 text: 'comment abuse ' + buildUUID()
117 })
138 118
139 await waitJobs(servers) 119 await waitJobs(servers)
140 120
141 const resComments = await getVideoCommentThreads(servers[1].url, video.uuid, 0, 5) 121 const { data } = await servers[1].comments.listThreads({ videoId: video.uuid })
142 const commentId = resComments.body.data[0].id 122 const commentId = data[0].id
143 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, commentId, reason: 'super reason' }) 123 await servers[1].abuses.report({ commentId, reason: 'super reason' })
144 124
145 await waitJobs(servers) 125 await waitJobs(servers)
146 await checkNewCommentAbuseForModerators(baseParams, video.uuid, name, 'presence') 126 await checkNewCommentAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
147 }) 127 })
148 128
149 it('Should send a notification to moderators on local account abuse', async function () { 129 it('Should send a notification to moderators on local account abuse', async function () {
150 this.timeout(20000) 130 this.timeout(20000)
151 131
152 const username = 'user' + new Date().getTime() 132 const username = 'user' + new Date().getTime()
153 const resUser = await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username, password: 'donald' }) 133 const { account } = await servers[0].users.create({ username, password: 'donald' })
154 const accountId = resUser.body.user.account.id 134 const accountId = account.id
155 135
156 await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, accountId, reason: 'super reason' }) 136 await servers[0].abuses.report({ accountId, reason: 'super reason' })
157 137
158 await waitJobs(servers) 138 await waitJobs(servers)
159 await checkNewAccountAbuseForModerators(baseParams, username, 'presence') 139 await checkNewAccountAbuseForModerators({ ...baseParams, displayName: username, checkType: 'presence' })
160 }) 140 })
161 141
162 it('Should send a notification to moderators on remote account abuse', async function () { 142 it('Should send a notification to moderators on remote account abuse', async function () {
163 this.timeout(20000) 143 this.timeout(20000)
164 144
165 const username = 'user' + new Date().getTime() 145 const username = 'user' + new Date().getTime()
166 const tmpToken = await generateUserAccessToken(servers[0], username) 146 const tmpToken = await servers[0].users.generateUserAndToken(username)
167 await uploadVideo(servers[0].url, tmpToken, { name: 'super video' }) 147 await servers[0].videos.upload({ token: tmpToken, attributes: { name: 'super video' } })
168 148
169 await waitJobs(servers) 149 await waitJobs(servers)
170 150
171 const resAccount = await getAccount(servers[1].url, username + '@' + servers[0].host) 151 const account = await servers[1].accounts.get({ accountName: username + '@' + servers[0].host })
172 await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, accountId: resAccount.body.id, reason: 'super reason' }) 152 await servers[1].abuses.report({ accountId: account.id, reason: 'super reason' })
173 153
174 await waitJobs(servers) 154 await waitJobs(servers)
175 await checkNewAccountAbuseForModerators(baseParams, username, 'presence') 155 await checkNewAccountAbuseForModerators({ ...baseParams, displayName: username, checkType: 'presence' })
176 }) 156 })
177 }) 157 })
178 158
@@ -189,29 +169,28 @@ describe('Test moderation notifications', function () {
189 } 169 }
190 170
191 const name = 'abuse ' + buildUUID() 171 const name = 'abuse ' + buildUUID()
192 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 172 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
193 const video = resVideo.body.video
194 173
195 const res = await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: video.id, reason: 'super reason' }) 174 const body = await servers[0].abuses.report({ token: userAccessToken, videoId: video.id, reason: 'super reason' })
196 abuseId = res.body.abuse.id 175 abuseId = body.abuse.id
197 }) 176 })
198 177
199 it('Should send a notification to reporter if the abuse has been accepted', async function () { 178 it('Should send a notification to reporter if the abuse has been accepted', async function () {
200 this.timeout(10000) 179 this.timeout(10000)
201 180
202 await updateAbuse(servers[0].url, servers[0].accessToken, abuseId, { state: AbuseState.ACCEPTED }) 181 await servers[0].abuses.update({ abuseId, body: { state: AbuseState.ACCEPTED } })
203 await waitJobs(servers) 182 await waitJobs(servers)
204 183
205 await checkAbuseStateChange(baseParams, abuseId, AbuseState.ACCEPTED, 'presence') 184 await checkAbuseStateChange({ ...baseParams, abuseId, state: AbuseState.ACCEPTED, checkType: 'presence' })
206 }) 185 })
207 186
208 it('Should send a notification to reporter if the abuse has been rejected', async function () { 187 it('Should send a notification to reporter if the abuse has been rejected', async function () {
209 this.timeout(10000) 188 this.timeout(10000)
210 189
211 await updateAbuse(servers[0].url, servers[0].accessToken, abuseId, { state: AbuseState.REJECTED }) 190 await servers[0].abuses.update({ abuseId, body: { state: AbuseState.REJECTED } })
212 await waitJobs(servers) 191 await waitJobs(servers)
213 192
214 await checkAbuseStateChange(baseParams, abuseId, AbuseState.REJECTED, 'presence') 193 await checkAbuseStateChange({ ...baseParams, abuseId, state: AbuseState.REJECTED, checkType: 'presence' })
215 }) 194 })
216 }) 195 })
217 196
@@ -237,17 +216,16 @@ describe('Test moderation notifications', function () {
237 } 216 }
238 217
239 const name = 'abuse ' + buildUUID() 218 const name = 'abuse ' + buildUUID()
240 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 219 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
241 const video = resVideo.body.video
242 220
243 { 221 {
244 const res = await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: video.id, reason: 'super reason' }) 222 const body = await servers[0].abuses.report({ token: userAccessToken, videoId: video.id, reason: 'super reason' })
245 abuseId = res.body.abuse.id 223 abuseId = body.abuse.id
246 } 224 }
247 225
248 { 226 {
249 const res = await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: video.id, reason: 'super reason 2' }) 227 const body = await servers[0].abuses.report({ token: userAccessToken, videoId: video.id, reason: 'super reason 2' })
250 abuseId2 = res.body.abuse.id 228 abuseId2 = body.abuse.id
251 } 229 }
252 }) 230 })
253 231
@@ -255,40 +233,43 @@ describe('Test moderation notifications', function () {
255 this.timeout(10000) 233 this.timeout(10000)
256 234
257 const message = 'my super message to users' 235 const message = 'my super message to users'
258 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, message) 236 await servers[0].abuses.addMessage({ abuseId, message })
259 await waitJobs(servers) 237 await waitJobs(servers)
260 238
261 await checkNewAbuseMessage(baseParamsUser, abuseId, message, 'user_1@example.com', 'presence') 239 await checkNewAbuseMessage({ ...baseParamsUser, abuseId, message, toEmail: 'user_1@example.com', checkType: 'presence' })
262 }) 240 })
263 241
264 it('Should not send a notification to the admin if sent by the admin', async function () { 242 it('Should not send a notification to the admin if sent by the admin', async function () {
265 this.timeout(10000) 243 this.timeout(10000)
266 244
267 const message = 'my super message that should not be sent to the admin' 245 const message = 'my super message that should not be sent to the admin'
268 await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, message) 246 await servers[0].abuses.addMessage({ abuseId, message })
269 await waitJobs(servers) 247 await waitJobs(servers)
270 248
271 await checkNewAbuseMessage(baseParamsAdmin, abuseId, message, 'admin' + servers[0].internalServerNumber + '@example.com', 'absence') 249 const toEmail = 'admin' + servers[0].internalServerNumber + '@example.com'
250 await checkNewAbuseMessage({ ...baseParamsAdmin, abuseId, message, toEmail, checkType: 'absence' })
272 }) 251 })
273 252
274 it('Should send a notification to moderators', async function () { 253 it('Should send a notification to moderators', async function () {
275 this.timeout(10000) 254 this.timeout(10000)
276 255
277 const message = 'my super message to moderators' 256 const message = 'my super message to moderators'
278 await addAbuseMessage(servers[0].url, userAccessToken, abuseId2, message) 257 await servers[0].abuses.addMessage({ token: userAccessToken, abuseId: abuseId2, message })
279 await waitJobs(servers) 258 await waitJobs(servers)
280 259
281 await checkNewAbuseMessage(baseParamsAdmin, abuseId2, message, 'admin' + servers[0].internalServerNumber + '@example.com', 'presence') 260 const toEmail = 'admin' + servers[0].internalServerNumber + '@example.com'
261 await checkNewAbuseMessage({ ...baseParamsAdmin, abuseId: abuseId2, message, toEmail, checkType: 'presence' })
282 }) 262 })
283 263
284 it('Should not send a notification to reporter if sent by the reporter', async function () { 264 it('Should not send a notification to reporter if sent by the reporter', async function () {
285 this.timeout(10000) 265 this.timeout(10000)
286 266
287 const message = 'my super message that should not be sent to reporter' 267 const message = 'my super message that should not be sent to reporter'
288 await addAbuseMessage(servers[0].url, userAccessToken, abuseId2, message) 268 await servers[0].abuses.addMessage({ token: userAccessToken, abuseId: abuseId2, message })
289 await waitJobs(servers) 269 await waitJobs(servers)
290 270
291 await checkNewAbuseMessage(baseParamsUser, abuseId2, message, 'user_1@example.com', 'absence') 271 const toEmail = 'user_1@example.com'
272 await checkNewAbuseMessage({ ...baseParamsUser, abuseId: abuseId2, message, toEmail, checkType: 'absence' })
292 }) 273 })
293 }) 274 })
294 275
@@ -308,30 +289,28 @@ describe('Test moderation notifications', function () {
308 this.timeout(10000) 289 this.timeout(10000)
309 290
310 const name = 'video for abuse ' + buildUUID() 291 const name = 'video for abuse ' + buildUUID()
311 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 292 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
312 const uuid = resVideo.body.video.uuid
313 293
314 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) 294 await servers[0].blacklist.add({ videoId: uuid })
315 295
316 await waitJobs(servers) 296 await waitJobs(servers)
317 await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'blacklist') 297 await checkNewBlacklistOnMyVideo({ ...baseParams, shortUUID, videoName: name, blacklistType: 'blacklist' })
318 }) 298 })
319 299
320 it('Should send a notification to video owner on unblacklist', async function () { 300 it('Should send a notification to video owner on unblacklist', async function () {
321 this.timeout(10000) 301 this.timeout(10000)
322 302
323 const name = 'video for abuse ' + buildUUID() 303 const name = 'video for abuse ' + buildUUID()
324 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) 304 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userAccessToken, attributes: { name } })
325 const uuid = resVideo.body.video.uuid
326 305
327 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid) 306 await servers[0].blacklist.add({ videoId: uuid })
328 307
329 await waitJobs(servers) 308 await waitJobs(servers)
330 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid) 309 await servers[0].blacklist.remove({ videoId: uuid })
331 await waitJobs(servers) 310 await waitJobs(servers)
332 311
333 await wait(500) 312 await wait(500)
334 await checkNewBlacklistOnMyVideo(baseParams, uuid, name, 'unblacklist') 313 await checkNewBlacklistOnMyVideo({ ...baseParams, shortUUID, videoName: name, blacklistType: 'unblacklist' })
335 }) 314 })
336 }) 315 })
337 316
@@ -350,14 +329,14 @@ describe('Test moderation notifications', function () {
350 it('Should send a notification only to moderators when a user registers on the instance', async function () { 329 it('Should send a notification only to moderators when a user registers on the instance', async function () {
351 this.timeout(10000) 330 this.timeout(10000)
352 331
353 await registerUser(servers[0].url, 'user_45', 'password') 332 await servers[0].users.register({ username: 'user_45' })
354 333
355 await waitJobs(servers) 334 await waitJobs(servers)
356 335
357 await checkUserRegistered(baseParams, 'user_45', 'presence') 336 await checkUserRegistered({ ...baseParams, username: 'user_45', checkType: 'presence' })
358 337
359 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 338 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
360 await checkUserRegistered(immutableAssign(baseParams, userOverride), 'user_45', 'absence') 339 await checkUserRegistered({ ...baseParams, ...userOverride, username: 'user_45', checkType: 'absence' })
361 }) 340 })
362 }) 341 })
363 342
@@ -392,20 +371,20 @@ describe('Test moderation notifications', function () {
392 it('Should send a notification only to admin when there is a new instance follower', async function () { 371 it('Should send a notification only to admin when there is a new instance follower', async function () {
393 this.timeout(20000) 372 this.timeout(20000)
394 373
395 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 374 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
396 375
397 await waitJobs(servers) 376 await waitJobs(servers)
398 377
399 await checkNewInstanceFollower(baseParams, 'localhost:' + servers[2].port, 'presence') 378 await checkNewInstanceFollower({ ...baseParams, followerHost: 'localhost:' + servers[2].port, checkType: 'presence' })
400 379
401 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 380 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
402 await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:' + servers[2].port, 'absence') 381 await checkNewInstanceFollower({ ...baseParams, ...userOverride, followerHost: 'localhost:' + servers[2].port, checkType: 'absence' })
403 }) 382 })
404 383
405 it('Should send a notification on auto follow back', async function () { 384 it('Should send a notification on auto follow back', async function () {
406 this.timeout(40000) 385 this.timeout(40000)
407 386
408 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 387 await servers[2].follows.unfollow({ target: servers[0] })
409 await waitJobs(servers) 388 await waitJobs(servers)
410 389
411 const config = { 390 const config = {
@@ -415,41 +394,41 @@ describe('Test moderation notifications', function () {
415 } 394 }
416 } 395 }
417 } 396 }
418 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 397 await servers[0].config.updateCustomSubConfig({ newConfig: config })
419 398
420 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 399 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
421 400
422 await waitJobs(servers) 401 await waitJobs(servers)
423 402
424 const followerHost = servers[0].host 403 const followerHost = servers[0].host
425 const followingHost = servers[2].host 404 const followingHost = servers[2].host
426 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') 405 await checkAutoInstanceFollowing({ ...baseParams, followerHost, followingHost, checkType: 'presence' })
427 406
428 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } 407 const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } }
429 await checkAutoInstanceFollowing(immutableAssign(baseParams, userOverride), followerHost, followingHost, 'absence') 408 await checkAutoInstanceFollowing({ ...baseParams, ...userOverride, followerHost, followingHost, checkType: 'absence' })
430 409
431 config.followings.instance.autoFollowBack.enabled = false 410 config.followings.instance.autoFollowBack.enabled = false
432 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 411 await servers[0].config.updateCustomSubConfig({ newConfig: config })
433 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 412 await servers[0].follows.unfollow({ target: servers[2] })
434 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 413 await servers[2].follows.unfollow({ target: servers[0] })
435 }) 414 })
436 415
437 it('Should send a notification on auto instances index follow', async function () { 416 it('Should send a notification on auto instances index follow', async function () {
438 this.timeout(30000) 417 this.timeout(30000)
439 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 418 await servers[0].follows.unfollow({ target: servers[1] })
440 419
441 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 420 await servers[0].config.updateCustomSubConfig({ newConfig: config })
442 421
443 await wait(5000) 422 await wait(5000)
444 await waitJobs(servers) 423 await waitJobs(servers)
445 424
446 const followerHost = servers[0].host 425 const followerHost = servers[0].host
447 const followingHost = servers[1].host 426 const followingHost = servers[1].host
448 await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') 427 await checkAutoInstanceFollowing({ ...baseParams, followerHost, followingHost, checkType: 'presence' })
449 428
450 config.followings.instance.autoFollowIndex.enabled = false 429 config.followings.instance.autoFollowIndex.enabled = false
451 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 430 await servers[0].config.updateCustomSubConfig({ newConfig: config })
452 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 431 await servers[0].follows.unfollow({ target: servers[1] })
453 }) 432 })
454 }) 433 })
455 434
@@ -457,7 +436,8 @@ describe('Test moderation notifications', function () {
457 let userBaseParams: CheckerBaseParams 436 let userBaseParams: CheckerBaseParams
458 let adminBaseParamsServer1: CheckerBaseParams 437 let adminBaseParamsServer1: CheckerBaseParams
459 let adminBaseParamsServer2: CheckerBaseParams 438 let adminBaseParamsServer2: CheckerBaseParams
460 let videoUUID: string 439 let uuid: string
440 let shortUUID: string
461 let videoName: string 441 let videoName: string
462 let currentCustomConfig: CustomConfig 442 let currentCustomConfig: CustomConfig
463 443
@@ -484,9 +464,11 @@ describe('Test moderation notifications', function () {
484 token: userAccessToken 464 token: userAccessToken
485 } 465 }
486 466
487 const resCustomConfig = await getCustomConfig(servers[0].url, servers[0].accessToken) 467 currentCustomConfig = await servers[0].config.getCustomConfig()
488 currentCustomConfig = resCustomConfig.body 468
489 const autoBlacklistTestsCustomConfig = immutableAssign(currentCustomConfig, { 469 const autoBlacklistTestsCustomConfig = {
470 ...currentCustomConfig,
471
490 autoBlacklist: { 472 autoBlacklist: {
491 videos: { 473 videos: {
492 ofUsers: { 474 ofUsers: {
@@ -494,43 +476,44 @@ describe('Test moderation notifications', function () {
494 } 476 }
495 } 477 }
496 } 478 }
497 }) 479 }
480
498 // enable transcoding otherwise own publish notification after transcoding not expected 481 // enable transcoding otherwise own publish notification after transcoding not expected
499 autoBlacklistTestsCustomConfig.transcoding.enabled = true 482 autoBlacklistTestsCustomConfig.transcoding.enabled = true
500 await updateCustomConfig(servers[0].url, servers[0].accessToken, autoBlacklistTestsCustomConfig) 483 await servers[0].config.updateCustomConfig({ newCustomConfig: autoBlacklistTestsCustomConfig })
501
502 await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port)
503 await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port)
504 484
485 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
486 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
505 }) 487 })
506 488
507 it('Should send notification to moderators on new video with auto-blacklist', async function () { 489 it('Should send notification to moderators on new video with auto-blacklist', async function () {
508 this.timeout(40000) 490 this.timeout(40000)
509 491
510 videoName = 'video with auto-blacklist ' + buildUUID() 492 videoName = 'video with auto-blacklist ' + buildUUID()
511 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName }) 493 const video = await servers[0].videos.upload({ token: userAccessToken, attributes: { name: videoName } })
512 videoUUID = resVideo.body.video.uuid 494 shortUUID = video.shortUUID
495 uuid = video.uuid
513 496
514 await waitJobs(servers) 497 await waitJobs(servers)
515 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, videoUUID, videoName, 'presence') 498 await checkVideoAutoBlacklistForModerators({ ...adminBaseParamsServer1, shortUUID, videoName, checkType: 'presence' })
516 }) 499 })
517 500
518 it('Should not send video publish notification if auto-blacklisted', async function () { 501 it('Should not send video publish notification if auto-blacklisted', async function () {
519 await checkVideoIsPublished(userBaseParams, videoName, videoUUID, 'absence') 502 await checkVideoIsPublished({ ...userBaseParams, videoName, shortUUID, checkType: 'absence' })
520 }) 503 })
521 504
522 it('Should not send a local user subscription notification if auto-blacklisted', async function () { 505 it('Should not send a local user subscription notification if auto-blacklisted', async function () {
523 await checkNewVideoFromSubscription(adminBaseParamsServer1, videoName, videoUUID, 'absence') 506 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'absence' })
524 }) 507 })
525 508
526 it('Should not send a remote user subscription notification if auto-blacklisted', async function () { 509 it('Should not send a remote user subscription notification if auto-blacklisted', async function () {
527 await checkNewVideoFromSubscription(adminBaseParamsServer2, videoName, videoUUID, 'absence') 510 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'absence' })
528 }) 511 })
529 512
530 it('Should send video published and unblacklist after video unblacklisted', async function () { 513 it('Should send video published and unblacklist after video unblacklisted', async function () {
531 this.timeout(40000) 514 this.timeout(40000)
532 515
533 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoUUID) 516 await servers[0].blacklist.remove({ videoId: uuid })
534 517
535 await waitJobs(servers) 518 await waitJobs(servers)
536 519
@@ -541,11 +524,11 @@ describe('Test moderation notifications', function () {
541 }) 524 })
542 525
543 it('Should send a local user subscription notification after removed from blacklist', async function () { 526 it('Should send a local user subscription notification after removed from blacklist', async function () {
544 await checkNewVideoFromSubscription(adminBaseParamsServer1, videoName, videoUUID, 'presence') 527 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'presence' })
545 }) 528 })
546 529
547 it('Should send a remote user subscription notification after removed from blacklist', async function () { 530 it('Should send a remote user subscription notification after removed from blacklist', async function () {
548 await checkNewVideoFromSubscription(adminBaseParamsServer2, videoName, videoUUID, 'presence') 531 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'presence' })
549 }) 532 })
550 533
551 it('Should send unblacklist but not published/subscription notes after unblacklisted if scheduled update pending', async function () { 534 it('Should send unblacklist but not published/subscription notes after unblacklisted if scheduled update pending', async function () {
@@ -555,29 +538,28 @@ describe('Test moderation notifications', function () {
555 538
556 const name = 'video with auto-blacklist and future schedule ' + buildUUID() 539 const name = 'video with auto-blacklist and future schedule ' + buildUUID()
557 540
558 const data = { 541 const attributes = {
559 name, 542 name,
560 privacy: VideoPrivacy.PRIVATE, 543 privacy: VideoPrivacy.PRIVATE,
561 scheduleUpdate: { 544 scheduleUpdate: {
562 updateAt: updateAt.toISOString(), 545 updateAt: updateAt.toISOString(),
563 privacy: VideoPrivacy.PUBLIC 546 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
564 } 547 }
565 } 548 }
566 549
567 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data) 550 const { shortUUID, uuid } = await servers[0].videos.upload({ token: userAccessToken, attributes })
568 const uuid = resVideo.body.video.uuid
569 551
570 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid) 552 await servers[0].blacklist.remove({ videoId: uuid })
571 553
572 await waitJobs(servers) 554 await waitJobs(servers)
573 await checkNewBlacklistOnMyVideo(userBaseParams, uuid, name, 'unblacklist') 555 await checkNewBlacklistOnMyVideo({ ...userBaseParams, shortUUID, videoName: name, blacklistType: 'unblacklist' })
574 556
575 // FIXME: Can't test absence as two notifications sent to same user and util only checks last one 557 // FIXME: Can't test absence as two notifications sent to same user and util only checks last one
576 // One notification might be better anyways 558 // One notification might be better anyways
577 // await checkVideoIsPublished(userBaseParams, name, uuid, 'absence') 559 // await checkVideoIsPublished(userBaseParams, name, uuid, 'absence')
578 560
579 await checkNewVideoFromSubscription(adminBaseParamsServer1, name, uuid, 'absence') 561 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
580 await checkNewVideoFromSubscription(adminBaseParamsServer2, name, uuid, 'absence') 562 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
581 }) 563 })
582 564
583 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () { 565 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () {
@@ -588,22 +570,21 @@ describe('Test moderation notifications', function () {
588 570
589 const name = 'video with schedule done and still auto-blacklisted ' + buildUUID() 571 const name = 'video with schedule done and still auto-blacklisted ' + buildUUID()
590 572
591 const data = { 573 const attributes = {
592 name, 574 name,
593 privacy: VideoPrivacy.PRIVATE, 575 privacy: VideoPrivacy.PRIVATE,
594 scheduleUpdate: { 576 scheduleUpdate: {
595 updateAt: updateAt.toISOString(), 577 updateAt: updateAt.toISOString(),
596 privacy: VideoPrivacy.PUBLIC 578 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
597 } 579 }
598 } 580 }
599 581
600 const resVideo = await uploadVideo(servers[0].url, userAccessToken, data) 582 const { shortUUID } = await servers[0].videos.upload({ token: userAccessToken, attributes })
601 const uuid = resVideo.body.video.uuid
602 583
603 await wait(6000) 584 await wait(6000)
604 await checkVideoIsPublished(userBaseParams, name, uuid, 'absence') 585 await checkVideoIsPublished({ ...userBaseParams, videoName: name, shortUUID, checkType: 'absence' })
605 await checkNewVideoFromSubscription(adminBaseParamsServer1, name, uuid, 'absence') 586 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
606 await checkNewVideoFromSubscription(adminBaseParamsServer2, name, uuid, 'absence') 587 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
607 }) 588 })
608 589
609 it('Should not send a notification to moderators on new video without auto-blacklist', async function () { 590 it('Should not send a notification to moderators on new video without auto-blacklist', async function () {
@@ -612,18 +593,17 @@ describe('Test moderation notifications', function () {
612 const name = 'video without auto-blacklist ' + buildUUID() 593 const name = 'video without auto-blacklist ' + buildUUID()
613 594
614 // admin with blacklist right will not be auto-blacklisted 595 // admin with blacklist right will not be auto-blacklisted
615 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name }) 596 const { shortUUID } = await servers[0].videos.upload({ attributes: { name } })
616 const uuid = resVideo.body.video.uuid
617 597
618 await waitJobs(servers) 598 await waitJobs(servers)
619 await checkVideoAutoBlacklistForModerators(adminBaseParamsServer1, uuid, name, 'absence') 599 await checkVideoAutoBlacklistForModerators({ ...adminBaseParamsServer1, shortUUID, videoName: name, checkType: 'absence' })
620 }) 600 })
621 601
622 after(async () => { 602 after(async () => {
623 await updateCustomConfig(servers[0].url, servers[0].accessToken, currentCustomConfig) 603 await servers[0].config.updateCustomConfig({ newCustomConfig: currentCustomConfig })
624 604
625 await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 605 await servers[0].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
626 await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 606 await servers[1].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
627 }) 607 })
628 }) 608 })
629 609
diff --git a/server/tests/api/notifications/notifications-api.ts b/server/tests/api/notifications/notifications-api.ts
index b81995449..a529a9bf7 100644
--- a/server/tests/api/notifications/notifications-api.ts
+++ b/server/tests/api/notifications/notifications-api.ts
@@ -2,28 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { addUserSubscription } from '@shared/extra-utils/users/user-subscriptions'
6import { cleanupTests, getMyUserInformation, immutableAssign, uploadRandomVideo, waitJobs } from '../../../../shared/extra-utils'
7import { ServerInfo } from '../../../../shared/extra-utils/index'
8import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
9import { 5import {
10 CheckerBaseParams, 6 CheckerBaseParams,
11 checkNewVideoFromSubscription, 7 checkNewVideoFromSubscription,
8 cleanupTests,
12 getAllNotificationsSettings, 9 getAllNotificationsSettings,
13 getUserNotifications, 10 MockSmtpServer,
14 markAsReadAllNotifications, 11 PeerTubeServer,
15 markAsReadNotifications,
16 prepareNotificationsTest, 12 prepareNotificationsTest,
17 updateMyNotificationSettings 13 waitJobs
18} from '../../../../shared/extra-utils/users/user-notifications' 14} from '@shared/extra-utils'
19import { User, UserNotification, UserNotificationSettingValue } from '../../../../shared/models/users' 15import { UserNotification, UserNotificationSettingValue } from '@shared/models'
20 16
21const expect = chai.expect 17const expect = chai.expect
22 18
23describe('Test notifications API', function () { 19describe('Test notifications API', function () {
24 let server: ServerInfo 20 let server: PeerTubeServer
25 let userNotifications: UserNotification[] = [] 21 let userNotifications: UserNotification[] = []
26 let userAccessToken: string 22 let userToken: string
27 let emails: object[] = [] 23 let emails: object[] = []
28 24
29 before(async function () { 25 before(async function () {
@@ -31,14 +27,14 @@ describe('Test notifications API', function () {
31 27
32 const res = await prepareNotificationsTest(1) 28 const res = await prepareNotificationsTest(1)
33 emails = res.emails 29 emails = res.emails
34 userAccessToken = res.userAccessToken 30 userToken = res.userAccessToken
35 userNotifications = res.userNotifications 31 userNotifications = res.userNotifications
36 server = res.servers[0] 32 server = res.servers[0]
37 33
38 await addUserSubscription(server.url, userAccessToken, 'root_channel@localhost:' + server.port) 34 await server.subscriptions.add({ token: userToken, targetUri: 'root_channel@localhost:' + server.port })
39 35
40 for (let i = 0; i < 10; i++) { 36 for (let i = 0; i < 10; i++) {
41 await uploadRandomVideo(server, false) 37 await server.videos.randomUpload({ wait: false })
42 } 38 }
43 39
44 await waitJobs([ server ]) 40 await waitJobs([ server ])
@@ -47,49 +43,46 @@ describe('Test notifications API', function () {
47 describe('Mark as read', function () { 43 describe('Mark as read', function () {
48 44
49 it('Should mark as read some notifications', async function () { 45 it('Should mark as read some notifications', async function () {
50 const res = await getUserNotifications(server.url, userAccessToken, 2, 3) 46 const { data } = await server.notifications.list({ token: userToken, start: 2, count: 3 })
51 const ids = res.body.data.map(n => n.id) 47 const ids = data.map(n => n.id)
52 48
53 await markAsReadNotifications(server.url, userAccessToken, ids) 49 await server.notifications.markAsRead({ token: userToken, ids })
54 }) 50 })
55 51
56 it('Should have the notifications marked as read', async function () { 52 it('Should have the notifications marked as read', async function () {
57 const res = await getUserNotifications(server.url, userAccessToken, 0, 10) 53 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10 })
58 54
59 const notifications = res.body.data as UserNotification[] 55 expect(data[0].read).to.be.false
60 expect(notifications[0].read).to.be.false 56 expect(data[1].read).to.be.false
61 expect(notifications[1].read).to.be.false 57 expect(data[2].read).to.be.true
62 expect(notifications[2].read).to.be.true 58 expect(data[3].read).to.be.true
63 expect(notifications[3].read).to.be.true 59 expect(data[4].read).to.be.true
64 expect(notifications[4].read).to.be.true 60 expect(data[5].read).to.be.false
65 expect(notifications[5].read).to.be.false
66 }) 61 })
67 62
68 it('Should only list read notifications', async function () { 63 it('Should only list read notifications', async function () {
69 const res = await getUserNotifications(server.url, userAccessToken, 0, 10, false) 64 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: false })
70 65
71 const notifications = res.body.data as UserNotification[] 66 for (const notification of data) {
72 for (const notification of notifications) {
73 expect(notification.read).to.be.true 67 expect(notification.read).to.be.true
74 } 68 }
75 }) 69 })
76 70
77 it('Should only list unread notifications', async function () { 71 it('Should only list unread notifications', async function () {
78 const res = await getUserNotifications(server.url, userAccessToken, 0, 10, true) 72 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: true })
79 73
80 const notifications = res.body.data as UserNotification[] 74 for (const notification of data) {
81 for (const notification of notifications) {
82 expect(notification.read).to.be.false 75 expect(notification.read).to.be.false
83 } 76 }
84 }) 77 })
85 78
86 it('Should mark as read all notifications', async function () { 79 it('Should mark as read all notifications', async function () {
87 await markAsReadAllNotifications(server.url, userAccessToken) 80 await server.notifications.markAsReadAll({ token: userToken })
88 81
89 const res = await getUserNotifications(server.url, userAccessToken, 0, 10, true) 82 const body = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: true })
90 83
91 expect(res.body.total).to.equal(0) 84 expect(body.total).to.equal(0)
92 expect(res.body.data).to.have.lengthOf(0) 85 expect(body.data).to.have.lengthOf(0)
93 }) 86 })
94 }) 87 })
95 88
@@ -101,99 +94,102 @@ describe('Test notifications API', function () {
101 server: server, 94 server: server,
102 emails, 95 emails,
103 socketNotifications: userNotifications, 96 socketNotifications: userNotifications,
104 token: userAccessToken 97 token: userToken
105 } 98 }
106 }) 99 })
107 100
108 it('Should not have notifications', async function () { 101 it('Should not have notifications', async function () {
109 this.timeout(20000) 102 this.timeout(20000)
110 103
111 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 104 await server.notifications.updateMySettings({
112 newVideoFromSubscription: UserNotificationSettingValue.NONE 105 token: userToken,
113 })) 106 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.NONE }
107 })
114 108
115 { 109 {
116 const res = await getMyUserInformation(server.url, userAccessToken) 110 const info = await server.users.getMyInfo({ token: userToken })
117 const info = res.body as User
118 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE) 111 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE)
119 } 112 }
120 113
121 const { name, uuid } = await uploadRandomVideo(server) 114 const { name, shortUUID } = await server.videos.randomUpload()
122 115
123 const check = { web: true, mail: true } 116 const check = { web: true, mail: true }
124 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') 117 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
125 }) 118 })
126 119
127 it('Should only have web notifications', async function () { 120 it('Should only have web notifications', async function () {
128 this.timeout(20000) 121 this.timeout(20000)
129 122
130 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 123 await server.notifications.updateMySettings({
131 newVideoFromSubscription: UserNotificationSettingValue.WEB 124 token: userToken,
132 })) 125 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.WEB }
126 })
133 127
134 { 128 {
135 const res = await getMyUserInformation(server.url, userAccessToken) 129 const info = await server.users.getMyInfo({ token: userToken })
136 const info = res.body as User
137 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB) 130 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB)
138 } 131 }
139 132
140 const { name, uuid } = await uploadRandomVideo(server) 133 const { name, shortUUID } = await server.videos.randomUpload()
141 134
142 { 135 {
143 const check = { mail: true, web: false } 136 const check = { mail: true, web: false }
144 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') 137 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
145 } 138 }
146 139
147 { 140 {
148 const check = { mail: false, web: true } 141 const check = { mail: false, web: true }
149 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence') 142 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'presence' })
150 } 143 }
151 }) 144 })
152 145
153 it('Should only have mail notifications', async function () { 146 it('Should only have mail notifications', async function () {
154 this.timeout(20000) 147 this.timeout(20000)
155 148
156 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 149 await server.notifications.updateMySettings({
157 newVideoFromSubscription: UserNotificationSettingValue.EMAIL 150 token: userToken,
158 })) 151 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.EMAIL }
152 })
159 153
160 { 154 {
161 const res = await getMyUserInformation(server.url, userAccessToken) 155 const info = await server.users.getMyInfo({ token: userToken })
162 const info = res.body as User
163 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL) 156 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL)
164 } 157 }
165 158
166 const { name, uuid } = await uploadRandomVideo(server) 159 const { name, shortUUID } = await server.videos.randomUpload()
167 160
168 { 161 {
169 const check = { mail: false, web: true } 162 const check = { mail: false, web: true }
170 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'absence') 163 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
171 } 164 }
172 165
173 { 166 {
174 const check = { mail: true, web: false } 167 const check = { mail: true, web: false }
175 await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), name, uuid, 'presence') 168 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'presence' })
176 } 169 }
177 }) 170 })
178 171
179 it('Should have email and web notifications', async function () { 172 it('Should have email and web notifications', async function () {
180 this.timeout(20000) 173 this.timeout(20000)
181 174
182 await updateMyNotificationSettings(server.url, userAccessToken, immutableAssign(getAllNotificationsSettings(), { 175 await server.notifications.updateMySettings({
183 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL 176 token: userToken,
184 })) 177 settings: {
178 ...getAllNotificationsSettings(),
179 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
180 }
181 })
185 182
186 { 183 {
187 const res = await getMyUserInformation(server.url, userAccessToken) 184 const info = await server.users.getMyInfo({ token: userToken })
188 const info = res.body as User
189 expect(info.notificationSettings.newVideoFromSubscription).to.equal( 185 expect(info.notificationSettings.newVideoFromSubscription).to.equal(
190 UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL 186 UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
191 ) 187 )
192 } 188 }
193 189
194 const { name, uuid } = await uploadRandomVideo(server) 190 const { name, shortUUID } = await server.videos.randomUpload()
195 191
196 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 192 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
197 }) 193 })
198 }) 194 })
199 195
diff --git a/server/tests/api/notifications/user-notifications.ts b/server/tests/api/notifications/user-notifications.ts
index e981c1718..e53ab2aa5 100644
--- a/server/tests/api/notifications/user-notifications.ts
+++ b/server/tests/api/notifications/user-notifications.ts
@@ -4,34 +4,26 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { buildUUID } from '@server/helpers/uuid' 5import { buildUUID } from '@server/helpers/uuid'
6import { 6import {
7 cleanupTests,
8 updateMyUser,
9 updateVideo,
10 updateVideoChannel,
11 uploadRandomVideoOnServers,
12 wait
13} from '../../../../shared/extra-utils'
14import { ServerInfo } from '../../../../shared/extra-utils/index'
15import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
16import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
17import {
18 CheckerBaseParams, 7 CheckerBaseParams,
19 checkMyVideoImportIsFinished, 8 checkMyVideoImportIsFinished,
20 checkNewActorFollow, 9 checkNewActorFollow,
21 checkNewVideoFromSubscription, 10 checkNewVideoFromSubscription,
22 checkVideoIsPublished, 11 checkVideoIsPublished,
23 getLastNotification, 12 cleanupTests,
24 prepareNotificationsTest 13 FIXTURE_URLS,
25} from '../../../../shared/extra-utils/users/user-notifications' 14 MockSmtpServer,
26import { addUserSubscription, removeUserSubscription } from '../../../../shared/extra-utils/users/user-subscriptions' 15 PeerTubeServer,
27import { getBadVideoUrl, getGoodVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports' 16 prepareNotificationsTest,
28import { UserNotification, UserNotificationType } from '../../../../shared/models/users' 17 uploadRandomVideoOnServers,
29import { VideoPrivacy } from '../../../../shared/models/videos' 18 wait,
19 waitJobs
20} from '@shared/extra-utils'
21import { UserNotification, UserNotificationType, VideoPrivacy } from '@shared/models'
30 22
31const expect = chai.expect 23const expect = chai.expect
32 24
33describe('Test user notifications', function () { 25describe('Test user notifications', function () {
34 let servers: ServerInfo[] = [] 26 let servers: PeerTubeServer[] = []
35 let userAccessToken: string 27 let userAccessToken: string
36 let userNotifications: UserNotification[] = [] 28 let userNotifications: UserNotification[] = []
37 let adminNotifications: UserNotification[] = [] 29 let adminNotifications: UserNotification[] = []
@@ -69,7 +61,7 @@ describe('Test user notifications', function () {
69 61
70 await uploadRandomVideoOnServers(servers, 1) 62 await uploadRandomVideoOnServers(servers, 1)
71 63
72 const notification = await getLastNotification(servers[0].url, userAccessToken) 64 const notification = await servers[0].notifications.getLastest({ token: userAccessToken })
73 expect(notification).to.be.undefined 65 expect(notification).to.be.undefined
74 66
75 expect(emails).to.have.lengthOf(0) 67 expect(emails).to.have.lengthOf(0)
@@ -79,21 +71,21 @@ describe('Test user notifications', function () {
79 it('Should send a new video notification if the user follows the local video publisher', async function () { 71 it('Should send a new video notification if the user follows the local video publisher', async function () {
80 this.timeout(15000) 72 this.timeout(15000)
81 73
82 await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) 74 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[0].port })
83 await waitJobs(servers) 75 await waitJobs(servers)
84 76
85 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1) 77 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
86 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 78 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
87 }) 79 })
88 80
89 it('Should send a new video notification from a remote account', async function () { 81 it('Should send a new video notification from a remote account', async function () {
90 this.timeout(150000) // Server 2 has transcoding enabled 82 this.timeout(150000) // Server 2 has transcoding enabled
91 83
92 await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[1].port) 84 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[1].port })
93 await waitJobs(servers) 85 await waitJobs(servers)
94 86
95 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2) 87 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2)
96 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 88 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
97 }) 89 })
98 90
99 it('Should send a new video notification on a scheduled publication', async function () { 91 it('Should send a new video notification on a scheduled publication', async function () {
@@ -106,13 +98,13 @@ describe('Test user notifications', function () {
106 privacy: VideoPrivacy.PRIVATE, 98 privacy: VideoPrivacy.PRIVATE,
107 scheduleUpdate: { 99 scheduleUpdate: {
108 updateAt: updateAt.toISOString(), 100 updateAt: updateAt.toISOString(),
109 privacy: VideoPrivacy.PUBLIC 101 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
110 } 102 }
111 } 103 }
112 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 104 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
113 105
114 await wait(6000) 106 await wait(6000)
115 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 107 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
116 }) 108 })
117 109
118 it('Should send a new video notification on a remote scheduled publication', async function () { 110 it('Should send a new video notification on a remote scheduled publication', async function () {
@@ -125,14 +117,14 @@ describe('Test user notifications', function () {
125 privacy: VideoPrivacy.PRIVATE, 117 privacy: VideoPrivacy.PRIVATE,
126 scheduleUpdate: { 118 scheduleUpdate: {
127 updateAt: updateAt.toISOString(), 119 updateAt: updateAt.toISOString(),
128 privacy: VideoPrivacy.PUBLIC 120 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
129 } 121 }
130 } 122 }
131 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 123 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
132 await waitJobs(servers) 124 await waitJobs(servers)
133 125
134 await wait(6000) 126 await wait(6000)
135 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 127 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
136 }) 128 })
137 129
138 it('Should not send a notification before the video is published', async function () { 130 it('Should not send a notification before the video is published', async function () {
@@ -144,64 +136,64 @@ describe('Test user notifications', function () {
144 privacy: VideoPrivacy.PRIVATE, 136 privacy: VideoPrivacy.PRIVATE,
145 scheduleUpdate: { 137 scheduleUpdate: {
146 updateAt: updateAt.toISOString(), 138 updateAt: updateAt.toISOString(),
147 privacy: VideoPrivacy.PUBLIC 139 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
148 } 140 }
149 } 141 }
150 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 142 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
151 143
152 await wait(6000) 144 await wait(6000)
153 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 145 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
154 }) 146 })
155 147
156 it('Should send a new video notification when a video becomes public', async function () { 148 it('Should send a new video notification when a video becomes public', async function () {
157 this.timeout(50000) 149 this.timeout(50000)
158 150
159 const data = { privacy: VideoPrivacy.PRIVATE } 151 const data = { privacy: VideoPrivacy.PRIVATE }
160 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 152 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
161 153
162 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 154 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
163 155
164 await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) 156 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
165 157
166 await waitJobs(servers) 158 await waitJobs(servers)
167 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 159 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
168 }) 160 })
169 161
170 it('Should send a new video notification when a remote video becomes public', async function () { 162 it('Should send a new video notification when a remote video becomes public', async function () {
171 this.timeout(50000) 163 this.timeout(50000)
172 164
173 const data = { privacy: VideoPrivacy.PRIVATE } 165 const data = { privacy: VideoPrivacy.PRIVATE }
174 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 166 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
175 167
176 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 168 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
177 169
178 await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC }) 170 await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
179 171
180 await waitJobs(servers) 172 await waitJobs(servers)
181 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 173 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
182 }) 174 })
183 175
184 it('Should not send a new video notification when a video becomes unlisted', async function () { 176 it('Should not send a new video notification when a video becomes unlisted', async function () {
185 this.timeout(50000) 177 this.timeout(50000)
186 178
187 const data = { privacy: VideoPrivacy.PRIVATE } 179 const data = { privacy: VideoPrivacy.PRIVATE }
188 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1, data) 180 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
189 181
190 await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) 182 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
191 183
192 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 184 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
193 }) 185 })
194 186
195 it('Should not send a new video notification when a remote video becomes unlisted', async function () { 187 it('Should not send a new video notification when a remote video becomes unlisted', async function () {
196 this.timeout(50000) 188 this.timeout(50000)
197 189
198 const data = { privacy: VideoPrivacy.PRIVATE } 190 const data = { privacy: VideoPrivacy.PRIVATE }
199 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 191 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
200 192
201 await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) 193 await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
202 194
203 await waitJobs(servers) 195 await waitJobs(servers)
204 await checkNewVideoFromSubscription(baseParams, name, uuid, 'absence') 196 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
205 }) 197 })
206 198
207 it('Should send a new video notification after a video import', async function () { 199 it('Should send a new video notification after a video import', async function () {
@@ -213,14 +205,13 @@ describe('Test user notifications', function () {
213 name, 205 name,
214 channelId, 206 channelId,
215 privacy: VideoPrivacy.PUBLIC, 207 privacy: VideoPrivacy.PUBLIC,
216 targetUrl: getGoodVideoUrl() 208 targetUrl: FIXTURE_URLS.goodVideo
217 } 209 }
218 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 210 const { video } = await servers[0].imports.importVideo({ attributes })
219 const uuid = res.body.video.uuid
220 211
221 await waitJobs(servers) 212 await waitJobs(servers)
222 213
223 await checkNewVideoFromSubscription(baseParams, name, uuid, 'presence') 214 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
224 }) 215 })
225 }) 216 })
226 217
@@ -239,10 +230,10 @@ describe('Test user notifications', function () {
239 it('Should not send a notification if transcoding is not enabled', async function () { 230 it('Should not send a notification if transcoding is not enabled', async function () {
240 this.timeout(50000) 231 this.timeout(50000)
241 232
242 const { name, uuid } = await uploadRandomVideoOnServers(servers, 1) 233 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
243 await waitJobs(servers) 234 await waitJobs(servers)
244 235
245 await checkVideoIsPublished(baseParams, name, uuid, 'absence') 236 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
246 }) 237 })
247 238
248 it('Should not send a notification if the wait transcoding is false', async function () { 239 it('Should not send a notification if the wait transcoding is false', async function () {
@@ -251,7 +242,7 @@ describe('Test user notifications', function () {
251 await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: false }) 242 await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: false })
252 await waitJobs(servers) 243 await waitJobs(servers)
253 244
254 const notification = await getLastNotification(servers[0].url, userAccessToken) 245 const notification = await servers[0].notifications.getLastest({ token: userAccessToken })
255 if (notification) { 246 if (notification) {
256 expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED) 247 expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED)
257 } 248 }
@@ -260,19 +251,19 @@ describe('Test user notifications', function () {
260 it('Should send a notification even if the video is not transcoded in other resolutions', async function () { 251 it('Should send a notification even if the video is not transcoded in other resolutions', async function () {
261 this.timeout(50000) 252 this.timeout(50000)
262 253
263 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' }) 254 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
264 await waitJobs(servers) 255 await waitJobs(servers)
265 256
266 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 257 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
267 }) 258 })
268 259
269 it('Should send a notification with a transcoded video', async function () { 260 it('Should send a notification with a transcoded video', async function () {
270 this.timeout(50000) 261 this.timeout(50000)
271 262
272 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true }) 263 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
273 await waitJobs(servers) 264 await waitJobs(servers)
274 265
275 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 266 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
276 }) 267 })
277 268
278 it('Should send a notification when an imported video is transcoded', async function () { 269 it('Should send a notification when an imported video is transcoded', async function () {
@@ -284,14 +275,13 @@ describe('Test user notifications', function () {
284 name, 275 name,
285 channelId, 276 channelId,
286 privacy: VideoPrivacy.PUBLIC, 277 privacy: VideoPrivacy.PUBLIC,
287 targetUrl: getGoodVideoUrl(), 278 targetUrl: FIXTURE_URLS.goodVideo,
288 waitTranscoding: true 279 waitTranscoding: true
289 } 280 }
290 const res = await importVideo(servers[1].url, servers[1].accessToken, attributes) 281 const { video } = await servers[1].imports.importVideo({ attributes })
291 const uuid = res.body.video.uuid
292 282
293 await waitJobs(servers) 283 await waitJobs(servers)
294 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 284 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
295 }) 285 })
296 286
297 it('Should send a notification when the scheduled update has been proceeded', async function () { 287 it('Should send a notification when the scheduled update has been proceeded', async function () {
@@ -304,13 +294,13 @@ describe('Test user notifications', function () {
304 privacy: VideoPrivacy.PRIVATE, 294 privacy: VideoPrivacy.PRIVATE,
305 scheduleUpdate: { 295 scheduleUpdate: {
306 updateAt: updateAt.toISOString(), 296 updateAt: updateAt.toISOString(),
307 privacy: VideoPrivacy.PUBLIC 297 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
308 } 298 }
309 } 299 }
310 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 300 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
311 301
312 await wait(6000) 302 await wait(6000)
313 await checkVideoIsPublished(baseParams, name, uuid, 'presence') 303 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
314 }) 304 })
315 305
316 it('Should not send a notification before the video is published', async function () { 306 it('Should not send a notification before the video is published', async function () {
@@ -322,13 +312,13 @@ describe('Test user notifications', function () {
322 privacy: VideoPrivacy.PRIVATE, 312 privacy: VideoPrivacy.PRIVATE,
323 scheduleUpdate: { 313 scheduleUpdate: {
324 updateAt: updateAt.toISOString(), 314 updateAt: updateAt.toISOString(),
325 privacy: VideoPrivacy.PUBLIC 315 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
326 } 316 }
327 } 317 }
328 const { name, uuid } = await uploadRandomVideoOnServers(servers, 2, data) 318 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
329 319
330 await wait(6000) 320 await wait(6000)
331 await checkVideoIsPublished(baseParams, name, uuid, 'absence') 321 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
332 }) 322 })
333 }) 323 })
334 324
@@ -353,13 +343,14 @@ describe('Test user notifications', function () {
353 name, 343 name,
354 channelId, 344 channelId,
355 privacy: VideoPrivacy.PRIVATE, 345 privacy: VideoPrivacy.PRIVATE,
356 targetUrl: getBadVideoUrl() 346 targetUrl: FIXTURE_URLS.badVideo
357 } 347 }
358 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 348 const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
359 const uuid = res.body.video.uuid
360 349
361 await waitJobs(servers) 350 await waitJobs(servers)
362 await checkMyVideoImportIsFinished(baseParams, name, uuid, getBadVideoUrl(), false, 'presence') 351
352 const url = FIXTURE_URLS.badVideo
353 await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: false, checkType: 'presence' })
363 }) 354 })
364 355
365 it('Should send a notification when the video import succeeded', async function () { 356 it('Should send a notification when the video import succeeded', async function () {
@@ -371,13 +362,14 @@ describe('Test user notifications', function () {
371 name, 362 name,
372 channelId, 363 channelId,
373 privacy: VideoPrivacy.PRIVATE, 364 privacy: VideoPrivacy.PRIVATE,
374 targetUrl: getGoodVideoUrl() 365 targetUrl: FIXTURE_URLS.goodVideo
375 } 366 }
376 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 367 const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
377 const uuid = res.body.video.uuid
378 368
379 await waitJobs(servers) 369 await waitJobs(servers)
380 await checkMyVideoImportIsFinished(baseParams, name, uuid, getGoodVideoUrl(), true, 'presence') 370
371 const url = FIXTURE_URLS.goodVideo
372 await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: true, checkType: 'presence' })
381 }) 373 })
382 }) 374 })
383 375
@@ -394,47 +386,56 @@ describe('Test user notifications', function () {
394 token: userAccessToken 386 token: userAccessToken
395 } 387 }
396 388
397 await updateMyUser({ 389 await servers[0].users.updateMe({ displayName: 'super root name' })
398 url: servers[0].url,
399 accessToken: servers[0].accessToken,
400 displayName: 'super root name'
401 })
402 390
403 await updateMyUser({ 391 await servers[0].users.updateMe({
404 url: servers[0].url, 392 token: userAccessToken,
405 accessToken: userAccessToken,
406 displayName: myUserName 393 displayName: myUserName
407 }) 394 })
408 395
409 await updateMyUser({ 396 await servers[1].users.updateMe({ displayName: 'super root 2 name' })
410 url: servers[1].url,
411 accessToken: servers[1].accessToken,
412 displayName: 'super root 2 name'
413 })
414 397
415 await updateVideoChannel(servers[0].url, userAccessToken, 'user_1_channel', { displayName: myChannelName }) 398 await servers[0].channels.update({
399 token: userAccessToken,
400 channelName: 'user_1_channel',
401 attributes: { displayName: myChannelName }
402 })
416 }) 403 })
417 404
418 it('Should notify when a local channel is following one of our channel', async function () { 405 it('Should notify when a local channel is following one of our channel', async function () {
419 this.timeout(50000) 406 this.timeout(50000)
420 407
421 await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 408 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
422 await waitJobs(servers) 409 await waitJobs(servers)
423 410
424 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence') 411 await checkNewActorFollow({
412 ...baseParams,
413 followType: 'channel',
414 followerName: 'root',
415 followerDisplayName: 'super root name',
416 followingDisplayName: myChannelName,
417 checkType: 'presence'
418 })
425 419
426 await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) 420 await servers[0].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
427 }) 421 })
428 422
429 it('Should notify when a remote channel is following one of our channel', async function () { 423 it('Should notify when a remote channel is following one of our channel', async function () {
430 this.timeout(50000) 424 this.timeout(50000)
431 425
432 await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 426 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@localhost:' + servers[0].port })
433 await waitJobs(servers) 427 await waitJobs(servers)
434 428
435 await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence') 429 await checkNewActorFollow({
430 ...baseParams,
431 followType: 'channel',
432 followerName: 'root',
433 followerDisplayName: 'super root 2 name',
434 followingDisplayName: myChannelName,
435 checkType: 'presence'
436 })
436 437
437 await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) 438 await servers[1].subscriptions.remove({ uri: 'user_1_channel@localhost:' + servers[0].port })
438 }) 439 })
439 440
440 // PeerTube does not support accout -> account follows 441 // PeerTube does not support accout -> account follows
diff --git a/server/tests/api/redundancy/manage-redundancy.ts b/server/tests/api/redundancy/manage-redundancy.ts
index 4253124c8..5fd464ded 100644
--- a/server/tests/api/redundancy/manage-redundancy.ts
+++ b/server/tests/api/redundancy/manage-redundancy.ts
@@ -1,32 +1,30 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
7 doubleFollow, 8 doubleFollow,
8 flushAndRunMultipleServers, 9 PeerTubeServer,
9 getLocalIdByUUID, 10 RedundancyCommand,
10 ServerInfo,
11 setAccessTokensToServers, 11 setAccessTokensToServers,
12 uploadVideo, 12 waitJobs
13 uploadVideoAndGetId, 13} from '@shared/extra-utils'
14 waitUntilLog 14import { VideoPrivacy, VideoRedundanciesTarget } from '@shared/models'
15} from '../../../../shared/extra-utils'
16import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
17import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy, updateRedundancy } from '@shared/extra-utils/server/redundancy'
18import { VideoPrivacy, VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
19 15
20const expect = chai.expect 16const expect = chai.expect
21 17
22describe('Test manage videos redundancy', function () { 18describe('Test manage videos redundancy', function () {
23 const targets: VideoRedundanciesTarget[] = [ 'my-videos', 'remote-videos' ] 19 const targets: VideoRedundanciesTarget[] = [ 'my-videos', 'remote-videos' ]
24 20
25 let servers: ServerInfo[] 21 let servers: PeerTubeServer[]
26 let video1Server2UUID: string 22 let video1Server2UUID: string
27 let video2Server2UUID: string 23 let video2Server2UUID: string
28 let redundanciesToRemove: number[] = [] 24 let redundanciesToRemove: number[] = []
29 25
26 let commands: RedundancyCommand[]
27
30 before(async function () { 28 before(async function () {
31 this.timeout(120000) 29 this.timeout(120000)
32 30
@@ -50,40 +48,38 @@ describe('Test manage videos redundancy', function () {
50 } 48 }
51 } 49 }
52 } 50 }
53 servers = await flushAndRunMultipleServers(3, config) 51 servers = await createMultipleServers(3, config)
54 52
55 // Get the access tokens 53 // Get the access tokens
56 await setAccessTokensToServers(servers) 54 await setAccessTokensToServers(servers)
57 55
56 commands = servers.map(s => s.redundancy)
57
58 { 58 {
59 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) 59 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 1 server 2' } })
60 video1Server2UUID = res.body.video.uuid 60 video1Server2UUID = uuid
61 } 61 }
62 62
63 { 63 {
64 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 2 server 2' }) 64 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 2 server 2' } })
65 video2Server2UUID = res.body.video.uuid 65 video2Server2UUID = uuid
66 } 66 }
67 67
68 await waitJobs(servers) 68 await waitJobs(servers)
69 69
70 // Server 1 and server 2 follow each other 70 // Server 1 and server 2 follow each other
71 await doubleFollow(servers[0], servers[1]) 71 await doubleFollow(servers[0], servers[1])
72 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, true) 72 await commands[0].updateRedundancy({ host: servers[1].host, redundancyAllowed: true })
73 73
74 await waitJobs(servers) 74 await waitJobs(servers)
75 }) 75 })
76 76
77 it('Should not have redundancies on server 3', async function () { 77 it('Should not have redundancies on server 3', async function () {
78 for (const target of targets) { 78 for (const target of targets) {
79 const res = await listVideoRedundancies({ 79 const body = await commands[2].listVideos({ target })
80 url: servers[2].url,
81 accessToken: servers[2].accessToken,
82 target
83 })
84 80
85 expect(res.body.total).to.equal(0) 81 expect(body.total).to.equal(0)
86 expect(res.body.data).to.have.lengthOf(0) 82 expect(body.data).to.have.lengthOf(0)
87 } 83 }
88 }) 84 })
89 85
@@ -91,31 +87,22 @@ describe('Test manage videos redundancy', function () {
91 this.timeout(120000) 87 this.timeout(120000)
92 88
93 await waitJobs(servers) 89 await waitJobs(servers)
94 await waitUntilLog(servers[0], 'Duplicated ', 10) 90 await servers[0].servers.waitUntilLog('Duplicated ', 10)
95 await waitJobs(servers) 91 await waitJobs(servers)
96 92
97 const res = await listVideoRedundancies({ 93 const body = await commands[1].listVideos({ target: 'remote-videos' })
98 url: servers[1].url,
99 accessToken: servers[1].accessToken,
100 target: 'remote-videos'
101 })
102 94
103 expect(res.body.total).to.equal(0) 95 expect(body.total).to.equal(0)
104 expect(res.body.data).to.have.lengthOf(0) 96 expect(body.data).to.have.lengthOf(0)
105 }) 97 })
106 98
107 it('Should have "my-videos" redundancies on server 2', async function () { 99 it('Should have "my-videos" redundancies on server 2', async function () {
108 this.timeout(120000) 100 this.timeout(120000)
109 101
110 const res = await listVideoRedundancies({ 102 const body = await commands[1].listVideos({ target: 'my-videos' })
111 url: servers[1].url, 103 expect(body.total).to.equal(2)
112 accessToken: servers[1].accessToken,
113 target: 'my-videos'
114 })
115
116 expect(res.body.total).to.equal(2)
117 104
118 const videos = res.body.data as VideoRedundancy[] 105 const videos = body.data
119 expect(videos).to.have.lengthOf(2) 106 expect(videos).to.have.lengthOf(2)
120 107
121 const videos1 = videos.find(v => v.uuid === video1Server2UUID) 108 const videos1 = videos.find(v => v.uuid === video1Server2UUID)
@@ -139,28 +126,19 @@ describe('Test manage videos redundancy', function () {
139 }) 126 })
140 127
141 it('Should not have "my-videos" redundancies on server 1', async function () { 128 it('Should not have "my-videos" redundancies on server 1', async function () {
142 const res = await listVideoRedundancies({ 129 const body = await commands[0].listVideos({ target: 'my-videos' })
143 url: servers[0].url,
144 accessToken: servers[0].accessToken,
145 target: 'my-videos'
146 })
147 130
148 expect(res.body.total).to.equal(0) 131 expect(body.total).to.equal(0)
149 expect(res.body.data).to.have.lengthOf(0) 132 expect(body.data).to.have.lengthOf(0)
150 }) 133 })
151 134
152 it('Should have "remote-videos" redundancies on server 1', async function () { 135 it('Should have "remote-videos" redundancies on server 1', async function () {
153 this.timeout(120000) 136 this.timeout(120000)
154 137
155 const res = await listVideoRedundancies({ 138 const body = await commands[0].listVideos({ target: 'remote-videos' })
156 url: servers[0].url, 139 expect(body.total).to.equal(2)
157 accessToken: servers[0].accessToken,
158 target: 'remote-videos'
159 })
160 140
161 expect(res.body.total).to.equal(2) 141 const videos = body.data
162
163 const videos = res.body.data as VideoRedundancy[]
164 expect(videos).to.have.lengthOf(2) 142 expect(videos).to.have.lengthOf(2)
165 143
166 const videos1 = videos.find(v => v.uuid === video1Server2UUID) 144 const videos1 = videos.find(v => v.uuid === video1Server2UUID)
@@ -185,81 +163,67 @@ describe('Test manage videos redundancy', function () {
185 163
186 it('Should correctly paginate and sort results', async function () { 164 it('Should correctly paginate and sort results', async function () {
187 { 165 {
188 const res = await listVideoRedundancies({ 166 const body = await commands[0].listVideos({
189 url: servers[0].url,
190 accessToken: servers[0].accessToken,
191 target: 'remote-videos', 167 target: 'remote-videos',
192 sort: 'name', 168 sort: 'name',
193 start: 0, 169 start: 0,
194 count: 2 170 count: 2
195 }) 171 })
196 172
197 const videos = res.body.data 173 const videos = body.data
198 expect(videos[0].name).to.equal('video 1 server 2') 174 expect(videos[0].name).to.equal('video 1 server 2')
199 expect(videos[1].name).to.equal('video 2 server 2') 175 expect(videos[1].name).to.equal('video 2 server 2')
200 } 176 }
201 177
202 { 178 {
203 const res = await listVideoRedundancies({ 179 const body = await commands[0].listVideos({
204 url: servers[0].url,
205 accessToken: servers[0].accessToken,
206 target: 'remote-videos', 180 target: 'remote-videos',
207 sort: '-name', 181 sort: '-name',
208 start: 0, 182 start: 0,
209 count: 2 183 count: 2
210 }) 184 })
211 185
212 const videos = res.body.data 186 const videos = body.data
213 expect(videos[0].name).to.equal('video 2 server 2') 187 expect(videos[0].name).to.equal('video 2 server 2')
214 expect(videos[1].name).to.equal('video 1 server 2') 188 expect(videos[1].name).to.equal('video 1 server 2')
215 } 189 }
216 190
217 { 191 {
218 const res = await listVideoRedundancies({ 192 const body = await commands[0].listVideos({
219 url: servers[0].url,
220 accessToken: servers[0].accessToken,
221 target: 'remote-videos', 193 target: 'remote-videos',
222 sort: '-name', 194 sort: '-name',
223 start: 1, 195 start: 1,
224 count: 1 196 count: 1
225 }) 197 })
226 198
227 const videos = res.body.data 199 expect(body.data[0].name).to.equal('video 1 server 2')
228 expect(videos[0].name).to.equal('video 1 server 2')
229 } 200 }
230 }) 201 })
231 202
232 it('Should manually add a redundancy and list it', async function () { 203 it('Should manually add a redundancy and list it', async function () {
233 this.timeout(120000) 204 this.timeout(120000)
234 205
235 const uuid = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3 server 2', privacy: VideoPrivacy.UNLISTED })).uuid 206 const uuid = (await servers[1].videos.quickUpload({ name: 'video 3 server 2', privacy: VideoPrivacy.UNLISTED })).uuid
236 await waitJobs(servers) 207 await waitJobs(servers)
237 const videoId = await getLocalIdByUUID(servers[0].url, uuid) 208 const videoId = await servers[0].videos.getId({ uuid })
238 209
239 await addVideoRedundancy({ 210 await commands[0].addVideo({ videoId })
240 url: servers[0].url,
241 accessToken: servers[0].accessToken,
242 videoId
243 })
244 211
245 await waitJobs(servers) 212 await waitJobs(servers)
246 await waitUntilLog(servers[0], 'Duplicated ', 15) 213 await servers[0].servers.waitUntilLog('Duplicated ', 15)
247 await waitJobs(servers) 214 await waitJobs(servers)
248 215
249 { 216 {
250 const res = await listVideoRedundancies({ 217 const body = await commands[0].listVideos({
251 url: servers[0].url,
252 accessToken: servers[0].accessToken,
253 target: 'remote-videos', 218 target: 'remote-videos',
254 sort: '-name', 219 sort: '-name',
255 start: 0, 220 start: 0,
256 count: 5 221 count: 5
257 }) 222 })
258 223
259 const videos = res.body.data 224 const video = body.data[0]
260 expect(videos[0].name).to.equal('video 3 server 2')
261 225
262 const video = videos[0] 226 expect(video.name).to.equal('video 3 server 2')
263 expect(video.redundancies.files).to.have.lengthOf(4) 227 expect(video.redundancies.files).to.have.lengthOf(4)
264 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) 228 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1)
265 229
@@ -276,19 +240,15 @@ describe('Test manage videos redundancy', function () {
276 } 240 }
277 } 241 }
278 242
279 const res = await listVideoRedundancies({ 243 const body = await commands[1].listVideos({
280 url: servers[1].url,
281 accessToken: servers[1].accessToken,
282 target: 'my-videos', 244 target: 'my-videos',
283 sort: '-name', 245 sort: '-name',
284 start: 0, 246 start: 0,
285 count: 5 247 count: 5
286 }) 248 })
287 249
288 const videos = res.body.data 250 const video = body.data[0]
289 expect(videos[0].name).to.equal('video 3 server 2') 251 expect(video.name).to.equal('video 3 server 2')
290
291 const video = videos[0]
292 expect(video.redundancies.files).to.have.lengthOf(4) 252 expect(video.redundancies.files).to.have.lengthOf(4)
293 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) 253 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1)
294 254
@@ -307,64 +267,47 @@ describe('Test manage videos redundancy', function () {
307 this.timeout(120000) 267 this.timeout(120000)
308 268
309 for (const redundancyId of redundanciesToRemove) { 269 for (const redundancyId of redundanciesToRemove) {
310 await removeVideoRedundancy({ 270 await commands[0].removeVideo({ redundancyId })
311 url: servers[0].url,
312 accessToken: servers[0].accessToken,
313 redundancyId
314 })
315 } 271 }
316 272
317 { 273 {
318 const res = await listVideoRedundancies({ 274 const body = await commands[0].listVideos({
319 url: servers[0].url,
320 accessToken: servers[0].accessToken,
321 target: 'remote-videos', 275 target: 'remote-videos',
322 sort: '-name', 276 sort: '-name',
323 start: 0, 277 start: 0,
324 count: 5 278 count: 5
325 }) 279 })
326 280
327 const videos = res.body.data 281 const videos = body.data
328 expect(videos).to.have.lengthOf(2)
329 282
330 expect(videos[0].name).to.equal('video 2 server 2') 283 expect(videos).to.have.lengthOf(2)
331 284
332 redundanciesToRemove = []
333 const video = videos[0] 285 const video = videos[0]
286 expect(video.name).to.equal('video 2 server 2')
334 expect(video.redundancies.files).to.have.lengthOf(4) 287 expect(video.redundancies.files).to.have.lengthOf(4)
335 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1) 288 expect(video.redundancies.streamingPlaylists).to.have.lengthOf(1)
336 289
337 const redundancies = video.redundancies.files.concat(video.redundancies.streamingPlaylists) 290 const redundancies = video.redundancies.files.concat(video.redundancies.streamingPlaylists)
338 291
339 for (const r of redundancies) { 292 redundanciesToRemove = redundancies.map(r => r.id)
340 redundanciesToRemove.push(r.id)
341 }
342 } 293 }
343 }) 294 })
344 295
345 it('Should remove another (auto) redundancy', async function () { 296 it('Should remove another (auto) redundancy', async function () {
346 { 297 for (const redundancyId of redundanciesToRemove) {
347 for (const redundancyId of redundanciesToRemove) { 298 await commands[0].removeVideo({ redundancyId })
348 await removeVideoRedundancy({ 299 }
349 url: servers[0].url,
350 accessToken: servers[0].accessToken,
351 redundancyId
352 })
353 }
354 300
355 const res = await listVideoRedundancies({ 301 const body = await commands[0].listVideos({
356 url: servers[0].url, 302 target: 'remote-videos',
357 accessToken: servers[0].accessToken, 303 sort: '-name',
358 target: 'remote-videos', 304 start: 0,
359 sort: '-name', 305 count: 5
360 start: 0, 306 })
361 count: 5
362 })
363 307
364 const videos = res.body.data 308 const videos = body.data
365 expect(videos[0].name).to.equal('video 1 server 2') 309 expect(videos).to.have.lengthOf(1)
366 expect(videos).to.have.lengthOf(1) 310 expect(videos[0].name).to.equal('video 1 server 2')
367 }
368 }) 311 })
369 312
370 after(async function () { 313 after(async function () {
diff --git a/server/tests/api/redundancy/redundancy-constraints.ts b/server/tests/api/redundancy/redundancy-constraints.ts
index 1cb1603bc..933a2c776 100644
--- a/server/tests/api/redundancy/redundancy-constraints.ts
+++ b/server/tests/api/redundancy/redundancy-constraints.ts
@@ -1,29 +1,14 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import { expect } from 'chai'
5import { listVideoRedundancies, updateRedundancy } from '@shared/extra-utils/server/redundancy' 5import { cleanupTests, createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { VideoPrivacy } from '@shared/models' 6import { VideoPrivacy } from '@shared/models'
7import {
8 cleanupTests,
9 flushAndRunServer,
10 follow,
11 killallServers,
12 reRunServer,
13 ServerInfo,
14 setAccessTokensToServers,
15 updateVideo,
16 uploadVideo,
17 waitUntilLog
18} from '../../../../shared/extra-utils'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20
21const expect = chai.expect
22 7
23describe('Test redundancy constraints', function () { 8describe('Test redundancy constraints', function () {
24 let remoteServer: ServerInfo 9 let remoteServer: PeerTubeServer
25 let localServer: ServerInfo 10 let localServer: PeerTubeServer
26 let servers: ServerInfo[] 11 let servers: PeerTubeServer[]
27 12
28 const remoteServerConfig = { 13 const remoteServerConfig = {
29 redundancy: { 14 redundancy: {
@@ -43,38 +28,30 @@ describe('Test redundancy constraints', function () {
43 28
44 async function uploadWrapper (videoName: string) { 29 async function uploadWrapper (videoName: string) {
45 // Wait for transcoding 30 // Wait for transcoding
46 const res = await uploadVideo(localServer.url, localServer.accessToken, { name: 'to transcode', privacy: VideoPrivacy.PRIVATE }) 31 const { id } = await localServer.videos.upload({ attributes: { name: 'to transcode', privacy: VideoPrivacy.PRIVATE } })
47 await waitJobs([ localServer ]) 32 await waitJobs([ localServer ])
48 33
49 // Update video to schedule a federation 34 // Update video to schedule a federation
50 await updateVideo(localServer.url, localServer.accessToken, res.body.video.id, { name: videoName, privacy: VideoPrivacy.PUBLIC }) 35 await localServer.videos.update({ id, attributes: { name: videoName, privacy: VideoPrivacy.PUBLIC } })
51 } 36 }
52 37
53 async function getTotalRedundanciesLocalServer () { 38 async function getTotalRedundanciesLocalServer () {
54 const res = await listVideoRedundancies({ 39 const body = await localServer.redundancy.listVideos({ target: 'my-videos' })
55 url: localServer.url,
56 accessToken: localServer.accessToken,
57 target: 'my-videos'
58 })
59 40
60 return res.body.total 41 return body.total
61 } 42 }
62 43
63 async function getTotalRedundanciesRemoteServer () { 44 async function getTotalRedundanciesRemoteServer () {
64 const res = await listVideoRedundancies({ 45 const body = await remoteServer.redundancy.listVideos({ target: 'remote-videos' })
65 url: remoteServer.url,
66 accessToken: remoteServer.accessToken,
67 target: 'remote-videos'
68 })
69 46
70 return res.body.total 47 return body.total
71 } 48 }
72 49
73 before(async function () { 50 before(async function () {
74 this.timeout(120000) 51 this.timeout(120000)
75 52
76 { 53 {
77 remoteServer = await flushAndRunServer(1, remoteServerConfig) 54 remoteServer = await createSingleServer(1, remoteServerConfig)
78 } 55 }
79 56
80 { 57 {
@@ -85,7 +62,7 @@ describe('Test redundancy constraints', function () {
85 } 62 }
86 } 63 }
87 } 64 }
88 localServer = await flushAndRunServer(2, config) 65 localServer = await createSingleServer(2, config)
89 } 66 }
90 67
91 servers = [ remoteServer, localServer ] 68 servers = [ remoteServer, localServer ]
@@ -93,14 +70,14 @@ describe('Test redundancy constraints', function () {
93 // Get the access tokens 70 // Get the access tokens
94 await setAccessTokensToServers(servers) 71 await setAccessTokensToServers(servers)
95 72
96 await uploadVideo(localServer.url, localServer.accessToken, { name: 'video 1 server 2' }) 73 await localServer.videos.upload({ attributes: { name: 'video 1 server 2' } })
97 74
98 await waitJobs(servers) 75 await waitJobs(servers)
99 76
100 // Server 1 and server 2 follow each other 77 // Server 1 and server 2 follow each other
101 await follow(remoteServer.url, [ localServer.url ], remoteServer.accessToken) 78 await remoteServer.follows.follow({ hosts: [ localServer.url ] })
102 await waitJobs(servers) 79 await waitJobs(servers)
103 await updateRedundancy(remoteServer.url, remoteServer.accessToken, localServer.host, true) 80 await remoteServer.redundancy.updateRedundancy({ host: localServer.host, redundancyAllowed: true })
104 81
105 await waitJobs(servers) 82 await waitJobs(servers)
106 }) 83 })
@@ -109,7 +86,7 @@ describe('Test redundancy constraints', function () {
109 this.timeout(120000) 86 this.timeout(120000)
110 87
111 await waitJobs(servers) 88 await waitJobs(servers)
112 await waitUntilLog(remoteServer, 'Duplicated ', 5) 89 await remoteServer.servers.waitUntilLog('Duplicated ', 5)
113 await waitJobs(servers) 90 await waitJobs(servers)
114 91
115 { 92 {
@@ -134,11 +111,11 @@ describe('Test redundancy constraints', function () {
134 } 111 }
135 } 112 }
136 await killallServers([ localServer ]) 113 await killallServers([ localServer ])
137 await reRunServer(localServer, config) 114 await localServer.run(config)
138 115
139 await uploadWrapper('video 2 server 2') 116 await uploadWrapper('video 2 server 2')
140 117
141 await waitUntilLog(remoteServer, 'Duplicated ', 10) 118 await remoteServer.servers.waitUntilLog('Duplicated ', 10)
142 await waitJobs(servers) 119 await waitJobs(servers)
143 120
144 { 121 {
@@ -163,11 +140,11 @@ describe('Test redundancy constraints', function () {
163 } 140 }
164 } 141 }
165 await killallServers([ localServer ]) 142 await killallServers([ localServer ])
166 await reRunServer(localServer, config) 143 await localServer.run(config)
167 144
168 await uploadWrapper('video 3 server 2') 145 await uploadWrapper('video 3 server 2')
169 146
170 await waitUntilLog(remoteServer, 'Duplicated ', 15) 147 await remoteServer.servers.waitUntilLog('Duplicated ', 15)
171 await waitJobs(servers) 148 await waitJobs(servers)
172 149
173 { 150 {
@@ -184,11 +161,11 @@ describe('Test redundancy constraints', function () {
184 it('Should have redundancy on server 1 and on server 2 with followings filter now server 2 follows server 1', async function () { 161 it('Should have redundancy on server 1 and on server 2 with followings filter now server 2 follows server 1', async function () {
185 this.timeout(120000) 162 this.timeout(120000)
186 163
187 await follow(localServer.url, [ remoteServer.url ], localServer.accessToken) 164 await localServer.follows.follow({ hosts: [ remoteServer.url ] })
188 await waitJobs(servers) 165 await waitJobs(servers)
189 166
190 await uploadWrapper('video 4 server 2') 167 await uploadWrapper('video 4 server 2')
191 await waitUntilLog(remoteServer, 'Duplicated ', 20) 168 await remoteServer.servers.waitUntilLog('Duplicated ', 20)
192 await waitJobs(servers) 169 await waitJobs(servers)
193 170
194 { 171 {
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts
index 0e0a73b9d..e1a12f5f8 100644
--- a/server/tests/api/redundancy/redundancy.ts
+++ b/server/tests/api/redundancy/redundancy.ts
@@ -4,72 +4,63 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { readdir } from 'fs-extra' 5import { readdir } from 'fs-extra'
6import * as magnetUtil from 'magnet-uri' 6import * as magnetUtil from 'magnet-uri'
7import { join } from 'path' 7import { basename, join } from 'path'
8import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
9import { 8import {
10 checkSegmentHash, 9 checkSegmentHash,
11 checkVideoFilesWereRemoved, 10 checkVideoFilesWereRemoved,
12 cleanupTests, 11 cleanupTests,
12 createMultipleServers,
13 doubleFollow, 13 doubleFollow,
14 flushAndRunMultipleServers,
15 getFollowingListPaginationAndSort,
16 getVideo,
17 getVideoWithToken,
18 immutableAssign,
19 killallServers, 14 killallServers,
20 makeGetRequest, 15 makeRawRequest,
21 removeVideo, 16 PeerTubeServer,
22 reRunServer,
23 root, 17 root,
24 ServerInfo, 18 saveVideoInServers,
25 setAccessTokensToServers, 19 setAccessTokensToServers,
26 unfollow,
27 updateVideo,
28 uploadVideo,
29 viewVideo,
30 wait, 20 wait,
31 waitUntilLog 21 waitJobs
32} from '../../../../shared/extra-utils' 22} from '@shared/extra-utils'
33import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
34import { 23import {
35 addVideoRedundancy, 24 HttpStatusCode,
36 listVideoRedundancies, 25 VideoDetails,
37 removeVideoRedundancy, 26 VideoFile,
38 updateRedundancy 27 VideoPrivacy,
39} from '../../../../shared/extra-utils/server/redundancy' 28 VideoRedundancyStrategy,
40import { getStats } from '../../../../shared/extra-utils/server/stats' 29 VideoRedundancyStrategyWithManual
41import { ActorFollow } from '../../../../shared/models/actors' 30} from '@shared/models'
42import { VideoRedundancy, VideoRedundancyStrategy, VideoRedundancyStrategyWithManual } from '../../../../shared/models/redundancy'
43import { ServerStats } from '../../../../shared/models/server/server-stats.model'
44import { VideoDetails, VideoPrivacy } from '../../../../shared/models/videos'
45 31
46const expect = chai.expect 32const expect = chai.expect
47 33
48let servers: ServerInfo[] = [] 34let servers: PeerTubeServer[] = []
49let video1Server2UUID: string 35let video1Server2: VideoDetails
50let video1Server2Id: number
51 36
52function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[], server: ServerInfo) { 37async function checkMagnetWebseeds (file: VideoFile, baseWebseeds: string[], server: PeerTubeServer) {
53 const parsed = magnetUtil.decode(file.magnetUri) 38 const parsed = magnetUtil.decode(file.magnetUri)
54 39
55 for (const ws of baseWebseeds) { 40 for (const ws of baseWebseeds) {
56 const found = parsed.urlList.find(url => url === `${ws}-${file.resolution.id}.mp4`) 41 const found = parsed.urlList.find(url => url === `${ws}${basename(file.fileUrl)}`)
57 expect(found, `Webseed ${ws} not found in ${file.magnetUri} on server ${server.url}`).to.not.be.undefined 42 expect(found, `Webseed ${ws} not found in ${file.magnetUri} on server ${server.url}`).to.not.be.undefined
58 } 43 }
59 44
60 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length) 45 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length)
46
47 for (const url of parsed.urlList) {
48 await makeRawRequest(url, HttpStatusCode.OK_200)
49 }
61} 50}
62 51
63async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) { 52async function createSingleServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) {
64 const strategies: any[] = [] 53 const strategies: any[] = []
65 54
66 if (strategy !== null) { 55 if (strategy !== null) {
67 strategies.push( 56 strategies.push(
68 immutableAssign({ 57 {
69 min_lifetime: '1 hour', 58 min_lifetime: '1 hour',
70 strategy: strategy, 59 strategy: strategy,
71 size: '400KB' 60 size: '400KB',
72 }, additionalParams) 61
62 ...additionalParams
63 }
73 ) 64 )
74 } 65 }
75 66
@@ -90,17 +81,16 @@ async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, add
90 } 81 }
91 } 82 }
92 83
93 servers = await flushAndRunMultipleServers(3, config) 84 servers = await createMultipleServers(3, config)
94 85
95 // Get the access tokens 86 // Get the access tokens
96 await setAccessTokensToServers(servers) 87 await setAccessTokensToServers(servers)
97 88
98 { 89 {
99 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) 90 const { id } = await servers[1].videos.upload({ attributes: { name: 'video 1 server 2' } })
100 video1Server2UUID = res.body.video.uuid 91 video1Server2 = await servers[1].videos.get({ id })
101 video1Server2Id = res.body.video.id
102 92
103 await viewVideo(servers[1].url, video1Server2UUID) 93 await servers[1].videos.view({ id })
104 } 94 }
105 95
106 await waitJobs(servers) 96 await waitJobs(servers)
@@ -115,55 +105,65 @@ async function flushAndRunServers (strategy: VideoRedundancyStrategy | null, add
115 await waitJobs(servers) 105 await waitJobs(servers)
116} 106}
117 107
108async function ensureSameFilenames (videoUUID: string) {
109 let webtorrentFilenames: string[]
110 let hlsFilenames: string[]
111
112 for (const server of servers) {
113 const video = await server.videos.getWithToken({ id: videoUUID })
114
115 // Ensure we use the same filenames that the origin
116
117 const localWebtorrentFilenames = video.files.map(f => basename(f.fileUrl)).sort()
118 const localHLSFilenames = video.streamingPlaylists[0].files.map(f => basename(f.fileUrl)).sort()
119
120 if (webtorrentFilenames) expect(webtorrentFilenames).to.deep.equal(localWebtorrentFilenames)
121 else webtorrentFilenames = localWebtorrentFilenames
122
123 if (hlsFilenames) expect(hlsFilenames).to.deep.equal(localHLSFilenames)
124 else hlsFilenames = localHLSFilenames
125 }
126
127 return { webtorrentFilenames, hlsFilenames }
128}
129
118async function check1WebSeed (videoUUID?: string) { 130async function check1WebSeed (videoUUID?: string) {
119 if (!videoUUID) videoUUID = video1Server2UUID 131 if (!videoUUID) videoUUID = video1Server2.uuid
120 132
121 const webseeds = [ 133 const webseeds = [
122 `http://localhost:${servers[1].port}/static/webseed/${videoUUID}` 134 `http://localhost:${servers[1].port}/static/webseed/`
123 ] 135 ]
124 136
125 for (const server of servers) { 137 for (const server of servers) {
126 // With token to avoid issues with video follow constraints 138 // With token to avoid issues with video follow constraints
127 const res = await getVideoWithToken(server.url, server.accessToken, videoUUID) 139 const video = await server.videos.getWithToken({ id: videoUUID })
128 140
129 const video: VideoDetails = res.body
130 for (const f of video.files) { 141 for (const f of video.files) {
131 checkMagnetWebseeds(f, webseeds, server) 142 await checkMagnetWebseeds(f, webseeds, server)
132 } 143 }
133 } 144 }
145
146 await ensureSameFilenames(videoUUID)
134} 147}
135 148
136async function check2Webseeds (videoUUID?: string) { 149async function check2Webseeds (videoUUID?: string) {
137 if (!videoUUID) videoUUID = video1Server2UUID 150 if (!videoUUID) videoUUID = video1Server2.uuid
138 151
139 const webseeds = [ 152 const webseeds = [
140 `http://localhost:${servers[0].port}/static/redundancy/${videoUUID}`, 153 `http://localhost:${servers[0].port}/static/redundancy/`,
141 `http://localhost:${servers[1].port}/static/webseed/${videoUUID}` 154 `http://localhost:${servers[1].port}/static/webseed/`
142 ] 155 ]
143 156
144 for (const server of servers) { 157 for (const server of servers) {
145 const res = await getVideo(server.url, videoUUID) 158 const video = await server.videos.get({ id: videoUUID })
146
147 const video: VideoDetails = res.body
148 159
149 for (const file of video.files) { 160 for (const file of video.files) {
150 checkMagnetWebseeds(file, webseeds, server) 161 await checkMagnetWebseeds(file, webseeds, server)
151
152 await makeGetRequest({
153 url: servers[0].url,
154 statusCodeExpected: HttpStatusCode.OK_200,
155 path: '/static/redundancy/' + `${videoUUID}-${file.resolution.id}.mp4`,
156 contentType: null
157 })
158 await makeGetRequest({
159 url: servers[1].url,
160 statusCodeExpected: HttpStatusCode.OK_200,
161 path: `/static/webseed/${videoUUID}-${file.resolution.id}.mp4`,
162 contentType: null
163 })
164 } 162 }
165 } 163 }
166 164
165 const { webtorrentFilenames } = await ensureSameFilenames(videoUUID)
166
167 const directories = [ 167 const directories = [
168 'test' + servers[0].internalServerNumber + '/redundancy', 168 'test' + servers[0].internalServerNumber + '/redundancy',
169 'test' + servers[1].internalServerNumber + '/videos' 169 'test' + servers[1].internalServerNumber + '/videos'
@@ -173,32 +173,31 @@ async function check2Webseeds (videoUUID?: string) {
173 const files = await readdir(join(root(), directory)) 173 const files = await readdir(join(root(), directory))
174 expect(files).to.have.length.at.least(4) 174 expect(files).to.have.length.at.least(4)
175 175
176 for (const resolution of [ 240, 360, 480, 720 ]) { 176 // Ensure we files exist on disk
177 expect(files.find(f => f === `${videoUUID}-${resolution}.mp4`)).to.not.be.undefined 177 expect(files.find(f => webtorrentFilenames.includes(f))).to.exist
178 }
179 } 178 }
180} 179}
181 180
182async function check0PlaylistRedundancies (videoUUID?: string) { 181async function check0PlaylistRedundancies (videoUUID?: string) {
183 if (!videoUUID) videoUUID = video1Server2UUID 182 if (!videoUUID) videoUUID = video1Server2.uuid
184 183
185 for (const server of servers) { 184 for (const server of servers) {
186 // With token to avoid issues with video follow constraints 185 // With token to avoid issues with video follow constraints
187 const res = await getVideoWithToken(server.url, server.accessToken, videoUUID) 186 const video = await server.videos.getWithToken({ id: videoUUID })
188 const video: VideoDetails = res.body
189 187
190 expect(video.streamingPlaylists).to.be.an('array') 188 expect(video.streamingPlaylists).to.be.an('array')
191 expect(video.streamingPlaylists).to.have.lengthOf(1) 189 expect(video.streamingPlaylists).to.have.lengthOf(1)
192 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(0) 190 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(0)
193 } 191 }
192
193 await ensureSameFilenames(videoUUID)
194} 194}
195 195
196async function check1PlaylistRedundancies (videoUUID?: string) { 196async function check1PlaylistRedundancies (videoUUID?: string) {
197 if (!videoUUID) videoUUID = video1Server2UUID 197 if (!videoUUID) videoUUID = video1Server2.uuid
198 198
199 for (const server of servers) { 199 for (const server of servers) {
200 const res = await getVideo(server.url, videoUUID) 200 const video = await server.videos.get({ id: videoUUID })
201 const video: VideoDetails = res.body
202 201
203 expect(video.streamingPlaylists).to.have.lengthOf(1) 202 expect(video.streamingPlaylists).to.have.lengthOf(1)
204 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(1) 203 expect(video.streamingPlaylists[0].redundancies).to.have.lengthOf(1)
@@ -211,13 +210,15 @@ async function check1PlaylistRedundancies (videoUUID?: string) {
211 const baseUrlPlaylist = servers[1].url + '/static/streaming-playlists/hls' 210 const baseUrlPlaylist = servers[1].url + '/static/streaming-playlists/hls'
212 const baseUrlSegment = servers[0].url + '/static/redundancy/hls' 211 const baseUrlSegment = servers[0].url + '/static/redundancy/hls'
213 212
214 const res = await getVideo(servers[0].url, videoUUID) 213 const video = await servers[0].videos.get({ id: videoUUID })
215 const hlsPlaylist = (res.body as VideoDetails).streamingPlaylists[0] 214 const hlsPlaylist = video.streamingPlaylists[0]
216 215
217 for (const resolution of [ 240, 360, 480, 720 ]) { 216 for (const resolution of [ 240, 360, 480, 720 ]) {
218 await checkSegmentHash(baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist) 217 await checkSegmentHash({ server: servers[1], baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist })
219 } 218 }
220 219
220 const { hlsFilenames } = await ensureSameFilenames(videoUUID)
221
221 const directories = [ 222 const directories = [
222 'test' + servers[0].internalServerNumber + '/redundancy/hls', 223 'test' + servers[0].internalServerNumber + '/redundancy/hls',
223 'test' + servers[1].internalServerNumber + '/streaming-playlists/hls' 224 'test' + servers[1].internalServerNumber + '/streaming-playlists/hls'
@@ -227,11 +228,8 @@ async function check1PlaylistRedundancies (videoUUID?: string) {
227 const files = await readdir(join(root(), directory, videoUUID)) 228 const files = await readdir(join(root(), directory, videoUUID))
228 expect(files).to.have.length.at.least(4) 229 expect(files).to.have.length.at.least(4)
229 230
230 for (const resolution of [ 240, 360, 480, 720 ]) { 231 // Ensure we files exist on disk
231 const filename = `${videoUUID}-${resolution}-fragmented.mp4` 232 expect(files.find(f => hlsFilenames.includes(f))).to.exist
232
233 expect(files.find(f => f === filename)).to.not.be.undefined
234 }
235 } 233 }
236} 234}
237 235
@@ -244,9 +242,7 @@ async function checkStatsGlobal (strategy: VideoRedundancyStrategyWithManual) {
244 statsLength = 2 242 statsLength = 2
245 } 243 }
246 244
247 const res = await getStats(servers[0].url) 245 const data = await servers[0].stats.get()
248 const data: ServerStats = res.body
249
250 expect(data.videosRedundancy).to.have.lengthOf(statsLength) 246 expect(data.videosRedundancy).to.have.lengthOf(statsLength)
251 247
252 const stat = data.videosRedundancy[0] 248 const stat = data.videosRedundancy[0]
@@ -272,14 +268,20 @@ async function checkStatsWithoutRedundancy (strategy: VideoRedundancyStrategyWit
272 expect(stat.totalVideos).to.equal(0) 268 expect(stat.totalVideos).to.equal(0)
273} 269}
274 270
275async function enableRedundancyOnServer1 () { 271async function findServerFollows () {
276 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, true) 272 const body = await servers[0].follows.getFollowings({ start: 0, count: 5, sort: '-createdAt' })
277 273 const follows = body.data
278 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: '-createdAt' })
279 const follows: ActorFollow[] = res.body.data
280 const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`) 274 const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`)
281 const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`) 275 const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`)
282 276
277 return { server2, server3 }
278}
279
280async function enableRedundancyOnServer1 () {
281 await servers[0].redundancy.updateRedundancy({ host: servers[1].host, redundancyAllowed: true })
282
283 const { server2, server3 } = await findServerFollows()
284
283 expect(server3).to.not.be.undefined 285 expect(server3).to.not.be.undefined
284 expect(server3.following.hostRedundancyAllowed).to.be.false 286 expect(server3.following.hostRedundancyAllowed).to.be.false
285 287
@@ -288,12 +290,9 @@ async function enableRedundancyOnServer1 () {
288} 290}
289 291
290async function disableRedundancyOnServer1 () { 292async function disableRedundancyOnServer1 () {
291 await updateRedundancy(servers[0].url, servers[0].accessToken, servers[1].host, false) 293 await servers[0].redundancy.updateRedundancy({ host: servers[1].host, redundancyAllowed: false })
292 294
293 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: '-createdAt' }) 295 const { server2, server3 } = await findServerFollows()
294 const follows: ActorFollow[] = res.body.data
295 const server2 = follows.find(f => f.following.host === `localhost:${servers[1].port}`)
296 const server3 = follows.find(f => f.following.host === `localhost:${servers[2].port}`)
297 296
298 expect(server3).to.not.be.undefined 297 expect(server3).to.not.be.undefined
299 expect(server3.following.hostRedundancyAllowed).to.be.false 298 expect(server3.following.hostRedundancyAllowed).to.be.false
@@ -310,7 +309,7 @@ describe('Test videos redundancy', function () {
310 before(function () { 309 before(function () {
311 this.timeout(120000) 310 this.timeout(120000)
312 311
313 return flushAndRunServers(strategy) 312 return createSingleServers(strategy)
314 }) 313 })
315 314
316 it('Should have 1 webseed on the first video', async function () { 315 it('Should have 1 webseed on the first video', async function () {
@@ -327,7 +326,7 @@ describe('Test videos redundancy', function () {
327 this.timeout(80000) 326 this.timeout(80000)
328 327
329 await waitJobs(servers) 328 await waitJobs(servers)
330 await waitUntilLog(servers[0], 'Duplicated ', 5) 329 await servers[0].servers.waitUntilLog('Duplicated ', 5)
331 await waitJobs(servers) 330 await waitJobs(servers)
332 331
333 await check2Webseeds() 332 await check2Webseeds()
@@ -346,7 +345,7 @@ describe('Test videos redundancy', function () {
346 await check1WebSeed() 345 await check1WebSeed()
347 await check0PlaylistRedundancies() 346 await check0PlaylistRedundancies()
348 347
349 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ 'videos', join('playlists', 'hls') ]) 348 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
350 }) 349 })
351 350
352 after(async function () { 351 after(async function () {
@@ -360,7 +359,7 @@ describe('Test videos redundancy', function () {
360 before(function () { 359 before(function () {
361 this.timeout(120000) 360 this.timeout(120000)
362 361
363 return flushAndRunServers(strategy) 362 return createSingleServers(strategy)
364 }) 363 })
365 364
366 it('Should have 1 webseed on the first video', async function () { 365 it('Should have 1 webseed on the first video', async function () {
@@ -377,7 +376,7 @@ describe('Test videos redundancy', function () {
377 this.timeout(80000) 376 this.timeout(80000)
378 377
379 await waitJobs(servers) 378 await waitJobs(servers)
380 await waitUntilLog(servers[0], 'Duplicated ', 5) 379 await servers[0].servers.waitUntilLog('Duplicated ', 5)
381 await waitJobs(servers) 380 await waitJobs(servers)
382 381
383 await check2Webseeds() 382 await check2Webseeds()
@@ -388,7 +387,7 @@ describe('Test videos redundancy', function () {
388 it('Should unfollow on server 1 and remove duplicated videos', async function () { 387 it('Should unfollow on server 1 and remove duplicated videos', async function () {
389 this.timeout(80000) 388 this.timeout(80000)
390 389
391 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 390 await servers[0].follows.unfollow({ target: servers[1] })
392 391
393 await waitJobs(servers) 392 await waitJobs(servers)
394 await wait(5000) 393 await wait(5000)
@@ -396,7 +395,7 @@ describe('Test videos redundancy', function () {
396 await check1WebSeed() 395 await check1WebSeed()
397 await check0PlaylistRedundancies() 396 await check0PlaylistRedundancies()
398 397
399 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ 'videos' ]) 398 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
400 }) 399 })
401 400
402 after(async function () { 401 after(async function () {
@@ -410,7 +409,7 @@ describe('Test videos redundancy', function () {
410 before(function () { 409 before(function () {
411 this.timeout(120000) 410 this.timeout(120000)
412 411
413 return flushAndRunServers(strategy, { min_views: 3 }) 412 return createSingleServers(strategy, { min_views: 3 })
414 }) 413 })
415 414
416 it('Should have 1 webseed on the first video', async function () { 415 it('Should have 1 webseed on the first video', async function () {
@@ -438,8 +437,8 @@ describe('Test videos redundancy', function () {
438 it('Should view 2 times the first video to have > min_views config', async function () { 437 it('Should view 2 times the first video to have > min_views config', async function () {
439 this.timeout(80000) 438 this.timeout(80000)
440 439
441 await viewVideo(servers[0].url, video1Server2UUID) 440 await servers[0].videos.view({ id: video1Server2.uuid })
442 await viewVideo(servers[2].url, video1Server2UUID) 441 await servers[2].videos.view({ id: video1Server2.uuid })
443 442
444 await wait(10000) 443 await wait(10000)
445 await waitJobs(servers) 444 await waitJobs(servers)
@@ -449,7 +448,7 @@ describe('Test videos redundancy', function () {
449 this.timeout(80000) 448 this.timeout(80000)
450 449
451 await waitJobs(servers) 450 await waitJobs(servers)
452 await waitUntilLog(servers[0], 'Duplicated ', 5) 451 await servers[0].servers.waitUntilLog('Duplicated ', 5)
453 await waitJobs(servers) 452 await waitJobs(servers)
454 453
455 await check2Webseeds() 454 await check2Webseeds()
@@ -460,12 +459,13 @@ describe('Test videos redundancy', function () {
460 it('Should remove the video and the redundancy files', async function () { 459 it('Should remove the video and the redundancy files', async function () {
461 this.timeout(20000) 460 this.timeout(20000)
462 461
463 await removeVideo(servers[1].url, servers[1].accessToken, video1Server2UUID) 462 await saveVideoInServers(servers, video1Server2.uuid)
463 await servers[1].videos.remove({ id: video1Server2.uuid })
464 464
465 await waitJobs(servers) 465 await waitJobs(servers)
466 466
467 for (const server of servers) { 467 for (const server of servers) {
468 await checkVideoFilesWereRemoved(video1Server2UUID, server.internalServerNumber) 468 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
469 } 469 }
470 }) 470 })
471 471
@@ -480,7 +480,7 @@ describe('Test videos redundancy', function () {
480 before(async function () { 480 before(async function () {
481 this.timeout(120000) 481 this.timeout(120000)
482 482
483 await flushAndRunServers(strategy, { min_views: 3 }, false) 483 await createSingleServers(strategy, { min_views: 3 }, false)
484 }) 484 })
485 485
486 it('Should have 0 playlist redundancy on the first video', async function () { 486 it('Should have 0 playlist redundancy on the first video', async function () {
@@ -506,14 +506,14 @@ describe('Test videos redundancy', function () {
506 it('Should have 1 redundancy on the first video', async function () { 506 it('Should have 1 redundancy on the first video', async function () {
507 this.timeout(160000) 507 this.timeout(160000)
508 508
509 await viewVideo(servers[0].url, video1Server2UUID) 509 await servers[0].videos.view({ id: video1Server2.uuid })
510 await viewVideo(servers[2].url, video1Server2UUID) 510 await servers[2].videos.view({ id: video1Server2.uuid })
511 511
512 await wait(10000) 512 await wait(10000)
513 await waitJobs(servers) 513 await waitJobs(servers)
514 514
515 await waitJobs(servers) 515 await waitJobs(servers)
516 await waitUntilLog(servers[0], 'Duplicated ', 1) 516 await servers[0].servers.waitUntilLog('Duplicated ', 1)
517 await waitJobs(servers) 517 await waitJobs(servers)
518 518
519 await check1PlaylistRedundancies() 519 await check1PlaylistRedundancies()
@@ -523,12 +523,13 @@ describe('Test videos redundancy', function () {
523 it('Should remove the video and the redundancy files', async function () { 523 it('Should remove the video and the redundancy files', async function () {
524 this.timeout(20000) 524 this.timeout(20000)
525 525
526 await removeVideo(servers[1].url, servers[1].accessToken, video1Server2UUID) 526 await saveVideoInServers(servers, video1Server2.uuid)
527 await servers[1].videos.remove({ id: video1Server2.uuid })
527 528
528 await waitJobs(servers) 529 await waitJobs(servers)
529 530
530 for (const server of servers) { 531 for (const server of servers) {
531 await checkVideoFilesWereRemoved(video1Server2UUID, server.internalServerNumber) 532 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
532 } 533 }
533 }) 534 })
534 535
@@ -541,7 +542,7 @@ describe('Test videos redundancy', function () {
541 before(function () { 542 before(function () {
542 this.timeout(120000) 543 this.timeout(120000)
543 544
544 return flushAndRunServers(null) 545 return createSingleServers(null)
545 }) 546 })
546 547
547 it('Should have 1 webseed on the first video', async function () { 548 it('Should have 1 webseed on the first video', async function () {
@@ -551,18 +552,14 @@ describe('Test videos redundancy', function () {
551 }) 552 })
552 553
553 it('Should create a redundancy on first video', async function () { 554 it('Should create a redundancy on first video', async function () {
554 await addVideoRedundancy({ 555 await servers[0].redundancy.addVideo({ videoId: video1Server2.id })
555 url: servers[0].url,
556 accessToken: servers[0].accessToken,
557 videoId: video1Server2Id
558 })
559 }) 556 })
560 557
561 it('Should have 2 webseeds on the first video', async function () { 558 it('Should have 2 webseeds on the first video', async function () {
562 this.timeout(80000) 559 this.timeout(80000)
563 560
564 await waitJobs(servers) 561 await waitJobs(servers)
565 await waitUntilLog(servers[0], 'Duplicated ', 5) 562 await servers[0].servers.waitUntilLog('Duplicated ', 5)
566 await waitJobs(servers) 563 await waitJobs(servers)
567 564
568 await check2Webseeds() 565 await check2Webseeds()
@@ -573,22 +570,15 @@ describe('Test videos redundancy', function () {
573 it('Should manually remove redundancies on server 1 and remove duplicated videos', async function () { 570 it('Should manually remove redundancies on server 1 and remove duplicated videos', async function () {
574 this.timeout(80000) 571 this.timeout(80000)
575 572
576 const res = await listVideoRedundancies({ 573 const body = await servers[0].redundancy.listVideos({ target: 'remote-videos' })
577 url: servers[0].url,
578 accessToken: servers[0].accessToken,
579 target: 'remote-videos'
580 })
581 574
582 const videos = res.body.data as VideoRedundancy[] 575 const videos = body.data
583 expect(videos).to.have.lengthOf(1) 576 expect(videos).to.have.lengthOf(1)
584 577
585 const video = videos[0] 578 const video = videos[0]
579
586 for (const r of video.redundancies.files.concat(video.redundancies.streamingPlaylists)) { 580 for (const r of video.redundancies.files.concat(video.redundancies.streamingPlaylists)) {
587 await removeVideoRedundancy({ 581 await servers[0].redundancy.removeVideo({ redundancyId: r.id })
588 url: servers[0].url,
589 accessToken: servers[0].accessToken,
590 redundancyId: r.id
591 })
592 } 582 }
593 583
594 await waitJobs(servers) 584 await waitJobs(servers)
@@ -597,7 +587,7 @@ describe('Test videos redundancy', function () {
597 await check1WebSeed() 587 await check1WebSeed()
598 await check0PlaylistRedundancies() 588 await check0PlaylistRedundancies()
599 589
600 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].serverNumber, [ 'videos' ]) 590 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
601 }) 591 })
602 592
603 after(async function () { 593 after(async function () {
@@ -608,10 +598,9 @@ describe('Test videos redundancy', function () {
608 describe('Test expiration', function () { 598 describe('Test expiration', function () {
609 const strategy = 'recently-added' 599 const strategy = 'recently-added'
610 600
611 async function checkContains (servers: ServerInfo[], str: string) { 601 async function checkContains (servers: PeerTubeServer[], str: string) {
612 for (const server of servers) { 602 for (const server of servers) {
613 const res = await getVideo(server.url, video1Server2UUID) 603 const video = await server.videos.get({ id: video1Server2.uuid })
614 const video: VideoDetails = res.body
615 604
616 for (const f of video.files) { 605 for (const f of video.files) {
617 expect(f.magnetUri).to.contain(str) 606 expect(f.magnetUri).to.contain(str)
@@ -619,10 +608,9 @@ describe('Test videos redundancy', function () {
619 } 608 }
620 } 609 }
621 610
622 async function checkNotContains (servers: ServerInfo[], str: string) { 611 async function checkNotContains (servers: PeerTubeServer[], str: string) {
623 for (const server of servers) { 612 for (const server of servers) {
624 const res = await getVideo(server.url, video1Server2UUID) 613 const video = await server.videos.get({ id: video1Server2.uuid })
625 const video: VideoDetails = res.body
626 614
627 for (const f of video.files) { 615 for (const f of video.files) {
628 expect(f.magnetUri).to.not.contain(str) 616 expect(f.magnetUri).to.not.contain(str)
@@ -633,7 +621,7 @@ describe('Test videos redundancy', function () {
633 before(async function () { 621 before(async function () {
634 this.timeout(120000) 622 this.timeout(120000)
635 623
636 await flushAndRunServers(strategy, { min_lifetime: '7 seconds', min_views: 0 }) 624 await createSingleServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
637 625
638 await enableRedundancyOnServer1() 626 await enableRedundancyOnServer1()
639 }) 627 })
@@ -656,7 +644,7 @@ describe('Test videos redundancy', function () {
656 it('Should stop server 1 and expire video redundancy', async function () { 644 it('Should stop server 1 and expire video redundancy', async function () {
657 this.timeout(80000) 645 this.timeout(80000)
658 646
659 killallServers([ servers[0] ]) 647 await killallServers([ servers[0] ])
660 648
661 await wait(15000) 649 await wait(15000)
662 650
@@ -675,25 +663,25 @@ describe('Test videos redundancy', function () {
675 before(async function () { 663 before(async function () {
676 this.timeout(120000) 664 this.timeout(120000)
677 665
678 await flushAndRunServers(strategy, { min_lifetime: '7 seconds', min_views: 0 }) 666 await createSingleServers(strategy, { min_lifetime: '7 seconds', min_views: 0 })
679 667
680 await enableRedundancyOnServer1() 668 await enableRedundancyOnServer1()
681 669
682 await waitJobs(servers) 670 await waitJobs(servers)
683 await waitUntilLog(servers[0], 'Duplicated ', 5) 671 await servers[0].servers.waitUntilLog('Duplicated ', 5)
684 await waitJobs(servers) 672 await waitJobs(servers)
685 673
686 await check2Webseeds(video1Server2UUID) 674 await check2Webseeds()
687 await check1PlaylistRedundancies(video1Server2UUID) 675 await check1PlaylistRedundancies()
688 await checkStatsWith1Redundancy(strategy) 676 await checkStatsWith1Redundancy(strategy)
689 677
690 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 2 server 2', privacy: VideoPrivacy.PRIVATE }) 678 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 2 server 2', privacy: VideoPrivacy.PRIVATE } })
691 video2Server2UUID = res.body.video.uuid 679 video2Server2UUID = uuid
692 680
693 // Wait transcoding before federation 681 // Wait transcoding before federation
694 await waitJobs(servers) 682 await waitJobs(servers)
695 683
696 await updateVideo(servers[1].url, servers[1].accessToken, video2Server2UUID, { privacy: VideoPrivacy.PUBLIC }) 684 await servers[1].videos.update({ id: video2Server2UUID, attributes: { privacy: VideoPrivacy.PUBLIC } })
697 }) 685 })
698 686
699 it('Should cache video 2 webseeds on the first video', async function () { 687 it('Should cache video 2 webseeds on the first video', async function () {
@@ -707,8 +695,8 @@ describe('Test videos redundancy', function () {
707 await wait(1000) 695 await wait(1000)
708 696
709 try { 697 try {
710 await check1WebSeed(video1Server2UUID) 698 await check1WebSeed()
711 await check0PlaylistRedundancies(video1Server2UUID) 699 await check0PlaylistRedundancies()
712 700
713 await check2Webseeds(video2Server2UUID) 701 await check2Webseeds(video2Server2UUID)
714 await check1PlaylistRedundancies(video2Server2UUID) 702 await check1PlaylistRedundancies(video2Server2UUID)
@@ -725,8 +713,8 @@ describe('Test videos redundancy', function () {
725 713
726 await waitJobs(servers) 714 await waitJobs(servers)
727 715
728 killallServers([ servers[0] ]) 716 await killallServers([ servers[0] ])
729 await reRunServer(servers[0], { 717 await servers[0].run({
730 redundancy: { 718 redundancy: {
731 videos: { 719 videos: {
732 check_interval: '1 second', 720 check_interval: '1 second',
@@ -737,7 +725,7 @@ describe('Test videos redundancy', function () {
737 725
738 await waitJobs(servers) 726 await waitJobs(servers)
739 727
740 await checkVideoFilesWereRemoved(video1Server2UUID, servers[0].internalServerNumber, [ join('redundancy', 'hls') ]) 728 await checkVideoFilesWereRemoved({ server: servers[0], video: video1Server2, onlyVideoFiles: true })
741 }) 729 })
742 730
743 after(async function () { 731 after(async function () {
diff --git a/server/tests/api/search/search-activitypub-video-channels.ts b/server/tests/api/search/search-activitypub-video-channels.ts
index e83eb7171..426cbc8e1 100644
--- a/server/tests/api/search/search-activitypub-video-channels.ts
+++ b/server/tests/api/search/search-activitypub-video-channels.ts
@@ -1,69 +1,63 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 addVideoChannel,
7 cleanupTests, 6 cleanupTests,
8 createUser, 7 createMultipleServers,
9 deleteVideoChannel, 8 PeerTubeServer,
10 flushAndRunMultipleServers, 9 SearchCommand,
11 getVideoChannelsList,
12 getVideoChannelVideos,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 updateMyUser, 11 wait,
16 updateVideo, 12 waitJobs
17 updateVideoChannel, 13} from '@shared/extra-utils'
18 uploadVideo, 14import { VideoChannel } from '@shared/models'
19 userLogin,
20 wait
21} from '../../../../shared/extra-utils'
22import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23import { VideoChannel } from '../../../../shared/models/videos'
24import { searchVideoChannel } from '../../../../shared/extra-utils/search/video-channels'
25 15
26const expect = chai.expect 16const expect = chai.expect
27 17
28describe('Test ActivityPub video channels search', function () { 18describe('Test ActivityPub video channels search', function () {
29 let servers: ServerInfo[] 19 let servers: PeerTubeServer[]
30 let userServer2Token: string 20 let userServer2Token: string
31 let videoServer2UUID: string 21 let videoServer2UUID: string
32 let channelIdServer2: number 22 let channelIdServer2: number
23 let command: SearchCommand
33 24
34 before(async function () { 25 before(async function () {
35 this.timeout(120000) 26 this.timeout(120000)
36 27
37 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
38 29
39 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
40 31
41 { 32 {
42 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: 'user1_server1', password: 'password' }) 33 await servers[0].users.create({ username: 'user1_server1', password: 'password' })
43 const channel = { 34 const channel = {
44 name: 'channel1_server1', 35 name: 'channel1_server1',
45 displayName: 'Channel 1 server 1' 36 displayName: 'Channel 1 server 1'
46 } 37 }
47 await addVideoChannel(servers[0].url, servers[0].accessToken, channel) 38 await servers[0].channels.create({ attributes: channel })
48 } 39 }
49 40
50 { 41 {
51 const user = { username: 'user1_server2', password: 'password' } 42 const user = { username: 'user1_server2', password: 'password' }
52 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 43 await servers[1].users.create({ username: user.username, password: user.password })
53 userServer2Token = await userLogin(servers[1], user) 44 userServer2Token = await servers[1].login.getAccessToken(user)
54 45
55 const channel = { 46 const channel = {
56 name: 'channel1_server2', 47 name: 'channel1_server2',
57 displayName: 'Channel 1 server 2' 48 displayName: 'Channel 1 server 2'
58 } 49 }
59 const resChannel = await addVideoChannel(servers[1].url, userServer2Token, channel) 50 const created = await servers[1].channels.create({ token: userServer2Token, attributes: channel })
60 channelIdServer2 = resChannel.body.videoChannel.id 51 channelIdServer2 = created.id
61 52
62 const res = await uploadVideo(servers[1].url, userServer2Token, { name: 'video 1 server 2', channelId: channelIdServer2 }) 53 const attributes = { name: 'video 1 server 2', channelId: channelIdServer2 }
63 videoServer2UUID = res.body.video.uuid 54 const { uuid } = await servers[1].videos.upload({ token: userServer2Token, attributes })
55 videoServer2UUID = uuid
64 } 56 }
65 57
66 await waitJobs(servers) 58 await waitJobs(servers)
59
60 command = servers[0].search
67 }) 61 })
68 62
69 it('Should not find a remote video channel', async function () { 63 it('Should not find a remote video channel', async function () {
@@ -71,21 +65,21 @@ describe('Test ActivityPub video channels search', function () {
71 65
72 { 66 {
73 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server3' 67 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server3'
74 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 68 const body = await command.searchChannels({ search, token: servers[0].accessToken })
75 69
76 expect(res.body.total).to.equal(0) 70 expect(body.total).to.equal(0)
77 expect(res.body.data).to.be.an('array') 71 expect(body.data).to.be.an('array')
78 expect(res.body.data).to.have.lengthOf(0) 72 expect(body.data).to.have.lengthOf(0)
79 } 73 }
80 74
81 { 75 {
82 // Without token 76 // Without token
83 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 77 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
84 const res = await searchVideoChannel(servers[0].url, search) 78 const body = await command.searchChannels({ search })
85 79
86 expect(res.body.total).to.equal(0) 80 expect(body.total).to.equal(0)
87 expect(res.body.data).to.be.an('array') 81 expect(body.data).to.be.an('array')
88 expect(res.body.data).to.have.lengthOf(0) 82 expect(body.data).to.have.lengthOf(0)
89 } 83 }
90 }) 84 })
91 85
@@ -96,13 +90,13 @@ describe('Test ActivityPub video channels search', function () {
96 ] 90 ]
97 91
98 for (const search of searches) { 92 for (const search of searches) {
99 const res = await searchVideoChannel(servers[0].url, search) 93 const body = await command.searchChannels({ search })
100 94
101 expect(res.body.total).to.equal(1) 95 expect(body.total).to.equal(1)
102 expect(res.body.data).to.be.an('array') 96 expect(body.data).to.be.an('array')
103 expect(res.body.data).to.have.lengthOf(1) 97 expect(body.data).to.have.lengthOf(1)
104 expect(res.body.data[0].name).to.equal('channel1_server1') 98 expect(body.data[0].name).to.equal('channel1_server1')
105 expect(res.body.data[0].displayName).to.equal('Channel 1 server 1') 99 expect(body.data[0].displayName).to.equal('Channel 1 server 1')
106 } 100 }
107 }) 101 })
108 102
@@ -110,13 +104,13 @@ describe('Test ActivityPub video channels search', function () {
110 const search = 'http://localhost:' + servers[0].port + '/c/channel1_server1' 104 const search = 'http://localhost:' + servers[0].port + '/c/channel1_server1'
111 105
112 for (const token of [ undefined, servers[0].accessToken ]) { 106 for (const token of [ undefined, servers[0].accessToken ]) {
113 const res = await searchVideoChannel(servers[0].url, search, token) 107 const body = await command.searchChannels({ search, token })
114 108
115 expect(res.body.total).to.equal(1) 109 expect(body.total).to.equal(1)
116 expect(res.body.data).to.be.an('array') 110 expect(body.data).to.be.an('array')
117 expect(res.body.data).to.have.lengthOf(1) 111 expect(body.data).to.have.lengthOf(1)
118 expect(res.body.data[0].name).to.equal('channel1_server1') 112 expect(body.data[0].name).to.equal('channel1_server1')
119 expect(res.body.data[0].displayName).to.equal('Channel 1 server 1') 113 expect(body.data[0].displayName).to.equal('Channel 1 server 1')
120 } 114 }
121 }) 115 })
122 116
@@ -129,23 +123,23 @@ describe('Test ActivityPub video channels search', function () {
129 ] 123 ]
130 124
131 for (const search of searches) { 125 for (const search of searches) {
132 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 126 const body = await command.searchChannels({ search, token: servers[0].accessToken })
133 127
134 expect(res.body.total).to.equal(1) 128 expect(body.total).to.equal(1)
135 expect(res.body.data).to.be.an('array') 129 expect(body.data).to.be.an('array')
136 expect(res.body.data).to.have.lengthOf(1) 130 expect(body.data).to.have.lengthOf(1)
137 expect(res.body.data[0].name).to.equal('channel1_server2') 131 expect(body.data[0].name).to.equal('channel1_server2')
138 expect(res.body.data[0].displayName).to.equal('Channel 1 server 2') 132 expect(body.data[0].displayName).to.equal('Channel 1 server 2')
139 } 133 }
140 }) 134 })
141 135
142 it('Should not list this remote video channel', async function () { 136 it('Should not list this remote video channel', async function () {
143 const res = await getVideoChannelsList(servers[0].url, 0, 5) 137 const body = await servers[0].channels.list()
144 expect(res.body.total).to.equal(3) 138 expect(body.total).to.equal(3)
145 expect(res.body.data).to.have.lengthOf(3) 139 expect(body.data).to.have.lengthOf(3)
146 expect(res.body.data[0].name).to.equal('channel1_server1') 140 expect(body.data[0].name).to.equal('channel1_server1')
147 expect(res.body.data[1].name).to.equal('user1_server1_channel') 141 expect(body.data[1].name).to.equal('user1_server1_channel')
148 expect(res.body.data[2].name).to.equal('root_channel') 142 expect(body.data[2].name).to.equal('root_channel')
149 }) 143 })
150 144
151 it('Should list video channel videos of server 2 without token', async function () { 145 it('Should list video channel videos of server 2 without token', async function () {
@@ -153,34 +147,43 @@ describe('Test ActivityPub video channels search', function () {
153 147
154 await waitJobs(servers) 148 await waitJobs(servers)
155 149
156 const res = await getVideoChannelVideos(servers[0].url, null, 'channel1_server2@localhost:' + servers[1].port, 0, 5) 150 const { total, data } = await servers[0].videos.listByChannel({
157 expect(res.body.total).to.equal(0) 151 token: null,
158 expect(res.body.data).to.have.lengthOf(0) 152 handle: 'channel1_server2@localhost:' + servers[1].port
153 })
154 expect(total).to.equal(0)
155 expect(data).to.have.lengthOf(0)
159 }) 156 })
160 157
161 it('Should list video channel videos of server 2 with token', async function () { 158 it('Should list video channel videos of server 2 with token', async function () {
162 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:' + servers[1].port, 0, 5) 159 const { total, data } = await servers[0].videos.listByChannel({
160 handle: 'channel1_server2@localhost:' + servers[1].port
161 })
163 162
164 expect(res.body.total).to.equal(1) 163 expect(total).to.equal(1)
165 expect(res.body.data[0].name).to.equal('video 1 server 2') 164 expect(data[0].name).to.equal('video 1 server 2')
166 }) 165 })
167 166
168 it('Should update video channel of server 2, and refresh it on server 1', async function () { 167 it('Should update video channel of server 2, and refresh it on server 1', async function () {
169 this.timeout(60000) 168 this.timeout(60000)
170 169
171 await updateVideoChannel(servers[1].url, userServer2Token, 'channel1_server2', { displayName: 'channel updated' }) 170 await servers[1].channels.update({
172 await updateMyUser({ url: servers[1].url, accessToken: userServer2Token, displayName: 'user updated' }) 171 token: userServer2Token,
172 channelName: 'channel1_server2',
173 attributes: { displayName: 'channel updated' }
174 })
175 await servers[1].users.updateMe({ token: userServer2Token, displayName: 'user updated' })
173 176
174 await waitJobs(servers) 177 await waitJobs(servers)
175 // Expire video channel 178 // Expire video channel
176 await wait(10000) 179 await wait(10000)
177 180
178 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 181 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
179 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 182 const body = await command.searchChannels({ search, token: servers[0].accessToken })
180 expect(res.body.total).to.equal(1) 183 expect(body.total).to.equal(1)
181 expect(res.body.data).to.have.lengthOf(1) 184 expect(body.data).to.have.lengthOf(1)
182 185
183 const videoChannel: VideoChannel = res.body.data[0] 186 const videoChannel: VideoChannel = body.data[0]
184 expect(videoChannel.displayName).to.equal('channel updated') 187 expect(videoChannel.displayName).to.equal('channel updated')
185 188
186 // We don't return the owner account for now 189 // We don't return the owner account for now
@@ -190,8 +193,8 @@ describe('Test ActivityPub video channels search', function () {
190 it('Should update and add a video on server 2, and update it on server 1 after a search', async function () { 193 it('Should update and add a video on server 2, and update it on server 1 after a search', async function () {
191 this.timeout(60000) 194 this.timeout(60000)
192 195
193 await updateVideo(servers[1].url, userServer2Token, videoServer2UUID, { name: 'video 1 updated' }) 196 await servers[1].videos.update({ token: userServer2Token, id: videoServer2UUID, attributes: { name: 'video 1 updated' } })
194 await uploadVideo(servers[1].url, userServer2Token, { name: 'video 2 server 2', channelId: channelIdServer2 }) 197 await servers[1].videos.upload({ token: userServer2Token, attributes: { name: 'video 2 server 2', channelId: channelIdServer2 } })
195 198
196 await waitJobs(servers) 199 await waitJobs(servers)
197 200
@@ -199,31 +202,31 @@ describe('Test ActivityPub video channels search', function () {
199 await wait(10000) 202 await wait(10000)
200 203
201 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 204 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
202 await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 205 await command.searchChannels({ search, token: servers[0].accessToken })
203 206
204 await waitJobs(servers) 207 await waitJobs(servers)
205 208
206 const videoChannelName = 'channel1_server2@localhost:' + servers[1].port 209 const handle = 'channel1_server2@localhost:' + servers[1].port
207 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, videoChannelName, 0, 5, '-createdAt') 210 const { total, data } = await servers[0].videos.listByChannel({ handle, sort: '-createdAt' })
208 211
209 expect(res.body.total).to.equal(2) 212 expect(total).to.equal(2)
210 expect(res.body.data[0].name).to.equal('video 2 server 2') 213 expect(data[0].name).to.equal('video 2 server 2')
211 expect(res.body.data[1].name).to.equal('video 1 updated') 214 expect(data[1].name).to.equal('video 1 updated')
212 }) 215 })
213 216
214 it('Should delete video channel of server 2, and delete it on server 1', async function () { 217 it('Should delete video channel of server 2, and delete it on server 1', async function () {
215 this.timeout(60000) 218 this.timeout(60000)
216 219
217 await deleteVideoChannel(servers[1].url, userServer2Token, 'channel1_server2') 220 await servers[1].channels.delete({ token: userServer2Token, channelName: 'channel1_server2' })
218 221
219 await waitJobs(servers) 222 await waitJobs(servers)
220 // Expire video 223 // Expire video
221 await wait(10000) 224 await wait(10000)
222 225
223 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2' 226 const search = 'http://localhost:' + servers[1].port + '/video-channels/channel1_server2'
224 const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) 227 const body = await command.searchChannels({ search, token: servers[0].accessToken })
225 expect(res.body.total).to.equal(0) 228 expect(body.total).to.equal(0)
226 expect(res.body.data).to.have.lengthOf(0) 229 expect(body.data).to.have.lengthOf(0)
227 }) 230 })
228 231
229 after(async function () { 232 after(async function () {
diff --git a/server/tests/api/search/search-activitypub-video-playlists.ts b/server/tests/api/search/search-activitypub-video-playlists.ts
index 4c08e9548..33ca7be12 100644
--- a/server/tests/api/search/search-activitypub-video-playlists.ts
+++ b/server/tests/api/search/search-activitypub-video-playlists.ts
@@ -3,113 +3,102 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addVideoInPlaylist,
7 cleanupTests, 6 cleanupTests,
8 createVideoPlaylist, 7 createMultipleServers,
9 deleteVideoPlaylist, 8 PeerTubeServer,
10 flushAndRunMultipleServers, 9 SearchCommand,
11 getVideoPlaylistsList,
12 searchVideoPlaylists,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 setDefaultVideoChannel, 11 setDefaultVideoChannel,
16 uploadVideoAndGetId, 12 wait,
17 wait 13 waitJobs
18} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 15import { VideoPlaylistPrivacy } from '@shared/models'
20import { VideoPlaylist, VideoPlaylistPrivacy } from '../../../../shared/models/videos'
21 16
22const expect = chai.expect 17const expect = chai.expect
23 18
24describe('Test ActivityPub playlists search', function () { 19describe('Test ActivityPub playlists search', function () {
25 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
26 let playlistServer1UUID: string 21 let playlistServer1UUID: string
27 let playlistServer2UUID: string 22 let playlistServer2UUID: string
28 let video2Server2: string 23 let video2Server2: string
29 24
25 let command: SearchCommand
26
30 before(async function () { 27 before(async function () {
31 this.timeout(120000) 28 this.timeout(120000)
32 29
33 servers = await flushAndRunMultipleServers(2) 30 servers = await createMultipleServers(2)
34 31
35 await setAccessTokensToServers(servers) 32 await setAccessTokensToServers(servers)
36 await setDefaultVideoChannel(servers) 33 await setDefaultVideoChannel(servers)
37 34
38 { 35 {
39 const video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).uuid 36 const video1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).uuid
40 const video2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).uuid 37 const video2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).uuid
41 38
42 const attributes = { 39 const attributes = {
43 displayName: 'playlist 1 on server 1', 40 displayName: 'playlist 1 on server 1',
44 privacy: VideoPlaylistPrivacy.PUBLIC, 41 privacy: VideoPlaylistPrivacy.PUBLIC,
45 videoChannelId: servers[0].videoChannel.id 42 videoChannelId: servers[0].store.channel.id
46 } 43 }
47 const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs: attributes }) 44 const created = await servers[0].playlists.create({ attributes })
48 playlistServer1UUID = res.body.videoPlaylist.uuid 45 playlistServer1UUID = created.uuid
49 46
50 for (const videoId of [ video1, video2 ]) { 47 for (const videoId of [ video1, video2 ]) {
51 await addVideoInPlaylist({ 48 await servers[0].playlists.addElement({ playlistId: playlistServer1UUID, attributes: { videoId } })
52 url: servers[0].url,
53 token: servers[0].accessToken,
54 playlistId: playlistServer1UUID,
55 elementAttrs: { videoId }
56 })
57 } 49 }
58 } 50 }
59 51
60 { 52 {
61 const videoId = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 1' })).uuid 53 const videoId = (await servers[1].videos.quickUpload({ name: 'video 1' })).uuid
62 video2Server2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 2' })).uuid 54 video2Server2 = (await servers[1].videos.quickUpload({ name: 'video 2' })).uuid
63 55
64 const attributes = { 56 const attributes = {
65 displayName: 'playlist 1 on server 2', 57 displayName: 'playlist 1 on server 2',
66 privacy: VideoPlaylistPrivacy.PUBLIC, 58 privacy: VideoPlaylistPrivacy.PUBLIC,
67 videoChannelId: servers[1].videoChannel.id 59 videoChannelId: servers[1].store.channel.id
68 } 60 }
69 const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs: attributes }) 61 const created = await servers[1].playlists.create({ attributes })
70 playlistServer2UUID = res.body.videoPlaylist.uuid 62 playlistServer2UUID = created.uuid
71 63
72 await addVideoInPlaylist({ 64 await servers[1].playlists.addElement({ playlistId: playlistServer2UUID, attributes: { videoId } })
73 url: servers[1].url,
74 token: servers[1].accessToken,
75 playlistId: playlistServer2UUID,
76 elementAttrs: { videoId }
77 })
78 } 65 }
79 66
80 await waitJobs(servers) 67 await waitJobs(servers)
68
69 command = servers[0].search
81 }) 70 })
82 71
83 it('Should not find a remote playlist', async function () { 72 it('Should not find a remote playlist', async function () {
84 { 73 {
85 const search = 'http://localhost:' + servers[1].port + '/video-playlists/43' 74 const search = 'http://localhost:' + servers[1].port + '/video-playlists/43'
86 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 75 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
87 76
88 expect(res.body.total).to.equal(0) 77 expect(body.total).to.equal(0)
89 expect(res.body.data).to.be.an('array') 78 expect(body.data).to.be.an('array')
90 expect(res.body.data).to.have.lengthOf(0) 79 expect(body.data).to.have.lengthOf(0)
91 } 80 }
92 81
93 { 82 {
94 // Without token 83 // Without token
95 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID 84 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID
96 const res = await searchVideoPlaylists(servers[0].url, search) 85 const body = await command.searchPlaylists({ search })
97 86
98 expect(res.body.total).to.equal(0) 87 expect(body.total).to.equal(0)
99 expect(res.body.data).to.be.an('array') 88 expect(body.data).to.be.an('array')
100 expect(res.body.data).to.have.lengthOf(0) 89 expect(body.data).to.have.lengthOf(0)
101 } 90 }
102 }) 91 })
103 92
104 it('Should search a local playlist', async function () { 93 it('Should search a local playlist', async function () {
105 const search = 'http://localhost:' + servers[0].port + '/video-playlists/' + playlistServer1UUID 94 const search = 'http://localhost:' + servers[0].port + '/video-playlists/' + playlistServer1UUID
106 const res = await searchVideoPlaylists(servers[0].url, search) 95 const body = await command.searchPlaylists({ search })
107 96
108 expect(res.body.total).to.equal(1) 97 expect(body.total).to.equal(1)
109 expect(res.body.data).to.be.an('array') 98 expect(body.data).to.be.an('array')
110 expect(res.body.data).to.have.lengthOf(1) 99 expect(body.data).to.have.lengthOf(1)
111 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 1') 100 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
112 expect(res.body.data[0].videosLength).to.equal(2) 101 expect(body.data[0].videosLength).to.equal(2)
113 }) 102 })
114 103
115 it('Should search a local playlist with an alternative URL', async function () { 104 it('Should search a local playlist with an alternative URL', async function () {
@@ -120,13 +109,13 @@ describe('Test ActivityPub playlists search', function () {
120 109
121 for (const search of searches) { 110 for (const search of searches) {
122 for (const token of [ undefined, servers[0].accessToken ]) { 111 for (const token of [ undefined, servers[0].accessToken ]) {
123 const res = await searchVideoPlaylists(servers[0].url, search, token) 112 const body = await command.searchPlaylists({ search, token })
124 113
125 expect(res.body.total).to.equal(1) 114 expect(body.total).to.equal(1)
126 expect(res.body.data).to.be.an('array') 115 expect(body.data).to.be.an('array')
127 expect(res.body.data).to.have.lengthOf(1) 116 expect(body.data).to.have.lengthOf(1)
128 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 1') 117 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
129 expect(res.body.data[0].videosLength).to.equal(2) 118 expect(body.data[0].videosLength).to.equal(2)
130 } 119 }
131 } 120 }
132 }) 121 })
@@ -139,32 +128,27 @@ describe('Test ActivityPub playlists search', function () {
139 ] 128 ]
140 129
141 for (const search of searches) { 130 for (const search of searches) {
142 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 131 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
143 132
144 expect(res.body.total).to.equal(1) 133 expect(body.total).to.equal(1)
145 expect(res.body.data).to.be.an('array') 134 expect(body.data).to.be.an('array')
146 expect(res.body.data).to.have.lengthOf(1) 135 expect(body.data).to.have.lengthOf(1)
147 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 2') 136 expect(body.data[0].displayName).to.equal('playlist 1 on server 2')
148 expect(res.body.data[0].videosLength).to.equal(1) 137 expect(body.data[0].videosLength).to.equal(1)
149 } 138 }
150 }) 139 })
151 140
152 it('Should not list this remote playlist', async function () { 141 it('Should not list this remote playlist', async function () {
153 const res = await getVideoPlaylistsList(servers[0].url, 0, 10) 142 const body = await servers[0].playlists.list({ start: 0, count: 10 })
154 expect(res.body.total).to.equal(1) 143 expect(body.total).to.equal(1)
155 expect(res.body.data).to.have.lengthOf(1) 144 expect(body.data).to.have.lengthOf(1)
156 expect(res.body.data[0].displayName).to.equal('playlist 1 on server 1') 145 expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
157 }) 146 })
158 147
159 it('Should update the playlist of server 2, and refresh it on server 1', async function () { 148 it('Should update the playlist of server 2, and refresh it on server 1', async function () {
160 this.timeout(60000) 149 this.timeout(60000)
161 150
162 await addVideoInPlaylist({ 151 await servers[1].playlists.addElement({ playlistId: playlistServer2UUID, attributes: { videoId: video2Server2 } })
163 url: servers[1].url,
164 token: servers[1].accessToken,
165 playlistId: playlistServer2UUID,
166 elementAttrs: { videoId: video2Server2 }
167 })
168 152
169 await waitJobs(servers) 153 await waitJobs(servers)
170 // Expire playlist 154 // Expire playlist
@@ -172,23 +156,23 @@ describe('Test ActivityPub playlists search', function () {
172 156
173 // Will run refresh async 157 // Will run refresh async
174 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID 158 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID
175 await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 159 await command.searchPlaylists({ search, token: servers[0].accessToken })
176 160
177 // Wait refresh 161 // Wait refresh
178 await wait(5000) 162 await wait(5000)
179 163
180 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 164 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
181 expect(res.body.total).to.equal(1) 165 expect(body.total).to.equal(1)
182 expect(res.body.data).to.have.lengthOf(1) 166 expect(body.data).to.have.lengthOf(1)
183 167
184 const playlist: VideoPlaylist = res.body.data[0] 168 const playlist = body.data[0]
185 expect(playlist.videosLength).to.equal(2) 169 expect(playlist.videosLength).to.equal(2)
186 }) 170 })
187 171
188 it('Should delete playlist of server 2, and delete it on server 1', async function () { 172 it('Should delete playlist of server 2, and delete it on server 1', async function () {
189 this.timeout(60000) 173 this.timeout(60000)
190 174
191 await deleteVideoPlaylist(servers[1].url, servers[1].accessToken, playlistServer2UUID) 175 await servers[1].playlists.delete({ playlistId: playlistServer2UUID })
192 176
193 await waitJobs(servers) 177 await waitJobs(servers)
194 // Expiration 178 // Expiration
@@ -196,14 +180,14 @@ describe('Test ActivityPub playlists search', function () {
196 180
197 // Will run refresh async 181 // Will run refresh async
198 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID 182 const search = 'http://localhost:' + servers[1].port + '/video-playlists/' + playlistServer2UUID
199 await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 183 await command.searchPlaylists({ search, token: servers[0].accessToken })
200 184
201 // Wait refresh 185 // Wait refresh
202 await wait(5000) 186 await wait(5000)
203 187
204 const res = await searchVideoPlaylists(servers[0].url, search, servers[0].accessToken) 188 const body = await command.searchPlaylists({ search, token: servers[0].accessToken })
205 expect(res.body.total).to.equal(0) 189 expect(body.total).to.equal(0)
206 expect(res.body.data).to.have.lengthOf(0) 190 expect(body.data).to.have.lengthOf(0)
207 }) 191 })
208 192
209 after(async function () { 193 after(async function () {
diff --git a/server/tests/api/search/search-activitypub-videos.ts b/server/tests/api/search/search-activitypub-videos.ts
index e9b4978da..b3cfcacca 100644
--- a/server/tests/api/search/search-activitypub-videos.ts
+++ b/server/tests/api/search/search-activitypub-videos.ts
@@ -1,92 +1,90 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 addVideoChannel,
7 cleanupTests, 6 cleanupTests,
8 flushAndRunMultipleServers, 7 createMultipleServers,
9 getVideosList, 8 PeerTubeServer,
10 removeVideo, 9 SearchCommand,
11 searchVideo,
12 searchVideoWithToken,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 updateVideo, 11 wait,
16 uploadVideo, 12 waitJobs
17 wait 13} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 14import { VideoPrivacy } from '@shared/models'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20import { Video, VideoPrivacy } from '../../../../shared/models/videos'
21 15
22const expect = chai.expect 16const expect = chai.expect
23 17
24describe('Test ActivityPub videos search', function () { 18describe('Test ActivityPub videos search', function () {
25 let servers: ServerInfo[] 19 let servers: PeerTubeServer[]
26 let videoServer1UUID: string 20 let videoServer1UUID: string
27 let videoServer2UUID: string 21 let videoServer2UUID: string
28 22
23 let command: SearchCommand
24
29 before(async function () { 25 before(async function () {
30 this.timeout(120000) 26 this.timeout(120000)
31 27
32 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
33 29
34 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
35 31
36 { 32 {
37 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 1 on server 1' }) 33 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1 on server 1' } })
38 videoServer1UUID = res.body.video.uuid 34 videoServer1UUID = uuid
39 } 35 }
40 36
41 { 37 {
42 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 on server 2' }) 38 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video 1 on server 2' } })
43 videoServer2UUID = res.body.video.uuid 39 videoServer2UUID = uuid
44 } 40 }
45 41
46 await waitJobs(servers) 42 await waitJobs(servers)
43
44 command = servers[0].search
47 }) 45 })
48 46
49 it('Should not find a remote video', async function () { 47 it('Should not find a remote video', async function () {
50 { 48 {
51 const search = 'http://localhost:' + servers[1].port + '/videos/watch/43' 49 const search = 'http://localhost:' + servers[1].port + '/videos/watch/43'
52 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 50 const body = await command.searchVideos({ search, token: servers[0].accessToken })
53 51
54 expect(res.body.total).to.equal(0) 52 expect(body.total).to.equal(0)
55 expect(res.body.data).to.be.an('array') 53 expect(body.data).to.be.an('array')
56 expect(res.body.data).to.have.lengthOf(0) 54 expect(body.data).to.have.lengthOf(0)
57 } 55 }
58 56
59 { 57 {
60 // Without token 58 // Without token
61 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID 59 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID
62 const res = await searchVideo(servers[0].url, search) 60 const body = await command.searchVideos({ search })
63 61
64 expect(res.body.total).to.equal(0) 62 expect(body.total).to.equal(0)
65 expect(res.body.data).to.be.an('array') 63 expect(body.data).to.be.an('array')
66 expect(res.body.data).to.have.lengthOf(0) 64 expect(body.data).to.have.lengthOf(0)
67 } 65 }
68 }) 66 })
69 67
70 it('Should search a local video', async function () { 68 it('Should search a local video', async function () {
71 const search = 'http://localhost:' + servers[0].port + '/videos/watch/' + videoServer1UUID 69 const search = 'http://localhost:' + servers[0].port + '/videos/watch/' + videoServer1UUID
72 const res = await searchVideo(servers[0].url, search) 70 const body = await command.searchVideos({ search })
73 71
74 expect(res.body.total).to.equal(1) 72 expect(body.total).to.equal(1)
75 expect(res.body.data).to.be.an('array') 73 expect(body.data).to.be.an('array')
76 expect(res.body.data).to.have.lengthOf(1) 74 expect(body.data).to.have.lengthOf(1)
77 expect(res.body.data[0].name).to.equal('video 1 on server 1') 75 expect(body.data[0].name).to.equal('video 1 on server 1')
78 }) 76 })
79 77
80 it('Should search a local video with an alternative URL', async function () { 78 it('Should search a local video with an alternative URL', async function () {
81 const search = 'http://localhost:' + servers[0].port + '/w/' + videoServer1UUID 79 const search = 'http://localhost:' + servers[0].port + '/w/' + videoServer1UUID
82 const res1 = await searchVideo(servers[0].url, search) 80 const body1 = await command.searchVideos({ search })
83 const res2 = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 81 const body2 = await command.searchVideos({ search, token: servers[0].accessToken })
84 82
85 for (const res of [ res1, res2 ]) { 83 for (const body of [ body1, body2 ]) {
86 expect(res.body.total).to.equal(1) 84 expect(body.total).to.equal(1)
87 expect(res.body.data).to.be.an('array') 85 expect(body.data).to.be.an('array')
88 expect(res.body.data).to.have.lengthOf(1) 86 expect(body.data).to.have.lengthOf(1)
89 expect(res.body.data[0].name).to.equal('video 1 on server 1') 87 expect(body.data[0].name).to.equal('video 1 on server 1')
90 } 88 }
91 }) 89 })
92 90
@@ -97,20 +95,20 @@ describe('Test ActivityPub videos search', function () {
97 ] 95 ]
98 96
99 for (const search of searches) { 97 for (const search of searches) {
100 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 98 const body = await command.searchVideos({ search, token: servers[0].accessToken })
101 99
102 expect(res.body.total).to.equal(1) 100 expect(body.total).to.equal(1)
103 expect(res.body.data).to.be.an('array') 101 expect(body.data).to.be.an('array')
104 expect(res.body.data).to.have.lengthOf(1) 102 expect(body.data).to.have.lengthOf(1)
105 expect(res.body.data[0].name).to.equal('video 1 on server 2') 103 expect(body.data[0].name).to.equal('video 1 on server 2')
106 } 104 }
107 }) 105 })
108 106
109 it('Should not list this remote video', async function () { 107 it('Should not list this remote video', async function () {
110 const res = await getVideosList(servers[0].url) 108 const { total, data } = await servers[0].videos.list()
111 expect(res.body.total).to.equal(1) 109 expect(total).to.equal(1)
112 expect(res.body.data).to.have.lengthOf(1) 110 expect(data).to.have.lengthOf(1)
113 expect(res.body.data[0].name).to.equal('video 1 on server 1') 111 expect(data[0].name).to.equal('video 1 on server 1')
114 }) 112 })
115 113
116 it('Should update video of server 2, and refresh it on server 1', async function () { 114 it('Should update video of server 2, and refresh it on server 1', async function () {
@@ -120,8 +118,8 @@ describe('Test ActivityPub videos search', function () {
120 name: 'super_channel', 118 name: 'super_channel',
121 displayName: 'super channel' 119 displayName: 'super channel'
122 } 120 }
123 const resChannel = await addVideoChannel(servers[1].url, servers[1].accessToken, channelAttributes) 121 const created = await servers[1].channels.create({ attributes: channelAttributes })
124 const videoChannelId = resChannel.body.videoChannel.id 122 const videoChannelId = created.id
125 123
126 const attributes = { 124 const attributes = {
127 name: 'updated', 125 name: 'updated',
@@ -129,7 +127,7 @@ describe('Test ActivityPub videos search', function () {
129 privacy: VideoPrivacy.UNLISTED, 127 privacy: VideoPrivacy.UNLISTED,
130 channelId: videoChannelId 128 channelId: videoChannelId
131 } 129 }
132 await updateVideo(servers[1].url, servers[1].accessToken, videoServer2UUID, attributes) 130 await servers[1].videos.update({ id: videoServer2UUID, attributes })
133 131
134 await waitJobs(servers) 132 await waitJobs(servers)
135 // Expire video 133 // Expire video
@@ -137,16 +135,16 @@ describe('Test ActivityPub videos search', function () {
137 135
138 // Will run refresh async 136 // Will run refresh async
139 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID 137 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID
140 await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 138 await command.searchVideos({ search, token: servers[0].accessToken })
141 139
142 // Wait refresh 140 // Wait refresh
143 await wait(5000) 141 await wait(5000)
144 142
145 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 143 const body = await command.searchVideos({ search, token: servers[0].accessToken })
146 expect(res.body.total).to.equal(1) 144 expect(body.total).to.equal(1)
147 expect(res.body.data).to.have.lengthOf(1) 145 expect(body.data).to.have.lengthOf(1)
148 146
149 const video: Video = res.body.data[0] 147 const video = body.data[0]
150 expect(video.name).to.equal('updated') 148 expect(video.name).to.equal('updated')
151 expect(video.channel.name).to.equal('super_channel') 149 expect(video.channel.name).to.equal('super_channel')
152 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED) 150 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
@@ -155,7 +153,7 @@ describe('Test ActivityPub videos search', function () {
155 it('Should delete video of server 2, and delete it on server 1', async function () { 153 it('Should delete video of server 2, and delete it on server 1', async function () {
156 this.timeout(120000) 154 this.timeout(120000)
157 155
158 await removeVideo(servers[1].url, servers[1].accessToken, videoServer2UUID) 156 await servers[1].videos.remove({ id: videoServer2UUID })
159 157
160 await waitJobs(servers) 158 await waitJobs(servers)
161 // Expire video 159 // Expire video
@@ -163,14 +161,14 @@ describe('Test ActivityPub videos search', function () {
163 161
164 // Will run refresh async 162 // Will run refresh async
165 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID 163 const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID
166 await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 164 await command.searchVideos({ search, token: servers[0].accessToken })
167 165
168 // Wait refresh 166 // Wait refresh
169 await wait(5000) 167 await wait(5000)
170 168
171 const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) 169 const body = await command.searchVideos({ search, token: servers[0].accessToken })
172 expect(res.body.total).to.equal(0) 170 expect(body.total).to.equal(0)
173 expect(res.body.data).to.have.lengthOf(0) 171 expect(body.data).to.have.lengthOf(0)
174 }) 172 })
175 173
176 after(async function () { 174 after(async function () {
diff --git a/server/tests/api/search/search-channels.ts b/server/tests/api/search/search-channels.ts
index daca2aebe..8a01aff90 100644
--- a/server/tests/api/search/search-channels.ts
+++ b/server/tests/api/search/search-channels.ts
@@ -2,44 +2,65 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { searchVideoChannel, advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels'
6import { 5import {
7 addVideoChannel,
8 cleanupTests, 6 cleanupTests,
9 createUser, 7 createSingleServer,
10 flushAndRunServer, 8 doubleFollow,
11 ServerInfo, 9 PeerTubeServer,
10 SearchCommand,
12 setAccessTokensToServers 11 setAccessTokensToServers
13} from '../../../../shared/extra-utils' 12} from '@shared/extra-utils'
14import { VideoChannel } from '@shared/models' 13import { VideoChannel } from '@shared/models'
15 14
16const expect = chai.expect 15const expect = chai.expect
17 16
18describe('Test channels search', function () { 17describe('Test channels search', function () {
19 let server: ServerInfo = null 18 let server: PeerTubeServer
19 let remoteServer: PeerTubeServer
20 let command: SearchCommand
20 21
21 before(async function () { 22 before(async function () {
22 this.timeout(30000) 23 this.timeout(120000)
23 24
24 server = await flushAndRunServer(1) 25 const servers = await Promise.all([
26 createSingleServer(1),
27 createSingleServer(2, { transcoding: { enabled: false } })
28 ])
29 server = servers[0]
30 remoteServer = servers[1]
25 31
26 await setAccessTokensToServers([ server ]) 32 await setAccessTokensToServers([ server, remoteServer ])
27 33
28 { 34 {
29 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user1', password: 'password' }) 35 await server.users.create({ username: 'user1' })
30 const channel = { 36 const channel = {
31 name: 'squall_channel', 37 name: 'squall_channel',
32 displayName: 'Squall channel' 38 displayName: 'Squall channel'
33 } 39 }
34 await addVideoChannel(server.url, server.accessToken, channel) 40 await server.channels.create({ attributes: channel })
35 } 41 }
42
43 {
44 await remoteServer.users.create({ username: 'user1' })
45 const channel = {
46 name: 'zell_channel',
47 displayName: 'Zell channel'
48 }
49 const { id } = await remoteServer.channels.create({ attributes: channel })
50
51 await remoteServer.videos.upload({ attributes: { channelId: id } })
52 }
53
54 await doubleFollow(server, remoteServer)
55
56 command = server.search
36 }) 57 })
37 58
38 it('Should make a simple search and not have results', async function () { 59 it('Should make a simple search and not have results', async function () {
39 const res = await searchVideoChannel(server.url, 'abc') 60 const body = await command.searchChannels({ search: 'abc' })
40 61
41 expect(res.body.total).to.equal(0) 62 expect(body.total).to.equal(0)
42 expect(res.body.data).to.have.lengthOf(0) 63 expect(body.data).to.have.lengthOf(0)
43 }) 64 })
44 65
45 it('Should make a search and have results', async function () { 66 it('Should make a search and have results', async function () {
@@ -49,11 +70,11 @@ describe('Test channels search', function () {
49 start: 0, 70 start: 0,
50 count: 1 71 count: 1
51 } 72 }
52 const res = await advancedVideoChannelSearch(server.url, search) 73 const body = await command.advancedChannelSearch({ search })
53 expect(res.body.total).to.equal(1) 74 expect(body.total).to.equal(1)
54 expect(res.body.data).to.have.lengthOf(1) 75 expect(body.data).to.have.lengthOf(1)
55 76
56 const channel: VideoChannel = res.body.data[0] 77 const channel: VideoChannel = body.data[0]
57 expect(channel.name).to.equal('squall_channel') 78 expect(channel.name).to.equal('squall_channel')
58 expect(channel.displayName).to.equal('Squall channel') 79 expect(channel.displayName).to.equal('Squall channel')
59 } 80 }
@@ -65,15 +86,64 @@ describe('Test channels search', function () {
65 count: 1 86 count: 1
66 } 87 }
67 88
68 const res = await advancedVideoChannelSearch(server.url, search) 89 const body = await command.advancedChannelSearch({ search })
90 expect(body.total).to.equal(1)
91 expect(body.data).to.have.lengthOf(0)
92 }
93 })
94
95 it('Should filter by host', async function () {
96 {
97 const search = { search: 'channel', host: remoteServer.host }
69 98
70 expect(res.body.total).to.equal(1) 99 const body = await command.advancedChannelSearch({ search })
100 expect(body.total).to.equal(1)
101 expect(body.data).to.have.lengthOf(1)
102 expect(body.data[0].displayName).to.equal('Zell channel')
103 }
104
105 {
106 const search = { search: 'Sq', host: server.host }
71 107
72 expect(res.body.data).to.have.lengthOf(0) 108 const body = await command.advancedChannelSearch({ search })
109 expect(body.total).to.equal(1)
110 expect(body.data).to.have.lengthOf(1)
111 expect(body.data[0].displayName).to.equal('Squall channel')
112 }
113
114 {
115 const search = { search: 'Squall', host: 'example.com' }
116
117 const body = await command.advancedChannelSearch({ search })
118 expect(body.total).to.equal(0)
119 expect(body.data).to.have.lengthOf(0)
120 }
121 })
122
123 it('Should filter by names', async function () {
124 {
125 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel' ] } })
126 expect(body.total).to.equal(1)
127 expect(body.data).to.have.lengthOf(1)
128 expect(body.data[0].displayName).to.equal('Squall channel')
129 }
130
131 {
132 const body = await command.advancedChannelSearch({ search: { handles: [ 'chocobozzz_channel' ] } })
133 expect(body.total).to.equal(0)
134 expect(body.data).to.have.lengthOf(0)
135 }
136
137 {
138 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel@' + remoteServer.host ] } })
139 expect(body.total).to.equal(2)
140 expect(body.data).to.have.lengthOf(2)
141 expect(body.data[0].displayName).to.equal('Squall channel')
142 expect(body.data[1].displayName).to.equal('Zell channel')
73 } 143 }
74 }) 144 })
75 145
76 after(async function () { 146 after(async function () {
77 await cleanupTests([ server ]) 147 await cleanupTests([ server, remoteServer ])
78 }) 148 })
79}) 149})
diff --git a/server/tests/api/search/search-index.ts b/server/tests/api/search/search-index.ts
index 00f79232a..4c8b1f608 100644
--- a/server/tests/api/search/search-index.ts
+++ b/server/tests/api/search/search-index.ts
@@ -2,36 +2,34 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { advancedVideoChannelSearch, searchVideoChannel } from '@shared/extra-utils/search/video-channels' 5import { cleanupTests, createSingleServer, PeerTubeServer, SearchCommand, setAccessTokensToServers } from '@shared/extra-utils'
6import { Video, VideoChannel, VideoPlaylist, VideoPlaylistPrivacy, VideoPlaylistType, VideosSearchQuery } from '@shared/models'
7import { 6import {
8 advancedVideoPlaylistSearch, 7 BooleanBothQuery,
9 advancedVideosSearch, 8 VideoChannelsSearchQuery,
10 cleanupTests, 9 VideoPlaylistPrivacy,
11 flushAndRunServer, 10 VideoPlaylistsSearchQuery,
12 immutableAssign, 11 VideoPlaylistType,
13 searchVideo, 12 VideosSearchQuery
14 searchVideoPlaylists, 13} from '@shared/models'
15 ServerInfo,
16 setAccessTokensToServers,
17 updateCustomSubConfig,
18 uploadVideo
19} from '../../../../shared/extra-utils'
20 14
21const expect = chai.expect 15const expect = chai.expect
22 16
23describe('Test videos search', function () { 17describe('Test videos search', function () {
24 let server: ServerInfo = null
25 const localVideoName = 'local video' + new Date().toISOString() 18 const localVideoName = 'local video' + new Date().toISOString()
26 19
20 let server: PeerTubeServer = null
21 let command: SearchCommand
22
27 before(async function () { 23 before(async function () {
28 this.timeout(30000) 24 this.timeout(30000)
29 25
30 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
31 27
32 await setAccessTokensToServers([ server ]) 28 await setAccessTokensToServers([ server ])
33 29
34 await uploadVideo(server.url, server.accessToken, { name: localVideoName }) 30 await server.videos.upload({ attributes: { name: localVideoName } })
31
32 command = server.search
35 }) 33 })
36 34
37 describe('Default search', async function () { 35 describe('Default search', async function () {
@@ -39,163 +37,213 @@ describe('Test videos search', function () {
39 it('Should make a local videos search by default', async function () { 37 it('Should make a local videos search by default', async function () {
40 this.timeout(10000) 38 this.timeout(10000)
41 39
42 await updateCustomSubConfig(server.url, server.accessToken, { 40 await server.config.updateCustomSubConfig({
43 search: { 41 newConfig: {
44 searchIndex: { 42 search: {
45 enabled: true, 43 searchIndex: {
46 isDefaultSearch: false, 44 enabled: true,
47 disableLocalSearch: false 45 isDefaultSearch: false,
46 disableLocalSearch: false
47 }
48 } 48 }
49 } 49 }
50 }) 50 })
51 51
52 const res = await searchVideo(server.url, 'local video') 52 const body = await command.searchVideos({ search: 'local video' })
53 53
54 expect(res.body.total).to.equal(1) 54 expect(body.total).to.equal(1)
55 expect(res.body.data[0].name).to.equal(localVideoName) 55 expect(body.data[0].name).to.equal(localVideoName)
56 }) 56 })
57 57
58 it('Should make a local channels search by default', async function () { 58 it('Should make a local channels search by default', async function () {
59 const res = await searchVideoChannel(server.url, 'root') 59 const body = await command.searchChannels({ search: 'root' })
60 60
61 expect(res.body.total).to.equal(1) 61 expect(body.total).to.equal(1)
62 expect(res.body.data[0].name).to.equal('root_channel') 62 expect(body.data[0].name).to.equal('root_channel')
63 expect(res.body.data[0].host).to.equal('localhost:' + server.port) 63 expect(body.data[0].host).to.equal('localhost:' + server.port)
64 }) 64 })
65 65
66 it('Should make an index videos search by default', async function () { 66 it('Should make an index videos search by default', async function () {
67 await updateCustomSubConfig(server.url, server.accessToken, { 67 await server.config.updateCustomSubConfig({
68 search: { 68 newConfig: {
69 searchIndex: { 69 search: {
70 enabled: true, 70 searchIndex: {
71 isDefaultSearch: true, 71 enabled: true,
72 disableLocalSearch: false 72 isDefaultSearch: true,
73 disableLocalSearch: false
74 }
73 } 75 }
74 } 76 }
75 }) 77 })
76 78
77 const res = await searchVideo(server.url, 'local video') 79 const body = await command.searchVideos({ search: 'local video' })
78 expect(res.body.total).to.be.greaterThan(2) 80 expect(body.total).to.be.greaterThan(2)
79 }) 81 })
80 82
81 it('Should make an index channels search by default', async function () { 83 it('Should make an index channels search by default', async function () {
82 const res = await searchVideoChannel(server.url, 'root') 84 const body = await command.searchChannels({ search: 'root' })
83 expect(res.body.total).to.be.greaterThan(2) 85 expect(body.total).to.be.greaterThan(2)
84 }) 86 })
85 87
86 it('Should make an index videos search if local search is disabled', async function () { 88 it('Should make an index videos search if local search is disabled', async function () {
87 await updateCustomSubConfig(server.url, server.accessToken, { 89 await server.config.updateCustomSubConfig({
88 search: { 90 newConfig: {
89 searchIndex: { 91 search: {
90 enabled: true, 92 searchIndex: {
91 isDefaultSearch: false, 93 enabled: true,
92 disableLocalSearch: true 94 isDefaultSearch: false,
95 disableLocalSearch: true
96 }
93 } 97 }
94 } 98 }
95 }) 99 })
96 100
97 const res = await searchVideo(server.url, 'local video') 101 const body = await command.searchVideos({ search: 'local video' })
98 expect(res.body.total).to.be.greaterThan(2) 102 expect(body.total).to.be.greaterThan(2)
99 }) 103 })
100 104
101 it('Should make an index channels search if local search is disabled', async function () { 105 it('Should make an index channels search if local search is disabled', async function () {
102 const res = await searchVideoChannel(server.url, 'root') 106 const body = await command.searchChannels({ search: 'root' })
103 expect(res.body.total).to.be.greaterThan(2) 107 expect(body.total).to.be.greaterThan(2)
104 }) 108 })
105 }) 109 })
106 110
107 describe('Videos search', async function () { 111 describe('Videos search', async function () {
108 112
113 async function check (search: VideosSearchQuery, exists = true) {
114 const body = await command.advancedVideoSearch({ search })
115
116 if (exists === false) {
117 expect(body.total).to.equal(0)
118 expect(body.data).to.have.lengthOf(0)
119 return
120 }
121
122 expect(body.total).to.equal(1)
123 expect(body.data).to.have.lengthOf(1)
124
125 const video = body.data[0]
126
127 expect(video.name).to.equal('What is PeerTube?')
128 expect(video.category.label).to.equal('Science & Technology')
129 expect(video.licence.label).to.equal('Attribution - Share Alike')
130 expect(video.privacy.label).to.equal('Public')
131 expect(video.duration).to.equal(113)
132 expect(video.thumbnailUrl.startsWith('https://framatube.org/static/thumbnails')).to.be.true
133
134 expect(video.account.host).to.equal('framatube.org')
135 expect(video.account.name).to.equal('framasoft')
136 expect(video.account.url).to.equal('https://framatube.org/accounts/framasoft')
137 expect(video.account.avatar).to.exist
138
139 expect(video.channel.host).to.equal('framatube.org')
140 expect(video.channel.name).to.equal('bf54d359-cfad-4935-9d45-9d6be93f63e8')
141 expect(video.channel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8')
142 expect(video.channel.avatar).to.exist
143 }
144
145 const baseSearch: VideosSearchQuery = {
146 search: 'what is peertube',
147 start: 0,
148 count: 2,
149 categoryOneOf: [ 15 ],
150 licenceOneOf: [ 2 ],
151 tagsAllOf: [ 'framasoft', 'peertube' ],
152 startDate: '2018-10-01T10:50:46.396Z',
153 endDate: '2018-10-01T10:55:46.396Z'
154 }
155
109 it('Should make a simple search and not have results', async function () { 156 it('Should make a simple search and not have results', async function () {
110 const res = await searchVideo(server.url, 'djidane'.repeat(50)) 157 const body = await command.searchVideos({ search: 'djidane'.repeat(50) })
111 158
112 expect(res.body.total).to.equal(0) 159 expect(body.total).to.equal(0)
113 expect(res.body.data).to.have.lengthOf(0) 160 expect(body.data).to.have.lengthOf(0)
114 }) 161 })
115 162
116 it('Should make a simple search and have results', async function () { 163 it('Should make a simple search and have results', async function () {
117 const res = await searchVideo(server.url, 'What is PeerTube') 164 const body = await command.searchVideos({ search: 'What is PeerTube' })
118 165
119 expect(res.body.total).to.be.greaterThan(1) 166 expect(body.total).to.be.greaterThan(1)
120 }) 167 })
121 168
122 it('Should make a complex search', async function () { 169 it('Should make a simple search', async function () {
123 170 await check(baseSearch)
124 async function check (search: VideosSearchQuery, exists = true) { 171 })
125 const res = await advancedVideosSearch(server.url, search)
126
127 if (exists === false) {
128 expect(res.body.total).to.equal(0)
129 expect(res.body.data).to.have.lengthOf(0)
130 return
131 }
132
133 expect(res.body.total).to.equal(1)
134 expect(res.body.data).to.have.lengthOf(1)
135
136 const video: Video = res.body.data[0]
137
138 expect(video.name).to.equal('What is PeerTube?')
139 expect(video.category.label).to.equal('Science & Technology')
140 expect(video.licence.label).to.equal('Attribution - Share Alike')
141 expect(video.privacy.label).to.equal('Public')
142 expect(video.duration).to.equal(113)
143 expect(video.thumbnailUrl.startsWith('https://framatube.org/static/thumbnails')).to.be.true
144 172
145 expect(video.account.host).to.equal('framatube.org') 173 it('Should search by start date', async function () {
146 expect(video.account.name).to.equal('framasoft') 174 const search = { ...baseSearch, startDate: '2018-10-01T10:54:46.396Z' }
147 expect(video.account.url).to.equal('https://framatube.org/accounts/framasoft') 175 await check(search, false)
148 expect(video.account.avatar).to.exist 176 })
149 177
150 expect(video.channel.host).to.equal('framatube.org') 178 it('Should search by tags', async function () {
151 expect(video.channel.name).to.equal('bf54d359-cfad-4935-9d45-9d6be93f63e8') 179 const search = { ...baseSearch, tagsAllOf: [ 'toto', 'framasoft' ] }
152 expect(video.channel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8') 180 await check(search, false)
153 expect(video.channel.avatar).to.exist 181 })
154 }
155 182
156 const baseSearch: VideosSearchQuery = { 183 it('Should search by duration', async function () {
157 search: 'what is peertube', 184 const search = { ...baseSearch, durationMin: 2000 }
158 start: 0, 185 await check(search, false)
159 count: 2, 186 })
160 categoryOneOf: [ 15 ],
161 licenceOneOf: [ 2 ],
162 tagsAllOf: [ 'framasoft', 'peertube' ],
163 startDate: '2018-10-01T10:50:46.396Z',
164 endDate: '2018-10-01T10:55:46.396Z'
165 }
166 187
188 it('Should search by nsfw attribute', async function () {
167 { 189 {
168 await check(baseSearch) 190 const search = { ...baseSearch, nsfw: 'true' as BooleanBothQuery }
191 await check(search, false)
169 } 192 }
170 193
171 { 194 {
172 const search = immutableAssign(baseSearch, { startDate: '2018-10-01T10:54:46.396Z' }) 195 const search = { ...baseSearch, nsfw: 'false' as BooleanBothQuery }
173 await check(search, false) 196 await check(search, true)
174 } 197 }
175 198
176 { 199 {
177 const search = immutableAssign(baseSearch, { tagsAllOf: [ 'toto', 'framasoft' ] }) 200 const search = { ...baseSearch, nsfw: 'both' as BooleanBothQuery }
178 await check(search, false) 201 await check(search, true)
179 } 202 }
203 })
180 204
205 it('Should search by host', async function () {
181 { 206 {
182 const search = immutableAssign(baseSearch, { durationMin: 2000 }) 207 const search = { ...baseSearch, host: 'example.com' }
183 await check(search, false) 208 await check(search, false)
184 } 209 }
185 210
186 { 211 {
187 const search = immutableAssign(baseSearch, { nsfw: 'true' }) 212 const search = { ...baseSearch, host: 'framatube.org' }
188 await check(search, false) 213 await check(search, true)
189 } 214 }
215 })
216
217 it('Should search by uuids', async function () {
218 const goodUUID = '9c9de5e8-0a1e-484a-b099-e80766180a6d'
219 const goodShortUUID = 'kkGMgK9ZtnKfYAgnEtQxbv'
220 const badUUID = 'c29c5b77-4a04-493d-96a9-2e9267e308f0'
221 const badShortUUID = 'rP5RgUeX9XwTSrspCdkDej'
190 222
191 { 223 {
192 const search = immutableAssign(baseSearch, { nsfw: 'false' }) 224 const uuidsMatrix = [
193 await check(search, true) 225 [ goodUUID ],
226 [ goodUUID, badShortUUID ],
227 [ badShortUUID, goodShortUUID ],
228 [ goodUUID, goodShortUUID ]
229 ]
230
231 for (const uuids of uuidsMatrix) {
232 const search = { ...baseSearch, uuids }
233 await check(search, true)
234 }
194 } 235 }
195 236
196 { 237 {
197 const search = immutableAssign(baseSearch, { nsfw: 'both' }) 238 const uuidsMatrix = [
198 await check(search, true) 239 [ badUUID ],
240 [ badShortUUID ]
241 ]
242
243 for (const uuids of uuidsMatrix) {
244 const search = { ...baseSearch, uuids }
245 await check(search, false)
246 }
199 } 247 }
200 }) 248 })
201 249
@@ -206,37 +254,44 @@ describe('Test videos search', function () {
206 count: 5 254 count: 5
207 } 255 }
208 256
209 const res = await advancedVideosSearch(server.url, search) 257 const body = await command.advancedVideoSearch({ search })
210 258
211 expect(res.body.total).to.be.greaterThan(5) 259 expect(body.total).to.be.greaterThan(5)
212 expect(res.body.data).to.have.lengthOf(5) 260 expect(body.data).to.have.lengthOf(5)
213 }) 261 })
214 262
215 it('Should use the nsfw instance policy as default', async function () { 263 it('Should use the nsfw instance policy as default', async function () {
216 let nsfwUUID: string 264 let nsfwUUID: string
217 265
218 { 266 {
219 await updateCustomSubConfig(server.url, server.accessToken, { instance: { defaultNSFWPolicy: 'display' } }) 267 await server.config.updateCustomSubConfig({
268 newConfig: {
269 instance: { defaultNSFWPolicy: 'display' }
270 }
271 })
220 272
221 const res = await searchVideo(server.url, 'NSFW search index', '-match') 273 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
222 const video = res.body.data[0] as Video 274 expect(body.data).to.have.length.greaterThan(0)
223 275
224 expect(res.body.data).to.have.length.greaterThan(0) 276 const video = body.data[0]
225 expect(video.nsfw).to.be.true 277 expect(video.nsfw).to.be.true
226 278
227 nsfwUUID = video.uuid 279 nsfwUUID = video.uuid
228 } 280 }
229 281
230 { 282 {
231 await updateCustomSubConfig(server.url, server.accessToken, { instance: { defaultNSFWPolicy: 'do_not_list' } }) 283 await server.config.updateCustomSubConfig({
284 newConfig: {
285 instance: { defaultNSFWPolicy: 'do_not_list' }
286 }
287 })
232 288
233 const res = await searchVideo(server.url, 'NSFW search index', '-match') 289 const body = await command.searchVideos({ search: 'NSFW search index', sort: '-match' })
234 290
235 try { 291 try {
236 expect(res.body.data).to.have.lengthOf(0) 292 expect(body.data).to.have.lengthOf(0)
237 } catch (err) { 293 } catch {
238 // 294 const video = body.data[0]
239 const video = res.body.data[0] as Video
240 295
241 expect(video.uuid).not.equal(nsfwUUID) 296 expect(video.uuid).not.equal(nsfwUUID)
242 } 297 }
@@ -246,20 +301,19 @@ describe('Test videos search', function () {
246 301
247 describe('Channels search', async function () { 302 describe('Channels search', async function () {
248 303
249 it('Should make a simple search and not have results', async function () { 304 async function check (search: VideoChannelsSearchQuery, exists = true) {
250 const res = await searchVideoChannel(server.url, 'a'.repeat(500)) 305 const body = await command.advancedChannelSearch({ search })
251 306
252 expect(res.body.total).to.equal(0) 307 if (exists === false) {
253 expect(res.body.data).to.have.lengthOf(0) 308 expect(body.total).to.equal(0)
254 }) 309 expect(body.data).to.have.lengthOf(0)
255 310 return
256 it('Should make a search and have results', async function () { 311 }
257 const res = await advancedVideoChannelSearch(server.url, { search: 'Framasoft', sort: 'createdAt' })
258 312
259 expect(res.body.total).to.be.greaterThan(0) 313 expect(body.total).to.be.greaterThan(0)
260 expect(res.body.data).to.have.length.greaterThan(0) 314 expect(body.data).to.have.length.greaterThan(0)
261 315
262 const videoChannel: VideoChannel = res.body.data[0] 316 const videoChannel = body.data[0]
263 expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8') 317 expect(videoChannel.url).to.equal('https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8')
264 expect(videoChannel.host).to.equal('framatube.org') 318 expect(videoChannel.host).to.equal('framatube.org')
265 expect(videoChannel.avatar).to.exist 319 expect(videoChannel.avatar).to.exist
@@ -269,32 +323,53 @@ describe('Test videos search', function () {
269 expect(videoChannel.ownerAccount.name).to.equal('framasoft') 323 expect(videoChannel.ownerAccount.name).to.equal('framasoft')
270 expect(videoChannel.ownerAccount.host).to.equal('framatube.org') 324 expect(videoChannel.ownerAccount.host).to.equal('framatube.org')
271 expect(videoChannel.ownerAccount.avatar).to.exist 325 expect(videoChannel.ownerAccount.avatar).to.exist
326 }
327
328 it('Should make a simple search and not have results', async function () {
329 const body = await command.searchChannels({ search: 'a'.repeat(500) })
330
331 expect(body.total).to.equal(0)
332 expect(body.data).to.have.lengthOf(0)
333 })
334
335 it('Should make a search and have results', async function () {
336 await check({ search: 'Framasoft', sort: 'createdAt' }, true)
337 })
338
339 it('Should make host search and have appropriate results', async function () {
340 await check({ search: 'Framasoft', host: 'example.com' }, false)
341 await check({ search: 'Framasoft', host: 'framatube.org' }, true)
342 })
343
344 it('Should make handles search and have appropriate results', async function () {
345 await check({ handles: [ 'bf54d359-cfad-4935-9d45-9d6be93f63e8@framatube.org' ] }, true)
346 await check({ handles: [ 'jeanine', 'bf54d359-cfad-4935-9d45-9d6be93f63e8@framatube.org' ] }, true)
347 await check({ handles: [ 'jeanine', 'chocobozzz_channel2@peertube2.cpy.re' ] }, false)
272 }) 348 })
273 349
274 it('Should have a correct pagination', async function () { 350 it('Should have a correct pagination', async function () {
275 const res = await advancedVideoChannelSearch(server.url, { search: 'root', start: 0, count: 2 }) 351 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
276 352
277 expect(res.body.total).to.be.greaterThan(2) 353 expect(body.total).to.be.greaterThan(2)
278 expect(res.body.data).to.have.lengthOf(2) 354 expect(body.data).to.have.lengthOf(2)
279 }) 355 })
280 }) 356 })
281 357
282 describe('Playlists search', async function () { 358 describe('Playlists search', async function () {
283 359
284 it('Should make a simple search and not have results', async function () { 360 async function check (search: VideoPlaylistsSearchQuery, exists = true) {
285 const res = await searchVideoPlaylists(server.url, 'a'.repeat(500)) 361 const body = await command.advancedPlaylistSearch({ search })
286 362
287 expect(res.body.total).to.equal(0) 363 if (exists === false) {
288 expect(res.body.data).to.have.lengthOf(0) 364 expect(body.total).to.equal(0)
289 }) 365 expect(body.data).to.have.lengthOf(0)
290 366 return
291 it('Should make a search and have results', async function () { 367 }
292 const res = await advancedVideoPlaylistSearch(server.url, { search: 'E2E playlist', sort: '-match' })
293 368
294 expect(res.body.total).to.be.greaterThan(0) 369 expect(body.total).to.be.greaterThan(0)
295 expect(res.body.data).to.have.length.greaterThan(0) 370 expect(body.data).to.have.length.greaterThan(0)
296 371
297 const videoPlaylist: VideoPlaylist = res.body.data[0] 372 const videoPlaylist = body.data[0]
298 373
299 expect(videoPlaylist.url).to.equal('https://peertube2.cpy.re/videos/watch/playlist/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a') 374 expect(videoPlaylist.url).to.equal('https://peertube2.cpy.re/videos/watch/playlist/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a')
300 expect(videoPlaylist.thumbnailUrl).to.exist 375 expect(videoPlaylist.thumbnailUrl).to.exist
@@ -319,13 +394,62 @@ describe('Test videos search', function () {
319 expect(videoPlaylist.videoChannel.name).to.equal('chocobozzz_channel') 394 expect(videoPlaylist.videoChannel.name).to.equal('chocobozzz_channel')
320 expect(videoPlaylist.videoChannel.host).to.equal('peertube2.cpy.re') 395 expect(videoPlaylist.videoChannel.host).to.equal('peertube2.cpy.re')
321 expect(videoPlaylist.videoChannel.avatar).to.exist 396 expect(videoPlaylist.videoChannel.avatar).to.exist
397 }
398
399 it('Should make a simple search and not have results', async function () {
400 const body = await command.searchPlaylists({ search: 'a'.repeat(500) })
401
402 expect(body.total).to.equal(0)
403 expect(body.data).to.have.lengthOf(0)
404 })
405
406 it('Should make a search and have results', async function () {
407 await check({ search: 'E2E playlist', sort: '-match' }, true)
408 })
409
410 it('Should make host search and have appropriate results', async function () {
411 await check({ search: 'E2E playlist', host: 'example.com' }, false)
412 await check({ search: 'E2E playlist', host: 'peertube2.cpy.re', sort: '-match' }, true)
413 })
414
415 it('Should make a search by uuids and have appropriate results', async function () {
416 const goodUUID = '73804a40-da9a-40c2-b1eb-2c6d9eec8f0a'
417 const goodShortUUID = 'fgei1ws1oa6FCaJ2qZPG29'
418 const badUUID = 'c29c5b77-4a04-493d-96a9-2e9267e308f0'
419 const badShortUUID = 'rP5RgUeX9XwTSrspCdkDej'
420
421 {
422 const uuidsMatrix = [
423 [ goodUUID ],
424 [ goodUUID, badShortUUID ],
425 [ badShortUUID, goodShortUUID ],
426 [ goodUUID, goodShortUUID ]
427 ]
428
429 for (const uuids of uuidsMatrix) {
430 const search = { search: 'E2E playlist', sort: '-match', uuids }
431 await check(search, true)
432 }
433 }
434
435 {
436 const uuidsMatrix = [
437 [ badUUID ],
438 [ badShortUUID ]
439 ]
440
441 for (const uuids of uuidsMatrix) {
442 const search = { search: 'E2E playlist', sort: '-match', uuids }
443 await check(search, false)
444 }
445 }
322 }) 446 })
323 447
324 it('Should have a correct pagination', async function () { 448 it('Should have a correct pagination', async function () {
325 const res = await advancedVideoChannelSearch(server.url, { search: 'root', start: 0, count: 2 }) 449 const body = await command.advancedChannelSearch({ search: { search: 'root', start: 0, count: 2 } })
326 450
327 expect(res.body.total).to.be.greaterThan(2) 451 expect(body.total).to.be.greaterThan(2)
328 expect(res.body.data).to.have.lengthOf(2) 452 expect(body.data).to.have.lengthOf(2)
329 }) 453 })
330 }) 454 })
331 455
diff --git a/server/tests/api/search/search-playlists.ts b/server/tests/api/search/search-playlists.ts
index ab17d55e9..15aac029a 100644
--- a/server/tests/api/search/search-playlists.ts
+++ b/server/tests/api/search/search-playlists.ts
@@ -2,82 +2,86 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoPlaylist, VideoPlaylistPrivacy } from '@shared/models'
6import { 5import {
7 addVideoInPlaylist,
8 advancedVideoPlaylistSearch,
9 cleanupTests, 6 cleanupTests,
10 createVideoPlaylist, 7 createSingleServer,
11 flushAndRunServer, 8 doubleFollow,
12 searchVideoPlaylists, 9 PeerTubeServer,
13 ServerInfo, 10 SearchCommand,
14 setAccessTokensToServers, 11 setAccessTokensToServers,
15 setDefaultVideoChannel, 12 setDefaultVideoChannel
16 uploadVideoAndGetId 13} from '@shared/extra-utils'
17} from '../../../../shared/extra-utils' 14import { VideoPlaylistPrivacy } from '@shared/models'
18 15
19const expect = chai.expect 16const expect = chai.expect
20 17
21describe('Test playlists search', function () { 18describe('Test playlists search', function () {
22 let server: ServerInfo = null 19 let server: PeerTubeServer
20 let remoteServer: PeerTubeServer
21 let command: SearchCommand
22 let playlistUUID: string
23 let playlistShortUUID: string
23 24
24 before(async function () { 25 before(async function () {
25 this.timeout(30000) 26 this.timeout(120000)
26 27
27 server = await flushAndRunServer(1) 28 const servers = await Promise.all([
29 createSingleServer(1),
30 createSingleServer(2, { transcoding: { enabled: false } })
31 ])
32 server = servers[0]
33 remoteServer = servers[1]
28 34
29 await setAccessTokensToServers([ server ]) 35 await setAccessTokensToServers([ remoteServer, server ])
30 await setDefaultVideoChannel([ server ]) 36 await setDefaultVideoChannel([ remoteServer, server ])
31
32 const videoId = (await uploadVideoAndGetId({ server: server, videoName: 'video' })).uuid
33 37
34 { 38 {
39 const videoId = (await server.videos.upload()).uuid
40
35 const attributes = { 41 const attributes = {
36 displayName: 'Dr. Kenzo Tenma hospital videos', 42 displayName: 'Dr. Kenzo Tenma hospital videos',
37 privacy: VideoPlaylistPrivacy.PUBLIC, 43 privacy: VideoPlaylistPrivacy.PUBLIC,
38 videoChannelId: server.videoChannel.id 44 videoChannelId: server.store.channel.id
39 } 45 }
40 const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 46 const created = await server.playlists.create({ attributes })
41 47 playlistUUID = created.uuid
42 await addVideoInPlaylist({ 48 playlistShortUUID = created.shortUUID
43 url: server.url, 49
44 token: server.accessToken, 50 await server.playlists.addElement({ playlistId: created.id, attributes: { videoId } })
45 playlistId: res.body.videoPlaylist.id,
46 elementAttrs: { videoId }
47 })
48 } 51 }
49 52
50 { 53 {
54 const videoId = (await remoteServer.videos.upload()).uuid
55
51 const attributes = { 56 const attributes = {
52 displayName: 'Johan & Anna Libert musics', 57 displayName: 'Johan & Anna Libert music videos',
53 privacy: VideoPlaylistPrivacy.PUBLIC, 58 privacy: VideoPlaylistPrivacy.PUBLIC,
54 videoChannelId: server.videoChannel.id 59 videoChannelId: remoteServer.store.channel.id
55 } 60 }
56 const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 61 const created = await remoteServer.playlists.create({ attributes })
57 62
58 await addVideoInPlaylist({ 63 await remoteServer.playlists.addElement({ playlistId: created.id, attributes: { videoId } })
59 url: server.url,
60 token: server.accessToken,
61 playlistId: res.body.videoPlaylist.id,
62 elementAttrs: { videoId }
63 })
64 } 64 }
65 65
66 { 66 {
67 const attributes = { 67 const attributes = {
68 displayName: 'Inspector Lunge playlist', 68 displayName: 'Inspector Lunge playlist',
69 privacy: VideoPlaylistPrivacy.PUBLIC, 69 privacy: VideoPlaylistPrivacy.PUBLIC,
70 videoChannelId: server.videoChannel.id 70 videoChannelId: server.store.channel.id
71 } 71 }
72 await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) 72 await server.playlists.create({ attributes })
73 } 73 }
74
75 await doubleFollow(server, remoteServer)
76
77 command = server.search
74 }) 78 })
75 79
76 it('Should make a simple search and not have results', async function () { 80 it('Should make a simple search and not have results', async function () {
77 const res = await searchVideoPlaylists(server.url, 'abc') 81 const body = await command.searchPlaylists({ search: 'abc' })
78 82
79 expect(res.body.total).to.equal(0) 83 expect(body.total).to.equal(0)
80 expect(res.body.data).to.have.lengthOf(0) 84 expect(body.data).to.have.lengthOf(0)
81 }) 85 })
82 86
83 it('Should make a search and have results', async function () { 87 it('Should make a search and have results', async function () {
@@ -87,27 +91,72 @@ describe('Test playlists search', function () {
87 start: 0, 91 start: 0,
88 count: 1 92 count: 1
89 } 93 }
90 const res = await advancedVideoPlaylistSearch(server.url, search) 94 const body = await command.advancedPlaylistSearch({ search })
91 expect(res.body.total).to.equal(1) 95 expect(body.total).to.equal(1)
92 expect(res.body.data).to.have.lengthOf(1) 96 expect(body.data).to.have.lengthOf(1)
93 97
94 const playlist: VideoPlaylist = res.body.data[0] 98 const playlist = body.data[0]
95 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos') 99 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos')
96 expect(playlist.url).to.equal(server.url + '/video-playlists/' + playlist.uuid) 100 expect(playlist.url).to.equal(server.url + '/video-playlists/' + playlist.uuid)
97 } 101 }
98 102
99 { 103 {
100 const search = { 104 const search = {
101 search: 'Anna Livert', 105 search: 'Anna Livert music',
102 start: 0, 106 start: 0,
103 count: 1 107 count: 1
104 } 108 }
105 const res = await advancedVideoPlaylistSearch(server.url, search) 109 const body = await command.advancedPlaylistSearch({ search })
106 expect(res.body.total).to.equal(1) 110 expect(body.total).to.equal(1)
107 expect(res.body.data).to.have.lengthOf(1) 111 expect(body.data).to.have.lengthOf(1)
112
113 const playlist = body.data[0]
114 expect(playlist.displayName).to.equal('Johan & Anna Libert music videos')
115 }
116 })
117
118 it('Should filter by host', async function () {
119 {
120 const search = { search: 'tenma', host: server.host }
121 const body = await command.advancedPlaylistSearch({ search })
122 expect(body.total).to.equal(1)
123 expect(body.data).to.have.lengthOf(1)
124
125 const playlist = body.data[0]
126 expect(playlist.displayName).to.equal('Dr. Kenzo Tenma hospital videos')
127 }
128
129 {
130 const search = { search: 'Anna', host: 'example.com' }
131 const body = await command.advancedPlaylistSearch({ search })
132 expect(body.total).to.equal(0)
133 expect(body.data).to.have.lengthOf(0)
134 }
135
136 {
137 const search = { search: 'video', host: remoteServer.host }
138 const body = await command.advancedPlaylistSearch({ search })
139 expect(body.total).to.equal(1)
140 expect(body.data).to.have.lengthOf(1)
141
142 const playlist = body.data[0]
143 expect(playlist.displayName).to.equal('Johan & Anna Libert music videos')
144 }
145 })
146
147 it('Should filter by UUIDs', async function () {
148 for (const uuid of [ playlistUUID, playlistShortUUID ]) {
149 const body = await command.advancedPlaylistSearch({ search: { uuids: [ uuid ] } })
150
151 expect(body.total).to.equal(1)
152 expect(body.data[0].displayName).to.equal('Dr. Kenzo Tenma hospital videos')
153 }
154
155 {
156 const body = await command.advancedPlaylistSearch({ search: { uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] } })
108 157
109 const playlist: VideoPlaylist = res.body.data[0] 158 expect(body.total).to.equal(0)
110 expect(playlist.displayName).to.equal('Johan & Anna Libert musics') 159 expect(body.data).to.have.lengthOf(0)
111 } 160 }
112 }) 161 })
113 162
@@ -117,12 +166,12 @@ describe('Test playlists search', function () {
117 start: 0, 166 start: 0,
118 count: 1 167 count: 1
119 } 168 }
120 const res = await advancedVideoPlaylistSearch(server.url, search) 169 const body = await command.advancedPlaylistSearch({ search })
121 expect(res.body.total).to.equal(0) 170 expect(body.total).to.equal(0)
122 expect(res.body.data).to.have.lengthOf(0) 171 expect(body.data).to.have.lengthOf(0)
123 }) 172 })
124 173
125 after(async function () { 174 after(async function () {
126 await cleanupTests([ server ]) 175 await cleanupTests([ server, remoteServer ])
127 }) 176 })
128}) 177})
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts
index 5b8907961..bd1e4d266 100644
--- a/server/tests/api/search/search-videos.ts
+++ b/server/tests/api/search/search-videos.ts
@@ -2,40 +2,42 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoPrivacy } from '@shared/models'
6import { 5import {
7 advancedVideosSearch,
8 cleanupTests, 6 cleanupTests,
9 createLive, 7 createSingleServer,
10 flushAndRunServer, 8 doubleFollow,
11 immutableAssign, 9 PeerTubeServer,
12 searchVideo, 10 SearchCommand,
13 sendRTMPStreamInVideo,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 setDefaultVideoChannel, 12 setDefaultVideoChannel,
17 stopFfmpeg, 13 stopFfmpeg,
18 updateCustomSubConfig, 14 wait
19 uploadVideo, 15} from '@shared/extra-utils'
20 wait, 16import { VideoPrivacy } from '@shared/models'
21 waitUntilLivePublished
22} from '../../../../shared/extra-utils'
23import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
24 17
25const expect = chai.expect 18const expect = chai.expect
26 19
27describe('Test videos search', function () { 20describe('Test videos search', function () {
28 let server: ServerInfo = null 21 let server: PeerTubeServer
22 let remoteServer: PeerTubeServer
29 let startDate: string 23 let startDate: string
30 let videoUUID: string 24 let videoUUID: string
25 let videoShortUUID: string
26
27 let command: SearchCommand
31 28
32 before(async function () { 29 before(async function () {
33 this.timeout(60000) 30 this.timeout(120000)
34 31
35 server = await flushAndRunServer(1) 32 const servers = await Promise.all([
33 createSingleServer(1),
34 createSingleServer(2)
35 ])
36 server = servers[0]
37 remoteServer = servers[1]
36 38
37 await setAccessTokensToServers([ server ]) 39 await setAccessTokensToServers([ server, remoteServer ])
38 await setDefaultVideoChannel([ server ]) 40 await setDefaultVideoChannel([ server, remoteServer ])
39 41
40 { 42 {
41 const attributes1 = { 43 const attributes1 = {
@@ -46,57 +48,50 @@ describe('Test videos search', function () {
46 nsfw: false, 48 nsfw: false,
47 language: 'fr' 49 language: 'fr'
48 } 50 }
49 await uploadVideo(server.url, server.accessToken, attributes1) 51 await server.videos.upload({ attributes: attributes1 })
50 52
51 const attributes2 = immutableAssign(attributes1, { name: attributes1.name + ' - 2', fixture: 'video_short.mp4' }) 53 const attributes2 = { ...attributes1, name: attributes1.name + ' - 2', fixture: 'video_short.mp4' }
52 await uploadVideo(server.url, server.accessToken, attributes2) 54 await server.videos.upload({ attributes: attributes2 })
53 55
54 { 56 {
55 const attributes3 = immutableAssign(attributes1, { name: attributes1.name + ' - 3', language: undefined }) 57 const attributes3 = { ...attributes1, name: attributes1.name + ' - 3', language: undefined }
56 const res = await uploadVideo(server.url, server.accessToken, attributes3) 58 const { id, uuid, shortUUID } = await server.videos.upload({ attributes: attributes3 })
57 const videoId = res.body.video.id 59 videoUUID = uuid
58 videoUUID = res.body.video.uuid 60 videoShortUUID = shortUUID
59 61
60 await createVideoCaption({ 62 await server.captions.add({
61 url: server.url,
62 accessToken: server.accessToken,
63 language: 'en', 63 language: 'en',
64 videoId, 64 videoId: id,
65 fixture: 'subtitle-good2.vtt', 65 fixture: 'subtitle-good2.vtt',
66 mimeType: 'application/octet-stream' 66 mimeType: 'application/octet-stream'
67 }) 67 })
68 68
69 await createVideoCaption({ 69 await server.captions.add({
70 url: server.url,
71 accessToken: server.accessToken,
72 language: 'aa', 70 language: 'aa',
73 videoId, 71 videoId: id,
74 fixture: 'subtitle-good2.vtt', 72 fixture: 'subtitle-good2.vtt',
75 mimeType: 'application/octet-stream' 73 mimeType: 'application/octet-stream'
76 }) 74 })
77 } 75 }
78 76
79 const attributes4 = immutableAssign(attributes1, { name: attributes1.name + ' - 4', language: 'pl', nsfw: true }) 77 const attributes4 = { ...attributes1, name: attributes1.name + ' - 4', language: 'pl', nsfw: true }
80 await uploadVideo(server.url, server.accessToken, attributes4) 78 await server.videos.upload({ attributes: attributes4 })
81 79
82 await wait(1000) 80 await wait(1000)
83 81
84 startDate = new Date().toISOString() 82 startDate = new Date().toISOString()
85 83
86 const attributes5 = immutableAssign(attributes1, { name: attributes1.name + ' - 5', licence: 2, language: undefined }) 84 const attributes5 = { ...attributes1, name: attributes1.name + ' - 5', licence: 2, language: undefined }
87 await uploadVideo(server.url, server.accessToken, attributes5) 85 await server.videos.upload({ attributes: attributes5 })
88 86
89 const attributes6 = immutableAssign(attributes1, { name: attributes1.name + ' - 6', tags: [ 't1', 't2' ] }) 87 const attributes6 = { ...attributes1, name: attributes1.name + ' - 6', tags: [ 't1', 't2' ] }
90 await uploadVideo(server.url, server.accessToken, attributes6) 88 await server.videos.upload({ attributes: attributes6 })
91 89
92 const attributes7 = immutableAssign(attributes1, { 90 const attributes7 = { ...attributes1, name: attributes1.name + ' - 7', originallyPublishedAt: '2019-02-12T09:58:08.286Z' }
93 name: attributes1.name + ' - 7', 91 await server.videos.upload({ attributes: attributes7 })
94 originallyPublishedAt: '2019-02-12T09:58:08.286Z'
95 })
96 await uploadVideo(server.url, server.accessToken, attributes7)
97 92
98 const attributes8 = immutableAssign(attributes1, { name: attributes1.name + ' - 8', licence: 4 }) 93 const attributes8 = { ...attributes1, name: attributes1.name + ' - 8', licence: 4 }
99 await uploadVideo(server.url, server.accessToken, attributes8) 94 await server.videos.upload({ attributes: attributes8 })
100 } 95 }
101 96
102 { 97 {
@@ -107,9 +102,9 @@ describe('Test videos search', function () {
107 licence: 2, 102 licence: 2,
108 language: 'en' 103 language: 'en'
109 } 104 }
110 await uploadVideo(server.url, server.accessToken, attributes) 105 await server.videos.upload({ attributes: attributes })
111 106
112 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes, { name: attributes.name + ' duplicate' })) 107 await server.videos.upload({ attributes: { ...attributes, name: attributes.name + ' duplicate' } })
113 } 108 }
114 109
115 { 110 {
@@ -120,7 +115,7 @@ describe('Test videos search', function () {
120 licence: 3, 115 licence: 3,
121 language: 'pl' 116 language: 'pl'
122 } 117 }
123 await uploadVideo(server.url, server.accessToken, attributes) 118 await server.videos.upload({ attributes: attributes })
124 } 119 }
125 120
126 { 121 {
@@ -129,11 +124,11 @@ describe('Test videos search', function () {
129 tags: [ 'aaaa', 'bbbb', 'cccc' ], 124 tags: [ 'aaaa', 'bbbb', 'cccc' ],
130 category: 1 125 category: 1
131 } 126 }
132 await uploadVideo(server.url, server.accessToken, attributes1) 127 await server.videos.upload({ attributes: attributes1 })
133 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { category: 2 })) 128 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
134 129
135 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'cccc', 'dddd' ] })) 130 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'cccc', 'dddd' ] } })
136 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'eeee', 'ffff' ] })) 131 await server.videos.upload({ attributes: { ...attributes1, tags: [ 'eeee', 'ffff' ] } })
137 } 132 }
138 133
139 { 134 {
@@ -141,24 +136,33 @@ describe('Test videos search', function () {
141 name: 'aaaa 2', 136 name: 'aaaa 2',
142 category: 1 137 category: 1
143 } 138 }
144 await uploadVideo(server.url, server.accessToken, attributes1) 139 await server.videos.upload({ attributes: attributes1 })
145 await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { category: 2 })) 140 await server.videos.upload({ attributes: { ...attributes1, category: 2 } })
141 }
142
143 {
144 await remoteServer.videos.upload({ attributes: { name: 'remote video 1' } })
145 await remoteServer.videos.upload({ attributes: { name: 'remote video 2' } })
146 } 146 }
147
148 await doubleFollow(server, remoteServer)
149
150 command = server.search
147 }) 151 })
148 152
149 it('Should make a simple search and not have results', async function () { 153 it('Should make a simple search and not have results', async function () {
150 const res = await searchVideo(server.url, 'abc') 154 const body = await command.searchVideos({ search: 'abc' })
151 155
152 expect(res.body.total).to.equal(0) 156 expect(body.total).to.equal(0)
153 expect(res.body.data).to.have.lengthOf(0) 157 expect(body.data).to.have.lengthOf(0)
154 }) 158 })
155 159
156 it('Should make a simple search and have results', async function () { 160 it('Should make a simple search and have results', async function () {
157 const res = await searchVideo(server.url, '4444 5555 duplicate') 161 const body = await command.searchVideos({ search: '4444 5555 duplicate' })
158 162
159 expect(res.body.total).to.equal(2) 163 expect(body.total).to.equal(2)
160 164
161 const videos = res.body.data 165 const videos = body.data
162 expect(videos).to.have.lengthOf(2) 166 expect(videos).to.have.lengthOf(2)
163 167
164 // bestmatch 168 // bestmatch
@@ -167,15 +171,15 @@ describe('Test videos search', function () {
167 }) 171 })
168 172
169 it('Should make a search on tags too, and have results', async function () { 173 it('Should make a search on tags too, and have results', async function () {
170 const query = { 174 const search = {
171 search: 'aaaa', 175 search: 'aaaa',
172 categoryOneOf: [ 1 ] 176 categoryOneOf: [ 1 ]
173 } 177 }
174 const res = await advancedVideosSearch(server.url, query) 178 const body = await command.advancedVideoSearch({ search })
175 179
176 expect(res.body.total).to.equal(2) 180 expect(body.total).to.equal(2)
177 181
178 const videos = res.body.data 182 const videos = body.data
179 expect(videos).to.have.lengthOf(2) 183 expect(videos).to.have.lengthOf(2)
180 184
181 // bestmatch 185 // bestmatch
@@ -184,14 +188,14 @@ describe('Test videos search', function () {
184 }) 188 })
185 189
186 it('Should filter on tags without a search', async function () { 190 it('Should filter on tags without a search', async function () {
187 const query = { 191 const search = {
188 tagsAllOf: [ 'bbbb' ] 192 tagsAllOf: [ 'bbbb' ]
189 } 193 }
190 const res = await advancedVideosSearch(server.url, query) 194 const body = await command.advancedVideoSearch({ search })
191 195
192 expect(res.body.total).to.equal(2) 196 expect(body.total).to.equal(2)
193 197
194 const videos = res.body.data 198 const videos = body.data
195 expect(videos).to.have.lengthOf(2) 199 expect(videos).to.have.lengthOf(2)
196 200
197 expect(videos[0].name).to.equal('9999') 201 expect(videos[0].name).to.equal('9999')
@@ -199,14 +203,14 @@ describe('Test videos search', function () {
199 }) 203 })
200 204
201 it('Should filter on category without a search', async function () { 205 it('Should filter on category without a search', async function () {
202 const query = { 206 const search = {
203 categoryOneOf: [ 3 ] 207 categoryOneOf: [ 3 ]
204 } 208 }
205 const res = await advancedVideosSearch(server.url, query) 209 const body = await command.advancedVideoSearch({ search: search })
206 210
207 expect(res.body.total).to.equal(1) 211 expect(body.total).to.equal(1)
208 212
209 const videos = res.body.data 213 const videos = body.data
210 expect(videos).to.have.lengthOf(1) 214 expect(videos).to.have.lengthOf(1)
211 215
212 expect(videos[0].name).to.equal('6666 7777 8888') 216 expect(videos[0].name).to.equal('6666 7777 8888')
@@ -218,11 +222,16 @@ describe('Test videos search', function () {
218 categoryOneOf: [ 1 ], 222 categoryOneOf: [ 1 ],
219 tagsOneOf: [ 'aAaa', 'ffff' ] 223 tagsOneOf: [ 'aAaa', 'ffff' ]
220 } 224 }
221 const res1 = await advancedVideosSearch(server.url, query)
222 expect(res1.body.total).to.equal(2)
223 225
224 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsOneOf: [ 'blabla' ] })) 226 {
225 expect(res2.body.total).to.equal(0) 227 const body = await command.advancedVideoSearch({ search: query })
228 expect(body.total).to.equal(2)
229 }
230
231 {
232 const body = await command.advancedVideoSearch({ search: { ...query, tagsOneOf: [ 'blabla' ] } })
233 expect(body.total).to.equal(0)
234 }
226 }) 235 })
227 236
228 it('Should search by tags (all of)', async function () { 237 it('Should search by tags (all of)', async function () {
@@ -231,14 +240,21 @@ describe('Test videos search', function () {
231 categoryOneOf: [ 1 ], 240 categoryOneOf: [ 1 ],
232 tagsAllOf: [ 'CCcc' ] 241 tagsAllOf: [ 'CCcc' ]
233 } 242 }
234 const res1 = await advancedVideosSearch(server.url, query)
235 expect(res1.body.total).to.equal(2)
236 243
237 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'blAbla' ] })) 244 {
238 expect(res2.body.total).to.equal(0) 245 const body = await command.advancedVideoSearch({ search: query })
246 expect(body.total).to.equal(2)
247 }
248
249 {
250 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'blAbla' ] } })
251 expect(body.total).to.equal(0)
252 }
239 253
240 const res3 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'bbbb', 'CCCC' ] })) 254 {
241 expect(res3.body.total).to.equal(1) 255 const body = await command.advancedVideoSearch({ search: { ...query, tagsAllOf: [ 'bbbb', 'CCCC' ] } })
256 expect(body.total).to.equal(1)
257 }
242 }) 258 })
243 259
244 it('Should search by category', async function () { 260 it('Should search by category', async function () {
@@ -246,12 +262,17 @@ describe('Test videos search', function () {
246 search: '6666', 262 search: '6666',
247 categoryOneOf: [ 3 ] 263 categoryOneOf: [ 3 ]
248 } 264 }
249 const res1 = await advancedVideosSearch(server.url, query)
250 expect(res1.body.total).to.equal(1)
251 expect(res1.body.data[0].name).to.equal('6666 7777 8888')
252 265
253 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { categoryOneOf: [ 2 ] })) 266 {
254 expect(res2.body.total).to.equal(0) 267 const body = await command.advancedVideoSearch({ search: query })
268 expect(body.total).to.equal(1)
269 expect(body.data[0].name).to.equal('6666 7777 8888')
270 }
271
272 {
273 const body = await command.advancedVideoSearch({ search: { ...query, categoryOneOf: [ 2 ] } })
274 expect(body.total).to.equal(0)
275 }
255 }) 276 })
256 277
257 it('Should search by licence', async function () { 278 it('Should search by licence', async function () {
@@ -259,13 +280,18 @@ describe('Test videos search', function () {
259 search: '4444 5555', 280 search: '4444 5555',
260 licenceOneOf: [ 2 ] 281 licenceOneOf: [ 2 ]
261 } 282 }
262 const res1 = await advancedVideosSearch(server.url, query)
263 expect(res1.body.total).to.equal(2)
264 expect(res1.body.data[0].name).to.equal('3333 4444 5555')
265 expect(res1.body.data[1].name).to.equal('3333 4444 5555 duplicate')
266 283
267 const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { licenceOneOf: [ 3 ] })) 284 {
268 expect(res2.body.total).to.equal(0) 285 const body = await command.advancedVideoSearch({ search: query })
286 expect(body.total).to.equal(2)
287 expect(body.data[0].name).to.equal('3333 4444 5555')
288 expect(body.data[1].name).to.equal('3333 4444 5555 duplicate')
289 }
290
291 {
292 const body = await command.advancedVideoSearch({ search: { ...query, licenceOneOf: [ 3 ] } })
293 expect(body.total).to.equal(0)
294 }
269 }) 295 })
270 296
271 it('Should search by languages', async function () { 297 it('Should search by languages', async function () {
@@ -275,23 +301,23 @@ describe('Test videos search', function () {
275 } 301 }
276 302
277 { 303 {
278 const res = await advancedVideosSearch(server.url, query) 304 const body = await command.advancedVideoSearch({ search: query })
279 expect(res.body.total).to.equal(2) 305 expect(body.total).to.equal(2)
280 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 306 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
281 expect(res.body.data[1].name).to.equal('1111 2222 3333 - 4') 307 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
282 } 308 }
283 309
284 { 310 {
285 const res = await advancedVideosSearch(server.url, immutableAssign(query, { languageOneOf: [ 'pl', 'en', '_unknown' ] })) 311 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'pl', 'en', '_unknown' ] } })
286 expect(res.body.total).to.equal(3) 312 expect(body.total).to.equal(3)
287 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 313 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
288 expect(res.body.data[1].name).to.equal('1111 2222 3333 - 4') 314 expect(body.data[1].name).to.equal('1111 2222 3333 - 4')
289 expect(res.body.data[2].name).to.equal('1111 2222 3333 - 5') 315 expect(body.data[2].name).to.equal('1111 2222 3333 - 5')
290 } 316 }
291 317
292 { 318 {
293 const res = await advancedVideosSearch(server.url, immutableAssign(query, { languageOneOf: [ 'eo' ] })) 319 const body = await command.advancedVideoSearch({ search: { ...query, languageOneOf: [ 'eo' ] } })
294 expect(res.body.total).to.equal(0) 320 expect(body.total).to.equal(0)
295 } 321 }
296 }) 322 })
297 323
@@ -301,10 +327,10 @@ describe('Test videos search', function () {
301 startDate 327 startDate
302 } 328 }
303 329
304 const res = await advancedVideosSearch(server.url, query) 330 const body = await command.advancedVideoSearch({ search: query })
305 expect(res.body.total).to.equal(4) 331 expect(body.total).to.equal(4)
306 332
307 const videos = res.body.data 333 const videos = body.data
308 expect(videos[0].name).to.equal('1111 2222 3333 - 5') 334 expect(videos[0].name).to.equal('1111 2222 3333 - 5')
309 expect(videos[1].name).to.equal('1111 2222 3333 - 6') 335 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
310 expect(videos[2].name).to.equal('1111 2222 3333 - 7') 336 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
@@ -320,10 +346,10 @@ describe('Test videos search', function () {
320 licenceOneOf: [ 1, 4 ] 346 licenceOneOf: [ 1, 4 ]
321 } 347 }
322 348
323 const res = await advancedVideosSearch(server.url, query) 349 const body = await command.advancedVideoSearch({ search: query })
324 expect(res.body.total).to.equal(4) 350 expect(body.total).to.equal(4)
325 351
326 const videos = res.body.data 352 const videos = body.data
327 expect(videos[0].name).to.equal('1111 2222 3333') 353 expect(videos[0].name).to.equal('1111 2222 3333')
328 expect(videos[1].name).to.equal('1111 2222 3333 - 6') 354 expect(videos[1].name).to.equal('1111 2222 3333 - 6')
329 expect(videos[2].name).to.equal('1111 2222 3333 - 7') 355 expect(videos[2].name).to.equal('1111 2222 3333 - 7')
@@ -340,10 +366,10 @@ describe('Test videos search', function () {
340 sort: '-name' 366 sort: '-name'
341 } 367 }
342 368
343 const res = await advancedVideosSearch(server.url, query) 369 const body = await command.advancedVideoSearch({ search: query })
344 expect(res.body.total).to.equal(4) 370 expect(body.total).to.equal(4)
345 371
346 const videos = res.body.data 372 const videos = body.data
347 expect(videos[0].name).to.equal('1111 2222 3333 - 8') 373 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
348 expect(videos[1].name).to.equal('1111 2222 3333 - 7') 374 expect(videos[1].name).to.equal('1111 2222 3333 - 7')
349 expect(videos[2].name).to.equal('1111 2222 3333 - 6') 375 expect(videos[2].name).to.equal('1111 2222 3333 - 6')
@@ -362,10 +388,10 @@ describe('Test videos search', function () {
362 count: 1 388 count: 1
363 } 389 }
364 390
365 const res = await advancedVideosSearch(server.url, query) 391 const body = await command.advancedVideoSearch({ search: query })
366 expect(res.body.total).to.equal(4) 392 expect(body.total).to.equal(4)
367 393
368 const videos = res.body.data 394 const videos = body.data
369 expect(videos[0].name).to.equal('1111 2222 3333 - 8') 395 expect(videos[0].name).to.equal('1111 2222 3333 - 8')
370 }) 396 })
371 397
@@ -381,10 +407,10 @@ describe('Test videos search', function () {
381 count: 1 407 count: 1
382 } 408 }
383 409
384 const res = await advancedVideosSearch(server.url, query) 410 const body = await command.advancedVideoSearch({ search: query })
385 expect(res.body.total).to.equal(4) 411 expect(body.total).to.equal(4)
386 412
387 const videos = res.body.data 413 const videos = body.data
388 expect(videos[0].name).to.equal('1111 2222 3333') 414 expect(videos[0].name).to.equal('1111 2222 3333')
389 }) 415 })
390 416
@@ -398,99 +424,140 @@ describe('Test videos search', function () {
398 } 424 }
399 425
400 { 426 {
401 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' }) 427 const query = { ...baseQuery, originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' }
402 const res = await advancedVideosSearch(server.url, query) 428 const body = await command.advancedVideoSearch({ search: query })
403 429
404 expect(res.body.total).to.equal(1) 430 expect(body.total).to.equal(1)
405 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 431 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
406 } 432 }
407 433
408 { 434 {
409 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' }) 435 const query = { ...baseQuery, originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' }
410 const res = await advancedVideosSearch(server.url, query) 436 const body = await command.advancedVideoSearch({ search: query })
411 437
412 expect(res.body.total).to.equal(1) 438 expect(body.total).to.equal(1)
413 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 439 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
414 } 440 }
415 441
416 { 442 {
417 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' }) 443 const query = { ...baseQuery, originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' }
418 const res = await advancedVideosSearch(server.url, query) 444 const body = await command.advancedVideoSearch({ search: query })
419 445
420 expect(res.body.total).to.equal(0) 446 expect(body.total).to.equal(0)
421 } 447 }
422 448
423 { 449 {
424 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' }) 450 const query = { ...baseQuery, originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' }
425 const res = await advancedVideosSearch(server.url, query) 451 const body = await command.advancedVideoSearch({ search: query })
426 452
427 expect(res.body.total).to.equal(0) 453 expect(body.total).to.equal(0)
428 } 454 }
429 455
430 { 456 {
431 const query = immutableAssign(baseQuery, { 457 const query = {
458 ...baseQuery,
432 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z', 459 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
433 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z' 460 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z'
434 }) 461 }
435 const res = await advancedVideosSearch(server.url, query) 462 const body = await command.advancedVideoSearch({ search: query })
436 463
437 expect(res.body.total).to.equal(0) 464 expect(body.total).to.equal(0)
438 } 465 }
439 466
440 { 467 {
441 const query = immutableAssign(baseQuery, { 468 const query = {
469 ...baseQuery,
442 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z', 470 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
443 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z' 471 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z'
444 }) 472 }
445 const res = await advancedVideosSearch(server.url, query) 473 const body = await command.advancedVideoSearch({ search: query })
446 474
447 expect(res.body.total).to.equal(1) 475 expect(body.total).to.equal(1)
448 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7') 476 expect(body.data[0].name).to.equal('1111 2222 3333 - 7')
449 } 477 }
450 }) 478 })
451 479
452 it('Should search by UUID', async function () { 480 it('Should search by UUID', async function () {
453 const search = videoUUID 481 const search = videoUUID
454 const res = await advancedVideosSearch(server.url, { search }) 482 const body = await command.advancedVideoSearch({ search: { search } })
483
484 expect(body.total).to.equal(1)
485 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
486 })
487
488 it('Should filter by UUIDs', async function () {
489 for (const uuid of [ videoUUID, videoShortUUID ]) {
490 const body = await command.advancedVideoSearch({ search: { uuids: [ uuid ] } })
491
492 expect(body.total).to.equal(1)
493 expect(body.data[0].name).to.equal('1111 2222 3333 - 3')
494 }
495
496 {
497 const body = await command.advancedVideoSearch({ search: { uuids: [ 'dfd70b83-639f-4980-94af-304a56ab4b35' ] } })
498
499 expect(body.total).to.equal(0)
500 expect(body.data).to.have.lengthOf(0)
501 }
502 })
503
504 it('Should search by host', async function () {
505 {
506 const body = await command.advancedVideoSearch({ search: { search: '6666 7777 8888', host: server.host } })
507 expect(body.total).to.equal(1)
508 expect(body.data[0].name).to.equal('6666 7777 8888')
509 }
455 510
456 expect(res.body.total).to.equal(1) 511 {
457 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') 512 const body = await command.advancedVideoSearch({ search: { search: '1111', host: 'example.com' } })
513 expect(body.total).to.equal(0)
514 expect(body.data).to.have.lengthOf(0)
515 }
516
517 {
518 const body = await command.advancedVideoSearch({ search: { search: 'remote', host: remoteServer.host } })
519 expect(body.total).to.equal(2)
520 expect(body.data).to.have.lengthOf(2)
521 expect(body.data[0].name).to.equal('remote video 1')
522 expect(body.data[1].name).to.equal('remote video 2')
523 }
458 }) 524 })
459 525
460 it('Should search by live', async function () { 526 it('Should search by live', async function () {
461 this.timeout(30000) 527 this.timeout(60000)
462 528
463 { 529 {
464 const options = { 530 const newConfig = {
465 search: { 531 search: {
466 searchIndex: { enabled: false } 532 searchIndex: { enabled: false }
467 }, 533 },
468 live: { enabled: true } 534 live: { enabled: true }
469 } 535 }
470 await updateCustomSubConfig(server.url, server.accessToken, options) 536 await server.config.updateCustomSubConfig({ newConfig })
471 } 537 }
472 538
473 { 539 {
474 const res = await advancedVideosSearch(server.url, { isLive: true }) 540 const body = await command.advancedVideoSearch({ search: { isLive: true } })
475 541
476 expect(res.body.total).to.equal(0) 542 expect(body.total).to.equal(0)
477 expect(res.body.data).to.have.lengthOf(0) 543 expect(body.data).to.have.lengthOf(0)
478 } 544 }
479 545
480 { 546 {
481 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.videoChannel.id } 547 const liveCommand = server.live
482 const resLive = await createLive(server.url, server.accessToken, liveOptions) 548
483 const liveVideoId = resLive.body.video.uuid 549 const liveAttributes = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.store.channel.id }
550 const live = await liveCommand.create({ fields: liveAttributes })
484 551
485 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId) 552 const ffmpegCommand = await liveCommand.sendRTMPStreamInVideo({ videoId: live.id })
486 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 553 await liveCommand.waitUntilPublished({ videoId: live.id })
487 554
488 const res = await advancedVideosSearch(server.url, { isLive: true }) 555 const body = await command.advancedVideoSearch({ search: { isLive: true } })
489 556
490 expect(res.body.total).to.equal(1) 557 expect(body.total).to.equal(1)
491 expect(res.body.data[0].name).to.equal('live') 558 expect(body.data[0].name).to.equal('live')
492 559
493 await stopFfmpeg(command) 560 await stopFfmpeg(ffmpegCommand)
494 } 561 }
495 }) 562 })
496 563
diff --git a/server/tests/api/server/auto-follows.ts b/server/tests/api/server/auto-follows.ts
index 1519b263f..ce7b51925 100644
--- a/server/tests/api/server/auto-follows.ts
+++ b/server/tests/api/server/auto-follows.ts
@@ -3,64 +3,45 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 acceptFollower,
7 cleanupTests, 6 cleanupTests,
8 flushAndRunMultipleServers, 7 createMultipleServers,
9 MockInstancesIndex, 8 MockInstancesIndex,
10 ServerInfo, 9 PeerTubeServer,
11 setAccessTokensToServers, 10 setAccessTokensToServers,
12 unfollow, 11 wait,
13 updateCustomSubConfig, 12 waitJobs
14 wait 13} from '@shared/extra-utils'
15} from '../../../../shared/extra-utils/index'
16import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort } from '../../../../shared/extra-utils/server/follows'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import { ActorFollow } from '../../../../shared/models/actors'
19 14
20const expect = chai.expect 15const expect = chai.expect
21 16
22async function checkFollow (follower: ServerInfo, following: ServerInfo, exists: boolean) { 17async function checkFollow (follower: PeerTubeServer, following: PeerTubeServer, exists: boolean) {
23 { 18 {
24 const res = await getFollowersListPaginationAndSort({ url: following.url, start: 0, count: 5, sort: '-createdAt' }) 19 const body = await following.follows.getFollowers({ start: 0, count: 5, sort: '-createdAt' })
25 const follows = res.body.data as ActorFollow[] 20 const follow = body.data.find(f => f.follower.host === follower.host && f.state === 'accepted')
26 21
27 const follow = follows.find(f => { 22 if (exists === true) expect(follow).to.exist
28 return f.follower.host === follower.host && f.state === 'accepted' 23 else expect(follow).to.be.undefined
29 })
30
31 if (exists === true) {
32 expect(follow).to.exist
33 } else {
34 expect(follow).to.be.undefined
35 }
36 } 24 }
37 25
38 { 26 {
39 const res = await getFollowingListPaginationAndSort({ url: follower.url, start: 0, count: 5, sort: '-createdAt' }) 27 const body = await follower.follows.getFollowings({ start: 0, count: 5, sort: '-createdAt' })
40 const follows = res.body.data as ActorFollow[] 28 const follow = body.data.find(f => f.following.host === following.host && f.state === 'accepted')
41
42 const follow = follows.find(f => {
43 return f.following.host === following.host && f.state === 'accepted'
44 })
45 29
46 if (exists === true) { 30 if (exists === true) expect(follow).to.exist
47 expect(follow).to.exist 31 else expect(follow).to.be.undefined
48 } else {
49 expect(follow).to.be.undefined
50 }
51 } 32 }
52} 33}
53 34
54async function server1Follows2 (servers: ServerInfo[]) { 35async function server1Follows2 (servers: PeerTubeServer[]) {
55 await follow(servers[0].url, [ servers[1].host ], servers[0].accessToken) 36 await servers[0].follows.follow({ hosts: [ servers[1].host ] })
56 37
57 await waitJobs(servers) 38 await waitJobs(servers)
58} 39}
59 40
60async function resetFollows (servers: ServerInfo[]) { 41async function resetFollows (servers: PeerTubeServer[]) {
61 try { 42 try {
62 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 43 await servers[0].follows.unfollow({ target: servers[1] })
63 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 44 await servers[1].follows.unfollow({ target: servers[0] })
64 } catch { /* empty */ 45 } catch { /* empty */
65 } 46 }
66 47
@@ -71,12 +52,12 @@ async function resetFollows (servers: ServerInfo[]) {
71} 52}
72 53
73describe('Test auto follows', function () { 54describe('Test auto follows', function () {
74 let servers: ServerInfo[] = [] 55 let servers: PeerTubeServer[] = []
75 56
76 before(async function () { 57 before(async function () {
77 this.timeout(30000) 58 this.timeout(30000)
78 59
79 servers = await flushAndRunMultipleServers(3) 60 servers = await createMultipleServers(3)
80 61
81 // Get the access tokens 62 // Get the access tokens
82 await setAccessTokensToServers(servers) 63 await setAccessTokensToServers(servers)
@@ -105,7 +86,7 @@ describe('Test auto follows', function () {
105 } 86 }
106 } 87 }
107 } 88 }
108 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 89 await servers[1].config.updateCustomSubConfig({ newConfig: config })
109 90
110 await server1Follows2(servers) 91 await server1Follows2(servers)
111 92
@@ -130,14 +111,14 @@ describe('Test auto follows', function () {
130 } 111 }
131 } 112 }
132 } 113 }
133 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 114 await servers[1].config.updateCustomSubConfig({ newConfig: config })
134 115
135 await server1Follows2(servers) 116 await server1Follows2(servers)
136 117
137 await checkFollow(servers[0], servers[1], false) 118 await checkFollow(servers[0], servers[1], false)
138 await checkFollow(servers[1], servers[0], false) 119 await checkFollow(servers[1], servers[0], false)
139 120
140 await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@' + servers[0].host) 121 await servers[1].follows.acceptFollower({ follower: 'peertube@' + servers[0].host })
141 await waitJobs(servers) 122 await waitJobs(servers)
142 123
143 await checkFollow(servers[0], servers[1], true) 124 await checkFollow(servers[0], servers[1], true)
@@ -147,7 +128,7 @@ describe('Test auto follows', function () {
147 128
148 config.followings.instance.autoFollowBack.enabled = false 129 config.followings.instance.autoFollowBack.enabled = false
149 config.followers.instance.manualApproval = false 130 config.followers.instance.manualApproval = false
150 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 131 await servers[1].config.updateCustomSubConfig({ newConfig: config })
151 }) 132 })
152 }) 133 })
153 134
@@ -184,7 +165,7 @@ describe('Test auto follows', function () {
184 } 165 }
185 } 166 }
186 } 167 }
187 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 168 await servers[0].config.updateCustomSubConfig({ newConfig: config })
188 169
189 await wait(5000) 170 await wait(5000)
190 await waitJobs(servers) 171 await waitJobs(servers)
diff --git a/server/tests/api/server/bulk.ts b/server/tests/api/server/bulk.ts
index 80fa7fce6..16cbcd5c3 100644
--- a/server/tests/api/server/bulk.ts
+++ b/server/tests/api/server/bulk.ts
@@ -2,91 +2,83 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { Video, VideoComment } from '@shared/models'
6import { 5import {
7 addVideoCommentReply, 6 BulkCommand,
8 addVideoCommentThread,
9 bulkRemoveCommentsOf,
10 cleanupTests, 7 cleanupTests,
11 createUser, 8 createMultipleServers,
12 doubleFollow, 9 doubleFollow,
13 flushAndRunMultipleServers, 10 PeerTubeServer,
14 getVideoCommentThreads,
15 getVideosList,
16 ServerInfo,
17 setAccessTokensToServers, 11 setAccessTokensToServers,
18 uploadVideo,
19 userLogin,
20 waitJobs 12 waitJobs
21} from '../../../../shared/extra-utils/index' 13} from '@shared/extra-utils'
22 14
23const expect = chai.expect 15const expect = chai.expect
24 16
25describe('Test bulk actions', function () { 17describe('Test bulk actions', function () {
26 const commentsUser3: { videoId: number, commentId: number }[] = [] 18 const commentsUser3: { videoId: number, commentId: number }[] = []
27 19
28 let servers: ServerInfo[] = [] 20 let servers: PeerTubeServer[] = []
29 let user1AccessToken: string 21 let user1Token: string
30 let user2AccessToken: string 22 let user2Token: string
31 let user3AccessToken: string 23 let user3Token: string
24
25 let bulkCommand: BulkCommand
32 26
33 before(async function () { 27 before(async function () {
34 this.timeout(30000) 28 this.timeout(30000)
35 29
36 servers = await flushAndRunMultipleServers(2) 30 servers = await createMultipleServers(2)
37 31
38 // Get the access tokens 32 // Get the access tokens
39 await setAccessTokensToServers(servers) 33 await setAccessTokensToServers(servers)
40 34
41 { 35 {
42 const user = { username: 'user1', password: 'password' } 36 const user = { username: 'user1', password: 'password' }
43 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 37 await servers[0].users.create({ username: user.username, password: user.password })
44 38
45 user1AccessToken = await userLogin(servers[0], user) 39 user1Token = await servers[0].login.getAccessToken(user)
46 } 40 }
47 41
48 { 42 {
49 const user = { username: 'user2', password: 'password' } 43 const user = { username: 'user2', password: 'password' }
50 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 44 await servers[0].users.create({ username: user.username, password: user.password })
51 45
52 user2AccessToken = await userLogin(servers[0], user) 46 user2Token = await servers[0].login.getAccessToken(user)
53 } 47 }
54 48
55 { 49 {
56 const user = { username: 'user3', password: 'password' } 50 const user = { username: 'user3', password: 'password' }
57 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 51 await servers[1].users.create({ username: user.username, password: user.password })
58 52
59 user3AccessToken = await userLogin(servers[1], user) 53 user3Token = await servers[1].login.getAccessToken(user)
60 } 54 }
61 55
62 await doubleFollow(servers[0], servers[1]) 56 await doubleFollow(servers[0], servers[1])
57
58 bulkCommand = new BulkCommand(servers[0])
63 }) 59 })
64 60
65 describe('Bulk remove comments', function () { 61 describe('Bulk remove comments', function () {
66 async function checkInstanceCommentsRemoved () { 62 async function checkInstanceCommentsRemoved () {
67 { 63 {
68 const res = await getVideosList(servers[0].url) 64 const { data } = await servers[0].videos.list()
69 const videos = res.body.data as Video[]
70 65
71 // Server 1 should not have these comments anymore 66 // Server 1 should not have these comments anymore
72 for (const video of videos) { 67 for (const video of data) {
73 const resThreads = await getVideoCommentThreads(servers[0].url, video.id, 0, 10) 68 const { data } = await servers[0].comments.listThreads({ videoId: video.id })
74 const comments = resThreads.body.data as VideoComment[] 69 const comment = data.find(c => c.text === 'comment by user 3')
75 const comment = comments.find(c => c.text === 'comment by user 3')
76 70
77 expect(comment).to.not.exist 71 expect(comment).to.not.exist
78 } 72 }
79 } 73 }
80 74
81 { 75 {
82 const res = await getVideosList(servers[1].url) 76 const { data } = await servers[1].videos.list()
83 const videos = res.body.data as Video[]
84 77
85 // Server 1 should not have these comments on videos of server 1 78 // Server 1 should not have these comments on videos of server 1
86 for (const video of videos) { 79 for (const video of data) {
87 const resThreads = await getVideoCommentThreads(servers[1].url, video.id, 0, 10) 80 const { data } = await servers[1].comments.listThreads({ videoId: video.id })
88 const comments = resThreads.body.data as VideoComment[] 81 const comment = data.find(c => c.text === 'comment by user 3')
89 const comment = comments.find(c => c.text === 'comment by user 3')
90 82
91 if (video.account.host === 'localhost:' + servers[0].port) { 83 if (video.account.host === 'localhost:' + servers[0].port) {
92 expect(comment).to.not.exist 84 expect(comment).to.not.exist
@@ -100,30 +92,31 @@ describe('Test bulk actions', function () {
100 before(async function () { 92 before(async function () {
101 this.timeout(120000) 93 this.timeout(120000)
102 94
103 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 1 server 1' }) 95 await servers[0].videos.upload({ attributes: { name: 'video 1 server 1' } })
104 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 2 server 1' }) 96 await servers[0].videos.upload({ attributes: { name: 'video 2 server 1' } })
105 await uploadVideo(servers[0].url, user1AccessToken, { name: 'video 3 server 1' }) 97 await servers[0].videos.upload({ token: user1Token, attributes: { name: 'video 3 server 1' } })
106 98
107 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video 1 server 2' }) 99 await servers[1].videos.upload({ attributes: { name: 'video 1 server 2' } })
108 100
109 await waitJobs(servers) 101 await waitJobs(servers)
110 102
111 { 103 {
112 const res = await getVideosList(servers[0].url) 104 const { data } = await servers[0].videos.list()
113 for (const video of res.body.data) { 105 for (const video of data) {
114 await addVideoCommentThread(servers[0].url, servers[0].accessToken, video.id, 'comment by root server 1') 106 await servers[0].comments.createThread({ videoId: video.id, text: 'comment by root server 1' })
115 await addVideoCommentThread(servers[0].url, user1AccessToken, video.id, 'comment by user 1') 107 await servers[0].comments.createThread({ token: user1Token, videoId: video.id, text: 'comment by user 1' })
116 await addVideoCommentThread(servers[0].url, user2AccessToken, video.id, 'comment by user 2') 108 await servers[0].comments.createThread({ token: user2Token, videoId: video.id, text: 'comment by user 2' })
117 } 109 }
118 } 110 }
119 111
120 { 112 {
121 const res = await getVideosList(servers[1].url) 113 const { data } = await servers[1].videos.list()
122 for (const video of res.body.data) {
123 await addVideoCommentThread(servers[1].url, servers[1].accessToken, video.id, 'comment by root server 2')
124 114
125 const res = await addVideoCommentThread(servers[1].url, user3AccessToken, video.id, 'comment by user 3') 115 for (const video of data) {
126 commentsUser3.push({ videoId: video.id, commentId: res.body.comment.id }) 116 await servers[1].comments.createThread({ videoId: video.id, text: 'comment by root server 2' })
117
118 const comment = await servers[1].comments.createThread({ token: user3Token, videoId: video.id, text: 'comment by user 3' })
119 commentsUser3.push({ videoId: video.id, commentId: comment.id })
127 } 120 }
128 } 121 }
129 122
@@ -133,9 +126,8 @@ describe('Test bulk actions', function () {
133 it('Should delete comments of an account on my videos', async function () { 126 it('Should delete comments of an account on my videos', async function () {
134 this.timeout(60000) 127 this.timeout(60000)
135 128
136 await bulkRemoveCommentsOf({ 129 await bulkCommand.removeCommentsOf({
137 url: servers[0].url, 130 token: user1Token,
138 token: user1AccessToken,
139 attributes: { 131 attributes: {
140 accountName: 'user2', 132 accountName: 'user2',
141 scope: 'my-videos' 133 scope: 'my-videos'
@@ -145,18 +137,14 @@ describe('Test bulk actions', function () {
145 await waitJobs(servers) 137 await waitJobs(servers)
146 138
147 for (const server of servers) { 139 for (const server of servers) {
148 const res = await getVideosList(server.url) 140 const { data } = await server.videos.list()
149 141
150 for (const video of res.body.data) { 142 for (const video of data) {
151 const resThreads = await getVideoCommentThreads(server.url, video.id, 0, 10) 143 const { data } = await server.comments.listThreads({ videoId: video.id })
152 const comments = resThreads.body.data as VideoComment[] 144 const comment = data.find(c => c.text === 'comment by user 2')
153 const comment = comments.find(c => c.text === 'comment by user 2')
154 145
155 if (video.name === 'video 3 server 1') { 146 if (video.name === 'video 3 server 1') expect(comment).to.not.exist
156 expect(comment).to.not.exist 147 else expect(comment).to.exist
157 } else {
158 expect(comment).to.exist
159 }
160 } 148 }
161 } 149 }
162 }) 150 })
@@ -164,9 +152,7 @@ describe('Test bulk actions', function () {
164 it('Should delete comments of an account on the instance', async function () { 152 it('Should delete comments of an account on the instance', async function () {
165 this.timeout(60000) 153 this.timeout(60000)
166 154
167 await bulkRemoveCommentsOf({ 155 await bulkCommand.removeCommentsOf({
168 url: servers[0].url,
169 token: servers[0].accessToken,
170 attributes: { 156 attributes: {
171 accountName: 'user3@localhost:' + servers[1].port, 157 accountName: 'user3@localhost:' + servers[1].port,
172 scope: 'instance' 158 scope: 'instance'
@@ -182,7 +168,12 @@ describe('Test bulk actions', function () {
182 this.timeout(60000) 168 this.timeout(60000)
183 169
184 for (const obj of commentsUser3) { 170 for (const obj of commentsUser3) {
185 await addVideoCommentReply(servers[1].url, user3AccessToken, obj.videoId, obj.commentId, 'comment by user 3 bis') 171 await servers[1].comments.addReply({
172 token: user3Token,
173 videoId: obj.videoId,
174 toCommentId: obj.commentId,
175 text: 'comment by user 3 bis'
176 })
186 } 177 }
187 178
188 await waitJobs(servers) 179 await waitJobs(servers)
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index 19bf9582c..fd61e95df 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -2,31 +2,20 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { About } from '../../../../shared/models/server/about.model'
6import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
7import { 5import {
8 cleanupTests, 6 cleanupTests,
9 deleteCustomConfig, 7 createSingleServer,
10 flushAndRunServer,
11 getAbout,
12 getConfig,
13 getCustomConfig,
14 killallServers, 8 killallServers,
15 makeGetRequest, 9 makeGetRequest,
16 parallelTests, 10 parallelTests,
17 registerUser, 11 PeerTubeServer,
18 reRunServer, 12 setAccessTokensToServers
19 ServerInfo, 13} from '@shared/extra-utils'
20 setAccessTokensToServers, 14import { CustomConfig, HttpStatusCode } from '@shared/models'
21 updateCustomConfig,
22 uploadVideo
23} from '../../../../shared/extra-utils'
24import { ServerConfig } from '../../../../shared/models'
25import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
26 15
27const expect = chai.expect 16const expect = chai.expect
28 17
29function checkInitialConfig (server: ServerInfo, data: CustomConfig) { 18function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
30 expect(data.instance.name).to.equal('PeerTube') 19 expect(data.instance.name).to.equal('PeerTube')
31 expect(data.instance.shortDescription).to.equal( 20 expect(data.instance.shortDescription).to.equal(
32 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' 21 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
@@ -213,18 +202,17 @@ function checkUpdatedConfig (data: CustomConfig) {
213} 202}
214 203
215describe('Test config', function () { 204describe('Test config', function () {
216 let server = null 205 let server: PeerTubeServer = null
217 206
218 before(async function () { 207 before(async function () {
219 this.timeout(30000) 208 this.timeout(30000)
220 209
221 server = await flushAndRunServer(1) 210 server = await createSingleServer(1)
222 await setAccessTokensToServers([ server ]) 211 await setAccessTokensToServers([ server ])
223 }) 212 })
224 213
225 it('Should have a correct config on a server with registration enabled', async function () { 214 it('Should have a correct config on a server with registration enabled', async function () {
226 const res = await getConfig(server.url) 215 const data = await server.config.getConfig()
227 const data: ServerConfig = res.body
228 216
229 expect(data.signup.allowed).to.be.true 217 expect(data.signup.allowed).to.be.true
230 }) 218 })
@@ -233,35 +221,32 @@ describe('Test config', function () {
233 this.timeout(5000) 221 this.timeout(5000)
234 222
235 await Promise.all([ 223 await Promise.all([
236 registerUser(server.url, 'user1', 'super password'), 224 server.users.register({ username: 'user1' }),
237 registerUser(server.url, 'user2', 'super password'), 225 server.users.register({ username: 'user2' }),
238 registerUser(server.url, 'user3', 'super password') 226 server.users.register({ username: 'user3' })
239 ]) 227 ])
240 228
241 const res = await getConfig(server.url) 229 const data = await server.config.getConfig()
242 const data: ServerConfig = res.body
243 230
244 expect(data.signup.allowed).to.be.false 231 expect(data.signup.allowed).to.be.false
245 }) 232 })
246 233
247 it('Should have the correct video allowed extensions', async function () { 234 it('Should have the correct video allowed extensions', async function () {
248 const res = await getConfig(server.url) 235 const data = await server.config.getConfig()
249 const data: ServerConfig = res.body
250 236
251 expect(data.video.file.extensions).to.have.lengthOf(3) 237 expect(data.video.file.extensions).to.have.lengthOf(3)
252 expect(data.video.file.extensions).to.contain('.mp4') 238 expect(data.video.file.extensions).to.contain('.mp4')
253 expect(data.video.file.extensions).to.contain('.webm') 239 expect(data.video.file.extensions).to.contain('.webm')
254 expect(data.video.file.extensions).to.contain('.ogv') 240 expect(data.video.file.extensions).to.contain('.ogv')
255 241
256 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) 242 await server.videos.upload({ attributes: { fixture: 'video_short.mkv' }, expectedStatus: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415 })
257 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415) 243 await server.videos.upload({ attributes: { fixture: 'sample.ogg' }, expectedStatus: HttpStatusCode.UNSUPPORTED_MEDIA_TYPE_415 })
258 244
259 expect(data.contactForm.enabled).to.be.true 245 expect(data.contactForm.enabled).to.be.true
260 }) 246 })
261 247
262 it('Should get the customized configuration', async function () { 248 it('Should get the customized configuration', async function () {
263 const res = await getCustomConfig(server.url, server.accessToken) 249 const data = await server.config.getCustomConfig()
264 const data = res.body as CustomConfig
265 250
266 checkInitialConfig(server, data) 251 checkInitialConfig(server, data)
267 }) 252 })
@@ -438,19 +423,16 @@ describe('Test config', function () {
438 } 423 }
439 } 424 }
440 } 425 }
441 await updateCustomConfig(server.url, server.accessToken, newCustomConfig) 426 await server.config.updateCustomConfig({ newCustomConfig })
442
443 const res = await getCustomConfig(server.url, server.accessToken)
444 const data = res.body
445 427
428 const data = await server.config.getCustomConfig()
446 checkUpdatedConfig(data) 429 checkUpdatedConfig(data)
447 }) 430 })
448 431
449 it('Should have the correct updated video allowed extensions', async function () { 432 it('Should have the correct updated video allowed extensions', async function () {
450 this.timeout(10000) 433 this.timeout(10000)
451 434
452 const res = await getConfig(server.url) 435 const data = await server.config.getConfig()
453 const data: ServerConfig = res.body
454 436
455 expect(data.video.file.extensions).to.have.length.above(4) 437 expect(data.video.file.extensions).to.have.length.above(4)
456 expect(data.video.file.extensions).to.contain('.mp4') 438 expect(data.video.file.extensions).to.contain('.mp4')
@@ -463,26 +445,24 @@ describe('Test config', function () {
463 expect(data.video.file.extensions).to.contain('.ogg') 445 expect(data.video.file.extensions).to.contain('.ogg')
464 expect(data.video.file.extensions).to.contain('.flac') 446 expect(data.video.file.extensions).to.contain('.flac')
465 447
466 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, HttpStatusCode.OK_200) 448 await server.videos.upload({ attributes: { fixture: 'video_short.mkv' }, expectedStatus: HttpStatusCode.OK_200 })
467 await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, HttpStatusCode.OK_200) 449 await server.videos.upload({ attributes: { fixture: 'sample.ogg' }, expectedStatus: HttpStatusCode.OK_200 })
468 }) 450 })
469 451
470 it('Should have the configuration updated after a restart', async function () { 452 it('Should have the configuration updated after a restart', async function () {
471 this.timeout(10000) 453 this.timeout(10000)
472 454
473 killallServers([ server ]) 455 await killallServers([ server ])
474 456
475 await reRunServer(server) 457 await server.run()
476 458
477 const res = await getCustomConfig(server.url, server.accessToken) 459 const data = await server.config.getCustomConfig()
478 const data = res.body
479 460
480 checkUpdatedConfig(data) 461 checkUpdatedConfig(data)
481 }) 462 })
482 463
483 it('Should fetch the about information', async function () { 464 it('Should fetch the about information', async function () {
484 const res = await getAbout(server.url) 465 const data = await server.config.getAbout()
485 const data: About = res.body
486 466
487 expect(data.instance.name).to.equal('PeerTube updated') 467 expect(data.instance.name).to.equal('PeerTube updated')
488 expect(data.instance.shortDescription).to.equal('my short description') 468 expect(data.instance.shortDescription).to.equal('my short description')
@@ -504,11 +484,9 @@ describe('Test config', function () {
504 it('Should remove the custom configuration', async function () { 484 it('Should remove the custom configuration', async function () {
505 this.timeout(10000) 485 this.timeout(10000)
506 486
507 await deleteCustomConfig(server.url, server.accessToken) 487 await server.config.deleteCustomConfig()
508
509 const res = await getCustomConfig(server.url, server.accessToken)
510 const data = res.body
511 488
489 const data = await server.config.getCustomConfig()
512 checkInitialConfig(server, data) 490 checkInitialConfig(server, data)
513 }) 491 })
514 492
@@ -519,26 +497,26 @@ describe('Test config', function () {
519 const res = await makeGetRequest({ 497 const res = await makeGetRequest({
520 url: server.url, 498 url: server.url,
521 path: '/api/v1/config', 499 path: '/api/v1/config',
522 statusCodeExpected: 200 500 expectedStatus: 200
523 }) 501 })
524 502
525 expect(res.headers['x-frame-options']).to.exist 503 expect(res.headers['x-frame-options']).to.exist
526 } 504 }
527 505
528 killallServers([ server ]) 506 await killallServers([ server ])
529 507
530 const config = { 508 const config = {
531 security: { 509 security: {
532 frameguard: { enabled: false } 510 frameguard: { enabled: false }
533 } 511 }
534 } 512 }
535 server = await reRunServer(server, config) 513 await server.run(config)
536 514
537 { 515 {
538 const res = await makeGetRequest({ 516 const res = await makeGetRequest({
539 url: server.url, 517 url: server.url,
540 path: '/api/v1/config', 518 path: '/api/v1/config',
541 statusCodeExpected: 200 519 expectedStatus: 200
542 }) 520 })
543 521
544 expect(res.headers['x-frame-options']).to.not.exist 522 expect(res.headers['x-frame-options']).to.not.exist
diff --git a/server/tests/api/server/contact-form.ts b/server/tests/api/server/contact-form.ts
index 8851ad55e..c555661ad 100644
--- a/server/tests/api/server/contact-form.ts
+++ b/server/tests/api/server/contact-form.ts
@@ -1,18 +1,25 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo, setAccessTokensToServers, wait } from '../../../../shared/extra-utils' 4import * as chai from 'chai'
6import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' 5import {
7import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 6 cleanupTests,
8import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form' 7 ContactFormCommand,
9import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 8 createSingleServer,
9 MockSmtpServer,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 wait,
13 waitJobs
14} from '@shared/extra-utils'
15import { HttpStatusCode } from '@shared/models'
10 16
11const expect = chai.expect 17const expect = chai.expect
12 18
13describe('Test contact form', function () { 19describe('Test contact form', function () {
14 let server: ServerInfo 20 let server: PeerTubeServer
15 const emails: object[] = [] 21 const emails: object[] = []
22 let command: ContactFormCommand
16 23
17 before(async function () { 24 before(async function () {
18 this.timeout(30000) 25 this.timeout(30000)
@@ -25,15 +32,16 @@ describe('Test contact form', function () {
25 port 32 port
26 } 33 }
27 } 34 }
28 server = await flushAndRunServer(1, overrideConfig) 35 server = await createSingleServer(1, overrideConfig)
29 await setAccessTokensToServers([ server ]) 36 await setAccessTokensToServers([ server ])
37
38 command = server.contactForm
30 }) 39 })
31 40
32 it('Should send a contact form', async function () { 41 it('Should send a contact form', async function () {
33 this.timeout(10000) 42 this.timeout(10000)
34 43
35 await sendContactForm({ 44 await command.send({
36 url: server.url,
37 fromEmail: 'toto@example.com', 45 fromEmail: 'toto@example.com',
38 body: 'my super message', 46 body: 'my super message',
39 subject: 'my subject', 47 subject: 'my subject',
@@ -58,16 +66,14 @@ describe('Test contact form', function () {
58 66
59 await wait(1000) 67 await wait(1000)
60 68
61 await sendContactForm({ 69 await command.send({
62 url: server.url,
63 fromEmail: 'toto@example.com', 70 fromEmail: 'toto@example.com',
64 body: 'my super message', 71 body: 'my super message',
65 subject: 'my subject', 72 subject: 'my subject',
66 fromName: 'Super toto' 73 fromName: 'Super toto'
67 }) 74 })
68 75
69 await sendContactForm({ 76 await command.send({
70 url: server.url,
71 fromEmail: 'toto@example.com', 77 fromEmail: 'toto@example.com',
72 body: 'my super message', 78 body: 'my super message',
73 fromName: 'Super toto', 79 fromName: 'Super toto',
@@ -79,8 +85,7 @@ describe('Test contact form', function () {
79 it('Should be able to send another contact form after a while', async function () { 85 it('Should be able to send another contact form after a while', async function () {
80 await wait(1000) 86 await wait(1000)
81 87
82 await sendContactForm({ 88 await command.send({
83 url: server.url,
84 fromEmail: 'toto@example.com', 89 fromEmail: 'toto@example.com',
85 fromName: 'Super toto', 90 fromName: 'Super toto',
86 subject: 'my subject', 91 subject: 'my subject',
diff --git a/server/tests/api/server/email.ts b/server/tests/api/server/email.ts
index 92768d9df..5f97edbc2 100644
--- a/server/tests/api/server/email.ts
+++ b/server/tests/api/server/email.ts
@@ -2,37 +2,18 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import { cleanupTests, createSingleServer, MockSmtpServer, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6 addVideoToBlacklist, 6import { HttpStatusCode } from '@shared/models'
7 askResetPassword,
8 askSendVerifyEmail,
9 blockUser,
10 cleanupTests,
11 createUser,
12 flushAndRunServer,
13 removeVideoFromBlacklist,
14 reportAbuse,
15 resetPassword,
16 ServerInfo,
17 setAccessTokensToServers,
18 unblockUser,
19 uploadVideo,
20 userLogin,
21 verifyEmail
22} from '../../../../shared/extra-utils'
23import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
24import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
25import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
26 7
27const expect = chai.expect 8const expect = chai.expect
28 9
29describe('Test emails', function () { 10describe('Test emails', function () {
30 let server: ServerInfo 11 let server: PeerTubeServer
31 let userId: number 12 let userId: number
32 let userId2: number 13 let userId2: number
33 let userAccessToken: string 14 let userAccessToken: string
34 15
35 let videoUUID: string 16 let videoShortUUID: string
36 let videoId: number 17 let videoId: number
37 18
38 let videoUserUUID: string 19 let videoUserUUID: string
@@ -58,31 +39,29 @@ describe('Test emails', function () {
58 port: emailPort 39 port: emailPort
59 } 40 }
60 } 41 }
61 server = await flushAndRunServer(1, overrideConfig) 42 server = await createSingleServer(1, overrideConfig)
62 await setAccessTokensToServers([ server ]) 43 await setAccessTokensToServers([ server ])
63 44
64 { 45 {
65 const res = await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 46 const created = await server.users.create({ username: user.username, password: user.password })
66 userId = res.body.user.id 47 userId = created.id
67 48
68 userAccessToken = await userLogin(server, user) 49 userAccessToken = await server.login.getAccessToken(user)
69 } 50 }
70 51
71 { 52 {
72 const attributes = { 53 const attributes = { name: 'my super user video' }
73 name: 'my super user video' 54 const { uuid } = await server.videos.upload({ token: userAccessToken, attributes })
74 } 55 videoUserUUID = uuid
75 const res = await uploadVideo(server.url, userAccessToken, attributes)
76 videoUserUUID = res.body.video.uuid
77 } 56 }
78 57
79 { 58 {
80 const attributes = { 59 const attributes = {
81 name: 'my super name' 60 name: 'my super name'
82 } 61 }
83 const res = await uploadVideo(server.url, server.accessToken, attributes) 62 const { shortUUID, id } = await server.videos.upload({ attributes })
84 videoUUID = res.body.video.uuid 63 videoShortUUID = shortUUID
85 videoId = res.body.video.id 64 videoId = id
86 } 65 }
87 }) 66 })
88 67
@@ -91,7 +70,7 @@ describe('Test emails', function () {
91 it('Should ask to reset the password', async function () { 70 it('Should ask to reset the password', async function () {
92 this.timeout(10000) 71 this.timeout(10000)
93 72
94 await askResetPassword(server.url, 'user_1@example.com') 73 await server.users.askResetPassword({ email: 'user_1@example.com' })
95 74
96 await waitJobs(server) 75 await waitJobs(server)
97 expect(emails).to.have.lengthOf(1) 76 expect(emails).to.have.lengthOf(1)
@@ -117,34 +96,40 @@ describe('Test emails', function () {
117 }) 96 })
118 97
119 it('Should not reset the password with an invalid verification string', async function () { 98 it('Should not reset the password with an invalid verification string', async function () {
120 await resetPassword(server.url, userId, verificationString + 'b', 'super_password2', HttpStatusCode.FORBIDDEN_403) 99 await server.users.resetPassword({
100 userId,
101 verificationString: verificationString + 'b',
102 password: 'super_password2',
103 expectedStatus: HttpStatusCode.FORBIDDEN_403
104 })
121 }) 105 })
122 106
123 it('Should reset the password', async function () { 107 it('Should reset the password', async function () {
124 await resetPassword(server.url, userId, verificationString, 'super_password2') 108 await server.users.resetPassword({ userId, verificationString, password: 'super_password2' })
125 }) 109 })
126 110
127 it('Should not reset the password with the same verification string', async function () { 111 it('Should not reset the password with the same verification string', async function () {
128 await resetPassword(server.url, userId, verificationString, 'super_password3', HttpStatusCode.FORBIDDEN_403) 112 await server.users.resetPassword({
113 userId,
114 verificationString,
115 password: 'super_password3',
116 expectedStatus: HttpStatusCode.FORBIDDEN_403
117 })
129 }) 118 })
130 119
131 it('Should login with this new password', async function () { 120 it('Should login with this new password', async function () {
132 user.password = 'super_password2' 121 user.password = 'super_password2'
133 122
134 await userLogin(server, user) 123 await server.login.getAccessToken(user)
135 }) 124 })
136 }) 125 })
137 126
138 describe('When creating a user without password', function () { 127 describe('When creating a user without password', function () {
128
139 it('Should send a create password email', async function () { 129 it('Should send a create password email', async function () {
140 this.timeout(10000) 130 this.timeout(10000)
141 131
142 await createUser({ 132 await server.users.create({ username: 'create_password', password: '' })
143 url: server.url,
144 accessToken: server.accessToken,
145 username: 'create_password',
146 password: ''
147 })
148 133
149 await waitJobs(server) 134 await waitJobs(server)
150 expect(emails).to.have.lengthOf(2) 135 expect(emails).to.have.lengthOf(2)
@@ -170,15 +155,24 @@ describe('Test emails', function () {
170 }) 155 })
171 156
172 it('Should not reset the password with an invalid verification string', async function () { 157 it('Should not reset the password with an invalid verification string', async function () {
173 await resetPassword(server.url, userId2, verificationString2 + 'c', 'newly_created_password', HttpStatusCode.FORBIDDEN_403) 158 await server.users.resetPassword({
159 userId: userId2,
160 verificationString: verificationString2 + 'c',
161 password: 'newly_created_password',
162 expectedStatus: HttpStatusCode.FORBIDDEN_403
163 })
174 }) 164 })
175 165
176 it('Should reset the password', async function () { 166 it('Should reset the password', async function () {
177 await resetPassword(server.url, userId2, verificationString2, 'newly_created_password') 167 await server.users.resetPassword({
168 userId: userId2,
169 verificationString: verificationString2,
170 password: 'newly_created_password'
171 })
178 }) 172 })
179 173
180 it('Should login with this new password', async function () { 174 it('Should login with this new password', async function () {
181 await userLogin(server, { 175 await server.login.getAccessToken({
182 username: 'create_password', 176 username: 'create_password',
183 password: 'newly_created_password' 177 password: 'newly_created_password'
184 }) 178 })
@@ -186,11 +180,12 @@ describe('Test emails', function () {
186 }) 180 })
187 181
188 describe('When creating an abuse', function () { 182 describe('When creating an abuse', function () {
183
189 it('Should send the notification email', async function () { 184 it('Should send the notification email', async function () {
190 this.timeout(10000) 185 this.timeout(10000)
191 186
192 const reason = 'my super bad reason' 187 const reason = 'my super bad reason'
193 await reportAbuse({ url: server.url, token: server.accessToken, videoId, reason }) 188 await server.abuses.report({ videoId, reason })
194 189
195 await waitJobs(server) 190 await waitJobs(server)
196 expect(emails).to.have.lengthOf(3) 191 expect(emails).to.have.lengthOf(3)
@@ -201,7 +196,7 @@ describe('Test emails', function () {
201 expect(email['from'][0]['address']).equal('test-admin@localhost') 196 expect(email['from'][0]['address']).equal('test-admin@localhost')
202 expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com') 197 expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com')
203 expect(email['subject']).contains('abuse') 198 expect(email['subject']).contains('abuse')
204 expect(email['text']).contains(videoUUID) 199 expect(email['text']).contains(videoShortUUID)
205 }) 200 })
206 }) 201 })
207 202
@@ -211,7 +206,7 @@ describe('Test emails', function () {
211 this.timeout(10000) 206 this.timeout(10000)
212 207
213 const reason = 'my super bad reason' 208 const reason = 'my super bad reason'
214 await blockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204, reason) 209 await server.users.banUser({ userId, reason })
215 210
216 await waitJobs(server) 211 await waitJobs(server)
217 expect(emails).to.have.lengthOf(4) 212 expect(emails).to.have.lengthOf(4)
@@ -229,7 +224,7 @@ describe('Test emails', function () {
229 it('Should send the notification email when unblocking a user', async function () { 224 it('Should send the notification email when unblocking a user', async function () {
230 this.timeout(10000) 225 this.timeout(10000)
231 226
232 await unblockUser(server.url, userId, server.accessToken, HttpStatusCode.NO_CONTENT_204) 227 await server.users.unbanUser({ userId })
233 228
234 await waitJobs(server) 229 await waitJobs(server)
235 expect(emails).to.have.lengthOf(5) 230 expect(emails).to.have.lengthOf(5)
@@ -249,7 +244,7 @@ describe('Test emails', function () {
249 this.timeout(10000) 244 this.timeout(10000)
250 245
251 const reason = 'my super reason' 246 const reason = 'my super reason'
252 await addVideoToBlacklist(server.url, server.accessToken, videoUserUUID, reason) 247 await server.blacklist.add({ videoId: videoUserUUID, reason })
253 248
254 await waitJobs(server) 249 await waitJobs(server)
255 expect(emails).to.have.lengthOf(6) 250 expect(emails).to.have.lengthOf(6)
@@ -267,7 +262,7 @@ describe('Test emails', function () {
267 it('Should send the notification email', async function () { 262 it('Should send the notification email', async function () {
268 this.timeout(10000) 263 this.timeout(10000)
269 264
270 await removeVideoFromBlacklist(server.url, server.accessToken, videoUserUUID) 265 await server.blacklist.remove({ videoId: videoUserUUID })
271 266
272 await waitJobs(server) 267 await waitJobs(server)
273 expect(emails).to.have.lengthOf(7) 268 expect(emails).to.have.lengthOf(7)
@@ -292,7 +287,7 @@ describe('Test emails', function () {
292 it('Should ask to send the verification email', async function () { 287 it('Should ask to send the verification email', async function () {
293 this.timeout(10000) 288 this.timeout(10000)
294 289
295 await askSendVerifyEmail(server.url, 'user_1@example.com') 290 await server.users.askSendVerifyEmail({ email: 'user_1@example.com' })
296 291
297 await waitJobs(server) 292 await waitJobs(server)
298 expect(emails).to.have.lengthOf(8) 293 expect(emails).to.have.lengthOf(8)
@@ -318,11 +313,16 @@ describe('Test emails', function () {
318 }) 313 })
319 314
320 it('Should not verify the email with an invalid verification string', async function () { 315 it('Should not verify the email with an invalid verification string', async function () {
321 await verifyEmail(server.url, userId, verificationString + 'b', false, HttpStatusCode.FORBIDDEN_403) 316 await server.users.verifyEmail({
317 userId,
318 verificationString: verificationString + 'b',
319 isPendingEmail: false,
320 expectedStatus: HttpStatusCode.FORBIDDEN_403
321 })
322 }) 322 })
323 323
324 it('Should verify the email', async function () { 324 it('Should verify the email', async function () {
325 await verifyEmail(server.url, userId, verificationString) 325 await server.users.verifyEmail({ userId, verificationString })
326 }) 326 })
327 }) 327 })
328 328
diff --git a/server/tests/api/server/follow-constraints.ts b/server/tests/api/server/follow-constraints.ts
index 3f2f71f46..471f5d8d0 100644
--- a/server/tests/api/server/follow-constraints.ts
+++ b/server/tests/api/server/follow-constraints.ts
@@ -1,56 +1,41 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { 4import * as chai from 'chai'
6 cleanupTests, 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
7 doubleFollow, 6import { HttpStatusCode, PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
8 flushAndRunMultipleServers,
9 getAccountVideos,
10 getVideo,
11 getVideoChannelVideos,
12 getVideoWithToken,
13 ServerInfo,
14 setAccessTokensToServers,
15 uploadVideo
16} from '../../../../shared/extra-utils'
17import { unfollow } from '../../../../shared/extra-utils/server/follows'
18import { userLogin } from '../../../../shared/extra-utils/users/login'
19import { createUser } from '../../../../shared/extra-utils/users/users'
20import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
21import { PeerTubeProblemDocument, ServerErrorCode } from '@shared/models'
22 7
23const expect = chai.expect 8const expect = chai.expect
24 9
25describe('Test follow constraints', function () { 10describe('Test follow constraints', function () {
26 let servers: ServerInfo[] = [] 11 let servers: PeerTubeServer[] = []
27 let video1UUID: string 12 let video1UUID: string
28 let video2UUID: string 13 let video2UUID: string
29 let userAccessToken: string 14 let userToken: string
30 15
31 before(async function () { 16 before(async function () {
32 this.timeout(90000) 17 this.timeout(90000)
33 18
34 servers = await flushAndRunMultipleServers(2) 19 servers = await createMultipleServers(2)
35 20
36 // Get the access tokens 21 // Get the access tokens
37 await setAccessTokensToServers(servers) 22 await setAccessTokensToServers(servers)
38 23
39 { 24 {
40 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' }) 25 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video server 1' } })
41 video1UUID = res.body.video.uuid 26 video1UUID = uuid
42 } 27 }
43 { 28 {
44 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' }) 29 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video server 2' } })
45 video2UUID = res.body.video.uuid 30 video2UUID = uuid
46 } 31 }
47 32
48 const user = { 33 const user = {
49 username: 'user1', 34 username: 'user1',
50 password: 'super_password' 35 password: 'super_password'
51 } 36 }
52 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 37 await servers[0].users.create({ username: user.username, password: user.password })
53 userAccessToken = await userLogin(servers[0], user) 38 userToken = await servers[0].login.getAccessToken(user)
54 39
55 await doubleFollow(servers[0], servers[1]) 40 await doubleFollow(servers[0], servers[1])
56 }) 41 })
@@ -60,81 +45,81 @@ describe('Test follow constraints', function () {
60 describe('With an unlogged user', function () { 45 describe('With an unlogged user', function () {
61 46
62 it('Should get the local video', async function () { 47 it('Should get the local video', async function () {
63 await getVideo(servers[0].url, video1UUID, HttpStatusCode.OK_200) 48 await servers[0].videos.get({ id: video1UUID })
64 }) 49 })
65 50
66 it('Should get the remote video', async function () { 51 it('Should get the remote video', async function () {
67 await getVideo(servers[0].url, video2UUID, HttpStatusCode.OK_200) 52 await servers[0].videos.get({ id: video2UUID })
68 }) 53 })
69 54
70 it('Should list local account videos', async function () { 55 it('Should list local account videos', async function () {
71 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) 56 const { total, data } = await servers[0].videos.listByAccount({ handle: 'root@localhost:' + servers[0].port })
72 57
73 expect(res.body.total).to.equal(1) 58 expect(total).to.equal(1)
74 expect(res.body.data).to.have.lengthOf(1) 59 expect(data).to.have.lengthOf(1)
75 }) 60 })
76 61
77 it('Should list remote account videos', async function () { 62 it('Should list remote account videos', async function () {
78 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) 63 const { total, data } = await servers[0].videos.listByAccount({ handle: 'root@localhost:' + servers[1].port })
79 64
80 expect(res.body.total).to.equal(1) 65 expect(total).to.equal(1)
81 expect(res.body.data).to.have.lengthOf(1) 66 expect(data).to.have.lengthOf(1)
82 }) 67 })
83 68
84 it('Should list local channel videos', async function () { 69 it('Should list local channel videos', async function () {
85 const videoChannelName = 'root_channel@localhost:' + servers[0].port 70 const handle = 'root_channel@localhost:' + servers[0].port
86 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 71 const { total, data } = await servers[0].videos.listByChannel({ handle })
87 72
88 expect(res.body.total).to.equal(1) 73 expect(total).to.equal(1)
89 expect(res.body.data).to.have.lengthOf(1) 74 expect(data).to.have.lengthOf(1)
90 }) 75 })
91 76
92 it('Should list remote channel videos', async function () { 77 it('Should list remote channel videos', async function () {
93 const videoChannelName = 'root_channel@localhost:' + servers[1].port 78 const handle = 'root_channel@localhost:' + servers[1].port
94 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 79 const { total, data } = await servers[0].videos.listByChannel({ handle })
95 80
96 expect(res.body.total).to.equal(1) 81 expect(total).to.equal(1)
97 expect(res.body.data).to.have.lengthOf(1) 82 expect(data).to.have.lengthOf(1)
98 }) 83 })
99 }) 84 })
100 85
101 describe('With a logged user', function () { 86 describe('With a logged user', function () {
102 it('Should get the local video', async function () { 87 it('Should get the local video', async function () {
103 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, HttpStatusCode.OK_200) 88 await servers[0].videos.getWithToken({ token: userToken, id: video1UUID })
104 }) 89 })
105 90
106 it('Should get the remote video', async function () { 91 it('Should get the remote video', async function () {
107 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, HttpStatusCode.OK_200) 92 await servers[0].videos.getWithToken({ token: userToken, id: video2UUID })
108 }) 93 })
109 94
110 it('Should list local account videos', async function () { 95 it('Should list local account videos', async function () {
111 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) 96 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[0].port })
112 97
113 expect(res.body.total).to.equal(1) 98 expect(total).to.equal(1)
114 expect(res.body.data).to.have.lengthOf(1) 99 expect(data).to.have.lengthOf(1)
115 }) 100 })
116 101
117 it('Should list remote account videos', async function () { 102 it('Should list remote account videos', async function () {
118 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) 103 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[1].port })
119 104
120 expect(res.body.total).to.equal(1) 105 expect(total).to.equal(1)
121 expect(res.body.data).to.have.lengthOf(1) 106 expect(data).to.have.lengthOf(1)
122 }) 107 })
123 108
124 it('Should list local channel videos', async function () { 109 it('Should list local channel videos', async function () {
125 const videoChannelName = 'root_channel@localhost:' + servers[0].port 110 const handle = 'root_channel@localhost:' + servers[0].port
126 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 111 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
127 112
128 expect(res.body.total).to.equal(1) 113 expect(total).to.equal(1)
129 expect(res.body.data).to.have.lengthOf(1) 114 expect(data).to.have.lengthOf(1)
130 }) 115 })
131 116
132 it('Should list remote channel videos', async function () { 117 it('Should list remote channel videos', async function () {
133 const videoChannelName = 'root_channel@localhost:' + servers[1].port 118 const handle = 'root_channel@localhost:' + servers[1].port
134 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 119 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
135 120
136 expect(res.body.total).to.equal(1) 121 expect(total).to.equal(1)
137 expect(res.body.data).to.have.lengthOf(1) 122 expect(data).to.have.lengthOf(1)
138 }) 123 })
139 }) 124 })
140 }) 125 })
@@ -144,19 +129,18 @@ describe('Test follow constraints', function () {
144 before(async function () { 129 before(async function () {
145 this.timeout(30000) 130 this.timeout(30000)
146 131
147 await unfollow(servers[0].url, servers[0].accessToken, servers[1]) 132 await servers[0].follows.unfollow({ target: servers[1] })
148 }) 133 })
149 134
150 describe('With an unlogged user', function () { 135 describe('With an unlogged user', function () {
151 136
152 it('Should get the local video', async function () { 137 it('Should get the local video', async function () {
153 await getVideo(servers[0].url, video1UUID, HttpStatusCode.OK_200) 138 await servers[0].videos.get({ id: video1UUID })
154 }) 139 })
155 140
156 it('Should not get the remote video', async function () { 141 it('Should not get the remote video', async function () {
157 const res = await getVideo(servers[0].url, video2UUID, HttpStatusCode.FORBIDDEN_403) 142 const body = await servers[0].videos.get({ id: video2UUID, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
158 143 const error = body as unknown as PeerTubeProblemDocument
159 const error = res.body as PeerTubeProblemDocument
160 144
161 const doc = 'https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/does_not_respect_follow_constraints' 145 const doc = 'https://docs.joinpeertube.org/api-rest-reference.html#section/Errors/does_not_respect_follow_constraints'
162 expect(error.type).to.equal(doc) 146 expect(error.type).to.equal(doc)
@@ -171,73 +155,79 @@ describe('Test follow constraints', function () {
171 }) 155 })
172 156
173 it('Should list local account videos', async function () { 157 it('Should list local account videos', async function () {
174 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) 158 const { total, data } = await servers[0].videos.listByAccount({
159 token: null,
160 handle: 'root@localhost:' + servers[0].port
161 })
175 162
176 expect(res.body.total).to.equal(1) 163 expect(total).to.equal(1)
177 expect(res.body.data).to.have.lengthOf(1) 164 expect(data).to.have.lengthOf(1)
178 }) 165 })
179 166
180 it('Should not list remote account videos', async function () { 167 it('Should not list remote account videos', async function () {
181 const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) 168 const { total, data } = await servers[0].videos.listByAccount({
169 token: null,
170 handle: 'root@localhost:' + servers[1].port
171 })
182 172
183 expect(res.body.total).to.equal(0) 173 expect(total).to.equal(0)
184 expect(res.body.data).to.have.lengthOf(0) 174 expect(data).to.have.lengthOf(0)
185 }) 175 })
186 176
187 it('Should list local channel videos', async function () { 177 it('Should list local channel videos', async function () {
188 const videoChannelName = 'root_channel@localhost:' + servers[0].port 178 const handle = 'root_channel@localhost:' + servers[0].port
189 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 179 const { total, data } = await servers[0].videos.listByChannel({ token: null, handle })
190 180
191 expect(res.body.total).to.equal(1) 181 expect(total).to.equal(1)
192 expect(res.body.data).to.have.lengthOf(1) 182 expect(data).to.have.lengthOf(1)
193 }) 183 })
194 184
195 it('Should not list remote channel videos', async function () { 185 it('Should not list remote channel videos', async function () {
196 const videoChannelName = 'root_channel@localhost:' + servers[1].port 186 const handle = 'root_channel@localhost:' + servers[1].port
197 const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) 187 const { total, data } = await servers[0].videos.listByChannel({ token: null, handle })
198 188
199 expect(res.body.total).to.equal(0) 189 expect(total).to.equal(0)
200 expect(res.body.data).to.have.lengthOf(0) 190 expect(data).to.have.lengthOf(0)
201 }) 191 })
202 }) 192 })
203 193
204 describe('With a logged user', function () { 194 describe('With a logged user', function () {
205 it('Should get the local video', async function () { 195 it('Should get the local video', async function () {
206 await getVideoWithToken(servers[0].url, userAccessToken, video1UUID, HttpStatusCode.OK_200) 196 await servers[0].videos.getWithToken({ token: userToken, id: video1UUID })
207 }) 197 })
208 198
209 it('Should get the remote video', async function () { 199 it('Should get the remote video', async function () {
210 await getVideoWithToken(servers[0].url, userAccessToken, video2UUID, HttpStatusCode.OK_200) 200 await servers[0].videos.getWithToken({ token: userToken, id: video2UUID })
211 }) 201 })
212 202
213 it('Should list local account videos', async function () { 203 it('Should list local account videos', async function () {
214 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) 204 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[0].port })
215 205
216 expect(res.body.total).to.equal(1) 206 expect(total).to.equal(1)
217 expect(res.body.data).to.have.lengthOf(1) 207 expect(data).to.have.lengthOf(1)
218 }) 208 })
219 209
220 it('Should list remote account videos', async function () { 210 it('Should list remote account videos', async function () {
221 const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) 211 const { total, data } = await servers[0].videos.listByAccount({ token: userToken, handle: 'root@localhost:' + servers[1].port })
222 212
223 expect(res.body.total).to.equal(1) 213 expect(total).to.equal(1)
224 expect(res.body.data).to.have.lengthOf(1) 214 expect(data).to.have.lengthOf(1)
225 }) 215 })
226 216
227 it('Should list local channel videos', async function () { 217 it('Should list local channel videos', async function () {
228 const videoChannelName = 'root_channel@localhost:' + servers[0].port 218 const handle = 'root_channel@localhost:' + servers[0].port
229 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 219 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
230 220
231 expect(res.body.total).to.equal(1) 221 expect(total).to.equal(1)
232 expect(res.body.data).to.have.lengthOf(1) 222 expect(data).to.have.lengthOf(1)
233 }) 223 })
234 224
235 it('Should list remote channel videos', async function () { 225 it('Should list remote channel videos', async function () {
236 const videoChannelName = 'root_channel@localhost:' + servers[1].port 226 const handle = 'root_channel@localhost:' + servers[1].port
237 const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) 227 const { total, data } = await servers[0].videos.listByChannel({ token: userToken, handle })
238 228
239 expect(res.body.total).to.equal(1) 229 expect(total).to.equal(1)
240 expect(res.body.data).to.have.lengthOf(1) 230 expect(data).to.have.lengthOf(1)
241 }) 231 })
242 }) 232 })
243 }) 233 })
diff --git a/server/tests/api/server/follows-moderation.ts b/server/tests/api/server/follows-moderation.ts
index 73c212a32..921f51043 100644
--- a/server/tests/api/server/follows-moderation.ts
+++ b/server/tests/api/server/follows-moderation.ts
@@ -1,77 +1,66 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 acceptFollower,
7 cleanupTests, 6 cleanupTests,
8 flushAndRunMultipleServers, 7 createMultipleServers,
9 ServerInfo, 8 FollowsCommand,
9 PeerTubeServer,
10 setAccessTokensToServers, 10 setAccessTokensToServers,
11 updateCustomSubConfig 11 waitJobs
12} from '../../../../shared/extra-utils/index' 12} from '@shared/extra-utils'
13import {
14 follow,
15 getFollowersListPaginationAndSort,
16 getFollowingListPaginationAndSort,
17 rejectFollower,
18 removeFollower
19} from '../../../../shared/extra-utils/server/follows'
20import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
21import { ActorFollow } from '../../../../shared/models/actors'
22 13
23const expect = chai.expect 14const expect = chai.expect
24 15
25async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'accepted') { 16async function checkServer1And2HasFollowers (servers: PeerTubeServer[], state = 'accepted') {
26 { 17 const fns = [
27 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 18 servers[0].follows.getFollowings.bind(servers[0].follows),
28 expect(res.body.total).to.equal(1) 19 servers[1].follows.getFollowers.bind(servers[1].follows)
29 20 ]
30 const follow = res.body.data[0] as ActorFollow
31 expect(follow.state).to.equal(state)
32 expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube')
33 expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube')
34 }
35 21
36 { 22 for (const fn of fns) {
37 const res = await getFollowersListPaginationAndSort({ url: servers[1].url, start: 0, count: 5, sort: 'createdAt' }) 23 const body = await fn({ start: 0, count: 5, sort: 'createdAt' })
38 expect(res.body.total).to.equal(1) 24 expect(body.total).to.equal(1)
39 25
40 const follow = res.body.data[0] as ActorFollow 26 const follow = body.data[0]
41 expect(follow.state).to.equal(state) 27 expect(follow.state).to.equal(state)
42 expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube') 28 expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube')
43 expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube') 29 expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube')
44 } 30 }
45} 31}
46 32
47async function checkNoFollowers (servers: ServerInfo[]) { 33async function checkNoFollowers (servers: PeerTubeServer[]) {
48 { 34 const fns = [
49 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 35 servers[0].follows.getFollowings.bind(servers[0].follows),
50 expect(res.body.total).to.equal(0) 36 servers[1].follows.getFollowers.bind(servers[1].follows)
51 } 37 ]
52 38
53 { 39 for (const fn of fns) {
54 const res = await getFollowersListPaginationAndSort({ url: servers[1].url, start: 0, count: 5, sort: 'createdAt' }) 40 const body = await fn({ start: 0, count: 5, sort: 'createdAt' })
55 expect(res.body.total).to.equal(0) 41 expect(body.total).to.equal(0)
56 } 42 }
57} 43}
58 44
59describe('Test follows moderation', function () { 45describe('Test follows moderation', function () {
60 let servers: ServerInfo[] = [] 46 let servers: PeerTubeServer[] = []
47 let commands: FollowsCommand[]
61 48
62 before(async function () { 49 before(async function () {
63 this.timeout(30000) 50 this.timeout(30000)
64 51
65 servers = await flushAndRunMultipleServers(3) 52 servers = await createMultipleServers(3)
66 53
67 // Get the access tokens 54 // Get the access tokens
68 await setAccessTokensToServers(servers) 55 await setAccessTokensToServers(servers)
56
57 commands = servers.map(s => s.follows)
69 }) 58 })
70 59
71 it('Should have server 1 following server 2', async function () { 60 it('Should have server 1 following server 2', async function () {
72 this.timeout(30000) 61 this.timeout(30000)
73 62
74 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 63 await commands[0].follow({ hosts: [ servers[1].url ] })
75 64
76 await waitJobs(servers) 65 await waitJobs(servers)
77 }) 66 })
@@ -83,7 +72,7 @@ describe('Test follows moderation', function () {
83 it('Should remove follower on server 2', async function () { 72 it('Should remove follower on server 2', async function () {
84 this.timeout(10000) 73 this.timeout(10000)
85 74
86 await removeFollower(servers[1].url, servers[1].accessToken, servers[0]) 75 await commands[1].removeFollower({ follower: servers[0] })
87 76
88 await waitJobs(servers) 77 await waitJobs(servers)
89 }) 78 })
@@ -104,9 +93,9 @@ describe('Test follows moderation', function () {
104 } 93 }
105 } 94 }
106 95
107 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig) 96 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
108 97
109 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 98 await commands[0].follow({ hosts: [ servers[1].url ] })
110 await waitJobs(servers) 99 await waitJobs(servers)
111 100
112 await checkNoFollowers(servers) 101 await checkNoFollowers(servers)
@@ -124,9 +113,9 @@ describe('Test follows moderation', function () {
124 } 113 }
125 } 114 }
126 115
127 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig) 116 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
128 117
129 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 118 await commands[0].follow({ hosts: [ servers[1].url ] })
130 await waitJobs(servers) 119 await waitJobs(servers)
131 120
132 await checkServer1And2HasFollowers(servers) 121 await checkServer1And2HasFollowers(servers)
@@ -135,7 +124,7 @@ describe('Test follows moderation', function () {
135 it('Should manually approve followers', async function () { 124 it('Should manually approve followers', async function () {
136 this.timeout(20000) 125 this.timeout(20000)
137 126
138 await removeFollower(servers[1].url, servers[1].accessToken, servers[0]) 127 await commands[1].removeFollower({ follower: servers[0] })
139 await waitJobs(servers) 128 await waitJobs(servers)
140 129
141 const subConfig = { 130 const subConfig = {
@@ -147,10 +136,10 @@ describe('Test follows moderation', function () {
147 } 136 }
148 } 137 }
149 138
150 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, subConfig) 139 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
151 await updateCustomSubConfig(servers[2].url, servers[2].accessToken, subConfig) 140 await servers[2].config.updateCustomSubConfig({ newConfig: subConfig })
152 141
153 await follow(servers[0].url, [ servers[1].url ], servers[0].accessToken) 142 await commands[0].follow({ hosts: [ servers[1].url ] })
154 await waitJobs(servers) 143 await waitJobs(servers)
155 144
156 await checkServer1And2HasFollowers(servers, 'pending') 145 await checkServer1And2HasFollowers(servers, 'pending')
@@ -159,7 +148,7 @@ describe('Test follows moderation', function () {
159 it('Should accept a follower', async function () { 148 it('Should accept a follower', async function () {
160 this.timeout(10000) 149 this.timeout(10000)
161 150
162 await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@localhost:' + servers[0].port) 151 await commands[1].acceptFollower({ follower: 'peertube@localhost:' + servers[0].port })
163 await waitJobs(servers) 152 await waitJobs(servers)
164 153
165 await checkServer1And2HasFollowers(servers) 154 await checkServer1And2HasFollowers(servers)
@@ -168,32 +157,32 @@ describe('Test follows moderation', function () {
168 it('Should reject another follower', async function () { 157 it('Should reject another follower', async function () {
169 this.timeout(20000) 158 this.timeout(20000)
170 159
171 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) 160 await commands[0].follow({ hosts: [ servers[2].url ] })
172 await waitJobs(servers) 161 await waitJobs(servers)
173 162
174 { 163 {
175 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 164 const body = await commands[0].getFollowings({ start: 0, count: 5, sort: 'createdAt' })
176 expect(res.body.total).to.equal(2) 165 expect(body.total).to.equal(2)
177 } 166 }
178 167
179 { 168 {
180 const res = await getFollowersListPaginationAndSort({ url: servers[1].url, start: 0, count: 5, sort: 'createdAt' }) 169 const body = await commands[1].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
181 expect(res.body.total).to.equal(1) 170 expect(body.total).to.equal(1)
182 } 171 }
183 172
184 { 173 {
185 const res = await getFollowersListPaginationAndSort({ url: servers[2].url, start: 0, count: 5, sort: 'createdAt' }) 174 const body = await commands[2].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
186 expect(res.body.total).to.equal(1) 175 expect(body.total).to.equal(1)
187 } 176 }
188 177
189 await rejectFollower(servers[2].url, servers[2].accessToken, 'peertube@localhost:' + servers[0].port) 178 await commands[2].rejectFollower({ follower: 'peertube@localhost:' + servers[0].port })
190 await waitJobs(servers) 179 await waitJobs(servers)
191 180
192 await checkServer1And2HasFollowers(servers) 181 await checkServer1And2HasFollowers(servers)
193 182
194 { 183 {
195 const res = await getFollowersListPaginationAndSort({ url: servers[2].url, start: 0, count: 5, sort: 'createdAt' }) 184 const body = await commands[2].getFollowers({ start: 0, count: 5, sort: 'createdAt' })
196 expect(res.body.total).to.equal(0) 185 expect(body.total).to.equal(0)
197 } 186 }
198 }) 187 })
199 188
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index 9e5aa00c7..832ba561a 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -3,325 +3,374 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addVideoCommentReply,
7 addVideoCommentThread,
8 cleanupTests, 6 cleanupTests,
9 completeVideoCheck, 7 completeVideoCheck,
10 createUser, 8 createMultipleServers,
11 createVideoCaption,
12 dateIsValid, 9 dateIsValid,
13 deleteVideoComment,
14 expectAccountFollows, 10 expectAccountFollows,
15 flushAndRunMultipleServers, 11 expectChannelsFollows,
16 follow, 12 PeerTubeServer,
17 getFollowersListPaginationAndSort,
18 getFollowingListPaginationAndSort,
19 getVideoCommentThreads,
20 getVideosList,
21 getVideoThreadComments,
22 listVideoCaptions,
23 rateVideo,
24 ServerInfo,
25 setAccessTokensToServers, 13 setAccessTokensToServers,
26 testCaptionFile, 14 testCaptionFile,
27 unfollow,
28 uploadVideo,
29 userLogin,
30 waitJobs 15 waitJobs
31} from '@shared/extra-utils' 16} from '@shared/extra-utils'
32import { Video, VideoCaption, VideoComment, VideoCommentThreadTree, VideoPrivacy } from '@shared/models' 17import { VideoCreateResult, VideoPrivacy } from '@shared/models'
33 18
34const expect = chai.expect 19const expect = chai.expect
35 20
36describe('Test follows', function () { 21describe('Test follows', function () {
37 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
38 23
39 before(async function () { 24 before(async function () {
40 this.timeout(30000) 25 this.timeout(30000)
41 26
42 servers = await flushAndRunMultipleServers(3) 27 servers = await createMultipleServers(3)
43 28
44 // Get the access tokens 29 // Get the access tokens
45 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
46 }) 31 })
47 32
48 it('Should not have followers', async function () { 33 describe('Data propagation after follow', function () {
49 for (const server of servers) {
50 const res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
51 const follows = res.body.data
52 34
53 expect(res.body.total).to.equal(0) 35 it('Should not have followers/followings', async function () {
54 expect(follows).to.be.an('array') 36 for (const server of servers) {
55 expect(follows.length).to.equal(0) 37 const bodies = await Promise.all([
56 } 38 server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' }),
57 }) 39 server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' })
58 40 ])
59 it('Should not have following', async function () {
60 for (const server of servers) {
61 const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
62 const follows = res.body.data
63 41
64 expect(res.body.total).to.equal(0) 42 for (const body of bodies) {
65 expect(follows).to.be.an('array') 43 expect(body.total).to.equal(0)
66 expect(follows.length).to.equal(0)
67 }
68 })
69 44
70 it('Should have server 1 following server 2 and 3', async function () { 45 const follows = body.data
71 this.timeout(30000) 46 expect(follows).to.be.an('array')
47 expect(follows).to.have.lengthOf(0)
48 }
49 }
50 })
72 51
73 await follow(servers[0].url, [ servers[1].url, servers[2].url ], servers[0].accessToken) 52 it('Should have server 1 following root account of server 2 and server 3', async function () {
53 this.timeout(30000)
74 54
75 await waitJobs(servers) 55 await servers[0].follows.follow({
76 }) 56 hosts: [ servers[2].url ],
57 handles: [ 'root@' + servers[1].host ]
58 })
77 59
78 it('Should have 2 followings on server 1', async function () { 60 await waitJobs(servers)
79 let res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 1, sort: 'createdAt' }) 61 })
80 let follows = res.body.data
81 62
82 expect(res.body.total).to.equal(2) 63 it('Should have 2 followings on server 1', async function () {
83 expect(follows).to.be.an('array') 64 const body = await servers[0].follows.getFollowings({ start: 0, count: 1, sort: 'createdAt' })
84 expect(follows.length).to.equal(1) 65 expect(body.total).to.equal(2)
85 66
86 res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 1, count: 1, sort: 'createdAt' }) 67 let follows = body.data
87 follows = follows.concat(res.body.data) 68 expect(follows).to.be.an('array')
69 expect(follows).to.have.lengthOf(1)
88 70
89 const server2Follow = follows.find(f => f.following.host === 'localhost:' + servers[1].port) 71 const body2 = await servers[0].follows.getFollowings({ start: 1, count: 1, sort: 'createdAt' })
90 const server3Follow = follows.find(f => f.following.host === 'localhost:' + servers[2].port) 72 follows = follows.concat(body2.data)
91 73
92 expect(server2Follow).to.not.be.undefined 74 const server2Follow = follows.find(f => f.following.host === servers[1].host)
93 expect(server3Follow).to.not.be.undefined 75 const server3Follow = follows.find(f => f.following.host === servers[2].host)
94 expect(server2Follow.state).to.equal('accepted')
95 expect(server3Follow.state).to.equal('accepted')
96 })
97 76
98 it('Should search/filter followings on server 1', async function () { 77 expect(server2Follow).to.not.be.undefined
99 const sort = 'createdAt' 78 expect(server2Follow.following.name).to.equal('root')
100 const start = 0 79 expect(server2Follow.state).to.equal('accepted')
101 const count = 1
102 const url = servers[0].url
103 80
104 { 81 expect(server3Follow).to.not.be.undefined
105 const search = ':' + servers[1].port 82 expect(server3Follow.following.name).to.equal('peertube')
83 expect(server3Follow.state).to.equal('accepted')
84 })
106 85
107 { 86 it('Should have 0 followings on server 2 and 3', async function () {
108 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search }) 87 for (const server of [ servers[1], servers[2] ]) {
109 const follows = res.body.data 88 const body = await server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' })
89 expect(body.total).to.equal(0)
110 90
111 expect(res.body.total).to.equal(1) 91 const follows = body.data
112 expect(follows.length).to.equal(1) 92 expect(follows).to.be.an('array')
113 expect(follows[0].following.host).to.equal('localhost:' + servers[1].port) 93 expect(follows).to.have.lengthOf(0)
114 } 94 }
95 })
115 96
116 { 97 it('Should have 1 followers on server 3', async function () {
117 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' }) 98 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' })
118 expect(res.body.total).to.equal(1) 99 expect(body.total).to.equal(1)
119 expect(res.body.data).to.have.lengthOf(1) 100
101 const follows = body.data
102 expect(follows).to.be.an('array')
103 expect(follows).to.have.lengthOf(1)
104 expect(follows[0].follower.host).to.equal('localhost:' + servers[0].port)
105 })
106
107 it('Should have 0 followers on server 1 and 2', async function () {
108 for (const server of [ servers[0], servers[1] ]) {
109 const body = await server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' })
110 expect(body.total).to.equal(0)
111
112 const follows = body.data
113 expect(follows).to.be.an('array')
114 expect(follows).to.have.lengthOf(0)
120 } 115 }
116 })
117
118 it('Should search/filter followings on server 1', async function () {
119 const sort = 'createdAt'
120 const start = 0
121 const count = 1
121 122
122 { 123 {
123 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' }) 124 const search = ':' + servers[1].port
124 expect(res.body.total).to.equal(0) 125
125 expect(res.body.data).to.have.lengthOf(0) 126 {
127 const body = await servers[0].follows.getFollowings({ start, count, sort, search })
128 expect(body.total).to.equal(1)
129
130 const follows = body.data
131 expect(follows).to.have.lengthOf(1)
132 expect(follows[0].following.host).to.equal(servers[1].host)
133 }
134
135 {
136 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted' })
137 expect(body.total).to.equal(1)
138 expect(body.data).to.have.lengthOf(1)
139 }
140
141 {
142 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted', actorType: 'Person' })
143 expect(body.total).to.equal(1)
144 expect(body.data).to.have.lengthOf(1)
145 }
146
147 {
148 const body = await servers[0].follows.getFollowings({
149 start,
150 count,
151 sort,
152 search,
153 state: 'accepted',
154 actorType: 'Application'
155 })
156 expect(body.total).to.equal(0)
157 expect(body.data).to.have.lengthOf(0)
158 }
159
160 {
161 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'pending' })
162 expect(body.total).to.equal(0)
163 expect(body.data).to.have.lengthOf(0)
164 }
126 } 165 }
127 166
128 { 167 {
129 const res = await getFollowingListPaginationAndSort({ 168 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'root' })
130 url, 169 expect(body.total).to.equal(1)
131 start, 170 expect(body.data).to.have.lengthOf(1)
132 count,
133 sort,
134 search,
135 state: 'accepted',
136 actorType: 'Application'
137 })
138 expect(res.body.total).to.equal(1)
139 expect(res.body.data).to.have.lengthOf(1)
140 } 171 }
141 172
142 { 173 {
143 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'pending' }) 174 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'bla' })
144 expect(res.body.total).to.equal(0) 175 expect(body.total).to.equal(0)
145 expect(res.body.data).to.have.lengthOf(0) 176
177 expect(body.data).to.have.lengthOf(0)
146 } 178 }
147 } 179 })
148 180
149 { 181 it('Should search/filter followers on server 2', async function () {
150 const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search: 'bla' }) 182 const start = 0
151 const follows = res.body.data 183 const count = 5
184 const sort = 'createdAt'
152 185
153 expect(res.body.total).to.equal(0) 186 {
154 expect(follows.length).to.equal(0) 187 const search = servers[0].port + ''
155 }
156 })
157 188
158 it('Should have 0 followings on server 2 and 3', async function () { 189 {
159 for (const server of [ servers[1], servers[2] ]) { 190 const body = await servers[2].follows.getFollowers({ start, count, sort, search })
160 const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' }) 191 expect(body.total).to.equal(1)
161 const follows = res.body.data
162 192
163 expect(res.body.total).to.equal(0) 193 const follows = body.data
164 expect(follows).to.be.an('array') 194 expect(follows).to.have.lengthOf(1)
165 expect(follows.length).to.equal(0) 195 expect(follows[0].following.host).to.equal(servers[2].host)
166 } 196 }
167 })
168 197
169 it('Should have 1 followers on server 2 and 3', async function () { 198 {
170 for (const server of [ servers[1], servers[2] ]) { 199 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted' })
171 const res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 1, sort: 'createdAt' }) 200 expect(body.total).to.equal(1)
201 expect(body.data).to.have.lengthOf(1)
202 }
172 203
173 const follows = res.body.data 204 {
174 expect(res.body.total).to.equal(1) 205 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted', actorType: 'Person' })
175 expect(follows).to.be.an('array') 206 expect(body.total).to.equal(0)
176 expect(follows.length).to.equal(1) 207 expect(body.data).to.have.lengthOf(0)
177 expect(follows[0].follower.host).to.equal('localhost:' + servers[0].port) 208 }
178 }
179 })
180 209
181 it('Should search/filter followers on server 2', async function () { 210 {
182 const url = servers[2].url 211 const body = await servers[2].follows.getFollowers({
183 const start = 0 212 start,
184 const count = 5 213 count,
185 const sort = 'createdAt' 214 sort,
215 search,
216 state: 'accepted',
217 actorType: 'Application'
218 })
219 expect(body.total).to.equal(1)
220 expect(body.data).to.have.lengthOf(1)
221 }
186 222
187 { 223 {
188 const search = servers[0].port + '' 224 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'pending' })
225 expect(body.total).to.equal(0)
226 expect(body.data).to.have.lengthOf(0)
227 }
228 }
189 229
190 { 230 {
191 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search }) 231 const body = await servers[2].follows.getFollowers({ start, count, sort, search: 'bla' })
192 const follows = res.body.data 232 expect(body.total).to.equal(0)
193 233
194 expect(res.body.total).to.equal(1) 234 const follows = body.data
195 expect(follows.length).to.equal(1) 235 expect(follows).to.have.lengthOf(0)
196 expect(follows[0].following.host).to.equal('localhost:' + servers[2].port)
197 } 236 }
237 })
198 238
199 { 239 it('Should have the correct follows counts', async function () {
200 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' }) 240 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 })
201 expect(res.body.total).to.equal(1) 241 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
202 expect(res.body.data).to.have.lengthOf(1) 242 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
203 }
204 243
205 { 244 // Server 2 and 3 does not know server 1 follow another server (there was not a refresh)
206 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' }) 245 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
207 expect(res.body.total).to.equal(0) 246 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
208 expect(res.body.data).to.have.lengthOf(0) 247 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
209 }
210 248
211 { 249 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
212 const res = await getFollowersListPaginationAndSort({ 250 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
213 url, 251 })
214 start,
215 count,
216 sort,
217 search,
218 state: 'accepted',
219 actorType: 'Application'
220 })
221 expect(res.body.total).to.equal(1)
222 expect(res.body.data).to.have.lengthOf(1)
223 }
224 252
225 { 253 it('Should unfollow server 3 on server 1', async function () {
226 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'pending' }) 254 this.timeout(15000)
227 expect(res.body.total).to.equal(0)
228 expect(res.body.data).to.have.lengthOf(0)
229 }
230 }
231 255
232 { 256 await servers[0].follows.unfollow({ target: servers[2] })
233 const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search: 'bla' })
234 const follows = res.body.data
235 257
236 expect(res.body.total).to.equal(0) 258 await waitJobs(servers)
237 expect(follows.length).to.equal(0) 259 })
238 }
239 })
240 260
241 it('Should have 0 followers on server 1', async function () { 261 it('Should not follow server 3 on server 1 anymore', async function () {
242 const res = await getFollowersListPaginationAndSort({ url: servers[0].url, start: 0, count: 5, sort: 'createdAt' }) 262 const body = await servers[0].follows.getFollowings({ start: 0, count: 2, sort: 'createdAt' })
243 const follows = res.body.data 263 expect(body.total).to.equal(1)
244 264
245 expect(res.body.total).to.equal(0) 265 const follows = body.data
246 expect(follows).to.be.an('array') 266 expect(follows).to.be.an('array')
247 expect(follows.length).to.equal(0) 267 expect(follows).to.have.lengthOf(1)
248 })
249 268
250 it('Should have the correct follows counts', async function () { 269 expect(follows[0].following.host).to.equal(servers[1].host)
251 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) 270 })
252 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0)
253 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0)
254 271
255 // Server 2 and 3 does not know server 1 follow another server (there was not a refresh) 272 it('Should not have server 1 as follower on server 3 anymore', async function () {
256 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) 273 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' })
257 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) 274 expect(body.total).to.equal(0)
258 275
259 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 1) 276 const follows = body.data
260 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) 277 expect(follows).to.be.an('array')
261 }) 278 expect(follows).to.have.lengthOf(0)
279 })
262 280
263 it('Should unfollow server 3 on server 1', async function () { 281 it('Should have the correct follows counts after the unfollow', async function () {
264 this.timeout(5000) 282 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
283 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
284 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 })
265 285
266 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 286 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
287 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
288 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
267 289
268 await waitJobs(servers) 290 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 0 })
269 }) 291 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 })
292 })
270 293
271 it('Should not follow server 3 on server 1 anymore', async function () { 294 it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () {
272 const res = await getFollowingListPaginationAndSort({ url: servers[0].url, start: 0, count: 2, sort: 'createdAt' }) 295 this.timeout(60000)
273 const follows = res.body.data
274 296
275 expect(res.body.total).to.equal(1) 297 await servers[1].videos.upload({ attributes: { name: 'server2' } })
276 expect(follows).to.be.an('array') 298 await servers[2].videos.upload({ attributes: { name: 'server3' } })
277 expect(follows.length).to.equal(1)
278 299
279 expect(follows[0].following.host).to.equal('localhost:' + servers[1].port) 300 await waitJobs(servers)
280 })
281 301
282 it('Should not have server 1 as follower on server 3 anymore', async function () { 302 {
283 const res = await getFollowersListPaginationAndSort({ url: servers[2].url, start: 0, count: 1, sort: 'createdAt' }) 303 const { total, data } = await servers[0].videos.list()
304 expect(total).to.equal(1)
305 expect(data[0].name).to.equal('server2')
306 }
284 307
285 const follows = res.body.data 308 {
286 expect(res.body.total).to.equal(0) 309 const { total, data } = await servers[1].videos.list()
287 expect(follows).to.be.an('array') 310 expect(total).to.equal(1)
288 expect(follows.length).to.equal(0) 311 expect(data[0].name).to.equal('server2')
289 }) 312 }
313
314 {
315 const { total, data } = await servers[2].videos.list()
316 expect(total).to.equal(1)
317 expect(data[0].name).to.equal('server3')
318 }
319 })
290 320
291 it('Should have the correct follows counts 2', async function () { 321 it('Should remove account follow', async function () {
292 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 1) 322 this.timeout(15000)
293 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0)
294 323
295 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) 324 await servers[0].follows.unfollow({ target: 'root@' + servers[1].host })
296 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0)
297 325
298 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 0) 326 await waitJobs(servers)
299 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 0, 0) 327 })
300 })
301 328
302 it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () { 329 it('Should have removed the account follow', async function () {
303 this.timeout(60000) 330 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
331 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
304 332
305 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'server2' }) 333 {
306 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3' }) 334 const { total, data } = await servers[0].follows.getFollowings()
335 expect(total).to.equal(0)
336 expect(data).to.have.lengthOf(0)
337 }
307 338
308 await waitJobs(servers) 339 {
340 const { total, data } = await servers[0].videos.list()
341 expect(total).to.equal(0)
342 expect(data).to.have.lengthOf(0)
343 }
344 })
309 345
310 let res = await getVideosList(servers[0].url) 346 it('Should follow a channel', async function () {
311 expect(res.body.total).to.equal(1) 347 this.timeout(15000)
312 expect(res.body.data[0].name).to.equal('server2')
313 348
314 res = await getVideosList(servers[1].url) 349 await servers[0].follows.follow({
315 expect(res.body.total).to.equal(1) 350 handles: [ 'root_channel@' + servers[1].host ]
316 expect(res.body.data[0].name).to.equal('server2') 351 })
317 352
318 res = await getVideosList(servers[2].url) 353 await waitJobs(servers)
319 expect(res.body.total).to.equal(1) 354
320 expect(res.body.data[0].name).to.equal('server3') 355 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
356 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
357
358 {
359 const { total, data } = await servers[0].follows.getFollowings()
360 expect(total).to.equal(1)
361 expect(data).to.have.lengthOf(1)
362 }
363
364 {
365 const { total, data } = await servers[0].videos.list()
366 expect(total).to.equal(1)
367 expect(data).to.have.lengthOf(1)
368 }
369 })
321 }) 370 })
322 371
323 describe('Should propagate data on a new following', function () { 372 describe('Should propagate data on a new server follow', function () {
324 let video4: Video 373 let video4: VideoCreateResult
325 374
326 before(async function () { 375 before(async function () {
327 this.timeout(50000) 376 this.timeout(50000)
@@ -334,100 +383,75 @@ describe('Test follows', function () {
334 tags: [ 'tag1', 'tag2', 'tag3' ] 383 tags: [ 'tag1', 'tag2', 'tag3' ]
335 } 384 }
336 385
337 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-2' }) 386 await servers[2].videos.upload({ attributes: { name: 'server3-2' } })
338 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-3' }) 387 await servers[2].videos.upload({ attributes: { name: 'server3-3' } })
339 await uploadVideo(servers[2].url, servers[2].accessToken, video4Attributes) 388 video4 = await servers[2].videos.upload({ attributes: video4Attributes })
340 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-5' }) 389 await servers[2].videos.upload({ attributes: { name: 'server3-5' } })
341 await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-6' }) 390 await servers[2].videos.upload({ attributes: { name: 'server3-6' } })
342 391
343 { 392 {
344 const user = { username: 'captain', password: 'password' } 393 const userAccessToken = await servers[2].users.generateUserAndToken('captain')
345 await createUser({ url: servers[2].url, accessToken: servers[2].accessToken, username: user.username, password: user.password })
346 const userAccessToken = await userLogin(servers[2], user)
347
348 const resVideos = await getVideosList(servers[2].url)
349 video4 = resVideos.body.data.find(v => v.name === 'server3-4')
350 394
351 { 395 await servers[2].videos.rate({ id: video4.id, rating: 'like' })
352 await rateVideo(servers[2].url, servers[2].accessToken, video4.id, 'like') 396 await servers[2].videos.rate({ token: userAccessToken, id: video4.id, rating: 'dislike' })
353 await rateVideo(servers[2].url, userAccessToken, video4.id, 'dislike') 397 }
354 }
355
356 {
357 {
358 const text = 'my super first comment'
359 const res = await addVideoCommentThread(servers[2].url, servers[2].accessToken, video4.id, text)
360 const threadId = res.body.comment.id
361
362 const text1 = 'my super answer to thread 1'
363 const childCommentRes = await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text1)
364 const childCommentId = childCommentRes.body.comment.id
365
366 const text2 = 'my super answer to answer of thread 1'
367 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, childCommentId, text2)
368
369 const text3 = 'my second answer to thread 1'
370 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text3)
371 }
372 398
373 { 399 {
374 const text = 'will be deleted' 400 await servers[2].comments.createThread({ videoId: video4.id, text: 'my super first comment' })
375 const res = await addVideoCommentThread(servers[2].url, servers[2].accessToken, video4.id, text)
376 const threadId = res.body.comment.id
377 401
378 const text1 = 'answer to deleted' 402 await servers[2].comments.addReplyToLastThread({ text: 'my super answer to thread 1' })
379 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text1) 403 await servers[2].comments.addReplyToLastReply({ text: 'my super answer to answer of thread 1' })
404 await servers[2].comments.addReplyToLastThread({ text: 'my second answer to thread 1' })
405 }
380 406
381 const text2 = 'will also be deleted' 407 {
382 const childCommentRes = await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, threadId, text2) 408 const { id: threadId } = await servers[2].comments.createThread({ videoId: video4.id, text: 'will be deleted' })
383 const childCommentId = childCommentRes.body.comment.id 409 await servers[2].comments.addReplyToLastThread({ text: 'answer to deleted' })
384 410
385 const text3 = 'my second answer to deleted' 411 const { id: replyId } = await servers[2].comments.addReplyToLastThread({ text: 'will also be deleted' })
386 await addVideoCommentReply(servers[2].url, servers[2].accessToken, video4.id, childCommentId, text3)
387 412
388 await deleteVideoComment(servers[2].url, servers[2].accessToken, video4.id, threadId) 413 await servers[2].comments.addReplyToLastReply({ text: 'my second answer to deleted' })
389 await deleteVideoComment(servers[2].url, servers[2].accessToken, video4.id, childCommentId)
390 }
391 }
392 414
393 { 415 await servers[2].comments.delete({ videoId: video4.id, commentId: threadId })
394 await createVideoCaption({ 416 await servers[2].comments.delete({ videoId: video4.id, commentId: replyId })
395 url: servers[2].url,
396 accessToken: servers[2].accessToken,
397 language: 'ar',
398 videoId: video4.id,
399 fixture: 'subtitle-good2.vtt'
400 })
401 }
402 } 417 }
403 418
419 await servers[2].captions.add({
420 language: 'ar',
421 videoId: video4.id,
422 fixture: 'subtitle-good2.vtt'
423 })
424
404 await waitJobs(servers) 425 await waitJobs(servers)
405 426
406 // Server 1 follows server 3 427 // Server 1 follows server 3
407 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) 428 await servers[0].follows.follow({ hosts: [ servers[2].url ] })
408 429
409 await waitJobs(servers) 430 await waitJobs(servers)
410 }) 431 })
411 432
412 it('Should have the correct follows counts 3', async function () { 433 it('Should have the correct follows counts', async function () {
413 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) 434 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 })
414 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) 435 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
415 await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0) 436 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
437 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
416 438
417 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) 439 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
418 await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) 440 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
441 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
442 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
419 443
420 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 1) 444 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
421 await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) 445 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
422 }) 446 })
423 447
424 it('Should have propagated videos', async function () { 448 it('Should have propagated videos', async function () {
425 const res = await getVideosList(servers[0].url) 449 const { total, data } = await servers[0].videos.list()
426 expect(res.body.total).to.equal(7) 450 expect(total).to.equal(7)
427 451
428 const video2 = res.body.data.find(v => v.name === 'server3-2') 452 const video2 = data.find(v => v.name === 'server3-2')
429 video4 = res.body.data.find(v => v.name === 'server3-4') 453 video4 = data.find(v => v.name === 'server3-4')
430 const video6 = res.body.data.find(v => v.name === 'server3-6') 454 const video6 = data.find(v => v.name === 'server3-6')
431 455
432 expect(video2).to.not.be.undefined 456 expect(video2).to.not.be.undefined
433 expect(video4).to.not.be.undefined 457 expect(video4).to.not.be.undefined
@@ -444,7 +468,7 @@ describe('Test follows', function () {
444 support: 'my super support text', 468 support: 'my super support text',
445 account: { 469 account: {
446 name: 'root', 470 name: 'root',
447 host: 'localhost:' + servers[2].port 471 host: servers[2].host
448 }, 472 },
449 isLocal, 473 isLocal,
450 commentsEnabled: true, 474 commentsEnabled: true,
@@ -468,33 +492,31 @@ describe('Test follows', function () {
468 } 492 }
469 ] 493 ]
470 } 494 }
471 await completeVideoCheck(servers[0].url, video4, checkAttributes) 495 await completeVideoCheck(servers[0], video4, checkAttributes)
472 }) 496 })
473 497
474 it('Should have propagated comments', async function () { 498 it('Should have propagated comments', async function () {
475 const res1 = await getVideoCommentThreads(servers[0].url, video4.id, 0, 5, 'createdAt') 499 const { total, data } = await servers[0].comments.listThreads({ videoId: video4.id, sort: 'createdAt' })
476 500
477 expect(res1.body.total).to.equal(2) 501 expect(total).to.equal(2)
478 expect(res1.body.data).to.be.an('array') 502 expect(data).to.be.an('array')
479 expect(res1.body.data).to.have.lengthOf(2) 503 expect(data).to.have.lengthOf(2)
480 504
481 { 505 {
482 const comment: VideoComment = res1.body.data[0] 506 const comment = data[0]
483 expect(comment.inReplyToCommentId).to.be.null 507 expect(comment.inReplyToCommentId).to.be.null
484 expect(comment.text).equal('my super first comment') 508 expect(comment.text).equal('my super first comment')
485 expect(comment.videoId).to.equal(video4.id) 509 expect(comment.videoId).to.equal(video4.id)
486 expect(comment.id).to.equal(comment.threadId) 510 expect(comment.id).to.equal(comment.threadId)
487 expect(comment.account.name).to.equal('root') 511 expect(comment.account.name).to.equal('root')
488 expect(comment.account.host).to.equal('localhost:' + servers[2].port) 512 expect(comment.account.host).to.equal(servers[2].host)
489 expect(comment.totalReplies).to.equal(3) 513 expect(comment.totalReplies).to.equal(3)
490 expect(dateIsValid(comment.createdAt as string)).to.be.true 514 expect(dateIsValid(comment.createdAt as string)).to.be.true
491 expect(dateIsValid(comment.updatedAt as string)).to.be.true 515 expect(dateIsValid(comment.updatedAt as string)).to.be.true
492 516
493 const threadId = comment.threadId 517 const threadId = comment.threadId
494 518
495 const res2 = await getVideoThreadComments(servers[0].url, video4.id, threadId) 519 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId })
496
497 const tree: VideoCommentThreadTree = res2.body
498 expect(tree.comment.text).equal('my super first comment') 520 expect(tree.comment.text).equal('my super first comment')
499 expect(tree.children).to.have.lengthOf(2) 521 expect(tree.children).to.have.lengthOf(2)
500 522
@@ -512,7 +534,7 @@ describe('Test follows', function () {
512 } 534 }
513 535
514 { 536 {
515 const deletedComment: VideoComment = res1.body.data[1] 537 const deletedComment = data[1]
516 expect(deletedComment).to.not.be.undefined 538 expect(deletedComment).to.not.be.undefined
517 expect(deletedComment.isDeleted).to.be.true 539 expect(deletedComment.isDeleted).to.be.true
518 expect(deletedComment.deletedAt).to.not.be.null 540 expect(deletedComment.deletedAt).to.not.be.null
@@ -522,9 +544,7 @@ describe('Test follows', function () {
522 expect(deletedComment.totalReplies).to.equal(2) 544 expect(deletedComment.totalReplies).to.equal(2)
523 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true 545 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
524 546
525 const res2 = await getVideoThreadComments(servers[0].url, video4.id, deletedComment.threadId) 547 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId: deletedComment.threadId })
526
527 const tree: VideoCommentThreadTree = res2.body
528 const [ commentRoot, deletedChildRoot ] = tree.children 548 const [ commentRoot, deletedChildRoot ] = tree.children
529 549
530 expect(deletedChildRoot).to.not.be.undefined 550 expect(deletedChildRoot).to.not.be.undefined
@@ -549,11 +569,11 @@ describe('Test follows', function () {
549 }) 569 })
550 570
551 it('Should have propagated captions', async function () { 571 it('Should have propagated captions', async function () {
552 const res = await listVideoCaptions(servers[0].url, video4.id) 572 const body = await servers[0].captions.list({ videoId: video4.id })
553 expect(res.body.total).to.equal(1) 573 expect(body.total).to.equal(1)
554 expect(res.body.data).to.have.lengthOf(1) 574 expect(body.data).to.have.lengthOf(1)
555 575
556 const caption1: VideoCaption = res.body.data[0] 576 const caption1 = body.data[0]
557 expect(caption1.language.id).to.equal('ar') 577 expect(caption1.language.id).to.equal('ar')
558 expect(caption1.language.label).to.equal('Arabic') 578 expect(caption1.language.label).to.equal('Arabic')
559 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/.+-ar.vtt$')) 579 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/.+-ar.vtt$'))
@@ -563,14 +583,39 @@ describe('Test follows', function () {
563 it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () { 583 it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () {
564 this.timeout(5000) 584 this.timeout(5000)
565 585
566 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 586 await servers[0].follows.unfollow({ target: servers[2] })
587
588 await waitJobs(servers)
589
590 const { total } = await servers[0].videos.list()
591 expect(total).to.equal(1)
592 })
593 })
594
595 describe('Should propagate data on a new channel follow', function () {
596
597 before(async function () {
598 this.timeout(60000)
599
600 await servers[2].videos.upload({ attributes: { name: 'server3-7' } })
567 601
568 await waitJobs(servers) 602 await waitJobs(servers)
569 603
570 const res = await getVideosList(servers[0].url) 604 const video = await servers[0].videos.find({ name: 'server3-7' })
571 expect(res.body.total).to.equal(1) 605 expect(video).to.not.exist
572 }) 606 })
573 607
608 it('Should have propagated channel video', async function () {
609 this.timeout(60000)
610
611 await servers[0].follows.follow({ handles: [ 'root_channel@' + servers[2].host ] })
612
613 await waitJobs(servers)
614
615 const video = await servers[0].videos.find({ name: 'server3-7' })
616
617 expect(video).to.exist
618 })
574 }) 619 })
575 620
576 after(async function () { 621 after(async function () {
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts
index d57d72f5e..2f3950354 100644
--- a/server/tests/api/server/handle-down.ts
+++ b/server/tests/api/server/handle-down.ts
@@ -1,51 +1,31 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { JobState, Video } from '../../../../shared/models' 4import * as chai from 'chai'
6import { VideoPrivacy } from '../../../../shared/models/videos'
7import { VideoCommentThreadTree } from '../../../../shared/models/videos/comment/video-comment.model'
8
9import { 5import {
10 cleanupTests, 6 cleanupTests,
11 closeAllSequelize, 7 CommentsCommand,
12 completeVideoCheck, 8 completeVideoCheck,
13 flushAndRunMultipleServers, 9 createMultipleServers,
14 getVideo,
15 getVideosList,
16 immutableAssign,
17 killallServers, 10 killallServers,
18 reRunServer, 11 PeerTubeServer,
19 ServerInfo,
20 setAccessTokensToServers, 12 setAccessTokensToServers,
21 setActorFollowScores, 13 wait,
22 unfollow, 14 waitJobs
23 updateVideo, 15} from '@shared/extra-utils'
24 uploadVideo, 16import { HttpStatusCode, JobState, VideoCreateResult, VideoPrivacy } from '@shared/models'
25 uploadVideoAndGetId,
26 wait
27} from '../../../../shared/extra-utils'
28import { follow, getFollowersListPaginationAndSort } from '../../../../shared/extra-utils/server/follows'
29import { getJobsListPaginationAndSort, waitJobs } from '../../../../shared/extra-utils/server/jobs'
30import {
31 addVideoCommentReply,
32 addVideoCommentThread,
33 getVideoCommentThreads,
34 getVideoThreadComments
35} from '../../../../shared/extra-utils/videos/video-comments'
36import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
37 17
38const expect = chai.expect 18const expect = chai.expect
39 19
40describe('Test handle downs', function () { 20describe('Test handle downs', function () {
41 let servers: ServerInfo[] = [] 21 let servers: PeerTubeServer[] = []
42 let threadIdServer1: number 22 let threadIdServer1: number
43 let threadIdServer2: number 23 let threadIdServer2: number
44 let commentIdServer1: number 24 let commentIdServer1: number
45 let commentIdServer2: number 25 let commentIdServer2: number
46 let missedVideo1: Video 26 let missedVideo1: VideoCreateResult
47 let missedVideo2: Video 27 let missedVideo2: VideoCreateResult
48 let unlistedVideo: Video 28 let unlistedVideo: VideoCreateResult
49 29
50 const videoIdsServer1: string[] = [] 30 const videoIdsServer1: string[] = []
51 31
@@ -62,17 +42,18 @@ describe('Test handle downs', function () {
62 fixture: 'video_short1.webm' 42 fixture: 'video_short1.webm'
63 } 43 }
64 44
65 const unlistedVideoAttributes = immutableAssign(videoAttributes, { 45 const unlistedVideoAttributes = { ...videoAttributes, privacy: VideoPrivacy.UNLISTED }
66 privacy: VideoPrivacy.UNLISTED
67 })
68 46
69 let checkAttributes: any 47 let checkAttributes: any
70 let unlistedCheckAttributes: any 48 let unlistedCheckAttributes: any
71 49
50 let commentCommands: CommentsCommand[]
51
72 before(async function () { 52 before(async function () {
73 this.timeout(30000) 53 this.timeout(30000)
74 54
75 servers = await flushAndRunMultipleServers(3) 55 servers = await createMultipleServers(3)
56 commentCommands = servers.map(s => s.comments)
76 57
77 checkAttributes = { 58 checkAttributes = {
78 name: 'my super name for server 1', 59 name: 'my super name for server 1',
@@ -106,9 +87,7 @@ describe('Test handle downs', function () {
106 } 87 }
107 ] 88 ]
108 } 89 }
109 unlistedCheckAttributes = immutableAssign(checkAttributes, { 90 unlistedCheckAttributes = { ...checkAttributes, privacy: VideoPrivacy.UNLISTED }
110 privacy: VideoPrivacy.UNLISTED
111 })
112 91
113 // Get the access tokens 92 // Get the access tokens
114 await setAccessTokensToServers(servers) 93 await setAccessTokensToServers(servers)
@@ -118,58 +97,53 @@ describe('Test handle downs', function () {
118 this.timeout(240000) 97 this.timeout(240000)
119 98
120 // Server 2 and 3 follow server 1 99 // Server 2 and 3 follow server 1
121 await follow(servers[1].url, [ servers[0].url ], servers[1].accessToken) 100 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
122 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 101 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
123 102
124 await waitJobs(servers) 103 await waitJobs(servers)
125 104
126 // Upload a video to server 1 105 // Upload a video to server 1
127 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 106 await servers[0].videos.upload({ attributes: videoAttributes })
128 107
129 await waitJobs(servers) 108 await waitJobs(servers)
130 109
131 // And check all servers have this video 110 // And check all servers have this video
132 for (const server of servers) { 111 for (const server of servers) {
133 const res = await getVideosList(server.url) 112 const { data } = await server.videos.list()
134 expect(res.body.data).to.be.an('array') 113 expect(data).to.be.an('array')
135 expect(res.body.data).to.have.lengthOf(1) 114 expect(data).to.have.lengthOf(1)
136 } 115 }
137 116
138 // Kill server 2 117 // Kill server 2
139 killallServers([ servers[1] ]) 118 await killallServers([ servers[1] ])
140 119
141 // Remove server 2 follower 120 // Remove server 2 follower
142 for (let i = 0; i < 10; i++) { 121 for (let i = 0; i < 10; i++) {
143 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 122 await servers[0].videos.upload({ attributes: videoAttributes })
144 } 123 }
145 124
146 await waitJobs([ servers[0], servers[2] ]) 125 await waitJobs([ servers[0], servers[2] ])
147 126
148 // Kill server 3 127 // Kill server 3
149 killallServers([ servers[2] ]) 128 await killallServers([ servers[2] ])
150 129
151 const resLastVideo1 = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 130 missedVideo1 = await servers[0].videos.upload({ attributes: videoAttributes })
152 missedVideo1 = resLastVideo1.body.video
153 131
154 const resLastVideo2 = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 132 missedVideo2 = await servers[0].videos.upload({ attributes: videoAttributes })
155 missedVideo2 = resLastVideo2.body.video
156 133
157 // Unlisted video 134 // Unlisted video
158 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, unlistedVideoAttributes) 135 unlistedVideo = await servers[0].videos.upload({ attributes: unlistedVideoAttributes })
159 unlistedVideo = resVideo.body.video
160 136
161 // Add comments to video 2 137 // Add comments to video 2
162 { 138 {
163 const text = 'thread 1' 139 const text = 'thread 1'
164 let resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, missedVideo2.uuid, text) 140 let comment = await commentCommands[0].createThread({ videoId: missedVideo2.uuid, text })
165 let comment = resComment.body.comment
166 threadIdServer1 = comment.id 141 threadIdServer1 = comment.id
167 142
168 resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, comment.id, 'comment 1-1') 143 comment = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-1' })
169 comment = resComment.body.comment
170 144
171 resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, comment.id, 'comment 1-2') 145 const created = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-2' })
172 commentIdServer1 = resComment.body.comment.id 146 commentIdServer1 = created.id
173 } 147 }
174 148
175 await waitJobs(servers[0]) 149 await waitJobs(servers[0])
@@ -177,90 +151,87 @@ describe('Test handle downs', function () {
177 await wait(11000) 151 await wait(11000)
178 152
179 // Only server 3 is still a follower of server 1 153 // Only server 3 is still a follower of server 1
180 const res = await getFollowersListPaginationAndSort({ url: servers[0].url, start: 0, count: 2, sort: 'createdAt' }) 154 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
181 expect(res.body.data).to.be.an('array') 155 expect(body.data).to.be.an('array')
182 expect(res.body.data).to.have.lengthOf(1) 156 expect(body.data).to.have.lengthOf(1)
183 expect(res.body.data[0].follower.host).to.equal('localhost:' + servers[2].port) 157 expect(body.data[0].follower.host).to.equal('localhost:' + servers[2].port)
184 }) 158 })
185 159
186 it('Should not have pending/processing jobs anymore', async function () { 160 it('Should not have pending/processing jobs anymore', async function () {
187 const states: JobState[] = [ 'waiting', 'active' ] 161 const states: JobState[] = [ 'waiting', 'active' ]
188 162
189 for (const state of states) { 163 for (const state of states) {
190 const res = await getJobsListPaginationAndSort({ 164 const body = await servers[0].jobs.getJobsList({
191 url: servers[0].url,
192 accessToken: servers[0].accessToken,
193 state: state, 165 state: state,
194 start: 0, 166 start: 0,
195 count: 50, 167 count: 50,
196 sort: '-createdAt' 168 sort: '-createdAt'
197 }) 169 })
198 expect(res.body.data).to.have.length(0) 170 expect(body.data).to.have.length(0)
199 } 171 }
200 }) 172 })
201 173
202 it('Should re-follow server 1', async function () { 174 it('Should re-follow server 1', async function () {
203 this.timeout(35000) 175 this.timeout(35000)
204 176
205 await reRunServer(servers[1]) 177 await servers[1].run()
206 await reRunServer(servers[2]) 178 await servers[2].run()
207 179
208 await unfollow(servers[1].url, servers[1].accessToken, servers[0]) 180 await servers[1].follows.unfollow({ target: servers[0] })
209 await waitJobs(servers) 181 await waitJobs(servers)
210 182
211 await follow(servers[1].url, [ servers[0].url ], servers[1].accessToken) 183 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
212 184
213 await waitJobs(servers) 185 await waitJobs(servers)
214 186
215 const res = await getFollowersListPaginationAndSort({ url: servers[0].url, start: 0, count: 2, sort: 'createdAt' }) 187 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
216 expect(res.body.data).to.be.an('array') 188 expect(body.data).to.be.an('array')
217 expect(res.body.data).to.have.lengthOf(2) 189 expect(body.data).to.have.lengthOf(2)
218 }) 190 })
219 191
220 it('Should send an update to server 3, and automatically fetch the video', async function () { 192 it('Should send an update to server 3, and automatically fetch the video', async function () {
221 this.timeout(15000) 193 this.timeout(15000)
222 194
223 const res1 = await getVideosList(servers[2].url) 195 {
224 expect(res1.body.data).to.be.an('array') 196 const { data } = await servers[2].videos.list()
225 expect(res1.body.data).to.have.lengthOf(11) 197 expect(data).to.be.an('array')
198 expect(data).to.have.lengthOf(11)
199 }
226 200
227 await updateVideo(servers[0].url, servers[0].accessToken, missedVideo1.uuid, {}) 201 await servers[0].videos.update({ id: missedVideo1.uuid })
228 await updateVideo(servers[0].url, servers[0].accessToken, unlistedVideo.uuid, {}) 202 await servers[0].videos.update({ id: unlistedVideo.uuid })
229 203
230 await waitJobs(servers) 204 await waitJobs(servers)
231 205
232 const res = await getVideosList(servers[2].url) 206 {
233 expect(res.body.data).to.be.an('array') 207 const { data } = await servers[2].videos.list()
234 // 1 video is unlisted 208 expect(data).to.be.an('array')
235 expect(res.body.data).to.have.lengthOf(12) 209 // 1 video is unlisted
210 expect(data).to.have.lengthOf(12)
211 }
236 212
237 // Check unlisted video 213 // Check unlisted video
238 const resVideo = await getVideo(servers[2].url, unlistedVideo.uuid) 214 const video = await servers[2].videos.get({ id: unlistedVideo.uuid })
239 expect(resVideo.body).not.to.be.undefined 215 await completeVideoCheck(servers[2], video, unlistedCheckAttributes)
240
241 await completeVideoCheck(servers[2].url, resVideo.body, unlistedCheckAttributes)
242 }) 216 })
243 217
244 it('Should send comments on a video to server 3, and automatically fetch the video', async function () { 218 it('Should send comments on a video to server 3, and automatically fetch the video', async function () {
245 this.timeout(25000) 219 this.timeout(25000)
246 220
247 await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, commentIdServer1, 'comment 1-3') 221 await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer1, text: 'comment 1-3' })
248 222
249 await waitJobs(servers) 223 await waitJobs(servers)
250 224
251 const resVideo = await getVideo(servers[2].url, missedVideo2.uuid) 225 await servers[2].videos.get({ id: missedVideo2.uuid })
252 expect(resVideo.body).not.to.be.undefined
253 226
254 { 227 {
255 let resComment = await getVideoCommentThreads(servers[2].url, missedVideo2.uuid, 0, 5) 228 const { data } = await servers[2].comments.listThreads({ videoId: missedVideo2.uuid })
256 expect(resComment.body.data).to.be.an('array') 229 expect(data).to.be.an('array')
257 expect(resComment.body.data).to.have.lengthOf(1) 230 expect(data).to.have.lengthOf(1)
258
259 threadIdServer2 = resComment.body.data[0].id
260 231
261 resComment = await getVideoThreadComments(servers[2].url, missedVideo2.uuid, threadIdServer2) 232 threadIdServer2 = data[0].id
262 233
263 const tree: VideoCommentThreadTree = resComment.body 234 const tree = await servers[2].comments.getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer2 })
264 expect(tree.comment.text).equal('thread 1') 235 expect(tree.comment.text).equal('thread 1')
265 expect(tree.children).to.have.lengthOf(1) 236 expect(tree.children).to.have.lengthOf(1)
266 237
@@ -283,57 +254,54 @@ describe('Test handle downs', function () {
283 it('Should correctly reply to the comment', async function () { 254 it('Should correctly reply to the comment', async function () {
284 this.timeout(15000) 255 this.timeout(15000)
285 256
286 await addVideoCommentReply(servers[2].url, servers[2].accessToken, missedVideo2.uuid, commentIdServer2, 'comment 1-4') 257 await servers[2].comments.addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer2, text: 'comment 1-4' })
287 258
288 await waitJobs(servers) 259 await waitJobs(servers)
289 260
290 { 261 const tree = await commentCommands[0].getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer1 })
291 const resComment = await getVideoThreadComments(servers[0].url, missedVideo2.uuid, threadIdServer1)
292 262
293 const tree: VideoCommentThreadTree = resComment.body 263 expect(tree.comment.text).equal('thread 1')
294 expect(tree.comment.text).equal('thread 1') 264 expect(tree.children).to.have.lengthOf(1)
295 expect(tree.children).to.have.lengthOf(1)
296 265
297 const firstChild = tree.children[0] 266 const firstChild = tree.children[0]
298 expect(firstChild.comment.text).to.equal('comment 1-1') 267 expect(firstChild.comment.text).to.equal('comment 1-1')
299 expect(firstChild.children).to.have.lengthOf(1) 268 expect(firstChild.children).to.have.lengthOf(1)
300 269
301 const childOfFirstChild = firstChild.children[0] 270 const childOfFirstChild = firstChild.children[0]
302 expect(childOfFirstChild.comment.text).to.equal('comment 1-2') 271 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
303 expect(childOfFirstChild.children).to.have.lengthOf(1) 272 expect(childOfFirstChild.children).to.have.lengthOf(1)
304 273
305 const childOfChildFirstChild = childOfFirstChild.children[0] 274 const childOfChildFirstChild = childOfFirstChild.children[0]
306 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3') 275 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
307 expect(childOfChildFirstChild.children).to.have.lengthOf(1) 276 expect(childOfChildFirstChild.children).to.have.lengthOf(1)
308 277
309 const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0] 278 const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0]
310 expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4') 279 expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4')
311 expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0) 280 expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0)
312 }
313 }) 281 })
314 282
315 it('Should upload many videos on server 1', async function () { 283 it('Should upload many videos on server 1', async function () {
316 this.timeout(120000) 284 this.timeout(120000)
317 285
318 for (let i = 0; i < 10; i++) { 286 for (let i = 0; i < 10; i++) {
319 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video ' + i })).uuid 287 const uuid = (await servers[0].videos.quickUpload({ name: 'video ' + i })).uuid
320 videoIdsServer1.push(uuid) 288 videoIdsServer1.push(uuid)
321 } 289 }
322 290
323 await waitJobs(servers) 291 await waitJobs(servers)
324 292
325 for (const id of videoIdsServer1) { 293 for (const id of videoIdsServer1) {
326 await getVideo(servers[1].url, id) 294 await servers[1].videos.get({ id })
327 } 295 }
328 296
329 await waitJobs(servers) 297 await waitJobs(servers)
330 await setActorFollowScores(servers[1].internalServerNumber, 20) 298 await servers[1].sql.setActorFollowScores(20)
331 299
332 // Wait video expiration 300 // Wait video expiration
333 await wait(11000) 301 await wait(11000)
334 302
335 // Refresh video -> score + 10 = 30 303 // Refresh video -> score + 10 = 30
336 await getVideo(servers[1].url, videoIdsServer1[0]) 304 await servers[1].videos.get({ id: videoIdsServer1[0] })
337 305
338 await waitJobs(servers) 306 await waitJobs(servers)
339 }) 307 })
@@ -341,27 +309,25 @@ describe('Test handle downs', function () {
341 it('Should remove followings that are down', async function () { 309 it('Should remove followings that are down', async function () {
342 this.timeout(120000) 310 this.timeout(120000)
343 311
344 killallServers([ servers[0] ]) 312 await killallServers([ servers[0] ])
345 313
346 // Wait video expiration 314 // Wait video expiration
347 await wait(11000) 315 await wait(11000)
348 316
349 for (let i = 0; i < 5; i++) { 317 for (let i = 0; i < 5; i++) {
350 try { 318 try {
351 await getVideo(servers[1].url, videoIdsServer1[i]) 319 await servers[1].videos.get({ id: videoIdsServer1[i] })
352 await waitJobs([ servers[1] ]) 320 await waitJobs([ servers[1] ])
353 await wait(1500) 321 await wait(1500)
354 } catch {} 322 } catch {}
355 } 323 }
356 324
357 for (const id of videoIdsServer1) { 325 for (const id of videoIdsServer1) {
358 await getVideo(servers[1].url, id, HttpStatusCode.FORBIDDEN_403) 326 await servers[1].videos.get({ id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
359 } 327 }
360 }) 328 })
361 329
362 after(async function () { 330 after(async function () {
363 await closeAllSequelize([ servers[1] ])
364
365 await cleanupTests(servers) 331 await cleanupTests(servers)
366 }) 332 })
367}) 333})
diff --git a/server/tests/api/server/homepage.ts b/server/tests/api/server/homepage.ts
index e8ba89ca6..cb3ba5677 100644
--- a/server/tests/api/server/homepage.ts
+++ b/server/tests/api/server/homepage.ts
@@ -2,51 +2,48 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '@shared/core-utils' 5import { HttpStatusCode } from '@shared/models'
6import { CustomPage, ServerConfig } from '@shared/models'
7import { 6import {
8 cleanupTests, 7 cleanupTests,
9 flushAndRunServer, 8 createSingleServer,
10 getConfig, 9 CustomPagesCommand,
11 getInstanceHomepage,
12 killallServers, 10 killallServers,
13 reRunServer, 11 PeerTubeServer,
14 ServerInfo, 12 setAccessTokensToServers
15 setAccessTokensToServers,
16 updateInstanceHomepage
17} from '../../../../shared/extra-utils/index' 13} from '../../../../shared/extra-utils/index'
18 14
19const expect = chai.expect 15const expect = chai.expect
20 16
21async function getHomepageState (server: ServerInfo) { 17async function getHomepageState (server: PeerTubeServer) {
22 const res = await getConfig(server.url) 18 const config = await server.config.getConfig()
23 19
24 const config = res.body as ServerConfig
25 return config.homepage.enabled 20 return config.homepage.enabled
26} 21}
27 22
28describe('Test instance homepage actions', function () { 23describe('Test instance homepage actions', function () {
29 let server: ServerInfo 24 let server: PeerTubeServer
25 let command: CustomPagesCommand
30 26
31 before(async function () { 27 before(async function () {
32 this.timeout(30000) 28 this.timeout(30000)
33 29
34 server = await flushAndRunServer(1) 30 server = await createSingleServer(1)
35 await setAccessTokensToServers([ server ]) 31 await setAccessTokensToServers([ server ])
32
33 command = server.customPage
36 }) 34 })
37 35
38 it('Should not have a homepage', async function () { 36 it('Should not have a homepage', async function () {
39 const state = await getHomepageState(server) 37 const state = await getHomepageState(server)
40 expect(state).to.be.false 38 expect(state).to.be.false
41 39
42 await getInstanceHomepage(server.url, HttpStatusCode.NOT_FOUND_404) 40 await command.getInstanceHomepage({ expectedStatus: HttpStatusCode.NOT_FOUND_404 })
43 }) 41 })
44 42
45 it('Should set a homepage', async function () { 43 it('Should set a homepage', async function () {
46 await updateInstanceHomepage(server.url, server.accessToken, '<picsou-magazine></picsou-magazine>') 44 await command.updateInstanceHomepage({ content: '<picsou-magazine></picsou-magazine>' })
47 45
48 const res = await getInstanceHomepage(server.url) 46 const page = await command.getInstanceHomepage()
49 const page: CustomPage = res.body
50 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>') 47 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>')
51 48
52 const state = await getHomepageState(server) 49 const state = await getHomepageState(server)
@@ -56,12 +53,11 @@ describe('Test instance homepage actions', function () {
56 it('Should have the same homepage after a restart', async function () { 53 it('Should have the same homepage after a restart', async function () {
57 this.timeout(30000) 54 this.timeout(30000)
58 55
59 killallServers([ server ]) 56 await killallServers([ server ])
60 57
61 await reRunServer(server) 58 await server.run()
62 59
63 const res = await getInstanceHomepage(server.url) 60 const page = await command.getInstanceHomepage()
64 const page: CustomPage = res.body
65 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>') 61 expect(page.content).to.equal('<picsou-magazine></picsou-magazine>')
66 62
67 const state = await getHomepageState(server) 63 const state = await getHomepageState(server)
@@ -69,10 +65,9 @@ describe('Test instance homepage actions', function () {
69 }) 65 })
70 66
71 it('Should empty the homepage', async function () { 67 it('Should empty the homepage', async function () {
72 await updateInstanceHomepage(server.url, server.accessToken, '') 68 await command.updateInstanceHomepage({ content: '' })
73 69
74 const res = await getInstanceHomepage(server.url) 70 const page = await command.getInstanceHomepage()
75 const page: CustomPage = res.body
76 expect(page.content).to.be.empty 71 expect(page.content).to.be.empty
77 72
78 const state = await getHomepageState(server) 73 const state = await getHomepageState(server)
diff --git a/server/tests/api/server/jobs.ts b/server/tests/api/server/jobs.ts
index d0e222997..c10c154c2 100644
--- a/server/tests/api/server/jobs.ts
+++ b/server/tests/api/server/jobs.ts
@@ -1,24 +1,26 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index' 4import * as chai from 'chai'
6import { doubleFollow } from '../../../../shared/extra-utils/server/follows' 5import {
7import { getJobsList, getJobsListPaginationAndSort, waitJobs } from '../../../../shared/extra-utils/server/jobs' 6 cleanupTests,
8import { flushAndRunMultipleServers } from '../../../../shared/extra-utils/server/servers' 7 createMultipleServers,
9import { uploadVideo } from '../../../../shared/extra-utils/videos/videos' 8 dateIsValid,
10import { dateIsValid } from '../../../../shared/extra-utils/miscs/miscs' 9 doubleFollow,
11import { Job } from '../../../../shared/models/server' 10 PeerTubeServer,
11 setAccessTokensToServers,
12 waitJobs
13} from '@shared/extra-utils'
12 14
13const expect = chai.expect 15const expect = chai.expect
14 16
15describe('Test jobs', function () { 17describe('Test jobs', function () {
16 let servers: ServerInfo[] 18 let servers: PeerTubeServer[]
17 19
18 before(async function () { 20 before(async function () {
19 this.timeout(30000) 21 this.timeout(30000)
20 22
21 servers = await flushAndRunMultipleServers(2) 23 servers = await createMultipleServers(2)
22 24
23 await setAccessTokensToServers(servers) 25 await setAccessTokensToServers(servers)
24 26
@@ -27,36 +29,34 @@ describe('Test jobs', function () {
27 }) 29 })
28 30
29 it('Should create some jobs', async function () { 31 it('Should create some jobs', async function () {
30 this.timeout(60000) 32 this.timeout(120000)
31 33
32 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' }) 34 await servers[1].videos.upload({ attributes: { name: 'video1' } })
33 await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' }) 35 await servers[1].videos.upload({ attributes: { name: 'video2' } })
34 36
35 await waitJobs(servers) 37 await waitJobs(servers)
36 }) 38 })
37 39
38 it('Should list jobs', async function () { 40 it('Should list jobs', async function () {
39 const res = await getJobsList(servers[1].url, servers[1].accessToken, 'completed') 41 const body = await servers[1].jobs.getJobsList({ state: 'completed' })
40 expect(res.body.total).to.be.above(2) 42 expect(body.total).to.be.above(2)
41 expect(res.body.data).to.have.length.above(2) 43 expect(body.data).to.have.length.above(2)
42 }) 44 })
43 45
44 it('Should list jobs with sort, pagination and job type', async function () { 46 it('Should list jobs with sort, pagination and job type', async function () {
45 { 47 {
46 const res = await getJobsListPaginationAndSort({ 48 const body = await servers[1].jobs.getJobsList({
47 url: servers[1].url,
48 accessToken: servers[1].accessToken,
49 state: 'completed', 49 state: 'completed',
50 start: 1, 50 start: 1,
51 count: 2, 51 count: 2,
52 sort: 'createdAt' 52 sort: 'createdAt'
53 }) 53 })
54 expect(res.body.total).to.be.above(2) 54 expect(body.total).to.be.above(2)
55 expect(res.body.data).to.have.lengthOf(2) 55 expect(body.data).to.have.lengthOf(2)
56 56
57 let job: Job = res.body.data[0] 57 let job = body.data[0]
58 // Skip repeat jobs 58 // Skip repeat jobs
59 if (job.type === 'videos-views') job = res.body.data[1] 59 if (job.type === 'videos-views') job = body.data[1]
60 60
61 expect(job.state).to.equal('completed') 61 expect(job.state).to.equal('completed')
62 expect(job.type.startsWith('activitypub-')).to.be.true 62 expect(job.type.startsWith('activitypub-')).to.be.true
@@ -66,29 +66,26 @@ describe('Test jobs', function () {
66 } 66 }
67 67
68 { 68 {
69 const res = await getJobsListPaginationAndSort({ 69 const body = await servers[1].jobs.getJobsList({
70 url: servers[1].url,
71 accessToken: servers[1].accessToken,
72 state: 'completed', 70 state: 'completed',
73 start: 0, 71 start: 0,
74 count: 100, 72 count: 100,
75 sort: 'createdAt', 73 sort: 'createdAt',
76 jobType: 'activitypub-http-broadcast' 74 jobType: 'activitypub-http-broadcast'
77 }) 75 })
78 expect(res.body.total).to.be.above(2) 76 expect(body.total).to.be.above(2)
79 77
80 for (const j of res.body.data as Job[]) { 78 for (const j of body.data) {
81 expect(j.type).to.equal('activitypub-http-broadcast') 79 expect(j.type).to.equal('activitypub-http-broadcast')
82 } 80 }
83 } 81 }
84 }) 82 })
85 83
86 it('Should list all jobs', async function () { 84 it('Should list all jobs', async function () {
87 const res = await getJobsList(servers[1].url, servers[1].accessToken) 85 const body = await servers[1].jobs.getJobsList()
88 86 expect(body.total).to.be.above(2)
89 const jobs = res.body.data as Job[]
90 87
91 expect(res.body.total).to.be.above(2) 88 const jobs = body.data
92 expect(jobs).to.have.length.above(2) 89 expect(jobs).to.have.length.above(2)
93 90
94 // We know there are a least 1 delayed job (video views) and 1 completed job (broadcast) 91 // We know there are a least 1 delayed job (video views) and 1 completed job (broadcast)
diff --git a/server/tests/api/server/logs.ts b/server/tests/api/server/logs.ts
index bc398ea73..bcd94dda3 100644
--- a/server/tests/api/server/logs.ts
+++ b/server/tests/api/server/logs.ts
@@ -4,27 +4,27 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 flushAndRunServer, 7 createSingleServer,
8 killallServers, 8 killallServers,
9 makePingRequest, 9 LogsCommand,
10 reRunServer, 10 PeerTubeServer,
11 ServerInfo, 11 setAccessTokensToServers,
12 setAccessTokensToServers 12 waitJobs
13} from '../../../../shared/extra-utils/index' 13} from '@shared/extra-utils'
14import { getAuditLogs, getLogs } from '../../../../shared/extra-utils/logs/logs'
15import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
16import { uploadVideo } from '../../../../shared/extra-utils/videos/videos'
17 14
18const expect = chai.expect 15const expect = chai.expect
19 16
20describe('Test logs', function () { 17describe('Test logs', function () {
21 let server: ServerInfo 18 let server: PeerTubeServer
19 let logsCommand: LogsCommand
22 20
23 before(async function () { 21 before(async function () {
24 this.timeout(30000) 22 this.timeout(30000)
25 23
26 server = await flushAndRunServer(1) 24 server = await createSingleServer(1)
27 await setAccessTokensToServers([ server ]) 25 await setAccessTokensToServers([ server ])
26
27 logsCommand = server.logs
28 }) 28 })
29 29
30 describe('With the standard log file', function () { 30 describe('With the standard log file', function () {
@@ -32,16 +32,16 @@ describe('Test logs', function () {
32 it('Should get logs with a start date', async function () { 32 it('Should get logs with a start date', async function () {
33 this.timeout(20000) 33 this.timeout(20000)
34 34
35 await uploadVideo(server.url, server.accessToken, { name: 'video 1' }) 35 await server.videos.upload({ attributes: { name: 'video 1' } })
36 await waitJobs([ server ]) 36 await waitJobs([ server ])
37 37
38 const now = new Date() 38 const now = new Date()
39 39
40 await uploadVideo(server.url, server.accessToken, { name: 'video 2' }) 40 await server.videos.upload({ attributes: { name: 'video 2' } })
41 await waitJobs([ server ]) 41 await waitJobs([ server ])
42 42
43 const res = await getLogs(server.url, server.accessToken, now) 43 const body = await logsCommand.getLogs({ startDate: now })
44 const logsString = JSON.stringify(res.body) 44 const logsString = JSON.stringify(body)
45 45
46 expect(logsString.includes('video 1')).to.be.false 46 expect(logsString.includes('video 1')).to.be.false
47 expect(logsString.includes('video 2')).to.be.true 47 expect(logsString.includes('video 2')).to.be.true
@@ -50,21 +50,21 @@ describe('Test logs', function () {
50 it('Should get logs with an end date', async function () { 50 it('Should get logs with an end date', async function () {
51 this.timeout(30000) 51 this.timeout(30000)
52 52
53 await uploadVideo(server.url, server.accessToken, { name: 'video 3' }) 53 await server.videos.upload({ attributes: { name: 'video 3' } })
54 await waitJobs([ server ]) 54 await waitJobs([ server ])
55 55
56 const now1 = new Date() 56 const now1 = new Date()
57 57
58 await uploadVideo(server.url, server.accessToken, { name: 'video 4' }) 58 await server.videos.upload({ attributes: { name: 'video 4' } })
59 await waitJobs([ server ]) 59 await waitJobs([ server ])
60 60
61 const now2 = new Date() 61 const now2 = new Date()
62 62
63 await uploadVideo(server.url, server.accessToken, { name: 'video 5' }) 63 await server.videos.upload({ attributes: { name: 'video 5' } })
64 await waitJobs([ server ]) 64 await waitJobs([ server ])
65 65
66 const res = await getLogs(server.url, server.accessToken, now1, now2) 66 const body = await logsCommand.getLogs({ startDate: now1, endDate: now2 })
67 const logsString = JSON.stringify(res.body) 67 const logsString = JSON.stringify(body)
68 68
69 expect(logsString.includes('video 3')).to.be.false 69 expect(logsString.includes('video 3')).to.be.false
70 expect(logsString.includes('video 4')).to.be.true 70 expect(logsString.includes('video 4')).to.be.true
@@ -76,19 +76,19 @@ describe('Test logs', function () {
76 76
77 const now = new Date() 77 const now = new Date()
78 78
79 await uploadVideo(server.url, server.accessToken, { name: 'video 6' }) 79 await server.videos.upload({ attributes: { name: 'video 6' } })
80 await waitJobs([ server ]) 80 await waitJobs([ server ])
81 81
82 { 82 {
83 const res = await getLogs(server.url, server.accessToken, now, undefined, 'info') 83 const body = await logsCommand.getLogs({ startDate: now, level: 'info' })
84 const logsString = JSON.stringify(res.body) 84 const logsString = JSON.stringify(body)
85 85
86 expect(logsString.includes('video 6')).to.be.true 86 expect(logsString.includes('video 6')).to.be.true
87 } 87 }
88 88
89 { 89 {
90 const res = await getLogs(server.url, server.accessToken, now, undefined, 'warn') 90 const body = await logsCommand.getLogs({ startDate: now, level: 'warn' })
91 const logsString = JSON.stringify(res.body) 91 const logsString = JSON.stringify(body)
92 92
93 expect(logsString.includes('video 6')).to.be.false 93 expect(logsString.includes('video 6')).to.be.false
94 } 94 }
@@ -99,10 +99,10 @@ describe('Test logs', function () {
99 99
100 const now = new Date() 100 const now = new Date()
101 101
102 await makePingRequest(server) 102 await server.servers.ping()
103 103
104 const res = await getLogs(server.url, server.accessToken, now, undefined, 'info') 104 const body = await logsCommand.getLogs({ startDate: now, level: 'info' })
105 const logsString = JSON.stringify(res.body) 105 const logsString = JSON.stringify(body)
106 106
107 expect(logsString.includes('/api/v1/ping')).to.be.true 107 expect(logsString.includes('/api/v1/ping')).to.be.true
108 }) 108 })
@@ -110,16 +110,16 @@ describe('Test logs', function () {
110 it('Should not log ping requests', async function () { 110 it('Should not log ping requests', async function () {
111 this.timeout(30000) 111 this.timeout(30000)
112 112
113 killallServers([ server ]) 113 await killallServers([ server ])
114 114
115 await reRunServer(server, { log: { log_ping_requests: false } }) 115 await server.run({ log: { log_ping_requests: false } })
116 116
117 const now = new Date() 117 const now = new Date()
118 118
119 await makePingRequest(server) 119 await server.servers.ping()
120 120
121 const res = await getLogs(server.url, server.accessToken, now, undefined, 'info') 121 const body = await logsCommand.getLogs({ startDate: now, level: 'info' })
122 const logsString = JSON.stringify(res.body) 122 const logsString = JSON.stringify(body)
123 123
124 expect(logsString.includes('/api/v1/ping')).to.be.false 124 expect(logsString.includes('/api/v1/ping')).to.be.false
125 }) 125 })
@@ -129,23 +129,23 @@ describe('Test logs', function () {
129 it('Should get logs with a start date', async function () { 129 it('Should get logs with a start date', async function () {
130 this.timeout(20000) 130 this.timeout(20000)
131 131
132 await uploadVideo(server.url, server.accessToken, { name: 'video 7' }) 132 await server.videos.upload({ attributes: { name: 'video 7' } })
133 await waitJobs([ server ]) 133 await waitJobs([ server ])
134 134
135 const now = new Date() 135 const now = new Date()
136 136
137 await uploadVideo(server.url, server.accessToken, { name: 'video 8' }) 137 await server.videos.upload({ attributes: { name: 'video 8' } })
138 await waitJobs([ server ]) 138 await waitJobs([ server ])
139 139
140 const res = await getAuditLogs(server.url, server.accessToken, now) 140 const body = await logsCommand.getAuditLogs({ startDate: now })
141 const logsString = JSON.stringify(res.body) 141 const logsString = JSON.stringify(body)
142 142
143 expect(logsString.includes('video 7')).to.be.false 143 expect(logsString.includes('video 7')).to.be.false
144 expect(logsString.includes('video 8')).to.be.true 144 expect(logsString.includes('video 8')).to.be.true
145 145
146 expect(res.body).to.have.lengthOf(1) 146 expect(body).to.have.lengthOf(1)
147 147
148 const item = res.body[0] 148 const item = body[0]
149 149
150 const message = JSON.parse(item.message) 150 const message = JSON.parse(item.message)
151 expect(message.domain).to.equal('videos') 151 expect(message.domain).to.equal('videos')
@@ -155,21 +155,21 @@ describe('Test logs', function () {
155 it('Should get logs with an end date', async function () { 155 it('Should get logs with an end date', async function () {
156 this.timeout(30000) 156 this.timeout(30000)
157 157
158 await uploadVideo(server.url, server.accessToken, { name: 'video 9' }) 158 await server.videos.upload({ attributes: { name: 'video 9' } })
159 await waitJobs([ server ]) 159 await waitJobs([ server ])
160 160
161 const now1 = new Date() 161 const now1 = new Date()
162 162
163 await uploadVideo(server.url, server.accessToken, { name: 'video 10' }) 163 await server.videos.upload({ attributes: { name: 'video 10' } })
164 await waitJobs([ server ]) 164 await waitJobs([ server ])
165 165
166 const now2 = new Date() 166 const now2 = new Date()
167 167
168 await uploadVideo(server.url, server.accessToken, { name: 'video 11' }) 168 await server.videos.upload({ attributes: { name: 'video 11' } })
169 await waitJobs([ server ]) 169 await waitJobs([ server ])
170 170
171 const res = await getAuditLogs(server.url, server.accessToken, now1, now2) 171 const body = await logsCommand.getAuditLogs({ startDate: now1, endDate: now2 })
172 const logsString = JSON.stringify(res.body) 172 const logsString = JSON.stringify(body)
173 173
174 expect(logsString.includes('video 9')).to.be.false 174 expect(logsString.includes('video 9')).to.be.false
175 expect(logsString.includes('video 10')).to.be.true 175 expect(logsString.includes('video 10')).to.be.true
diff --git a/server/tests/api/server/no-client.ts b/server/tests/api/server/no-client.ts
index d589f51f3..719813ae9 100644
--- a/server/tests/api/server/no-client.ts
+++ b/server/tests/api/server/no-client.ts
@@ -1,16 +1,15 @@
1import 'mocha' 1import 'mocha'
2import * as request from 'supertest' 2import * as request from 'supertest'
3import { ServerInfo } from '../../../../shared/extra-utils' 3import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/extra-utils'
4import { cleanupTests, flushAndRunServer } from '../../../../shared/extra-utils/server/servers' 4import { HttpStatusCode } from '@shared/models'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6 5
7describe('Start and stop server without web client routes', function () { 6describe('Start and stop server without web client routes', function () {
8 let server: ServerInfo 7 let server: PeerTubeServer
9 8
10 before(async function () { 9 before(async function () {
11 this.timeout(30000) 10 this.timeout(30000)
12 11
13 server = await flushAndRunServer(1, {}, [ '--no-client' ]) 12 server = await createSingleServer(1, {}, { peertubeArgs: [ '--no-client' ] })
14 }) 13 })
15 14
16 it('Should fail getting the client', function () { 15 it('Should fail getting the client', function () {
diff --git a/server/tests/api/server/plugins.ts b/server/tests/api/server/plugins.ts
index 6b61c7c33..5f9f4ffdd 100644
--- a/server/tests/api/server/plugins.ts
+++ b/server/tests/api/server/plugins.ts
@@ -2,41 +2,23 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '@shared/core-utils'
6import { 5import {
7 cleanupTests, 6 cleanupTests,
8 closeAllSequelize, 7 createSingleServer,
9 flushAndRunServer,
10 getConfig,
11 getMyUserInformation,
12 getPlugin,
13 getPluginPackageJSON,
14 getPluginTestPath,
15 getPublicSettings,
16 installPlugin,
17 killallServers, 8 killallServers,
18 listAvailablePlugins, 9 PeerTubeServer,
19 listPlugins, 10 PluginsCommand,
20 reRunServer,
21 ServerInfo,
22 setAccessTokensToServers, 11 setAccessTokensToServers,
23 setPluginVersion,
24 testHelloWorldRegisteredSettings, 12 testHelloWorldRegisteredSettings,
25 uninstallPlugin, 13 wait
26 updateCustomSubConfig,
27 updateMyUser,
28 updatePlugin,
29 updatePluginPackageJSON,
30 updatePluginSettings,
31 wait,
32 waitUntilLog
33} from '@shared/extra-utils' 14} from '@shared/extra-utils'
34import { PeerTubePlugin, PeerTubePluginIndex, PluginPackageJson, PluginType, PublicServerSetting, ServerConfig, User } from '@shared/models' 15import { HttpStatusCode, PluginType } from '@shared/models'
35 16
36const expect = chai.expect 17const expect = chai.expect
37 18
38describe('Test plugins', function () { 19describe('Test plugins', function () {
39 let server: ServerInfo = null 20 let server: PeerTubeServer = null
21 let command: PluginsCommand
40 22
41 before(async function () { 23 before(async function () {
42 this.timeout(30000) 24 this.timeout(30000)
@@ -46,68 +28,61 @@ describe('Test plugins', function () {
46 index: { check_latest_versions_interval: '5 seconds' } 28 index: { check_latest_versions_interval: '5 seconds' }
47 } 29 }
48 } 30 }
49 server = await flushAndRunServer(1, configOverride) 31 server = await createSingleServer(1, configOverride)
50 await setAccessTokensToServers([ server ]) 32 await setAccessTokensToServers([ server ])
33
34 command = server.plugins
51 }) 35 })
52 36
53 it('Should list and search available plugins and themes', async function () { 37 it('Should list and search available plugins and themes', async function () {
54 this.timeout(30000) 38 this.timeout(30000)
55 39
56 { 40 {
57 const res = await listAvailablePlugins({ 41 const body = await command.listAvailable({
58 url: server.url,
59 accessToken: server.accessToken,
60 count: 1, 42 count: 1,
61 start: 0, 43 start: 0,
62 pluginType: PluginType.THEME, 44 pluginType: PluginType.THEME,
63 search: 'background-red' 45 search: 'background-red'
64 }) 46 })
65 47
66 expect(res.body.total).to.be.at.least(1) 48 expect(body.total).to.be.at.least(1)
67 expect(res.body.data).to.have.lengthOf(1) 49 expect(body.data).to.have.lengthOf(1)
68 } 50 }
69 51
70 { 52 {
71 const res1 = await listAvailablePlugins({ 53 const body1 = await command.listAvailable({
72 url: server.url,
73 accessToken: server.accessToken,
74 count: 2, 54 count: 2,
75 start: 0, 55 start: 0,
76 sort: 'npmName' 56 sort: 'npmName'
77 }) 57 })
78 const data1: PeerTubePluginIndex[] = res1.body.data 58 expect(body1.total).to.be.at.least(2)
79 59
80 expect(res1.body.total).to.be.at.least(2) 60 const data1 = body1.data
81 expect(data1).to.have.lengthOf(2) 61 expect(data1).to.have.lengthOf(2)
82 62
83 const res2 = await listAvailablePlugins({ 63 const body2 = await command.listAvailable({
84 url: server.url,
85 accessToken: server.accessToken,
86 count: 2, 64 count: 2,
87 start: 0, 65 start: 0,
88 sort: '-npmName' 66 sort: '-npmName'
89 }) 67 })
90 const data2: PeerTubePluginIndex[] = res2.body.data 68 expect(body2.total).to.be.at.least(2)
91 69
92 expect(res2.body.total).to.be.at.least(2) 70 const data2 = body2.data
93 expect(data2).to.have.lengthOf(2) 71 expect(data2).to.have.lengthOf(2)
94 72
95 expect(data1[0].npmName).to.not.equal(data2[0].npmName) 73 expect(data1[0].npmName).to.not.equal(data2[0].npmName)
96 } 74 }
97 75
98 { 76 {
99 const res = await listAvailablePlugins({ 77 const body = await command.listAvailable({
100 url: server.url,
101 accessToken: server.accessToken,
102 count: 10, 78 count: 10,
103 start: 0, 79 start: 0,
104 pluginType: PluginType.THEME, 80 pluginType: PluginType.THEME,
105 search: 'background-red', 81 search: 'background-red',
106 currentPeerTubeEngine: '1.0.0' 82 currentPeerTubeEngine: '1.0.0'
107 }) 83 })
108 const data: PeerTubePluginIndex[] = res.body.data
109 84
110 const p = data.find(p => p.npmName === 'peertube-theme-background-red') 85 const p = body.data.find(p => p.npmName === 'peertube-theme-background-red')
111 expect(p).to.be.undefined 86 expect(p).to.be.undefined
112 } 87 }
113 }) 88 })
@@ -115,22 +90,12 @@ describe('Test plugins', function () {
115 it('Should install a plugin and a theme', async function () { 90 it('Should install a plugin and a theme', async function () {
116 this.timeout(30000) 91 this.timeout(30000)
117 92
118 await installPlugin({ 93 await command.install({ npmName: 'peertube-plugin-hello-world' })
119 url: server.url, 94 await command.install({ npmName: 'peertube-theme-background-red' })
120 accessToken: server.accessToken,
121 npmName: 'peertube-plugin-hello-world'
122 })
123
124 await installPlugin({
125 url: server.url,
126 accessToken: server.accessToken,
127 npmName: 'peertube-theme-background-red'
128 })
129 }) 95 })
130 96
131 it('Should have the plugin loaded in the configuration', async function () { 97 it('Should have the plugin loaded in the configuration', async function () {
132 const res = await getConfig(server.url) 98 const config = await server.config.getConfig()
133 const config: ServerConfig = res.body
134 99
135 const theme = config.theme.registered.find(r => r.name === 'background-red') 100 const theme = config.theme.registered.find(r => r.name === 'background-red')
136 expect(theme).to.not.be.undefined 101 expect(theme).to.not.be.undefined
@@ -140,66 +105,56 @@ describe('Test plugins', function () {
140 }) 105 })
141 106
142 it('Should update the default theme in the configuration', async function () { 107 it('Should update the default theme in the configuration', async function () {
143 await updateCustomSubConfig(server.url, server.accessToken, { theme: { default: 'background-red' } }) 108 await server.config.updateCustomSubConfig({
144 109 newConfig: {
145 const res = await getConfig(server.url) 110 theme: { default: 'background-red' }
146 const config: ServerConfig = res.body 111 }
112 })
147 113
114 const config = await server.config.getConfig()
148 expect(config.theme.default).to.equal('background-red') 115 expect(config.theme.default).to.equal('background-red')
149 }) 116 })
150 117
151 it('Should update my default theme', async function () { 118 it('Should update my default theme', async function () {
152 await updateMyUser({ 119 await server.users.updateMe({ theme: 'background-red' })
153 url: server.url,
154 accessToken: server.accessToken,
155 theme: 'background-red'
156 })
157 120
158 const res = await getMyUserInformation(server.url, server.accessToken) 121 const user = await server.users.getMyInfo()
159 expect((res.body as User).theme).to.equal('background-red') 122 expect(user.theme).to.equal('background-red')
160 }) 123 })
161 124
162 it('Should list plugins and themes', async function () { 125 it('Should list plugins and themes', async function () {
163 { 126 {
164 const res = await listPlugins({ 127 const body = await command.list({
165 url: server.url,
166 accessToken: server.accessToken,
167 count: 1, 128 count: 1,
168 start: 0, 129 start: 0,
169 pluginType: PluginType.THEME 130 pluginType: PluginType.THEME
170 }) 131 })
171 const data: PeerTubePlugin[] = res.body.data 132 expect(body.total).to.be.at.least(1)
172 133
173 expect(res.body.total).to.be.at.least(1) 134 const data = body.data
174 expect(data).to.have.lengthOf(1) 135 expect(data).to.have.lengthOf(1)
175 expect(data[0].name).to.equal('background-red') 136 expect(data[0].name).to.equal('background-red')
176 } 137 }
177 138
178 { 139 {
179 const res = await listPlugins({ 140 const { data } = await command.list({
180 url: server.url,
181 accessToken: server.accessToken,
182 count: 2, 141 count: 2,
183 start: 0, 142 start: 0,
184 sort: 'name' 143 sort: 'name'
185 }) 144 })
186 const data: PeerTubePlugin[] = res.body.data
187 145
188 expect(data[0].name).to.equal('background-red') 146 expect(data[0].name).to.equal('background-red')
189 expect(data[1].name).to.equal('hello-world') 147 expect(data[1].name).to.equal('hello-world')
190 } 148 }
191 149
192 { 150 {
193 const res = await listPlugins({ 151 const body = await command.list({
194 url: server.url,
195 accessToken: server.accessToken,
196 count: 2, 152 count: 2,
197 start: 1, 153 start: 1,
198 sort: 'name' 154 sort: 'name'
199 }) 155 })
200 const data: PeerTubePlugin[] = res.body.data
201 156
202 expect(data[0].name).to.equal('hello-world') 157 expect(body.data[0].name).to.equal('hello-world')
203 } 158 }
204 }) 159 })
205 160
@@ -208,9 +163,8 @@ describe('Test plugins', function () {
208 }) 163 })
209 164
210 it('Should get public settings', async function () { 165 it('Should get public settings', async function () {
211 const res = await getPublicSettings({ url: server.url, npmName: 'peertube-plugin-hello-world' }) 166 const body = await command.getPublicSettings({ npmName: 'peertube-plugin-hello-world' })
212 167 const publicSettings = body.publicSettings
213 const publicSettings = (res.body as PublicServerSetting).publicSettings
214 168
215 expect(Object.keys(publicSettings)).to.have.lengthOf(1) 169 expect(Object.keys(publicSettings)).to.have.lengthOf(1)
216 expect(Object.keys(publicSettings)).to.deep.equal([ 'user-name' ]) 170 expect(Object.keys(publicSettings)).to.deep.equal([ 'user-name' ])
@@ -222,9 +176,7 @@ describe('Test plugins', function () {
222 'admin-name': 'Cid' 176 'admin-name': 'Cid'
223 } 177 }
224 178
225 await updatePluginSettings({ 179 await command.updateSettings({
226 url: server.url,
227 accessToken: server.accessToken,
228 npmName: 'peertube-plugin-hello-world', 180 npmName: 'peertube-plugin-hello-world',
229 settings 181 settings
230 }) 182 })
@@ -233,18 +185,12 @@ describe('Test plugins', function () {
233 it('Should have watched settings changes', async function () { 185 it('Should have watched settings changes', async function () {
234 this.timeout(10000) 186 this.timeout(10000)
235 187
236 await waitUntilLog(server, 'Settings changed!') 188 await server.servers.waitUntilLog('Settings changed!')
237 }) 189 })
238 190
239 it('Should get a plugin and a theme', async function () { 191 it('Should get a plugin and a theme', async function () {
240 { 192 {
241 const res = await getPlugin({ 193 const plugin = await command.get({ npmName: 'peertube-plugin-hello-world' })
242 url: server.url,
243 accessToken: server.accessToken,
244 npmName: 'peertube-plugin-hello-world'
245 })
246
247 const plugin: PeerTubePlugin = res.body
248 194
249 expect(plugin.type).to.equal(PluginType.PLUGIN) 195 expect(plugin.type).to.equal(PluginType.PLUGIN)
250 expect(plugin.name).to.equal('hello-world') 196 expect(plugin.name).to.equal('hello-world')
@@ -262,13 +208,7 @@ describe('Test plugins', function () {
262 } 208 }
263 209
264 { 210 {
265 const res = await getPlugin({ 211 const plugin = await command.get({ npmName: 'peertube-theme-background-red' })
266 url: server.url,
267 accessToken: server.accessToken,
268 npmName: 'peertube-theme-background-red'
269 })
270
271 const plugin: PeerTubePlugin = res.body
272 212
273 expect(plugin.type).to.equal(PluginType.THEME) 213 expect(plugin.type).to.equal(PluginType.THEME)
274 expect(plugin.name).to.equal('background-red') 214 expect(plugin.name).to.equal('background-red')
@@ -292,101 +232,66 @@ describe('Test plugins', function () {
292 await wait(6000) 232 await wait(6000)
293 233
294 // Fake update our plugin version 234 // Fake update our plugin version
295 await setPluginVersion(server.internalServerNumber, 'hello-world', '0.0.1') 235 await server.sql.setPluginVersion('hello-world', '0.0.1')
296 236
297 // Fake update package.json 237 // Fake update package.json
298 const packageJSON: PluginPackageJson = await getPluginPackageJSON(server, 'peertube-plugin-hello-world') 238 const packageJSON = await command.getPackageJSON('peertube-plugin-hello-world')
299 const oldVersion = packageJSON.version 239 const oldVersion = packageJSON.version
300 240
301 packageJSON.version = '0.0.1' 241 packageJSON.version = '0.0.1'
302 await updatePluginPackageJSON(server, 'peertube-plugin-hello-world', packageJSON) 242 await command.updatePackageJSON('peertube-plugin-hello-world', packageJSON)
303 243
304 // Restart the server to take into account this change 244 // Restart the server to take into account this change
305 killallServers([ server ]) 245 await killallServers([ server ])
306 await reRunServer(server) 246 await server.run()
307 247
308 { 248 {
309 const res = await listPlugins({ 249 const body = await command.list({ pluginType: PluginType.PLUGIN })
310 url: server.url,
311 accessToken: server.accessToken,
312 pluginType: PluginType.PLUGIN
313 })
314
315 const plugin: PeerTubePlugin = res.body.data[0]
316 250
251 const plugin = body.data[0]
317 expect(plugin.version).to.equal('0.0.1') 252 expect(plugin.version).to.equal('0.0.1')
318 expect(plugin.latestVersion).to.exist 253 expect(plugin.latestVersion).to.exist
319 expect(plugin.latestVersion).to.not.equal('0.0.1') 254 expect(plugin.latestVersion).to.not.equal('0.0.1')
320 } 255 }
321 256
322 { 257 {
323 await updatePlugin({ 258 await command.update({ npmName: 'peertube-plugin-hello-world' })
324 url: server.url,
325 accessToken: server.accessToken,
326 npmName: 'peertube-plugin-hello-world'
327 })
328
329 const res = await listPlugins({
330 url: server.url,
331 accessToken: server.accessToken,
332 pluginType: PluginType.PLUGIN
333 })
334 259
335 const plugin: PeerTubePlugin = res.body.data[0] 260 const body = await command.list({ pluginType: PluginType.PLUGIN })
336 261
262 const plugin = body.data[0]
337 expect(plugin.version).to.equal(oldVersion) 263 expect(plugin.version).to.equal(oldVersion)
338 264
339 const updatedPackageJSON: PluginPackageJson = await getPluginPackageJSON(server, 'peertube-plugin-hello-world') 265 const updatedPackageJSON = await command.getPackageJSON('peertube-plugin-hello-world')
340 expect(updatedPackageJSON.version).to.equal(oldVersion) 266 expect(updatedPackageJSON.version).to.equal(oldVersion)
341 } 267 }
342 }) 268 })
343 269
344 it('Should uninstall the plugin', async function () { 270 it('Should uninstall the plugin', async function () {
345 await uninstallPlugin({ 271 await command.uninstall({ npmName: 'peertube-plugin-hello-world' })
346 url: server.url,
347 accessToken: server.accessToken,
348 npmName: 'peertube-plugin-hello-world'
349 })
350
351 const res = await listPlugins({
352 url: server.url,
353 accessToken: server.accessToken,
354 pluginType: PluginType.PLUGIN
355 })
356 272
357 expect(res.body.total).to.equal(0) 273 const body = await command.list({ pluginType: PluginType.PLUGIN })
358 expect(res.body.data).to.have.lengthOf(0) 274 expect(body.total).to.equal(0)
275 expect(body.data).to.have.lengthOf(0)
359 }) 276 })
360 277
361 it('Should list uninstalled plugins', async function () { 278 it('Should list uninstalled plugins', async function () {
362 const res = await listPlugins({ 279 const body = await command.list({ pluginType: PluginType.PLUGIN, uninstalled: true })
363 url: server.url, 280 expect(body.total).to.equal(1)
364 accessToken: server.accessToken, 281 expect(body.data).to.have.lengthOf(1)
365 pluginType: PluginType.PLUGIN,
366 uninstalled: true
367 })
368
369 expect(res.body.total).to.equal(1)
370 expect(res.body.data).to.have.lengthOf(1)
371 282
372 const plugin: PeerTubePlugin = res.body.data[0] 283 const plugin = body.data[0]
373 expect(plugin.name).to.equal('hello-world') 284 expect(plugin.name).to.equal('hello-world')
374 expect(plugin.enabled).to.be.false 285 expect(plugin.enabled).to.be.false
375 expect(plugin.uninstalled).to.be.true 286 expect(plugin.uninstalled).to.be.true
376 }) 287 })
377 288
378 it('Should uninstall the theme', async function () { 289 it('Should uninstall the theme', async function () {
379 await uninstallPlugin({ 290 await command.uninstall({ npmName: 'peertube-theme-background-red' })
380 url: server.url,
381 accessToken: server.accessToken,
382 npmName: 'peertube-theme-background-red'
383 })
384 }) 291 })
385 292
386 it('Should have updated the configuration', async function () { 293 it('Should have updated the configuration', async function () {
387 // get /config (default theme + registered themes + registered plugins) 294 const config = await server.config.getConfig()
388 const res = await getConfig(server.url)
389 const config: ServerConfig = res.body
390 295
391 expect(config.theme.default).to.equal('default') 296 expect(config.theme.default).to.equal('default')
392 297
@@ -398,42 +303,33 @@ describe('Test plugins', function () {
398 }) 303 })
399 304
400 it('Should have updated the user theme', async function () { 305 it('Should have updated the user theme', async function () {
401 const res = await getMyUserInformation(server.url, server.accessToken) 306 const user = await server.users.getMyInfo()
402 expect((res.body as User).theme).to.equal('instance-default') 307 expect(user.theme).to.equal('instance-default')
403 }) 308 })
404 309
405 it('Should not install a broken plugin', async function () { 310 it('Should not install a broken plugin', async function () {
406 this.timeout(60000) 311 this.timeout(60000)
407 312
408 async function check () { 313 async function check () {
409 const res = await listPlugins({ 314 const body = await command.list({ pluginType: PluginType.PLUGIN })
410 url: server.url, 315 const plugins = body.data
411 accessToken: server.accessToken,
412 pluginType: PluginType.PLUGIN
413 })
414
415 const plugins: PeerTubePlugin[] = res.body.data
416
417 expect(plugins.find(p => p.name === 'test-broken')).to.not.exist 316 expect(plugins.find(p => p.name === 'test-broken')).to.not.exist
418 } 317 }
419 318
420 await installPlugin({ 319 await command.install({
421 url: server.url, 320 path: PluginsCommand.getPluginTestPath('-broken'),
422 accessToken: server.accessToken,
423 path: getPluginTestPath('-broken'),
424 expectedStatus: HttpStatusCode.BAD_REQUEST_400 321 expectedStatus: HttpStatusCode.BAD_REQUEST_400
425 }) 322 })
426 323
427 await check() 324 await check()
428 325
429 killallServers([ server ]) 326 await killallServers([ server ])
430 await reRunServer(server) 327 await server.run()
431 328
432 await check() 329 await check()
433 }) 330 })
434 331
435 after(async function () { 332 after(async function () {
436 await closeAllSequelize([ server ])
437 await cleanupTests([ server ]) 333 await cleanupTests([ server ])
438 }) 334 })
439}) 335})
diff --git a/server/tests/api/server/reverse-proxy.ts b/server/tests/api/server/reverse-proxy.ts
index 17d1ee4a5..484f88d67 100644
--- a/server/tests/api/server/reverse-proxy.ts
+++ b/server/tests/api/server/reverse-proxy.ts
@@ -1,16 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import { expect } from 'chai'
4import * as chai from 'chai' 4import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, wait } from '@shared/extra-utils'
5import { cleanupTests, getVideo, registerUser, uploadVideo, userLogin, viewVideo, wait } from '../../../../shared/extra-utils' 5import { HttpStatusCode } from '@shared/models'
6import { flushAndRunServer, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
8
9const expect = chai.expect
10 6
11describe('Test application behind a reverse proxy', function () { 7describe('Test application behind a reverse proxy', function () {
12 let server = null 8 let server: PeerTubeServer
13 let videoId 9 let videoId: string
14 10
15 before(async function () { 11 before(async function () {
16 this.timeout(30000) 12 this.timeout(30000)
@@ -34,85 +30,85 @@ describe('Test application behind a reverse proxy', function () {
34 } 30 }
35 } 31 }
36 32
37 server = await flushAndRunServer(1, config) 33 server = await createSingleServer(1, config)
38 await setAccessTokensToServers([ server ]) 34 await setAccessTokensToServers([ server ])
39 35
40 const { body } = await uploadVideo(server.url, server.accessToken, {}) 36 const { uuid } = await server.videos.upload()
41 videoId = body.video.uuid 37 videoId = uuid
42 }) 38 })
43 39
44 it('Should view a video only once with the same IP by default', async function () { 40 it('Should view a video only once with the same IP by default', async function () {
45 this.timeout(20000) 41 this.timeout(20000)
46 42
47 await viewVideo(server.url, videoId) 43 await server.videos.view({ id: videoId })
48 await viewVideo(server.url, videoId) 44 await server.videos.view({ id: videoId })
49 45
50 // Wait the repeatable job 46 // Wait the repeatable job
51 await wait(8000) 47 await wait(8000)
52 48
53 const { body } = await getVideo(server.url, videoId) 49 const video = await server.videos.get({ id: videoId })
54 expect(body.views).to.equal(1) 50 expect(video.views).to.equal(1)
55 }) 51 })
56 52
57 it('Should view a video 2 times with the X-Forwarded-For header set', async function () { 53 it('Should view a video 2 times with the X-Forwarded-For header set', async function () {
58 this.timeout(20000) 54 this.timeout(20000)
59 55
60 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.1,127.0.0.1') 56 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.1,127.0.0.1' })
61 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.2,127.0.0.1') 57 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.2,127.0.0.1' })
62 58
63 // Wait the repeatable job 59 // Wait the repeatable job
64 await wait(8000) 60 await wait(8000)
65 61
66 const { body } = await getVideo(server.url, videoId) 62 const video = await server.videos.get({ id: videoId })
67 expect(body.views).to.equal(3) 63 expect(video.views).to.equal(3)
68 }) 64 })
69 65
70 it('Should view a video only once with the same client IP in the X-Forwarded-For header', async function () { 66 it('Should view a video only once with the same client IP in the X-Forwarded-For header', async function () {
71 this.timeout(20000) 67 this.timeout(20000)
72 68
73 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.4,0.0.0.3,::ffff:127.0.0.1') 69 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.4,0.0.0.3,::ffff:127.0.0.1' })
74 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.5,0.0.0.3,127.0.0.1') 70 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.5,0.0.0.3,127.0.0.1' })
75 71
76 // Wait the repeatable job 72 // Wait the repeatable job
77 await wait(8000) 73 await wait(8000)
78 74
79 const { body } = await getVideo(server.url, videoId) 75 const video = await server.videos.get({ id: videoId })
80 expect(body.views).to.equal(4) 76 expect(video.views).to.equal(4)
81 }) 77 })
82 78
83 it('Should view a video two times with a different client IP in the X-Forwarded-For header', async function () { 79 it('Should view a video two times with a different client IP in the X-Forwarded-For header', async function () {
84 this.timeout(20000) 80 this.timeout(20000)
85 81
86 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.8,0.0.0.6,127.0.0.1') 82 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.8,0.0.0.6,127.0.0.1' })
87 await viewVideo(server.url, videoId, HttpStatusCode.NO_CONTENT_204, '0.0.0.8,0.0.0.7,127.0.0.1') 83 await server.videos.view({ id: videoId, xForwardedFor: '0.0.0.8,0.0.0.7,127.0.0.1' })
88 84
89 // Wait the repeatable job 85 // Wait the repeatable job
90 await wait(8000) 86 await wait(8000)
91 87
92 const { body } = await getVideo(server.url, videoId) 88 const video = await server.videos.get({ id: videoId })
93 expect(body.views).to.equal(6) 89 expect(video.views).to.equal(6)
94 }) 90 })
95 91
96 it('Should rate limit logins', async function () { 92 it('Should rate limit logins', async function () {
97 const user = { username: 'root', password: 'fail' } 93 const user = { username: 'root', password: 'fail' }
98 94
99 for (let i = 0; i < 19; i++) { 95 for (let i = 0; i < 19; i++) {
100 await userLogin(server, user, HttpStatusCode.BAD_REQUEST_400) 96 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
101 } 97 }
102 98
103 await userLogin(server, user, HttpStatusCode.TOO_MANY_REQUESTS_429) 99 await server.login.login({ user, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
104 }) 100 })
105 101
106 it('Should rate limit signup', async function () { 102 it('Should rate limit signup', async function () {
107 for (let i = 0; i < 10; i++) { 103 for (let i = 0; i < 10; i++) {
108 try { 104 try {
109 await registerUser(server.url, 'test' + i, 'password') 105 await server.users.register({ username: 'test' + i })
110 } catch { 106 } catch {
111 // empty 107 // empty
112 } 108 }
113 } 109 }
114 110
115 await registerUser(server.url, 'test42', 'password', HttpStatusCode.TOO_MANY_REQUESTS_429) 111 await server.users.register({ username: 'test42', expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
116 }) 112 })
117 113
118 it('Should not rate limit failed signup', async function () { 114 it('Should not rate limit failed signup', async function () {
@@ -121,10 +117,10 @@ describe('Test application behind a reverse proxy', function () {
121 await wait(7000) 117 await wait(7000)
122 118
123 for (let i = 0; i < 3; i++) { 119 for (let i = 0; i < 3; i++) {
124 await registerUser(server.url, 'test' + i, 'password', HttpStatusCode.CONFLICT_409) 120 await server.users.register({ username: 'test' + i, expectedStatus: HttpStatusCode.CONFLICT_409 })
125 } 121 }
126 122
127 await registerUser(server.url, 'test43', 'password', HttpStatusCode.NO_CONTENT_204) 123 await server.users.register({ username: 'test43', expectedStatus: HttpStatusCode.NO_CONTENT_204 })
128 124
129 }) 125 })
130 126
@@ -135,13 +131,13 @@ describe('Test application behind a reverse proxy', function () {
135 131
136 for (let i = 0; i < 100; i++) { 132 for (let i = 0; i < 100; i++) {
137 try { 133 try {
138 await getVideo(server.url, videoId) 134 await server.videos.get({ id: videoId })
139 } catch { 135 } catch {
140 // don't care if it fails 136 // don't care if it fails
141 } 137 }
142 } 138 }
143 139
144 await getVideo(server.url, videoId, HttpStatusCode.TOO_MANY_REQUESTS_429) 140 await server.videos.get({ id: videoId, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
145 }) 141 })
146 142
147 after(async function () { 143 after(async function () {
diff --git a/server/tests/api/server/services.ts b/server/tests/api/server/services.ts
index ea64e4040..69d030dbb 100644
--- a/server/tests/api/server/services.ts
+++ b/server/tests/api/server/services.ts
@@ -2,23 +2,13 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, setDefaultVideoChannel } from '@shared/extra-utils'
5import { Video, VideoPlaylistPrivacy } from '@shared/models' 6import { Video, VideoPlaylistPrivacy } from '@shared/models'
6import {
7 addVideoInPlaylist,
8 createVideoPlaylist,
9 getOEmbed,
10 getVideosList,
11 ServerInfo,
12 setAccessTokensToServers,
13 setDefaultVideoChannel,
14 uploadVideo
15} from '../../../../shared/extra-utils'
16import { cleanupTests, flushAndRunServer } from '../../../../shared/extra-utils/server/servers'
17 7
18const expect = chai.expect 8const expect = chai.expect
19 9
20describe('Test services', function () { 10describe('Test services', function () {
21 let server: ServerInfo = null 11 let server: PeerTubeServer = null
22 let playlistUUID: string 12 let playlistUUID: string
23 let playlistDisplayName: string 13 let playlistDisplayName: string
24 let video: Video 14 let video: Video
@@ -26,40 +16,34 @@ describe('Test services', function () {
26 before(async function () { 16 before(async function () {
27 this.timeout(30000) 17 this.timeout(30000)
28 18
29 server = await flushAndRunServer(1) 19 server = await createSingleServer(1)
30 20
31 await setAccessTokensToServers([ server ]) 21 await setAccessTokensToServers([ server ])
32 await setDefaultVideoChannel([ server ]) 22 await setDefaultVideoChannel([ server ])
33 23
34 { 24 {
35 const videoAttributes = { 25 const attributes = { name: 'my super name' }
36 name: 'my super name' 26 await server.videos.upload({ attributes })
37 }
38 await uploadVideo(server.url, server.accessToken, videoAttributes)
39 27
40 const res = await getVideosList(server.url) 28 const { data } = await server.videos.list()
41 video = res.body.data[0] 29 video = data[0]
42 } 30 }
43 31
44 { 32 {
45 const res = await createVideoPlaylist({ 33 const created = await server.playlists.create({
46 url: server.url, 34 attributes: {
47 token: server.accessToken,
48 playlistAttrs: {
49 displayName: 'The Life and Times of Scrooge McDuck', 35 displayName: 'The Life and Times of Scrooge McDuck',
50 privacy: VideoPlaylistPrivacy.PUBLIC, 36 privacy: VideoPlaylistPrivacy.PUBLIC,
51 videoChannelId: server.videoChannel.id 37 videoChannelId: server.store.channel.id
52 } 38 }
53 }) 39 })
54 40
55 playlistUUID = res.body.videoPlaylist.uuid 41 playlistUUID = created.uuid
56 playlistDisplayName = 'The Life and Times of Scrooge McDuck' 42 playlistDisplayName = 'The Life and Times of Scrooge McDuck'
57 43
58 await addVideoInPlaylist({ 44 await server.playlists.addElement({
59 url: server.url, 45 playlistId: created.id,
60 token: server.accessToken, 46 attributes: {
61 playlistId: res.body.videoPlaylist.id,
62 elementAttrs: {
63 videoId: video.id 47 videoId: video.id
64 } 48 }
65 }) 49 })
@@ -70,7 +54,7 @@ describe('Test services', function () {
70 for (const basePath of [ '/videos/watch/', '/w/' ]) { 54 for (const basePath of [ '/videos/watch/', '/w/' ]) {
71 const oembedUrl = 'http://localhost:' + server.port + basePath + video.uuid 55 const oembedUrl = 'http://localhost:' + server.port + basePath + video.uuid
72 56
73 const res = await getOEmbed(server.url, oembedUrl) 57 const res = await server.services.getOEmbed({ oembedUrl })
74 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 58 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
75 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` + 59 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
76 'frameborder="0" allowfullscreen></iframe>' 60 'frameborder="0" allowfullscreen></iframe>'
@@ -78,7 +62,7 @@ describe('Test services', function () {
78 62
79 expect(res.body.html).to.equal(expectedHtml) 63 expect(res.body.html).to.equal(expectedHtml)
80 expect(res.body.title).to.equal(video.name) 64 expect(res.body.title).to.equal(video.name)
81 expect(res.body.author_name).to.equal(server.videoChannel.displayName) 65 expect(res.body.author_name).to.equal(server.store.channel.displayName)
82 expect(res.body.width).to.equal(560) 66 expect(res.body.width).to.equal(560)
83 expect(res.body.height).to.equal(315) 67 expect(res.body.height).to.equal(315)
84 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) 68 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl)
@@ -91,14 +75,14 @@ describe('Test services', function () {
91 for (const basePath of [ '/videos/watch/playlist/', '/w/p/' ]) { 75 for (const basePath of [ '/videos/watch/playlist/', '/w/p/' ]) {
92 const oembedUrl = 'http://localhost:' + server.port + basePath + playlistUUID 76 const oembedUrl = 'http://localhost:' + server.port + basePath + playlistUUID
93 77
94 const res = await getOEmbed(server.url, oembedUrl) 78 const res = await server.services.getOEmbed({ oembedUrl })
95 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 79 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
96 `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` + 80 `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` +
97 'frameborder="0" allowfullscreen></iframe>' 81 'frameborder="0" allowfullscreen></iframe>'
98 82
99 expect(res.body.html).to.equal(expectedHtml) 83 expect(res.body.html).to.equal(expectedHtml)
100 expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck') 84 expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck')
101 expect(res.body.author_name).to.equal(server.videoChannel.displayName) 85 expect(res.body.author_name).to.equal(server.store.channel.displayName)
102 expect(res.body.width).to.equal(560) 86 expect(res.body.width).to.equal(560)
103 expect(res.body.height).to.equal(315) 87 expect(res.body.height).to.equal(315)
104 expect(res.body.thumbnail_url).exist 88 expect(res.body.thumbnail_url).exist
@@ -114,14 +98,14 @@ describe('Test services', function () {
114 const maxHeight = 50 98 const maxHeight = 50
115 const maxWidth = 50 99 const maxWidth = 50
116 100
117 const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) 101 const res = await server.services.getOEmbed({ oembedUrl, format, maxHeight, maxWidth })
118 const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' + 102 const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' +
119 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` + 103 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
120 'frameborder="0" allowfullscreen></iframe>' 104 'frameborder="0" allowfullscreen></iframe>'
121 105
122 expect(res.body.html).to.equal(expectedHtml) 106 expect(res.body.html).to.equal(expectedHtml)
123 expect(res.body.title).to.equal(video.name) 107 expect(res.body.title).to.equal(video.name)
124 expect(res.body.author_name).to.equal(server.videoChannel.displayName) 108 expect(res.body.author_name).to.equal(server.store.channel.displayName)
125 expect(res.body.height).to.equal(50) 109 expect(res.body.height).to.equal(50)
126 expect(res.body.width).to.equal(50) 110 expect(res.body.width).to.equal(50)
127 expect(res.body).to.not.have.property('thumbnail_url') 111 expect(res.body).to.not.have.property('thumbnail_url')
diff --git a/server/tests/api/server/stats.ts b/server/tests/api/server/stats.ts
index 304181a6d..5ec771429 100644
--- a/server/tests/api/server/stats.ts
+++ b/server/tests/api/server/stats.ts
@@ -3,33 +3,20 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 addVideoChannel,
7 cleanupTests, 6 cleanupTests,
8 createUser, 7 createMultipleServers,
9 createVideoPlaylist,
10 doubleFollow, 8 doubleFollow,
11 flushAndRunMultipleServers, 9 PeerTubeServer,
12 follow, 10 setAccessTokensToServers,
13 ServerInfo, 11 wait,
14 unfollow, 12 waitJobs
15 updateCustomSubConfig, 13} from '@shared/extra-utils'
16 uploadVideo, 14import { ActivityType, VideoPlaylistPrivacy } from '@shared/models'
17 userLogin,
18 viewVideo,
19 wait
20} from '../../../../shared/extra-utils'
21import { setAccessTokensToServers } from '../../../../shared/extra-utils/index'
22import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23import { getStats } from '../../../../shared/extra-utils/server/stats'
24import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
25import { ServerStats } from '../../../../shared/models/server/server-stats.model'
26import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
27import { ActivityType } from '@shared/models'
28 15
29const expect = chai.expect 16const expect = chai.expect
30 17
31describe('Test stats (excluding redundancy)', function () { 18describe('Test stats (excluding redundancy)', function () {
32 let servers: ServerInfo[] = [] 19 let servers: PeerTubeServer[] = []
33 let channelId 20 let channelId
34 const user = { 21 const user = {
35 username: 'user1', 22 username: 'user1',
@@ -39,31 +26,29 @@ describe('Test stats (excluding redundancy)', function () {
39 before(async function () { 26 before(async function () {
40 this.timeout(60000) 27 this.timeout(60000)
41 28
42 servers = await flushAndRunMultipleServers(3) 29 servers = await createMultipleServers(3)
43 30
44 await setAccessTokensToServers(servers) 31 await setAccessTokensToServers(servers)
45 32
46 await doubleFollow(servers[0], servers[1]) 33 await doubleFollow(servers[0], servers[1])
47 34
48 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 35 await servers[0].users.create({ username: user.username, password: user.password })
49 36
50 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { fixture: 'video_short.webm' }) 37 const { uuid } = await servers[0].videos.upload({ attributes: { fixture: 'video_short.webm' } })
51 const videoUUID = resVideo.body.video.uuid
52 38
53 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'comment') 39 await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
54 40
55 await viewVideo(servers[0].url, videoUUID) 41 await servers[0].videos.view({ id: uuid })
56 42
57 // Wait the video views repeatable job 43 // Wait the video views repeatable job
58 await wait(8000) 44 await wait(8000)
59 45
60 await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) 46 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
61 await waitJobs(servers) 47 await waitJobs(servers)
62 }) 48 })
63 49
64 it('Should have the correct stats on instance 1', async function () { 50 it('Should have the correct stats on instance 1', async function () {
65 const res = await getStats(servers[0].url) 51 const data = await servers[0].stats.get()
66 const data: ServerStats = res.body
67 52
68 expect(data.totalLocalVideoComments).to.equal(1) 53 expect(data.totalLocalVideoComments).to.equal(1)
69 expect(data.totalLocalVideos).to.equal(1) 54 expect(data.totalLocalVideos).to.equal(1)
@@ -78,8 +63,7 @@ describe('Test stats (excluding redundancy)', function () {
78 }) 63 })
79 64
80 it('Should have the correct stats on instance 2', async function () { 65 it('Should have the correct stats on instance 2', async function () {
81 const res = await getStats(servers[1].url) 66 const data = await servers[1].stats.get()
82 const data: ServerStats = res.body
83 67
84 expect(data.totalLocalVideoComments).to.equal(0) 68 expect(data.totalLocalVideoComments).to.equal(0)
85 expect(data.totalLocalVideos).to.equal(0) 69 expect(data.totalLocalVideos).to.equal(0)
@@ -94,8 +78,7 @@ describe('Test stats (excluding redundancy)', function () {
94 }) 78 })
95 79
96 it('Should have the correct stats on instance 3', async function () { 80 it('Should have the correct stats on instance 3', async function () {
97 const res = await getStats(servers[2].url) 81 const data = await servers[2].stats.get()
98 const data: ServerStats = res.body
99 82
100 expect(data.totalLocalVideoComments).to.equal(0) 83 expect(data.totalLocalVideoComments).to.equal(0)
101 expect(data.totalLocalVideos).to.equal(0) 84 expect(data.totalLocalVideos).to.equal(0)
@@ -111,11 +94,10 @@ describe('Test stats (excluding redundancy)', function () {
111 it('Should have the correct total videos stats after an unfollow', async function () { 94 it('Should have the correct total videos stats after an unfollow', async function () {
112 this.timeout(15000) 95 this.timeout(15000)
113 96
114 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 97 await servers[2].follows.unfollow({ target: servers[0] })
115 await waitJobs(servers) 98 await waitJobs(servers)
116 99
117 const res = await getStats(servers[2].url) 100 const data = await servers[2].stats.get()
118 const data: ServerStats = res.body
119 101
120 expect(data.totalVideos).to.equal(0) 102 expect(data.totalVideos).to.equal(0)
121 }) 103 })
@@ -124,18 +106,18 @@ describe('Test stats (excluding redundancy)', function () {
124 const server = servers[0] 106 const server = servers[0]
125 107
126 { 108 {
127 const res = await getStats(server.url) 109 const data = await server.stats.get()
128 const data: ServerStats = res.body 110
129 expect(data.totalDailyActiveUsers).to.equal(1) 111 expect(data.totalDailyActiveUsers).to.equal(1)
130 expect(data.totalWeeklyActiveUsers).to.equal(1) 112 expect(data.totalWeeklyActiveUsers).to.equal(1)
131 expect(data.totalMonthlyActiveUsers).to.equal(1) 113 expect(data.totalMonthlyActiveUsers).to.equal(1)
132 } 114 }
133 115
134 { 116 {
135 await userLogin(server, user) 117 await server.login.getAccessToken(user)
118
119 const data = await server.stats.get()
136 120
137 const res = await getStats(server.url)
138 const data: ServerStats = res.body
139 expect(data.totalDailyActiveUsers).to.equal(2) 121 expect(data.totalDailyActiveUsers).to.equal(2)
140 expect(data.totalWeeklyActiveUsers).to.equal(2) 122 expect(data.totalWeeklyActiveUsers).to.equal(2)
141 expect(data.totalMonthlyActiveUsers).to.equal(2) 123 expect(data.totalMonthlyActiveUsers).to.equal(2)
@@ -146,33 +128,33 @@ describe('Test stats (excluding redundancy)', function () {
146 const server = servers[0] 128 const server = servers[0]
147 129
148 { 130 {
149 const res = await getStats(server.url) 131 const data = await server.stats.get()
150 const data: ServerStats = res.body 132
151 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1) 133 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1)
152 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1) 134 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1)
153 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1) 135 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1)
154 } 136 }
155 137
156 { 138 {
157 const channelAttributes = { 139 const attributes = {
158 name: 'stats_channel', 140 name: 'stats_channel',
159 displayName: 'My stats channel' 141 displayName: 'My stats channel'
160 } 142 }
161 const resChannel = await addVideoChannel(server.url, server.accessToken, channelAttributes) 143 const created = await server.channels.create({ attributes })
162 channelId = resChannel.body.videoChannel.id 144 channelId = created.id
145
146 const data = await server.stats.get()
163 147
164 const res = await getStats(server.url)
165 const data: ServerStats = res.body
166 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1) 148 expect(data.totalLocalDailyActiveVideoChannels).to.equal(1)
167 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1) 149 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(1)
168 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1) 150 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(1)
169 } 151 }
170 152
171 { 153 {
172 await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.webm', channelId }) 154 await server.videos.upload({ attributes: { fixture: 'video_short.webm', channelId } })
155
156 const data = await server.stats.get()
173 157
174 const res = await getStats(server.url)
175 const data: ServerStats = res.body
176 expect(data.totalLocalDailyActiveVideoChannels).to.equal(2) 158 expect(data.totalLocalDailyActiveVideoChannels).to.equal(2)
177 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(2) 159 expect(data.totalLocalWeeklyActiveVideoChannels).to.equal(2)
178 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(2) 160 expect(data.totalLocalMonthlyActiveVideoChannels).to.equal(2)
@@ -183,66 +165,62 @@ describe('Test stats (excluding redundancy)', function () {
183 const server = servers[0] 165 const server = servers[0]
184 166
185 { 167 {
186 const resStats = await getStats(server.url) 168 const data = await server.stats.get()
187 const dataStats: ServerStats = resStats.body 169 expect(data.totalLocalPlaylists).to.equal(0)
188 expect(dataStats.totalLocalPlaylists).to.equal(0)
189 } 170 }
190 171
191 { 172 {
192 await createVideoPlaylist({ 173 await server.playlists.create({
193 url: server.url, 174 attributes: {
194 token: server.accessToken,
195 playlistAttrs: {
196 displayName: 'playlist for count', 175 displayName: 'playlist for count',
197 privacy: VideoPlaylistPrivacy.PUBLIC, 176 privacy: VideoPlaylistPrivacy.PUBLIC,
198 videoChannelId: channelId 177 videoChannelId: channelId
199 } 178 }
200 }) 179 })
201 180
202 const resStats = await getStats(server.url) 181 const data = await server.stats.get()
203 const dataStats: ServerStats = resStats.body 182 expect(data.totalLocalPlaylists).to.equal(1)
204 expect(dataStats.totalLocalPlaylists).to.equal(1)
205 } 183 }
206 }) 184 })
207 185
208 it('Should correctly count video file sizes if transcoding is enabled', async function () { 186 it('Should correctly count video file sizes if transcoding is enabled', async function () {
209 this.timeout(60000) 187 this.timeout(60000)
210 188
211 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 189 await servers[0].config.updateCustomSubConfig({
212 transcoding: { 190 newConfig: {
213 enabled: true, 191 transcoding: {
214 webtorrent: { 192 enabled: true,
215 enabled: true 193 webtorrent: {
216 }, 194 enabled: true
217 hls: { 195 },
218 enabled: true 196 hls: {
219 }, 197 enabled: true
220 resolutions: { 198 },
221 '0p': false, 199 resolutions: {
222 '240p': false, 200 '0p': false,
223 '360p': false, 201 '240p': false,
224 '480p': false, 202 '360p': false,
225 '720p': false, 203 '480p': false,
226 '1080p': false, 204 '720p': false,
227 '1440p': false, 205 '1080p': false,
228 '2160p': false 206 '1440p': false,
207 '2160p': false
208 }
229 } 209 }
230 } 210 }
231 }) 211 })
232 212
233 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video', fixture: 'video_short.webm' }) 213 await servers[0].videos.upload({ attributes: { name: 'video', fixture: 'video_short.webm' } })
234 214
235 await waitJobs(servers) 215 await waitJobs(servers)
236 216
237 { 217 {
238 const res = await getStats(servers[1].url) 218 const data = await servers[1].stats.get()
239 const data: ServerStats = res.body
240 expect(data.totalLocalVideoFilesSize).to.equal(0) 219 expect(data.totalLocalVideoFilesSize).to.equal(0)
241 } 220 }
242 221
243 { 222 {
244 const res = await getStats(servers[0].url) 223 const data = await servers[0].stats.get()
245 const data: ServerStats = res.body
246 expect(data.totalLocalVideoFilesSize).to.be.greaterThan(500000) 224 expect(data.totalLocalVideoFilesSize).to.be.greaterThan(500000)
247 expect(data.totalLocalVideoFilesSize).to.be.lessThan(600000) 225 expect(data.totalLocalVideoFilesSize).to.be.lessThan(600000)
248 } 226 }
@@ -251,27 +229,27 @@ describe('Test stats (excluding redundancy)', function () {
251 it('Should have the correct AP stats', async function () { 229 it('Should have the correct AP stats', async function () {
252 this.timeout(60000) 230 this.timeout(60000)
253 231
254 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 232 await servers[0].config.updateCustomSubConfig({
255 transcoding: { 233 newConfig: {
256 enabled: false 234 transcoding: {
235 enabled: false
236 }
257 } 237 }
258 }) 238 })
259 239
260 const res1 = await getStats(servers[1].url) 240 const first = await servers[1].stats.get()
261 const first = res1.body as ServerStats
262 241
263 for (let i = 0; i < 10; i++) { 242 for (let i = 0; i < 10; i++) {
264 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 243 await servers[0].videos.upload({ attributes: { name: 'video' } })
265 } 244 }
266 245
267 await waitJobs(servers) 246 await waitJobs(servers)
268 247
269 await wait(6000) 248 await wait(6000)
270 249
271 const res2 = await getStats(servers[1].url) 250 const second = await servers[1].stats.get()
272 const second: ServerStats = res2.body
273
274 expect(second.totalActivityPubMessagesProcessed).to.be.greaterThan(first.totalActivityPubMessagesProcessed) 251 expect(second.totalActivityPubMessagesProcessed).to.be.greaterThan(first.totalActivityPubMessagesProcessed)
252
275 const apTypes: ActivityType[] = [ 253 const apTypes: ActivityType[] = [
276 'Create', 'Update', 'Delete', 'Follow', 'Accept', 'Announce', 'Undo', 'Like', 'Reject', 'View', 'Dislike', 'Flag' 254 'Create', 'Update', 'Delete', 'Follow', 'Accept', 'Announce', 'Undo', 'Like', 'Reject', 'View', 'Dislike', 'Flag'
277 ] 255 ]
@@ -291,9 +269,7 @@ describe('Test stats (excluding redundancy)', function () {
291 269
292 await wait(6000) 270 await wait(6000)
293 271
294 const res3 = await getStats(servers[1].url) 272 const third = await servers[1].stats.get()
295 const third: ServerStats = res3.body
296
297 expect(third.totalActivityPubMessagesWaiting).to.equal(0) 273 expect(third.totalActivityPubMessagesWaiting).to.equal(0)
298 expect(third.activityPubMessagesProcessedPerSecond).to.be.lessThan(second.activityPubMessagesProcessedPerSecond) 274 expect(third.activityPubMessagesProcessedPerSecond).to.be.lessThan(second.activityPubMessagesProcessedPerSecond)
299 }) 275 })
diff --git a/server/tests/api/server/tracker.ts b/server/tests/api/server/tracker.ts
index 4b86e0b90..f597ac60c 100644
--- a/server/tests/api/server/tracker.ts
+++ b/server/tests/api/server/tracker.ts
@@ -1,36 +1,23 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await,@typescript-eslint/no-floating-promises */
2 2
3import * as magnetUtil from 'magnet-uri'
4import 'mocha' 3import 'mocha'
5import { 4import * as magnetUtil from 'magnet-uri'
6 cleanupTests,
7 flushAndRunServer,
8 getVideo,
9 killallServers,
10 reRunServer,
11 ServerInfo,
12 uploadVideo
13} from '../../../../shared/extra-utils'
14import { setAccessTokensToServers } from '../../../../shared/extra-utils/index'
15import { VideoDetails } from '../../../../shared/models/videos'
16import * as WebTorrent from 'webtorrent' 5import * as WebTorrent from 'webtorrent'
6import { cleanupTests, createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
17 7
18describe('Test tracker', function () { 8describe('Test tracker', function () {
19 let server: ServerInfo 9 let server: PeerTubeServer
20 let badMagnet: string 10 let badMagnet: string
21 let goodMagnet: string 11 let goodMagnet: string
22 12
23 before(async function () { 13 before(async function () {
24 this.timeout(60000) 14 this.timeout(60000)
25 server = await flushAndRunServer(1) 15 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 16 await setAccessTokensToServers([ server ])
27 17
28 { 18 {
29 const res = await uploadVideo(server.url, server.accessToken, {}) 19 const { uuid } = await server.videos.upload()
30 const videoUUID = res.body.video.uuid 20 const video = await server.videos.get({ id: uuid })
31
32 const resGet = await getVideo(server.url, videoUUID)
33 const video: VideoDetails = resGet.body
34 goodMagnet = video.files[0].magnetUri 21 goodMagnet = video.files[0].magnetUri
35 22
36 const parsed = magnetUtil.decode(goodMagnet) 23 const parsed = magnetUtil.decode(goodMagnet)
@@ -61,8 +48,7 @@ describe('Test tracker', function () {
61 const errCb = () => done(new Error('Tracker is enabled')) 48 const errCb = () => done(new Error('Tracker is enabled'))
62 49
63 killallServers([ server ]) 50 killallServers([ server ])
64 51 .then(() => server.run({ tracker: { enabled: false } }))
65 reRunServer(server, { tracker: { enabled: false } })
66 .then(() => { 52 .then(() => {
67 const webtorrent = new WebTorrent() 53 const webtorrent = new WebTorrent()
68 54
@@ -86,8 +72,7 @@ describe('Test tracker', function () {
86 this.timeout(20000) 72 this.timeout(20000)
87 73
88 killallServers([ server ]) 74 killallServers([ server ])
89 75 .then(() => server.run())
90 reRunServer(server)
91 .then(() => { 76 .then(() => {
92 const webtorrent = new WebTorrent() 77 const webtorrent = new WebTorrent()
93 78
diff --git a/server/tests/api/users/user-subscriptions.ts b/server/tests/api/users/user-subscriptions.ts
index 60676a37b..77b99886d 100644
--- a/server/tests/api/users/user-subscriptions.ts
+++ b/server/tests/api/users/user-subscriptions.ts
@@ -1,42 +1,30 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createUser, 7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 flushAndRunMultipleServers, 9 PeerTubeServer,
10 follow, 10 setAccessTokensToServers,
11 getVideosList, 11 SubscriptionsCommand,
12 unfollow, 12 waitJobs
13 updateVideo, 13} from '@shared/extra-utils'
14 userLogin
15} from '../../../../shared/extra-utils'
16import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index'
17import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
18import { Video, VideoChannel } from '../../../../shared/models/videos'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20import {
21 addUserSubscription,
22 areSubscriptionsExist,
23 getUserSubscription,
24 listUserSubscriptions,
25 listUserSubscriptionVideos,
26 removeUserSubscription
27} from '../../../../shared/extra-utils/users/user-subscriptions'
28 14
29const expect = chai.expect 15const expect = chai.expect
30 16
31describe('Test users subscriptions', function () { 17describe('Test users subscriptions', function () {
32 let servers: ServerInfo[] = [] 18 let servers: PeerTubeServer[] = []
33 const users: { accessToken: string }[] = [] 19 const users: { accessToken: string }[] = []
34 let video3UUID: string 20 let video3UUID: string
35 21
22 let command: SubscriptionsCommand
23
36 before(async function () { 24 before(async function () {
37 this.timeout(120000) 25 this.timeout(120000)
38 26
39 servers = await flushAndRunMultipleServers(3) 27 servers = await createMultipleServers(3)
40 28
41 // Get the access tokens 29 // Get the access tokens
42 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
@@ -47,47 +35,50 @@ describe('Test users subscriptions', function () {
47 { 35 {
48 for (const server of servers) { 36 for (const server of servers) {
49 const user = { username: 'user' + server.serverNumber, password: 'password' } 37 const user = { username: 'user' + server.serverNumber, password: 'password' }
50 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 38 await server.users.create({ username: user.username, password: user.password })
51 39
52 const accessToken = await userLogin(server, user) 40 const accessToken = await server.login.getAccessToken(user)
53 users.push({ accessToken }) 41 users.push({ accessToken })
54 42
55 const videoName1 = 'video 1-' + server.serverNumber 43 const videoName1 = 'video 1-' + server.serverNumber
56 await uploadVideo(server.url, accessToken, { name: videoName1 }) 44 await server.videos.upload({ token: accessToken, attributes: { name: videoName1 } })
57 45
58 const videoName2 = 'video 2-' + server.serverNumber 46 const videoName2 = 'video 2-' + server.serverNumber
59 await uploadVideo(server.url, accessToken, { name: videoName2 }) 47 await server.videos.upload({ token: accessToken, attributes: { name: videoName2 } })
60 } 48 }
61 } 49 }
62 50
63 await waitJobs(servers) 51 await waitJobs(servers)
52
53 command = servers[0].subscriptions
64 }) 54 })
65 55
66 it('Should display videos of server 2 on server 1', async function () { 56 it('Should display videos of server 2 on server 1', async function () {
67 const res = await getVideosList(servers[0].url) 57 const { total } = await servers[0].videos.list()
68 58
69 expect(res.body.total).to.equal(4) 59 expect(total).to.equal(4)
70 }) 60 })
71 61
72 it('User of server 1 should follow user of server 3 and root of server 1', async function () { 62 it('User of server 1 should follow user of server 3 and root of server 1', async function () {
73 this.timeout(60000) 63 this.timeout(60000)
74 64
75 await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 65 await command.add({ token: users[0].accessToken, targetUri: 'user3_channel@localhost:' + servers[2].port })
76 await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) 66 await command.add({ token: users[0].accessToken, targetUri: 'root_channel@localhost:' + servers[0].port })
77 67
78 await waitJobs(servers) 68 await waitJobs(servers)
79 69
80 const res = await uploadVideo(servers[2].url, users[2].accessToken, { name: 'video server 3 added after follow' }) 70 const attributes = { name: 'video server 3 added after follow' }
81 video3UUID = res.body.video.uuid 71 const { uuid } = await servers[2].videos.upload({ token: users[2].accessToken, attributes })
72 video3UUID = uuid
82 73
83 await waitJobs(servers) 74 await waitJobs(servers)
84 }) 75 })
85 76
86 it('Should not display videos of server 3 on server 1', async function () { 77 it('Should not display videos of server 3 on server 1', async function () {
87 const res = await getVideosList(servers[0].url) 78 const { total, data } = await servers[0].videos.list()
79 expect(total).to.equal(4)
88 80
89 expect(res.body.total).to.equal(4) 81 for (const video of data) {
90 for (const video of res.body.data) {
91 expect(video.name).to.not.contain('1-3') 82 expect(video.name).to.not.contain('1-3')
92 expect(video.name).to.not.contain('2-3') 83 expect(video.name).to.not.contain('2-3')
93 expect(video.name).to.not.contain('video server 3 added after follow') 84 expect(video.name).to.not.contain('video server 3 added after follow')
@@ -96,17 +87,17 @@ describe('Test users subscriptions', function () {
96 87
97 it('Should list subscriptions', async function () { 88 it('Should list subscriptions', async function () {
98 { 89 {
99 const res = await listUserSubscriptions({ url: servers[0].url, token: servers[0].accessToken }) 90 const body = await command.list()
100 expect(res.body.total).to.equal(0) 91 expect(body.total).to.equal(0)
101 expect(res.body.data).to.be.an('array') 92 expect(body.data).to.be.an('array')
102 expect(res.body.data).to.have.lengthOf(0) 93 expect(body.data).to.have.lengthOf(0)
103 } 94 }
104 95
105 { 96 {
106 const res = await listUserSubscriptions({ url: servers[0].url, token: users[0].accessToken, sort: 'createdAt' }) 97 const body = await command.list({ token: users[0].accessToken, sort: 'createdAt' })
107 expect(res.body.total).to.equal(2) 98 expect(body.total).to.equal(2)
108 99
109 const subscriptions: VideoChannel[] = res.body.data 100 const subscriptions = body.data
110 expect(subscriptions).to.be.an('array') 101 expect(subscriptions).to.be.an('array')
111 expect(subscriptions).to.have.lengthOf(2) 102 expect(subscriptions).to.have.lengthOf(2)
112 103
@@ -117,8 +108,7 @@ describe('Test users subscriptions', function () {
117 108
118 it('Should get subscription', async function () { 109 it('Should get subscription', async function () {
119 { 110 {
120 const res = await getUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 111 const videoChannel = await command.get({ token: users[0].accessToken, uri: 'user3_channel@localhost:' + servers[2].port })
121 const videoChannel: VideoChannel = res.body
122 112
123 expect(videoChannel.name).to.equal('user3_channel') 113 expect(videoChannel.name).to.equal('user3_channel')
124 expect(videoChannel.host).to.equal('localhost:' + servers[2].port) 114 expect(videoChannel.host).to.equal('localhost:' + servers[2].port)
@@ -128,8 +118,7 @@ describe('Test users subscriptions', function () {
128 } 118 }
129 119
130 { 120 {
131 const res = await getUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) 121 const videoChannel = await command.get({ token: users[0].accessToken, uri: 'root_channel@localhost:' + servers[0].port })
132 const videoChannel: VideoChannel = res.body
133 122
134 expect(videoChannel.name).to.equal('root_channel') 123 expect(videoChannel.name).to.equal('root_channel')
135 expect(videoChannel.host).to.equal('localhost:' + servers[0].port) 124 expect(videoChannel.host).to.equal('localhost:' + servers[0].port)
@@ -147,8 +136,7 @@ describe('Test users subscriptions', function () {
147 'user3_channel@localhost:' + servers[0].port 136 'user3_channel@localhost:' + servers[0].port
148 ] 137 ]
149 138
150 const res = await areSubscriptionsExist(servers[0].url, users[0].accessToken, uris) 139 const body = await command.exist({ token: users[0].accessToken, uris })
151 const body = res.body
152 140
153 expect(body['user3_channel@localhost:' + servers[2].port]).to.be.true 141 expect(body['user3_channel@localhost:' + servers[2].port]).to.be.true
154 expect(body['root2_channel@localhost:' + servers[0].port]).to.be.false 142 expect(body['root2_channel@localhost:' + servers[0].port]).to.be.false
@@ -158,45 +146,31 @@ describe('Test users subscriptions', function () {
158 146
159 it('Should search among subscriptions', async function () { 147 it('Should search among subscriptions', async function () {
160 { 148 {
161 const res = await listUserSubscriptions({ 149 const body = await command.list({ token: users[0].accessToken, sort: '-createdAt', search: 'user3_channel' })
162 url: servers[0].url, 150 expect(body.total).to.equal(1)
163 token: users[0].accessToken, 151 expect(body.data).to.have.lengthOf(1)
164 sort: '-createdAt',
165 search: 'user3_channel'
166 })
167 expect(res.body.total).to.equal(1)
168
169 const subscriptions = res.body.data
170 expect(subscriptions).to.have.lengthOf(1)
171 } 152 }
172 153
173 { 154 {
174 const res = await listUserSubscriptions({ 155 const body = await command.list({ token: users[0].accessToken, sort: '-createdAt', search: 'toto' })
175 url: servers[0].url, 156 expect(body.total).to.equal(0)
176 token: users[0].accessToken, 157 expect(body.data).to.have.lengthOf(0)
177 sort: '-createdAt',
178 search: 'toto'
179 })
180 expect(res.body.total).to.equal(0)
181
182 const subscriptions = res.body.data
183 expect(subscriptions).to.have.lengthOf(0)
184 } 158 }
185 }) 159 })
186 160
187 it('Should list subscription videos', async function () { 161 it('Should list subscription videos', async function () {
188 { 162 {
189 const res = await listUserSubscriptionVideos(servers[0].url, servers[0].accessToken) 163 const body = await command.listVideos()
190 expect(res.body.total).to.equal(0) 164 expect(body.total).to.equal(0)
191 expect(res.body.data).to.be.an('array') 165 expect(body.data).to.be.an('array')
192 expect(res.body.data).to.have.lengthOf(0) 166 expect(body.data).to.have.lengthOf(0)
193 } 167 }
194 168
195 { 169 {
196 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 170 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
197 expect(res.body.total).to.equal(3) 171 expect(body.total).to.equal(3)
198 172
199 const videos: Video[] = res.body.data 173 const videos = body.data
200 expect(videos).to.be.an('array') 174 expect(videos).to.be.an('array')
201 expect(videos).to.have.lengthOf(3) 175 expect(videos).to.have.lengthOf(3)
202 176
@@ -210,22 +184,22 @@ describe('Test users subscriptions', function () {
210 this.timeout(60000) 184 this.timeout(60000)
211 185
212 const videoName = 'video server 1 added after follow' 186 const videoName = 'video server 1 added after follow'
213 await uploadVideo(servers[0].url, servers[0].accessToken, { name: videoName }) 187 await servers[0].videos.upload({ attributes: { name: videoName } })
214 188
215 await waitJobs(servers) 189 await waitJobs(servers)
216 190
217 { 191 {
218 const res = await listUserSubscriptionVideos(servers[0].url, servers[0].accessToken) 192 const body = await command.listVideos()
219 expect(res.body.total).to.equal(0) 193 expect(body.total).to.equal(0)
220 expect(res.body.data).to.be.an('array') 194 expect(body.data).to.be.an('array')
221 expect(res.body.data).to.have.lengthOf(0) 195 expect(body.data).to.have.lengthOf(0)
222 } 196 }
223 197
224 { 198 {
225 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 199 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
226 expect(res.body.total).to.equal(4) 200 expect(body.total).to.equal(4)
227 201
228 const videos: Video[] = res.body.data 202 const videos = body.data
229 expect(videos).to.be.an('array') 203 expect(videos).to.be.an('array')
230 expect(videos).to.have.lengthOf(4) 204 expect(videos).to.have.lengthOf(4)
231 205
@@ -236,10 +210,10 @@ describe('Test users subscriptions', function () {
236 } 210 }
237 211
238 { 212 {
239 const res = await getVideosList(servers[0].url) 213 const { data, total } = await servers[0].videos.list()
214 expect(total).to.equal(5)
240 215
241 expect(res.body.total).to.equal(5) 216 for (const video of data) {
242 for (const video of res.body.data) {
243 expect(video.name).to.not.contain('1-3') 217 expect(video.name).to.not.contain('1-3')
244 expect(video.name).to.not.contain('2-3') 218 expect(video.name).to.not.contain('2-3')
245 expect(video.name).to.not.contain('video server 3 added after follow') 219 expect(video.name).to.not.contain('video server 3 added after follow')
@@ -250,17 +224,16 @@ describe('Test users subscriptions', function () {
250 it('Should have server 1 follow server 3 and display server 3 videos', async function () { 224 it('Should have server 1 follow server 3 and display server 3 videos', async function () {
251 this.timeout(60000) 225 this.timeout(60000)
252 226
253 await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken) 227 await servers[0].follows.follow({ hosts: [ servers[2].url ] })
254 228
255 await waitJobs(servers) 229 await waitJobs(servers)
256 230
257 const res = await getVideosList(servers[0].url) 231 const { data, total } = await servers[0].videos.list()
258 232 expect(total).to.equal(8)
259 expect(res.body.total).to.equal(8)
260 233
261 const names = [ '1-3', '2-3', 'video server 3 added after follow' ] 234 const names = [ '1-3', '2-3', 'video server 3 added after follow' ]
262 for (const name of names) { 235 for (const name of names) {
263 const video = res.body.data.find(v => v.name.indexOf(name) === -1) 236 const video = data.find(v => v.name.includes(name))
264 expect(video).to.not.be.undefined 237 expect(video).to.not.be.undefined
265 } 238 }
266 }) 239 })
@@ -268,14 +241,14 @@ describe('Test users subscriptions', function () {
268 it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () { 241 it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () {
269 this.timeout(60000) 242 this.timeout(60000)
270 243
271 await unfollow(servers[0].url, servers[0].accessToken, servers[2]) 244 await servers[0].follows.unfollow({ target: servers[2] })
272 245
273 await waitJobs(servers) 246 await waitJobs(servers)
274 247
275 const res = await getVideosList(servers[0].url) 248 const { total, data } = await servers[0].videos.list()
249 expect(total).to.equal(5)
276 250
277 expect(res.body.total).to.equal(5) 251 for (const video of data) {
278 for (const video of res.body.data) {
279 expect(video.name).to.not.contain('1-3') 252 expect(video.name).to.not.contain('1-3')
280 expect(video.name).to.not.contain('2-3') 253 expect(video.name).to.not.contain('2-3')
281 expect(video.name).to.not.contain('video server 3 added after follow') 254 expect(video.name).to.not.contain('video server 3 added after follow')
@@ -284,17 +257,17 @@ describe('Test users subscriptions', function () {
284 257
285 it('Should still list subscription videos', async function () { 258 it('Should still list subscription videos', async function () {
286 { 259 {
287 const res = await listUserSubscriptionVideos(servers[0].url, servers[0].accessToken) 260 const body = await command.listVideos()
288 expect(res.body.total).to.equal(0) 261 expect(body.total).to.equal(0)
289 expect(res.body.data).to.be.an('array') 262 expect(body.data).to.be.an('array')
290 expect(res.body.data).to.have.lengthOf(0) 263 expect(body.data).to.have.lengthOf(0)
291 } 264 }
292 265
293 { 266 {
294 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 267 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
295 expect(res.body.total).to.equal(4) 268 expect(body.total).to.equal(4)
296 269
297 const videos: Video[] = res.body.data 270 const videos = body.data
298 expect(videos).to.be.an('array') 271 expect(videos).to.be.an('array')
299 expect(videos).to.have.lengthOf(4) 272 expect(videos).to.have.lengthOf(4)
300 273
@@ -308,58 +281,55 @@ describe('Test users subscriptions', function () {
308 it('Should update a video of server 3 and see the updated video on server 1', async function () { 281 it('Should update a video of server 3 and see the updated video on server 1', async function () {
309 this.timeout(30000) 282 this.timeout(30000)
310 283
311 await updateVideo(servers[2].url, users[2].accessToken, video3UUID, { name: 'video server 3 added after follow updated' }) 284 await servers[2].videos.update({ id: video3UUID, attributes: { name: 'video server 3 added after follow updated' } })
312 285
313 await waitJobs(servers) 286 await waitJobs(servers)
314 287
315 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 288 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
316 const videos: Video[] = res.body.data 289 expect(body.data[2].name).to.equal('video server 3 added after follow updated')
317 expect(videos[2].name).to.equal('video server 3 added after follow updated')
318 }) 290 })
319 291
320 it('Should remove user of server 3 subscription', async function () { 292 it('Should remove user of server 3 subscription', async function () {
321 this.timeout(30000) 293 this.timeout(30000)
322 294
323 await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 295 await command.remove({ token: users[0].accessToken, uri: 'user3_channel@localhost:' + servers[2].port })
324 296
325 await waitJobs(servers) 297 await waitJobs(servers)
326 }) 298 })
327 299
328 it('Should not display its videos anymore', async function () { 300 it('Should not display its videos anymore', async function () {
329 { 301 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
330 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 302 expect(body.total).to.equal(1)
331 expect(res.body.total).to.equal(1)
332 303
333 const videos: Video[] = res.body.data 304 const videos = body.data
334 expect(videos).to.be.an('array') 305 expect(videos).to.be.an('array')
335 expect(videos).to.have.lengthOf(1) 306 expect(videos).to.have.lengthOf(1)
336 307
337 expect(videos[0].name).to.equal('video server 1 added after follow') 308 expect(videos[0].name).to.equal('video server 1 added after follow')
338 }
339 }) 309 })
340 310
341 it('Should remove the root subscription and not display the videos anymore', async function () { 311 it('Should remove the root subscription and not display the videos anymore', async function () {
342 this.timeout(30000) 312 this.timeout(30000)
343 313
344 await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) 314 await command.remove({ token: users[0].accessToken, uri: 'root_channel@localhost:' + servers[0].port })
345 315
346 await waitJobs(servers) 316 await waitJobs(servers)
347 317
348 { 318 {
349 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 319 const body = await command.list({ token: users[0].accessToken, sort: 'createdAt' })
350 expect(res.body.total).to.equal(0) 320 expect(body.total).to.equal(0)
351 321
352 const videos: Video[] = res.body.data 322 const videos = body.data
353 expect(videos).to.be.an('array') 323 expect(videos).to.be.an('array')
354 expect(videos).to.have.lengthOf(0) 324 expect(videos).to.have.lengthOf(0)
355 } 325 }
356 }) 326 })
357 327
358 it('Should correctly display public videos on server 1', async function () { 328 it('Should correctly display public videos on server 1', async function () {
359 const res = await getVideosList(servers[0].url) 329 const { total, data } = await servers[0].videos.list()
330 expect(total).to.equal(5)
360 331
361 expect(res.body.total).to.equal(5) 332 for (const video of data) {
362 for (const video of res.body.data) {
363 expect(video.name).to.not.contain('1-3') 333 expect(video.name).to.not.contain('1-3')
364 expect(video.name).to.not.contain('2-3') 334 expect(video.name).to.not.contain('2-3')
365 expect(video.name).to.not.contain('video server 3 added after follow updated') 335 expect(video.name).to.not.contain('video server 3 added after follow updated')
@@ -369,15 +339,15 @@ describe('Test users subscriptions', function () {
369 it('Should follow user of server 3 again', async function () { 339 it('Should follow user of server 3 again', async function () {
370 this.timeout(60000) 340 this.timeout(60000)
371 341
372 await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) 342 await command.add({ token: users[0].accessToken, targetUri: 'user3_channel@localhost:' + servers[2].port })
373 343
374 await waitJobs(servers) 344 await waitJobs(servers)
375 345
376 { 346 {
377 const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt') 347 const body = await command.listVideos({ token: users[0].accessToken, sort: 'createdAt' })
378 expect(res.body.total).to.equal(3) 348 expect(body.total).to.equal(3)
379 349
380 const videos: Video[] = res.body.data 350 const videos = body.data
381 expect(videos).to.be.an('array') 351 expect(videos).to.be.an('array')
382 expect(videos).to.have.lengthOf(3) 352 expect(videos).to.have.lengthOf(3)
383 353
@@ -387,10 +357,10 @@ describe('Test users subscriptions', function () {
387 } 357 }
388 358
389 { 359 {
390 const res = await getVideosList(servers[0].url) 360 const { total, data } = await servers[0].videos.list()
361 expect(total).to.equal(5)
391 362
392 expect(res.body.total).to.equal(5) 363 for (const video of data) {
393 for (const video of res.body.data) {
394 expect(video.name).to.not.contain('1-3') 364 expect(video.name).to.not.contain('1-3')
395 expect(video.name).to.not.contain('2-3') 365 expect(video.name).to.not.contain('2-3')
396 expect(video.name).to.not.contain('video server 3 added after follow updated') 366 expect(video.name).to.not.contain('video server 3 added after follow updated')
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
index f60c66e4b..d0ca82b07 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -1,34 +1,30 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { Account } from '../../../../shared/models/actors' 4import * as chai from 'chai'
6import { 5import {
6 checkActorFilesWereRemoved,
7 checkTmpIsEmpty, 7 checkTmpIsEmpty,
8 checkVideoFilesWereRemoved, 8 checkVideoFilesWereRemoved,
9 cleanupTests, 9 cleanupTests,
10 createUser, 10 createMultipleServers,
11 doubleFollow, 11 doubleFollow,
12 flushAndRunMultipleServers, 12 PeerTubeServer,
13 getAccountVideos, 13 saveVideoInServers,
14 getVideoChannelsList, 14 setAccessTokensToServers,
15 removeUser, 15 testImage,
16 updateMyUser, 16 waitJobs
17 userLogin 17} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 18import { MyUser } from '@shared/models'
19import { getMyUserInformation, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../../../shared/extra-utils/index'
20import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../../../shared/extra-utils/users/accounts'
21import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
22import { User } from '../../../../shared/models/users'
23import { VideoChannel } from '../../../../shared/models/videos'
24import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
25 19
26const expect = chai.expect 20const expect = chai.expect
27 21
28describe('Test users with multiple servers', function () { 22describe('Test users with multiple servers', function () {
29 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
30 let user: User 24
25 let user: MyUser
31 let userId: number 26 let userId: number
27
32 let videoUUID: string 28 let videoUUID: string
33 let userAccessToken: string 29 let userAccessToken: string
34 let userAvatarFilename: string 30 let userAvatarFilename: string
@@ -36,7 +32,7 @@ describe('Test users with multiple servers', function () {
36 before(async function () { 32 before(async function () {
37 this.timeout(120_000) 33 this.timeout(120_000)
38 34
39 servers = await flushAndRunMultipleServers(3) 35 servers = await createMultipleServers(3)
40 36
41 // Get the access tokens 37 // Get the access tokens
42 await setAccessTokensToServers(servers) 38 await setAccessTokensToServers(servers)
@@ -49,43 +45,31 @@ describe('Test users with multiple servers', function () {
49 await doubleFollow(servers[1], servers[2]) 45 await doubleFollow(servers[1], servers[2])
50 46
51 // The root user of server 1 is propagated to servers 2 and 3 47 // The root user of server 1 is propagated to servers 2 and 3
52 await uploadVideo(servers[0].url, servers[0].accessToken, {}) 48 await servers[0].videos.upload()
53 49
54 { 50 {
55 const user = { 51 const username = 'user1'
56 username: 'user1', 52 const created = await servers[0].users.create({ username })
57 password: 'password' 53 userId = created.id
58 } 54 userAccessToken = await servers[0].login.getAccessToken(username)
59 const res = await createUser({
60 url: servers[0].url,
61 accessToken: servers[0].accessToken,
62 username: user.username,
63 password: user.password
64 })
65 userId = res.body.user.id
66 userAccessToken = await userLogin(servers[0], user)
67 } 55 }
68 56
69 { 57 {
70 const resVideo = await uploadVideo(servers[0].url, userAccessToken, {}) 58 const { uuid } = await servers[0].videos.upload({ token: userAccessToken })
71 videoUUID = resVideo.body.video.uuid 59 videoUUID = uuid
72 }
73 60
74 await waitJobs(servers) 61 await waitJobs(servers)
62
63 await saveVideoInServers(servers, videoUUID)
64 }
75 }) 65 })
76 66
77 it('Should be able to update my display name', async function () { 67 it('Should be able to update my display name', async function () {
78 this.timeout(10000) 68 this.timeout(10000)
79 69
80 await updateMyUser({ 70 await servers[0].users.updateMe({ displayName: 'my super display name' })
81 url: servers[0].url,
82 accessToken: servers[0].accessToken,
83 displayName: 'my super display name'
84 })
85
86 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
87 user = res.body
88 71
72 user = await servers[0].users.getMyInfo()
89 expect(user.account.displayName).to.equal('my super display name') 73 expect(user.account.displayName).to.equal('my super display name')
90 74
91 await waitJobs(servers) 75 await waitJobs(servers)
@@ -94,14 +78,9 @@ describe('Test users with multiple servers', function () {
94 it('Should be able to update my description', async function () { 78 it('Should be able to update my description', async function () {
95 this.timeout(10_000) 79 this.timeout(10_000)
96 80
97 await updateMyUser({ 81 await servers[0].users.updateMe({ description: 'my super description updated' })
98 url: servers[0].url,
99 accessToken: servers[0].accessToken,
100 description: 'my super description updated'
101 })
102 82
103 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 83 user = await servers[0].users.getMyInfo()
104 user = res.body
105 expect(user.account.displayName).to.equal('my super display name') 84 expect(user.account.displayName).to.equal('my super display name')
106 expect(user.account.description).to.equal('my super description updated') 85 expect(user.account.description).to.equal('my super description updated')
107 86
@@ -113,15 +92,9 @@ describe('Test users with multiple servers', function () {
113 92
114 const fixture = 'avatar2.png' 93 const fixture = 'avatar2.png'
115 94
116 await updateMyAvatar({ 95 await servers[0].users.updateMyAvatar({ fixture })
117 url: servers[0].url,
118 accessToken: servers[0].accessToken,
119 fixture
120 })
121
122 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
123 user = res.body
124 96
97 user = await servers[0].users.getMyInfo()
125 userAvatarFilename = user.account.avatar.path 98 userAvatarFilename = user.account.avatar.path
126 99
127 await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png') 100 await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png')
@@ -133,13 +106,12 @@ describe('Test users with multiple servers', function () {
133 let createdAt: string | Date 106 let createdAt: string | Date
134 107
135 for (const server of servers) { 108 for (const server of servers) {
136 const resAccounts = await getAccountsList(server.url, '-createdAt') 109 const body = await server.accounts.list({ sort: '-createdAt' })
137 110
138 const resList = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account 111 const resList = body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port)
139 expect(resList).not.to.be.undefined 112 expect(resList).not.to.be.undefined
140 113
141 const resAccount = await getAccount(server.url, resList.name + '@' + resList.host) 114 const account = await server.accounts.get({ accountName: resList.name + '@' + resList.host })
142 const account = resAccount.body as Account
143 115
144 if (!createdAt) createdAt = account.createdAt 116 if (!createdAt) createdAt = account.createdAt
145 117
@@ -161,31 +133,29 @@ describe('Test users with multiple servers', function () {
161 133
162 it('Should list account videos', async function () { 134 it('Should list account videos', async function () {
163 for (const server of servers) { 135 for (const server of servers) {
164 const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5) 136 const { total, data } = await server.videos.listByAccount({ handle: 'user1@localhost:' + servers[0].port })
165 137
166 expect(res.body.total).to.equal(1) 138 expect(total).to.equal(1)
167 expect(res.body.data).to.be.an('array') 139 expect(data).to.be.an('array')
168 expect(res.body.data).to.have.lengthOf(1) 140 expect(data).to.have.lengthOf(1)
169 expect(res.body.data[0].uuid).to.equal(videoUUID) 141 expect(data[0].uuid).to.equal(videoUUID)
170 } 142 }
171 }) 143 })
172 144
173 it('Should search through account videos', async function () { 145 it('Should search through account videos', async function () {
174 this.timeout(10_000) 146 this.timeout(10_000)
175 147
176 const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'Kami no chikara' }) 148 const created = await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'Kami no chikara' } })
177 149
178 await waitJobs(servers) 150 await waitJobs(servers)
179 151
180 for (const server of servers) { 152 for (const server of servers) {
181 const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5, undefined, { 153 const { total, data } = await server.videos.listByAccount({ handle: 'user1@localhost:' + servers[0].port, search: 'Kami' })
182 search: 'Kami' 154
183 }) 155 expect(total).to.equal(1)
184 156 expect(data).to.be.an('array')
185 expect(res.body.total).to.equal(1) 157 expect(data).to.have.lengthOf(1)
186 expect(res.body.data).to.be.an('array') 158 expect(data[0].uuid).to.equal(created.uuid)
187 expect(res.body.data).to.have.lengthOf(1)
188 expect(res.body.data[0].uuid).to.equal(resVideo.body.video.uuid)
189 } 159 }
190 }) 160 })
191 161
@@ -193,32 +163,28 @@ describe('Test users with multiple servers', function () {
193 this.timeout(10_000) 163 this.timeout(10_000)
194 164
195 for (const server of servers) { 165 for (const server of servers) {
196 const resAccounts = await getAccountsList(server.url, '-createdAt') 166 const body = await server.accounts.list({ sort: '-createdAt' })
197 167
198 const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account 168 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port)
199 expect(accountDeleted).not.to.be.undefined 169 expect(accountDeleted).not.to.be.undefined
200 170
201 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 171 const { data } = await server.channels.list()
202 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 172 const videoChannelDeleted = data.find(a => a.displayName === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port)
203 return a.displayName === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port
204 }) as VideoChannel
205 expect(videoChannelDeleted).not.to.be.undefined 173 expect(videoChannelDeleted).not.to.be.undefined
206 } 174 }
207 175
208 await removeUser(servers[0].url, userId, servers[0].accessToken) 176 await servers[0].users.remove({ userId })
209 177
210 await waitJobs(servers) 178 await waitJobs(servers)
211 179
212 for (const server of servers) { 180 for (const server of servers) {
213 const resAccounts = await getAccountsList(server.url, '-createdAt') 181 const body = await server.accounts.list({ sort: '-createdAt' })
214 182
215 const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account 183 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port)
216 expect(accountDeleted).to.be.undefined 184 expect(accountDeleted).to.be.undefined
217 185
218 const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) 186 const { data } = await server.channels.list()
219 const videoChannelDeleted = resVideoChannels.body.data.find(a => { 187 const videoChannelDeleted = data.find(a => a.name === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port)
220 return a.name === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port
221 }) as VideoChannel
222 expect(videoChannelDeleted).to.be.undefined 188 expect(videoChannelDeleted).to.be.undefined
223 } 189 }
224 }) 190 })
@@ -231,7 +197,7 @@ describe('Test users with multiple servers', function () {
231 197
232 it('Should not have video files', async () => { 198 it('Should not have video files', async () => {
233 for (const server of servers) { 199 for (const server of servers) {
234 await checkVideoFilesWereRemoved(videoUUID, server.internalServerNumber) 200 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
235 } 201 }
236 }) 202 })
237 203
diff --git a/server/tests/api/users/users-verification.ts b/server/tests/api/users/users-verification.ts
index e0f2f2112..f54463359 100644
--- a/server/tests/api/users/users-verification.ts
+++ b/server/tests/api/users/users-verification.ts
@@ -1,30 +1,14 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { 4import * as chai from 'chai'
6 cleanupTests, 5import { cleanupTests, createSingleServer, MockSmtpServer, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
7 flushAndRunServer, 6import { HttpStatusCode } from '@shared/models'
8 getMyUserInformation,
9 getUserInformation,
10 login,
11 registerUser,
12 ServerInfo,
13 updateCustomSubConfig,
14 updateMyUser,
15 userLogin,
16 verifyEmail
17} from '../../../../shared/extra-utils'
18import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
19import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
20import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
21import { User } from '../../../../shared/models/users'
22import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
23 7
24const expect = chai.expect 8const expect = chai.expect
25 9
26describe('Test users account verification', function () { 10describe('Test users account verification', function () {
27 let server: ServerInfo 11 let server: PeerTubeServer
28 let userId: number 12 let userId: number
29 let userAccessToken: string 13 let userAccessToken: string
30 let verificationString: string 14 let verificationString: string
@@ -50,7 +34,7 @@ describe('Test users account verification', function () {
50 port 34 port
51 } 35 }
52 } 36 }
53 server = await flushAndRunServer(1, overrideConfig) 37 server = await createSingleServer(1, overrideConfig)
54 38
55 await setAccessTokensToServers([ server ]) 39 await setAccessTokensToServers([ server ])
56 }) 40 })
@@ -58,15 +42,17 @@ describe('Test users account verification', function () {
58 it('Should register user and send verification email if verification required', async function () { 42 it('Should register user and send verification email if verification required', async function () {
59 this.timeout(30000) 43 this.timeout(30000)
60 44
61 await updateCustomSubConfig(server.url, server.accessToken, { 45 await server.config.updateCustomSubConfig({
62 signup: { 46 newConfig: {
63 enabled: true, 47 signup: {
64 requiresEmailVerification: true, 48 enabled: true,
65 limit: 10 49 requiresEmailVerification: true,
50 limit: 10
51 }
66 } 52 }
67 }) 53 })
68 54
69 await registerUser(server.url, user1.username, user1.password) 55 await server.users.register(user1)
70 56
71 await waitJobs(server) 57 await waitJobs(server)
72 expectedEmailsLength++ 58 expectedEmailsLength++
@@ -85,23 +71,23 @@ describe('Test users account verification', function () {
85 71
86 userId = parseInt(userIdMatches[1], 10) 72 userId = parseInt(userIdMatches[1], 10)
87 73
88 const resUserInfo = await getUserInformation(server.url, server.accessToken, userId) 74 const body = await server.users.get({ userId })
89 expect(resUserInfo.body.emailVerified).to.be.false 75 expect(body.emailVerified).to.be.false
90 }) 76 })
91 77
92 it('Should not allow login for user with unverified email', async function () { 78 it('Should not allow login for user with unverified email', async function () {
93 const resLogin = await login(server.url, server.client, user1, HttpStatusCode.BAD_REQUEST_400) 79 const { detail } = await server.login.login({ user: user1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
94 expect(resLogin.body.detail).to.contain('User email is not verified.') 80 expect(detail).to.contain('User email is not verified.')
95 }) 81 })
96 82
97 it('Should verify the user via email and allow login', async function () { 83 it('Should verify the user via email and allow login', async function () {
98 await verifyEmail(server.url, userId, verificationString) 84 await server.users.verifyEmail({ userId, verificationString })
99 85
100 const res = await login(server.url, server.client, user1) 86 const body = await server.login.login({ user: user1 })
101 userAccessToken = res.body.access_token 87 userAccessToken = body.access_token
102 88
103 const resUserVerified = await getUserInformation(server.url, server.accessToken, userId) 89 const user = await server.users.get({ userId })
104 expect(resUserVerified.body.emailVerified).to.be.true 90 expect(user.emailVerified).to.be.true
105 }) 91 })
106 92
107 it('Should be able to change the user email', async function () { 93 it('Should be able to change the user email', async function () {
@@ -110,9 +96,8 @@ describe('Test users account verification', function () {
110 let updateVerificationString: string 96 let updateVerificationString: string
111 97
112 { 98 {
113 await updateMyUser({ 99 await server.users.updateMe({
114 url: server.url, 100 token: userAccessToken,
115 accessToken: userAccessToken,
116 email: 'updated@example.com', 101 email: 'updated@example.com',
117 currentPassword: user1.password 102 currentPassword: user1.password
118 }) 103 })
@@ -128,19 +113,15 @@ describe('Test users account verification', function () {
128 } 113 }
129 114
130 { 115 {
131 const res = await getMyUserInformation(server.url, userAccessToken) 116 const me = await server.users.getMyInfo({ token: userAccessToken })
132 const me: User = res.body
133
134 expect(me.email).to.equal('user_1@example.com') 117 expect(me.email).to.equal('user_1@example.com')
135 expect(me.pendingEmail).to.equal('updated@example.com') 118 expect(me.pendingEmail).to.equal('updated@example.com')
136 } 119 }
137 120
138 { 121 {
139 await verifyEmail(server.url, userId, updateVerificationString, true) 122 await server.users.verifyEmail({ userId, verificationString: updateVerificationString, isPendingEmail: true })
140
141 const res = await getMyUserInformation(server.url, userAccessToken)
142 const me: User = res.body
143 123
124 const me = await server.users.getMyInfo({ token: userAccessToken })
144 expect(me.email).to.equal('updated@example.com') 125 expect(me.email).to.equal('updated@example.com')
145 expect(me.pendingEmail).to.be.null 126 expect(me.pendingEmail).to.be.null
146 } 127 }
@@ -148,35 +129,39 @@ describe('Test users account verification', function () {
148 129
149 it('Should register user not requiring email verification if setting not enabled', async function () { 130 it('Should register user not requiring email verification if setting not enabled', async function () {
150 this.timeout(5000) 131 this.timeout(5000)
151 await updateCustomSubConfig(server.url, server.accessToken, { 132 await server.config.updateCustomSubConfig({
152 signup: { 133 newConfig: {
153 enabled: true, 134 signup: {
154 requiresEmailVerification: false, 135 enabled: true,
155 limit: 10 136 requiresEmailVerification: false,
137 limit: 10
138 }
156 } 139 }
157 }) 140 })
158 141
159 await registerUser(server.url, user2.username, user2.password) 142 await server.users.register(user2)
160 143
161 await waitJobs(server) 144 await waitJobs(server)
162 expect(emails).to.have.lengthOf(expectedEmailsLength) 145 expect(emails).to.have.lengthOf(expectedEmailsLength)
163 146
164 const accessToken = await userLogin(server, user2) 147 const accessToken = await server.login.getAccessToken(user2)
165 148
166 const resMyUserInfo = await getMyUserInformation(server.url, accessToken) 149 const user = await server.users.getMyInfo({ token: accessToken })
167 expect(resMyUserInfo.body.emailVerified).to.be.null 150 expect(user.emailVerified).to.be.null
168 }) 151 })
169 152
170 it('Should allow login for user with unverified email when setting later enabled', async function () { 153 it('Should allow login for user with unverified email when setting later enabled', async function () {
171 await updateCustomSubConfig(server.url, server.accessToken, { 154 await server.config.updateCustomSubConfig({
172 signup: { 155 newConfig: {
173 enabled: true, 156 signup: {
174 requiresEmailVerification: true, 157 enabled: true,
175 limit: 10 158 requiresEmailVerification: true,
159 limit: 10
160 }
176 } 161 }
177 }) 162 })
178 163
179 await userLogin(server, user2) 164 await server.login.getAccessToken(user2)
180 }) 165 })
181 166
182 after(async function () { 167 after(async function () {
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 87ba775f6..1419ae820 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -2,63 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { AbuseState, AbuseUpdate, MyUser, User, UserRole, Video, VideoPlaylistType } from '@shared/models'
6import { CustomConfig, OAuth2ErrorCode } from '@shared/models/server'
7import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
8import { 5import {
9 addVideoCommentThread,
10 blockUser,
11 cleanupTests, 6 cleanupTests,
12 closeAllSequelize, 7 createSingleServer,
13 createUser,
14 deleteMe,
15 flushAndRunServer,
16 getAccountRatings,
17 getAdminAbusesList,
18 getBlacklistedVideosList,
19 getCustomConfig,
20 getMyUserInformation,
21 getMyUserVideoQuotaUsed,
22 getMyUserVideoRating,
23 getUserInformation,
24 getUsersList,
25 getUsersListPaginationAndSort,
26 getVideoChannel,
27 getVideosList,
28 installPlugin,
29 killallServers, 8 killallServers,
30 login,
31 makePutBodyRequest, 9 makePutBodyRequest,
32 rateVideo, 10 PeerTubeServer,
33 registerUserWithChannel, 11 setAccessTokensToServers,
34 removeUser,
35 removeVideo,
36 reportAbuse,
37 reRunServer,
38 ServerInfo,
39 setTokenField,
40 testImage, 12 testImage,
41 unblockUser,
42 updateAbuse,
43 updateCustomSubConfig,
44 updateMyAvatar,
45 updateMyUser,
46 updateUser,
47 uploadVideo,
48 userLogin,
49 waitJobs 13 waitJobs
50} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
51import { follow } from '../../../../shared/extra-utils/server/follows' 15import { AbuseState, HttpStatusCode, OAuth2ErrorCode, UserAdminFlag, UserRole, Video, VideoPlaylistType } from '@shared/models'
52import { logout, refreshToken, setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
53import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
54import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
55 16
56const expect = chai.expect 17const expect = chai.expect
57 18
58describe('Test users', function () { 19describe('Test users', function () {
59 let server: ServerInfo 20 let server: PeerTubeServer
60 let accessToken: string 21 let token: string
61 let accessTokenUser: string 22 let userToken: string
62 let videoId: number 23 let videoId: number
63 let userId: number 24 let userId: number
64 const user = { 25 const user = {
@@ -69,7 +30,7 @@ describe('Test users', function () {
69 before(async function () { 30 before(async function () {
70 this.timeout(30000) 31 this.timeout(30000)
71 32
72 server = await flushAndRunServer(1, { 33 server = await createSingleServer(1, {
73 rates_limit: { 34 rates_limit: {
74 login: { 35 login: {
75 max: 30 36 max: 30
@@ -79,7 +40,7 @@ describe('Test users', function () {
79 40
80 await setAccessTokensToServers([ server ]) 41 await setAccessTokensToServers([ server ])
81 42
82 await installPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-theme-background-red' }) 43 await server.plugins.install({ npmName: 'peertube-theme-background-red' })
83 }) 44 })
84 45
85 describe('OAuth client', function () { 46 describe('OAuth client', function () {
@@ -90,158 +51,156 @@ describe('Test users', function () {
90 it('Should remove the last client') 51 it('Should remove the last client')
91 52
92 it('Should not login with an invalid client id', async function () { 53 it('Should not login with an invalid client id', async function () {
93 const client = { id: 'client', secret: server.client.secret } 54 const client = { id: 'client', secret: server.store.client.secret }
94 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) 55 const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
95 56
96 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) 57 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT)
97 expect(res.body.error).to.contain('client is invalid') 58 expect(body.error).to.contain('client is invalid')
98 expect(res.body.type.startsWith('https://')).to.be.true 59 expect(body.type.startsWith('https://')).to.be.true
99 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) 60 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT)
100 }) 61 })
101 62
102 it('Should not login with an invalid client secret', async function () { 63 it('Should not login with an invalid client secret', async function () {
103 const client = { id: server.client.id, secret: 'coucou' } 64 const client = { id: server.store.client.id, secret: 'coucou' }
104 const res = await login(server.url, client, server.user, HttpStatusCode.BAD_REQUEST_400) 65 const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
105 66
106 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT) 67 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT)
107 expect(res.body.error).to.contain('client is invalid') 68 expect(body.error).to.contain('client is invalid')
108 expect(res.body.type.startsWith('https://')).to.be.true 69 expect(body.type.startsWith('https://')).to.be.true
109 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT) 70 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT)
110 }) 71 })
111 }) 72 })
112 73
113 describe('Login', function () { 74 describe('Login', function () {
114 75
115 it('Should not login with an invalid username', async function () { 76 it('Should not login with an invalid username', async function () {
116 const user = { username: 'captain crochet', password: server.user.password } 77 const user = { username: 'captain crochet', password: server.store.user.password }
117 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) 78 const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
118 79
119 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) 80 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT)
120 expect(res.body.error).to.contain('credentials are invalid') 81 expect(body.error).to.contain('credentials are invalid')
121 expect(res.body.type.startsWith('https://')).to.be.true 82 expect(body.type.startsWith('https://')).to.be.true
122 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) 83 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT)
123 }) 84 })
124 85
125 it('Should not login with an invalid password', async function () { 86 it('Should not login with an invalid password', async function () {
126 const user = { username: server.user.username, password: 'mew_three' } 87 const user = { username: server.store.user.username, password: 'mew_three' }
127 const res = await login(server.url, server.client, user, HttpStatusCode.BAD_REQUEST_400) 88 const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
128 89
129 expect(res.body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT) 90 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT)
130 expect(res.body.error).to.contain('credentials are invalid') 91 expect(body.error).to.contain('credentials are invalid')
131 expect(res.body.type.startsWith('https://')).to.be.true 92 expect(body.type.startsWith('https://')).to.be.true
132 expect(res.body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT) 93 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT)
133 }) 94 })
134 95
135 it('Should not be able to upload a video', async function () { 96 it('Should not be able to upload a video', async function () {
136 accessToken = 'my_super_token' 97 token = 'my_super_token'
137 98
138 const videoAttributes = {} 99 await server.videos.upload({ token, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
139 await uploadVideo(server.url, accessToken, videoAttributes, HttpStatusCode.UNAUTHORIZED_401)
140 }) 100 })
141 101
142 it('Should not be able to follow', async function () { 102 it('Should not be able to follow', async function () {
143 accessToken = 'my_super_token' 103 token = 'my_super_token'
144 await follow(server.url, [ 'http://example.com' ], accessToken, HttpStatusCode.UNAUTHORIZED_401) 104
105 await server.follows.follow({
106 hosts: [ 'http://example.com' ],
107 token,
108 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
109 })
145 }) 110 })
146 111
147 it('Should not be able to unfollow') 112 it('Should not be able to unfollow')
148 113
149 it('Should be able to login', async function () { 114 it('Should be able to login', async function () {
150 const res = await login(server.url, server.client, server.user, HttpStatusCode.OK_200) 115 const body = await server.login.login({ expectedStatus: HttpStatusCode.OK_200 })
151 116
152 accessToken = res.body.access_token 117 token = body.access_token
153 }) 118 })
154 119
155 it('Should be able to login with an insensitive username', async function () { 120 it('Should be able to login with an insensitive username', async function () {
156 const user = { username: 'RoOt', password: server.user.password } 121 const user = { username: 'RoOt', password: server.store.user.password }
157 await login(server.url, server.client, user, HttpStatusCode.OK_200) 122 await server.login.login({ user, expectedStatus: HttpStatusCode.OK_200 })
158 123
159 const user2 = { username: 'rOoT', password: server.user.password } 124 const user2 = { username: 'rOoT', password: server.store.user.password }
160 await login(server.url, server.client, user2, HttpStatusCode.OK_200) 125 await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 })
161 126
162 const user3 = { username: 'ROOt', password: server.user.password } 127 const user3 = { username: 'ROOt', password: server.store.user.password }
163 await login(server.url, server.client, user3, HttpStatusCode.OK_200) 128 await server.login.login({ user: user3, expectedStatus: HttpStatusCode.OK_200 })
164 }) 129 })
165 }) 130 })
166 131
167 describe('Upload', function () { 132 describe('Upload', function () {
168 133
169 it('Should upload the video with the correct token', async function () { 134 it('Should upload the video with the correct token', async function () {
170 const videoAttributes = {} 135 await server.videos.upload({ token })
171 await uploadVideo(server.url, accessToken, videoAttributes) 136 const { data } = await server.videos.list()
172 const res = await getVideosList(server.url) 137 const video = data[0]
173 const video = res.body.data[0]
174 138
175 expect(video.account.name).to.equal('root') 139 expect(video.account.name).to.equal('root')
176 videoId = video.id 140 videoId = video.id
177 }) 141 })
178 142
179 it('Should upload the video again with the correct token', async function () { 143 it('Should upload the video again with the correct token', async function () {
180 const videoAttributes = {} 144 await server.videos.upload({ token })
181 await uploadVideo(server.url, accessToken, videoAttributes)
182 }) 145 })
183 }) 146 })
184 147
185 describe('Ratings', function () { 148 describe('Ratings', function () {
186 149
187 it('Should retrieve a video rating', async function () { 150 it('Should retrieve a video rating', async function () {
188 await rateVideo(server.url, accessToken, videoId, 'like') 151 await server.videos.rate({ id: videoId, rating: 'like' })
189 const res = await getMyUserVideoRating(server.url, accessToken, videoId) 152 const rating = await server.users.getMyRating({ token, videoId })
190 const rating = res.body
191 153
192 expect(rating.videoId).to.equal(videoId) 154 expect(rating.videoId).to.equal(videoId)
193 expect(rating.rating).to.equal('like') 155 expect(rating.rating).to.equal('like')
194 }) 156 })
195 157
196 it('Should retrieve ratings list', async function () { 158 it('Should retrieve ratings list', async function () {
197 await rateVideo(server.url, accessToken, videoId, 'like') 159 await server.videos.rate({ id: videoId, rating: 'like' })
198 160
199 const res = await getAccountRatings(server.url, server.user.username, server.accessToken, null, HttpStatusCode.OK_200) 161 const body = await server.accounts.listRatings({ accountName: server.store.user.username })
200 const ratings = res.body
201 162
202 expect(ratings.total).to.equal(1) 163 expect(body.total).to.equal(1)
203 expect(ratings.data[0].video.id).to.equal(videoId) 164 expect(body.data[0].video.id).to.equal(videoId)
204 expect(ratings.data[0].rating).to.equal('like') 165 expect(body.data[0].rating).to.equal('like')
205 }) 166 })
206 167
207 it('Should retrieve ratings list by rating type', async function () { 168 it('Should retrieve ratings list by rating type', async function () {
208 { 169 {
209 const res = await getAccountRatings(server.url, server.user.username, server.accessToken, 'like') 170 const body = await server.accounts.listRatings({ accountName: server.store.user.username, rating: 'like' })
210 const ratings = res.body 171 expect(body.data.length).to.equal(1)
211 expect(ratings.data.length).to.equal(1)
212 } 172 }
213 173
214 { 174 {
215 const res = await getAccountRatings(server.url, server.user.username, server.accessToken, 'dislike') 175 const body = await server.accounts.listRatings({ accountName: server.store.user.username, rating: 'dislike' })
216 const ratings = res.body 176 expect(body.data.length).to.equal(0)
217 expect(ratings.data.length).to.equal(0)
218 } 177 }
219 }) 178 })
220 }) 179 })
221 180
222 describe('Remove video', function () { 181 describe('Remove video', function () {
223 it('Should not be able to remove the video with an incorrect token', async function () { 182 it('Should not be able to remove the video with an incorrect token', async function () {
224 await removeVideo(server.url, 'bad_token', videoId, HttpStatusCode.UNAUTHORIZED_401) 183 await server.videos.remove({ token: 'bad_token', id: videoId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
225 }) 184 })
226 185
227 it('Should not be able to remove the video with the token of another account') 186 it('Should not be able to remove the video with the token of another account')
228 187
229 it('Should be able to remove the video with the correct token', async function () { 188 it('Should be able to remove the video with the correct token', async function () {
230 await removeVideo(server.url, accessToken, videoId) 189 await server.videos.remove({ token, id: videoId })
231 }) 190 })
232 }) 191 })
233 192
234 describe('Logout', function () { 193 describe('Logout', function () {
235 it('Should logout (revoke token)', async function () { 194 it('Should logout (revoke token)', async function () {
236 await logout(server.url, server.accessToken) 195 await server.login.logout({ token: server.accessToken })
237 }) 196 })
238 197
239 it('Should not be able to get the user information', async function () { 198 it('Should not be able to get the user information', async function () {
240 await getMyUserInformation(server.url, server.accessToken, HttpStatusCode.UNAUTHORIZED_401) 199 await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
241 }) 200 })
242 201
243 it('Should not be able to upload a video', async function () { 202 it('Should not be able to upload a video', async function () {
244 await uploadVideo(server.url, server.accessToken, { name: 'video' }, HttpStatusCode.UNAUTHORIZED_401) 203 await server.videos.upload({ attributes: { name: 'video' }, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
245 }) 204 })
246 205
247 it('Should not be able to rate a video', async function () { 206 it('Should not be able to rate a video', async function () {
@@ -255,79 +214,70 @@ describe('Test users', function () {
255 path: path + videoId, 214 path: path + videoId,
256 token: 'wrong token', 215 token: 'wrong token',
257 fields: data, 216 fields: data,
258 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401 217 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
259 } 218 }
260 await makePutBodyRequest(options) 219 await makePutBodyRequest(options)
261 }) 220 })
262 221
263 it('Should be able to login again', async function () { 222 it('Should be able to login again', async function () {
264 const res = await login(server.url, server.client, server.user) 223 const body = await server.login.login()
265 server.accessToken = res.body.access_token 224 server.accessToken = body.access_token
266 server.refreshToken = res.body.refresh_token 225 server.refreshToken = body.refresh_token
267 }) 226 })
268 227
269 it('Should be able to get my user information again', async function () { 228 it('Should be able to get my user information again', async function () {
270 await getMyUserInformation(server.url, server.accessToken) 229 await server.users.getMyInfo()
271 }) 230 })
272 231
273 it('Should have an expired access token', async function () { 232 it('Should have an expired access token', async function () {
274 this.timeout(15000) 233 this.timeout(15000)
275 234
276 await setTokenField(server.internalServerNumber, server.accessToken, 'accessTokenExpiresAt', new Date().toISOString()) 235 await server.sql.setTokenField(server.accessToken, 'accessTokenExpiresAt', new Date().toISOString())
277 await setTokenField(server.internalServerNumber, server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString()) 236 await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString())
278 237
279 killallServers([ server ]) 238 await killallServers([ server ])
280 await reRunServer(server) 239 await server.run()
281 240
282 await getMyUserInformation(server.url, server.accessToken, 401) 241 await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
283 }) 242 })
284 243
285 it('Should not be able to refresh an access token with an expired refresh token', async function () { 244 it('Should not be able to refresh an access token with an expired refresh token', async function () {
286 await refreshToken(server, server.refreshToken, 400) 245 await server.login.refreshToken({ refreshToken: server.refreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
287 }) 246 })
288 247
289 it('Should refresh the token', async function () { 248 it('Should refresh the token', async function () {
290 this.timeout(15000) 249 this.timeout(15000)
291 250
292 const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString() 251 const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString()
293 await setTokenField(server.internalServerNumber, server.accessToken, 'refreshTokenExpiresAt', futureDate) 252 await server.sql.setTokenField(server.accessToken, 'refreshTokenExpiresAt', futureDate)
294 253
295 killallServers([ server ]) 254 await killallServers([ server ])
296 await reRunServer(server) 255 await server.run()
297 256
298 const res = await refreshToken(server, server.refreshToken) 257 const res = await server.login.refreshToken({ refreshToken: server.refreshToken })
299 server.accessToken = res.body.access_token 258 server.accessToken = res.body.access_token
300 server.refreshToken = res.body.refresh_token 259 server.refreshToken = res.body.refresh_token
301 }) 260 })
302 261
303 it('Should be able to get my user information again', async function () { 262 it('Should be able to get my user information again', async function () {
304 await getMyUserInformation(server.url, server.accessToken) 263 await server.users.getMyInfo()
305 }) 264 })
306 }) 265 })
307 266
308 describe('Creating a user', function () { 267 describe('Creating a user', function () {
309 268
310 it('Should be able to create a new user', async function () { 269 it('Should be able to create a new user', async function () {
311 await createUser({ 270 await server.users.create({ ...user, videoQuota: 2 * 1024 * 1024, adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST })
312 url: server.url,
313 accessToken: accessToken,
314 username: user.username,
315 password: user.password,
316 videoQuota: 2 * 1024 * 1024,
317 adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST
318 })
319 }) 271 })
320 272
321 it('Should be able to login with this user', async function () { 273 it('Should be able to login with this user', async function () {
322 accessTokenUser = await userLogin(server, user) 274 userToken = await server.login.getAccessToken(user)
323 }) 275 })
324 276
325 it('Should be able to get user information', async function () { 277 it('Should be able to get user information', async function () {
326 const res1 = await getMyUserInformation(server.url, accessTokenUser) 278 const userMe = await server.users.getMyInfo({ token: userToken })
327 const userMe: MyUser = res1.body
328 279
329 const res2 = await getUserInformation(server.url, server.accessToken, userMe.id, true) 280 const userGet = await server.users.get({ userId: userMe.id, withStats: true })
330 const userGet: User = res2.body
331 281
332 for (const user of [ userMe, userGet ]) { 282 for (const user of [ userMe, userGet ]) {
333 expect(user.username).to.equal('user_1') 283 expect(user.username).to.equal('user_1')
@@ -363,34 +313,28 @@ describe('Test users', function () {
363 it('Should be able to upload a video with this user', async function () { 313 it('Should be able to upload a video with this user', async function () {
364 this.timeout(10000) 314 this.timeout(10000)
365 315
366 const videoAttributes = { 316 const attributes = {
367 name: 'super user video', 317 name: 'super user video',
368 fixture: 'video_short.webm' 318 fixture: 'video_short.webm'
369 } 319 }
370 await uploadVideo(server.url, accessTokenUser, videoAttributes) 320 await server.videos.upload({ token: userToken, attributes })
371 }) 321 })
372 322
373 it('Should have video quota updated', async function () { 323 it('Should have video quota updated', async function () {
374 const res = await getMyUserVideoQuotaUsed(server.url, accessTokenUser) 324 const quota = await server.users.getMyQuotaUsed({ token: userToken })
375 const data = res.body 325 expect(quota.videoQuotaUsed).to.equal(218910)
376
377 expect(data.videoQuotaUsed).to.equal(218910)
378
379 const resUsers = await getUsersList(server.url, server.accessToken)
380 326
381 const users: User[] = resUsers.body.data 327 const { data } = await server.users.list()
382 const tmpUser = users.find(u => u.username === user.username) 328 const tmpUser = data.find(u => u.username === user.username)
383 expect(tmpUser.videoQuotaUsed).to.equal(218910) 329 expect(tmpUser.videoQuotaUsed).to.equal(218910)
384 }) 330 })
385 331
386 it('Should be able to list my videos', async function () { 332 it('Should be able to list my videos', async function () {
387 const res = await getMyVideos(server.url, accessTokenUser, 0, 5) 333 const { total, data } = await server.videos.listMyVideos({ token: userToken })
388 expect(res.body.total).to.equal(1) 334 expect(total).to.equal(1)
335 expect(data).to.have.lengthOf(1)
389 336
390 const videos = res.body.data 337 const video: Video = data[0]
391 expect(videos).to.have.lengthOf(1)
392
393 const video: Video = videos[0]
394 expect(video.name).to.equal('super user video') 338 expect(video.name).to.equal('super user video')
395 expect(video.thumbnailPath).to.not.be.null 339 expect(video.thumbnailPath).to.not.be.null
396 expect(video.previewPath).to.not.be.null 340 expect(video.previewPath).to.not.be.null
@@ -398,19 +342,15 @@ describe('Test users', function () {
398 342
399 it('Should be able to search in my videos', async function () { 343 it('Should be able to search in my videos', async function () {
400 { 344 {
401 const res = await getMyVideos(server.url, accessTokenUser, 0, 5, '-createdAt', 'user video') 345 const { total, data } = await server.videos.listMyVideos({ token: userToken, sort: '-createdAt', search: 'user video' })
402 expect(res.body.total).to.equal(1) 346 expect(total).to.equal(1)
403 347 expect(data).to.have.lengthOf(1)
404 const videos = res.body.data
405 expect(videos).to.have.lengthOf(1)
406 } 348 }
407 349
408 { 350 {
409 const res = await getMyVideos(server.url, accessTokenUser, 0, 5, '-createdAt', 'toto') 351 const { total, data } = await server.videos.listMyVideos({ token: userToken, sort: '-createdAt', search: 'toto' })
410 expect(res.body.total).to.equal(0) 352 expect(total).to.equal(0)
411 353 expect(data).to.have.lengthOf(0)
412 const videos = res.body.data
413 expect(videos).to.have.lengthOf(0)
414 } 354 }
415 }) 355 })
416 356
@@ -418,28 +358,25 @@ describe('Test users', function () {
418 this.timeout(60000) 358 this.timeout(60000)
419 359
420 { 360 {
421 const res = await getCustomConfig(server.url, server.accessToken) 361 const config = await server.config.getCustomConfig()
422 const config = res.body as CustomConfig
423 config.transcoding.webtorrent.enabled = false 362 config.transcoding.webtorrent.enabled = false
424 config.transcoding.hls.enabled = true 363 config.transcoding.hls.enabled = true
425 config.transcoding.enabled = true 364 config.transcoding.enabled = true
426 await updateCustomSubConfig(server.url, server.accessToken, config) 365 await server.config.updateCustomSubConfig({ newConfig: config })
427 } 366 }
428 367
429 { 368 {
430 const videoAttributes = { 369 const attributes = {
431 name: 'super user video 2', 370 name: 'super user video 2',
432 fixture: 'video_short.webm' 371 fixture: 'video_short.webm'
433 } 372 }
434 await uploadVideo(server.url, accessTokenUser, videoAttributes) 373 await server.videos.upload({ token: userToken, attributes })
435 374
436 await waitJobs([ server ]) 375 await waitJobs([ server ])
437 } 376 }
438 377
439 { 378 {
440 const res = await getMyUserVideoQuotaUsed(server.url, accessTokenUser) 379 const data = await server.users.getMyQuotaUsed({ token: userToken })
441 const data = res.body
442
443 expect(data.videoQuotaUsed).to.be.greaterThan(220000) 380 expect(data.videoQuotaUsed).to.be.greaterThan(220000)
444 } 381 }
445 }) 382 })
@@ -448,21 +385,18 @@ describe('Test users', function () {
448 describe('Users listing', function () { 385 describe('Users listing', function () {
449 386
450 it('Should list all the users', async function () { 387 it('Should list all the users', async function () {
451 const res = await getUsersList(server.url, server.accessToken) 388 const { data, total } = await server.users.list()
452 const result = res.body
453 const total = result.total
454 const users = result.data
455 389
456 expect(total).to.equal(2) 390 expect(total).to.equal(2)
457 expect(users).to.be.an('array') 391 expect(data).to.be.an('array')
458 expect(users.length).to.equal(2) 392 expect(data.length).to.equal(2)
459 393
460 const user = users[0] 394 const user = data[0]
461 expect(user.username).to.equal('user_1') 395 expect(user.username).to.equal('user_1')
462 expect(user.email).to.equal('user_1@example.com') 396 expect(user.email).to.equal('user_1@example.com')
463 expect(user.nsfwPolicy).to.equal('display') 397 expect(user.nsfwPolicy).to.equal('display')
464 398
465 const rootUser = users[1] 399 const rootUser = data[1]
466 expect(rootUser.username).to.equal('root') 400 expect(rootUser.username).to.equal('root')
467 expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com') 401 expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com')
468 expect(user.nsfwPolicy).to.equal('display') 402 expect(user.nsfwPolicy).to.equal('display')
@@ -474,16 +408,12 @@ describe('Test users', function () {
474 }) 408 })
475 409
476 it('Should list only the first user by username asc', async function () { 410 it('Should list only the first user by username asc', async function () {
477 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, 'username') 411 const { total, data } = await server.users.list({ start: 0, count: 1, sort: 'username' })
478
479 const result = res.body
480 const total = result.total
481 const users = result.data
482 412
483 expect(total).to.equal(2) 413 expect(total).to.equal(2)
484 expect(users.length).to.equal(1) 414 expect(data.length).to.equal(1)
485 415
486 const user = users[0] 416 const user = data[0]
487 expect(user.username).to.equal('root') 417 expect(user.username).to.equal('root')
488 expect(user.email).to.equal('admin' + server.internalServerNumber + '@example.com') 418 expect(user.email).to.equal('admin' + server.internalServerNumber + '@example.com')
489 expect(user.roleLabel).to.equal('Administrator') 419 expect(user.roleLabel).to.equal('Administrator')
@@ -491,111 +421,90 @@ describe('Test users', function () {
491 }) 421 })
492 422
493 it('Should list only the first user by username desc', async function () { 423 it('Should list only the first user by username desc', async function () {
494 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-username') 424 const { total, data } = await server.users.list({ start: 0, count: 1, sort: '-username' })
495 const result = res.body
496 const total = result.total
497 const users = result.data
498 425
499 expect(total).to.equal(2) 426 expect(total).to.equal(2)
500 expect(users.length).to.equal(1) 427 expect(data.length).to.equal(1)
501 428
502 const user = users[0] 429 const user = data[0]
503 expect(user.username).to.equal('user_1') 430 expect(user.username).to.equal('user_1')
504 expect(user.email).to.equal('user_1@example.com') 431 expect(user.email).to.equal('user_1@example.com')
505 expect(user.nsfwPolicy).to.equal('display') 432 expect(user.nsfwPolicy).to.equal('display')
506 }) 433 })
507 434
508 it('Should list only the second user by createdAt desc', async function () { 435 it('Should list only the second user by createdAt desc', async function () {
509 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-createdAt') 436 const { data, total } = await server.users.list({ start: 0, count: 1, sort: '-createdAt' })
510 const result = res.body
511 const total = result.total
512 const users = result.data
513
514 expect(total).to.equal(2) 437 expect(total).to.equal(2)
515 expect(users.length).to.equal(1)
516 438
517 const user = users[0] 439 expect(data.length).to.equal(1)
440
441 const user = data[0]
518 expect(user.username).to.equal('user_1') 442 expect(user.username).to.equal('user_1')
519 expect(user.email).to.equal('user_1@example.com') 443 expect(user.email).to.equal('user_1@example.com')
520 expect(user.nsfwPolicy).to.equal('display') 444 expect(user.nsfwPolicy).to.equal('display')
521 }) 445 })
522 446
523 it('Should list all the users by createdAt asc', async function () { 447 it('Should list all the users by createdAt asc', async function () {
524 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt') 448 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt' })
525 const result = res.body
526 const total = result.total
527 const users = result.data
528 449
529 expect(total).to.equal(2) 450 expect(total).to.equal(2)
530 expect(users.length).to.equal(2) 451 expect(data.length).to.equal(2)
531 452
532 expect(users[0].username).to.equal('root') 453 expect(data[0].username).to.equal('root')
533 expect(users[0].email).to.equal('admin' + server.internalServerNumber + '@example.com') 454 expect(data[0].email).to.equal('admin' + server.internalServerNumber + '@example.com')
534 expect(users[0].nsfwPolicy).to.equal('display') 455 expect(data[0].nsfwPolicy).to.equal('display')
535 456
536 expect(users[1].username).to.equal('user_1') 457 expect(data[1].username).to.equal('user_1')
537 expect(users[1].email).to.equal('user_1@example.com') 458 expect(data[1].email).to.equal('user_1@example.com')
538 expect(users[1].nsfwPolicy).to.equal('display') 459 expect(data[1].nsfwPolicy).to.equal('display')
539 }) 460 })
540 461
541 it('Should search user by username', async function () { 462 it('Should search user by username', async function () {
542 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'oot') 463 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'oot' })
543 const users = res.body.data as User[] 464 expect(total).to.equal(1)
544 465 expect(data.length).to.equal(1)
545 expect(res.body.total).to.equal(1) 466 expect(data[0].username).to.equal('root')
546 expect(users.length).to.equal(1)
547
548 expect(users[0].username).to.equal('root')
549 }) 467 })
550 468
551 it('Should search user by email', async function () { 469 it('Should search user by email', async function () {
552 { 470 {
553 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'r_1@exam') 471 const { total, data } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'r_1@exam' })
554 const users = res.body.data as User[] 472 expect(total).to.equal(1)
555 473 expect(data.length).to.equal(1)
556 expect(res.body.total).to.equal(1) 474 expect(data[0].username).to.equal('user_1')
557 expect(users.length).to.equal(1) 475 expect(data[0].email).to.equal('user_1@example.com')
558
559 expect(users[0].username).to.equal('user_1')
560 expect(users[0].email).to.equal('user_1@example.com')
561 } 476 }
562 477
563 { 478 {
564 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', 'example') 479 const { total, data } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'example' })
565 const users = res.body.data as User[] 480 expect(total).to.equal(2)
566 481 expect(data.length).to.equal(2)
567 expect(res.body.total).to.equal(2) 482 expect(data[0].username).to.equal('root')
568 expect(users.length).to.equal(2) 483 expect(data[1].username).to.equal('user_1')
569
570 expect(users[0].username).to.equal('root')
571 expect(users[1].username).to.equal('user_1')
572 } 484 }
573 }) 485 })
574 }) 486 })
575 487
576 describe('Update my account', function () { 488 describe('Update my account', function () {
489
577 it('Should update my password', async function () { 490 it('Should update my password', async function () {
578 await updateMyUser({ 491 await server.users.updateMe({
579 url: server.url, 492 token: userToken,
580 accessToken: accessTokenUser,
581 currentPassword: 'super password', 493 currentPassword: 'super password',
582 password: 'new password' 494 password: 'new password'
583 }) 495 })
584 user.password = 'new password' 496 user.password = 'new password'
585 497
586 await userLogin(server, user, HttpStatusCode.OK_200) 498 await server.login.login({ user })
587 }) 499 })
588 500
589 it('Should be able to change the NSFW display attribute', async function () { 501 it('Should be able to change the NSFW display attribute', async function () {
590 await updateMyUser({ 502 await server.users.updateMe({
591 url: server.url, 503 token: userToken,
592 accessToken: accessTokenUser,
593 nsfwPolicy: 'do_not_list' 504 nsfwPolicy: 'do_not_list'
594 }) 505 })
595 506
596 const res = await getMyUserInformation(server.url, accessTokenUser) 507 const user = await server.users.getMyInfo({ token: userToken })
597 const user = res.body
598
599 expect(user.username).to.equal('user_1') 508 expect(user.username).to.equal('user_1')
600 expect(user.email).to.equal('user_1@example.com') 509 expect(user.email).to.equal('user_1@example.com')
601 expect(user.nsfwPolicy).to.equal('do_not_list') 510 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -606,42 +515,33 @@ describe('Test users', function () {
606 }) 515 })
607 516
608 it('Should be able to change the autoPlayVideo attribute', async function () { 517 it('Should be able to change the autoPlayVideo attribute', async function () {
609 await updateMyUser({ 518 await server.users.updateMe({
610 url: server.url, 519 token: userToken,
611 accessToken: accessTokenUser,
612 autoPlayVideo: false 520 autoPlayVideo: false
613 }) 521 })
614 522
615 const res = await getMyUserInformation(server.url, accessTokenUser) 523 const user = await server.users.getMyInfo({ token: userToken })
616 const user = res.body
617
618 expect(user.autoPlayVideo).to.be.false 524 expect(user.autoPlayVideo).to.be.false
619 }) 525 })
620 526
621 it('Should be able to change the autoPlayNextVideo attribute', async function () { 527 it('Should be able to change the autoPlayNextVideo attribute', async function () {
622 await updateMyUser({ 528 await server.users.updateMe({
623 url: server.url, 529 token: userToken,
624 accessToken: accessTokenUser,
625 autoPlayNextVideo: true 530 autoPlayNextVideo: true
626 }) 531 })
627 532
628 const res = await getMyUserInformation(server.url, accessTokenUser) 533 const user = await server.users.getMyInfo({ token: userToken })
629 const user = res.body
630
631 expect(user.autoPlayNextVideo).to.be.true 534 expect(user.autoPlayNextVideo).to.be.true
632 }) 535 })
633 536
634 it('Should be able to change the email attribute', async function () { 537 it('Should be able to change the email attribute', async function () {
635 await updateMyUser({ 538 await server.users.updateMe({
636 url: server.url, 539 token: userToken,
637 accessToken: accessTokenUser,
638 currentPassword: 'new password', 540 currentPassword: 'new password',
639 email: 'updated@example.com' 541 email: 'updated@example.com'
640 }) 542 })
641 543
642 const res = await getMyUserInformation(server.url, accessTokenUser) 544 const user = await server.users.getMyInfo({ token: userToken })
643 const user = res.body
644
645 expect(user.username).to.equal('user_1') 545 expect(user.username).to.equal('user_1')
646 expect(user.email).to.equal('updated@example.com') 546 expect(user.email).to.equal('updated@example.com')
647 expect(user.nsfwPolicy).to.equal('do_not_list') 547 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -654,15 +554,9 @@ describe('Test users', function () {
654 it('Should be able to update my avatar with a gif', async function () { 554 it('Should be able to update my avatar with a gif', async function () {
655 const fixture = 'avatar.gif' 555 const fixture = 'avatar.gif'
656 556
657 await updateMyAvatar({ 557 await server.users.updateMyAvatar({ token: userToken, fixture })
658 url: server.url,
659 accessToken: accessTokenUser,
660 fixture
661 })
662
663 const res = await getMyUserInformation(server.url, accessTokenUser)
664 const user = res.body
665 558
559 const user = await server.users.getMyInfo({ token: userToken })
666 await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.gif') 560 await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.gif')
667 }) 561 })
668 562
@@ -670,29 +564,17 @@ describe('Test users', function () {
670 for (const extension of [ '.png', '.gif' ]) { 564 for (const extension of [ '.png', '.gif' ]) {
671 const fixture = 'avatar' + extension 565 const fixture = 'avatar' + extension
672 566
673 await updateMyAvatar({ 567 await server.users.updateMyAvatar({ token: userToken, fixture })
674 url: server.url,
675 accessToken: accessTokenUser,
676 fixture
677 })
678
679 const res = await getMyUserInformation(server.url, accessTokenUser)
680 const user = res.body
681 568
569 const user = await server.users.getMyInfo({ token: userToken })
682 await testImage(server.url, 'avatar-resized', user.account.avatar.path, extension) 570 await testImage(server.url, 'avatar-resized', user.account.avatar.path, extension)
683 } 571 }
684 }) 572 })
685 573
686 it('Should be able to update my display name', async function () { 574 it('Should be able to update my display name', async function () {
687 await updateMyUser({ 575 await server.users.updateMe({ token: userToken, displayName: 'new display name' })
688 url: server.url,
689 accessToken: accessTokenUser,
690 displayName: 'new display name'
691 })
692
693 const res = await getMyUserInformation(server.url, accessTokenUser)
694 const user = res.body
695 576
577 const user = await server.users.getMyInfo({ token: userToken })
696 expect(user.username).to.equal('user_1') 578 expect(user.username).to.equal('user_1')
697 expect(user.email).to.equal('updated@example.com') 579 expect(user.email).to.equal('updated@example.com')
698 expect(user.nsfwPolicy).to.equal('do_not_list') 580 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -703,15 +585,9 @@ describe('Test users', function () {
703 }) 585 })
704 586
705 it('Should be able to update my description', async function () { 587 it('Should be able to update my description', async function () {
706 await updateMyUser({ 588 await server.users.updateMe({ token: userToken, description: 'my super description updated' })
707 url: server.url,
708 accessToken: accessTokenUser,
709 description: 'my super description updated'
710 })
711
712 const res = await getMyUserInformation(server.url, accessTokenUser)
713 const user: User = res.body
714 589
590 const user = await server.users.getMyInfo({ token: userToken })
715 expect(user.username).to.equal('user_1') 591 expect(user.username).to.equal('user_1')
716 expect(user.email).to.equal('updated@example.com') 592 expect(user.email).to.equal('updated@example.com')
717 expect(user.nsfwPolicy).to.equal('do_not_list') 593 expect(user.nsfwPolicy).to.equal('do_not_list')
@@ -725,30 +601,21 @@ describe('Test users', function () {
725 601
726 it('Should be able to update my theme', async function () { 602 it('Should be able to update my theme', async function () {
727 for (const theme of [ 'background-red', 'default', 'instance-default' ]) { 603 for (const theme of [ 'background-red', 'default', 'instance-default' ]) {
728 await updateMyUser({ 604 await server.users.updateMe({ token: userToken, theme })
729 url: server.url,
730 accessToken: accessTokenUser,
731 theme
732 })
733 605
734 const res = await getMyUserInformation(server.url, accessTokenUser) 606 const user = await server.users.getMyInfo({ token: userToken })
735 const body: User = res.body 607 expect(user.theme).to.equal(theme)
736
737 expect(body.theme).to.equal(theme)
738 } 608 }
739 }) 609 })
740 610
741 it('Should be able to update my modal preferences', async function () { 611 it('Should be able to update my modal preferences', async function () {
742 await updateMyUser({ 612 await server.users.updateMe({
743 url: server.url, 613 token: userToken,
744 accessToken: accessTokenUser,
745 noInstanceConfigWarningModal: true, 614 noInstanceConfigWarningModal: true,
746 noWelcomeModal: true 615 noWelcomeModal: true
747 }) 616 })
748 617
749 const res = await getMyUserInformation(server.url, accessTokenUser) 618 const user = await server.users.getMyInfo({ token: userToken })
750 const user: User = res.body
751
752 expect(user.noWelcomeModal).to.be.true 619 expect(user.noWelcomeModal).to.be.true
753 expect(user.noInstanceConfigWarningModal).to.be.true 620 expect(user.noInstanceConfigWarningModal).to.be.true
754 }) 621 })
@@ -756,10 +623,9 @@ describe('Test users', function () {
756 623
757 describe('Updating another user', function () { 624 describe('Updating another user', function () {
758 it('Should be able to update another user', async function () { 625 it('Should be able to update another user', async function () {
759 await updateUser({ 626 await server.users.update({
760 url: server.url,
761 userId, 627 userId,
762 accessToken, 628 token,
763 email: 'updated2@example.com', 629 email: 'updated2@example.com',
764 emailVerified: true, 630 emailVerified: true,
765 videoQuota: 42, 631 videoQuota: 42,
@@ -768,8 +634,7 @@ describe('Test users', function () {
768 pluginAuth: 'toto' 634 pluginAuth: 'toto'
769 }) 635 })
770 636
771 const res = await getUserInformation(server.url, accessToken, userId) 637 const user = await server.users.get({ token, userId })
772 const user = res.body as User
773 638
774 expect(user.username).to.equal('user_1') 639 expect(user.username).to.equal('user_1')
775 expect(user.email).to.equal('updated2@example.com') 640 expect(user.email).to.equal('updated2@example.com')
@@ -783,57 +648,50 @@ describe('Test users', function () {
783 }) 648 })
784 649
785 it('Should reset the auth plugin', async function () { 650 it('Should reset the auth plugin', async function () {
786 await updateUser({ url: server.url, userId, accessToken, pluginAuth: null }) 651 await server.users.update({ userId, token, pluginAuth: null })
787 652
788 const res = await getUserInformation(server.url, accessToken, userId) 653 const user = await server.users.get({ token, userId })
789 const user = res.body as User
790 expect(user.pluginAuth).to.be.null 654 expect(user.pluginAuth).to.be.null
791 }) 655 })
792 656
793 it('Should have removed the user token', async function () { 657 it('Should have removed the user token', async function () {
794 await getMyUserVideoQuotaUsed(server.url, accessTokenUser, HttpStatusCode.UNAUTHORIZED_401) 658 await server.users.getMyQuotaUsed({ token: userToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
795 659
796 accessTokenUser = await userLogin(server, user) 660 userToken = await server.login.getAccessToken(user)
797 }) 661 })
798 662
799 it('Should be able to update another user password', async function () { 663 it('Should be able to update another user password', async function () {
800 await updateUser({ 664 await server.users.update({ userId, token, password: 'password updated' })
801 url: server.url,
802 userId,
803 accessToken,
804 password: 'password updated'
805 })
806 665
807 await getMyUserVideoQuotaUsed(server.url, accessTokenUser, HttpStatusCode.UNAUTHORIZED_401) 666 await server.users.getMyQuotaUsed({ token: userToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
808 667
809 await userLogin(server, user, HttpStatusCode.BAD_REQUEST_400) 668 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
810 669
811 user.password = 'password updated' 670 user.password = 'password updated'
812 accessTokenUser = await userLogin(server, user) 671 userToken = await server.login.getAccessToken(user)
813 }) 672 })
814 }) 673 })
815 674
816 describe('Video blacklists', function () { 675 describe('Video blacklists', function () {
817 it('Should be able to list video blacklist by a moderator', async function () { 676 it('Should be able to list video blacklist by a moderator', async function () {
818 await getBlacklistedVideosList({ url: server.url, token: accessTokenUser }) 677 await server.blacklist.list({ token: userToken })
819 }) 678 })
820 }) 679 })
821 680
822 describe('Remove a user', function () { 681 describe('Remove a user', function () {
823 it('Should be able to remove this user', async function () { 682 it('Should be able to remove this user', async function () {
824 await removeUser(server.url, userId, accessToken) 683 await server.users.remove({ userId, token })
825 }) 684 })
826 685
827 it('Should not be able to login with this user', async function () { 686 it('Should not be able to login with this user', async function () {
828 await userLogin(server, user, HttpStatusCode.BAD_REQUEST_400) 687 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
829 }) 688 })
830 689
831 it('Should not have videos of this user', async function () { 690 it('Should not have videos of this user', async function () {
832 const res = await getVideosList(server.url) 691 const { data, total } = await server.videos.list()
833 692 expect(total).to.equal(1)
834 expect(res.body.total).to.equal(1)
835 693
836 const video = res.body.data[0] 694 const video = data[0]
837 expect(video.account.name).to.equal('root') 695 expect(video.account.name).to.equal('root')
838 }) 696 })
839 }) 697 })
@@ -845,7 +703,7 @@ describe('Test users', function () {
845 const user = { displayName: 'super user 15', username: 'user_15', password: 'my super password' } 703 const user = { displayName: 'super user 15', username: 'user_15', password: 'my super password' }
846 const channel = { name: 'my_user_15_channel', displayName: 'my channel rocks' } 704 const channel = { name: 'my_user_15_channel', displayName: 'my channel rocks' }
847 705
848 await registerUserWithChannel({ url: server.url, user, channel }) 706 await server.users.register({ ...user, channel })
849 }) 707 })
850 708
851 it('Should be able to login with this registered user', async function () { 709 it('Should be able to login with this registered user', async function () {
@@ -854,40 +712,36 @@ describe('Test users', function () {
854 password: 'my super password' 712 password: 'my super password'
855 } 713 }
856 714
857 user15AccessToken = await userLogin(server, user15) 715 user15AccessToken = await server.login.getAccessToken(user15)
858 }) 716 })
859 717
860 it('Should have the correct display name', async function () { 718 it('Should have the correct display name', async function () {
861 const res = await getMyUserInformation(server.url, user15AccessToken) 719 const user = await server.users.getMyInfo({ token: user15AccessToken })
862 const user: User = res.body
863
864 expect(user.account.displayName).to.equal('super user 15') 720 expect(user.account.displayName).to.equal('super user 15')
865 }) 721 })
866 722
867 it('Should have the correct video quota', async function () { 723 it('Should have the correct video quota', async function () {
868 const res = await getMyUserInformation(server.url, user15AccessToken) 724 const user = await server.users.getMyInfo({ token: user15AccessToken })
869 const user = res.body
870
871 expect(user.videoQuota).to.equal(5 * 1024 * 1024) 725 expect(user.videoQuota).to.equal(5 * 1024 * 1024)
872 }) 726 })
873 727
874 it('Should have created the channel', async function () { 728 it('Should have created the channel', async function () {
875 const res = await getVideoChannel(server.url, 'my_user_15_channel') 729 const { displayName } = await server.channels.get({ channelName: 'my_user_15_channel' })
876 730
877 expect(res.body.displayName).to.equal('my channel rocks') 731 expect(displayName).to.equal('my channel rocks')
878 }) 732 })
879 733
880 it('Should remove me', async function () { 734 it('Should remove me', async function () {
881 { 735 {
882 const res = await getUsersList(server.url, server.accessToken) 736 const { data } = await server.users.list()
883 expect(res.body.data.find(u => u.username === 'user_15')).to.not.be.undefined 737 expect(data.find(u => u.username === 'user_15')).to.not.be.undefined
884 } 738 }
885 739
886 await deleteMe(server.url, user15AccessToken) 740 await server.users.deleteMe({ token: user15AccessToken })
887 741
888 { 742 {
889 const res = await getUsersList(server.url, server.accessToken) 743 const { data } = await server.users.list()
890 expect(res.body.data.find(u => u.username === 'user_15')).to.be.undefined 744 expect(data.find(u => u.username === 'user_15')).to.be.undefined
891 } 745 }
892 }) 746 })
893 }) 747 })
@@ -901,49 +755,40 @@ describe('Test users', function () {
901 } 755 }
902 756
903 it('Should block a user', async function () { 757 it('Should block a user', async function () {
904 const resUser = await createUser({ 758 const user = await server.users.create({ ...user16 })
905 url: server.url, 759 user16Id = user.id
906 accessToken: server.accessToken,
907 username: user16.username,
908 password: user16.password
909 })
910 user16Id = resUser.body.user.id
911 760
912 user16AccessToken = await userLogin(server, user16) 761 user16AccessToken = await server.login.getAccessToken(user16)
913 762
914 await getMyUserInformation(server.url, user16AccessToken, HttpStatusCode.OK_200) 763 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.OK_200 })
915 await blockUser(server.url, user16Id, server.accessToken) 764 await server.users.banUser({ userId: user16Id })
916 765
917 await getMyUserInformation(server.url, user16AccessToken, HttpStatusCode.UNAUTHORIZED_401) 766 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
918 await userLogin(server, user16, HttpStatusCode.BAD_REQUEST_400) 767 await server.login.login({ user: user16, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
919 }) 768 })
920 769
921 it('Should search user by banned status', async function () { 770 it('Should search user by banned status', async function () {
922 { 771 {
923 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', undefined, true) 772 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', blocked: true })
924 const users = res.body.data as User[] 773 expect(total).to.equal(1)
774 expect(data.length).to.equal(1)
925 775
926 expect(res.body.total).to.equal(1) 776 expect(data[0].username).to.equal(user16.username)
927 expect(users.length).to.equal(1)
928
929 expect(users[0].username).to.equal(user16.username)
930 } 777 }
931 778
932 { 779 {
933 const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt', undefined, false) 780 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', blocked: false })
934 const users = res.body.data as User[] 781 expect(total).to.equal(1)
935 782 expect(data.length).to.equal(1)
936 expect(res.body.total).to.equal(1)
937 expect(users.length).to.equal(1)
938 783
939 expect(users[0].username).to.not.equal(user16.username) 784 expect(data[0].username).to.not.equal(user16.username)
940 } 785 }
941 }) 786 })
942 787
943 it('Should unblock a user', async function () { 788 it('Should unblock a user', async function () {
944 await unblockUser(server.url, user16Id, server.accessToken) 789 await server.users.unbanUser({ userId: user16Id })
945 user16AccessToken = await userLogin(server, user16) 790 user16AccessToken = await server.login.getAccessToken(user16)
946 await getMyUserInformation(server.url, user16AccessToken, HttpStatusCode.OK_200) 791 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.OK_200 })
947 }) 792 })
948 }) 793 })
949 794
@@ -956,19 +801,12 @@ describe('Test users', function () {
956 username: 'user_17', 801 username: 'user_17',
957 password: 'my super password' 802 password: 'my super password'
958 } 803 }
959 const resUser = await createUser({ 804 const created = await server.users.create({ ...user17 })
960 url: server.url,
961 accessToken: server.accessToken,
962 username: user17.username,
963 password: user17.password
964 })
965 805
966 user17Id = resUser.body.user.id 806 user17Id = created.id
967 user17AccessToken = await userLogin(server, user17) 807 user17AccessToken = await server.login.getAccessToken(user17)
968
969 const res = await getUserInformation(server.url, server.accessToken, user17Id, true)
970 const user: User = res.body
971 808
809 const user = await server.users.get({ userId: user17Id, withStats: true })
972 expect(user.videosCount).to.equal(0) 810 expect(user.videosCount).to.equal(0)
973 expect(user.videoCommentsCount).to.equal(0) 811 expect(user.videoCommentsCount).to.equal(0)
974 expect(user.abusesCount).to.equal(0) 812 expect(user.abusesCount).to.equal(0)
@@ -977,54 +815,43 @@ describe('Test users', function () {
977 }) 815 })
978 816
979 it('Should report correct videos count', async function () { 817 it('Should report correct videos count', async function () {
980 const videoAttributes = { 818 const attributes = { name: 'video to test user stats' }
981 name: 'video to test user stats' 819 await server.videos.upload({ token: user17AccessToken, attributes })
982 }
983 await uploadVideo(server.url, user17AccessToken, videoAttributes)
984 const res1 = await getVideosList(server.url)
985 videoId = res1.body.data.find(video => video.name === videoAttributes.name).id
986 820
987 const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) 821 const { data } = await server.videos.list()
988 const user: User = res2.body 822 videoId = data.find(video => video.name === attributes.name).id
989 823
824 const user = await server.users.get({ userId: user17Id, withStats: true })
990 expect(user.videosCount).to.equal(1) 825 expect(user.videosCount).to.equal(1)
991 }) 826 })
992 827
993 it('Should report correct video comments for user', async function () { 828 it('Should report correct video comments for user', async function () {
994 const text = 'super comment' 829 const text = 'super comment'
995 await addVideoCommentThread(server.url, user17AccessToken, videoId, text) 830 await server.comments.createThread({ token: user17AccessToken, videoId, text })
996
997 const res = await getUserInformation(server.url, server.accessToken, user17Id, true)
998 const user: User = res.body
999 831
832 const user = await server.users.get({ userId: user17Id, withStats: true })
1000 expect(user.videoCommentsCount).to.equal(1) 833 expect(user.videoCommentsCount).to.equal(1)
1001 }) 834 })
1002 835
1003 it('Should report correct abuses counts', async function () { 836 it('Should report correct abuses counts', async function () {
1004 const reason = 'my super bad reason' 837 const reason = 'my super bad reason'
1005 await reportAbuse({ url: server.url, token: user17AccessToken, videoId, reason }) 838 await server.abuses.report({ token: user17AccessToken, videoId, reason })
1006
1007 const res1 = await getAdminAbusesList({ url: server.url, token: server.accessToken })
1008 const abuseId = res1.body.data[0].id
1009 839
1010 const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) 840 const body1 = await server.abuses.getAdminList()
1011 const user2: User = res2.body 841 const abuseId = body1.data[0].id
1012 842
843 const user2 = await server.users.get({ userId: user17Id, withStats: true })
1013 expect(user2.abusesCount).to.equal(1) // number of incriminations 844 expect(user2.abusesCount).to.equal(1) // number of incriminations
1014 expect(user2.abusesCreatedCount).to.equal(1) // number of reports created 845 expect(user2.abusesCreatedCount).to.equal(1) // number of reports created
1015 846
1016 const body: AbuseUpdate = { state: AbuseState.ACCEPTED } 847 await server.abuses.update({ abuseId, body: { state: AbuseState.ACCEPTED } })
1017 await updateAbuse(server.url, server.accessToken, abuseId, body)
1018
1019 const res3 = await getUserInformation(server.url, server.accessToken, user17Id, true)
1020 const user3: User = res3.body
1021 848
849 const user3 = await server.users.get({ userId: user17Id, withStats: true })
1022 expect(user3.abusesAcceptedCount).to.equal(1) // number of reports created accepted 850 expect(user3.abusesAcceptedCount).to.equal(1) // number of reports created accepted
1023 }) 851 })
1024 }) 852 })
1025 853
1026 after(async function () { 854 after(async function () {
1027 await closeAllSequelize([ server ])
1028 await cleanupTests([ server ]) 855 await cleanupTests([ server ])
1029 }) 856 })
1030}) 857})
diff --git a/server/tests/api/videos/audio-only.ts b/server/tests/api/videos/audio-only.ts
index 7ddbd5cd9..7fac6e738 100644
--- a/server/tests/api/videos/audio-only.ts
+++ b/server/tests/api/videos/audio-only.ts
@@ -2,26 +2,16 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path'
6import { getAudioStream, getVideoStreamSize } from '@server/helpers/ffprobe-utils' 5import { getAudioStream, getVideoStreamSize } from '@server/helpers/ffprobe-utils'
7import { 6import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
8 buildServerDirectory,
9 cleanupTests,
10 doubleFollow,
11 flushAndRunMultipleServers,
12 getVideo,
13 ServerInfo,
14 setAccessTokensToServers,
15 uploadVideo,
16 waitJobs
17} from '../../../../shared/extra-utils'
18import { VideoDetails } from '../../../../shared/models/videos'
19 7
20const expect = chai.expect 8const expect = chai.expect
21 9
22describe('Test audio only video transcoding', function () { 10describe('Test audio only video transcoding', function () {
23 let servers: ServerInfo[] = [] 11 let servers: PeerTubeServer[] = []
24 let videoUUID: string 12 let videoUUID: string
13 let webtorrentAudioFileUrl: string
14 let fragmentedAudioFileUrl: string
25 15
26 before(async function () { 16 before(async function () {
27 this.timeout(120000) 17 this.timeout(120000)
@@ -47,7 +37,7 @@ describe('Test audio only video transcoding', function () {
47 } 37 }
48 } 38 }
49 } 39 }
50 servers = await flushAndRunMultipleServers(2, configOverride) 40 servers = await createMultipleServers(2, configOverride)
51 41
52 // Get the access tokens 42 // Get the access tokens
53 await setAccessTokensToServers(servers) 43 await setAccessTokensToServers(servers)
@@ -59,15 +49,13 @@ describe('Test audio only video transcoding', function () {
59 it('Should upload a video and transcode it', async function () { 49 it('Should upload a video and transcode it', async function () {
60 this.timeout(120000) 50 this.timeout(120000)
61 51
62 const resUpload = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'audio only' }) 52 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'audio only' } })
63 videoUUID = resUpload.body.video.uuid 53 videoUUID = uuid
64 54
65 await waitJobs(servers) 55 await waitJobs(servers)
66 56
67 for (const server of servers) { 57 for (const server of servers) {
68 const res = await getVideo(server.url, videoUUID) 58 const video = await server.videos.get({ id: videoUUID })
69 const video: VideoDetails = res.body
70
71 expect(video.streamingPlaylists).to.have.lengthOf(1) 59 expect(video.streamingPlaylists).to.have.lengthOf(1)
72 60
73 for (const files of [ video.files, video.streamingPlaylists[0].files ]) { 61 for (const files of [ video.files, video.streamingPlaylists[0].files ]) {
@@ -76,13 +64,18 @@ describe('Test audio only video transcoding', function () {
76 expect(files[1].resolution.id).to.equal(240) 64 expect(files[1].resolution.id).to.equal(240)
77 expect(files[2].resolution.id).to.equal(0) 65 expect(files[2].resolution.id).to.equal(0)
78 } 66 }
67
68 if (server.serverNumber === 1) {
69 webtorrentAudioFileUrl = video.files[2].fileUrl
70 fragmentedAudioFileUrl = video.streamingPlaylists[0].files[2].fileUrl
71 }
79 } 72 }
80 }) 73 })
81 74
82 it('0p transcoded video should not have video', async function () { 75 it('0p transcoded video should not have video', async function () {
83 const paths = [ 76 const paths = [
84 buildServerDirectory(servers[0], join('videos', videoUUID + '-0.mp4')), 77 servers[0].servers.buildWebTorrentFilePath(webtorrentAudioFileUrl),
85 buildServerDirectory(servers[0], join('streaming-playlists', 'hls', videoUUID, videoUUID + '-0-fragmented.mp4')) 78 servers[0].servers.buildFragmentedFilePath(videoUUID, fragmentedAudioFileUrl)
86 ] 79 ]
87 80
88 for (const path of paths) { 81 for (const path of paths) {
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index a8c8a889b..f9220e4b3 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -3,49 +3,29 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import * as request from 'supertest' 5import * as request from 'supertest'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 6import {
8 addVideoChannel,
9 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
10 checkTmpIsEmpty, 8 checkTmpIsEmpty,
11 checkVideoFilesWereRemoved, 9 checkVideoFilesWereRemoved,
12 cleanupTests, 10 cleanupTests,
13 completeVideoCheck, 11 completeVideoCheck,
14 createUser, 12 createMultipleServers,
15 dateIsValid, 13 dateIsValid,
16 doubleFollow, 14 doubleFollow,
17 flushAndRunMultipleServers, 15 PeerTubeServer,
18 getLocalVideos, 16 saveVideoInServers,
19 getVideo,
20 getVideoChannelsList,
21 getVideosList,
22 rateVideo,
23 removeVideo,
24 ServerInfo,
25 setAccessTokensToServers, 17 setAccessTokensToServers,
26 testImage, 18 testImage,
27 updateVideo,
28 uploadVideo,
29 userLogin,
30 viewVideo,
31 wait, 19 wait,
20 waitJobs,
32 webtorrentAdd 21 webtorrentAdd
33} from '../../../../shared/extra-utils' 22} from '@shared/extra-utils'
34import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 23import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models'
35import {
36 addVideoCommentReply,
37 addVideoCommentThread,
38 deleteVideoComment,
39 findCommentId,
40 getVideoCommentThreads,
41 getVideoThreadComments
42} from '../../../../shared/extra-utils/videos/video-comments'
43import { VideoComment, VideoCommentThreadTree, VideoPrivacy } from '../../../../shared/models/videos'
44 24
45const expect = chai.expect 25const expect = chai.expect
46 26
47describe('Test multiple servers', function () { 27describe('Test multiple servers', function () {
48 let servers: ServerInfo[] = [] 28 let servers: PeerTubeServer[] = []
49 const toRemove = [] 29 const toRemove = []
50 let videoUUID = '' 30 let videoUUID = ''
51 let videoChannelId: number 31 let videoChannelId: number
@@ -53,7 +33,7 @@ describe('Test multiple servers', function () {
53 before(async function () { 33 before(async function () {
54 this.timeout(120000) 34 this.timeout(120000)
55 35
56 servers = await flushAndRunMultipleServers(3) 36 servers = await createMultipleServers(3)
57 37
58 // Get the access tokens 38 // Get the access tokens
59 await setAccessTokensToServers(servers) 39 await setAccessTokensToServers(servers)
@@ -64,9 +44,9 @@ describe('Test multiple servers', function () {
64 displayName: 'my channel', 44 displayName: 'my channel',
65 description: 'super channel' 45 description: 'super channel'
66 } 46 }
67 await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 47 await servers[0].channels.create({ attributes: videoChannel })
68 const channelRes = await getVideoChannelsList(servers[0].url, 0, 1) 48 const { data } = await servers[0].channels.list({ start: 0, count: 1 })
69 videoChannelId = channelRes.body.data[0].id 49 videoChannelId = data[0].id
70 } 50 }
71 51
72 // Server 1 and server 2 follow each other 52 // Server 1 and server 2 follow each other
@@ -79,10 +59,9 @@ describe('Test multiple servers', function () {
79 59
80 it('Should not have videos for all servers', async function () { 60 it('Should not have videos for all servers', async function () {
81 for (const server of servers) { 61 for (const server of servers) {
82 const res = await getVideosList(server.url) 62 const { data } = await server.videos.list()
83 const videos = res.body.data 63 expect(data).to.be.an('array')
84 expect(videos).to.be.an('array') 64 expect(data.length).to.equal(0)
85 expect(videos.length).to.equal(0)
86 } 65 }
87 }) 66 })
88 67
@@ -90,7 +69,7 @@ describe('Test multiple servers', function () {
90 it('Should upload the video on server 1 and propagate on each server', async function () { 69 it('Should upload the video on server 1 and propagate on each server', async function () {
91 this.timeout(25000) 70 this.timeout(25000)
92 71
93 const videoAttributes = { 72 const attributes = {
94 name: 'my super name for server 1', 73 name: 'my super name for server 1',
95 category: 5, 74 category: 5,
96 licence: 4, 75 licence: 4,
@@ -103,7 +82,7 @@ describe('Test multiple servers', function () {
103 channelId: videoChannelId, 82 channelId: videoChannelId,
104 fixture: 'video_short1.webm' 83 fixture: 'video_short1.webm'
105 } 84 }
106 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 85 await servers[0].videos.upload({ attributes })
107 86
108 await waitJobs(servers) 87 await waitJobs(servers)
109 88
@@ -146,14 +125,13 @@ describe('Test multiple servers', function () {
146 ] 125 ]
147 } 126 }
148 127
149 const res = await getVideosList(server.url) 128 const { data } = await server.videos.list()
150 const videos = res.body.data 129 expect(data).to.be.an('array')
151 expect(videos).to.be.an('array') 130 expect(data.length).to.equal(1)
152 expect(videos.length).to.equal(1) 131 const video = data[0]
153 const video = videos[0]
154 132
155 await completeVideoCheck(server.url, video, checkAttributes) 133 await completeVideoCheck(server, video, checkAttributes)
156 publishedAt = video.publishedAt 134 publishedAt = video.publishedAt as string
157 } 135 }
158 }) 136 })
159 137
@@ -164,10 +142,10 @@ describe('Test multiple servers', function () {
164 username: 'user1', 142 username: 'user1',
165 password: 'super_password' 143 password: 'super_password'
166 } 144 }
167 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password }) 145 await servers[1].users.create({ username: user.username, password: user.password })
168 const userAccessToken = await userLogin(servers[1], user) 146 const userAccessToken = await servers[1].login.getAccessToken(user)
169 147
170 const videoAttributes = { 148 const attributes = {
171 name: 'my super name for server 2', 149 name: 'my super name for server 2',
172 category: 4, 150 category: 4,
173 licence: 3, 151 licence: 3,
@@ -180,7 +158,7 @@ describe('Test multiple servers', function () {
180 thumbnailfile: 'thumbnail.jpg', 158 thumbnailfile: 'thumbnail.jpg',
181 previewfile: 'preview.jpg' 159 previewfile: 'preview.jpg'
182 } 160 }
183 await uploadVideo(servers[1].url, userAccessToken, videoAttributes, HttpStatusCode.OK_200, 'resumable') 161 await servers[1].videos.upload({ token: userAccessToken, attributes, mode: 'resumable' })
184 162
185 // Transcoding 163 // Transcoding
186 await waitJobs(servers) 164 await waitJobs(servers)
@@ -235,65 +213,67 @@ describe('Test multiple servers', function () {
235 previewfile: 'preview' 213 previewfile: 'preview'
236 } 214 }
237 215
238 const res = await getVideosList(server.url) 216 const { data } = await server.videos.list()
239 const videos = res.body.data 217 expect(data).to.be.an('array')
240 expect(videos).to.be.an('array') 218 expect(data.length).to.equal(2)
241 expect(videos.length).to.equal(2) 219 const video = data[1]
242 const video = videos[1]
243 220
244 await completeVideoCheck(server.url, video, checkAttributes) 221 await completeVideoCheck(server, video, checkAttributes)
245 } 222 }
246 }) 223 })
247 224
248 it('Should upload two videos on server 3 and propagate on each server', async function () { 225 it('Should upload two videos on server 3 and propagate on each server', async function () {
249 this.timeout(45000) 226 this.timeout(45000)
250 227
251 const videoAttributes1 = { 228 {
252 name: 'my super name for server 3', 229 const attributes = {
253 category: 6, 230 name: 'my super name for server 3',
254 licence: 5, 231 category: 6,
255 language: 'de', 232 licence: 5,
256 nsfw: true, 233 language: 'de',
257 description: 'my super description for server 3', 234 nsfw: true,
258 support: 'my super support text for server 3', 235 description: 'my super description for server 3',
259 tags: [ 'tag1p3' ], 236 support: 'my super support text for server 3',
260 fixture: 'video_short3.webm' 237 tags: [ 'tag1p3' ],
238 fixture: 'video_short3.webm'
239 }
240 await servers[2].videos.upload({ attributes })
261 } 241 }
262 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1) 242
263 243 {
264 const videoAttributes2 = { 244 const attributes = {
265 name: 'my super name for server 3-2', 245 name: 'my super name for server 3-2',
266 category: 7, 246 category: 7,
267 licence: 6, 247 licence: 6,
268 language: 'ko', 248 language: 'ko',
269 nsfw: false, 249 nsfw: false,
270 description: 'my super description for server 3-2', 250 description: 'my super description for server 3-2',
271 support: 'my super support text for server 3-2', 251 support: 'my super support text for server 3-2',
272 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], 252 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
273 fixture: 'video_short.webm' 253 fixture: 'video_short.webm'
254 }
255 await servers[2].videos.upload({ attributes })
274 } 256 }
275 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes2)
276 257
277 await waitJobs(servers) 258 await waitJobs(servers)
278 259
279 // All servers should have this video 260 // All servers should have this video
280 for (const server of servers) { 261 for (const server of servers) {
281 const isLocal = server.url === 'http://localhost:' + servers[2].port 262 const isLocal = server.url === 'http://localhost:' + servers[2].port
282 const res = await getVideosList(server.url) 263 const { data } = await server.videos.list()
283 264
284 const videos = res.body.data 265 expect(data).to.be.an('array')
285 expect(videos).to.be.an('array') 266 expect(data.length).to.equal(4)
286 expect(videos.length).to.equal(4)
287 267
288 // We not sure about the order of the two last uploads 268 // We not sure about the order of the two last uploads
289 let video1 = null 269 let video1 = null
290 let video2 = null 270 let video2 = null
291 if (videos[2].name === 'my super name for server 3') { 271 if (data[2].name === 'my super name for server 3') {
292 video1 = videos[2] 272 video1 = data[2]
293 video2 = videos[3] 273 video2 = data[3]
294 } else { 274 } else {
295 video1 = videos[3] 275 video1 = data[3]
296 video2 = videos[2] 276 video2 = data[2]
297 } 277 }
298 278
299 const checkAttributesVideo1 = { 279 const checkAttributesVideo1 = {
@@ -328,7 +308,7 @@ describe('Test multiple servers', function () {
328 } 308 }
329 ] 309 ]
330 } 310 }
331 await completeVideoCheck(server.url, video1, checkAttributesVideo1) 311 await completeVideoCheck(server, video1, checkAttributesVideo1)
332 312
333 const checkAttributesVideo2 = { 313 const checkAttributesVideo2 = {
334 name: 'my super name for server 3-2', 314 name: 'my super name for server 3-2',
@@ -362,38 +342,38 @@ describe('Test multiple servers', function () {
362 } 342 }
363 ] 343 ]
364 } 344 }
365 await completeVideoCheck(server.url, video2, checkAttributesVideo2) 345 await completeVideoCheck(server, video2, checkAttributesVideo2)
366 } 346 }
367 }) 347 })
368 }) 348 })
369 349
370 describe('It should list local videos', function () { 350 describe('It should list local videos', function () {
371 it('Should list only local videos on server 1', async function () { 351 it('Should list only local videos on server 1', async function () {
372 const { body } = await getLocalVideos(servers[0].url) 352 const { data, total } = await servers[0].videos.list({ filter: 'local' })
373 353
374 expect(body.total).to.equal(1) 354 expect(total).to.equal(1)
375 expect(body.data).to.be.an('array') 355 expect(data).to.be.an('array')
376 expect(body.data.length).to.equal(1) 356 expect(data.length).to.equal(1)
377 expect(body.data[0].name).to.equal('my super name for server 1') 357 expect(data[0].name).to.equal('my super name for server 1')
378 }) 358 })
379 359
380 it('Should list only local videos on server 2', async function () { 360 it('Should list only local videos on server 2', async function () {
381 const { body } = await getLocalVideos(servers[1].url) 361 const { data, total } = await servers[1].videos.list({ filter: 'local' })
382 362
383 expect(body.total).to.equal(1) 363 expect(total).to.equal(1)
384 expect(body.data).to.be.an('array') 364 expect(data).to.be.an('array')
385 expect(body.data.length).to.equal(1) 365 expect(data.length).to.equal(1)
386 expect(body.data[0].name).to.equal('my super name for server 2') 366 expect(data[0].name).to.equal('my super name for server 2')
387 }) 367 })
388 368
389 it('Should list only local videos on server 3', async function () { 369 it('Should list only local videos on server 3', async function () {
390 const { body } = await getLocalVideos(servers[2].url) 370 const { data, total } = await servers[2].videos.list({ filter: 'local' })
391 371
392 expect(body.total).to.equal(2) 372 expect(total).to.equal(2)
393 expect(body.data).to.be.an('array') 373 expect(data).to.be.an('array')
394 expect(body.data.length).to.equal(2) 374 expect(data.length).to.equal(2)
395 expect(body.data[0].name).to.equal('my super name for server 3') 375 expect(data[0].name).to.equal('my super name for server 3')
396 expect(body.data[1].name).to.equal('my super name for server 3-2') 376 expect(data[1].name).to.equal('my super name for server 3-2')
397 }) 377 })
398 }) 378 })
399 379
@@ -401,15 +381,13 @@ describe('Test multiple servers', function () {
401 it('Should add the file 1 by asking server 3', async function () { 381 it('Should add the file 1 by asking server 3', async function () {
402 this.timeout(10000) 382 this.timeout(10000)
403 383
404 const res = await getVideosList(servers[2].url) 384 const { data } = await servers[2].videos.list()
405
406 const video = res.body.data[0]
407 toRemove.push(res.body.data[2])
408 toRemove.push(res.body.data[3])
409 385
410 const res2 = await getVideo(servers[2].url, video.id) 386 const video = data[0]
411 const videoDetails = res2.body 387 toRemove.push(data[2])
388 toRemove.push(data[3])
412 389
390 const videoDetails = await servers[2].videos.get({ id: video.id })
413 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 391 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
414 expect(torrent.files).to.be.an('array') 392 expect(torrent.files).to.be.an('array')
415 expect(torrent.files.length).to.equal(1) 393 expect(torrent.files.length).to.equal(1)
@@ -419,11 +397,10 @@ describe('Test multiple servers', function () {
419 it('Should add the file 2 by asking server 1', async function () { 397 it('Should add the file 2 by asking server 1', async function () {
420 this.timeout(10000) 398 this.timeout(10000)
421 399
422 const res = await getVideosList(servers[0].url) 400 const { data } = await servers[0].videos.list()
423 401
424 const video = res.body.data[1] 402 const video = data[1]
425 const res2 = await getVideo(servers[0].url, video.id) 403 const videoDetails = await servers[0].videos.get({ id: video.id })
426 const videoDetails = res2.body
427 404
428 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 405 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
429 expect(torrent.files).to.be.an('array') 406 expect(torrent.files).to.be.an('array')
@@ -434,11 +411,10 @@ describe('Test multiple servers', function () {
434 it('Should add the file 3 by asking server 2', async function () { 411 it('Should add the file 3 by asking server 2', async function () {
435 this.timeout(10000) 412 this.timeout(10000)
436 413
437 const res = await getVideosList(servers[1].url) 414 const { data } = await servers[1].videos.list()
438 415
439 const video = res.body.data[2] 416 const video = data[2]
440 const res2 = await getVideo(servers[1].url, video.id) 417 const videoDetails = await servers[1].videos.get({ id: video.id })
441 const videoDetails = res2.body
442 418
443 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true) 419 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
444 expect(torrent.files).to.be.an('array') 420 expect(torrent.files).to.be.an('array')
@@ -449,11 +425,10 @@ describe('Test multiple servers', function () {
449 it('Should add the file 3-2 by asking server 1', async function () { 425 it('Should add the file 3-2 by asking server 1', async function () {
450 this.timeout(10000) 426 this.timeout(10000)
451 427
452 const res = await getVideosList(servers[0].url) 428 const { data } = await servers[0].videos.list()
453 429
454 const video = res.body.data[3] 430 const video = data[3]
455 const res2 = await getVideo(servers[0].url, video.id) 431 const videoDetails = await servers[0].videos.get({ id: video.id })
456 const videoDetails = res2.body
457 432
458 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri) 433 const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
459 expect(torrent.files).to.be.an('array') 434 expect(torrent.files).to.be.an('array')
@@ -464,11 +439,10 @@ describe('Test multiple servers', function () {
464 it('Should add the file 2 in 360p by asking server 1', async function () { 439 it('Should add the file 2 in 360p by asking server 1', async function () {
465 this.timeout(10000) 440 this.timeout(10000)
466 441
467 const res = await getVideosList(servers[0].url) 442 const { data } = await servers[0].videos.list()
468 443
469 const video = res.body.data.find(v => v.name === 'my super name for server 2') 444 const video = data.find(v => v.name === 'my super name for server 2')
470 const res2 = await getVideo(servers[0].url, video.id) 445 const videoDetails = await servers[0].videos.get({ id: video.id })
471 const videoDetails = res2.body
472 446
473 const file = videoDetails.files.find(f => f.resolution.id === 360) 447 const file = videoDetails.files.find(f => f.resolution.id === 360)
474 expect(file).not.to.be.undefined 448 expect(file).not.to.be.undefined
@@ -487,30 +461,36 @@ describe('Test multiple servers', function () {
487 let remoteVideosServer3 = [] 461 let remoteVideosServer3 = []
488 462
489 before(async function () { 463 before(async function () {
490 const res1 = await getVideosList(servers[0].url) 464 {
491 remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 465 const { data } = await servers[0].videos.list()
466 remoteVideosServer1 = data.filter(video => video.isLocal === false).map(video => video.uuid)
467 }
492 468
493 const res2 = await getVideosList(servers[1].url) 469 {
494 remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 470 const { data } = await servers[1].videos.list()
471 remoteVideosServer2 = data.filter(video => video.isLocal === false).map(video => video.uuid)
472 }
495 473
496 const res3 = await getVideosList(servers[2].url) 474 {
497 localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid) 475 const { data } = await servers[2].videos.list()
498 remoteVideosServer3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 476 localVideosServer3 = data.filter(video => video.isLocal === true).map(video => video.uuid)
477 remoteVideosServer3 = data.filter(video => video.isLocal === false).map(video => video.uuid)
478 }
499 }) 479 })
500 480
501 it('Should view multiple videos on owned servers', async function () { 481 it('Should view multiple videos on owned servers', async function () {
502 this.timeout(30000) 482 this.timeout(30000)
503 483
504 await viewVideo(servers[2].url, localVideosServer3[0]) 484 await servers[2].videos.view({ id: localVideosServer3[0] })
505 await wait(1000) 485 await wait(1000)
506 486
507 await viewVideo(servers[2].url, localVideosServer3[0]) 487 await servers[2].videos.view({ id: localVideosServer3[0] })
508 await viewVideo(servers[2].url, localVideosServer3[1]) 488 await servers[2].videos.view({ id: localVideosServer3[1] })
509 489
510 await wait(1000) 490 await wait(1000)
511 491
512 await viewVideo(servers[2].url, localVideosServer3[0]) 492 await servers[2].videos.view({ id: localVideosServer3[0] })
513 await viewVideo(servers[2].url, localVideosServer3[0]) 493 await servers[2].videos.view({ id: localVideosServer3[0] })
514 494
515 await waitJobs(servers) 495 await waitJobs(servers)
516 496
@@ -520,11 +500,10 @@ describe('Test multiple servers', function () {
520 await waitJobs(servers) 500 await waitJobs(servers)
521 501
522 for (const server of servers) { 502 for (const server of servers) {
523 const res = await getVideosList(server.url) 503 const { data } = await server.videos.list()
524 504
525 const videos = res.body.data 505 const video0 = data.find(v => v.uuid === localVideosServer3[0])
526 const video0 = videos.find(v => v.uuid === localVideosServer3[0]) 506 const video1 = data.find(v => v.uuid === localVideosServer3[1])
527 const video1 = videos.find(v => v.uuid === localVideosServer3[1])
528 507
529 expect(video0.views).to.equal(3) 508 expect(video0.views).to.equal(3)
530 expect(video1.views).to.equal(1) 509 expect(video1.views).to.equal(1)
@@ -535,16 +514,16 @@ describe('Test multiple servers', function () {
535 this.timeout(45000) 514 this.timeout(45000)
536 515
537 const tasks: Promise<any>[] = [] 516 const tasks: Promise<any>[] = []
538 tasks.push(viewVideo(servers[0].url, remoteVideosServer1[0])) 517 tasks.push(servers[0].videos.view({ id: remoteVideosServer1[0] }))
539 tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0])) 518 tasks.push(servers[1].videos.view({ id: remoteVideosServer2[0] }))
540 tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0])) 519 tasks.push(servers[1].videos.view({ id: remoteVideosServer2[0] }))
541 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[0])) 520 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[0] }))
542 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 521 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
543 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 522 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
544 tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1])) 523 tasks.push(servers[2].videos.view({ id: remoteVideosServer3[1] }))
545 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 524 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
546 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 525 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
547 tasks.push(viewVideo(servers[2].url, localVideosServer3[1])) 526 tasks.push(servers[2].videos.view({ id: localVideosServer3[1] }))
548 527
549 await Promise.all(tasks) 528 await Promise.all(tasks)
550 529
@@ -558,18 +537,16 @@ describe('Test multiple servers', function () {
558 let baseVideos = null 537 let baseVideos = null
559 538
560 for (const server of servers) { 539 for (const server of servers) {
561 const res = await getVideosList(server.url) 540 const { data } = await server.videos.list()
562
563 const videos = res.body.data
564 541
565 // Initialize base videos for future comparisons 542 // Initialize base videos for future comparisons
566 if (baseVideos === null) { 543 if (baseVideos === null) {
567 baseVideos = videos 544 baseVideos = data
568 continue 545 continue
569 } 546 }
570 547
571 for (const baseVideo of baseVideos) { 548 for (const baseVideo of baseVideos) {
572 const sameVideo = videos.find(video => video.name === baseVideo.name) 549 const sameVideo = data.find(video => video.name === baseVideo.name)
573 expect(baseVideo.views).to.equal(sameVideo.views) 550 expect(baseVideo.views).to.equal(sameVideo.views)
574 } 551 }
575 } 552 }
@@ -578,35 +555,34 @@ describe('Test multiple servers', function () {
578 it('Should like and dislikes videos on different services', async function () { 555 it('Should like and dislikes videos on different services', async function () {
579 this.timeout(50000) 556 this.timeout(50000)
580 557
581 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') 558 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
582 await wait(500) 559 await wait(500)
583 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike') 560 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'dislike' })
584 await wait(500) 561 await wait(500)
585 await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') 562 await servers[0].videos.rate({ id: remoteVideosServer1[0], rating: 'like' })
586 await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like') 563 await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'like' })
587 await wait(500) 564 await wait(500)
588 await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike') 565 await servers[2].videos.rate({ id: localVideosServer3[1], rating: 'dislike' })
589 await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike') 566 await servers[2].videos.rate({ id: remoteVideosServer3[1], rating: 'dislike' })
590 await wait(500) 567 await wait(500)
591 await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like') 568 await servers[2].videos.rate({ id: remoteVideosServer3[0], rating: 'like' })
592 569
593 await waitJobs(servers) 570 await waitJobs(servers)
594 await wait(5000) 571 await wait(5000)
572 await waitJobs(servers)
595 573
596 let baseVideos = null 574 let baseVideos = null
597 for (const server of servers) { 575 for (const server of servers) {
598 const res = await getVideosList(server.url) 576 const { data } = await server.videos.list()
599
600 const videos = res.body.data
601 577
602 // Initialize base videos for future comparisons 578 // Initialize base videos for future comparisons
603 if (baseVideos === null) { 579 if (baseVideos === null) {
604 baseVideos = videos 580 baseVideos = data
605 continue 581 continue
606 } 582 }
607 583
608 for (const baseVideo of baseVideos) { 584 for (const baseVideo of baseVideos) {
609 const sameVideo = videos.find(video => video.name === baseVideo.name) 585 const sameVideo = data.find(video => video.name === baseVideo.name)
610 expect(baseVideo.likes).to.equal(sameVideo.likes) 586 expect(baseVideo.likes).to.equal(sameVideo.likes)
611 expect(baseVideo.dislikes).to.equal(sameVideo.dislikes) 587 expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
612 } 588 }
@@ -632,7 +608,7 @@ describe('Test multiple servers', function () {
632 previewfile: 'preview.jpg' 608 previewfile: 'preview.jpg'
633 } 609 }
634 610
635 await updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, attributes) 611 await servers[2].videos.update({ id: toRemove[0].id, attributes })
636 612
637 await waitJobs(servers) 613 await waitJobs(servers)
638 }) 614 })
@@ -641,10 +617,9 @@ describe('Test multiple servers', function () {
641 this.timeout(10000) 617 this.timeout(10000)
642 618
643 for (const server of servers) { 619 for (const server of servers) {
644 const res = await getVideosList(server.url) 620 const { data } = await server.videos.list()
645 621
646 const videos = res.body.data 622 const videoUpdated = data.find(video => video.name === 'my super video updated')
647 const videoUpdated = videos.find(video => video.name === 'my super video updated')
648 expect(!!videoUpdated).to.be.true 623 expect(!!videoUpdated).to.be.true
649 624
650 const isLocal = server.url === 'http://localhost:' + servers[2].port 625 const isLocal = server.url === 'http://localhost:' + servers[2].port
@@ -683,49 +658,46 @@ describe('Test multiple servers', function () {
683 thumbnailfile: 'thumbnail', 658 thumbnailfile: 'thumbnail',
684 previewfile: 'preview' 659 previewfile: 'preview'
685 } 660 }
686 await completeVideoCheck(server.url, videoUpdated, checkAttributes) 661 await completeVideoCheck(server, videoUpdated, checkAttributes)
687 } 662 }
688 }) 663 })
689 664
690 it('Should remove the videos 3 and 3-2 by asking server 3', async function () { 665 it('Should remove the videos 3 and 3-2 by asking server 3 and correctly delete files', async function () {
691 this.timeout(10000) 666 this.timeout(30000)
692 667
693 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id) 668 for (const id of [ toRemove[0].id, toRemove[1].id ]) {
694 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[1].id) 669 await saveVideoInServers(servers, id)
695 670
696 await waitJobs(servers) 671 await servers[2].videos.remove({ id })
697 })
698 672
699 it('Should not have files of videos 3 and 3-2 on each server', async function () { 673 await waitJobs(servers)
700 for (const server of servers) { 674
701 await checkVideoFilesWereRemoved(toRemove[0].uuid, server.internalServerNumber) 675 for (const server of servers) {
702 await checkVideoFilesWereRemoved(toRemove[1].uuid, server.internalServerNumber) 676 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
677 }
703 } 678 }
704 }) 679 })
705 680
706 it('Should have videos 1 and 3 on each server', async function () { 681 it('Should have videos 1 and 3 on each server', async function () {
707 for (const server of servers) { 682 for (const server of servers) {
708 const res = await getVideosList(server.url) 683 const { data } = await server.videos.list()
709 684
710 const videos = res.body.data 685 expect(data).to.be.an('array')
711 expect(videos).to.be.an('array') 686 expect(data.length).to.equal(2)
712 expect(videos.length).to.equal(2) 687 expect(data[0].name).not.to.equal(data[1].name)
713 expect(videos[0].name).not.to.equal(videos[1].name) 688 expect(data[0].name).not.to.equal(toRemove[0].name)
714 expect(videos[0].name).not.to.equal(toRemove[0].name) 689 expect(data[1].name).not.to.equal(toRemove[0].name)
715 expect(videos[1].name).not.to.equal(toRemove[0].name) 690 expect(data[0].name).not.to.equal(toRemove[1].name)
716 expect(videos[0].name).not.to.equal(toRemove[1].name) 691 expect(data[1].name).not.to.equal(toRemove[1].name)
717 expect(videos[1].name).not.to.equal(toRemove[1].name) 692
718 693 videoUUID = data.find(video => video.name === 'my super name for server 1').uuid
719 videoUUID = videos.find(video => video.name === 'my super name for server 1').uuid
720 } 694 }
721 }) 695 })
722 696
723 it('Should get the same video by UUID on each server', async function () { 697 it('Should get the same video by UUID on each server', async function () {
724 let baseVideo = null 698 let baseVideo = null
725 for (const server of servers) { 699 for (const server of servers) {
726 const res = await getVideo(server.url, videoUUID) 700 const video = await server.videos.get({ id: videoUUID })
727
728 const video = res.body
729 701
730 if (baseVideo === null) { 702 if (baseVideo === null) {
731 baseVideo = video 703 baseVideo = video
@@ -748,8 +720,7 @@ describe('Test multiple servers', function () {
748 720
749 it('Should get the preview from each server', async function () { 721 it('Should get the preview from each server', async function () {
750 for (const server of servers) { 722 for (const server of servers) {
751 const res = await getVideo(server.url, videoUUID) 723 const video = await server.videos.get({ id: videoUUID })
752 const video = res.body
753 724
754 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 725 await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
755 } 726 }
@@ -764,36 +735,36 @@ describe('Test multiple servers', function () {
764 735
765 { 736 {
766 const text = 'my super first comment' 737 const text = 'my super first comment'
767 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, text) 738 await servers[0].comments.createThread({ videoId: videoUUID, text })
768 } 739 }
769 740
770 { 741 {
771 const text = 'my super second comment' 742 const text = 'my super second comment'
772 await addVideoCommentThread(servers[2].url, servers[2].accessToken, videoUUID, text) 743 await servers[2].comments.createThread({ videoId: videoUUID, text })
773 } 744 }
774 745
775 await waitJobs(servers) 746 await waitJobs(servers)
776 747
777 { 748 {
778 const threadId = await findCommentId(servers[1].url, videoUUID, 'my super first comment') 749 const threadId = await servers[1].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
779 750
780 const text = 'my super answer to thread 1' 751 const text = 'my super answer to thread 1'
781 await addVideoCommentReply(servers[1].url, servers[1].accessToken, videoUUID, threadId, text) 752 await servers[1].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text })
782 } 753 }
783 754
784 await waitJobs(servers) 755 await waitJobs(servers)
785 756
786 { 757 {
787 const threadId = await findCommentId(servers[2].url, videoUUID, 'my super first comment') 758 const threadId = await servers[2].comments.findCommentId({ videoId: videoUUID, text: 'my super first comment' })
788 759
789 const res2 = await getVideoThreadComments(servers[2].url, videoUUID, threadId) 760 const body = await servers[2].comments.getThread({ videoId: videoUUID, threadId })
790 const childCommentId = res2.body.children[0].comment.id 761 const childCommentId = body.children[0].comment.id
791 762
792 const text3 = 'my second answer to thread 1' 763 const text3 = 'my second answer to thread 1'
793 await addVideoCommentReply(servers[2].url, servers[2].accessToken, videoUUID, threadId, text3) 764 await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text: text3 })
794 765
795 const text2 = 'my super answer to answer of thread 1' 766 const text2 = 'my super answer to answer of thread 1'
796 await addVideoCommentReply(servers[2].url, servers[2].accessToken, videoUUID, childCommentId, text2) 767 await servers[2].comments.addReply({ videoId: videoUUID, toCommentId: childCommentId, text: text2 })
797 } 768 }
798 769
799 await waitJobs(servers) 770 await waitJobs(servers)
@@ -801,14 +772,14 @@ describe('Test multiple servers', function () {
801 772
802 it('Should have these threads', async function () { 773 it('Should have these threads', async function () {
803 for (const server of servers) { 774 for (const server of servers) {
804 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 775 const body = await server.comments.listThreads({ videoId: videoUUID })
805 776
806 expect(res.body.total).to.equal(2) 777 expect(body.total).to.equal(2)
807 expect(res.body.data).to.be.an('array') 778 expect(body.data).to.be.an('array')
808 expect(res.body.data).to.have.lengthOf(2) 779 expect(body.data).to.have.lengthOf(2)
809 780
810 { 781 {
811 const comment: VideoComment = res.body.data.find(c => c.text === 'my super first comment') 782 const comment = body.data.find(c => c.text === 'my super first comment')
812 expect(comment).to.not.be.undefined 783 expect(comment).to.not.be.undefined
813 expect(comment.inReplyToCommentId).to.be.null 784 expect(comment.inReplyToCommentId).to.be.null
814 expect(comment.account.name).to.equal('root') 785 expect(comment.account.name).to.equal('root')
@@ -819,7 +790,7 @@ describe('Test multiple servers', function () {
819 } 790 }
820 791
821 { 792 {
822 const comment: VideoComment = res.body.data.find(c => c.text === 'my super second comment') 793 const comment = body.data.find(c => c.text === 'my super second comment')
823 expect(comment).to.not.be.undefined 794 expect(comment).to.not.be.undefined
824 expect(comment.inReplyToCommentId).to.be.null 795 expect(comment.inReplyToCommentId).to.be.null
825 expect(comment.account.name).to.equal('root') 796 expect(comment.account.name).to.equal('root')
@@ -833,12 +804,11 @@ describe('Test multiple servers', function () {
833 804
834 it('Should have these comments', async function () { 805 it('Should have these comments', async function () {
835 for (const server of servers) { 806 for (const server of servers) {
836 const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 807 const body = await server.comments.listThreads({ videoId: videoUUID })
837 const threadId = res1.body.data.find(c => c.text === 'my super first comment').id 808 const threadId = body.data.find(c => c.text === 'my super first comment').id
838 809
839 const res2 = await getVideoThreadComments(server.url, videoUUID, threadId) 810 const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
840 811
841 const tree: VideoCommentThreadTree = res2.body
842 expect(tree.comment.text).equal('my super first comment') 812 expect(tree.comment.text).equal('my super first comment')
843 expect(tree.comment.account.name).equal('root') 813 expect(tree.comment.account.name).equal('root')
844 expect(tree.comment.account.host).equal('localhost:' + servers[0].port) 814 expect(tree.comment.account.host).equal('localhost:' + servers[0].port)
@@ -867,19 +837,17 @@ describe('Test multiple servers', function () {
867 it('Should delete a reply', async function () { 837 it('Should delete a reply', async function () {
868 this.timeout(10000) 838 this.timeout(10000)
869 839
870 await deleteVideoComment(servers[2].url, servers[2].accessToken, videoUUID, childOfFirstChild.comment.id) 840 await servers[2].comments.delete({ videoId: videoUUID, commentId: childOfFirstChild.comment.id })
871 841
872 await waitJobs(servers) 842 await waitJobs(servers)
873 }) 843 })
874 844
875 it('Should have this comment marked as deleted', async function () { 845 it('Should have this comment marked as deleted', async function () {
876 for (const server of servers) { 846 for (const server of servers) {
877 const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 847 const { data } = await server.comments.listThreads({ videoId: videoUUID })
878 const threadId = res1.body.data.find(c => c.text === 'my super first comment').id 848 const threadId = data.find(c => c.text === 'my super first comment').id
879
880 const res2 = await getVideoThreadComments(server.url, videoUUID, threadId)
881 849
882 const tree: VideoCommentThreadTree = res2.body 850 const tree = await server.comments.getThread({ videoId: videoUUID, threadId })
883 expect(tree.comment.text).equal('my super first comment') 851 expect(tree.comment.text).equal('my super first comment')
884 852
885 const firstChild = tree.children[0] 853 const firstChild = tree.children[0]
@@ -900,23 +868,23 @@ describe('Test multiple servers', function () {
900 it('Should delete the thread comments', async function () { 868 it('Should delete the thread comments', async function () {
901 this.timeout(10000) 869 this.timeout(10000)
902 870
903 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 5) 871 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
904 const threadId = res.body.data.find(c => c.text === 'my super first comment').id 872 const commentId = data.find(c => c.text === 'my super first comment').id
905 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 873 await servers[0].comments.delete({ videoId: videoUUID, commentId })
906 874
907 await waitJobs(servers) 875 await waitJobs(servers)
908 }) 876 })
909 877
910 it('Should have the threads marked as deleted on other servers too', async function () { 878 it('Should have the threads marked as deleted on other servers too', async function () {
911 for (const server of servers) { 879 for (const server of servers) {
912 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 880 const body = await server.comments.listThreads({ videoId: videoUUID })
913 881
914 expect(res.body.total).to.equal(2) 882 expect(body.total).to.equal(2)
915 expect(res.body.data).to.be.an('array') 883 expect(body.data).to.be.an('array')
916 expect(res.body.data).to.have.lengthOf(2) 884 expect(body.data).to.have.lengthOf(2)
917 885
918 { 886 {
919 const comment: VideoComment = res.body.data[0] 887 const comment = body.data[0]
920 expect(comment).to.not.be.undefined 888 expect(comment).to.not.be.undefined
921 expect(comment.inReplyToCommentId).to.be.null 889 expect(comment.inReplyToCommentId).to.be.null
922 expect(comment.account.name).to.equal('root') 890 expect(comment.account.name).to.equal('root')
@@ -927,7 +895,7 @@ describe('Test multiple servers', function () {
927 } 895 }
928 896
929 { 897 {
930 const deletedComment: VideoComment = res.body.data[1] 898 const deletedComment = body.data[1]
931 expect(deletedComment).to.not.be.undefined 899 expect(deletedComment).to.not.be.undefined
932 expect(deletedComment.isDeleted).to.be.true 900 expect(deletedComment.isDeleted).to.be.true
933 expect(deletedComment.deletedAt).to.not.be.null 901 expect(deletedComment.deletedAt).to.not.be.null
@@ -945,22 +913,22 @@ describe('Test multiple servers', function () {
945 it('Should delete a remote thread by the origin server', async function () { 913 it('Should delete a remote thread by the origin server', async function () {
946 this.timeout(5000) 914 this.timeout(5000)
947 915
948 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 5) 916 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
949 const threadId = res.body.data.find(c => c.text === 'my super second comment').id 917 const commentId = data.find(c => c.text === 'my super second comment').id
950 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 918 await servers[0].comments.delete({ videoId: videoUUID, commentId })
951 919
952 await waitJobs(servers) 920 await waitJobs(servers)
953 }) 921 })
954 922
955 it('Should have the threads marked as deleted on other servers too', async function () { 923 it('Should have the threads marked as deleted on other servers too', async function () {
956 for (const server of servers) { 924 for (const server of servers) {
957 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 925 const body = await server.comments.listThreads({ videoId: videoUUID })
958 926
959 expect(res.body.total).to.equal(2) 927 expect(body.total).to.equal(2)
960 expect(res.body.data).to.have.lengthOf(2) 928 expect(body.data).to.have.lengthOf(2)
961 929
962 { 930 {
963 const comment: VideoComment = res.body.data[0] 931 const comment = body.data[0]
964 expect(comment.text).to.equal('') 932 expect(comment.text).to.equal('')
965 expect(comment.isDeleted).to.be.true 933 expect(comment.isDeleted).to.be.true
966 expect(comment.createdAt).to.not.be.null 934 expect(comment.createdAt).to.not.be.null
@@ -970,7 +938,7 @@ describe('Test multiple servers', function () {
970 } 938 }
971 939
972 { 940 {
973 const comment: VideoComment = res.body.data[1] 941 const comment = body.data[1]
974 expect(comment.text).to.equal('') 942 expect(comment.text).to.equal('')
975 expect(comment.isDeleted).to.be.true 943 expect(comment.isDeleted).to.be.true
976 expect(comment.createdAt).to.not.be.null 944 expect(comment.createdAt).to.not.be.null
@@ -989,17 +957,17 @@ describe('Test multiple servers', function () {
989 downloadEnabled: false 957 downloadEnabled: false
990 } 958 }
991 959
992 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, attributes) 960 await servers[0].videos.update({ id: videoUUID, attributes })
993 961
994 await waitJobs(servers) 962 await waitJobs(servers)
995 963
996 for (const server of servers) { 964 for (const server of servers) {
997 const res = await getVideo(server.url, videoUUID) 965 const video = await server.videos.get({ id: videoUUID })
998 expect(res.body.commentsEnabled).to.be.false 966 expect(video.commentsEnabled).to.be.false
999 expect(res.body.downloadEnabled).to.be.false 967 expect(video.downloadEnabled).to.be.false
1000 968
1001 const text = 'my super forbidden comment' 969 const text = 'my super forbidden comment'
1002 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, HttpStatusCode.CONFLICT_409) 970 await server.comments.createThread({ videoId: videoUUID, text, expectedStatus: HttpStatusCode.CONFLICT_409 })
1003 } 971 }
1004 }) 972 })
1005 }) 973 })
@@ -1024,8 +992,8 @@ describe('Test multiple servers', function () {
1024 await waitJobs(servers) 992 await waitJobs(servers)
1025 993
1026 for (const server of servers) { 994 for (const server of servers) {
1027 const res = await getVideosList(server.url) 995 const { data } = await server.videos.list()
1028 const video = res.body.data.find(v => v.name === 'minimum parameters') 996 const video = data.find(v => v.name === 'minimum parameters')
1029 997
1030 const isLocal = server.url === 'http://localhost:' + servers[1].port 998 const isLocal = server.url === 'http://localhost:' + servers[1].port
1031 const checkAttributes = { 999 const checkAttributes = {
@@ -1072,7 +1040,7 @@ describe('Test multiple servers', function () {
1072 } 1040 }
1073 ] 1041 ]
1074 } 1042 }
1075 await completeVideoCheck(server.url, video, checkAttributes) 1043 await completeVideoCheck(server, video, checkAttributes)
1076 } 1044 }
1077 }) 1045 })
1078 }) 1046 })
diff --git a/server/tests/api/videos/resumable-upload.ts b/server/tests/api/videos/resumable-upload.ts
index 4fc3317df..857859fd3 100644
--- a/server/tests/api/videos/resumable-upload.ts
+++ b/server/tests/api/videos/resumable-upload.ts
@@ -4,22 +4,15 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { pathExists, readdir, stat } from 'fs-extra' 5import { pathExists, readdir, stat } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { HttpStatusCode } from '@shared/core-utils'
8import { 7import {
9 buildAbsoluteFixturePath, 8 buildAbsoluteFixturePath,
10 buildServerDirectory,
11 cleanupTests, 9 cleanupTests,
12 flushAndRunServer, 10 createSingleServer,
13 getMyUserInformation, 11 PeerTubeServer,
14 prepareResumableUpload,
15 sendDebugCommand,
16 sendResumableChunks,
17 ServerInfo,
18 setAccessTokensToServers, 12 setAccessTokensToServers,
19 setDefaultVideoChannel, 13 setDefaultVideoChannel
20 updateUser
21} from '@shared/extra-utils' 14} from '@shared/extra-utils'
22import { MyUser, VideoPrivacy } from '@shared/models' 15import { HttpStatusCode, VideoPrivacy } from '@shared/models'
23 16
24const expect = chai.expect 17const expect = chai.expect
25 18
@@ -27,7 +20,7 @@ const expect = chai.expect
27 20
28describe('Test resumable upload', function () { 21describe('Test resumable upload', function () {
29 const defaultFixture = 'video_short.mp4' 22 const defaultFixture = 'video_short.mp4'
30 let server: ServerInfo 23 let server: PeerTubeServer
31 let rootId: number 24 let rootId: number
32 25
33 async function buildSize (fixture: string, size?: number) { 26 async function buildSize (fixture: string, size?: number) {
@@ -42,14 +35,14 @@ describe('Test resumable upload', function () {
42 35
43 const attributes = { 36 const attributes = {
44 name: 'video', 37 name: 'video',
45 channelId: server.videoChannel.id, 38 channelId: server.store.channel.id,
46 privacy: VideoPrivacy.PUBLIC, 39 privacy: VideoPrivacy.PUBLIC,
47 fixture: defaultFixture 40 fixture: defaultFixture
48 } 41 }
49 42
50 const mimetype = 'video/mp4' 43 const mimetype = 'video/mp4'
51 44
52 const res = await prepareResumableUpload({ url: server.url, token: server.accessToken, attributes, size, mimetype }) 45 const res = await server.videos.prepareResumableUpload({ attributes, size, mimetype })
53 46
54 return res.header['location'].split('?')[1] 47 return res.header['location'].split('?')[1]
55 } 48 }
@@ -67,15 +60,13 @@ describe('Test resumable upload', function () {
67 const size = await buildSize(defaultFixture, options.size) 60 const size = await buildSize(defaultFixture, options.size)
68 const absoluteFilePath = buildAbsoluteFixturePath(defaultFixture) 61 const absoluteFilePath = buildAbsoluteFixturePath(defaultFixture)
69 62
70 return sendResumableChunks({ 63 return server.videos.sendResumableChunks({
71 url: server.url,
72 token: server.accessToken,
73 pathUploadId, 64 pathUploadId,
74 videoFilePath: absoluteFilePath, 65 videoFilePath: absoluteFilePath,
75 size, 66 size,
76 contentLength, 67 contentLength,
77 contentRangeBuilder, 68 contentRangeBuilder,
78 specialStatus: expectedStatus 69 expectedStatus
79 }) 70 })
80 } 71 }
81 72
@@ -83,7 +74,7 @@ describe('Test resumable upload', function () {
83 const uploadId = uploadIdArg.replace(/^upload_id=/, '') 74 const uploadId = uploadIdArg.replace(/^upload_id=/, '')
84 75
85 const subPath = join('tmp', 'resumable-uploads', uploadId) 76 const subPath = join('tmp', 'resumable-uploads', uploadId)
86 const filePath = buildServerDirectory(server, subPath) 77 const filePath = server.servers.buildDirectory(subPath)
87 const exists = await pathExists(filePath) 78 const exists = await pathExists(filePath)
88 79
89 if (expectedSize === null) { 80 if (expectedSize === null) {
@@ -98,7 +89,7 @@ describe('Test resumable upload', function () {
98 89
99 async function countResumableUploads () { 90 async function countResumableUploads () {
100 const subPath = join('tmp', 'resumable-uploads') 91 const subPath = join('tmp', 'resumable-uploads')
101 const filePath = buildServerDirectory(server, subPath) 92 const filePath = server.servers.buildDirectory(subPath)
102 93
103 const files = await readdir(filePath) 94 const files = await readdir(filePath)
104 return files.length 95 return files.length
@@ -107,19 +98,14 @@ describe('Test resumable upload', function () {
107 before(async function () { 98 before(async function () {
108 this.timeout(30000) 99 this.timeout(30000)
109 100
110 server = await flushAndRunServer(1) 101 server = await createSingleServer(1)
111 await setAccessTokensToServers([ server ]) 102 await setAccessTokensToServers([ server ])
112 await setDefaultVideoChannel([ server ]) 103 await setDefaultVideoChannel([ server ])
113 104
114 const res = await getMyUserInformation(server.url, server.accessToken) 105 const body = await server.users.getMyInfo()
115 rootId = (res.body as MyUser).id 106 rootId = body.id
116 107
117 await updateUser({ 108 await server.users.update({ userId: rootId, videoQuota: 10_000_000 })
118 url: server.url,
119 userId: rootId,
120 accessToken: server.accessToken,
121 videoQuota: 10_000_000
122 })
123 }) 109 })
124 110
125 describe('Directory cleaning', function () { 111 describe('Directory cleaning', function () {
@@ -138,13 +124,13 @@ describe('Test resumable upload', function () {
138 }) 124 })
139 125
140 it('Should not delete recent uploads', async function () { 126 it('Should not delete recent uploads', async function () {
141 await sendDebugCommand(server.url, server.accessToken, { command: 'remove-dandling-resumable-uploads' }) 127 await server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
142 128
143 expect(await countResumableUploads()).to.equal(2) 129 expect(await countResumableUploads()).to.equal(2)
144 }) 130 })
145 131
146 it('Should delete old uploads', async function () { 132 it('Should delete old uploads', async function () {
147 await sendDebugCommand(server.url, server.accessToken, { command: 'remove-dandling-resumable-uploads' }) 133 await server.debug.sendCommand({ body: { command: 'remove-dandling-resumable-uploads' } })
148 134
149 expect(await countResumableUploads()).to.equal(0) 135 expect(await countResumableUploads()).to.equal(0)
150 }) 136 })
@@ -160,8 +146,7 @@ describe('Test resumable upload', function () {
160 }) 146 })
161 147
162 it('Should not accept more chunks than expected', async function () { 148 it('Should not accept more chunks than expected', async function () {
163 const size = 100 149 const uploadId = await prepareUpload(100)
164 const uploadId = await prepareUpload(size)
165 150
166 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409 }) 151 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409 })
167 await checkFileSize(uploadId, 0) 152 await checkFileSize(uploadId, 0)
@@ -170,8 +155,14 @@ describe('Test resumable upload', function () {
170 it('Should not accept more chunks than expected with an invalid content length/content range', async function () { 155 it('Should not accept more chunks than expected with an invalid content length/content range', async function () {
171 const uploadId = await prepareUpload(1500) 156 const uploadId = await prepareUpload(1500)
172 157
173 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentLength: 1000 }) 158 // Content length check seems to have changed in v16
174 await checkFileSize(uploadId, 0) 159 if (process.version.startsWith('v16')) {
160 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.CONFLICT_409, contentLength: 1000 })
161 await checkFileSize(uploadId, 1000)
162 } else {
163 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentLength: 1000 })
164 await checkFileSize(uploadId, 0)
165 }
175 }) 166 })
176 167
177 it('Should not accept more chunks than expected with an invalid content length', async function () { 168 it('Should not accept more chunks than expected with an invalid content length', async function () {
@@ -179,8 +170,13 @@ describe('Test resumable upload', function () {
179 170
180 const size = 1000 171 const size = 1000
181 172
182 const contentRangeBuilder = start => `bytes ${start}-${start + size - 1}/${size}` 173 // Content length check seems to have changed in v16
183 await sendChunks({ pathUploadId: uploadId, expectedStatus: HttpStatusCode.BAD_REQUEST_400, contentRangeBuilder, contentLength: size }) 174 const expectedStatus = process.version.startsWith('v16')
175 ? HttpStatusCode.CONFLICT_409
176 : HttpStatusCode.BAD_REQUEST_400
177
178 const contentRangeBuilder = (start: number) => `bytes ${start}-${start + size - 1}/${size}`
179 await sendChunks({ pathUploadId: uploadId, expectedStatus, contentRangeBuilder, contentLength: size })
184 await checkFileSize(uploadId, 0) 180 await checkFileSize(uploadId, 0)
185 }) 181 })
186 }) 182 })
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index 1058a1e9c..29dac6ec1 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -2,43 +2,26 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { keyBy } from 'lodash'
6
7import { 5import {
8 checkVideoFilesWereRemoved, 6 checkVideoFilesWereRemoved,
9 cleanupTests, 7 cleanupTests,
10 completeVideoCheck, 8 completeVideoCheck,
11 flushAndRunServer, 9 createSingleServer,
12 getVideo, 10 PeerTubeServer,
13 getVideoCategories,
14 getVideoLanguages,
15 getVideoLicences,
16 getVideoPrivacies,
17 getVideosList,
18 getVideosListPagination,
19 getVideosListSort,
20 getVideosWithFilters,
21 rateVideo,
22 removeVideo,
23 ServerInfo,
24 setAccessTokensToServers, 11 setAccessTokensToServers,
25 testImage, 12 testImage,
26 updateVideo,
27 uploadVideo,
28 viewVideo,
29 wait 13 wait
30} from '../../../../shared/extra-utils' 14} from '@shared/extra-utils'
31import { VideoPrivacy } from '../../../../shared/models/videos' 15import { Video, VideoPrivacy } from '@shared/models'
32import { HttpStatusCode } from '@shared/core-utils'
33 16
34const expect = chai.expect 17const expect = chai.expect
35 18
36describe('Test a single server', function () { 19describe('Test a single server', function () {
37 20
38 function runSuite (mode: 'legacy' | 'resumable') { 21 function runSuite (mode: 'legacy' | 'resumable') {
39 let server: ServerInfo = null 22 let server: PeerTubeServer = null
40 let videoId = -1 23 let videoId: number | string
41 let videoId2 = -1 24 let videoId2: string
42 let videoUUID = '' 25 let videoUUID = ''
43 let videosListBase: any[] = null 26 let videosListBase: any[] = null
44 27
@@ -111,134 +94,123 @@ describe('Test a single server', function () {
111 before(async function () { 94 before(async function () {
112 this.timeout(30000) 95 this.timeout(30000)
113 96
114 server = await flushAndRunServer(1) 97 server = await createSingleServer(1)
115 98
116 await setAccessTokensToServers([ server ]) 99 await setAccessTokensToServers([ server ])
117 }) 100 })
118 101
119 it('Should list video categories', async function () { 102 it('Should list video categories', async function () {
120 const res = await getVideoCategories(server.url) 103 const categories = await server.videos.getCategories()
121
122 const categories = res.body
123 expect(Object.keys(categories)).to.have.length.above(10) 104 expect(Object.keys(categories)).to.have.length.above(10)
124 105
125 expect(categories[11]).to.equal('News & Politics') 106 expect(categories[11]).to.equal('News & Politics')
126 }) 107 })
127 108
128 it('Should list video licences', async function () { 109 it('Should list video licences', async function () {
129 const res = await getVideoLicences(server.url) 110 const licences = await server.videos.getLicences()
130
131 const licences = res.body
132 expect(Object.keys(licences)).to.have.length.above(5) 111 expect(Object.keys(licences)).to.have.length.above(5)
133 112
134 expect(licences[3]).to.equal('Attribution - No Derivatives') 113 expect(licences[3]).to.equal('Attribution - No Derivatives')
135 }) 114 })
136 115
137 it('Should list video languages', async function () { 116 it('Should list video languages', async function () {
138 const res = await getVideoLanguages(server.url) 117 const languages = await server.videos.getLanguages()
139
140 const languages = res.body
141 expect(Object.keys(languages)).to.have.length.above(5) 118 expect(Object.keys(languages)).to.have.length.above(5)
142 119
143 expect(languages['ru']).to.equal('Russian') 120 expect(languages['ru']).to.equal('Russian')
144 }) 121 })
145 122
146 it('Should list video privacies', async function () { 123 it('Should list video privacies', async function () {
147 const res = await getVideoPrivacies(server.url) 124 const privacies = await server.videos.getPrivacies()
148
149 const privacies = res.body
150 expect(Object.keys(privacies)).to.have.length.at.least(3) 125 expect(Object.keys(privacies)).to.have.length.at.least(3)
151 126
152 expect(privacies[3]).to.equal('Private') 127 expect(privacies[3]).to.equal('Private')
153 }) 128 })
154 129
155 it('Should not have videos', async function () { 130 it('Should not have videos', async function () {
156 const res = await getVideosList(server.url) 131 const { data, total } = await server.videos.list()
157 132
158 expect(res.body.total).to.equal(0) 133 expect(total).to.equal(0)
159 expect(res.body.data).to.be.an('array') 134 expect(data).to.be.an('array')
160 expect(res.body.data.length).to.equal(0) 135 expect(data.length).to.equal(0)
161 }) 136 })
162 137
163 it('Should upload the video', async function () { 138 it('Should upload the video', async function () {
164 this.timeout(10000) 139 this.timeout(10000)
165 140
166 const videoAttributes = { 141 const attributes = {
167 name: 'my super name', 142 name: 'my super name',
168 category: 2, 143 category: 2,
169 nsfw: true, 144 nsfw: true,
170 licence: 6, 145 licence: 6,
171 tags: [ 'tag1', 'tag2', 'tag3' ] 146 tags: [ 'tag1', 'tag2', 'tag3' ]
172 } 147 }
173 const res = await uploadVideo(server.url, server.accessToken, videoAttributes, HttpStatusCode.OK_200, mode) 148 const video = await server.videos.upload({ attributes, mode })
174 expect(res.body.video).to.not.be.undefined 149 expect(video).to.not.be.undefined
175 expect(res.body.video.id).to.equal(1) 150 expect(video.id).to.equal(1)
176 expect(res.body.video.uuid).to.have.length.above(5) 151 expect(video.uuid).to.have.length.above(5)
177 152
178 videoId = res.body.video.id 153 videoId = video.id
179 videoUUID = res.body.video.uuid 154 videoUUID = video.uuid
180 }) 155 })
181 156
182 it('Should get and seed the uploaded video', async function () { 157 it('Should get and seed the uploaded video', async function () {
183 this.timeout(5000) 158 this.timeout(5000)
184 159
185 const res = await getVideosList(server.url) 160 const { data, total } = await server.videos.list()
186 161
187 expect(res.body.total).to.equal(1) 162 expect(total).to.equal(1)
188 expect(res.body.data).to.be.an('array') 163 expect(data).to.be.an('array')
189 expect(res.body.data.length).to.equal(1) 164 expect(data.length).to.equal(1)
190 165
191 const video = res.body.data[0] 166 const video = data[0]
192 await completeVideoCheck(server.url, video, getCheckAttributes()) 167 await completeVideoCheck(server, video, getCheckAttributes())
193 }) 168 })
194 169
195 it('Should get the video by UUID', async function () { 170 it('Should get the video by UUID', async function () {
196 this.timeout(5000) 171 this.timeout(5000)
197 172
198 const res = await getVideo(server.url, videoUUID) 173 const video = await server.videos.get({ id: videoUUID })
199 174 await completeVideoCheck(server, video, getCheckAttributes())
200 const video = res.body
201 await completeVideoCheck(server.url, video, getCheckAttributes())
202 }) 175 })
203 176
204 it('Should have the views updated', async function () { 177 it('Should have the views updated', async function () {
205 this.timeout(20000) 178 this.timeout(20000)
206 179
207 await viewVideo(server.url, videoId) 180 await server.videos.view({ id: videoId })
208 await viewVideo(server.url, videoId) 181 await server.videos.view({ id: videoId })
209 await viewVideo(server.url, videoId) 182 await server.videos.view({ id: videoId })
210 183
211 await wait(1500) 184 await wait(1500)
212 185
213 await viewVideo(server.url, videoId) 186 await server.videos.view({ id: videoId })
214 await viewVideo(server.url, videoId) 187 await server.videos.view({ id: videoId })
215 188
216 await wait(1500) 189 await wait(1500)
217 190
218 await viewVideo(server.url, videoId) 191 await server.videos.view({ id: videoId })
219 await viewVideo(server.url, videoId) 192 await server.videos.view({ id: videoId })
220 193
221 // Wait the repeatable job 194 // Wait the repeatable job
222 await wait(8000) 195 await wait(8000)
223 196
224 const res = await getVideo(server.url, videoId) 197 const video = await server.videos.get({ id: videoId })
225
226 const video = res.body
227 expect(video.views).to.equal(3) 198 expect(video.views).to.equal(3)
228 }) 199 })
229 200
230 it('Should remove the video', async function () { 201 it('Should remove the video', async function () {
231 await removeVideo(server.url, server.accessToken, videoId) 202 const video = await server.videos.get({ id: videoId })
203 await server.videos.remove({ id: videoId })
232 204
233 await checkVideoFilesWereRemoved(videoUUID, 1) 205 await checkVideoFilesWereRemoved({ video, server })
234 }) 206 })
235 207
236 it('Should not have videos', async function () { 208 it('Should not have videos', async function () {
237 const res = await getVideosList(server.url) 209 const { total, data } = await server.videos.list()
238 210
239 expect(res.body.total).to.equal(0) 211 expect(total).to.equal(0)
240 expect(res.body.data).to.be.an('array') 212 expect(data).to.be.an('array')
241 expect(res.body.data).to.have.lengthOf(0) 213 expect(data).to.have.lengthOf(0)
242 }) 214 })
243 215
244 it('Should upload 6 videos', async function () { 216 it('Should upload 6 videos', async function () {
@@ -250,7 +222,7 @@ describe('Test a single server', function () {
250 ]) 222 ])
251 223
252 for (const video of videos) { 224 for (const video of videos) {
253 const videoAttributes = { 225 const attributes = {
254 name: video + ' name', 226 name: video + ' name',
255 description: video + ' description', 227 description: video + ' description',
256 category: 2, 228 category: 2,
@@ -261,19 +233,20 @@ describe('Test a single server', function () {
261 fixture: video 233 fixture: video
262 } 234 }
263 235
264 await uploadVideo(server.url, server.accessToken, videoAttributes, HttpStatusCode.OK_200, mode) 236 await server.videos.upload({ attributes, mode })
265 } 237 }
266 }) 238 })
267 239
268 it('Should have the correct durations', async function () { 240 it('Should have the correct durations', async function () {
269 const res = await getVideosList(server.url) 241 const { total, data } = await server.videos.list()
242
243 expect(total).to.equal(6)
244 expect(data).to.be.an('array')
245 expect(data).to.have.lengthOf(6)
270 246
271 expect(res.body.total).to.equal(6) 247 const videosByName: { [ name: string ]: Video } = {}
272 const videos = res.body.data 248 data.forEach(v => { videosByName[v.name] = v })
273 expect(videos).to.be.an('array')
274 expect(videos).to.have.lengthOf(6)
275 249
276 const videosByName = keyBy<{ duration: number }>(videos, 'name')
277 expect(videosByName['video_short.mp4 name'].duration).to.equal(5) 250 expect(videosByName['video_short.mp4 name'].duration).to.equal(5)
278 expect(videosByName['video_short.ogv name'].duration).to.equal(5) 251 expect(videosByName['video_short.ogv name'].duration).to.equal(5)
279 expect(videosByName['video_short.webm name'].duration).to.equal(5) 252 expect(videosByName['video_short.webm name'].duration).to.equal(5)
@@ -283,96 +256,87 @@ describe('Test a single server', function () {
283 }) 256 })
284 257
285 it('Should have the correct thumbnails', async function () { 258 it('Should have the correct thumbnails', async function () {
286 const res = await getVideosList(server.url) 259 const { data } = await server.videos.list()
287 260
288 const videos = res.body.data
289 // For the next test 261 // For the next test
290 videosListBase = videos 262 videosListBase = data
291 263
292 for (const video of videos) { 264 for (const video of data) {
293 const videoName = video.name.replace(' name', '') 265 const videoName = video.name.replace(' name', '')
294 await testImage(server.url, videoName, video.thumbnailPath) 266 await testImage(server.url, videoName, video.thumbnailPath)
295 } 267 }
296 }) 268 })
297 269
298 it('Should list only the two first videos', async function () { 270 it('Should list only the two first videos', async function () {
299 const res = await getVideosListPagination(server.url, 0, 2, 'name') 271 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: 'name' })
300 272
301 const videos = res.body.data 273 expect(total).to.equal(6)
302 expect(res.body.total).to.equal(6) 274 expect(data.length).to.equal(2)
303 expect(videos.length).to.equal(2) 275 expect(data[0].name).to.equal(videosListBase[0].name)
304 expect(videos[0].name).to.equal(videosListBase[0].name) 276 expect(data[1].name).to.equal(videosListBase[1].name)
305 expect(videos[1].name).to.equal(videosListBase[1].name)
306 }) 277 })
307 278
308 it('Should list only the next three videos', async function () { 279 it('Should list only the next three videos', async function () {
309 const res = await getVideosListPagination(server.url, 2, 3, 'name') 280 const { total, data } = await server.videos.list({ start: 2, count: 3, sort: 'name' })
310 281
311 const videos = res.body.data 282 expect(total).to.equal(6)
312 expect(res.body.total).to.equal(6) 283 expect(data.length).to.equal(3)
313 expect(videos.length).to.equal(3) 284 expect(data[0].name).to.equal(videosListBase[2].name)
314 expect(videos[0].name).to.equal(videosListBase[2].name) 285 expect(data[1].name).to.equal(videosListBase[3].name)
315 expect(videos[1].name).to.equal(videosListBase[3].name) 286 expect(data[2].name).to.equal(videosListBase[4].name)
316 expect(videos[2].name).to.equal(videosListBase[4].name)
317 }) 287 })
318 288
319 it('Should list the last video', async function () { 289 it('Should list the last video', async function () {
320 const res = await getVideosListPagination(server.url, 5, 6, 'name') 290 const { total, data } = await server.videos.list({ start: 5, count: 6, sort: 'name' })
321 291
322 const videos = res.body.data 292 expect(total).to.equal(6)
323 expect(res.body.total).to.equal(6) 293 expect(data.length).to.equal(1)
324 expect(videos.length).to.equal(1) 294 expect(data[0].name).to.equal(videosListBase[5].name)
325 expect(videos[0].name).to.equal(videosListBase[5].name)
326 }) 295 })
327 296
328 it('Should not have the total field', async function () { 297 it('Should not have the total field', async function () {
329 const res = await getVideosListPagination(server.url, 5, 6, 'name', true) 298 const { total, data } = await server.videos.list({ start: 5, count: 6, sort: 'name', skipCount: true })
330 299
331 const videos = res.body.data 300 expect(total).to.not.exist
332 expect(res.body.total).to.not.exist 301 expect(data.length).to.equal(1)
333 expect(videos.length).to.equal(1) 302 expect(data[0].name).to.equal(videosListBase[5].name)
334 expect(videos[0].name).to.equal(videosListBase[5].name)
335 }) 303 })
336 304
337 it('Should list and sort by name in descending order', async function () { 305 it('Should list and sort by name in descending order', async function () {
338 const res = await getVideosListSort(server.url, '-name') 306 const { total, data } = await server.videos.list({ sort: '-name' })
339 307
340 const videos = res.body.data 308 expect(total).to.equal(6)
341 expect(res.body.total).to.equal(6) 309 expect(data.length).to.equal(6)
342 expect(videos.length).to.equal(6) 310 expect(data[0].name).to.equal('video_short.webm name')
343 expect(videos[0].name).to.equal('video_short.webm name') 311 expect(data[1].name).to.equal('video_short.ogv name')
344 expect(videos[1].name).to.equal('video_short.ogv name') 312 expect(data[2].name).to.equal('video_short.mp4 name')
345 expect(videos[2].name).to.equal('video_short.mp4 name') 313 expect(data[3].name).to.equal('video_short3.webm name')
346 expect(videos[3].name).to.equal('video_short3.webm name') 314 expect(data[4].name).to.equal('video_short2.webm name')
347 expect(videos[4].name).to.equal('video_short2.webm name') 315 expect(data[5].name).to.equal('video_short1.webm name')
348 expect(videos[5].name).to.equal('video_short1.webm name')
349 316
350 videoId = videos[3].uuid 317 videoId = data[3].uuid
351 videoId2 = videos[5].uuid 318 videoId2 = data[5].uuid
352 }) 319 })
353 320
354 it('Should list and sort by trending in descending order', async function () { 321 it('Should list and sort by trending in descending order', async function () {
355 const res = await getVideosListPagination(server.url, 0, 2, '-trending') 322 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-trending' })
356 323
357 const videos = res.body.data 324 expect(total).to.equal(6)
358 expect(res.body.total).to.equal(6) 325 expect(data.length).to.equal(2)
359 expect(videos.length).to.equal(2)
360 }) 326 })
361 327
362 it('Should list and sort by hotness in descending order', async function () { 328 it('Should list and sort by hotness in descending order', async function () {
363 const res = await getVideosListPagination(server.url, 0, 2, '-hot') 329 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-hot' })
364 330
365 const videos = res.body.data 331 expect(total).to.equal(6)
366 expect(res.body.total).to.equal(6) 332 expect(data.length).to.equal(2)
367 expect(videos.length).to.equal(2)
368 }) 333 })
369 334
370 it('Should list and sort by best in descending order', async function () { 335 it('Should list and sort by best in descending order', async function () {
371 const res = await getVideosListPagination(server.url, 0, 2, '-best') 336 const { total, data } = await server.videos.list({ start: 0, count: 2, sort: '-best' })
372 337
373 const videos = res.body.data 338 expect(total).to.equal(6)
374 expect(res.body.total).to.equal(6) 339 expect(data.length).to.equal(2)
375 expect(videos.length).to.equal(2)
376 }) 340 })
377 341
378 it('Should update a video', async function () { 342 it('Should update a video', async function () {
@@ -387,67 +351,66 @@ describe('Test a single server', function () {
387 downloadEnabled: false, 351 downloadEnabled: false,
388 tags: [ 'tagup1', 'tagup2' ] 352 tags: [ 'tagup1', 'tagup2' ]
389 } 353 }
390 await updateVideo(server.url, server.accessToken, videoId, attributes) 354 await server.videos.update({ id: videoId, attributes })
391 }) 355 })
392 356
393 it('Should filter by tags and category', async function () { 357 it('Should filter by tags and category', async function () {
394 const res1 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 4 ] }) 358 {
395 expect(res1.body.total).to.equal(1) 359 const { data, total } = await server.videos.list({ tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 4 ] })
396 expect(res1.body.data[0].name).to.equal('my super video updated') 360 expect(total).to.equal(1)
361 expect(data[0].name).to.equal('my super video updated')
362 }
397 363
398 const res2 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] }) 364 {
399 expect(res2.body.total).to.equal(0) 365 const { total } = await server.videos.list({ tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] })
366 expect(total).to.equal(0)
367 }
400 }) 368 })
401 369
402 it('Should have the video updated', async function () { 370 it('Should have the video updated', async function () {
403 this.timeout(60000) 371 this.timeout(60000)
404 372
405 const res = await getVideo(server.url, videoId) 373 const video = await server.videos.get({ id: videoId })
406 const video = res.body
407 374
408 await completeVideoCheck(server.url, video, updateCheckAttributes()) 375 await completeVideoCheck(server, video, updateCheckAttributes())
409 }) 376 })
410 377
411 it('Should update only the tags of a video', async function () { 378 it('Should update only the tags of a video', async function () {
412 const attributes = { 379 const attributes = {
413 tags: [ 'supertag', 'tag1', 'tag2' ] 380 tags: [ 'supertag', 'tag1', 'tag2' ]
414 } 381 }
415 await updateVideo(server.url, server.accessToken, videoId, attributes) 382 await server.videos.update({ id: videoId, attributes })
416 383
417 const res = await getVideo(server.url, videoId) 384 const video = await server.videos.get({ id: videoId })
418 const video = res.body
419 385
420 await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes(), attributes)) 386 await completeVideoCheck(server, video, Object.assign(updateCheckAttributes(), attributes))
421 }) 387 })
422 388
423 it('Should update only the description of a video', async function () { 389 it('Should update only the description of a video', async function () {
424 const attributes = { 390 const attributes = {
425 description: 'hello everybody' 391 description: 'hello everybody'
426 } 392 }
427 await updateVideo(server.url, server.accessToken, videoId, attributes) 393 await server.videos.update({ id: videoId, attributes })
428 394
429 const res = await getVideo(server.url, videoId) 395 const video = await server.videos.get({ id: videoId })
430 const video = res.body
431 396
432 const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes) 397 const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes)
433 await completeVideoCheck(server.url, video, expectedAttributes) 398 await completeVideoCheck(server, video, expectedAttributes)
434 }) 399 })
435 400
436 it('Should like a video', async function () { 401 it('Should like a video', async function () {
437 await rateVideo(server.url, server.accessToken, videoId, 'like') 402 await server.videos.rate({ id: videoId, rating: 'like' })
438 403
439 const res = await getVideo(server.url, videoId) 404 const video = await server.videos.get({ id: videoId })
440 const video = res.body
441 405
442 expect(video.likes).to.equal(1) 406 expect(video.likes).to.equal(1)
443 expect(video.dislikes).to.equal(0) 407 expect(video.dislikes).to.equal(0)
444 }) 408 })
445 409
446 it('Should dislike the same video', async function () { 410 it('Should dislike the same video', async function () {
447 await rateVideo(server.url, server.accessToken, videoId, 'dislike') 411 await server.videos.rate({ id: videoId, rating: 'dislike' })
448 412
449 const res = await getVideo(server.url, videoId) 413 const video = await server.videos.get({ id: videoId })
450 const video = res.body
451 414
452 expect(video.likes).to.equal(0) 415 expect(video.likes).to.equal(0)
453 expect(video.dislikes).to.equal(1) 416 expect(video.dislikes).to.equal(1)
@@ -457,10 +420,10 @@ describe('Test a single server', function () {
457 { 420 {
458 const now = new Date() 421 const now = new Date()
459 const attributes = { originallyPublishedAt: now.toISOString() } 422 const attributes = { originallyPublishedAt: now.toISOString() }
460 await updateVideo(server.url, server.accessToken, videoId, attributes) 423 await server.videos.update({ id: videoId, attributes })
461 424
462 const res = await getVideosListSort(server.url, '-originallyPublishedAt') 425 const { data } = await server.videos.list({ sort: '-originallyPublishedAt' })
463 const names = res.body.data.map(v => v.name) 426 const names = data.map(v => v.name)
464 427
465 expect(names[0]).to.equal('my super video updated') 428 expect(names[0]).to.equal('my super video updated')
466 expect(names[1]).to.equal('video_short2.webm name') 429 expect(names[1]).to.equal('video_short2.webm name')
@@ -473,10 +436,10 @@ describe('Test a single server', function () {
473 { 436 {
474 const now = new Date() 437 const now = new Date()
475 const attributes = { originallyPublishedAt: now.toISOString() } 438 const attributes = { originallyPublishedAt: now.toISOString() }
476 await updateVideo(server.url, server.accessToken, videoId2, attributes) 439 await server.videos.update({ id: videoId2, attributes })
477 440
478 const res = await getVideosListSort(server.url, '-originallyPublishedAt') 441 const { data } = await server.videos.list({ sort: '-originallyPublishedAt' })
479 const names = res.body.data.map(v => v.name) 442 const names = data.map(v => v.name)
480 443
481 expect(names[0]).to.equal('video_short1.webm name') 444 expect(names[0]).to.equal('video_short1.webm name')
482 expect(names[1]).to.equal('my super video updated') 445 expect(names[1]).to.equal('my super video updated')
diff --git a/server/tests/api/videos/video-captions.ts b/server/tests/api/videos/video-captions.ts
index 14ecedfa6..3bb0d131c 100644
--- a/server/tests/api/videos/video-captions.ts
+++ b/server/tests/api/videos/video-captions.ts
@@ -1,72 +1,61 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 checkVideoFilesWereRemoved, 6 checkVideoFilesWereRemoved,
7 cleanupTests, 7 cleanupTests,
8 createMultipleServers,
8 doubleFollow, 9 doubleFollow,
9 flushAndRunMultipleServers, 10 PeerTubeServer,
10 removeVideo, 11 setAccessTokensToServers,
11 uploadVideo, 12 testCaptionFile,
12 wait 13 wait,
13} from '../../../../shared/extra-utils' 14 waitJobs
14import { ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index' 15} from '@shared/extra-utils'
15import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
16import {
17 createVideoCaption,
18 deleteVideoCaption,
19 listVideoCaptions,
20 testCaptionFile
21} from '../../../../shared/extra-utils/videos/video-captions'
22import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
23 16
24const expect = chai.expect 17const expect = chai.expect
25 18
26describe('Test video captions', function () { 19describe('Test video captions', function () {
27 const uuidRegex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' 20 const uuidRegex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
28 21
29 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
30 let videoUUID: string 23 let videoUUID: string
31 24
32 before(async function () { 25 before(async function () {
33 this.timeout(60000) 26 this.timeout(60000)
34 27
35 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
36 29
37 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
38 await doubleFollow(servers[0], servers[1]) 31 await doubleFollow(servers[0], servers[1])
39 32
40 await waitJobs(servers) 33 await waitJobs(servers)
41 34
42 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'my video name' }) 35 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'my video name' } })
43 videoUUID = res.body.video.uuid 36 videoUUID = uuid
44 37
45 await waitJobs(servers) 38 await waitJobs(servers)
46 }) 39 })
47 40
48 it('Should list the captions and return an empty list', async function () { 41 it('Should list the captions and return an empty list', async function () {
49 for (const server of servers) { 42 for (const server of servers) {
50 const res = await listVideoCaptions(server.url, videoUUID) 43 const body = await server.captions.list({ videoId: videoUUID })
51 expect(res.body.total).to.equal(0) 44 expect(body.total).to.equal(0)
52 expect(res.body.data).to.have.lengthOf(0) 45 expect(body.data).to.have.lengthOf(0)
53 } 46 }
54 }) 47 })
55 48
56 it('Should create two new captions', async function () { 49 it('Should create two new captions', async function () {
57 this.timeout(30000) 50 this.timeout(30000)
58 51
59 await createVideoCaption({ 52 await servers[0].captions.add({
60 url: servers[0].url,
61 accessToken: servers[0].accessToken,
62 language: 'ar', 53 language: 'ar',
63 videoId: videoUUID, 54 videoId: videoUUID,
64 fixture: 'subtitle-good1.vtt' 55 fixture: 'subtitle-good1.vtt'
65 }) 56 })
66 57
67 await createVideoCaption({ 58 await servers[0].captions.add({
68 url: servers[0].url,
69 accessToken: servers[0].accessToken,
70 language: 'zh', 59 language: 'zh',
71 videoId: videoUUID, 60 videoId: videoUUID,
72 fixture: 'subtitle-good2.vtt', 61 fixture: 'subtitle-good2.vtt',
@@ -78,17 +67,17 @@ describe('Test video captions', function () {
78 67
79 it('Should list these uploaded captions', async function () { 68 it('Should list these uploaded captions', async function () {
80 for (const server of servers) { 69 for (const server of servers) {
81 const res = await listVideoCaptions(server.url, videoUUID) 70 const body = await server.captions.list({ videoId: videoUUID })
82 expect(res.body.total).to.equal(2) 71 expect(body.total).to.equal(2)
83 expect(res.body.data).to.have.lengthOf(2) 72 expect(body.data).to.have.lengthOf(2)
84 73
85 const caption1: VideoCaption = res.body.data[0] 74 const caption1 = body.data[0]
86 expect(caption1.language.id).to.equal('ar') 75 expect(caption1.language.id).to.equal('ar')
87 expect(caption1.language.label).to.equal('Arabic') 76 expect(caption1.language.label).to.equal('Arabic')
88 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$')) 77 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$'))
89 await testCaptionFile(server.url, caption1.captionPath, 'Subtitle good 1.') 78 await testCaptionFile(server.url, caption1.captionPath, 'Subtitle good 1.')
90 79
91 const caption2: VideoCaption = res.body.data[1] 80 const caption2 = body.data[1]
92 expect(caption2.language.id).to.equal('zh') 81 expect(caption2.language.id).to.equal('zh')
93 expect(caption2.language.label).to.equal('Chinese') 82 expect(caption2.language.label).to.equal('Chinese')
94 expect(caption2.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-zh.vtt$')) 83 expect(caption2.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-zh.vtt$'))
@@ -99,9 +88,7 @@ describe('Test video captions', function () {
99 it('Should replace an existing caption', async function () { 88 it('Should replace an existing caption', async function () {
100 this.timeout(30000) 89 this.timeout(30000)
101 90
102 await createVideoCaption({ 91 await servers[0].captions.add({
103 url: servers[0].url,
104 accessToken: servers[0].accessToken,
105 language: 'ar', 92 language: 'ar',
106 videoId: videoUUID, 93 videoId: videoUUID,
107 fixture: 'subtitle-good2.vtt' 94 fixture: 'subtitle-good2.vtt'
@@ -112,11 +99,11 @@ describe('Test video captions', function () {
112 99
113 it('Should have this caption updated', async function () { 100 it('Should have this caption updated', async function () {
114 for (const server of servers) { 101 for (const server of servers) {
115 const res = await listVideoCaptions(server.url, videoUUID) 102 const body = await server.captions.list({ videoId: videoUUID })
116 expect(res.body.total).to.equal(2) 103 expect(body.total).to.equal(2)
117 expect(res.body.data).to.have.lengthOf(2) 104 expect(body.data).to.have.lengthOf(2)
118 105
119 const caption1: VideoCaption = res.body.data[0] 106 const caption1 = body.data[0]
120 expect(caption1.language.id).to.equal('ar') 107 expect(caption1.language.id).to.equal('ar')
121 expect(caption1.language.label).to.equal('Arabic') 108 expect(caption1.language.label).to.equal('Arabic')
122 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$')) 109 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$'))
@@ -127,9 +114,7 @@ describe('Test video captions', function () {
127 it('Should replace an existing caption with a srt file and convert it', async function () { 114 it('Should replace an existing caption with a srt file and convert it', async function () {
128 this.timeout(30000) 115 this.timeout(30000)
129 116
130 await createVideoCaption({ 117 await servers[0].captions.add({
131 url: servers[0].url,
132 accessToken: servers[0].accessToken,
133 language: 'ar', 118 language: 'ar',
134 videoId: videoUUID, 119 videoId: videoUUID,
135 fixture: 'subtitle-good.srt' 120 fixture: 'subtitle-good.srt'
@@ -143,11 +128,11 @@ describe('Test video captions', function () {
143 128
144 it('Should have this caption updated and converted', async function () { 129 it('Should have this caption updated and converted', async function () {
145 for (const server of servers) { 130 for (const server of servers) {
146 const res = await listVideoCaptions(server.url, videoUUID) 131 const body = await server.captions.list({ videoId: videoUUID })
147 expect(res.body.total).to.equal(2) 132 expect(body.total).to.equal(2)
148 expect(res.body.data).to.have.lengthOf(2) 133 expect(body.data).to.have.lengthOf(2)
149 134
150 const caption1: VideoCaption = res.body.data[0] 135 const caption1 = body.data[0]
151 expect(caption1.language.id).to.equal('ar') 136 expect(caption1.language.id).to.equal('ar')
152 expect(caption1.language.label).to.equal('Arabic') 137 expect(caption1.language.label).to.equal('Arabic')
153 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$')) 138 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/' + uuidRegex + '-ar.vtt$'))
@@ -172,18 +157,18 @@ describe('Test video captions', function () {
172 it('Should remove one caption', async function () { 157 it('Should remove one caption', async function () {
173 this.timeout(30000) 158 this.timeout(30000)
174 159
175 await deleteVideoCaption(servers[0].url, servers[0].accessToken, videoUUID, 'ar') 160 await servers[0].captions.delete({ videoId: videoUUID, language: 'ar' })
176 161
177 await waitJobs(servers) 162 await waitJobs(servers)
178 }) 163 })
179 164
180 it('Should only list the caption that was not deleted', async function () { 165 it('Should only list the caption that was not deleted', async function () {
181 for (const server of servers) { 166 for (const server of servers) {
182 const res = await listVideoCaptions(server.url, videoUUID) 167 const body = await server.captions.list({ videoId: videoUUID })
183 expect(res.body.total).to.equal(1) 168 expect(body.total).to.equal(1)
184 expect(res.body.data).to.have.lengthOf(1) 169 expect(body.data).to.have.lengthOf(1)
185 170
186 const caption: VideoCaption = res.body.data[0] 171 const caption = body.data[0]
187 172
188 expect(caption.language.id).to.equal('zh') 173 expect(caption.language.id).to.equal('zh')
189 expect(caption.language.label).to.equal('Chinese') 174 expect(caption.language.label).to.equal('Chinese')
@@ -193,9 +178,12 @@ describe('Test video captions', function () {
193 }) 178 })
194 179
195 it('Should remove the video, and thus all video captions', async function () { 180 it('Should remove the video, and thus all video captions', async function () {
196 await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) 181 const video = await servers[0].videos.get({ id: videoUUID })
182 const { data: captions } = await servers[0].captions.list({ videoId: videoUUID })
183
184 await servers[0].videos.remove({ id: videoUUID })
197 185
198 await checkVideoFilesWereRemoved(videoUUID, 1) 186 await checkVideoFilesWereRemoved({ server: servers[0], video, captions })
199 }) 187 })
200 188
201 after(async function () { 189 after(async function () {
diff --git a/server/tests/api/videos/video-change-ownership.ts b/server/tests/api/videos/video-change-ownership.ts
index a3384851b..d6665fe4e 100644
--- a/server/tests/api/videos/video-change-ownership.ts
+++ b/server/tests/api/videos/video-change-ownership.ts
@@ -2,234 +2,212 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 5import {
7 acceptChangeOwnership, 6 ChangeOwnershipCommand,
8 changeVideoOwnership,
9 cleanupTests, 7 cleanupTests,
10 createLive, 8 createMultipleServers,
11 createUser, 9 createSingleServer,
12 doubleFollow, 10 doubleFollow,
13 flushAndRunMultipleServers, 11 PeerTubeServer,
14 flushAndRunServer,
15 getMyUserInformation,
16 getVideo,
17 getVideoChangeOwnershipList,
18 getVideosList,
19 refuseChangeOwnership,
20 ServerInfo,
21 setAccessTokensToServers, 12 setAccessTokensToServers,
22 setDefaultVideoChannel, 13 setDefaultVideoChannel,
23 updateCustomSubConfig, 14 waitJobs
24 uploadVideo, 15} from '@shared/extra-utils'
25 userLogin 16import { HttpStatusCode, VideoPrivacy } from '@shared/models'
26} from '../../../../shared/extra-utils'
27import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
28import { User } from '../../../../shared/models/users'
29import { VideoDetails, VideoPrivacy } from '../../../../shared/models/videos'
30 17
31const expect = chai.expect 18const expect = chai.expect
32 19
33describe('Test video change ownership - nominal', function () { 20describe('Test video change ownership - nominal', function () {
34 let servers: ServerInfo[] = [] 21 let servers: PeerTubeServer[] = []
35 const firstUser = { 22
36 username: 'first', 23 const firstUser = 'first'
37 password: 'My great password' 24 const secondUser = 'second'
38 } 25
39 const secondUser = { 26 let firstUserToken = ''
40 username: 'second',
41 password: 'My other password'
42 }
43
44 let firstUserAccessToken = ''
45 let firstUserChannelId: number 27 let firstUserChannelId: number
46 28
47 let secondUserAccessToken = '' 29 let secondUserToken = ''
48 let secondUserChannelId: number 30 let secondUserChannelId: number
49 31
50 let lastRequestChangeOwnershipId = '' 32 let lastRequestId: number
51 33
52 let liveId: number 34 let liveId: number
53 35
36 let command: ChangeOwnershipCommand
37
54 before(async function () { 38 before(async function () {
55 this.timeout(50000) 39 this.timeout(50000)
56 40
57 servers = await flushAndRunMultipleServers(2) 41 servers = await createMultipleServers(2)
58 await setAccessTokensToServers(servers) 42 await setAccessTokensToServers(servers)
59 await setDefaultVideoChannel(servers) 43 await setDefaultVideoChannel(servers)
60 44
61 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 45 await servers[0].config.updateCustomSubConfig({
62 transcoding: { 46 newConfig: {
63 enabled: false 47 transcoding: {
64 }, 48 enabled: false
65 live: { 49 },
66 enabled: true 50 live: {
51 enabled: true
52 }
67 } 53 }
68 }) 54 })
69 55
70 const videoQuota = 42000000 56 firstUserToken = await servers[0].users.generateUserAndToken(firstUser)
71 await createUser({ 57 secondUserToken = await servers[0].users.generateUserAndToken(secondUser)
72 url: servers[0].url,
73 accessToken: servers[0].accessToken,
74 username: firstUser.username,
75 password: firstUser.password,
76 videoQuota: videoQuota
77 })
78 await createUser({
79 url: servers[0].url,
80 accessToken: servers[0].accessToken,
81 username: secondUser.username,
82 password: secondUser.password,
83 videoQuota: videoQuota
84 })
85
86 firstUserAccessToken = await userLogin(servers[0], firstUser)
87 secondUserAccessToken = await userLogin(servers[0], secondUser)
88 58
89 { 59 {
90 const res = await getMyUserInformation(servers[0].url, firstUserAccessToken) 60 const { videoChannels } = await servers[0].users.getMyInfo({ token: firstUserToken })
91 const firstUserInformation: User = res.body 61 firstUserChannelId = videoChannels[0].id
92 firstUserChannelId = firstUserInformation.videoChannels[0].id
93 } 62 }
94 63
95 { 64 {
96 const res = await getMyUserInformation(servers[0].url, secondUserAccessToken) 65 const { videoChannels } = await servers[0].users.getMyInfo({ token: secondUserToken })
97 const secondUserInformation: User = res.body 66 secondUserChannelId = videoChannels[0].id
98 secondUserChannelId = secondUserInformation.videoChannels[0].id
99 } 67 }
100 68
101 { 69 {
102 const videoAttributes = { 70 const attributes = {
103 name: 'my super name', 71 name: 'my super name',
104 description: 'my super description' 72 description: 'my super description'
105 } 73 }
106 const res = await uploadVideo(servers[0].url, firstUserAccessToken, videoAttributes) 74 const { id } = await servers[0].videos.upload({ token: firstUserToken, attributes })
107 75
108 const resVideo = await getVideo(servers[0].url, res.body.video.id) 76 servers[0].store.videoCreated = await servers[0].videos.get({ id })
109 servers[0].video = resVideo.body
110 } 77 }
111 78
112 { 79 {
113 const attributes = { name: 'live', channelId: firstUserChannelId, privacy: VideoPrivacy.PUBLIC } 80 const attributes = { name: 'live', channelId: firstUserChannelId, privacy: VideoPrivacy.PUBLIC }
114 const res = await createLive(servers[0].url, firstUserAccessToken, attributes) 81 const video = await servers[0].live.create({ token: firstUserToken, fields: attributes })
115 82
116 liveId = res.body.video.id 83 liveId = video.id
117 } 84 }
118 85
86 command = servers[0].changeOwnership
87
119 await doubleFollow(servers[0], servers[1]) 88 await doubleFollow(servers[0], servers[1])
120 }) 89 })
121 90
122 it('Should not have video change ownership', async function () { 91 it('Should not have video change ownership', async function () {
123 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken) 92 {
93 const body = await command.list({ token: firstUserToken })
124 94
125 expect(resFirstUser.body.total).to.equal(0) 95 expect(body.total).to.equal(0)
126 expect(resFirstUser.body.data).to.be.an('array') 96 expect(body.data).to.be.an('array')
127 expect(resFirstUser.body.data.length).to.equal(0) 97 expect(body.data.length).to.equal(0)
98 }
128 99
129 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 100 {
101 const body = await command.list({ token: secondUserToken })
130 102
131 expect(resSecondUser.body.total).to.equal(0) 103 expect(body.total).to.equal(0)
132 expect(resSecondUser.body.data).to.be.an('array') 104 expect(body.data).to.be.an('array')
133 expect(resSecondUser.body.data.length).to.equal(0) 105 expect(body.data.length).to.equal(0)
106 }
134 }) 107 })
135 108
136 it('Should send a request to change ownership of a video', async function () { 109 it('Should send a request to change ownership of a video', async function () {
137 this.timeout(15000) 110 this.timeout(15000)
138 111
139 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username) 112 await command.create({ token: firstUserToken, videoId: servers[0].store.videoCreated.id, username: secondUser })
140 }) 113 })
141 114
142 it('Should only return a request to change ownership for the second user', async function () { 115 it('Should only return a request to change ownership for the second user', async function () {
143 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken) 116 {
117 const body = await command.list({ token: firstUserToken })
144 118
145 expect(resFirstUser.body.total).to.equal(0) 119 expect(body.total).to.equal(0)
146 expect(resFirstUser.body.data).to.be.an('array') 120 expect(body.data).to.be.an('array')
147 expect(resFirstUser.body.data.length).to.equal(0) 121 expect(body.data.length).to.equal(0)
122 }
148 123
149 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 124 {
125 const body = await command.list({ token: secondUserToken })
150 126
151 expect(resSecondUser.body.total).to.equal(1) 127 expect(body.total).to.equal(1)
152 expect(resSecondUser.body.data).to.be.an('array') 128 expect(body.data).to.be.an('array')
153 expect(resSecondUser.body.data.length).to.equal(1) 129 expect(body.data.length).to.equal(1)
154 130
155 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 131 lastRequestId = body.data[0].id
132 }
156 }) 133 })
157 134
158 it('Should accept the same change ownership request without crashing', async function () { 135 it('Should accept the same change ownership request without crashing', async function () {
159 this.timeout(10000) 136 this.timeout(10000)
160 137
161 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username) 138 await command.create({ token: firstUserToken, videoId: servers[0].store.videoCreated.id, username: secondUser })
162 }) 139 })
163 140
164 it('Should not create multiple change ownership requests while one is waiting', async function () { 141 it('Should not create multiple change ownership requests while one is waiting', async function () {
165 this.timeout(10000) 142 this.timeout(10000)
166 143
167 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 144 const body = await command.list({ token: secondUserToken })
168 145
169 expect(resSecondUser.body.total).to.equal(1) 146 expect(body.total).to.equal(1)
170 expect(resSecondUser.body.data).to.be.an('array') 147 expect(body.data).to.be.an('array')
171 expect(resSecondUser.body.data.length).to.equal(1) 148 expect(body.data.length).to.equal(1)
172 }) 149 })
173 150
174 it('Should not be possible to refuse the change of ownership from first user', async function () { 151 it('Should not be possible to refuse the change of ownership from first user', async function () {
175 this.timeout(10000) 152 this.timeout(10000)
176 153
177 await refuseChangeOwnership(servers[0].url, firstUserAccessToken, lastRequestChangeOwnershipId, HttpStatusCode.FORBIDDEN_403) 154 await command.refuse({ token: firstUserToken, ownershipId: lastRequestId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
178 }) 155 })
179 156
180 it('Should be possible to refuse the change of ownership from second user', async function () { 157 it('Should be possible to refuse the change of ownership from second user', async function () {
181 this.timeout(10000) 158 this.timeout(10000)
182 159
183 await refuseChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId) 160 await command.refuse({ token: secondUserToken, ownershipId: lastRequestId })
184 }) 161 })
185 162
186 it('Should send a new request to change ownership of a video', async function () { 163 it('Should send a new request to change ownership of a video', async function () {
187 this.timeout(15000) 164 this.timeout(15000)
188 165
189 await changeVideoOwnership(servers[0].url, firstUserAccessToken, servers[0].video.id, secondUser.username) 166 await command.create({ token: firstUserToken, videoId: servers[0].store.videoCreated.id, username: secondUser })
190 }) 167 })
191 168
192 it('Should return two requests to change ownership for the second user', async function () { 169 it('Should return two requests to change ownership for the second user', async function () {
193 const resFirstUser = await getVideoChangeOwnershipList(servers[0].url, firstUserAccessToken) 170 {
171 const body = await command.list({ token: firstUserToken })
194 172
195 expect(resFirstUser.body.total).to.equal(0) 173 expect(body.total).to.equal(0)
196 expect(resFirstUser.body.data).to.be.an('array') 174 expect(body.data).to.be.an('array')
197 expect(resFirstUser.body.data.length).to.equal(0) 175 expect(body.data.length).to.equal(0)
176 }
198 177
199 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 178 {
179 const body = await command.list({ token: secondUserToken })
200 180
201 expect(resSecondUser.body.total).to.equal(2) 181 expect(body.total).to.equal(2)
202 expect(resSecondUser.body.data).to.be.an('array') 182 expect(body.data).to.be.an('array')
203 expect(resSecondUser.body.data.length).to.equal(2) 183 expect(body.data.length).to.equal(2)
204 184
205 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 185 lastRequestId = body.data[0].id
186 }
206 }) 187 })
207 188
208 it('Should not be possible to accept the change of ownership from first user', async function () { 189 it('Should not be possible to accept the change of ownership from first user', async function () {
209 this.timeout(10000) 190 this.timeout(10000)
210 191
211 await acceptChangeOwnership( 192 await command.accept({
212 servers[0].url, 193 token: firstUserToken,
213 firstUserAccessToken, 194 ownershipId: lastRequestId,
214 lastRequestChangeOwnershipId, 195 channelId: secondUserChannelId,
215 secondUserChannelId, 196 expectedStatus: HttpStatusCode.FORBIDDEN_403
216 HttpStatusCode.FORBIDDEN_403 197 })
217 )
218 }) 198 })
219 199
220 it('Should be possible to accept the change of ownership from second user', async function () { 200 it('Should be possible to accept the change of ownership from second user', async function () {
221 this.timeout(10000) 201 this.timeout(10000)
222 202
223 await acceptChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId, secondUserChannelId) 203 await command.accept({ token: secondUserToken, ownershipId: lastRequestId, channelId: secondUserChannelId })
224 204
225 await waitJobs(servers) 205 await waitJobs(servers)
226 }) 206 })
227 207
228 it('Should have the channel of the video updated', async function () { 208 it('Should have the channel of the video updated', async function () {
229 for (const server of servers) { 209 for (const server of servers) {
230 const res = await getVideo(server.url, servers[0].video.uuid) 210 const video = await server.videos.get({ id: servers[0].store.videoCreated.uuid })
231
232 const video: VideoDetails = res.body
233 211
234 expect(video.name).to.equal('my super name') 212 expect(video.name).to.equal('my super name')
235 expect(video.channel.displayName).to.equal('Main second channel') 213 expect(video.channel.displayName).to.equal('Main second channel')
@@ -240,27 +218,25 @@ describe('Test video change ownership - nominal', function () {
240 it('Should send a request to change ownership of a live', async function () { 218 it('Should send a request to change ownership of a live', async function () {
241 this.timeout(15000) 219 this.timeout(15000)
242 220
243 await changeVideoOwnership(servers[0].url, firstUserAccessToken, liveId, secondUser.username) 221 await command.create({ token: firstUserToken, videoId: liveId, username: secondUser })
244 222
245 const resSecondUser = await getVideoChangeOwnershipList(servers[0].url, secondUserAccessToken) 223 const body = await command.list({ token: secondUserToken })
246 224
247 expect(resSecondUser.body.total).to.equal(3) 225 expect(body.total).to.equal(3)
248 expect(resSecondUser.body.data.length).to.equal(3) 226 expect(body.data.length).to.equal(3)
249 227
250 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 228 lastRequestId = body.data[0].id
251 }) 229 })
252 230
253 it('Should accept a live ownership change', async function () { 231 it('Should accept a live ownership change', async function () {
254 this.timeout(20000) 232 this.timeout(20000)
255 233
256 await acceptChangeOwnership(servers[0].url, secondUserAccessToken, lastRequestChangeOwnershipId, secondUserChannelId) 234 await command.accept({ token: secondUserToken, ownershipId: lastRequestId, channelId: secondUserChannelId })
257 235
258 await waitJobs(servers) 236 await waitJobs(servers)
259 237
260 for (const server of servers) { 238 for (const server of servers) {
261 const res = await getVideo(server.url, servers[0].video.uuid) 239 const video = await server.videos.get({ id: servers[0].store.videoCreated.uuid })
262
263 const video: VideoDetails = res.body
264 240
265 expect(video.name).to.equal('my super name') 241 expect(video.name).to.equal('my super name')
266 expect(video.channel.displayName).to.equal('Main second channel') 242 expect(video.channel.displayName).to.equal('Main second channel')
@@ -274,99 +250,79 @@ describe('Test video change ownership - nominal', function () {
274}) 250})
275 251
276describe('Test video change ownership - quota too small', function () { 252describe('Test video change ownership - quota too small', function () {
277 let server: ServerInfo 253 let server: PeerTubeServer
278 const firstUser = { 254 const firstUser = 'first'
279 username: 'first', 255 const secondUser = 'second'
280 password: 'My great password' 256
281 } 257 let firstUserToken = ''
282 const secondUser = { 258 let secondUserToken = ''
283 username: 'second', 259 let lastRequestId: number
284 password: 'My other password'
285 }
286 let firstUserAccessToken = ''
287 let secondUserAccessToken = ''
288 let lastRequestChangeOwnershipId = ''
289 260
290 before(async function () { 261 before(async function () {
291 this.timeout(50000) 262 this.timeout(50000)
292 263
293 // Run one server 264 // Run one server
294 server = await flushAndRunServer(1) 265 server = await createSingleServer(1)
295 await setAccessTokensToServers([ server ]) 266 await setAccessTokensToServers([ server ])
296 267
297 const videoQuota = 42000000 268 await server.users.create({ username: secondUser, videoQuota: 10 })
298 const limitedVideoQuota = 10
299 await createUser({
300 url: server.url,
301 accessToken: server.accessToken,
302 username: firstUser.username,
303 password: firstUser.password,
304 videoQuota: videoQuota
305 })
306 await createUser({
307 url: server.url,
308 accessToken: server.accessToken,
309 username: secondUser.username,
310 password: secondUser.password,
311 videoQuota: limitedVideoQuota
312 })
313 269
314 firstUserAccessToken = await userLogin(server, firstUser) 270 firstUserToken = await server.users.generateUserAndToken(firstUser)
315 secondUserAccessToken = await userLogin(server, secondUser) 271 secondUserToken = await server.login.getAccessToken(secondUser)
316 272
317 // Upload some videos on the server 273 // Upload some videos on the server
318 const video1Attributes = { 274 const attributes = {
319 name: 'my super name', 275 name: 'my super name',
320 description: 'my super description' 276 description: 'my super description'
321 } 277 }
322 await uploadVideo(server.url, firstUserAccessToken, video1Attributes) 278 await server.videos.upload({ token: firstUserToken, attributes })
323 279
324 await waitJobs(server) 280 await waitJobs(server)
325 281
326 const res = await getVideosList(server.url) 282 const { data } = await server.videos.list()
327 const videos = res.body.data 283 expect(data.length).to.equal(1)
328
329 expect(videos.length).to.equal(1)
330 284
331 server.video = videos.find(video => video.name === 'my super name') 285 server.store.videoCreated = data.find(video => video.name === 'my super name')
332 }) 286 })
333 287
334 it('Should send a request to change ownership of a video', async function () { 288 it('Should send a request to change ownership of a video', async function () {
335 this.timeout(15000) 289 this.timeout(15000)
336 290
337 await changeVideoOwnership(server.url, firstUserAccessToken, server.video.id, secondUser.username) 291 await server.changeOwnership.create({ token: firstUserToken, videoId: server.store.videoCreated.id, username: secondUser })
338 }) 292 })
339 293
340 it('Should only return a request to change ownership for the second user', async function () { 294 it('Should only return a request to change ownership for the second user', async function () {
341 const resFirstUser = await getVideoChangeOwnershipList(server.url, firstUserAccessToken) 295 {
296 const body = await server.changeOwnership.list({ token: firstUserToken })
342 297
343 expect(resFirstUser.body.total).to.equal(0) 298 expect(body.total).to.equal(0)
344 expect(resFirstUser.body.data).to.be.an('array') 299 expect(body.data).to.be.an('array')
345 expect(resFirstUser.body.data.length).to.equal(0) 300 expect(body.data.length).to.equal(0)
301 }
346 302
347 const resSecondUser = await getVideoChangeOwnershipList(server.url, secondUserAccessToken) 303 {
304 const body = await server.changeOwnership.list({ token: secondUserToken })
348 305
349 expect(resSecondUser.body.total).to.equal(1) 306 expect(body.total).to.equal(1)
350 expect(resSecondUser.body.data).to.be.an('array') 307 expect(body.data).to.be.an('array')
351 expect(resSecondUser.body.data.length).to.equal(1) 308 expect(body.data.length).to.equal(1)
352 309
353 lastRequestChangeOwnershipId = resSecondUser.body.data[0].id 310 lastRequestId = body.data[0].id
311 }
354 }) 312 })
355 313
356 it('Should not be possible to accept the change of ownership from second user because of exceeded quota', async function () { 314 it('Should not be possible to accept the change of ownership from second user because of exceeded quota', async function () {
357 this.timeout(10000) 315 this.timeout(10000)
358 316
359 const secondUserInformationResponse = await getMyUserInformation(server.url, secondUserAccessToken) 317 const { videoChannels } = await server.users.getMyInfo({ token: secondUserToken })
360 const secondUserInformation: User = secondUserInformationResponse.body 318 const channelId = videoChannels[0].id
361 const channelId = secondUserInformation.videoChannels[0].id
362 319
363 await acceptChangeOwnership( 320 await server.changeOwnership.accept({
364 server.url, 321 token: secondUserToken,
365 secondUserAccessToken, 322 ownershipId: lastRequestId,
366 lastRequestChangeOwnershipId,
367 channelId, 323 channelId,
368 HttpStatusCode.PAYLOAD_TOO_LARGE_413 324 expectedStatus: HttpStatusCode.PAYLOAD_TOO_LARGE_413
369 ) 325 })
370 }) 326 })
371 327
372 after(async function () { 328 after(async function () {
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index 865098777..c25754eb6 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -6,48 +6,28 @@ import { basename } from 'path'
6import { ACTOR_IMAGES_SIZE } from '@server/initializers/constants' 6import { ACTOR_IMAGES_SIZE } from '@server/initializers/constants'
7import { 7import {
8 cleanupTests, 8 cleanupTests,
9 createUser, 9 createMultipleServers,
10 deleteVideoChannelImage,
11 doubleFollow, 10 doubleFollow,
12 flushAndRunMultipleServers, 11 PeerTubeServer,
13 getActorImage, 12 setAccessTokensToServers,
14 getVideo,
15 getVideoChannel,
16 getVideoChannelVideos,
17 setDefaultVideoChannel, 13 setDefaultVideoChannel,
18 testFileExistsOrNot, 14 testFileExistsOrNot,
19 testImage, 15 testImage,
20 updateVideo, 16 wait,
21 updateVideoChannelImage, 17 waitJobs
22 uploadVideo, 18} from '@shared/extra-utils'
23 userLogin, 19import { User, VideoChannel } from '@shared/models'
24 wait
25} from '../../../../shared/extra-utils'
26import {
27 addVideoChannel,
28 deleteVideoChannel,
29 getAccountVideoChannelsList,
30 getMyUserInformation,
31 getVideoChannelsList,
32 ServerInfo,
33 setAccessTokensToServers,
34 updateVideoChannel,
35 viewVideo
36} from '../../../../shared/extra-utils/index'
37import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
38import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index'
39 20
40const expect = chai.expect 21const expect = chai.expect
41 22
42async function findChannel (server: ServerInfo, channelId: number) { 23async function findChannel (server: PeerTubeServer, channelId: number) {
43 const res = await getVideoChannelsList(server.url, 0, 5, '-name') 24 const body = await server.channels.list({ sort: '-name' })
44 const videoChannel = res.body.data.find(c => c.id === channelId)
45 25
46 return videoChannel as VideoChannel 26 return body.data.find(c => c.id === channelId)
47} 27}
48 28
49describe('Test video channels', function () { 29describe('Test video channels', function () {
50 let servers: ServerInfo[] 30 let servers: PeerTubeServer[]
51 let userInfo: User 31 let userInfo: User
52 let secondVideoChannelId: number 32 let secondVideoChannelId: number
53 let totoChannel: number 33 let totoChannel: number
@@ -60,7 +40,7 @@ describe('Test video channels', function () {
60 before(async function () { 40 before(async function () {
61 this.timeout(60000) 41 this.timeout(60000)
62 42
63 servers = await flushAndRunMultipleServers(2) 43 servers = await createMultipleServers(2)
64 44
65 await setAccessTokensToServers(servers) 45 await setAccessTokensToServers(servers)
66 await setDefaultVideoChannel(servers) 46 await setDefaultVideoChannel(servers)
@@ -69,11 +49,11 @@ describe('Test video channels', function () {
69 }) 49 })
70 50
71 it('Should have one video channel (created with root)', async () => { 51 it('Should have one video channel (created with root)', async () => {
72 const res = await getVideoChannelsList(servers[0].url, 0, 2) 52 const body = await servers[0].channels.list({ start: 0, count: 2 })
73 53
74 expect(res.body.total).to.equal(1) 54 expect(body.total).to.equal(1)
75 expect(res.body.data).to.be.an('array') 55 expect(body.data).to.be.an('array')
76 expect(res.body.data).to.have.lengthOf(1) 56 expect(body.data).to.have.lengthOf(1)
77 }) 57 })
78 58
79 it('Should create another video channel', async function () { 59 it('Should create another video channel', async function () {
@@ -86,23 +66,22 @@ describe('Test video channels', function () {
86 description: 'super video channel description', 66 description: 'super video channel description',
87 support: 'super video channel support text' 67 support: 'super video channel support text'
88 } 68 }
89 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 69 const created = await servers[0].channels.create({ attributes: videoChannel })
90 secondVideoChannelId = res.body.videoChannel.id 70 secondVideoChannelId = created.id
91 } 71 }
92 72
93 // The channel is 1 is propagated to servers 2 73 // The channel is 1 is propagated to servers 2
94 { 74 {
95 const videoAttributesArg = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' } 75 const attributes = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' }
96 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributesArg) 76 const { uuid } = await servers[0].videos.upload({ attributes })
97 videoUUID = res.body.video.uuid 77 videoUUID = uuid
98 } 78 }
99 79
100 await waitJobs(servers) 80 await waitJobs(servers)
101 }) 81 })
102 82
103 it('Should have two video channels when getting my information', async () => { 83 it('Should have two video channels when getting my information', async () => {
104 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 84 userInfo = await servers[0].users.getMyInfo()
105 userInfo = res.body
106 85
107 expect(userInfo.videoChannels).to.be.an('array') 86 expect(userInfo.videoChannels).to.be.an('array')
108 expect(userInfo.videoChannels).to.have.lengthOf(2) 87 expect(userInfo.videoChannels).to.have.lengthOf(2)
@@ -120,16 +99,14 @@ describe('Test video channels', function () {
120 }) 99 })
121 100
122 it('Should have two video channels when getting account channels on server 1', async function () { 101 it('Should have two video channels when getting account channels on server 1', async function () {
123 const res = await getAccountVideoChannelsList({ 102 const body = await servers[0].channels.listByAccount({ accountName })
124 url: servers[0].url, 103 expect(body.total).to.equal(2)
125 accountName 104
126 }) 105 const videoChannels = body.data
127 106
128 expect(res.body.total).to.equal(2) 107 expect(videoChannels).to.be.an('array')
129 expect(res.body.data).to.be.an('array') 108 expect(videoChannels).to.have.lengthOf(2)
130 expect(res.body.data).to.have.lengthOf(2)
131 109
132 const videoChannels = res.body.data
133 expect(videoChannels[0].name).to.equal('root_channel') 110 expect(videoChannels[0].name).to.equal('root_channel')
134 expect(videoChannels[0].displayName).to.equal('Main root channel') 111 expect(videoChannels[0].displayName).to.equal('Main root channel')
135 112
@@ -141,79 +118,69 @@ describe('Test video channels', function () {
141 118
142 it('Should paginate and sort account channels', async function () { 119 it('Should paginate and sort account channels', async function () {
143 { 120 {
144 const res = await getAccountVideoChannelsList({ 121 const body = await servers[0].channels.listByAccount({
145 url: servers[0].url,
146 accountName, 122 accountName,
147 start: 0, 123 start: 0,
148 count: 1, 124 count: 1,
149 sort: 'createdAt' 125 sort: 'createdAt'
150 }) 126 })
151 127
152 expect(res.body.total).to.equal(2) 128 expect(body.total).to.equal(2)
153 expect(res.body.data).to.have.lengthOf(1) 129 expect(body.data).to.have.lengthOf(1)
154 130
155 const videoChannel: VideoChannel = res.body.data[0] 131 const videoChannel: VideoChannel = body.data[0]
156 expect(videoChannel.name).to.equal('root_channel') 132 expect(videoChannel.name).to.equal('root_channel')
157 } 133 }
158 134
159 { 135 {
160 const res = await getAccountVideoChannelsList({ 136 const body = await servers[0].channels.listByAccount({
161 url: servers[0].url,
162 accountName, 137 accountName,
163 start: 0, 138 start: 0,
164 count: 1, 139 count: 1,
165 sort: '-createdAt' 140 sort: '-createdAt'
166 }) 141 })
167 142
168 expect(res.body.total).to.equal(2) 143 expect(body.total).to.equal(2)
169 expect(res.body.data).to.have.lengthOf(1) 144 expect(body.data).to.have.lengthOf(1)
170 145 expect(body.data[0].name).to.equal('second_video_channel')
171 const videoChannel: VideoChannel = res.body.data[0]
172 expect(videoChannel.name).to.equal('second_video_channel')
173 } 146 }
174 147
175 { 148 {
176 const res = await getAccountVideoChannelsList({ 149 const body = await servers[0].channels.listByAccount({
177 url: servers[0].url,
178 accountName, 150 accountName,
179 start: 1, 151 start: 1,
180 count: 1, 152 count: 1,
181 sort: '-createdAt' 153 sort: '-createdAt'
182 }) 154 })
183 155
184 expect(res.body.total).to.equal(2) 156 expect(body.total).to.equal(2)
185 expect(res.body.data).to.have.lengthOf(1) 157 expect(body.data).to.have.lengthOf(1)
186 158 expect(body.data[0].name).to.equal('root_channel')
187 const videoChannel: VideoChannel = res.body.data[0]
188 expect(videoChannel.name).to.equal('root_channel')
189 } 159 }
190 }) 160 })
191 161
192 it('Should have one video channel when getting account channels on server 2', async function () { 162 it('Should have one video channel when getting account channels on server 2', async function () {
193 const res = await getAccountVideoChannelsList({ 163 const body = await servers[1].channels.listByAccount({ accountName })
194 url: servers[1].url,
195 accountName
196 })
197 164
198 expect(res.body.total).to.equal(1) 165 expect(body.total).to.equal(1)
199 expect(res.body.data).to.be.an('array') 166 expect(body.data).to.be.an('array')
200 expect(res.body.data).to.have.lengthOf(1) 167 expect(body.data).to.have.lengthOf(1)
201 168
202 const videoChannels = res.body.data 169 const videoChannel = body.data[0]
203 expect(videoChannels[0].name).to.equal('second_video_channel') 170 expect(videoChannel.name).to.equal('second_video_channel')
204 expect(videoChannels[0].displayName).to.equal('second video channel') 171 expect(videoChannel.displayName).to.equal('second video channel')
205 expect(videoChannels[0].description).to.equal('super video channel description') 172 expect(videoChannel.description).to.equal('super video channel description')
206 expect(videoChannels[0].support).to.equal('super video channel support text') 173 expect(videoChannel.support).to.equal('super video channel support text')
207 }) 174 })
208 175
209 it('Should list video channels', async function () { 176 it('Should list video channels', async function () {
210 const res = await getVideoChannelsList(servers[0].url, 1, 1, '-name') 177 const body = await servers[0].channels.list({ start: 1, count: 1, sort: '-name' })
211 178
212 expect(res.body.total).to.equal(2) 179 expect(body.total).to.equal(2)
213 expect(res.body.data).to.be.an('array') 180 expect(body.data).to.be.an('array')
214 expect(res.body.data).to.have.lengthOf(1) 181 expect(body.data).to.have.lengthOf(1)
215 expect(res.body.data[0].name).to.equal('root_channel') 182 expect(body.data[0].name).to.equal('root_channel')
216 expect(res.body.data[0].displayName).to.equal('Main root channel') 183 expect(body.data[0].displayName).to.equal('Main root channel')
217 }) 184 })
218 185
219 it('Should update video channel', async function () { 186 it('Should update video channel', async function () {
@@ -225,30 +192,29 @@ describe('Test video channels', function () {
225 support: 'support updated' 192 support: 'support updated'
226 } 193 }
227 194
228 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) 195 await servers[0].channels.update({ channelName: 'second_video_channel', attributes: videoChannelAttributes })
229 196
230 await waitJobs(servers) 197 await waitJobs(servers)
231 }) 198 })
232 199
233 it('Should have video channel updated', async function () { 200 it('Should have video channel updated', async function () {
234 for (const server of servers) { 201 for (const server of servers) {
235 const res = await getVideoChannelsList(server.url, 0, 1, '-name') 202 const body = await server.channels.list({ start: 0, count: 1, sort: '-name' })
236 203
237 expect(res.body.total).to.equal(2) 204 expect(body.total).to.equal(2)
238 expect(res.body.data).to.be.an('array') 205 expect(body.data).to.be.an('array')
239 expect(res.body.data).to.have.lengthOf(1) 206 expect(body.data).to.have.lengthOf(1)
240 expect(res.body.data[0].name).to.equal('second_video_channel') 207
241 expect(res.body.data[0].displayName).to.equal('video channel updated') 208 expect(body.data[0].name).to.equal('second_video_channel')
242 expect(res.body.data[0].description).to.equal('video channel description updated') 209 expect(body.data[0].displayName).to.equal('video channel updated')
243 expect(res.body.data[0].support).to.equal('support updated') 210 expect(body.data[0].description).to.equal('video channel description updated')
211 expect(body.data[0].support).to.equal('support updated')
244 } 212 }
245 }) 213 })
246 214
247 it('Should not have updated the video support field', async function () { 215 it('Should not have updated the video support field', async function () {
248 for (const server of servers) { 216 for (const server of servers) {
249 const res = await getVideo(server.url, videoUUID) 217 const video = await server.videos.get({ id: videoUUID })
250 const video: VideoDetails = res.body
251
252 expect(video.support).to.equal('video support field') 218 expect(video.support).to.equal('video support field')
253 } 219 }
254 }) 220 })
@@ -261,14 +227,12 @@ describe('Test video channels', function () {
261 bulkVideosSupportUpdate: true 227 bulkVideosSupportUpdate: true
262 } 228 }
263 229
264 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) 230 await servers[0].channels.update({ channelName: 'second_video_channel', attributes: videoChannelAttributes })
265 231
266 await waitJobs(servers) 232 await waitJobs(servers)
267 233
268 for (const server of servers) { 234 for (const server of servers) {
269 const res = await getVideo(server.url, videoUUID) 235 const video = await server.videos.get({ id: videoUUID })
270 const video: VideoDetails = res.body
271
272 expect(video.support).to.equal(videoChannelAttributes.support) 236 expect(video.support).to.equal(videoChannelAttributes.support)
273 } 237 }
274 }) 238 })
@@ -278,10 +242,8 @@ describe('Test video channels', function () {
278 242
279 const fixture = 'avatar.png' 243 const fixture = 'avatar.png'
280 244
281 await updateVideoChannelImage({ 245 await servers[0].channels.updateImage({
282 url: servers[0].url, 246 channelName: 'second_video_channel',
283 accessToken: servers[0].accessToken,
284 videoChannelName: 'second_video_channel',
285 fixture, 247 fixture,
286 type: 'avatar' 248 type: 'avatar'
287 }) 249 })
@@ -295,7 +257,7 @@ describe('Test video channels', function () {
295 await testImage(server.url, 'avatar-resized', avatarPaths[server.port], '.png') 257 await testImage(server.url, 'avatar-resized', avatarPaths[server.port], '.png')
296 await testFileExistsOrNot(server, 'avatars', basename(avatarPaths[server.port]), true) 258 await testFileExistsOrNot(server, 'avatars', basename(avatarPaths[server.port]), true)
297 259
298 const row = await getActorImage(server.internalServerNumber, basename(avatarPaths[server.port])) 260 const row = await server.sql.getActorImage(basename(avatarPaths[server.port]))
299 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.AVATARS.height) 261 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.AVATARS.height)
300 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.AVATARS.width) 262 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.AVATARS.width)
301 } 263 }
@@ -306,10 +268,8 @@ describe('Test video channels', function () {
306 268
307 const fixture = 'banner.jpg' 269 const fixture = 'banner.jpg'
308 270
309 await updateVideoChannelImage({ 271 await servers[0].channels.updateImage({
310 url: servers[0].url, 272 channelName: 'second_video_channel',
311 accessToken: servers[0].accessToken,
312 videoChannelName: 'second_video_channel',
313 fixture, 273 fixture,
314 type: 'banner' 274 type: 'banner'
315 }) 275 })
@@ -317,14 +277,13 @@ describe('Test video channels', function () {
317 await waitJobs(servers) 277 await waitJobs(servers)
318 278
319 for (const server of servers) { 279 for (const server of servers) {
320 const res = await getVideoChannel(server.url, 'second_video_channel@' + servers[0].host) 280 const videoChannel = await server.channels.get({ channelName: 'second_video_channel@' + servers[0].host })
321 const videoChannel = res.body
322 281
323 bannerPaths[server.port] = videoChannel.banner.path 282 bannerPaths[server.port] = videoChannel.banner.path
324 await testImage(server.url, 'banner-resized', bannerPaths[server.port]) 283 await testImage(server.url, 'banner-resized', bannerPaths[server.port])
325 await testFileExistsOrNot(server, 'avatars', basename(bannerPaths[server.port]), true) 284 await testFileExistsOrNot(server, 'avatars', basename(bannerPaths[server.port]), true)
326 285
327 const row = await getActorImage(server.internalServerNumber, basename(bannerPaths[server.port])) 286 const row = await server.sql.getActorImage(basename(bannerPaths[server.port]))
328 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.BANNERS.height) 287 expect(row.height).to.equal(ACTOR_IMAGES_SIZE.BANNERS.height)
329 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.BANNERS.width) 288 expect(row.width).to.equal(ACTOR_IMAGES_SIZE.BANNERS.width)
330 } 289 }
@@ -333,12 +292,7 @@ describe('Test video channels', function () {
333 it('Should delete the video channel avatar', async function () { 292 it('Should delete the video channel avatar', async function () {
334 this.timeout(15000) 293 this.timeout(15000)
335 294
336 await deleteVideoChannelImage({ 295 await servers[0].channels.deleteImage({ channelName: 'second_video_channel', type: 'avatar' })
337 url: servers[0].url,
338 accessToken: servers[0].accessToken,
339 videoChannelName: 'second_video_channel',
340 type: 'avatar'
341 })
342 296
343 await waitJobs(servers) 297 await waitJobs(servers)
344 298
@@ -353,12 +307,7 @@ describe('Test video channels', function () {
353 it('Should delete the video channel banner', async function () { 307 it('Should delete the video channel banner', async function () {
354 this.timeout(15000) 308 this.timeout(15000)
355 309
356 await deleteVideoChannelImage({ 310 await servers[0].channels.deleteImage({ channelName: 'second_video_channel', type: 'banner' })
357 url: servers[0].url,
358 accessToken: servers[0].accessToken,
359 videoChannelName: 'second_video_channel',
360 type: 'banner'
361 })
362 311
363 await waitJobs(servers) 312 await waitJobs(servers)
364 313
@@ -375,18 +324,19 @@ describe('Test video channels', function () {
375 324
376 for (const server of servers) { 325 for (const server of servers) {
377 const channelURI = 'second_video_channel@localhost:' + servers[0].port 326 const channelURI = 'second_video_channel@localhost:' + servers[0].port
378 const res1 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) 327 const { total, data } = await server.videos.listByChannel({ handle: channelURI })
379 expect(res1.body.total).to.equal(1) 328
380 expect(res1.body.data).to.be.an('array') 329 expect(total).to.equal(1)
381 expect(res1.body.data).to.have.lengthOf(1) 330 expect(data).to.be.an('array')
382 expect(res1.body.data[0].name).to.equal('my video name') 331 expect(data).to.have.lengthOf(1)
332 expect(data[0].name).to.equal('my video name')
383 } 333 }
384 }) 334 })
385 335
386 it('Should change the video channel of a video', async function () { 336 it('Should change the video channel of a video', async function () {
387 this.timeout(10000) 337 this.timeout(10000)
388 338
389 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { channelId: servers[0].videoChannel.id }) 339 await servers[0].videos.update({ id: videoUUID, attributes: { channelId: servers[0].store.channel.id } })
390 340
391 await waitJobs(servers) 341 await waitJobs(servers)
392 }) 342 })
@@ -395,47 +345,50 @@ describe('Test video channels', function () {
395 this.timeout(10000) 345 this.timeout(10000)
396 346
397 for (const server of servers) { 347 for (const server of servers) {
398 const secondChannelURI = 'second_video_channel@localhost:' + servers[0].port 348 {
399 const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondChannelURI, 0, 5) 349 const secondChannelURI = 'second_video_channel@localhost:' + servers[0].port
400 expect(res1.body.total).to.equal(0) 350 const { total } = await server.videos.listByChannel({ handle: secondChannelURI })
401 351 expect(total).to.equal(0)
402 const channelURI = 'root_channel@localhost:' + servers[0].port 352 }
403 const res2 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) 353
404 expect(res2.body.total).to.equal(1) 354 {
405 355 const channelURI = 'root_channel@localhost:' + servers[0].port
406 const videos: Video[] = res2.body.data 356 const { total, data } = await server.videos.listByChannel({ handle: channelURI })
407 expect(videos).to.be.an('array') 357 expect(total).to.equal(1)
408 expect(videos).to.have.lengthOf(1) 358
409 expect(videos[0].name).to.equal('my video name') 359 expect(data).to.be.an('array')
360 expect(data).to.have.lengthOf(1)
361 expect(data[0].name).to.equal('my video name')
362 }
410 } 363 }
411 }) 364 })
412 365
413 it('Should delete video channel', async function () { 366 it('Should delete video channel', async function () {
414 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel') 367 await servers[0].channels.delete({ channelName: 'second_video_channel' })
415 }) 368 })
416 369
417 it('Should have video channel deleted', async function () { 370 it('Should have video channel deleted', async function () {
418 const res = await getVideoChannelsList(servers[0].url, 0, 10) 371 const body = await servers[0].channels.list({ start: 0, count: 10 })
419 372
420 expect(res.body.total).to.equal(1) 373 expect(body.total).to.equal(1)
421 expect(res.body.data).to.be.an('array') 374 expect(body.data).to.be.an('array')
422 expect(res.body.data).to.have.lengthOf(1) 375 expect(body.data).to.have.lengthOf(1)
423 expect(res.body.data[0].displayName).to.equal('Main root channel') 376 expect(body.data[0].displayName).to.equal('Main root channel')
424 }) 377 })
425 378
426 it('Should create the main channel with an uuid if there is a conflict', async function () { 379 it('Should create the main channel with an uuid if there is a conflict', async function () {
427 { 380 {
428 const videoChannel = { name: 'toto_channel', displayName: 'My toto channel' } 381 const videoChannel = { name: 'toto_channel', displayName: 'My toto channel' }
429 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 382 const created = await servers[0].channels.create({ attributes: videoChannel })
430 totoChannel = res.body.videoChannel.id 383 totoChannel = created.id
431 } 384 }
432 385
433 { 386 {
434 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: 'toto', password: 'password' }) 387 await servers[0].users.create({ username: 'toto', password: 'password' })
435 const accessToken = await userLogin(servers[0], { username: 'toto', password: 'password' }) 388 const accessToken = await servers[0].login.getAccessToken({ username: 'toto', password: 'password' })
436 389
437 const res = await getMyUserInformation(servers[0].url, accessToken) 390 const { videoChannels } = await servers[0].users.getMyInfo({ token: accessToken })
438 const videoChannel = res.body.videoChannels[0] 391 const videoChannel = videoChannels[0]
439 expect(videoChannel.name).to.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/) 392 expect(videoChannel.name).to.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/)
440 } 393 }
441 }) 394 })
@@ -444,15 +397,9 @@ describe('Test video channels', function () {
444 this.timeout(10000) 397 this.timeout(10000)
445 398
446 { 399 {
447 const res = await getAccountVideoChannelsList({ 400 const { data } = await servers[0].channels.listByAccount({ accountName, withStats: true })
448 url: servers[0].url,
449 accountName,
450 withStats: true
451 })
452
453 const channels: VideoChannel[] = res.body.data
454 401
455 for (const channel of channels) { 402 for (const channel of data) {
456 expect(channel).to.haveOwnProperty('viewsPerDay') 403 expect(channel).to.haveOwnProperty('viewsPerDay')
457 expect(channel.viewsPerDay).to.have.length(30 + 1) // daysPrior + today 404 expect(channel.viewsPerDay).to.have.length(30 + 1) // daysPrior + today
458 405
@@ -464,33 +411,24 @@ describe('Test video channels', function () {
464 } 411 }
465 412
466 { 413 {
467 // video has been posted on channel servers[0].videoChannel.id since last update 414 // video has been posted on channel servers[0].store.videoChannel.id since last update
468 await viewVideo(servers[0].url, videoUUID, 204, '0.0.0.1,127.0.0.1') 415 await servers[0].videos.view({ id: videoUUID, xForwardedFor: '0.0.0.1,127.0.0.1' })
469 await viewVideo(servers[0].url, videoUUID, 204, '0.0.0.2,127.0.0.1') 416 await servers[0].videos.view({ id: videoUUID, xForwardedFor: '0.0.0.2,127.0.0.1' })
470 417
471 // Wait the repeatable job 418 // Wait the repeatable job
472 await wait(8000) 419 await wait(8000)
473 420
474 const res = await getAccountVideoChannelsList({ 421 const { data } = await servers[0].channels.listByAccount({ accountName, withStats: true })
475 url: servers[0].url, 422 const channelWithView = data.find(channel => channel.id === servers[0].store.channel.id)
476 accountName,
477 withStats: true
478 })
479 const channelWithView = res.body.data.find((channel: VideoChannel) => channel.id === servers[0].videoChannel.id)
480 expect(channelWithView.viewsPerDay.slice(-1)[0].views).to.equal(2) 423 expect(channelWithView.viewsPerDay.slice(-1)[0].views).to.equal(2)
481 } 424 }
482 }) 425 })
483 426
484 it('Should report correct videos count', async function () { 427 it('Should report correct videos count', async function () {
485 const res = await getAccountVideoChannelsList({ 428 const { data } = await servers[0].channels.listByAccount({ accountName, withStats: true })
486 url: servers[0].url,
487 accountName,
488 withStats: true
489 })
490 const channels: VideoChannel[] = res.body.data
491 429
492 const totoChannel = channels.find(c => c.name === 'toto_channel') 430 const totoChannel = data.find(c => c.name === 'toto_channel')
493 const rootChannel = channels.find(c => c.name === 'root_channel') 431 const rootChannel = data.find(c => c.name === 'root_channel')
494 432
495 expect(rootChannel.videosCount).to.equal(1) 433 expect(rootChannel.videosCount).to.equal(1)
496 expect(totoChannel.videosCount).to.equal(0) 434 expect(totoChannel.videosCount).to.equal(0)
@@ -498,26 +436,18 @@ describe('Test video channels', function () {
498 436
499 it('Should search among account video channels', async function () { 437 it('Should search among account video channels', async function () {
500 { 438 {
501 const res = await getAccountVideoChannelsList({ 439 const body = await servers[0].channels.listByAccount({ accountName, search: 'root' })
502 url: servers[0].url, 440 expect(body.total).to.equal(1)
503 accountName,
504 search: 'root'
505 })
506 expect(res.body.total).to.equal(1)
507 441
508 const channels = res.body.data 442 const channels = body.data
509 expect(channels).to.have.lengthOf(1) 443 expect(channels).to.have.lengthOf(1)
510 } 444 }
511 445
512 { 446 {
513 const res = await getAccountVideoChannelsList({ 447 const body = await servers[0].channels.listByAccount({ accountName, search: 'does not exist' })
514 url: servers[0].url, 448 expect(body.total).to.equal(0)
515 accountName,
516 search: 'does not exist'
517 })
518 expect(res.body.total).to.equal(0)
519 449
520 const channels = res.body.data 450 const channels = body.data
521 expect(channels).to.have.lengthOf(0) 451 expect(channels).to.have.lengthOf(0)
522 } 452 }
523 }) 453 })
@@ -525,34 +455,24 @@ describe('Test video channels', function () {
525 it('Should list channels by updatedAt desc if a video has been uploaded', async function () { 455 it('Should list channels by updatedAt desc if a video has been uploaded', async function () {
526 this.timeout(30000) 456 this.timeout(30000)
527 457
528 await uploadVideo(servers[0].url, servers[0].accessToken, { channelId: totoChannel }) 458 await servers[0].videos.upload({ attributes: { channelId: totoChannel } })
529 await waitJobs(servers) 459 await waitJobs(servers)
530 460
531 for (const server of servers) { 461 for (const server of servers) {
532 const res = await getAccountVideoChannelsList({ 462 const { data } = await server.channels.listByAccount({ accountName, sort: '-updatedAt' })
533 url: server.url,
534 accountName,
535 sort: '-updatedAt'
536 })
537 463
538 const channels: VideoChannel[] = res.body.data 464 expect(data[0].name).to.equal('toto_channel')
539 expect(channels[0].name).to.equal('toto_channel') 465 expect(data[1].name).to.equal('root_channel')
540 expect(channels[1].name).to.equal('root_channel')
541 } 466 }
542 467
543 await uploadVideo(servers[0].url, servers[0].accessToken, { channelId: servers[0].videoChannel.id }) 468 await servers[0].videos.upload({ attributes: { channelId: servers[0].store.channel.id } })
544 await waitJobs(servers) 469 await waitJobs(servers)
545 470
546 for (const server of servers) { 471 for (const server of servers) {
547 const res = await getAccountVideoChannelsList({ 472 const { data } = await server.channels.listByAccount({ accountName, sort: '-updatedAt' })
548 url: server.url,
549 accountName,
550 sort: '-updatedAt'
551 })
552 473
553 const channels: VideoChannel[] = res.body.data 474 expect(data[0].name).to.equal('root_channel')
554 expect(channels[0].name).to.equal('root_channel') 475 expect(data[1].name).to.equal('toto_channel')
555 expect(channels[1].name).to.equal('toto_channel')
556 } 476 }
557 }) 477 })
558 478
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts
index b6b002307..61ee54540 100644
--- a/server/tests/api/videos/video-comments.ts
+++ b/server/tests/api/videos/video-comments.ts
@@ -2,80 +2,62 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoComment, VideoCommentAdmin, VideoCommentThreadTree } from '@shared/models'
6import { cleanupTests, testImage } from '../../../../shared/extra-utils'
7import { 5import {
8 createUser, 6 cleanupTests,
7 CommentsCommand,
8 createSingleServer,
9 dateIsValid, 9 dateIsValid,
10 flushAndRunServer, 10 PeerTubeServer,
11 getAccessToken,
12 ServerInfo,
13 setAccessTokensToServers, 11 setAccessTokensToServers,
14 updateMyAvatar, 12 testImage
15 uploadVideo 13} from '@shared/extra-utils'
16} from '../../../../shared/extra-utils/index'
17import {
18 addVideoCommentReply,
19 addVideoCommentThread,
20 deleteVideoComment,
21 getAdminVideoComments,
22 getVideoCommentThreads,
23 getVideoThreadComments
24} from '../../../../shared/extra-utils/videos/video-comments'
25 14
26const expect = chai.expect 15const expect = chai.expect
27 16
28describe('Test video comments', function () { 17describe('Test video comments', function () {
29 let server: ServerInfo 18 let server: PeerTubeServer
30 let videoId 19 let videoId: number
31 let videoUUID 20 let videoUUID: string
32 let threadId 21 let threadId: number
33 let replyToDeleteId: number 22 let replyToDeleteId: number
34 23
35 let userAccessTokenServer1: string 24 let userAccessTokenServer1: string
36 25
26 let command: CommentsCommand
27
37 before(async function () { 28 before(async function () {
38 this.timeout(30000) 29 this.timeout(30000)
39 30
40 server = await flushAndRunServer(1) 31 server = await createSingleServer(1)
41 32
42 await setAccessTokensToServers([ server ]) 33 await setAccessTokensToServers([ server ])
43 34
44 const res = await uploadVideo(server.url, server.accessToken, {}) 35 const { id, uuid } = await server.videos.upload()
45 videoUUID = res.body.video.uuid 36 videoUUID = uuid
46 videoId = res.body.video.id 37 videoId = id
47 38
48 await updateMyAvatar({ 39 await server.users.updateMyAvatar({ fixture: 'avatar.png' })
49 url: server.url,
50 accessToken: server.accessToken,
51 fixture: 'avatar.png'
52 })
53 40
54 await createUser({ 41 userAccessTokenServer1 = await server.users.generateUserAndToken('user1')
55 url: server.url, 42
56 accessToken: server.accessToken, 43 command = server.comments
57 username: 'user1',
58 password: 'password'
59 })
60 userAccessTokenServer1 = await getAccessToken(server.url, 'user1', 'password')
61 }) 44 })
62 45
63 describe('User comments', function () { 46 describe('User comments', function () {
64 47
65 it('Should not have threads on this video', async function () { 48 it('Should not have threads on this video', async function () {
66 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 49 const body = await command.listThreads({ videoId: videoUUID })
67 50
68 expect(res.body.total).to.equal(0) 51 expect(body.total).to.equal(0)
69 expect(res.body.totalNotDeletedComments).to.equal(0) 52 expect(body.totalNotDeletedComments).to.equal(0)
70 expect(res.body.data).to.be.an('array') 53 expect(body.data).to.be.an('array')
71 expect(res.body.data).to.have.lengthOf(0) 54 expect(body.data).to.have.lengthOf(0)
72 }) 55 })
73 56
74 it('Should create a thread in this video', async function () { 57 it('Should create a thread in this video', async function () {
75 const text = 'my super first comment' 58 const text = 'my super first comment'
76 59
77 const res = await addVideoCommentThread(server.url, server.accessToken, videoUUID, text) 60 const comment = await command.createThread({ videoId: videoUUID, text })
78 const comment = res.body.comment
79 61
80 expect(comment.inReplyToCommentId).to.be.null 62 expect(comment.inReplyToCommentId).to.be.null
81 expect(comment.text).equal('my super first comment') 63 expect(comment.text).equal('my super first comment')
@@ -91,14 +73,14 @@ describe('Test video comments', function () {
91 }) 73 })
92 74
93 it('Should list threads of this video', async function () { 75 it('Should list threads of this video', async function () {
94 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 76 const body = await command.listThreads({ videoId: videoUUID })
95 77
96 expect(res.body.total).to.equal(1) 78 expect(body.total).to.equal(1)
97 expect(res.body.totalNotDeletedComments).to.equal(1) 79 expect(body.totalNotDeletedComments).to.equal(1)
98 expect(res.body.data).to.be.an('array') 80 expect(body.data).to.be.an('array')
99 expect(res.body.data).to.have.lengthOf(1) 81 expect(body.data).to.have.lengthOf(1)
100 82
101 const comment: VideoComment = res.body.data[0] 83 const comment = body.data[0]
102 expect(comment.inReplyToCommentId).to.be.null 84 expect(comment.inReplyToCommentId).to.be.null
103 expect(comment.text).equal('my super first comment') 85 expect(comment.text).equal('my super first comment')
104 expect(comment.videoId).to.equal(videoId) 86 expect(comment.videoId).to.equal(videoId)
@@ -117,9 +99,9 @@ describe('Test video comments', function () {
117 }) 99 })
118 100
119 it('Should get all the thread created', async function () { 101 it('Should get all the thread created', async function () {
120 const res = await getVideoThreadComments(server.url, videoUUID, threadId) 102 const body = await command.getThread({ videoId: videoUUID, threadId })
121 103
122 const rootComment = res.body.comment 104 const rootComment = body.comment
123 expect(rootComment.inReplyToCommentId).to.be.null 105 expect(rootComment.inReplyToCommentId).to.be.null
124 expect(rootComment.text).equal('my super first comment') 106 expect(rootComment.text).equal('my super first comment')
125 expect(rootComment.videoId).to.equal(videoId) 107 expect(rootComment.videoId).to.equal(videoId)
@@ -129,20 +111,19 @@ describe('Test video comments', function () {
129 111
130 it('Should create multiple replies in this thread', async function () { 112 it('Should create multiple replies in this thread', async function () {
131 const text1 = 'my super answer to thread 1' 113 const text1 = 'my super answer to thread 1'
132 const childCommentRes = await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text1) 114 const created = await command.addReply({ videoId, toCommentId: threadId, text: text1 })
133 const childCommentId = childCommentRes.body.comment.id 115 const childCommentId = created.id
134 116
135 const text2 = 'my super answer to answer of thread 1' 117 const text2 = 'my super answer to answer of thread 1'
136 await addVideoCommentReply(server.url, server.accessToken, videoId, childCommentId, text2) 118 await command.addReply({ videoId, toCommentId: childCommentId, text: text2 })
137 119
138 const text3 = 'my second answer to thread 1' 120 const text3 = 'my second answer to thread 1'
139 await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text3) 121 await command.addReply({ videoId, toCommentId: threadId, text: text3 })
140 }) 122 })
141 123
142 it('Should get correctly the replies', async function () { 124 it('Should get correctly the replies', async function () {
143 const res = await getVideoThreadComments(server.url, videoUUID, threadId) 125 const tree = await command.getThread({ videoId: videoUUID, threadId })
144 126
145 const tree: VideoCommentThreadTree = res.body
146 expect(tree.comment.text).equal('my super first comment') 127 expect(tree.comment.text).equal('my super first comment')
147 expect(tree.children).to.have.lengthOf(2) 128 expect(tree.children).to.have.lengthOf(2)
148 129
@@ -163,42 +144,41 @@ describe('Test video comments', function () {
163 144
164 it('Should create other threads', async function () { 145 it('Should create other threads', async function () {
165 const text1 = 'super thread 2' 146 const text1 = 'super thread 2'
166 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text1) 147 await command.createThread({ videoId: videoUUID, text: text1 })
167 148
168 const text2 = 'super thread 3' 149 const text2 = 'super thread 3'
169 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text2) 150 await command.createThread({ videoId: videoUUID, text: text2 })
170 }) 151 })
171 152
172 it('Should list the threads', async function () { 153 it('Should list the threads', async function () {
173 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt') 154 const body = await command.listThreads({ videoId: videoUUID, sort: 'createdAt' })
174 155
175 expect(res.body.total).to.equal(3) 156 expect(body.total).to.equal(3)
176 expect(res.body.totalNotDeletedComments).to.equal(6) 157 expect(body.totalNotDeletedComments).to.equal(6)
177 expect(res.body.data).to.be.an('array') 158 expect(body.data).to.be.an('array')
178 expect(res.body.data).to.have.lengthOf(3) 159 expect(body.data).to.have.lengthOf(3)
179 160
180 expect(res.body.data[0].text).to.equal('my super first comment') 161 expect(body.data[0].text).to.equal('my super first comment')
181 expect(res.body.data[0].totalReplies).to.equal(3) 162 expect(body.data[0].totalReplies).to.equal(3)
182 expect(res.body.data[1].text).to.equal('super thread 2') 163 expect(body.data[1].text).to.equal('super thread 2')
183 expect(res.body.data[1].totalReplies).to.equal(0) 164 expect(body.data[1].totalReplies).to.equal(0)
184 expect(res.body.data[2].text).to.equal('super thread 3') 165 expect(body.data[2].text).to.equal('super thread 3')
185 expect(res.body.data[2].totalReplies).to.equal(0) 166 expect(body.data[2].totalReplies).to.equal(0)
186 }) 167 })
187 168
188 it('Should delete a reply', async function () { 169 it('Should delete a reply', async function () {
189 await deleteVideoComment(server.url, server.accessToken, videoId, replyToDeleteId) 170 await command.delete({ videoId, commentId: replyToDeleteId })
190 171
191 { 172 {
192 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt') 173 const body = await command.listThreads({ videoId: videoUUID, sort: 'createdAt' })
193 174
194 expect(res.body.total).to.equal(3) 175 expect(body.total).to.equal(3)
195 expect(res.body.totalNotDeletedComments).to.equal(5) 176 expect(body.totalNotDeletedComments).to.equal(5)
196 } 177 }
197 178
198 { 179 {
199 const res = await getVideoThreadComments(server.url, videoUUID, threadId) 180 const tree = await command.getThread({ videoId: videoUUID, threadId })
200 181
201 const tree: VideoCommentThreadTree = res.body
202 expect(tree.comment.text).equal('my super first comment') 182 expect(tree.comment.text).equal('my super first comment')
203 expect(tree.children).to.have.lengthOf(2) 183 expect(tree.children).to.have.lengthOf(2)
204 184
@@ -220,99 +200,88 @@ describe('Test video comments', function () {
220 }) 200 })
221 201
222 it('Should delete a complete thread', async function () { 202 it('Should delete a complete thread', async function () {
223 await deleteVideoComment(server.url, server.accessToken, videoId, threadId) 203 await command.delete({ videoId, commentId: threadId })
224 204
225 const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt') 205 const body = await command.listThreads({ videoId: videoUUID, sort: 'createdAt' })
226 expect(res.body.total).to.equal(3) 206 expect(body.total).to.equal(3)
227 expect(res.body.data).to.be.an('array') 207 expect(body.data).to.be.an('array')
228 expect(res.body.data).to.have.lengthOf(3) 208 expect(body.data).to.have.lengthOf(3)
229 209
230 expect(res.body.data[0].text).to.equal('') 210 expect(body.data[0].text).to.equal('')
231 expect(res.body.data[0].isDeleted).to.be.true 211 expect(body.data[0].isDeleted).to.be.true
232 expect(res.body.data[0].deletedAt).to.not.be.null 212 expect(body.data[0].deletedAt).to.not.be.null
233 expect(res.body.data[0].account).to.be.null 213 expect(body.data[0].account).to.be.null
234 expect(res.body.data[0].totalReplies).to.equal(2) 214 expect(body.data[0].totalReplies).to.equal(2)
235 expect(res.body.data[1].text).to.equal('super thread 2') 215 expect(body.data[1].text).to.equal('super thread 2')
236 expect(res.body.data[1].totalReplies).to.equal(0) 216 expect(body.data[1].totalReplies).to.equal(0)
237 expect(res.body.data[2].text).to.equal('super thread 3') 217 expect(body.data[2].text).to.equal('super thread 3')
238 expect(res.body.data[2].totalReplies).to.equal(0) 218 expect(body.data[2].totalReplies).to.equal(0)
239 }) 219 })
240 220
241 it('Should count replies from the video author correctly', async function () { 221 it('Should count replies from the video author correctly', async function () {
242 const text = 'my super first comment' 222 await command.createThread({ videoId: videoUUID, text: 'my super first comment' })
243 await addVideoCommentThread(server.url, server.accessToken, videoUUID, text) 223
244 let res = await getVideoCommentThreads(server.url, videoUUID, 0, 5) 224 const { data } = await command.listThreads({ videoId: videoUUID })
245 const comment: VideoComment = res.body.data[0] 225 const threadId2 = data[0].threadId
246 const threadId2 = comment.threadId
247 226
248 const text2 = 'a first answer to thread 4 by a third party' 227 const text2 = 'a first answer to thread 4 by a third party'
249 await addVideoCommentReply(server.url, userAccessTokenServer1, videoId, threadId2, text2) 228 await command.addReply({ token: userAccessTokenServer1, videoId, toCommentId: threadId2, text: text2 })
250 229
251 const text3 = 'my second answer to thread 4' 230 const text3 = 'my second answer to thread 4'
252 await addVideoCommentReply(server.url, server.accessToken, videoId, threadId2, text3) 231 await command.addReply({ videoId, toCommentId: threadId2, text: text3 })
253 232
254 res = await getVideoThreadComments(server.url, videoUUID, threadId2) 233 const tree = await command.getThread({ videoId: videoUUID, threadId: threadId2 })
255 const tree: VideoCommentThreadTree = res.body
256 expect(tree.comment.totalReplies).to.equal(tree.comment.totalRepliesFromVideoAuthor + 1) 234 expect(tree.comment.totalReplies).to.equal(tree.comment.totalRepliesFromVideoAuthor + 1)
257 }) 235 })
258 }) 236 })
259 237
260 describe('All instance comments', function () { 238 describe('All instance comments', function () {
261 async function getComments (options: any = {}) {
262 const res = await getAdminVideoComments(Object.assign({
263 url: server.url,
264 token: server.accessToken,
265 start: 0,
266 count: 10
267 }, options))
268
269 return { comments: res.body.data as VideoCommentAdmin[], total: res.body.total as number }
270 }
271 239
272 it('Should list instance comments as admin', async function () { 240 it('Should list instance comments as admin', async function () {
273 const { comments } = await getComments({ start: 0, count: 1 }) 241 const { data } = await command.listForAdmin({ start: 0, count: 1 })
274 242
275 expect(comments[0].text).to.equal('my second answer to thread 4') 243 expect(data[0].text).to.equal('my second answer to thread 4')
276 }) 244 })
277 245
278 it('Should filter instance comments by isLocal', async function () { 246 it('Should filter instance comments by isLocal', async function () {
279 const { total, comments } = await getComments({ isLocal: false }) 247 const { total, data } = await command.listForAdmin({ isLocal: false })
280 248
281 expect(comments).to.have.lengthOf(0) 249 expect(data).to.have.lengthOf(0)
282 expect(total).to.equal(0) 250 expect(total).to.equal(0)
283 }) 251 })
284 252
285 it('Should search instance comments by account', async function () { 253 it('Should search instance comments by account', async function () {
286 const { total, comments } = await getComments({ searchAccount: 'user' }) 254 const { total, data } = await command.listForAdmin({ searchAccount: 'user' })
287 255
288 expect(comments).to.have.lengthOf(1) 256 expect(data).to.have.lengthOf(1)
289 expect(total).to.equal(1) 257 expect(total).to.equal(1)
290 258
291 expect(comments[0].text).to.equal('a first answer to thread 4 by a third party') 259 expect(data[0].text).to.equal('a first answer to thread 4 by a third party')
292 }) 260 })
293 261
294 it('Should search instance comments by video', async function () { 262 it('Should search instance comments by video', async function () {
295 { 263 {
296 const { total, comments } = await getComments({ searchVideo: 'video' }) 264 const { total, data } = await command.listForAdmin({ searchVideo: 'video' })
297 265
298 expect(comments).to.have.lengthOf(7) 266 expect(data).to.have.lengthOf(7)
299 expect(total).to.equal(7) 267 expect(total).to.equal(7)
300 } 268 }
301 269
302 { 270 {
303 const { total, comments } = await getComments({ searchVideo: 'hello' }) 271 const { total, data } = await command.listForAdmin({ searchVideo: 'hello' })
304 272
305 expect(comments).to.have.lengthOf(0) 273 expect(data).to.have.lengthOf(0)
306 expect(total).to.equal(0) 274 expect(total).to.equal(0)
307 } 275 }
308 }) 276 })
309 277
310 it('Should search instance comments', async function () { 278 it('Should search instance comments', async function () {
311 const { total, comments } = await getComments({ search: 'super thread 3' }) 279 const { total, data } = await command.listForAdmin({ search: 'super thread 3' })
312 280
313 expect(comments).to.have.lengthOf(1)
314 expect(total).to.equal(1) 281 expect(total).to.equal(1)
315 expect(comments[0].text).to.equal('super thread 3') 282
283 expect(data).to.have.lengthOf(1)
284 expect(data[0].text).to.equal('super thread 3')
316 }) 285 })
317 }) 286 })
318 287
diff --git a/server/tests/api/videos/video-description.ts b/server/tests/api/videos/video-description.ts
index b8e98e45f..d22b4ed96 100644
--- a/server/tests/api/videos/video-description.ts
+++ b/server/tests/api/videos/video-description.ts
@@ -1,25 +1,13 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { 4import * as chai from 'chai'
6 cleanupTests, 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
7 flushAndRunMultipleServers,
8 getVideo,
9 getVideoDescription,
10 getVideosList,
11 ServerInfo,
12 setAccessTokensToServers,
13 updateVideo,
14 uploadVideo
15} from '../../../../shared/extra-utils/index'
16import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18 6
19const expect = chai.expect 7const expect = chai.expect
20 8
21describe('Test video description', function () { 9describe('Test video description', function () {
22 let servers: ServerInfo[] = [] 10 let servers: PeerTubeServer[] = []
23 let videoUUID = '' 11 let videoUUID = ''
24 let videoId: number 12 let videoId: number
25 const longDescription = 'my super description for server 1'.repeat(50) 13 const longDescription = 'my super description for server 1'.repeat(50)
@@ -28,7 +16,7 @@ describe('Test video description', function () {
28 this.timeout(40000) 16 this.timeout(40000)
29 17
30 // Run servers 18 // Run servers
31 servers = await flushAndRunMultipleServers(2) 19 servers = await createMultipleServers(2)
32 20
33 // Get the access tokens 21 // Get the access tokens
34 await setAccessTokensToServers(servers) 22 await setAccessTokensToServers(servers)
@@ -43,20 +31,19 @@ describe('Test video description', function () {
43 const attributes = { 31 const attributes = {
44 description: longDescription 32 description: longDescription
45 } 33 }
46 await uploadVideo(servers[0].url, servers[0].accessToken, attributes) 34 await servers[0].videos.upload({ attributes })
47 35
48 await waitJobs(servers) 36 await waitJobs(servers)
49 37
50 const res = await getVideosList(servers[0].url) 38 const { data } = await servers[0].videos.list()
51 39
52 videoId = res.body.data[0].id 40 videoId = data[0].id
53 videoUUID = res.body.data[0].uuid 41 videoUUID = data[0].uuid
54 }) 42 })
55 43
56 it('Should have a truncated description on each server', async function () { 44 it('Should have a truncated description on each server', async function () {
57 for (const server of servers) { 45 for (const server of servers) {
58 const res = await getVideo(server.url, videoUUID) 46 const video = await server.videos.get({ id: videoUUID })
59 const video = res.body
60 47
61 // 30 characters * 6 -> 240 characters 48 // 30 characters * 6 -> 240 characters
62 const truncatedDescription = 'my super description for server 1'.repeat(7) + 49 const truncatedDescription = 'my super description for server 1'.repeat(7) +
@@ -68,11 +55,10 @@ describe('Test video description', function () {
68 55
69 it('Should fetch long description on each server', async function () { 56 it('Should fetch long description on each server', async function () {
70 for (const server of servers) { 57 for (const server of servers) {
71 const res = await getVideo(server.url, videoUUID) 58 const video = await server.videos.get({ id: videoUUID })
72 const video = res.body
73 59
74 const res2 = await getVideoDescription(server.url, video.descriptionPath) 60 const { description } = await server.videos.getDescription({ descriptionPath: video.descriptionPath })
75 expect(res2.body.description).to.equal(longDescription) 61 expect(description).to.equal(longDescription)
76 } 62 }
77 }) 63 })
78 64
@@ -82,20 +68,19 @@ describe('Test video description', function () {
82 const attributes = { 68 const attributes = {
83 description: 'short description' 69 description: 'short description'
84 } 70 }
85 await updateVideo(servers[0].url, servers[0].accessToken, videoId, attributes) 71 await servers[0].videos.update({ id: videoId, attributes })
86 72
87 await waitJobs(servers) 73 await waitJobs(servers)
88 }) 74 })
89 75
90 it('Should have a small description on each server', async function () { 76 it('Should have a small description on each server', async function () {
91 for (const server of servers) { 77 for (const server of servers) {
92 const res = await getVideo(server.url, videoUUID) 78 const video = await server.videos.get({ id: videoUUID })
93 const video = res.body
94 79
95 expect(video.description).to.equal('short description') 80 expect(video.description).to.equal('short description')
96 81
97 const res2 = await getVideoDescription(server.url, video.descriptionPath) 82 const { description } = await server.videos.getDescription({ descriptionPath: video.descriptionPath })
98 expect(res2.body.description).to.equal('short description') 83 expect(description).to.equal('short description')
99 } 84 }
100 }) 85 })
101 86
diff --git a/server/tests/api/videos/video-hls.ts b/server/tests/api/videos/video-hls.ts
index 03ac3f321..961f0e617 100644
--- a/server/tests/api/videos/video-hls.ts
+++ b/server/tests/api/videos/video-hls.ts
@@ -2,38 +2,30 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path' 5import { basename, join } from 'path'
6import { removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils'
6import { 7import {
7 checkDirectoryIsEmpty, 8 checkDirectoryIsEmpty,
8 checkResolutionsInMasterPlaylist, 9 checkResolutionsInMasterPlaylist,
9 checkSegmentHash, 10 checkSegmentHash,
10 checkTmpIsEmpty, 11 checkTmpIsEmpty,
11 cleanupTests, 12 cleanupTests,
13 createMultipleServers,
12 doubleFollow, 14 doubleFollow,
13 flushAndRunMultipleServers,
14 getPlaylist,
15 getVideo,
16 makeRawRequest, 15 makeRawRequest,
17 removeVideo, 16 PeerTubeServer,
18 ServerInfo,
19 setAccessTokensToServers, 17 setAccessTokensToServers,
20 updateCustomSubConfig,
21 updateVideo,
22 uploadVideo,
23 waitJobs, 18 waitJobs,
24 webtorrentAdd 19 webtorrentAdd
25} from '../../../../shared/extra-utils' 20} from '@shared/extra-utils'
26import { VideoDetails } from '../../../../shared/models/videos' 21import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
27import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
28import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' 22import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
29import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
30 23
31const expect = chai.expect 24const expect = chai.expect
32 25
33async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOnly: boolean, resolutions = [ 240, 360, 480, 720 ]) { 26async function checkHlsPlaylist (servers: PeerTubeServer[], videoUUID: string, hlsOnly: boolean, resolutions = [ 240, 360, 480, 720 ]) {
34 for (const server of servers) { 27 for (const server of servers) {
35 const resVideoDetails = await getVideo(server.url, videoUUID) 28 const videoDetails = await server.videos.get({ id: videoUUID })
36 const videoDetails: VideoDetails = resVideoDetails.body
37 const baseUrl = `http://${videoDetails.account.host}` 29 const baseUrl = `http://${videoDetails.account.host}`
38 30
39 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) 31 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
@@ -47,14 +39,17 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
47 if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0) 39 if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0)
48 else expect(videoDetails.files).to.have.lengthOf(resolutions.length) 40 else expect(videoDetails.files).to.have.lengthOf(resolutions.length)
49 41
42 // Check JSON files
50 for (const resolution of resolutions) { 43 for (const resolution of resolutions) {
51 const file = hlsFiles.find(f => f.resolution.id === resolution) 44 const file = hlsFiles.find(f => f.resolution.id === resolution)
52 expect(file).to.not.be.undefined 45 expect(file).to.not.be.undefined
53 46
54 expect(file.magnetUri).to.have.lengthOf.above(2) 47 expect(file.magnetUri).to.have.lengthOf.above(2)
55 expect(file.torrentUrl).to.equal(`http://${server.host}/lazy-static/torrents/${videoDetails.uuid}-${file.resolution.id}-hls.torrent`) 48 expect(file.torrentUrl).to.match(
56 expect(file.fileUrl).to.equal( 49 new RegExp(`http://${server.host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`)
57 `${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${videoDetails.uuid}-${file.resolution.id}-fragmented.mp4` 50 )
51 expect(file.fileUrl).to.match(
52 new RegExp(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${uuidRegex}-${file.resolution.id}-fragmented.mp4`)
58 ) 53 )
59 expect(file.resolution.label).to.equal(resolution + 'p') 54 expect(file.resolution.label).to.equal(resolution + 'p')
60 55
@@ -67,11 +62,11 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
67 expect(torrent.files[0].path).to.exist.and.to.not.equal('') 62 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
68 } 63 }
69 64
65 // Check master playlist
70 { 66 {
71 await checkResolutionsInMasterPlaylist(hlsPlaylist.playlistUrl, resolutions) 67 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
72 68
73 const res = await getPlaylist(hlsPlaylist.playlistUrl) 69 const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
74 const masterPlaylist = res.text
75 70
76 for (const resolution of resolutions) { 71 for (const resolution of resolutions) {
77 expect(masterPlaylist).to.contain(`${resolution}.m3u8`) 72 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
@@ -79,12 +74,18 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
79 } 74 }
80 } 75 }
81 76
77 // Check resolution playlists
82 { 78 {
83 for (const resolution of resolutions) { 79 for (const resolution of resolutions) {
84 const res = await getPlaylist(`${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`) 80 const file = hlsFiles.find(f => f.resolution.id === resolution)
81 const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8'
82
83 const subPlaylist = await server.streamingPlaylists.get({
84 url: `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}`
85 })
85 86
86 const subPlaylist = res.text 87 expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`))
87 expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`) 88 expect(subPlaylist).to.contain(basename(file.fileUrl))
88 } 89 }
89 } 90 }
90 91
@@ -92,23 +93,31 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOn
92 const baseUrlAndPath = baseUrl + '/static/streaming-playlists/hls' 93 const baseUrlAndPath = baseUrl + '/static/streaming-playlists/hls'
93 94
94 for (const resolution of resolutions) { 95 for (const resolution of resolutions) {
95 await checkSegmentHash(baseUrlAndPath, baseUrlAndPath, videoUUID, resolution, hlsPlaylist) 96 await checkSegmentHash({
97 server,
98 baseUrlPlaylist: baseUrlAndPath,
99 baseUrlSegment: baseUrlAndPath,
100 videoUUID,
101 resolution,
102 hlsPlaylist
103 })
96 } 104 }
97 } 105 }
98 } 106 }
99} 107}
100 108
101describe('Test HLS videos', function () { 109describe('Test HLS videos', function () {
102 let servers: ServerInfo[] = [] 110 let servers: PeerTubeServer[] = []
103 let videoUUID = '' 111 let videoUUID = ''
104 let videoAudioUUID = '' 112 let videoAudioUUID = ''
105 113
106 function runTestSuite (hlsOnly: boolean) { 114 function runTestSuite (hlsOnly: boolean) {
115
107 it('Should upload a video and transcode it to HLS', async function () { 116 it('Should upload a video and transcode it to HLS', async function () {
108 this.timeout(120000) 117 this.timeout(120000)
109 118
110 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 1', fixture: 'video_short.webm' }) 119 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } })
111 videoUUID = res.body.video.uuid 120 videoUUID = uuid
112 121
113 await waitJobs(servers) 122 await waitJobs(servers)
114 123
@@ -118,8 +127,8 @@ describe('Test HLS videos', function () {
118 it('Should upload an audio file and transcode it to HLS', async function () { 127 it('Should upload an audio file and transcode it to HLS', async function () {
119 this.timeout(120000) 128 this.timeout(120000)
120 129
121 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video audio', fixture: 'sample.ogg' }) 130 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } })
122 videoAudioUUID = res.body.video.uuid 131 videoAudioUUID = uuid
123 132
124 await waitJobs(servers) 133 await waitJobs(servers)
125 134
@@ -129,7 +138,7 @@ describe('Test HLS videos', function () {
129 it('Should update the video', async function () { 138 it('Should update the video', async function () {
130 this.timeout(10000) 139 this.timeout(10000)
131 140
132 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video 1 updated' }) 141 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video 1 updated' } })
133 142
134 await waitJobs(servers) 143 await waitJobs(servers)
135 144
@@ -139,14 +148,14 @@ describe('Test HLS videos', function () {
139 it('Should delete videos', async function () { 148 it('Should delete videos', async function () {
140 this.timeout(10000) 149 this.timeout(10000)
141 150
142 await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) 151 await servers[0].videos.remove({ id: videoUUID })
143 await removeVideo(servers[0].url, servers[0].accessToken, videoAudioUUID) 152 await servers[0].videos.remove({ id: videoAudioUUID })
144 153
145 await waitJobs(servers) 154 await waitJobs(servers)
146 155
147 for (const server of servers) { 156 for (const server of servers) {
148 await getVideo(server.url, videoUUID, HttpStatusCode.NOT_FOUND_404) 157 await server.videos.get({ id: videoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
149 await getVideo(server.url, videoAudioUUID, HttpStatusCode.NOT_FOUND_404) 158 await server.videos.get({ id: videoAudioUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
150 } 159 }
151 }) 160 })
152 161
@@ -176,7 +185,7 @@ describe('Test HLS videos', function () {
176 } 185 }
177 } 186 }
178 } 187 }
179 servers = await flushAndRunMultipleServers(2, configOverride) 188 servers = await createMultipleServers(2, configOverride)
180 189
181 // Get the access tokens 190 // Get the access tokens
182 await setAccessTokensToServers(servers) 191 await setAccessTokensToServers(servers)
@@ -192,24 +201,26 @@ describe('Test HLS videos', function () {
192 describe('With only HLS enabled', function () { 201 describe('With only HLS enabled', function () {
193 202
194 before(async function () { 203 before(async function () {
195 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 204 await servers[0].config.updateCustomSubConfig({
196 transcoding: { 205 newConfig: {
197 enabled: true, 206 transcoding: {
198 allowAudioFiles: true, 207 enabled: true,
199 resolutions: { 208 allowAudioFiles: true,
200 '240p': true, 209 resolutions: {
201 '360p': true, 210 '240p': true,
202 '480p': true, 211 '360p': true,
203 '720p': true, 212 '480p': true,
204 '1080p': true, 213 '720p': true,
205 '1440p': true, 214 '1080p': true,
206 '2160p': true 215 '1440p': true,
207 }, 216 '2160p': true
208 hls: { 217 },
209 enabled: true 218 hls: {
210 }, 219 enabled: true
211 webtorrent: { 220 },
212 enabled: false 221 webtorrent: {
222 enabled: false
223 }
213 } 224 }
214 } 225 }
215 }) 226 })
diff --git a/server/tests/api/videos/video-imports.ts b/server/tests/api/videos/video-imports.ts
index 80834ca86..2eac130d2 100644
--- a/server/tests/api/videos/video-imports.ts
+++ b/server/tests/api/videos/video-imports.ts
@@ -3,43 +3,30 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 areHttpImportTestsDisabled,
6 cleanupTests, 7 cleanupTests,
8 createMultipleServers,
7 doubleFollow, 9 doubleFollow,
8 flushAndRunMultipleServers, 10 FIXTURE_URLS,
9 getMyUserInformation, 11 PeerTubeServer,
10 getMyVideos,
11 getVideo,
12 getVideosList,
13 immutableAssign,
14 listVideoCaptions,
15 ServerInfo,
16 setAccessTokensToServers, 12 setAccessTokensToServers,
17 testCaptionFile, 13 testCaptionFile,
18 updateCustomSubConfig 14 testImage,
19} from '../../../../shared/extra-utils' 15 waitJobs
20import { areHttpImportTestsDisabled, testImage } from '../../../../shared/extra-utils/miscs/miscs' 16} from '@shared/extra-utils'
21import { waitJobs } from '../../../../shared/extra-utils/server/jobs' 17import { VideoPrivacy, VideoResolution } from '@shared/models'
22import {
23 getMagnetURI,
24 getMyVideoImports,
25 getYoutubeHDRVideoUrl,
26 getYoutubeVideoUrl,
27 importVideo
28} from '../../../../shared/extra-utils/videos/video-imports'
29import { VideoCaption, VideoDetails, VideoImport, VideoPrivacy, VideoResolution } from '../../../../shared/models/videos'
30 18
31const expect = chai.expect 19const expect = chai.expect
32 20
33describe('Test video imports', function () { 21describe('Test video imports', function () {
34 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
35 let channelIdServer1: number 23 let channelIdServer1: number
36 let channelIdServer2: number 24 let channelIdServer2: number
37 25
38 if (areHttpImportTestsDisabled()) return 26 if (areHttpImportTestsDisabled()) return
39 27
40 async function checkVideosServer1 (url: string, idHttp: string, idMagnet: string, idTorrent: string) { 28 async function checkVideosServer1 (server: PeerTubeServer, idHttp: string, idMagnet: string, idTorrent: string) {
41 const resHttp = await getVideo(url, idHttp) 29 const videoHttp = await server.videos.get({ id: idHttp })
42 const videoHttp: VideoDetails = resHttp.body
43 30
44 expect(videoHttp.name).to.equal('small video - youtube') 31 expect(videoHttp.name).to.equal('small video - youtube')
45 // FIXME: youtube-dl seems broken 32 // FIXME: youtube-dl seems broken
@@ -56,10 +43,8 @@ describe('Test video imports', function () {
56 expect(originallyPublishedAt.getMonth()).to.equal(0) 43 expect(originallyPublishedAt.getMonth()).to.equal(0)
57 expect(originallyPublishedAt.getFullYear()).to.equal(2019) 44 expect(originallyPublishedAt.getFullYear()).to.equal(2019)
58 45
59 const resMagnet = await getVideo(url, idMagnet) 46 const videoMagnet = await server.videos.get({ id: idMagnet })
60 const videoMagnet: VideoDetails = resMagnet.body 47 const videoTorrent = await server.videos.get({ id: idTorrent })
61 const resTorrent = await getVideo(url, idTorrent)
62 const videoTorrent: VideoDetails = resTorrent.body
63 48
64 for (const video of [ videoMagnet, videoTorrent ]) { 49 for (const video of [ videoMagnet, videoTorrent ]) {
65 expect(video.category.label).to.equal('Misc') 50 expect(video.category.label).to.equal('Misc')
@@ -74,13 +59,12 @@ describe('Test video imports', function () {
74 expect(videoTorrent.name).to.contain('你好 世界 720p.mp4') 59 expect(videoTorrent.name).to.contain('你好 世界 720p.mp4')
75 expect(videoMagnet.name).to.contain('super peertube2 video') 60 expect(videoMagnet.name).to.contain('super peertube2 video')
76 61
77 const resCaptions = await listVideoCaptions(url, idHttp) 62 const bodyCaptions = await server.captions.list({ videoId: idHttp })
78 expect(resCaptions.body.total).to.equal(2) 63 expect(bodyCaptions.total).to.equal(2)
79 } 64 }
80 65
81 async function checkVideoServer2 (url: string, id: number | string) { 66 async function checkVideoServer2 (server: PeerTubeServer, id: number | string) {
82 const res = await getVideo(url, id) 67 const video = await server.videos.get({ id })
83 const video: VideoDetails = res.body
84 68
85 expect(video.name).to.equal('my super name') 69 expect(video.name).to.equal('my super name')
86 expect(video.category.label).to.equal('Entertainment') 70 expect(video.category.label).to.equal('Entertainment')
@@ -92,26 +76,26 @@ describe('Test video imports', function () {
92 76
93 expect(video.files).to.have.lengthOf(1) 77 expect(video.files).to.have.lengthOf(1)
94 78
95 const resCaptions = await listVideoCaptions(url, id) 79 const bodyCaptions = await server.captions.list({ videoId: id })
96 expect(resCaptions.body.total).to.equal(2) 80 expect(bodyCaptions.total).to.equal(2)
97 } 81 }
98 82
99 before(async function () { 83 before(async function () {
100 this.timeout(30_000) 84 this.timeout(30_000)
101 85
102 // Run servers 86 // Run servers
103 servers = await flushAndRunMultipleServers(2) 87 servers = await createMultipleServers(2)
104 88
105 await setAccessTokensToServers(servers) 89 await setAccessTokensToServers(servers)
106 90
107 { 91 {
108 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 92 const { videoChannels } = await servers[0].users.getMyInfo()
109 channelIdServer1 = res.body.videoChannels[0].id 93 channelIdServer1 = videoChannels[0].id
110 } 94 }
111 95
112 { 96 {
113 const res = await getMyUserInformation(servers[1].url, servers[1].accessToken) 97 const { videoChannels } = await servers[1].users.getMyInfo()
114 channelIdServer2 = res.body.videoChannels[0].id 98 channelIdServer2 = videoChannels[0].id
115 } 99 }
116 100
117 await doubleFollow(servers[0], servers[1]) 101 await doubleFollow(servers[0], servers[1])
@@ -126,18 +110,18 @@ describe('Test video imports', function () {
126 } 110 }
127 111
128 { 112 {
129 const attributes = immutableAssign(baseAttributes, { targetUrl: getYoutubeVideoUrl() }) 113 const attributes = { ...baseAttributes, targetUrl: FIXTURE_URLS.youtube }
130 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 114 const { video } = await servers[0].imports.importVideo({ attributes })
131 expect(res.body.video.name).to.equal('small video - youtube') 115 expect(video.name).to.equal('small video - youtube')
132 116
133 expect(res.body.video.thumbnailPath).to.match(new RegExp(`^/static/thumbnails/.+.jpg$`)) 117 expect(video.thumbnailPath).to.match(new RegExp(`^/static/thumbnails/.+.jpg$`))
134 expect(res.body.video.previewPath).to.match(new RegExp(`^/lazy-static/previews/.+.jpg$`)) 118 expect(video.previewPath).to.match(new RegExp(`^/lazy-static/previews/.+.jpg$`))
135 119
136 await testImage(servers[0].url, 'video_import_thumbnail', res.body.video.thumbnailPath) 120 await testImage(servers[0].url, 'video_import_thumbnail', video.thumbnailPath)
137 await testImage(servers[0].url, 'video_import_preview', res.body.video.previewPath) 121 await testImage(servers[0].url, 'video_import_preview', video.previewPath)
138 122
139 const resCaptions = await listVideoCaptions(servers[0].url, res.body.video.id) 123 const bodyCaptions = await servers[0].captions.list({ videoId: video.id })
140 const videoCaptions: VideoCaption[] = resCaptions.body.data 124 const videoCaptions = bodyCaptions.data
141 expect(videoCaptions).to.have.lengthOf(2) 125 expect(videoCaptions).to.have.lengthOf(2)
142 126
143 const enCaption = videoCaptions.find(caption => caption.language.id === 'en') 127 const enCaption = videoCaptions.find(caption => caption.language.id === 'en')
@@ -176,52 +160,52 @@ Ajouter un sous-titre est vraiment facile`)
176 } 160 }
177 161
178 { 162 {
179 const attributes = immutableAssign(baseAttributes, { 163 const attributes = {
180 magnetUri: getMagnetURI(), 164 ...baseAttributes,
165 magnetUri: FIXTURE_URLS.magnet,
181 description: 'this is a super torrent description', 166 description: 'this is a super torrent description',
182 tags: [ 'tag_torrent1', 'tag_torrent2' ] 167 tags: [ 'tag_torrent1', 'tag_torrent2' ]
183 }) 168 }
184 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 169 const { video } = await servers[0].imports.importVideo({ attributes })
185 expect(res.body.video.name).to.equal('super peertube2 video') 170 expect(video.name).to.equal('super peertube2 video')
186 } 171 }
187 172
188 { 173 {
189 const attributes = immutableAssign(baseAttributes, { 174 const attributes = {
175 ...baseAttributes,
190 torrentfile: 'video-720p.torrent' as any, 176 torrentfile: 'video-720p.torrent' as any,
191 description: 'this is a super torrent description', 177 description: 'this is a super torrent description',
192 tags: [ 'tag_torrent1', 'tag_torrent2' ] 178 tags: [ 'tag_torrent1', 'tag_torrent2' ]
193 }) 179 }
194 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 180 const { video } = await servers[0].imports.importVideo({ attributes })
195 expect(res.body.video.name).to.equal('你好 世界 720p.mp4') 181 expect(video.name).to.equal('你好 世界 720p.mp4')
196 } 182 }
197 }) 183 })
198 184
199 it('Should list the videos to import in my videos on server 1', async function () { 185 it('Should list the videos to import in my videos on server 1', async function () {
200 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5, 'createdAt') 186 const { total, data } = await servers[0].videos.listMyVideos({ sort: 'createdAt' })
201 187
202 expect(res.body.total).to.equal(3) 188 expect(total).to.equal(3)
203 189
204 const videos = res.body.data 190 expect(data).to.have.lengthOf(3)
205 expect(videos).to.have.lengthOf(3) 191 expect(data[0].name).to.equal('small video - youtube')
206 expect(videos[0].name).to.equal('small video - youtube') 192 expect(data[1].name).to.equal('super peertube2 video')
207 expect(videos[1].name).to.equal('super peertube2 video') 193 expect(data[2].name).to.equal('你好 世界 720p.mp4')
208 expect(videos[2].name).to.equal('你好 世界 720p.mp4')
209 }) 194 })
210 195
211 it('Should list the videos to import in my imports on server 1', async function () { 196 it('Should list the videos to import in my imports on server 1', async function () {
212 const res = await getMyVideoImports(servers[0].url, servers[0].accessToken, '-createdAt') 197 const { total, data: videoImports } = await servers[0].imports.getMyVideoImports({ sort: '-createdAt' })
198 expect(total).to.equal(3)
213 199
214 expect(res.body.total).to.equal(3)
215 const videoImports: VideoImport[] = res.body.data
216 expect(videoImports).to.have.lengthOf(3) 200 expect(videoImports).to.have.lengthOf(3)
217 201
218 expect(videoImports[2].targetUrl).to.equal(getYoutubeVideoUrl()) 202 expect(videoImports[2].targetUrl).to.equal(FIXTURE_URLS.youtube)
219 expect(videoImports[2].magnetUri).to.be.null 203 expect(videoImports[2].magnetUri).to.be.null
220 expect(videoImports[2].torrentName).to.be.null 204 expect(videoImports[2].torrentName).to.be.null
221 expect(videoImports[2].video.name).to.equal('small video - youtube') 205 expect(videoImports[2].video.name).to.equal('small video - youtube')
222 206
223 expect(videoImports[1].targetUrl).to.be.null 207 expect(videoImports[1].targetUrl).to.be.null
224 expect(videoImports[1].magnetUri).to.equal(getMagnetURI()) 208 expect(videoImports[1].magnetUri).to.equal(FIXTURE_URLS.magnet)
225 expect(videoImports[1].torrentName).to.be.null 209 expect(videoImports[1].torrentName).to.be.null
226 expect(videoImports[1].video.name).to.equal('super peertube2 video') 210 expect(videoImports[1].video.name).to.equal('super peertube2 video')
227 211
@@ -237,12 +221,12 @@ Ajouter un sous-titre est vraiment facile`)
237 await waitJobs(servers) 221 await waitJobs(servers)
238 222
239 for (const server of servers) { 223 for (const server of servers) {
240 const res = await getVideosList(server.url) 224 const { total, data } = await server.videos.list()
241 expect(res.body.total).to.equal(3) 225 expect(total).to.equal(3)
242 expect(res.body.data).to.have.lengthOf(3) 226 expect(data).to.have.lengthOf(3)
243 227
244 const [ videoHttp, videoMagnet, videoTorrent ] = res.body.data 228 const [ videoHttp, videoMagnet, videoTorrent ] = data
245 await checkVideosServer1(server.url, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid) 229 await checkVideosServer1(server, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid)
246 } 230 }
247 }) 231 })
248 232
@@ -250,7 +234,7 @@ Ajouter un sous-titre est vraiment facile`)
250 this.timeout(60_000) 234 this.timeout(60_000)
251 235
252 const attributes = { 236 const attributes = {
253 targetUrl: getYoutubeVideoUrl(), 237 targetUrl: FIXTURE_URLS.youtube,
254 channelId: channelIdServer2, 238 channelId: channelIdServer2,
255 privacy: VideoPrivacy.PUBLIC, 239 privacy: VideoPrivacy.PUBLIC,
256 category: 10, 240 category: 10,
@@ -260,8 +244,8 @@ Ajouter un sous-titre est vraiment facile`)
260 description: 'my super description', 244 description: 'my super description',
261 tags: [ 'supertag1', 'supertag2' ] 245 tags: [ 'supertag1', 'supertag2' ]
262 } 246 }
263 const res = await importVideo(servers[1].url, servers[1].accessToken, attributes) 247 const { video } = await servers[1].imports.importVideo({ attributes })
264 expect(res.body.video.name).to.equal('my super name') 248 expect(video.name).to.equal('my super name')
265 }) 249 })
266 250
267 it('Should have the videos listed on the two instances', async function () { 251 it('Should have the videos listed on the two instances', async function () {
@@ -270,14 +254,14 @@ Ajouter un sous-titre est vraiment facile`)
270 await waitJobs(servers) 254 await waitJobs(servers)
271 255
272 for (const server of servers) { 256 for (const server of servers) {
273 const res = await getVideosList(server.url) 257 const { total, data } = await server.videos.list()
274 expect(res.body.total).to.equal(4) 258 expect(total).to.equal(4)
275 expect(res.body.data).to.have.lengthOf(4) 259 expect(data).to.have.lengthOf(4)
276 260
277 await checkVideoServer2(server.url, res.body.data[0].uuid) 261 await checkVideoServer2(server, data[0].uuid)
278 262
279 const [ , videoHttp, videoMagnet, videoTorrent ] = res.body.data 263 const [ , videoHttp, videoMagnet, videoTorrent ] = data
280 await checkVideosServer1(server.url, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid) 264 await checkVideosServer1(server, videoHttp.uuid, videoMagnet.uuid, videoTorrent.uuid)
281 } 265 }
282 }) 266 })
283 267
@@ -286,18 +270,17 @@ Ajouter un sous-titre est vraiment facile`)
286 270
287 const attributes = { 271 const attributes = {
288 name: 'transcoded video', 272 name: 'transcoded video',
289 magnetUri: getMagnetURI(), 273 magnetUri: FIXTURE_URLS.magnet,
290 channelId: channelIdServer2, 274 channelId: channelIdServer2,
291 privacy: VideoPrivacy.PUBLIC 275 privacy: VideoPrivacy.PUBLIC
292 } 276 }
293 const res = await importVideo(servers[1].url, servers[1].accessToken, attributes) 277 const { video } = await servers[1].imports.importVideo({ attributes })
294 const videoUUID = res.body.video.uuid 278 const videoUUID = video.uuid
295 279
296 await waitJobs(servers) 280 await waitJobs(servers)
297 281
298 for (const server of servers) { 282 for (const server of servers) {
299 const res = await getVideo(server.url, videoUUID) 283 const video = await server.videos.get({ id: videoUUID })
300 const video: VideoDetails = res.body
301 284
302 expect(video.name).to.equal('transcoded video') 285 expect(video.name).to.equal('transcoded video')
303 expect(video.files).to.have.lengthOf(4) 286 expect(video.files).to.have.lengthOf(4)
@@ -333,22 +316,21 @@ Ajouter un sous-titre est vraiment facile`)
333 } 316 }
334 } 317 }
335 } 318 }
336 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 319 await servers[0].config.updateCustomSubConfig({ newConfig: config })
337 320
338 const attributes = { 321 const attributes = {
339 name: 'hdr video', 322 name: 'hdr video',
340 targetUrl: getYoutubeHDRVideoUrl(), 323 targetUrl: FIXTURE_URLS.youtubeHDR,
341 channelId: channelIdServer1, 324 channelId: channelIdServer1,
342 privacy: VideoPrivacy.PUBLIC 325 privacy: VideoPrivacy.PUBLIC
343 } 326 }
344 const res1 = await importVideo(servers[0].url, servers[0].accessToken, attributes) 327 const { video: videoImported } = await servers[0].imports.importVideo({ attributes })
345 const videoUUID = res1.body.video.uuid 328 const videoUUID = videoImported.uuid
346 329
347 await waitJobs(servers) 330 await waitJobs(servers)
348 331
349 // test resolution 332 // test resolution
350 const res2 = await getVideo(servers[0].url, videoUUID) 333 const video = await servers[0].videos.get({ id: videoUUID })
351 const video: VideoDetails = res2.body
352 expect(video.name).to.equal('hdr video') 334 expect(video.name).to.equal('hdr video')
353 const maxResolution = Math.max.apply(Math, video.files.map(function (o) { return o.resolution.id })) 335 const maxResolution = Math.max.apply(Math, video.files.map(function (o) { return o.resolution.id }))
354 expect(maxResolution, 'expected max resolution not met').to.equals(VideoResolution.H_1080P) 336 expect(maxResolution, 'expected max resolution not met').to.equals(VideoResolution.H_1080P)
diff --git a/server/tests/api/videos/video-nsfw.ts b/server/tests/api/videos/video-nsfw.ts
index b16b484b9..b5d183d62 100644
--- a/server/tests/api/videos/video-nsfw.ts
+++ b/server/tests/api/videos/video-nsfw.ts
@@ -1,117 +1,96 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, getVideosList, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../../../shared/extra-utils/index' 4import * as chai from 'chai'
6import { userLogin } from '../../../../shared/extra-utils/users/login' 5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
7import { createUser } from '../../../../shared/extra-utils/users/users' 6import { BooleanBothQuery, CustomConfig, ResultList, Video, VideosOverview } from '@shared/models'
8import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
9import {
10 flushAndRunServer,
11 getAccountVideos,
12 getConfig,
13 getCustomConfig,
14 getMyUserInformation,
15 getVideoChannelVideos,
16 getVideosListWithToken,
17 searchVideo,
18 searchVideoWithToken,
19 updateCustomConfig,
20 updateMyUser
21} from '../../../../shared/extra-utils'
22import { ServerConfig, VideosOverview } from '../../../../shared/models'
23import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
24import { User } from '../../../../shared/models/users'
25import { getVideosOverview, getVideosOverviewWithToken } from '@shared/extra-utils/overviews/overviews'
26 7
27const expect = chai.expect 8const expect = chai.expect
28 9
29function createOverviewRes (res: any) { 10function createOverviewRes (overview: VideosOverview) {
30 const overview = res.body as VideosOverview
31
32 const videos = overview.categories[0].videos 11 const videos = overview.categories[0].videos
33 return { body: { data: videos, total: videos.length } } 12 return { data: videos, total: videos.length }
34} 13}
35 14
36describe('Test video NSFW policy', function () { 15describe('Test video NSFW policy', function () {
37 let server: ServerInfo 16 let server: PeerTubeServer
38 let userAccessToken: string 17 let userAccessToken: string
39 let customConfig: CustomConfig 18 let customConfig: CustomConfig
40 19
41 function getVideosFunctions (token?: string, query = {}) { 20 async function getVideosFunctions (token?: string, query: { nsfw?: BooleanBothQuery } = {}) {
42 return getMyUserInformation(server.url, server.accessToken) 21 const user = await server.users.getMyInfo()
43 .then(res => { 22
44 const user: User = res.body 23 const channelName = user.videoChannels[0].name
45 const videoChannelName = user.videoChannels[0].name 24 const accountName = user.account.name + '@' + user.account.host
46 const accountName = user.account.name + '@' + user.account.host 25
47 const hasQuery = Object.keys(query).length !== 0 26 const hasQuery = Object.keys(query).length !== 0
48 let promises: Promise<any>[] 27 let promises: Promise<ResultList<Video>>[]
49 28
50 if (token) { 29 if (token) {
51 promises = [ 30 promises = [
52 getVideosListWithToken(server.url, token, query), 31 server.search.advancedVideoSearch({ token, search: { search: 'n', sort: '-publishedAt', ...query } }),
53 searchVideoWithToken(server.url, 'n', token, query), 32 server.videos.listWithToken({ token, ...query }),
54 getAccountVideos(server.url, token, accountName, 0, 5, undefined, query), 33 server.videos.listByAccount({ token, handle: accountName, ...query }),
55 getVideoChannelVideos(server.url, token, videoChannelName, 0, 5, undefined, query) 34 server.videos.listByChannel({ token, handle: channelName, ...query })
56 ] 35 ]
57 36
58 // Overviews do not support video filters 37 // Overviews do not support video filters
59 if (!hasQuery) { 38 if (!hasQuery) {
60 promises.push(getVideosOverviewWithToken(server.url, 1, token).then(res => createOverviewRes(res))) 39 const p = server.overviews.getVideos({ page: 1, token })
61 } 40 .then(res => createOverviewRes(res))
62 41 promises.push(p)
63 return Promise.all(promises) 42 }
64 } 43
65 44 return Promise.all(promises)
66 promises = [ 45 }
67 getVideosList(server.url), 46
68 searchVideo(server.url, 'n'), 47 promises = [
69 getAccountVideos(server.url, undefined, accountName, 0, 5), 48 server.search.searchVideos({ search: 'n', sort: '-publishedAt' }),
70 getVideoChannelVideos(server.url, undefined, videoChannelName, 0, 5) 49 server.videos.list(),
71 ] 50 server.videos.listByAccount({ token: null, handle: accountName }),
72 51 server.videos.listByChannel({ token: null, handle: channelName })
73 // Overviews do not support video filters 52 ]
74 if (!hasQuery) { 53
75 promises.push(getVideosOverview(server.url, 1).then(res => createOverviewRes(res))) 54 // Overviews do not support video filters
76 } 55 if (!hasQuery) {
77 56 const p = server.overviews.getVideos({ page: 1 })
78 return Promise.all(promises) 57 .then(res => createOverviewRes(res))
79 }) 58 promises.push(p)
59 }
60
61 return Promise.all(promises)
80 } 62 }
81 63
82 before(async function () { 64 before(async function () {
83 this.timeout(50000) 65 this.timeout(50000)
84 server = await flushAndRunServer(1) 66 server = await createSingleServer(1)
85 67
86 // Get the access tokens 68 // Get the access tokens
87 await setAccessTokensToServers([ server ]) 69 await setAccessTokensToServers([ server ])
88 70
89 { 71 {
90 const attributes = { name: 'nsfw', nsfw: true, category: 1 } 72 const attributes = { name: 'nsfw', nsfw: true, category: 1 }
91 await uploadVideo(server.url, server.accessToken, attributes) 73 await server.videos.upload({ attributes })
92 } 74 }
93 75
94 { 76 {
95 const attributes = { name: 'normal', nsfw: false, category: 1 } 77 const attributes = { name: 'normal', nsfw: false, category: 1 }
96 await uploadVideo(server.url, server.accessToken, attributes) 78 await server.videos.upload({ attributes })
97 } 79 }
98 80
99 { 81 customConfig = await server.config.getCustomConfig()
100 const res = await getCustomConfig(server.url, server.accessToken)
101 customConfig = res.body
102 }
103 }) 82 })
104 83
105 describe('Instance default NSFW policy', function () { 84 describe('Instance default NSFW policy', function () {
85
106 it('Should display NSFW videos with display default NSFW policy', async function () { 86 it('Should display NSFW videos with display default NSFW policy', async function () {
107 const resConfig = await getConfig(server.url) 87 const serverConfig = await server.config.getConfig()
108 const serverConfig: ServerConfig = resConfig.body
109 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('display') 88 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('display')
110 89
111 for (const res of await getVideosFunctions()) { 90 for (const body of await getVideosFunctions()) {
112 expect(res.body.total).to.equal(2) 91 expect(body.total).to.equal(2)
113 92
114 const videos = res.body.data 93 const videos = body.data
115 expect(videos).to.have.lengthOf(2) 94 expect(videos).to.have.lengthOf(2)
116 expect(videos[0].name).to.equal('normal') 95 expect(videos[0].name).to.equal('normal')
117 expect(videos[1].name).to.equal('nsfw') 96 expect(videos[1].name).to.equal('nsfw')
@@ -120,16 +99,15 @@ describe('Test video NSFW policy', function () {
120 99
121 it('Should not display NSFW videos with do_not_list default NSFW policy', async function () { 100 it('Should not display NSFW videos with do_not_list default NSFW policy', async function () {
122 customConfig.instance.defaultNSFWPolicy = 'do_not_list' 101 customConfig.instance.defaultNSFWPolicy = 'do_not_list'
123 await updateCustomConfig(server.url, server.accessToken, customConfig) 102 await server.config.updateCustomConfig({ newCustomConfig: customConfig })
124 103
125 const resConfig = await getConfig(server.url) 104 const serverConfig = await server.config.getConfig()
126 const serverConfig: ServerConfig = resConfig.body
127 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('do_not_list') 105 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('do_not_list')
128 106
129 for (const res of await getVideosFunctions()) { 107 for (const body of await getVideosFunctions()) {
130 expect(res.body.total).to.equal(1) 108 expect(body.total).to.equal(1)
131 109
132 const videos = res.body.data 110 const videos = body.data
133 expect(videos).to.have.lengthOf(1) 111 expect(videos).to.have.lengthOf(1)
134 expect(videos[0].name).to.equal('normal') 112 expect(videos[0].name).to.equal('normal')
135 } 113 }
@@ -137,16 +115,15 @@ describe('Test video NSFW policy', function () {
137 115
138 it('Should display NSFW videos with blur default NSFW policy', async function () { 116 it('Should display NSFW videos with blur default NSFW policy', async function () {
139 customConfig.instance.defaultNSFWPolicy = 'blur' 117 customConfig.instance.defaultNSFWPolicy = 'blur'
140 await updateCustomConfig(server.url, server.accessToken, customConfig) 118 await server.config.updateCustomConfig({ newCustomConfig: customConfig })
141 119
142 const resConfig = await getConfig(server.url) 120 const serverConfig = await server.config.getConfig()
143 const serverConfig: ServerConfig = resConfig.body
144 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('blur') 121 expect(serverConfig.instance.defaultNSFWPolicy).to.equal('blur')
145 122
146 for (const res of await getVideosFunctions()) { 123 for (const body of await getVideosFunctions()) {
147 expect(res.body.total).to.equal(2) 124 expect(body.total).to.equal(2)
148 125
149 const videos = res.body.data 126 const videos = body.data
150 expect(videos).to.have.lengthOf(2) 127 expect(videos).to.have.lengthOf(2)
151 expect(videos[0].name).to.equal('normal') 128 expect(videos[0].name).to.equal('normal')
152 expect(videos[1].name).to.equal('nsfw') 129 expect(videos[1].name).to.equal('nsfw')
@@ -159,24 +136,22 @@ describe('Test video NSFW policy', function () {
159 it('Should create a user having the default nsfw policy', async function () { 136 it('Should create a user having the default nsfw policy', async function () {
160 const username = 'user1' 137 const username = 'user1'
161 const password = 'my super password' 138 const password = 'my super password'
162 await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) 139 await server.users.create({ username: username, password: password })
163
164 userAccessToken = await userLogin(server, { username, password })
165 140
166 const res = await getMyUserInformation(server.url, userAccessToken) 141 userAccessToken = await server.login.getAccessToken({ username, password })
167 const user = res.body
168 142
143 const user = await server.users.getMyInfo({ token: userAccessToken })
169 expect(user.nsfwPolicy).to.equal('blur') 144 expect(user.nsfwPolicy).to.equal('blur')
170 }) 145 })
171 146
172 it('Should display NSFW videos with blur user NSFW policy', async function () { 147 it('Should display NSFW videos with blur user NSFW policy', async function () {
173 customConfig.instance.defaultNSFWPolicy = 'do_not_list' 148 customConfig.instance.defaultNSFWPolicy = 'do_not_list'
174 await updateCustomConfig(server.url, server.accessToken, customConfig) 149 await server.config.updateCustomConfig({ newCustomConfig: customConfig })
175 150
176 for (const res of await getVideosFunctions(userAccessToken)) { 151 for (const body of await getVideosFunctions(userAccessToken)) {
177 expect(res.body.total).to.equal(2) 152 expect(body.total).to.equal(2)
178 153
179 const videos = res.body.data 154 const videos = body.data
180 expect(videos).to.have.lengthOf(2) 155 expect(videos).to.have.lengthOf(2)
181 expect(videos[0].name).to.equal('normal') 156 expect(videos[0].name).to.equal('normal')
182 expect(videos[1].name).to.equal('nsfw') 157 expect(videos[1].name).to.equal('nsfw')
@@ -184,16 +159,12 @@ describe('Test video NSFW policy', function () {
184 }) 159 })
185 160
186 it('Should display NSFW videos with display user NSFW policy', async function () { 161 it('Should display NSFW videos with display user NSFW policy', async function () {
187 await updateMyUser({ 162 await server.users.updateMe({ nsfwPolicy: 'display' })
188 url: server.url,
189 accessToken: server.accessToken,
190 nsfwPolicy: 'display'
191 })
192 163
193 for (const res of await getVideosFunctions(server.accessToken)) { 164 for (const body of await getVideosFunctions(server.accessToken)) {
194 expect(res.body.total).to.equal(2) 165 expect(body.total).to.equal(2)
195 166
196 const videos = res.body.data 167 const videos = body.data
197 expect(videos).to.have.lengthOf(2) 168 expect(videos).to.have.lengthOf(2)
198 expect(videos[0].name).to.equal('normal') 169 expect(videos[0].name).to.equal('normal')
199 expect(videos[1].name).to.equal('nsfw') 170 expect(videos[1].name).to.equal('nsfw')
@@ -201,56 +172,51 @@ describe('Test video NSFW policy', function () {
201 }) 172 })
202 173
203 it('Should not display NSFW videos with do_not_list user NSFW policy', async function () { 174 it('Should not display NSFW videos with do_not_list user NSFW policy', async function () {
204 await updateMyUser({ 175 await server.users.updateMe({ nsfwPolicy: 'do_not_list' })
205 url: server.url,
206 accessToken: server.accessToken,
207 nsfwPolicy: 'do_not_list'
208 })
209 176
210 for (const res of await getVideosFunctions(server.accessToken)) { 177 for (const body of await getVideosFunctions(server.accessToken)) {
211 expect(res.body.total).to.equal(1) 178 expect(body.total).to.equal(1)
212 179
213 const videos = res.body.data 180 const videos = body.data
214 expect(videos).to.have.lengthOf(1) 181 expect(videos).to.have.lengthOf(1)
215 expect(videos[0].name).to.equal('normal') 182 expect(videos[0].name).to.equal('normal')
216 } 183 }
217 }) 184 })
218 185
219 it('Should be able to see my NSFW videos even with do_not_list user NSFW policy', async function () { 186 it('Should be able to see my NSFW videos even with do_not_list user NSFW policy', async function () {
220 const res = await getMyVideos(server.url, server.accessToken, 0, 5) 187 const { total, data } = await server.videos.listMyVideos()
221 expect(res.body.total).to.equal(2) 188 expect(total).to.equal(2)
222 189
223 const videos = res.body.data 190 expect(data).to.have.lengthOf(2)
224 expect(videos).to.have.lengthOf(2) 191 expect(data[0].name).to.equal('normal')
225 expect(videos[0].name).to.equal('normal') 192 expect(data[1].name).to.equal('nsfw')
226 expect(videos[1].name).to.equal('nsfw')
227 }) 193 })
228 194
229 it('Should display NSFW videos when the nsfw param === true', async function () { 195 it('Should display NSFW videos when the nsfw param === true', async function () {
230 for (const res of await getVideosFunctions(server.accessToken, { nsfw: true })) { 196 for (const body of await getVideosFunctions(server.accessToken, { nsfw: 'true' })) {
231 expect(res.body.total).to.equal(1) 197 expect(body.total).to.equal(1)
232 198
233 const videos = res.body.data 199 const videos = body.data
234 expect(videos).to.have.lengthOf(1) 200 expect(videos).to.have.lengthOf(1)
235 expect(videos[0].name).to.equal('nsfw') 201 expect(videos[0].name).to.equal('nsfw')
236 } 202 }
237 }) 203 })
238 204
239 it('Should hide NSFW videos when the nsfw param === true', async function () { 205 it('Should hide NSFW videos when the nsfw param === true', async function () {
240 for (const res of await getVideosFunctions(server.accessToken, { nsfw: false })) { 206 for (const body of await getVideosFunctions(server.accessToken, { nsfw: 'false' })) {
241 expect(res.body.total).to.equal(1) 207 expect(body.total).to.equal(1)
242 208
243 const videos = res.body.data 209 const videos = body.data
244 expect(videos).to.have.lengthOf(1) 210 expect(videos).to.have.lengthOf(1)
245 expect(videos[0].name).to.equal('normal') 211 expect(videos[0].name).to.equal('normal')
246 } 212 }
247 }) 213 })
248 214
249 it('Should display both videos when the nsfw param === both', async function () { 215 it('Should display both videos when the nsfw param === both', async function () {
250 for (const res of await getVideosFunctions(server.accessToken, { nsfw: 'both' })) { 216 for (const body of await getVideosFunctions(server.accessToken, { nsfw: 'both' })) {
251 expect(res.body.total).to.equal(2) 217 expect(body.total).to.equal(2)
252 218
253 const videos = res.body.data 219 const videos = body.data
254 expect(videos).to.have.lengthOf(2) 220 expect(videos).to.have.lengthOf(2)
255 expect(videos[0].name).to.equal('normal') 221 expect(videos[0].name).to.equal('normal')
256 expect(videos[1].name).to.equal('nsfw') 222 expect(videos[1].name).to.equal('nsfw')
diff --git a/server/tests/api/videos/video-playlist-thumbnails.ts b/server/tests/api/videos/video-playlist-thumbnails.ts
index a93a0b7de..f0b2ca169 100644
--- a/server/tests/api/videos/video-playlist-thumbnails.ts
+++ b/server/tests/api/videos/video-playlist-thumbnails.ts
@@ -1,21 +1,15 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 addVideoInPlaylist,
7 cleanupTests, 6 cleanupTests,
8 createVideoPlaylist, 7 createMultipleServers,
9 doubleFollow, 8 doubleFollow,
10 flushAndRunMultipleServers, 9 PeerTubeServer,
11 getVideoPlaylistsList,
12 removeVideoFromPlaylist,
13 reorderVideosPlaylist,
14 ServerInfo,
15 setAccessTokensToServers, 10 setAccessTokensToServers,
16 setDefaultVideoChannel, 11 setDefaultVideoChannel,
17 testImage, 12 testImage,
18 uploadVideoAndGetId,
19 waitJobs 13 waitJobs
20} from '../../../../shared/extra-utils' 14} from '../../../../shared/extra-utils'
21import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 15import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
@@ -23,10 +17,10 @@ import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/
23const expect = chai.expect 17const expect = chai.expect
24 18
25describe('Playlist thumbnail', function () { 19describe('Playlist thumbnail', function () {
26 let servers: ServerInfo[] = [] 20 let servers: PeerTubeServer[] = []
27 21
28 let playlistWithoutThumbnail: number 22 let playlistWithoutThumbnailId: number
29 let playlistWithThumbnail: number 23 let playlistWithThumbnailId: number
30 24
31 let withThumbnailE1: number 25 let withThumbnailE1: number
32 let withThumbnailE2: number 26 let withThumbnailE2: number
@@ -36,22 +30,22 @@ describe('Playlist thumbnail', function () {
36 let video1: number 30 let video1: number
37 let video2: number 31 let video2: number
38 32
39 async function getPlaylistWithoutThumbnail (server: ServerInfo) { 33 async function getPlaylistWithoutThumbnail (server: PeerTubeServer) {
40 const res = await getVideoPlaylistsList(server.url, 0, 10) 34 const body = await server.playlists.list({ start: 0, count: 10 })
41 35
42 return res.body.data.find(p => p.displayName === 'playlist without thumbnail') 36 return body.data.find(p => p.displayName === 'playlist without thumbnail')
43 } 37 }
44 38
45 async function getPlaylistWithThumbnail (server: ServerInfo) { 39 async function getPlaylistWithThumbnail (server: PeerTubeServer) {
46 const res = await getVideoPlaylistsList(server.url, 0, 10) 40 const body = await server.playlists.list({ start: 0, count: 10 })
47 41
48 return res.body.data.find(p => p.displayName === 'playlist with thumbnail') 42 return body.data.find(p => p.displayName === 'playlist with thumbnail')
49 } 43 }
50 44
51 before(async function () { 45 before(async function () {
52 this.timeout(120000) 46 this.timeout(120000)
53 47
54 servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: false } }) 48 servers = await createMultipleServers(2, { transcoding: { enabled: false } })
55 49
56 // Get the access tokens 50 // Get the access tokens
57 await setAccessTokensToServers(servers) 51 await setAccessTokensToServers(servers)
@@ -60,8 +54,8 @@ describe('Playlist thumbnail', function () {
60 // Server 1 and server 2 follow each other 54 // Server 1 and server 2 follow each other
61 await doubleFollow(servers[0], servers[1]) 55 await doubleFollow(servers[0], servers[1])
62 56
63 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).id 57 video1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).id
64 video2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).id 58 video2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).id
65 59
66 await waitJobs(servers) 60 await waitJobs(servers)
67 }) 61 })
@@ -69,24 +63,20 @@ describe('Playlist thumbnail', function () {
69 it('Should automatically update the thumbnail when adding an element', async function () { 63 it('Should automatically update the thumbnail when adding an element', async function () {
70 this.timeout(30000) 64 this.timeout(30000)
71 65
72 const res = await createVideoPlaylist({ 66 const created = await servers[1].playlists.create({
73 url: servers[1].url, 67 attributes: {
74 token: servers[1].accessToken,
75 playlistAttrs: {
76 displayName: 'playlist without thumbnail', 68 displayName: 'playlist without thumbnail',
77 privacy: VideoPlaylistPrivacy.PUBLIC, 69 privacy: VideoPlaylistPrivacy.PUBLIC,
78 videoChannelId: servers[1].videoChannel.id 70 videoChannelId: servers[1].store.channel.id
79 } 71 }
80 }) 72 })
81 playlistWithoutThumbnail = res.body.videoPlaylist.id 73 playlistWithoutThumbnailId = created.id
82 74
83 const res2 = await addVideoInPlaylist({ 75 const added = await servers[1].playlists.addElement({
84 url: servers[1].url, 76 playlistId: playlistWithoutThumbnailId,
85 token: servers[1].accessToken, 77 attributes: { videoId: video1 }
86 playlistId: playlistWithoutThumbnail,
87 elementAttrs: { videoId: video1 }
88 }) 78 })
89 withoutThumbnailE1 = res2.body.videoPlaylistElement.id 79 withoutThumbnailE1 = added.id
90 80
91 await waitJobs(servers) 81 await waitJobs(servers)
92 82
@@ -99,25 +89,21 @@ describe('Playlist thumbnail', function () {
99 it('Should not update the thumbnail if we explicitly uploaded a thumbnail', async function () { 89 it('Should not update the thumbnail if we explicitly uploaded a thumbnail', async function () {
100 this.timeout(30000) 90 this.timeout(30000)
101 91
102 const res = await createVideoPlaylist({ 92 const created = await servers[1].playlists.create({
103 url: servers[1].url, 93 attributes: {
104 token: servers[1].accessToken,
105 playlistAttrs: {
106 displayName: 'playlist with thumbnail', 94 displayName: 'playlist with thumbnail',
107 privacy: VideoPlaylistPrivacy.PUBLIC, 95 privacy: VideoPlaylistPrivacy.PUBLIC,
108 videoChannelId: servers[1].videoChannel.id, 96 videoChannelId: servers[1].store.channel.id,
109 thumbnailfile: 'thumbnail.jpg' 97 thumbnailfile: 'thumbnail.jpg'
110 } 98 }
111 }) 99 })
112 playlistWithThumbnail = res.body.videoPlaylist.id 100 playlistWithThumbnailId = created.id
113 101
114 const res2 = await addVideoInPlaylist({ 102 const added = await servers[1].playlists.addElement({
115 url: servers[1].url, 103 playlistId: playlistWithThumbnailId,
116 token: servers[1].accessToken, 104 attributes: { videoId: video1 }
117 playlistId: playlistWithThumbnail,
118 elementAttrs: { videoId: video1 }
119 }) 105 })
120 withThumbnailE1 = res2.body.videoPlaylistElement.id 106 withThumbnailE1 = added.id
121 107
122 await waitJobs(servers) 108 await waitJobs(servers)
123 109
@@ -130,19 +116,15 @@ describe('Playlist thumbnail', function () {
130 it('Should automatically update the thumbnail when moving the first element', async function () { 116 it('Should automatically update the thumbnail when moving the first element', async function () {
131 this.timeout(30000) 117 this.timeout(30000)
132 118
133 const res = await addVideoInPlaylist({ 119 const added = await servers[1].playlists.addElement({
134 url: servers[1].url, 120 playlistId: playlistWithoutThumbnailId,
135 token: servers[1].accessToken, 121 attributes: { videoId: video2 }
136 playlistId: playlistWithoutThumbnail,
137 elementAttrs: { videoId: video2 }
138 }) 122 })
139 withoutThumbnailE2 = res.body.videoPlaylistElement.id 123 withoutThumbnailE2 = added.id
140 124
141 await reorderVideosPlaylist({ 125 await servers[1].playlists.reorderElements({
142 url: servers[1].url, 126 playlistId: playlistWithoutThumbnailId,
143 token: servers[1].accessToken, 127 attributes: {
144 playlistId: playlistWithoutThumbnail,
145 elementAttrs: {
146 startPosition: 1, 128 startPosition: 1,
147 insertAfterPosition: 2 129 insertAfterPosition: 2
148 } 130 }
@@ -159,19 +141,15 @@ describe('Playlist thumbnail', function () {
159 it('Should not update the thumbnail when moving the first element if we explicitly uploaded a thumbnail', async function () { 141 it('Should not update the thumbnail when moving the first element if we explicitly uploaded a thumbnail', async function () {
160 this.timeout(30000) 142 this.timeout(30000)
161 143
162 const res = await addVideoInPlaylist({ 144 const added = await servers[1].playlists.addElement({
163 url: servers[1].url, 145 playlistId: playlistWithThumbnailId,
164 token: servers[1].accessToken, 146 attributes: { videoId: video2 }
165 playlistId: playlistWithThumbnail,
166 elementAttrs: { videoId: video2 }
167 }) 147 })
168 withThumbnailE2 = res.body.videoPlaylistElement.id 148 withThumbnailE2 = added.id
169 149
170 await reorderVideosPlaylist({ 150 await servers[1].playlists.reorderElements({
171 url: servers[1].url, 151 playlistId: playlistWithThumbnailId,
172 token: servers[1].accessToken, 152 attributes: {
173 playlistId: playlistWithThumbnail,
174 elementAttrs: {
175 startPosition: 1, 153 startPosition: 1,
176 insertAfterPosition: 2 154 insertAfterPosition: 2
177 } 155 }
@@ -188,11 +166,9 @@ describe('Playlist thumbnail', function () {
188 it('Should automatically update the thumbnail when deleting the first element', async function () { 166 it('Should automatically update the thumbnail when deleting the first element', async function () {
189 this.timeout(30000) 167 this.timeout(30000)
190 168
191 await removeVideoFromPlaylist({ 169 await servers[1].playlists.removeElement({
192 url: servers[1].url, 170 playlistId: playlistWithoutThumbnailId,
193 token: servers[1].accessToken, 171 elementId: withoutThumbnailE1
194 playlistId: playlistWithoutThumbnail,
195 playlistElementId: withoutThumbnailE1
196 }) 172 })
197 173
198 await waitJobs(servers) 174 await waitJobs(servers)
@@ -206,11 +182,9 @@ describe('Playlist thumbnail', function () {
206 it('Should not update the thumbnail when deleting the first element if we explicitly uploaded a thumbnail', async function () { 182 it('Should not update the thumbnail when deleting the first element if we explicitly uploaded a thumbnail', async function () {
207 this.timeout(30000) 183 this.timeout(30000)
208 184
209 await removeVideoFromPlaylist({ 185 await servers[1].playlists.removeElement({
210 url: servers[1].url, 186 playlistId: playlistWithThumbnailId,
211 token: servers[1].accessToken, 187 elementId: withThumbnailE1
212 playlistId: playlistWithThumbnail,
213 playlistElementId: withThumbnailE1
214 }) 188 })
215 189
216 await waitJobs(servers) 190 await waitJobs(servers)
@@ -224,11 +198,9 @@ describe('Playlist thumbnail', function () {
224 it('Should the thumbnail when we delete the last element', async function () { 198 it('Should the thumbnail when we delete the last element', async function () {
225 this.timeout(30000) 199 this.timeout(30000)
226 200
227 await removeVideoFromPlaylist({ 201 await servers[1].playlists.removeElement({
228 url: servers[1].url, 202 playlistId: playlistWithoutThumbnailId,
229 token: servers[1].accessToken, 203 elementId: withoutThumbnailE2
230 playlistId: playlistWithoutThumbnail,
231 playlistElementId: withoutThumbnailE2
232 }) 204 })
233 205
234 await waitJobs(servers) 206 await waitJobs(servers)
@@ -242,11 +214,9 @@ describe('Playlist thumbnail', function () {
242 it('Should not update the thumbnail when we delete the last element if we explicitly uploaded a thumbnail', async function () { 214 it('Should not update the thumbnail when we delete the last element if we explicitly uploaded a thumbnail', async function () {
243 this.timeout(30000) 215 this.timeout(30000)
244 216
245 await removeVideoFromPlaylist({ 217 await servers[1].playlists.removeElement({
246 url: servers[1].url, 218 playlistId: playlistWithThumbnailId,
247 token: servers[1].accessToken, 219 elementId: withThumbnailE2
248 playlistId: playlistWithThumbnail,
249 playlistElementId: withThumbnailE2
250 }) 220 })
251 221
252 await waitJobs(servers) 222 await waitJobs(servers)
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts
index da8de054b..f42aee2ff 100644
--- a/server/tests/api/videos/video-playlists.ts
+++ b/server/tests/api/videos/video-playlists.ts
@@ -2,71 +2,33 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
6import { 5import {
7 addVideoChannel,
8 addVideoInPlaylist,
9 addVideoToBlacklist,
10 checkPlaylistFilesWereRemoved, 6 checkPlaylistFilesWereRemoved,
11 cleanupTests, 7 cleanupTests,
12 createUser, 8 createMultipleServers,
13 createVideoPlaylist,
14 deleteVideoChannel,
15 deleteVideoPlaylist,
16 doubleFollow, 9 doubleFollow,
17 doVideosExistInMyPlaylist, 10 PeerTubeServer,
18 flushAndRunMultipleServers, 11 PlaylistsCommand,
19 generateUserAccessToken,
20 getAccessToken,
21 getAccountPlaylistsList,
22 getAccountPlaylistsListWithToken,
23 getMyUserInformation,
24 getPlaylistVideos,
25 getVideoChannelPlaylistsList,
26 getVideoPlaylist,
27 getVideoPlaylistPrivacies,
28 getVideoPlaylistsList,
29 getVideoPlaylistWithToken,
30 removeUser,
31 removeVideoFromBlacklist,
32 removeVideoFromPlaylist,
33 reorderVideosPlaylist,
34 ServerInfo,
35 setAccessTokensToServers, 12 setAccessTokensToServers,
36 setDefaultVideoChannel, 13 setDefaultVideoChannel,
37 testImage, 14 testImage,
38 unfollow,
39 updateVideo,
40 updateVideoPlaylist,
41 updateVideoPlaylistElement,
42 uploadVideo,
43 uploadVideoAndGetId,
44 userLogin,
45 wait, 15 wait,
46 waitJobs 16 waitJobs
47} from '../../../../shared/extra-utils' 17} from '@shared/extra-utils'
48import { 18import {
49 addAccountToAccountBlocklist, 19 HttpStatusCode,
50 addAccountToServerBlocklist, 20 VideoPlaylist,
51 addServerToAccountBlocklist, 21 VideoPlaylistCreateResult,
52 addServerToServerBlocklist, 22 VideoPlaylistElementType,
53 removeAccountFromAccountBlocklist, 23 VideoPlaylistPrivacy,
54 removeAccountFromServerBlocklist, 24 VideoPlaylistType,
55 removeServerFromAccountBlocklist, 25 VideoPrivacy
56 removeServerFromServerBlocklist 26} from '@shared/models'
57} from '../../../../shared/extra-utils/users/blocklist'
58import { User } from '../../../../shared/models/users'
59import { VideoPlaylistCreateResult, VideoPrivacy } from '../../../../shared/models/videos'
60import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
61import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
62import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
63import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
64import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
65 27
66const expect = chai.expect 28const expect = chai.expect
67 29
68async function checkPlaylistElementType ( 30async function checkPlaylistElementType (
69 servers: ServerInfo[], 31 servers: PeerTubeServer[],
70 playlistId: string, 32 playlistId: string,
71 type: VideoPlaylistElementType, 33 type: VideoPlaylistElementType,
72 position: number, 34 position: number,
@@ -74,10 +36,10 @@ async function checkPlaylistElementType (
74 total: number 36 total: number
75) { 37) {
76 for (const server of servers) { 38 for (const server of servers) {
77 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10) 39 const body = await server.playlists.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
78 expect(res.body.total).to.equal(total) 40 expect(body.total).to.equal(total)
79 41
80 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position) 42 const videoElement = body.data.find(e => e.position === position)
81 expect(videoElement.type).to.equal(type, 'On server ' + server.url) 43 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
82 44
83 if (type === VideoPlaylistElementType.REGULAR) { 45 if (type === VideoPlaylistElementType.REGULAR) {
@@ -90,11 +52,11 @@ async function checkPlaylistElementType (
90} 52}
91 53
92describe('Test video playlists', function () { 54describe('Test video playlists', function () {
93 let servers: ServerInfo[] = [] 55 let servers: PeerTubeServer[] = []
94 56
95 let playlistServer2Id1: number 57 let playlistServer2Id1: number
96 let playlistServer2Id2: number 58 let playlistServer2Id2: number
97 let playlistServer2UUID2: number 59 let playlistServer2UUID2: string
98 60
99 let playlistServer1Id: number 61 let playlistServer1Id: number
100 let playlistServer1UUID: string 62 let playlistServer1UUID: string
@@ -106,12 +68,14 @@ describe('Test video playlists', function () {
106 68
107 let nsfwVideoServer1: number 69 let nsfwVideoServer1: number
108 70
109 let userAccessTokenServer1: string 71 let userTokenServer1: string
72
73 let commands: PlaylistsCommand[]
110 74
111 before(async function () { 75 before(async function () {
112 this.timeout(120000) 76 this.timeout(120000)
113 77
114 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } }) 78 servers = await createMultipleServers(3, { transcoding: { enabled: false } })
115 79
116 // Get the access tokens 80 // Get the access tokens
117 await setAccessTokensToServers(servers) 81 await setAccessTokensToServers(servers)
@@ -122,86 +86,78 @@ describe('Test video playlists', function () {
122 // Server 1 and server 3 follow each other 86 // Server 1 and server 3 follow each other
123 await doubleFollow(servers[0], servers[2]) 87 await doubleFollow(servers[0], servers[2])
124 88
89 commands = servers.map(s => s.playlists)
90
125 { 91 {
126 servers[0].videos = [] 92 servers[0].store.videos = []
127 servers[1].videos = [] 93 servers[1].store.videos = []
128 servers[2].videos = [] 94 servers[2].store.videos = []
129 95
130 for (const server of servers) { 96 for (const server of servers) {
131 for (let i = 0; i < 7; i++) { 97 for (let i = 0; i < 7; i++) {
132 const name = `video ${i} server ${server.serverNumber}` 98 const name = `video ${i} server ${server.serverNumber}`
133 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false }) 99 const video = await server.videos.upload({ attributes: { name, nsfw: false } })
134 100
135 server.videos.push(resVideo.body.video) 101 server.store.videos.push(video)
136 } 102 }
137 } 103 }
138 } 104 }
139 105
140 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id 106 nsfwVideoServer1 = (await servers[0].videos.quickUpload({ name: 'NSFW video', nsfw: true })).id
141 107
142 { 108 userTokenServer1 = await servers[0].users.generateUserAndToken('user1')
143 await createUser({
144 url: servers[0].url,
145 accessToken: servers[0].accessToken,
146 username: 'user1',
147 password: 'password'
148 })
149 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
150 }
151 109
152 await waitJobs(servers) 110 await waitJobs(servers)
153 }) 111 })
154 112
155 describe('Get default playlists', function () { 113 describe('Get default playlists', function () {
114
156 it('Should list video playlist privacies', async function () { 115 it('Should list video playlist privacies', async function () {
157 const res = await getVideoPlaylistPrivacies(servers[0].url) 116 const privacies = await commands[0].getPrivacies()
158 117
159 const privacies = res.body
160 expect(Object.keys(privacies)).to.have.length.at.least(3) 118 expect(Object.keys(privacies)).to.have.length.at.least(3)
161
162 expect(privacies[3]).to.equal('Private') 119 expect(privacies[3]).to.equal('Private')
163 }) 120 })
164 121
165 it('Should list watch later playlist', async function () { 122 it('Should list watch later playlist', async function () {
166 const url = servers[0].url 123 const token = servers[0].accessToken
167 const accessToken = servers[0].accessToken
168 124
169 { 125 {
170 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER) 126 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
171 127
172 expect(res.body.total).to.equal(1) 128 expect(body.total).to.equal(1)
173 expect(res.body.data).to.have.lengthOf(1) 129 expect(body.data).to.have.lengthOf(1)
174 130
175 const playlist: VideoPlaylist = res.body.data[0] 131 const playlist = body.data[0]
176 expect(playlist.displayName).to.equal('Watch later') 132 expect(playlist.displayName).to.equal('Watch later')
177 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER) 133 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
178 expect(playlist.type.label).to.equal('Watch later') 134 expect(playlist.type.label).to.equal('Watch later')
179 } 135 }
180 136
181 { 137 {
182 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR) 138 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR })
183 139
184 expect(res.body.total).to.equal(0) 140 expect(body.total).to.equal(0)
185 expect(res.body.data).to.have.lengthOf(0) 141 expect(body.data).to.have.lengthOf(0)
186 } 142 }
187 143
188 { 144 {
189 const res = await getAccountPlaylistsList(url, 'root', 0, 5) 145 const body = await commands[0].listByAccount({ handle: 'root' })
190 expect(res.body.total).to.equal(0) 146 expect(body.total).to.equal(0)
191 expect(res.body.data).to.have.lengthOf(0) 147 expect(body.data).to.have.lengthOf(0)
192 } 148 }
193 }) 149 })
194 150
195 it('Should get private playlist for a classic user', async function () { 151 it('Should get private playlist for a classic user', async function () {
196 const token = await generateUserAccessToken(servers[0], 'toto') 152 const token = await servers[0].users.generateUserAndToken('toto')
197 153
198 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5) 154 const body = await commands[0].listByAccount({ token, handle: 'toto' })
199 155
200 expect(res.body.total).to.equal(1) 156 expect(body.total).to.equal(1)
201 expect(res.body.data).to.have.lengthOf(1) 157 expect(body.data).to.have.lengthOf(1)
202 158
203 const playlistId = res.body.data[0].id 159 const playlistId = body.data[0].id
204 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5) 160 await commands[0].listVideos({ token, playlistId })
205 }) 161 })
206 }) 162 })
207 163
@@ -210,15 +166,13 @@ describe('Test video playlists', function () {
210 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () { 166 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
211 this.timeout(30000) 167 this.timeout(30000)
212 168
213 await createVideoPlaylist({ 169 await commands[0].create({
214 url: servers[0].url, 170 attributes: {
215 token: servers[0].accessToken,
216 playlistAttrs: {
217 displayName: 'my super playlist', 171 displayName: 'my super playlist',
218 privacy: VideoPlaylistPrivacy.PUBLIC, 172 privacy: VideoPlaylistPrivacy.PUBLIC,
219 description: 'my super description', 173 description: 'my super description',
220 thumbnailfile: 'thumbnail.jpg', 174 thumbnailfile: 'thumbnail.jpg',
221 videoChannelId: servers[0].videoChannel.id 175 videoChannelId: servers[0].store.channel.id
222 } 176 }
223 }) 177 })
224 178
@@ -227,14 +181,13 @@ describe('Test video playlists', function () {
227 await wait(3000) 181 await wait(3000)
228 182
229 for (const server of servers) { 183 for (const server of servers) {
230 const res = await getVideoPlaylistsList(server.url, 0, 5) 184 const body = await server.playlists.list({ start: 0, count: 5 })
231 expect(res.body.total).to.equal(1) 185 expect(body.total).to.equal(1)
232 expect(res.body.data).to.have.lengthOf(1) 186 expect(body.data).to.have.lengthOf(1)
233 187
234 const playlistFromList = res.body.data[0] as VideoPlaylist 188 const playlistFromList = body.data[0]
235 189
236 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid) 190 const playlistFromGet = await server.playlists.get({ playlistId: playlistFromList.uuid })
237 const playlistFromGet = res2.body as VideoPlaylist
238 191
239 for (const playlist of [ playlistFromGet, playlistFromList ]) { 192 for (const playlist of [ playlistFromGet, playlistFromList ]) {
240 expect(playlist.id).to.be.a('number') 193 expect(playlist.id).to.be.a('number')
@@ -264,46 +217,38 @@ describe('Test video playlists', function () {
264 this.timeout(30000) 217 this.timeout(30000)
265 218
266 { 219 {
267 const res = await createVideoPlaylist({ 220 const playlist = await servers[1].playlists.create({
268 url: servers[1].url, 221 attributes: {
269 token: servers[1].accessToken,
270 playlistAttrs: {
271 displayName: 'playlist 2', 222 displayName: 'playlist 2',
272 privacy: VideoPlaylistPrivacy.PUBLIC, 223 privacy: VideoPlaylistPrivacy.PUBLIC,
273 videoChannelId: servers[1].videoChannel.id 224 videoChannelId: servers[1].store.channel.id
274 } 225 }
275 }) 226 })
276 playlistServer2Id1 = res.body.videoPlaylist.id 227 playlistServer2Id1 = playlist.id
277 } 228 }
278 229
279 { 230 {
280 const res = await createVideoPlaylist({ 231 const playlist = await servers[1].playlists.create({
281 url: servers[1].url, 232 attributes: {
282 token: servers[1].accessToken,
283 playlistAttrs: {
284 displayName: 'playlist 3', 233 displayName: 'playlist 3',
285 privacy: VideoPlaylistPrivacy.PUBLIC, 234 privacy: VideoPlaylistPrivacy.PUBLIC,
286 thumbnailfile: 'thumbnail.jpg', 235 thumbnailfile: 'thumbnail.jpg',
287 videoChannelId: servers[1].videoChannel.id 236 videoChannelId: servers[1].store.channel.id
288 } 237 }
289 }) 238 })
290 239
291 playlistServer2Id2 = res.body.videoPlaylist.id 240 playlistServer2Id2 = playlist.id
292 playlistServer2UUID2 = res.body.videoPlaylist.uuid 241 playlistServer2UUID2 = playlist.uuid
293 } 242 }
294 243
295 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) { 244 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
296 await addVideoInPlaylist({ 245 await servers[1].playlists.addElement({
297 url: servers[1].url,
298 token: servers[1].accessToken,
299 playlistId: id, 246 playlistId: id,
300 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 } 247 attributes: { videoId: servers[1].store.videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
301 }) 248 })
302 await addVideoInPlaylist({ 249 await servers[1].playlists.addElement({
303 url: servers[1].url,
304 token: servers[1].accessToken,
305 playlistId: id, 250 playlistId: id,
306 elementAttrs: { videoId: servers[1].videos[1].id } 251 attributes: { videoId: servers[1].store.videos[1].id }
307 }) 252 })
308 } 253 }
309 254
@@ -311,20 +256,20 @@ describe('Test video playlists', function () {
311 await wait(3000) 256 await wait(3000)
312 257
313 for (const server of [ servers[0], servers[1] ]) { 258 for (const server of [ servers[0], servers[1] ]) {
314 const res = await getVideoPlaylistsList(server.url, 0, 5) 259 const body = await server.playlists.list({ start: 0, count: 5 })
315 260
316 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2') 261 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
317 expect(playlist2).to.not.be.undefined 262 expect(playlist2).to.not.be.undefined
318 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath) 263 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
319 264
320 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3') 265 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
321 expect(playlist3).to.not.be.undefined 266 expect(playlist3).to.not.be.undefined
322 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath) 267 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
323 } 268 }
324 269
325 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 270 const body = await servers[2].playlists.list({ start: 0, count: 5 })
326 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined 271 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
327 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined 272 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
328 }) 273 })
329 274
330 it('Should have the playlist on server 3 after a new follow', async function () { 275 it('Should have the playlist on server 3 after a new follow', async function () {
@@ -333,13 +278,13 @@ describe('Test video playlists', function () {
333 // Server 2 and server 3 follow each other 278 // Server 2 and server 3 follow each other
334 await doubleFollow(servers[1], servers[2]) 279 await doubleFollow(servers[1], servers[2])
335 280
336 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 281 const body = await servers[2].playlists.list({ start: 0, count: 5 })
337 282
338 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2') 283 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
339 expect(playlist2).to.not.be.undefined 284 expect(playlist2).to.not.be.undefined
340 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath) 285 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
341 286
342 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined 287 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
343 }) 288 })
344 }) 289 })
345 290
@@ -349,22 +294,20 @@ describe('Test video playlists', function () {
349 this.timeout(30000) 294 this.timeout(30000)
350 295
351 { 296 {
352 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt') 297 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: 'createdAt' })
353 298 expect(body.total).to.equal(3)
354 expect(res.body.total).to.equal(3)
355 299
356 const data: VideoPlaylist[] = res.body.data 300 const data = body.data
357 expect(data).to.have.lengthOf(2) 301 expect(data).to.have.lengthOf(2)
358 expect(data[0].displayName).to.equal('playlist 2') 302 expect(data[0].displayName).to.equal('playlist 2')
359 expect(data[1].displayName).to.equal('playlist 3') 303 expect(data[1].displayName).to.equal('playlist 3')
360 } 304 }
361 305
362 { 306 {
363 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt') 307 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: '-createdAt' })
308 expect(body.total).to.equal(3)
364 309
365 expect(res.body.total).to.equal(3) 310 const data = body.data
366
367 const data: VideoPlaylist[] = res.body.data
368 expect(data).to.have.lengthOf(2) 311 expect(data).to.have.lengthOf(2)
369 expect(data[0].displayName).to.equal('playlist 2') 312 expect(data[0].displayName).to.equal('playlist 2')
370 expect(data[1].displayName).to.equal('my super playlist') 313 expect(data[1].displayName).to.equal('my super playlist')
@@ -375,11 +318,10 @@ describe('Test video playlists', function () {
375 this.timeout(30000) 318 this.timeout(30000)
376 319
377 { 320 {
378 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt') 321 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
379 322 expect(body.total).to.equal(1)
380 expect(res.body.total).to.equal(1)
381 323
382 const data: VideoPlaylist[] = res.body.data 324 const data = body.data
383 expect(data).to.have.lengthOf(1) 325 expect(data).to.have.lengthOf(1)
384 expect(data[0].displayName).to.equal('my super playlist') 326 expect(data[0].displayName).to.equal('my super playlist')
385 } 327 }
@@ -389,41 +331,37 @@ describe('Test video playlists', function () {
389 this.timeout(30000) 331 this.timeout(30000)
390 332
391 { 333 {
392 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt') 334 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
335 expect(body.total).to.equal(2)
393 336
394 expect(res.body.total).to.equal(2) 337 const data = body.data
395
396 const data: VideoPlaylist[] = res.body.data
397 expect(data).to.have.lengthOf(1) 338 expect(data).to.have.lengthOf(1)
398 expect(data[0].displayName).to.equal('playlist 2') 339 expect(data[0].displayName).to.equal('playlist 2')
399 } 340 }
400 341
401 { 342 {
402 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, 'createdAt') 343 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
403 344 expect(body.total).to.equal(2)
404 expect(res.body.total).to.equal(2)
405 345
406 const data: VideoPlaylist[] = res.body.data 346 const data = body.data
407 expect(data).to.have.lengthOf(1) 347 expect(data).to.have.lengthOf(1)
408 expect(data[0].displayName).to.equal('playlist 3') 348 expect(data[0].displayName).to.equal('playlist 3')
409 } 349 }
410 350
411 { 351 {
412 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3') 352 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
353 expect(body.total).to.equal(1)
413 354
414 expect(res.body.total).to.equal(1) 355 const data = body.data
415
416 const data: VideoPlaylist[] = res.body.data
417 expect(data).to.have.lengthOf(1) 356 expect(data).to.have.lengthOf(1)
418 expect(data[0].displayName).to.equal('playlist 3') 357 expect(data[0].displayName).to.equal('playlist 3')
419 } 358 }
420 359
421 { 360 {
422 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '4') 361 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
423 362 expect(body.total).to.equal(0)
424 expect(res.body.total).to.equal(0)
425 363
426 const data: VideoPlaylist[] = res.body.data 364 const data = body.data
427 expect(data).to.have.lengthOf(0) 365 expect(data).to.have.lengthOf(0)
428 } 366 }
429 }) 367 })
@@ -437,28 +375,22 @@ describe('Test video playlists', function () {
437 this.timeout(30000) 375 this.timeout(30000)
438 376
439 { 377 {
440 const res = await createVideoPlaylist({ 378 unlistedPlaylist = await servers[1].playlists.create({
441 url: servers[1].url, 379 attributes: {
442 token: servers[1].accessToken,
443 playlistAttrs: {
444 displayName: 'playlist unlisted', 380 displayName: 'playlist unlisted',
445 privacy: VideoPlaylistPrivacy.UNLISTED, 381 privacy: VideoPlaylistPrivacy.UNLISTED,
446 videoChannelId: servers[1].videoChannel.id 382 videoChannelId: servers[1].store.channel.id
447 } 383 }
448 }) 384 })
449 unlistedPlaylist = res.body.videoPlaylist
450 } 385 }
451 386
452 { 387 {
453 const res = await createVideoPlaylist({ 388 privatePlaylist = await servers[1].playlists.create({
454 url: servers[1].url, 389 attributes: {
455 token: servers[1].accessToken,
456 playlistAttrs: {
457 displayName: 'playlist private', 390 displayName: 'playlist private',
458 privacy: VideoPlaylistPrivacy.PRIVATE 391 privacy: VideoPlaylistPrivacy.PRIVATE
459 } 392 }
460 }) 393 })
461 privatePlaylist = res.body.videoPlaylist
462 } 394 }
463 395
464 await waitJobs(servers) 396 await waitJobs(servers)
@@ -468,15 +400,15 @@ describe('Test video playlists', function () {
468 it('Should not list unlisted or private playlists', async function () { 400 it('Should not list unlisted or private playlists', async function () {
469 for (const server of servers) { 401 for (const server of servers) {
470 const results = [ 402 const results = [
471 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'), 403 await server.playlists.listByAccount({ handle: 'root@localhost:' + servers[1].port, sort: '-createdAt' }),
472 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt') 404 await server.playlists.list({ start: 0, count: 2, sort: '-createdAt' })
473 ] 405 ]
474 406
475 expect(results[0].body.total).to.equal(2) 407 expect(results[0].total).to.equal(2)
476 expect(results[1].body.total).to.equal(3) 408 expect(results[1].total).to.equal(3)
477 409
478 for (const res of results) { 410 for (const body of results) {
479 const data: VideoPlaylist[] = res.body.data 411 const data = body.data
480 expect(data).to.have.lengthOf(2) 412 expect(data).to.have.lengthOf(2)
481 expect(data[0].displayName).to.equal('playlist 3') 413 expect(data[0].displayName).to.equal('playlist 3')
482 expect(data[1].displayName).to.equal('playlist 2') 414 expect(data[1].displayName).to.equal('playlist 2')
@@ -485,23 +417,23 @@ describe('Test video playlists', function () {
485 }) 417 })
486 418
487 it('Should not get unlisted playlist using only the id', async function () { 419 it('Should not get unlisted playlist using only the id', async function () {
488 await getVideoPlaylist(servers[1].url, unlistedPlaylist.id, 404) 420 await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
489 }) 421 })
490 422
491 it('Should get unlisted plyaylist using uuid or shortUUID', async function () { 423 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
492 await getVideoPlaylist(servers[1].url, unlistedPlaylist.uuid) 424 await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid })
493 await getVideoPlaylist(servers[1].url, unlistedPlaylist.shortUUID) 425 await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID })
494 }) 426 })
495 427
496 it('Should not get private playlist without token', async function () { 428 it('Should not get private playlist without token', async function () {
497 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) { 429 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
498 await getVideoPlaylist(servers[1].url, id, 401) 430 await servers[1].playlists.get({ playlistId: id, expectedStatus: 401 })
499 } 431 }
500 }) 432 })
501 433
502 it('Should get private playlist with a token', async function () { 434 it('Should get private playlist with a token', async function () {
503 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) { 435 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
504 await getVideoPlaylistWithToken(servers[1].url, servers[1].accessToken, id) 436 await servers[1].playlists.get({ token: servers[1].accessToken, playlistId: id })
505 } 437 }
506 }) 438 })
507 }) 439 })
@@ -511,15 +443,13 @@ describe('Test video playlists', function () {
511 it('Should update a playlist', async function () { 443 it('Should update a playlist', async function () {
512 this.timeout(30000) 444 this.timeout(30000)
513 445
514 await updateVideoPlaylist({ 446 await servers[1].playlists.update({
515 url: servers[1].url, 447 attributes: {
516 token: servers[1].accessToken,
517 playlistAttrs: {
518 displayName: 'playlist 3 updated', 448 displayName: 'playlist 3 updated',
519 description: 'description updated', 449 description: 'description updated',
520 privacy: VideoPlaylistPrivacy.UNLISTED, 450 privacy: VideoPlaylistPrivacy.UNLISTED,
521 thumbnailfile: 'thumbnail.jpg', 451 thumbnailfile: 'thumbnail.jpg',
522 videoChannelId: servers[1].videoChannel.id 452 videoChannelId: servers[1].store.channel.id
523 }, 453 },
524 playlistId: playlistServer2Id2 454 playlistId: playlistServer2Id2
525 }) 455 })
@@ -527,8 +457,7 @@ describe('Test video playlists', function () {
527 await waitJobs(servers) 457 await waitJobs(servers)
528 458
529 for (const server of servers) { 459 for (const server of servers) {
530 const res = await getVideoPlaylist(server.url, playlistServer2UUID2) 460 const playlist = await server.playlists.get({ playlistId: playlistServer2UUID2 })
531 const playlist: VideoPlaylist = res.body
532 461
533 expect(playlist.displayName).to.equal('playlist 3 updated') 462 expect(playlist.displayName).to.equal('playlist 3 updated')
534 expect(playlist.description).to.equal('description updated') 463 expect(playlist.description).to.equal('description updated')
@@ -554,39 +483,37 @@ describe('Test video playlists', function () {
554 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () { 483 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
555 this.timeout(30000) 484 this.timeout(30000)
556 485
557 const addVideo = (elementAttrs: any) => { 486 const addVideo = (attributes: any) => {
558 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs }) 487 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
559 } 488 }
560 489
561 const res = await createVideoPlaylist({ 490 const playlist = await commands[0].create({
562 url: servers[0].url, 491 attributes: {
563 token: servers[0].accessToken,
564 playlistAttrs: {
565 displayName: 'playlist 4', 492 displayName: 'playlist 4',
566 privacy: VideoPlaylistPrivacy.PUBLIC, 493 privacy: VideoPlaylistPrivacy.PUBLIC,
567 videoChannelId: servers[0].videoChannel.id 494 videoChannelId: servers[0].store.channel.id
568 } 495 }
569 }) 496 })
570 497
571 playlistServer1Id = res.body.videoPlaylist.id 498 playlistServer1Id = playlist.id
572 playlistServer1UUID = res.body.videoPlaylist.uuid 499 playlistServer1UUID = playlist.uuid
573 500
574 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 }) 501 await addVideo({ videoId: servers[0].store.videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
575 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 }) 502 await addVideo({ videoId: servers[2].store.videos[1].uuid, startTimestamp: 35 })
576 await addVideo({ videoId: servers[2].videos[2].uuid }) 503 await addVideo({ videoId: servers[2].store.videos[2].uuid })
577 { 504 {
578 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 }) 505 const element = await addVideo({ videoId: servers[0].store.videos[3].uuid, stopTimestamp: 35 })
579 playlistElementServer1Video4 = res.body.videoPlaylistElement.id 506 playlistElementServer1Video4 = element.id
580 } 507 }
581 508
582 { 509 {
583 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 }) 510 const element = await addVideo({ videoId: servers[0].store.videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
584 playlistElementServer1Video5 = res.body.videoPlaylistElement.id 511 playlistElementServer1Video5 = element.id
585 } 512 }
586 513
587 { 514 {
588 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 }) 515 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
589 playlistElementNSFW = res.body.videoPlaylistElement.id 516 playlistElementNSFW = element.id
590 517
591 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 }) 518 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
592 await addVideo({ videoId: nsfwVideoServer1 }) 519 await addVideo({ videoId: nsfwVideoServer1 })
@@ -599,64 +526,68 @@ describe('Test video playlists', function () {
599 this.timeout(30000) 526 this.timeout(30000)
600 527
601 for (const server of servers) { 528 for (const server of servers) {
602 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 529 {
603 530 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
604 expect(res.body.total).to.equal(8) 531
605 532 expect(body.total).to.equal(8)
606 const videoElements: VideoPlaylistElement[] = res.body.data 533
607 expect(videoElements).to.have.lengthOf(8) 534 const videoElements = body.data
608 535 expect(videoElements).to.have.lengthOf(8)
609 expect(videoElements[0].video.name).to.equal('video 0 server 1') 536
610 expect(videoElements[0].position).to.equal(1) 537 expect(videoElements[0].video.name).to.equal('video 0 server 1')
611 expect(videoElements[0].startTimestamp).to.equal(15) 538 expect(videoElements[0].position).to.equal(1)
612 expect(videoElements[0].stopTimestamp).to.equal(28) 539 expect(videoElements[0].startTimestamp).to.equal(15)
613 540 expect(videoElements[0].stopTimestamp).to.equal(28)
614 expect(videoElements[1].video.name).to.equal('video 1 server 3') 541
615 expect(videoElements[1].position).to.equal(2) 542 expect(videoElements[1].video.name).to.equal('video 1 server 3')
616 expect(videoElements[1].startTimestamp).to.equal(35) 543 expect(videoElements[1].position).to.equal(2)
617 expect(videoElements[1].stopTimestamp).to.be.null 544 expect(videoElements[1].startTimestamp).to.equal(35)
618 545 expect(videoElements[1].stopTimestamp).to.be.null
619 expect(videoElements[2].video.name).to.equal('video 2 server 3') 546
620 expect(videoElements[2].position).to.equal(3) 547 expect(videoElements[2].video.name).to.equal('video 2 server 3')
621 expect(videoElements[2].startTimestamp).to.be.null 548 expect(videoElements[2].position).to.equal(3)
622 expect(videoElements[2].stopTimestamp).to.be.null 549 expect(videoElements[2].startTimestamp).to.be.null
623 550 expect(videoElements[2].stopTimestamp).to.be.null
624 expect(videoElements[3].video.name).to.equal('video 3 server 1') 551
625 expect(videoElements[3].position).to.equal(4) 552 expect(videoElements[3].video.name).to.equal('video 3 server 1')
626 expect(videoElements[3].startTimestamp).to.be.null 553 expect(videoElements[3].position).to.equal(4)
627 expect(videoElements[3].stopTimestamp).to.equal(35) 554 expect(videoElements[3].startTimestamp).to.be.null
628 555 expect(videoElements[3].stopTimestamp).to.equal(35)
629 expect(videoElements[4].video.name).to.equal('video 4 server 1') 556
630 expect(videoElements[4].position).to.equal(5) 557 expect(videoElements[4].video.name).to.equal('video 4 server 1')
631 expect(videoElements[4].startTimestamp).to.equal(45) 558 expect(videoElements[4].position).to.equal(5)
632 expect(videoElements[4].stopTimestamp).to.equal(60) 559 expect(videoElements[4].startTimestamp).to.equal(45)
633 560 expect(videoElements[4].stopTimestamp).to.equal(60)
634 expect(videoElements[5].video.name).to.equal('NSFW video') 561
635 expect(videoElements[5].position).to.equal(6) 562 expect(videoElements[5].video.name).to.equal('NSFW video')
636 expect(videoElements[5].startTimestamp).to.equal(5) 563 expect(videoElements[5].position).to.equal(6)
637 expect(videoElements[5].stopTimestamp).to.be.null 564 expect(videoElements[5].startTimestamp).to.equal(5)
638 565 expect(videoElements[5].stopTimestamp).to.be.null
639 expect(videoElements[6].video.name).to.equal('NSFW video') 566
640 expect(videoElements[6].position).to.equal(7) 567 expect(videoElements[6].video.name).to.equal('NSFW video')
641 expect(videoElements[6].startTimestamp).to.equal(4) 568 expect(videoElements[6].position).to.equal(7)
642 expect(videoElements[6].stopTimestamp).to.be.null 569 expect(videoElements[6].startTimestamp).to.equal(4)
643 570 expect(videoElements[6].stopTimestamp).to.be.null
644 expect(videoElements[7].video.name).to.equal('NSFW video') 571
645 expect(videoElements[7].position).to.equal(8) 572 expect(videoElements[7].video.name).to.equal('NSFW video')
646 expect(videoElements[7].startTimestamp).to.be.null 573 expect(videoElements[7].position).to.equal(8)
647 expect(videoElements[7].stopTimestamp).to.be.null 574 expect(videoElements[7].startTimestamp).to.be.null
648 575 expect(videoElements[7].stopTimestamp).to.be.null
649 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2) 576 }
650 expect(res3.body.data).to.have.lengthOf(2) 577
578 {
579 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
580 expect(body.data).to.have.lengthOf(2)
581 }
651 } 582 }
652 }) 583 })
653 }) 584 })
654 585
655 describe('Element type', function () { 586 describe('Element type', function () {
656 let groupUser1: ServerInfo[] 587 let groupUser1: PeerTubeServer[]
657 let groupWithoutToken1: ServerInfo[] 588 let groupWithoutToken1: PeerTubeServer[]
658 let group1: ServerInfo[] 589 let group1: PeerTubeServer[]
659 let group2: ServerInfo[] 590 let group2: PeerTubeServer[]
660 591
661 let video1: string 592 let video1: string
662 let video2: string 593 let video2: string
@@ -665,31 +596,30 @@ describe('Test video playlists', function () {
665 before(async function () { 596 before(async function () {
666 this.timeout(60000) 597 this.timeout(60000)
667 598
668 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ] 599 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
669 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ] 600 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
670 group1 = [ servers[0] ] 601 group1 = [ servers[0] ]
671 group2 = [ servers[1], servers[2] ] 602 group2 = [ servers[1], servers[2] ]
672 603
673 const res = await createVideoPlaylist({ 604 const playlist = await commands[0].create({
674 url: servers[0].url, 605 token: userTokenServer1,
675 token: userAccessTokenServer1, 606 attributes: {
676 playlistAttrs: {
677 displayName: 'playlist 56', 607 displayName: 'playlist 56',
678 privacy: VideoPlaylistPrivacy.PUBLIC, 608 privacy: VideoPlaylistPrivacy.PUBLIC,
679 videoChannelId: servers[0].videoChannel.id 609 videoChannelId: servers[0].store.channel.id
680 } 610 }
681 }) 611 })
682 612
683 const playlistServer1Id2 = res.body.videoPlaylist.id 613 const playlistServer1Id2 = playlist.id
684 playlistServer1UUID2 = res.body.videoPlaylist.uuid 614 playlistServer1UUID2 = playlist.uuid
685 615
686 const addVideo = (elementAttrs: any) => { 616 const addVideo = (attributes: any) => {
687 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs }) 617 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
688 } 618 }
689 619
690 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid 620 video1 = (await servers[0].videos.quickUpload({ name: 'video 89', token: userTokenServer1 })).uuid
691 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid 621 video2 = (await servers[1].videos.quickUpload({ name: 'video 90' })).uuid
692 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid 622 video3 = (await servers[0].videos.quickUpload({ name: 'video 91', nsfw: true })).uuid
693 623
694 await waitJobs(servers) 624 await waitJobs(servers)
695 625
@@ -707,7 +637,7 @@ describe('Test video playlists', function () {
707 const position = 1 637 const position = 1
708 638
709 { 639 {
710 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE }) 640 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PRIVATE } })
711 await waitJobs(servers) 641 await waitJobs(servers)
712 642
713 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 643 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -717,7 +647,7 @@ describe('Test video playlists', function () {
717 } 647 }
718 648
719 { 649 {
720 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC }) 650 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } })
721 await waitJobs(servers) 651 await waitJobs(servers)
722 652
723 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 653 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -735,7 +665,7 @@ describe('Test video playlists', function () {
735 const position = 1 665 const position = 1
736 666
737 { 667 {
738 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true) 668 await servers[0].blacklist.add({ videoId: video1, reason: 'reason', unfederate: true })
739 await waitJobs(servers) 669 await waitJobs(servers)
740 670
741 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 671 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -745,7 +675,7 @@ describe('Test video playlists', function () {
745 } 675 }
746 676
747 { 677 {
748 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1) 678 await servers[0].blacklist.remove({ videoId: video1 })
749 await waitJobs(servers) 679 await waitJobs(servers)
750 680
751 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 681 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -759,56 +689,58 @@ describe('Test video playlists', function () {
759 it('Should update the element type if the account or server of the video is blocked', async function () { 689 it('Should update the element type if the account or server of the video is blocked', async function () {
760 this.timeout(90000) 690 this.timeout(90000)
761 691
692 const command = servers[0].blocklist
693
762 const name = 'video 90' 694 const name = 'video 90'
763 const position = 2 695 const position = 2
764 696
765 { 697 {
766 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port) 698 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
767 await waitJobs(servers) 699 await waitJobs(servers)
768 700
769 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 701 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
770 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 702 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
771 703
772 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port) 704 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
773 await waitJobs(servers) 705 await waitJobs(servers)
774 706
775 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 707 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
776 } 708 }
777 709
778 { 710 {
779 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port) 711 await command.addToMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
780 await waitJobs(servers) 712 await waitJobs(servers)
781 713
782 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 714 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
783 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 715 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
784 716
785 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port) 717 await command.removeFromMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
786 await waitJobs(servers) 718 await waitJobs(servers)
787 719
788 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 720 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
789 } 721 }
790 722
791 { 723 {
792 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port) 724 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
793 await waitJobs(servers) 725 await waitJobs(servers)
794 726
795 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 727 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
796 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 728 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
797 729
798 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port) 730 await command.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
799 await waitJobs(servers) 731 await waitJobs(servers)
800 732
801 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 733 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
802 } 734 }
803 735
804 { 736 {
805 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 737 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
806 await waitJobs(servers) 738 await waitJobs(servers)
807 739
808 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3) 740 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
809 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 741 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
810 742
811 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 743 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
812 await waitJobs(servers) 744 await waitJobs(servers)
813 745
814 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3) 746 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
@@ -816,10 +748,10 @@ describe('Test video playlists', function () {
816 }) 748 })
817 749
818 it('Should hide the video if it is NSFW', async function () { 750 it('Should hide the video if it is NSFW', async function () {
819 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false }) 751 const body = await commands[0].listVideos({ token: userTokenServer1, playlistId: playlistServer1UUID2, query: { nsfw: 'false' } })
820 expect(res.body.total).to.equal(3) 752 expect(body.total).to.equal(3)
821 753
822 const elements: VideoPlaylistElement[] = res.body.data 754 const elements = body.data
823 const element = elements.find(e => e.position === 3) 755 const element = elements.find(e => e.position === 3)
824 756
825 expect(element).to.exist 757 expect(element).to.exist
@@ -835,11 +767,9 @@ describe('Test video playlists', function () {
835 this.timeout(30000) 767 this.timeout(30000)
836 768
837 { 769 {
838 await reorderVideosPlaylist({ 770 await commands[0].reorderElements({
839 url: servers[0].url,
840 token: servers[0].accessToken,
841 playlistId: playlistServer1Id, 771 playlistId: playlistServer1Id,
842 elementAttrs: { 772 attributes: {
843 startPosition: 2, 773 startPosition: 2,
844 insertAfterPosition: 3 774 insertAfterPosition: 3
845 } 775 }
@@ -848,8 +778,8 @@ describe('Test video playlists', function () {
848 await waitJobs(servers) 778 await waitJobs(servers)
849 779
850 for (const server of servers) { 780 for (const server of servers) {
851 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 781 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
852 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name) 782 const names = body.data.map(v => v.video.name)
853 783
854 expect(names).to.deep.equal([ 784 expect(names).to.deep.equal([
855 'video 0 server 1', 785 'video 0 server 1',
@@ -865,11 +795,9 @@ describe('Test video playlists', function () {
865 } 795 }
866 796
867 { 797 {
868 await reorderVideosPlaylist({ 798 await commands[0].reorderElements({
869 url: servers[0].url,
870 token: servers[0].accessToken,
871 playlistId: playlistServer1Id, 799 playlistId: playlistServer1Id,
872 elementAttrs: { 800 attributes: {
873 startPosition: 1, 801 startPosition: 1,
874 reorderLength: 3, 802 reorderLength: 3,
875 insertAfterPosition: 4 803 insertAfterPosition: 4
@@ -879,8 +807,8 @@ describe('Test video playlists', function () {
879 await waitJobs(servers) 807 await waitJobs(servers)
880 808
881 for (const server of servers) { 809 for (const server of servers) {
882 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 810 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
883 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name) 811 const names = body.data.map(v => v.video.name)
884 812
885 expect(names).to.deep.equal([ 813 expect(names).to.deep.equal([
886 'video 3 server 1', 814 'video 3 server 1',
@@ -896,11 +824,9 @@ describe('Test video playlists', function () {
896 } 824 }
897 825
898 { 826 {
899 await reorderVideosPlaylist({ 827 await commands[0].reorderElements({
900 url: servers[0].url,
901 token: servers[0].accessToken,
902 playlistId: playlistServer1Id, 828 playlistId: playlistServer1Id,
903 elementAttrs: { 829 attributes: {
904 startPosition: 6, 830 startPosition: 6,
905 insertAfterPosition: 3 831 insertAfterPosition: 3
906 } 832 }
@@ -909,8 +835,7 @@ describe('Test video playlists', function () {
909 await waitJobs(servers) 835 await waitJobs(servers)
910 836
911 for (const server of servers) { 837 for (const server of servers) {
912 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 838 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
913 const elements: VideoPlaylistElement[] = res.body.data
914 const names = elements.map(v => v.video.name) 839 const names = elements.map(v => v.video.name)
915 840
916 expect(names).to.deep.equal([ 841 expect(names).to.deep.equal([
@@ -934,22 +859,18 @@ describe('Test video playlists', function () {
934 it('Should update startTimestamp/endTimestamp of some elements', async function () { 859 it('Should update startTimestamp/endTimestamp of some elements', async function () {
935 this.timeout(30000) 860 this.timeout(30000)
936 861
937 await updateVideoPlaylistElement({ 862 await commands[0].updateElement({
938 url: servers[0].url,
939 token: servers[0].accessToken,
940 playlistId: playlistServer1Id, 863 playlistId: playlistServer1Id,
941 playlistElementId: playlistElementServer1Video4, 864 elementId: playlistElementServer1Video4,
942 elementAttrs: { 865 attributes: {
943 startTimestamp: 1 866 startTimestamp: 1
944 } 867 }
945 }) 868 })
946 869
947 await updateVideoPlaylistElement({ 870 await commands[0].updateElement({
948 url: servers[0].url,
949 token: servers[0].accessToken,
950 playlistId: playlistServer1Id, 871 playlistId: playlistServer1Id,
951 playlistElementId: playlistElementServer1Video5, 872 elementId: playlistElementServer1Video5,
952 elementAttrs: { 873 attributes: {
953 stopTimestamp: null 874 stopTimestamp: null
954 } 875 }
955 }) 876 })
@@ -957,8 +878,7 @@ describe('Test video playlists', function () {
957 await waitJobs(servers) 878 await waitJobs(servers)
958 879
959 for (const server of servers) { 880 for (const server of servers) {
960 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 881 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
961 const elements: VideoPlaylistElement[] = res.body.data
962 882
963 expect(elements[0].video.name).to.equal('video 3 server 1') 883 expect(elements[0].video.name).to.equal('video 3 server 1')
964 expect(elements[0].position).to.equal(1) 884 expect(elements[0].position).to.equal(1)
@@ -974,17 +894,16 @@ describe('Test video playlists', function () {
974 894
975 it('Should check videos existence in my playlist', async function () { 895 it('Should check videos existence in my playlist', async function () {
976 const videoIds = [ 896 const videoIds = [
977 servers[0].videos[0].id, 897 servers[0].store.videos[0].id,
978 42000, 898 42000,
979 servers[0].videos[3].id, 899 servers[0].store.videos[3].id,
980 43000, 900 43000,
981 servers[0].videos[4].id 901 servers[0].store.videos[4].id
982 ] 902 ]
983 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds) 903 const obj = await commands[0].videosExist({ videoIds })
984 const obj = res.body as VideoExistInPlaylist
985 904
986 { 905 {
987 const elem = obj[servers[0].videos[0].id] 906 const elem = obj[servers[0].store.videos[0].id]
988 expect(elem).to.have.lengthOf(1) 907 expect(elem).to.have.lengthOf(1)
989 expect(elem[0].playlistElementId).to.exist 908 expect(elem[0].playlistElementId).to.exist
990 expect(elem[0].playlistId).to.equal(playlistServer1Id) 909 expect(elem[0].playlistId).to.equal(playlistServer1Id)
@@ -993,7 +912,7 @@ describe('Test video playlists', function () {
993 } 912 }
994 913
995 { 914 {
996 const elem = obj[servers[0].videos[3].id] 915 const elem = obj[servers[0].store.videos[3].id]
997 expect(elem).to.have.lengthOf(1) 916 expect(elem).to.have.lengthOf(1)
998 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4) 917 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
999 expect(elem[0].playlistId).to.equal(playlistServer1Id) 918 expect(elem[0].playlistId).to.equal(playlistServer1Id)
@@ -1002,7 +921,7 @@ describe('Test video playlists', function () {
1002 } 921 }
1003 922
1004 { 923 {
1005 const elem = obj[servers[0].videos[4].id] 924 const elem = obj[servers[0].store.videos[4].id]
1006 expect(elem).to.have.lengthOf(1) 925 expect(elem).to.have.lengthOf(1)
1007 expect(elem[0].playlistId).to.equal(playlistServer1Id) 926 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1008 expect(elem[0].startTimestamp).to.equal(45) 927 expect(elem[0].startTimestamp).to.equal(45)
@@ -1015,42 +934,29 @@ describe('Test video playlists', function () {
1015 934
1016 it('Should automatically update updatedAt field of playlists', async function () { 935 it('Should automatically update updatedAt field of playlists', async function () {
1017 const server = servers[1] 936 const server = servers[1]
1018 const videoId = servers[1].videos[5].id 937 const videoId = servers[1].store.videos[5].id
1019 938
1020 async function getPlaylistNames () { 939 async function getPlaylistNames () {
1021 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt') 940 const { data } = await server.playlists.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
1022 941
1023 return (res.body.data as VideoPlaylist[]).map(p => p.displayName) 942 return data.map(p => p.displayName)
1024 } 943 }
1025 944
1026 const elementAttrs = { videoId } 945 const attributes = { videoId }
1027 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs }) 946 const element1 = await server.playlists.addElement({ playlistId: playlistServer2Id1, attributes })
1028 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs }) 947 const element2 = await server.playlists.addElement({ playlistId: playlistServer2Id2, attributes })
1029
1030 const element1 = res1.body.videoPlaylistElement.id
1031 const element2 = res2.body.videoPlaylistElement.id
1032 948
1033 const names1 = await getPlaylistNames() 949 const names1 = await getPlaylistNames()
1034 expect(names1[0]).to.equal('playlist 3 updated') 950 expect(names1[0]).to.equal('playlist 3 updated')
1035 expect(names1[1]).to.equal('playlist 2') 951 expect(names1[1]).to.equal('playlist 2')
1036 952
1037 await removeVideoFromPlaylist({ 953 await server.playlists.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
1038 url: server.url,
1039 token: server.accessToken,
1040 playlistId: playlistServer2Id1,
1041 playlistElementId: element1
1042 })
1043 954
1044 const names2 = await getPlaylistNames() 955 const names2 = await getPlaylistNames()
1045 expect(names2[0]).to.equal('playlist 2') 956 expect(names2[0]).to.equal('playlist 2')
1046 expect(names2[1]).to.equal('playlist 3 updated') 957 expect(names2[1]).to.equal('playlist 3 updated')
1047 958
1048 await removeVideoFromPlaylist({ 959 await server.playlists.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
1049 url: server.url,
1050 token: server.accessToken,
1051 playlistId: playlistServer2Id2,
1052 playlistElementId: element2
1053 })
1054 960
1055 const names3 = await getPlaylistNames() 961 const names3 = await getPlaylistNames()
1056 expect(names3[0]).to.equal('playlist 3 updated') 962 expect(names3[0]).to.equal('playlist 3 updated')
@@ -1060,28 +966,16 @@ describe('Test video playlists', function () {
1060 it('Should delete some elements', async function () { 966 it('Should delete some elements', async function () {
1061 this.timeout(30000) 967 this.timeout(30000)
1062 968
1063 await removeVideoFromPlaylist({ 969 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
1064 url: servers[0].url, 970 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
1065 token: servers[0].accessToken,
1066 playlistId: playlistServer1Id,
1067 playlistElementId: playlistElementServer1Video4
1068 })
1069
1070 await removeVideoFromPlaylist({
1071 url: servers[0].url,
1072 token: servers[0].accessToken,
1073 playlistId: playlistServer1Id,
1074 playlistElementId: playlistElementNSFW
1075 })
1076 971
1077 await waitJobs(servers) 972 await waitJobs(servers)
1078 973
1079 for (const server of servers) { 974 for (const server of servers) {
1080 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10) 975 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
976 expect(body.total).to.equal(6)
1081 977
1082 expect(res.body.total).to.equal(6) 978 const elements = body.data
1083
1084 const elements: VideoPlaylistElement[] = res.body.data
1085 expect(elements).to.have.lengthOf(6) 979 expect(elements).to.have.lengthOf(6)
1086 980
1087 expect(elements[0].video.name).to.equal('video 0 server 1') 981 expect(elements[0].video.name).to.equal('video 0 server 1')
@@ -1107,34 +1001,31 @@ describe('Test video playlists', function () {
1107 it('Should be able to create a public playlist, and set it to private', async function () { 1001 it('Should be able to create a public playlist, and set it to private', async function () {
1108 this.timeout(30000) 1002 this.timeout(30000)
1109 1003
1110 const res = await createVideoPlaylist({ 1004 const videoPlaylistIds = await commands[0].create({
1111 url: servers[0].url, 1005 attributes: {
1112 token: servers[0].accessToken,
1113 playlistAttrs: {
1114 displayName: 'my super public playlist', 1006 displayName: 'my super public playlist',
1115 privacy: VideoPlaylistPrivacy.PUBLIC, 1007 privacy: VideoPlaylistPrivacy.PUBLIC,
1116 videoChannelId: servers[0].videoChannel.id 1008 videoChannelId: servers[0].store.channel.id
1117 } 1009 }
1118 }) 1010 })
1119 const videoPlaylistIds = res.body.videoPlaylist
1120 1011
1121 await waitJobs(servers) 1012 await waitJobs(servers)
1122 1013
1123 for (const server of servers) { 1014 for (const server of servers) {
1124 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200) 1015 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1125 } 1016 }
1126 1017
1127 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE } 1018 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1128 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs }) 1019 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1129 1020
1130 await waitJobs(servers) 1021 await waitJobs(servers)
1131 1022
1132 for (const server of [ servers[1], servers[2] ]) { 1023 for (const server of [ servers[1], servers[2] ]) {
1133 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404) 1024 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1134 } 1025 }
1135 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1136 1026
1137 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200) 1027 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1028 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1138 }) 1029 })
1139 }) 1030 })
1140 1031
@@ -1143,12 +1034,12 @@ describe('Test video playlists', function () {
1143 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () { 1034 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1144 this.timeout(30000) 1035 this.timeout(30000)
1145 1036
1146 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id) 1037 await commands[0].delete({ playlistId: playlistServer1Id })
1147 1038
1148 await waitJobs(servers) 1039 await waitJobs(servers)
1149 1040
1150 for (const server of servers) { 1041 for (const server of servers) {
1151 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404) 1042 await server.playlists.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1152 } 1043 }
1153 }) 1044 })
1154 1045
@@ -1163,75 +1054,61 @@ describe('Test video playlists', function () {
1163 it('Should unfollow servers 1 and 2 and hide their playlists', async function () { 1054 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1164 this.timeout(30000) 1055 this.timeout(30000)
1165 1056
1166 const finder = data => data.find(p => p.displayName === 'my super playlist') 1057 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1167 1058
1168 { 1059 {
1169 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 1060 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1170 expect(res.body.total).to.equal(3) 1061 expect(body.total).to.equal(3)
1171 expect(finder(res.body.data)).to.not.be.undefined 1062
1063 expect(finder(body.data)).to.not.be.undefined
1172 } 1064 }
1173 1065
1174 await unfollow(servers[2].url, servers[2].accessToken, servers[0]) 1066 await servers[2].follows.unfollow({ target: servers[0] })
1175 1067
1176 { 1068 {
1177 const res = await getVideoPlaylistsList(servers[2].url, 0, 5) 1069 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1178 expect(res.body.total).to.equal(1) 1070 expect(body.total).to.equal(1)
1179 1071
1180 expect(finder(res.body.data)).to.be.undefined 1072 expect(finder(body.data)).to.be.undefined
1181 } 1073 }
1182 }) 1074 })
1183 1075
1184 it('Should delete a channel and put the associated playlist in private mode', async function () { 1076 it('Should delete a channel and put the associated playlist in private mode', async function () {
1185 this.timeout(30000) 1077 this.timeout(30000)
1186 1078
1187 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' }) 1079 const channel = await servers[0].channels.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1188 const videoChannelId = res.body.videoChannel.id
1189 1080
1190 const res2 = await createVideoPlaylist({ 1081 const playlistCreated = await commands[0].create({
1191 url: servers[0].url, 1082 attributes: {
1192 token: servers[0].accessToken,
1193 playlistAttrs: {
1194 displayName: 'channel playlist', 1083 displayName: 'channel playlist',
1195 privacy: VideoPlaylistPrivacy.PUBLIC, 1084 privacy: VideoPlaylistPrivacy.PUBLIC,
1196 videoChannelId 1085 videoChannelId: channel.id
1197 } 1086 }
1198 }) 1087 })
1199 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1200 1088
1201 await waitJobs(servers) 1089 await waitJobs(servers)
1202 1090
1203 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel') 1091 await servers[0].channels.delete({ channelName: 'super_channel' })
1204 1092
1205 await waitJobs(servers) 1093 await waitJobs(servers)
1206 1094
1207 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID) 1095 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1208 expect(res3.body.displayName).to.equal('channel playlist') 1096 expect(body.displayName).to.equal('channel playlist')
1209 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE) 1097 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1210 1098
1211 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404) 1099 await servers[1].playlists.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1212 }) 1100 })
1213 1101
1214 it('Should delete an account and delete its playlists', async function () { 1102 it('Should delete an account and delete its playlists', async function () {
1215 this.timeout(30000) 1103 this.timeout(30000)
1216 1104
1217 const user = { username: 'user_1', password: 'password' } 1105 const { userId, token } = await servers[0].users.generate('user_1')
1218 const res = await createUser({
1219 url: servers[0].url,
1220 accessToken: servers[0].accessToken,
1221 username: user.username,
1222 password: user.password
1223 })
1224
1225 const userId = res.body.user.id
1226 const userAccessToken = await userLogin(servers[0], user)
1227 1106
1228 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken) 1107 const { videoChannels } = await servers[0].users.getMyInfo({ token })
1229 const userChannel = (resChannel.body as User).videoChannels[0] 1108 const userChannel = videoChannels[0]
1230 1109
1231 await createVideoPlaylist({ 1110 await commands[0].create({
1232 url: servers[0].url, 1111 attributes: {
1233 token: userAccessToken,
1234 playlistAttrs: {
1235 displayName: 'playlist to be deleted', 1112 displayName: 'playlist to be deleted',
1236 privacy: VideoPlaylistPrivacy.PUBLIC, 1113 privacy: VideoPlaylistPrivacy.PUBLIC,
1237 videoChannelId: userChannel.id 1114 videoChannelId: userChannel.id
@@ -1240,22 +1117,24 @@ describe('Test video playlists', function () {
1240 1117
1241 await waitJobs(servers) 1118 await waitJobs(servers)
1242 1119
1243 const finder = data => data.find(p => p.displayName === 'playlist to be deleted') 1120 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1244 1121
1245 { 1122 {
1246 for (const server of [ servers[0], servers[1] ]) { 1123 for (const server of [ servers[0], servers[1] ]) {
1247 const res = await getVideoPlaylistsList(server.url, 0, 15) 1124 const body = await server.playlists.list({ start: 0, count: 15 })
1248 expect(finder(res.body.data)).to.not.be.undefined 1125
1126 expect(finder(body.data)).to.not.be.undefined
1249 } 1127 }
1250 } 1128 }
1251 1129
1252 await removeUser(servers[0].url, userId, servers[0].accessToken) 1130 await servers[0].users.remove({ userId })
1253 await waitJobs(servers) 1131 await waitJobs(servers)
1254 1132
1255 { 1133 {
1256 for (const server of [ servers[0], servers[1] ]) { 1134 for (const server of [ servers[0], servers[1] ]) {
1257 const res = await getVideoPlaylistsList(server.url, 0, 15) 1135 const body = await server.playlists.list({ start: 0, count: 15 })
1258 expect(finder(res.body.data)).to.be.undefined 1136
1137 expect(finder(body.data)).to.be.undefined
1259 } 1138 }
1260 } 1139 }
1261 }) 1140 })
diff --git a/server/tests/api/videos/video-privacy.ts b/server/tests/api/videos/video-privacy.ts
index 950aeb7cf..b51b3bcdd 100644
--- a/server/tests/api/videos/video-privacy.ts
+++ b/server/tests/api/videos/video-privacy.ts
@@ -2,28 +2,13 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 5import { cleanupTests, createSingleServer, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { Video, VideoCreateResult } from '@shared/models' 6import { HttpStatusCode, VideoCreateResult, VideoPrivacy } from '@shared/models'
7import {
8 cleanupTests,
9 flushAndRunServer,
10 getVideosList,
11 getVideosListWithToken,
12 ServerInfo,
13 setAccessTokensToServers,
14 uploadVideo
15} from '../../../../shared/extra-utils/index'
16import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
17import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
18import { userLogin } from '../../../../shared/extra-utils/users/login'
19import { createUser } from '../../../../shared/extra-utils/users/users'
20import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/extra-utils/videos/videos'
21import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
22 7
23const expect = chai.expect 8const expect = chai.expect
24 9
25describe('Test video privacy', function () { 10describe('Test video privacy', function () {
26 const servers: ServerInfo[] = [] 11 const servers: PeerTubeServer[] = []
27 let anotherUserToken: string 12 let anotherUserToken: string
28 13
29 let privateVideoId: number 14 let privateVideoId: number
@@ -49,8 +34,8 @@ describe('Test video privacy', function () {
49 this.timeout(50000) 34 this.timeout(50000)
50 35
51 // Run servers 36 // Run servers
52 servers.push(await flushAndRunServer(1, dontFederateUnlistedConfig)) 37 servers.push(await createSingleServer(1, dontFederateUnlistedConfig))
53 servers.push(await flushAndRunServer(2)) 38 servers.push(await createSingleServer(2))
54 39
55 // Get the access tokens 40 // Get the access tokens
56 await setAccessTokensToServers(servers) 41 await setAccessTokensToServers(servers)
@@ -66,55 +51,53 @@ describe('Test video privacy', function () {
66 51
67 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) { 52 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
68 const attributes = { privacy } 53 const attributes = { privacy }
69 await uploadVideo(servers[0].url, servers[0].accessToken, attributes) 54 await servers[0].videos.upload({ attributes })
70 } 55 }
71 56
72 await waitJobs(servers) 57 await waitJobs(servers)
73 }) 58 })
74 59
75 it('Should not have these private and internal videos on server 2', async function () { 60 it('Should not have these private and internal videos on server 2', async function () {
76 const res = await getVideosList(servers[1].url) 61 const { total, data } = await servers[1].videos.list()
77 62
78 expect(res.body.total).to.equal(0) 63 expect(total).to.equal(0)
79 expect(res.body.data).to.have.lengthOf(0) 64 expect(data).to.have.lengthOf(0)
80 }) 65 })
81 66
82 it('Should not list the private and internal videos for an unauthenticated user on server 1', async function () { 67 it('Should not list the private and internal videos for an unauthenticated user on server 1', async function () {
83 const res = await getVideosList(servers[0].url) 68 const { total, data } = await servers[0].videos.list()
84 69
85 expect(res.body.total).to.equal(0) 70 expect(total).to.equal(0)
86 expect(res.body.data).to.have.lengthOf(0) 71 expect(data).to.have.lengthOf(0)
87 }) 72 })
88 73
89 it('Should not list the private video and list the internal video for an authenticated user on server 1', async function () { 74 it('Should not list the private video and list the internal video for an authenticated user on server 1', async function () {
90 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) 75 const { total, data } = await servers[0].videos.listWithToken()
91 76
92 expect(res.body.total).to.equal(1) 77 expect(total).to.equal(1)
93 expect(res.body.data).to.have.lengthOf(1) 78 expect(data).to.have.lengthOf(1)
94 79
95 expect(res.body.data[0].privacy.id).to.equal(VideoPrivacy.INTERNAL) 80 expect(data[0].privacy.id).to.equal(VideoPrivacy.INTERNAL)
96 }) 81 })
97 82
98 it('Should list my (private and internal) videos', async function () { 83 it('Should list my (private and internal) videos', async function () {
99 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 10) 84 const { total, data } = await servers[0].videos.listMyVideos()
100 85
101 expect(res.body.total).to.equal(2) 86 expect(total).to.equal(2)
102 expect(res.body.data).to.have.lengthOf(2) 87 expect(data).to.have.lengthOf(2)
103 88
104 const videos: Video[] = res.body.data 89 const privateVideo = data.find(v => v.privacy.id === VideoPrivacy.PRIVATE)
105
106 const privateVideo = videos.find(v => v.privacy.id === VideoPrivacy.PRIVATE)
107 privateVideoId = privateVideo.id 90 privateVideoId = privateVideo.id
108 privateVideoUUID = privateVideo.uuid 91 privateVideoUUID = privateVideo.uuid
109 92
110 const internalVideo = videos.find(v => v.privacy.id === VideoPrivacy.INTERNAL) 93 const internalVideo = data.find(v => v.privacy.id === VideoPrivacy.INTERNAL)
111 internalVideoId = internalVideo.id 94 internalVideoId = internalVideo.id
112 internalVideoUUID = internalVideo.uuid 95 internalVideoUUID = internalVideo.uuid
113 }) 96 })
114 97
115 it('Should not be able to watch the private/internal video with non authenticated user', async function () { 98 it('Should not be able to watch the private/internal video with non authenticated user', async function () {
116 await getVideo(servers[0].url, privateVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 99 await servers[0].videos.get({ id: privateVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
117 await getVideo(servers[0].url, internalVideoUUID, HttpStatusCode.UNAUTHORIZED_401) 100 await servers[0].videos.get({ id: internalVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
118 }) 101 })
119 102
120 it('Should not be able to watch the private video with another user', async function () { 103 it('Should not be able to watch the private video with another user', async function () {
@@ -124,18 +107,23 @@ describe('Test video privacy', function () {
124 username: 'hello', 107 username: 'hello',
125 password: 'super password' 108 password: 'super password'
126 } 109 }
127 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password }) 110 await servers[0].users.create({ username: user.username, password: user.password })
111
112 anotherUserToken = await servers[0].login.getAccessToken(user)
128 113
129 anotherUserToken = await userLogin(servers[0], user) 114 await servers[0].videos.getWithToken({
130 await getVideoWithToken(servers[0].url, anotherUserToken, privateVideoUUID, HttpStatusCode.FORBIDDEN_403) 115 token: anotherUserToken,
116 id: privateVideoUUID,
117 expectedStatus: HttpStatusCode.FORBIDDEN_403
118 })
131 }) 119 })
132 120
133 it('Should be able to watch the internal video with another user', async function () { 121 it('Should be able to watch the internal video with another user', async function () {
134 await getVideoWithToken(servers[0].url, anotherUserToken, internalVideoUUID, HttpStatusCode.OK_200) 122 await servers[0].videos.getWithToken({ token: anotherUserToken, id: internalVideoUUID })
135 }) 123 })
136 124
137 it('Should be able to watch the private video with the correct user', async function () { 125 it('Should be able to watch the private video with the correct user', async function () {
138 await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID, HttpStatusCode.OK_200) 126 await servers[0].videos.getWithToken({ id: privateVideoUUID })
139 }) 127 })
140 }) 128 })
141 129
@@ -148,7 +136,7 @@ describe('Test video privacy', function () {
148 name: 'unlisted video', 136 name: 'unlisted video',
149 privacy: VideoPrivacy.UNLISTED 137 privacy: VideoPrivacy.UNLISTED
150 } 138 }
151 await uploadVideo(servers[1].url, servers[1].accessToken, attributes) 139 await servers[1].videos.upload({ attributes })
152 140
153 // Server 2 has transcoding enabled 141 // Server 2 has transcoding enabled
154 await waitJobs(servers) 142 await waitJobs(servers)
@@ -156,32 +144,32 @@ describe('Test video privacy', function () {
156 144
157 it('Should not have this unlisted video listed on server 1 and 2', async function () { 145 it('Should not have this unlisted video listed on server 1 and 2', async function () {
158 for (const server of servers) { 146 for (const server of servers) {
159 const res = await getVideosList(server.url) 147 const { total, data } = await server.videos.list()
160 148
161 expect(res.body.total).to.equal(0) 149 expect(total).to.equal(0)
162 expect(res.body.data).to.have.lengthOf(0) 150 expect(data).to.have.lengthOf(0)
163 } 151 }
164 }) 152 })
165 153
166 it('Should list my (unlisted) videos', async function () { 154 it('Should list my (unlisted) videos', async function () {
167 const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 1) 155 const { total, data } = await servers[1].videos.listMyVideos()
168 156
169 expect(res.body.total).to.equal(1) 157 expect(total).to.equal(1)
170 expect(res.body.data).to.have.lengthOf(1) 158 expect(data).to.have.lengthOf(1)
171 159
172 unlistedVideo = res.body.data[0] 160 unlistedVideo = data[0]
173 }) 161 })
174 162
175 it('Should not be able to get this unlisted video using its id', async function () { 163 it('Should not be able to get this unlisted video using its id', async function () {
176 await getVideo(servers[1].url, unlistedVideo.id, 404) 164 await servers[1].videos.get({ id: unlistedVideo.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
177 }) 165 })
178 166
179 it('Should be able to get this unlisted video using its uuid/shortUUID', async function () { 167 it('Should be able to get this unlisted video using its uuid/shortUUID', async function () {
180 for (const server of servers) { 168 for (const server of servers) {
181 for (const id of [ unlistedVideo.uuid, unlistedVideo.shortUUID ]) { 169 for (const id of [ unlistedVideo.uuid, unlistedVideo.shortUUID ]) {
182 const res = await getVideo(server.url, id) 170 const video = await server.videos.get({ id })
183 171
184 expect(res.body.name).to.equal('unlisted video') 172 expect(video.name).to.equal('unlisted video')
185 } 173 }
186 } 174 }
187 }) 175 })
@@ -193,28 +181,28 @@ describe('Test video privacy', function () {
193 name: 'unlisted video', 181 name: 'unlisted video',
194 privacy: VideoPrivacy.UNLISTED 182 privacy: VideoPrivacy.UNLISTED
195 } 183 }
196 await uploadVideo(servers[0].url, servers[0].accessToken, attributes) 184 await servers[0].videos.upload({ attributes })
197 185
198 await waitJobs(servers) 186 await waitJobs(servers)
199 }) 187 })
200 188
201 it('Should list my new unlisted video', async function () { 189 it('Should list my new unlisted video', async function () {
202 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 3) 190 const { total, data } = await servers[0].videos.listMyVideos()
203 191
204 expect(res.body.total).to.equal(3) 192 expect(total).to.equal(3)
205 expect(res.body.data).to.have.lengthOf(3) 193 expect(data).to.have.lengthOf(3)
206 194
207 nonFederatedUnlistedVideoUUID = res.body.data[0].uuid 195 nonFederatedUnlistedVideoUUID = data[0].uuid
208 }) 196 })
209 197
210 it('Should be able to get non-federated unlisted video from origin', async function () { 198 it('Should be able to get non-federated unlisted video from origin', async function () {
211 const res = await getVideo(servers[0].url, nonFederatedUnlistedVideoUUID) 199 const video = await servers[0].videos.get({ id: nonFederatedUnlistedVideoUUID })
212 200
213 expect(res.body.name).to.equal('unlisted video') 201 expect(video.name).to.equal('unlisted video')
214 }) 202 })
215 203
216 it('Should not be able to get non-federated unlisted video from federated server', async function () { 204 it('Should not be able to get non-federated unlisted video from federated server', async function () {
217 await getVideo(servers[1].url, nonFederatedUnlistedVideoUUID, HttpStatusCode.NOT_FOUND_404) 205 await servers[1].videos.get({ id: nonFederatedUnlistedVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
218 }) 206 })
219 }) 207 })
220 208
@@ -226,20 +214,20 @@ describe('Test video privacy', function () {
226 now = Date.now() 214 now = Date.now()
227 215
228 { 216 {
229 const attribute = { 217 const attributes = {
230 name: 'private video becomes public', 218 name: 'private video becomes public',
231 privacy: VideoPrivacy.PUBLIC 219 privacy: VideoPrivacy.PUBLIC
232 } 220 }
233 221
234 await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute) 222 await servers[0].videos.update({ id: privateVideoId, attributes })
235 } 223 }
236 224
237 { 225 {
238 const attribute = { 226 const attributes = {
239 name: 'internal video becomes public', 227 name: 'internal video becomes public',
240 privacy: VideoPrivacy.PUBLIC 228 privacy: VideoPrivacy.PUBLIC
241 } 229 }
242 await updateVideo(servers[0].url, servers[0].accessToken, internalVideoId, attribute) 230 await servers[0].videos.update({ id: internalVideoId, attributes })
243 } 231 }
244 232
245 await waitJobs(servers) 233 await waitJobs(servers)
@@ -247,13 +235,12 @@ describe('Test video privacy', function () {
247 235
248 it('Should have this new public video listed on server 1 and 2', async function () { 236 it('Should have this new public video listed on server 1 and 2', async function () {
249 for (const server of servers) { 237 for (const server of servers) {
250 const res = await getVideosList(server.url) 238 const { total, data } = await server.videos.list()
251 expect(res.body.total).to.equal(2) 239 expect(total).to.equal(2)
252 expect(res.body.data).to.have.lengthOf(2) 240 expect(data).to.have.lengthOf(2)
253 241
254 const videos: Video[] = res.body.data 242 const privateVideo = data.find(v => v.name === 'private video becomes public')
255 const privateVideo = videos.find(v => v.name === 'private video becomes public') 243 const internalVideo = data.find(v => v.name === 'internal video becomes public')
256 const internalVideo = videos.find(v => v.name === 'internal video becomes public')
257 244
258 expect(privateVideo).to.not.be.undefined 245 expect(privateVideo).to.not.be.undefined
259 expect(internalVideo).to.not.be.undefined 246 expect(internalVideo).to.not.be.undefined
@@ -270,27 +257,25 @@ describe('Test video privacy', function () {
270 it('Should set these videos as private and internal', async function () { 257 it('Should set these videos as private and internal', async function () {
271 this.timeout(10000) 258 this.timeout(10000)
272 259
273 await updateVideo(servers[0].url, servers[0].accessToken, internalVideoId, { privacy: VideoPrivacy.PRIVATE }) 260 await servers[0].videos.update({ id: internalVideoId, attributes: { privacy: VideoPrivacy.PRIVATE } })
274 await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, { privacy: VideoPrivacy.INTERNAL }) 261 await servers[0].videos.update({ id: privateVideoId, attributes: { privacy: VideoPrivacy.INTERNAL } })
275 262
276 await waitJobs(servers) 263 await waitJobs(servers)
277 264
278 for (const server of servers) { 265 for (const server of servers) {
279 const res = await getVideosList(server.url) 266 const { total, data } = await server.videos.list()
280 267
281 expect(res.body.total).to.equal(0) 268 expect(total).to.equal(0)
282 expect(res.body.data).to.have.lengthOf(0) 269 expect(data).to.have.lengthOf(0)
283 } 270 }
284 271
285 { 272 {
286 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5) 273 const { total, data } = await servers[0].videos.listMyVideos()
287 const videos = res.body.data 274 expect(total).to.equal(3)
288 275 expect(data).to.have.lengthOf(3)
289 expect(res.body.total).to.equal(3)
290 expect(videos).to.have.lengthOf(3)
291 276
292 const privateVideo = videos.find(v => v.name === 'private video becomes public') 277 const privateVideo = data.find(v => v.name === 'private video becomes public')
293 const internalVideo = videos.find(v => v.name === 'internal video becomes public') 278 const internalVideo = data.find(v => v.name === 'internal video becomes public')
294 279
295 expect(privateVideo).to.not.be.undefined 280 expect(privateVideo).to.not.be.undefined
296 expect(internalVideo).to.not.be.undefined 281 expect(internalVideo).to.not.be.undefined
diff --git a/server/tests/api/videos/video-schedule-update.ts b/server/tests/api/videos/video-schedule-update.ts
index 204f43611..3f7738784 100644
--- a/server/tests/api/videos/video-schedule-update.ts
+++ b/server/tests/api/videos/video-schedule-update.ts
@@ -1,22 +1,17 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { VideoPrivacy } from '../../../../shared/models/videos' 4import * as chai from 'chai'
6import { 5import {
7 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 flushAndRunMultipleServers, 9 PeerTubeServer,
10 getMyVideos,
11 getVideosList,
12 getVideoWithToken,
13 ServerInfo,
14 setAccessTokensToServers, 10 setAccessTokensToServers,
15 updateVideo, 11 wait,
16 uploadVideo, 12 waitJobs
17 wait 13} from '@shared/extra-utils'
18} from '../../../../shared/extra-utils' 14import { VideoPrivacy } from '@shared/models'
19import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
20 15
21const expect = chai.expect 16const expect = chai.expect
22 17
@@ -28,14 +23,14 @@ function in10Seconds () {
28} 23}
29 24
30describe('Test video update scheduler', function () { 25describe('Test video update scheduler', function () {
31 let servers: ServerInfo[] = [] 26 let servers: PeerTubeServer[] = []
32 let video2UUID: string 27 let video2UUID: string
33 28
34 before(async function () { 29 before(async function () {
35 this.timeout(30000) 30 this.timeout(30000)
36 31
37 // Run servers 32 // Run servers
38 servers = await flushAndRunMultipleServers(2) 33 servers = await createMultipleServers(2)
39 34
40 await setAccessTokensToServers(servers) 35 await setAccessTokensToServers(servers)
41 36
@@ -45,35 +40,34 @@ describe('Test video update scheduler', function () {
45 it('Should upload a video and schedule an update in 10 seconds', async function () { 40 it('Should upload a video and schedule an update in 10 seconds', async function () {
46 this.timeout(10000) 41 this.timeout(10000)
47 42
48 const videoAttributes = { 43 const attributes = {
49 name: 'video 1', 44 name: 'video 1',
50 privacy: VideoPrivacy.PRIVATE, 45 privacy: VideoPrivacy.PRIVATE,
51 scheduleUpdate: { 46 scheduleUpdate: {
52 updateAt: in10Seconds().toISOString(), 47 updateAt: in10Seconds().toISOString(),
53 privacy: VideoPrivacy.PUBLIC 48 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
54 } 49 }
55 } 50 }
56 51
57 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 52 await servers[0].videos.upload({ attributes })
58 53
59 await waitJobs(servers) 54 await waitJobs(servers)
60 }) 55 })
61 56
62 it('Should not list the video (in privacy mode)', async function () { 57 it('Should not list the video (in privacy mode)', async function () {
63 for (const server of servers) { 58 for (const server of servers) {
64 const res = await getVideosList(server.url) 59 const { total } = await server.videos.list()
65 60
66 expect(res.body.total).to.equal(0) 61 expect(total).to.equal(0)
67 } 62 }
68 }) 63 })
69 64
70 it('Should have my scheduled video in my account videos', async function () { 65 it('Should have my scheduled video in my account videos', async function () {
71 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5) 66 const { total, data } = await servers[0].videos.listMyVideos()
72 expect(res.body.total).to.equal(1) 67 expect(total).to.equal(1)
73 68
74 const videoFromList = res.body.data[0] 69 const videoFromList = data[0]
75 const res2 = await getVideoWithToken(servers[0].url, servers[0].accessToken, videoFromList.uuid) 70 const videoFromGet = await servers[0].videos.getWithToken({ id: videoFromList.uuid })
76 const videoFromGet = res2.body
77 71
78 for (const video of [ videoFromList, videoFromGet ]) { 72 for (const video of [ videoFromList, videoFromGet ]) {
79 expect(video.name).to.equal('video 1') 73 expect(video.name).to.equal('video 1')
@@ -90,23 +84,23 @@ describe('Test video update scheduler', function () {
90 await waitJobs(servers) 84 await waitJobs(servers)
91 85
92 for (const server of servers) { 86 for (const server of servers) {
93 const res = await getVideosList(server.url) 87 const { total, data } = await server.videos.list()
94 88
95 expect(res.body.total).to.equal(1) 89 expect(total).to.equal(1)
96 expect(res.body.data[0].name).to.equal('video 1') 90 expect(data[0].name).to.equal('video 1')
97 } 91 }
98 }) 92 })
99 93
100 it('Should upload a video without scheduling an update', async function () { 94 it('Should upload a video without scheduling an update', async function () {
101 this.timeout(10000) 95 this.timeout(10000)
102 96
103 const videoAttributes = { 97 const attributes = {
104 name: 'video 2', 98 name: 'video 2',
105 privacy: VideoPrivacy.PRIVATE 99 privacy: VideoPrivacy.PRIVATE
106 } 100 }
107 101
108 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 102 const { uuid } = await servers[0].videos.upload({ attributes })
109 video2UUID = res.body.video.uuid 103 video2UUID = uuid
110 104
111 await waitJobs(servers) 105 await waitJobs(servers)
112 }) 106 })
@@ -114,31 +108,31 @@ describe('Test video update scheduler', function () {
114 it('Should update a video by scheduling an update', async function () { 108 it('Should update a video by scheduling an update', async function () {
115 this.timeout(10000) 109 this.timeout(10000)
116 110
117 const videoAttributes = { 111 const attributes = {
118 name: 'video 2 updated', 112 name: 'video 2 updated',
119 scheduleUpdate: { 113 scheduleUpdate: {
120 updateAt: in10Seconds().toISOString(), 114 updateAt: in10Seconds().toISOString(),
121 privacy: VideoPrivacy.PUBLIC 115 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
122 } 116 }
123 } 117 }
124 118
125 await updateVideo(servers[0].url, servers[0].accessToken, video2UUID, videoAttributes) 119 await servers[0].videos.update({ id: video2UUID, attributes })
126 await waitJobs(servers) 120 await waitJobs(servers)
127 }) 121 })
128 122
129 it('Should not display the updated video', async function () { 123 it('Should not display the updated video', async function () {
130 for (const server of servers) { 124 for (const server of servers) {
131 const res = await getVideosList(server.url) 125 const { total } = await server.videos.list()
132 126
133 expect(res.body.total).to.equal(1) 127 expect(total).to.equal(1)
134 } 128 }
135 }) 129 })
136 130
137 it('Should have my scheduled updated video in my account videos', async function () { 131 it('Should have my scheduled updated video in my account videos', async function () {
138 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5) 132 const { total, data } = await servers[0].videos.listMyVideos()
139 expect(res.body.total).to.equal(2) 133 expect(total).to.equal(2)
140 134
141 const video = res.body.data.find(v => v.uuid === video2UUID) 135 const video = data.find(v => v.uuid === video2UUID)
142 expect(video).not.to.be.undefined 136 expect(video).not.to.be.undefined
143 137
144 expect(video.name).to.equal('video 2 updated') 138 expect(video.name).to.equal('video 2 updated')
@@ -155,11 +149,10 @@ describe('Test video update scheduler', function () {
155 await waitJobs(servers) 149 await waitJobs(servers)
156 150
157 for (const server of servers) { 151 for (const server of servers) {
158 const res = await getVideosList(server.url) 152 const { total, data } = await server.videos.list()
159 153 expect(total).to.equal(2)
160 expect(res.body.total).to.equal(2)
161 154
162 const video = res.body.data.find(v => v.uuid === video2UUID) 155 const video = data.find(v => v.uuid === video2UUID)
163 expect(video).not.to.be.undefined 156 expect(video).not.to.be.undefined
164 expect(video.name).to.equal('video 2 updated') 157 expect(video.name).to.equal('video 2 updated')
165 } 158 }
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts
index ea5ffd239..2a09e95bf 100644
--- a/server/tests/api/videos/video-transcoder.ts
+++ b/server/tests/api/videos/video-transcoder.ts
@@ -2,36 +2,23 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { FfprobeData } from 'fluent-ffmpeg'
6import { omit } from 'lodash' 5import { omit } from 'lodash'
7import { join } from 'path'
8import { Job } from '@shared/models'
9import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants'
10import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
11import { 6import {
12 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
13 buildServerDirectory,
14 cleanupTests, 8 cleanupTests,
9 createMultipleServers,
15 doubleFollow, 10 doubleFollow,
16 flushAndRunMultipleServers,
17 generateHighBitrateVideo, 11 generateHighBitrateVideo,
18 generateVideoWithFramerate, 12 generateVideoWithFramerate,
19 getJobsListPaginationAndSort, 13 getFileSize,
20 getMyVideos,
21 getServerFileSize,
22 getVideo,
23 getVideoFileMetadataUrl,
24 getVideosList,
25 makeGetRequest, 14 makeGetRequest,
26 ServerInfo, 15 PeerTubeServer,
27 setAccessTokensToServers, 16 setAccessTokensToServers,
28 updateCustomSubConfig,
29 uploadVideo,
30 uploadVideoAndGetId,
31 waitJobs, 17 waitJobs,
32 webtorrentAdd 18 webtorrentAdd
33} from '../../../../shared/extra-utils' 19} from '@shared/extra-utils'
34import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos' 20import { getMaxBitrate, HttpStatusCode, VideoResolution, VideoState } from '@shared/models'
21import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants'
35import { 22import {
36 canDoQuickTranscode, 23 canDoQuickTranscode,
37 getAudioStream, 24 getAudioStream,
@@ -43,37 +30,39 @@ import {
43 30
44const expect = chai.expect 31const expect = chai.expect
45 32
46function updateConfigForTranscoding (server: ServerInfo) { 33function updateConfigForTranscoding (server: PeerTubeServer) {
47 return updateCustomSubConfig(server.url, server.accessToken, { 34 return server.config.updateCustomSubConfig({
48 transcoding: { 35 newConfig: {
49 enabled: true, 36 transcoding: {
50 allowAdditionalExtensions: true, 37 enabled: true,
51 allowAudioFiles: true, 38 allowAdditionalExtensions: true,
52 hls: { enabled: true }, 39 allowAudioFiles: true,
53 webtorrent: { enabled: true }, 40 hls: { enabled: true },
54 resolutions: { 41 webtorrent: { enabled: true },
55 '0p': false, 42 resolutions: {
56 '240p': true, 43 '0p': false,
57 '360p': true, 44 '240p': true,
58 '480p': true, 45 '360p': true,
59 '720p': true, 46 '480p': true,
60 '1080p': true, 47 '720p': true,
61 '1440p': true, 48 '1080p': true,
62 '2160p': true 49 '1440p': true,
50 '2160p': true
51 }
63 } 52 }
64 } 53 }
65 }) 54 })
66} 55}
67 56
68describe('Test video transcoding', function () { 57describe('Test video transcoding', function () {
69 let servers: ServerInfo[] = [] 58 let servers: PeerTubeServer[] = []
70 let video4k: string 59 let video4k: string
71 60
72 before(async function () { 61 before(async function () {
73 this.timeout(30_000) 62 this.timeout(30_000)
74 63
75 // Run servers 64 // Run servers
76 servers = await flushAndRunMultipleServers(2) 65 servers = await createMultipleServers(2)
77 66
78 await setAccessTokensToServers(servers) 67 await setAccessTokensToServers(servers)
79 68
@@ -87,21 +76,20 @@ describe('Test video transcoding', function () {
87 it('Should not transcode video on server 1', async function () { 76 it('Should not transcode video on server 1', async function () {
88 this.timeout(60_000) 77 this.timeout(60_000)
89 78
90 const videoAttributes = { 79 const attributes = {
91 name: 'my super name for server 1', 80 name: 'my super name for server 1',
92 description: 'my super description for server 1', 81 description: 'my super description for server 1',
93 fixture: 'video_short.webm' 82 fixture: 'video_short.webm'
94 } 83 }
95 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 84 await servers[0].videos.upload({ attributes })
96 85
97 await waitJobs(servers) 86 await waitJobs(servers)
98 87
99 for (const server of servers) { 88 for (const server of servers) {
100 const res = await getVideosList(server.url) 89 const { data } = await server.videos.list()
101 const video = res.body.data[0] 90 const video = data[0]
102 91
103 const res2 = await getVideo(server.url, video.id) 92 const videoDetails = await server.videos.get({ id: video.id })
104 const videoDetails = res2.body
105 expect(videoDetails.files).to.have.lengthOf(1) 93 expect(videoDetails.files).to.have.lengthOf(1)
106 94
107 const magnetUri = videoDetails.files[0].magnetUri 95 const magnetUri = videoDetails.files[0].magnetUri
@@ -117,21 +105,20 @@ describe('Test video transcoding', function () {
117 it('Should transcode video on server 2', async function () { 105 it('Should transcode video on server 2', async function () {
118 this.timeout(120_000) 106 this.timeout(120_000)
119 107
120 const videoAttributes = { 108 const attributes = {
121 name: 'my super name for server 2', 109 name: 'my super name for server 2',
122 description: 'my super description for server 2', 110 description: 'my super description for server 2',
123 fixture: 'video_short.webm' 111 fixture: 'video_short.webm'
124 } 112 }
125 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 113 await servers[1].videos.upload({ attributes })
126 114
127 await waitJobs(servers) 115 await waitJobs(servers)
128 116
129 for (const server of servers) { 117 for (const server of servers) {
130 const res = await getVideosList(server.url) 118 const { data } = await server.videos.list()
131 119
132 const video = res.body.data.find(v => v.name === videoAttributes.name) 120 const video = data.find(v => v.name === attributes.name)
133 const res2 = await getVideo(server.url, video.id) 121 const videoDetails = await server.videos.get({ id: video.id })
134 const videoDetails = res2.body
135 122
136 expect(videoDetails.files).to.have.lengthOf(4) 123 expect(videoDetails.files).to.have.lengthOf(4)
137 124
@@ -150,47 +137,50 @@ describe('Test video transcoding', function () {
150 137
151 { 138 {
152 // Upload the video, but wait transcoding 139 // Upload the video, but wait transcoding
153 const videoAttributes = { 140 const attributes = {
154 name: 'waiting video', 141 name: 'waiting video',
155 fixture: 'video_short1.webm', 142 fixture: 'video_short1.webm',
156 waitTranscoding: true 143 waitTranscoding: true
157 } 144 }
158 const resVideo = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 145 const { uuid } = await servers[1].videos.upload({ attributes })
159 const videoId = resVideo.body.video.uuid 146 const videoId = uuid
160 147
161 // Should be in transcode state 148 // Should be in transcode state
162 const { body } = await getVideo(servers[1].url, videoId) 149 const body = await servers[1].videos.get({ id: videoId })
163 expect(body.name).to.equal('waiting video') 150 expect(body.name).to.equal('waiting video')
164 expect(body.state.id).to.equal(VideoState.TO_TRANSCODE) 151 expect(body.state.id).to.equal(VideoState.TO_TRANSCODE)
165 expect(body.state.label).to.equal('To transcode') 152 expect(body.state.label).to.equal('To transcode')
166 expect(body.waitTranscoding).to.be.true 153 expect(body.waitTranscoding).to.be.true
167 154
168 // Should have my video 155 {
169 const resMyVideos = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 10) 156 // Should have my video
170 const videoToFindInMine = resMyVideos.body.data.find(v => v.name === videoAttributes.name) 157 const { data } = await servers[1].videos.listMyVideos()
171 expect(videoToFindInMine).not.to.be.undefined 158 const videoToFindInMine = data.find(v => v.name === attributes.name)
172 expect(videoToFindInMine.state.id).to.equal(VideoState.TO_TRANSCODE) 159 expect(videoToFindInMine).not.to.be.undefined
173 expect(videoToFindInMine.state.label).to.equal('To transcode') 160 expect(videoToFindInMine.state.id).to.equal(VideoState.TO_TRANSCODE)
174 expect(videoToFindInMine.waitTranscoding).to.be.true 161 expect(videoToFindInMine.state.label).to.equal('To transcode')
162 expect(videoToFindInMine.waitTranscoding).to.be.true
163 }
175 164
176 // Should not list this video 165 {
177 const resVideos = await getVideosList(servers[1].url) 166 // Should not list this video
178 const videoToFindInList = resVideos.body.data.find(v => v.name === videoAttributes.name) 167 const { data } = await servers[1].videos.list()
179 expect(videoToFindInList).to.be.undefined 168 const videoToFindInList = data.find(v => v.name === attributes.name)
169 expect(videoToFindInList).to.be.undefined
170 }
180 171
181 // Server 1 should not have the video yet 172 // Server 1 should not have the video yet
182 await getVideo(servers[0].url, videoId, HttpStatusCode.NOT_FOUND_404) 173 await servers[0].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
183 } 174 }
184 175
185 await waitJobs(servers) 176 await waitJobs(servers)
186 177
187 for (const server of servers) { 178 for (const server of servers) {
188 const res = await getVideosList(server.url) 179 const { data } = await server.videos.list()
189 const videoToFind = res.body.data.find(v => v.name === 'waiting video') 180 const videoToFind = data.find(v => v.name === 'waiting video')
190 expect(videoToFind).not.to.be.undefined 181 expect(videoToFind).not.to.be.undefined
191 182
192 const res2 = await getVideo(server.url, videoToFind.id) 183 const videoDetails = await server.videos.get({ id: videoToFind.id })
193 const videoDetails: VideoDetails = res2.body
194 184
195 expect(videoDetails.state.id).to.equal(VideoState.PUBLISHED) 185 expect(videoDetails.state.id).to.equal(VideoState.PUBLISHED)
196 expect(videoDetails.state.label).to.equal('Published') 186 expect(videoDetails.state.label).to.equal('Published')
@@ -211,22 +201,20 @@ describe('Test video transcoding', function () {
211 } 201 }
212 202
213 for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) { 203 for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) {
214 const videoAttributes = { 204 const attributes = {
215 name: fixture, 205 name: fixture,
216 fixture 206 fixture
217 } 207 }
218 208
219 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 209 await servers[1].videos.upload({ attributes })
220 210
221 await waitJobs(servers) 211 await waitJobs(servers)
222 212
223 for (const server of servers) { 213 for (const server of servers) {
224 const res = await getVideosList(server.url) 214 const { data } = await server.videos.list()
225
226 const video = res.body.data.find(v => v.name === videoAttributes.name)
227 const res2 = await getVideo(server.url, video.id)
228 const videoDetails = res2.body
229 215
216 const video = data.find(v => v.name === attributes.name)
217 const videoDetails = await server.videos.get({ id: video.id })
230 expect(videoDetails.files).to.have.lengthOf(4) 218 expect(videoDetails.files).to.have.lengthOf(4)
231 219
232 const magnetUri = videoDetails.files[0].magnetUri 220 const magnetUri = videoDetails.files[0].magnetUri
@@ -238,22 +226,20 @@ describe('Test video transcoding', function () {
238 it('Should transcode a 4k video', async function () { 226 it('Should transcode a 4k video', async function () {
239 this.timeout(200_000) 227 this.timeout(200_000)
240 228
241 const videoAttributes = { 229 const attributes = {
242 name: '4k video', 230 name: '4k video',
243 fixture: 'video_short_4k.mp4' 231 fixture: 'video_short_4k.mp4'
244 } 232 }
245 233
246 const resUpload = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 234 const { uuid } = await servers[1].videos.upload({ attributes })
247 video4k = resUpload.body.video.uuid 235 video4k = uuid
248 236
249 await waitJobs(servers) 237 await waitJobs(servers)
250 238
251 const resolutions = [ 240, 360, 480, 720, 1080, 1440, 2160 ] 239 const resolutions = [ 240, 360, 480, 720, 1080, 1440, 2160 ]
252 240
253 for (const server of servers) { 241 for (const server of servers) {
254 const res = await getVideo(server.url, video4k) 242 const videoDetails = await server.videos.get({ id: video4k })
255 const videoDetails: VideoDetails = res.body
256
257 expect(videoDetails.files).to.have.lengthOf(resolutions.length) 243 expect(videoDetails.files).to.have.lengthOf(resolutions.length)
258 244
259 for (const r of resolutions) { 245 for (const r of resolutions) {
@@ -269,24 +255,24 @@ describe('Test video transcoding', function () {
269 it('Should transcode high bit rate mp3 to proper bit rate', async function () { 255 it('Should transcode high bit rate mp3 to proper bit rate', async function () {
270 this.timeout(60_000) 256 this.timeout(60_000)
271 257
272 const videoAttributes = { 258 const attributes = {
273 name: 'mp3_256k', 259 name: 'mp3_256k',
274 fixture: 'video_short_mp3_256k.mp4' 260 fixture: 'video_short_mp3_256k.mp4'
275 } 261 }
276 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 262 await servers[1].videos.upload({ attributes })
277 263
278 await waitJobs(servers) 264 await waitJobs(servers)
279 265
280 for (const server of servers) { 266 for (const server of servers) {
281 const res = await getVideosList(server.url) 267 const { data } = await server.videos.list()
282 268
283 const video = res.body.data.find(v => v.name === videoAttributes.name) 269 const video = data.find(v => v.name === attributes.name)
284 const res2 = await getVideo(server.url, video.id) 270 const videoDetails = await server.videos.get({ id: video.id })
285 const videoDetails: VideoDetails = res2.body
286 271
287 expect(videoDetails.files).to.have.lengthOf(4) 272 expect(videoDetails.files).to.have.lengthOf(4)
288 273
289 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 274 const file = videoDetails.files.find(f => f.resolution.id === 240)
275 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
290 const probe = await getAudioStream(path) 276 const probe = await getAudioStream(path)
291 277
292 if (probe.audioStream) { 278 if (probe.audioStream) {
@@ -301,23 +287,23 @@ describe('Test video transcoding', function () {
301 it('Should transcode video with no audio and have no audio itself', async function () { 287 it('Should transcode video with no audio and have no audio itself', async function () {
302 this.timeout(60_000) 288 this.timeout(60_000)
303 289
304 const videoAttributes = { 290 const attributes = {
305 name: 'no_audio', 291 name: 'no_audio',
306 fixture: 'video_short_no_audio.mp4' 292 fixture: 'video_short_no_audio.mp4'
307 } 293 }
308 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 294 await servers[1].videos.upload({ attributes })
309 295
310 await waitJobs(servers) 296 await waitJobs(servers)
311 297
312 for (const server of servers) { 298 for (const server of servers) {
313 const res = await getVideosList(server.url) 299 const { data } = await server.videos.list()
314 300
315 const video = res.body.data.find(v => v.name === videoAttributes.name) 301 const video = data.find(v => v.name === attributes.name)
316 const res2 = await getVideo(server.url, video.id) 302 const videoDetails = await server.videos.get({ id: video.id })
317 const videoDetails: VideoDetails = res2.body 303
304 const file = videoDetails.files.find(f => f.resolution.id === 240)
305 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
318 306
319 expect(videoDetails.files).to.have.lengthOf(4)
320 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4'))
321 const probe = await getAudioStream(path) 307 const probe = await getAudioStream(path)
322 expect(probe).to.not.have.property('audioStream') 308 expect(probe).to.not.have.property('audioStream')
323 } 309 }
@@ -326,26 +312,27 @@ describe('Test video transcoding', function () {
326 it('Should leave the audio untouched, but properly transcode the video', async function () { 312 it('Should leave the audio untouched, but properly transcode the video', async function () {
327 this.timeout(60_000) 313 this.timeout(60_000)
328 314
329 const videoAttributes = { 315 const attributes = {
330 name: 'untouched_audio', 316 name: 'untouched_audio',
331 fixture: 'video_short.mp4' 317 fixture: 'video_short.mp4'
332 } 318 }
333 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 319 await servers[1].videos.upload({ attributes })
334 320
335 await waitJobs(servers) 321 await waitJobs(servers)
336 322
337 for (const server of servers) { 323 for (const server of servers) {
338 const res = await getVideosList(server.url) 324 const { data } = await server.videos.list()
339 325
340 const video = res.body.data.find(v => v.name === videoAttributes.name) 326 const video = data.find(v => v.name === attributes.name)
341 const res2 = await getVideo(server.url, video.id) 327 const videoDetails = await server.videos.get({ id: video.id })
342 const videoDetails: VideoDetails = res2.body
343 328
344 expect(videoDetails.files).to.have.lengthOf(4) 329 expect(videoDetails.files).to.have.lengthOf(4)
345 330
346 const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture) 331 const fixturePath = buildAbsoluteFixturePath(attributes.fixture)
347 const fixtureVideoProbe = await getAudioStream(fixturePath) 332 const fixtureVideoProbe = await getAudioStream(fixturePath)
348 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 333
334 const file = videoDetails.files.find(f => f.resolution.id === 240)
335 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
349 336
350 const videoProbe = await getAudioStream(path) 337 const videoProbe = await getAudioStream(path)
351 338
@@ -364,19 +351,21 @@ describe('Test video transcoding', function () {
364 function runSuite (mode: 'legacy' | 'resumable') { 351 function runSuite (mode: 'legacy' | 'resumable') {
365 352
366 before(async function () { 353 before(async function () {
367 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, { 354 await servers[1].config.updateCustomSubConfig({
368 transcoding: { 355 newConfig: {
369 hls: { enabled: true }, 356 transcoding: {
370 webtorrent: { enabled: true }, 357 hls: { enabled: true },
371 resolutions: { 358 webtorrent: { enabled: true },
372 '0p': false, 359 resolutions: {
373 '240p': false, 360 '0p': false,
374 '360p': false, 361 '240p': false,
375 '480p': false, 362 '360p': false,
376 '720p': false, 363 '480p': false,
377 '1080p': false, 364 '720p': false,
378 '1440p': false, 365 '1080p': false,
379 '2160p': false 366 '1440p': false,
367 '2160p': false
368 }
380 } 369 }
381 } 370 }
382 }) 371 })
@@ -385,22 +374,21 @@ describe('Test video transcoding', function () {
385 it('Should merge an audio file with the preview file', async function () { 374 it('Should merge an audio file with the preview file', async function () {
386 this.timeout(60_000) 375 this.timeout(60_000)
387 376
388 const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 377 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
389 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg, HttpStatusCode.OK_200, mode) 378 await servers[1].videos.upload({ attributes, mode })
390 379
391 await waitJobs(servers) 380 await waitJobs(servers)
392 381
393 for (const server of servers) { 382 for (const server of servers) {
394 const res = await getVideosList(server.url) 383 const { data } = await server.videos.list()
395 384
396 const video = res.body.data.find(v => v.name === 'audio_with_preview') 385 const video = data.find(v => v.name === 'audio_with_preview')
397 const res2 = await getVideo(server.url, video.id) 386 const videoDetails = await server.videos.get({ id: video.id })
398 const videoDetails: VideoDetails = res2.body
399 387
400 expect(videoDetails.files).to.have.lengthOf(1) 388 expect(videoDetails.files).to.have.lengthOf(1)
401 389
402 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: HttpStatusCode.OK_200 }) 390 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
403 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: HttpStatusCode.OK_200 }) 391 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
404 392
405 const magnetUri = videoDetails.files[0].magnetUri 393 const magnetUri = videoDetails.files[0].magnetUri
406 expect(magnetUri).to.contain('.mp4') 394 expect(magnetUri).to.contain('.mp4')
@@ -410,22 +398,21 @@ describe('Test video transcoding', function () {
410 it('Should upload an audio file and choose a default background image', async function () { 398 it('Should upload an audio file and choose a default background image', async function () {
411 this.timeout(60_000) 399 this.timeout(60_000)
412 400
413 const videoAttributesArg = { name: 'audio_without_preview', fixture: 'sample.ogg' } 401 const attributes = { name: 'audio_without_preview', fixture: 'sample.ogg' }
414 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg, HttpStatusCode.OK_200, mode) 402 await servers[1].videos.upload({ attributes, mode })
415 403
416 await waitJobs(servers) 404 await waitJobs(servers)
417 405
418 for (const server of servers) { 406 for (const server of servers) {
419 const res = await getVideosList(server.url) 407 const { data } = await server.videos.list()
420 408
421 const video = res.body.data.find(v => v.name === 'audio_without_preview') 409 const video = data.find(v => v.name === 'audio_without_preview')
422 const res2 = await getVideo(server.url, video.id) 410 const videoDetails = await server.videos.get({ id: video.id })
423 const videoDetails = res2.body
424 411
425 expect(videoDetails.files).to.have.lengthOf(1) 412 expect(videoDetails.files).to.have.lengthOf(1)
426 413
427 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: HttpStatusCode.OK_200 }) 414 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
428 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: HttpStatusCode.OK_200 }) 415 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
429 416
430 const magnetUri = videoDetails.files[0].magnetUri 417 const magnetUri = videoDetails.files[0].magnetUri
431 expect(magnetUri).to.contain('.mp4') 418 expect(magnetUri).to.contain('.mp4')
@@ -435,26 +422,27 @@ describe('Test video transcoding', function () {
435 it('Should upload an audio file and create an audio version only', async function () { 422 it('Should upload an audio file and create an audio version only', async function () {
436 this.timeout(60_000) 423 this.timeout(60_000)
437 424
438 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, { 425 await servers[1].config.updateCustomSubConfig({
439 transcoding: { 426 newConfig: {
440 hls: { enabled: true }, 427 transcoding: {
441 webtorrent: { enabled: true }, 428 hls: { enabled: true },
442 resolutions: { 429 webtorrent: { enabled: true },
443 '0p': true, 430 resolutions: {
444 '240p': false, 431 '0p': true,
445 '360p': false 432 '240p': false,
433 '360p': false
434 }
446 } 435 }
447 } 436 }
448 }) 437 })
449 438
450 const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 439 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
451 const resVideo = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributesArg, HttpStatusCode.OK_200, mode) 440 const { id } = await servers[1].videos.upload({ attributes, mode })
452 441
453 await waitJobs(servers) 442 await waitJobs(servers)
454 443
455 for (const server of servers) { 444 for (const server of servers) {
456 const res2 = await getVideo(server.url, resVideo.body.video.id) 445 const videoDetails = await server.videos.get({ id })
457 const videoDetails: VideoDetails = res2.body
458 446
459 for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) { 447 for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) {
460 expect(files).to.have.lengthOf(2) 448 expect(files).to.have.lengthOf(2)
@@ -480,21 +468,20 @@ describe('Test video transcoding', function () {
480 it('Should transcode a 60 FPS video', async function () { 468 it('Should transcode a 60 FPS video', async function () {
481 this.timeout(60_000) 469 this.timeout(60_000)
482 470
483 const videoAttributes = { 471 const attributes = {
484 name: 'my super 30fps name for server 2', 472 name: 'my super 30fps name for server 2',
485 description: 'my super 30fps description for server 2', 473 description: 'my super 30fps description for server 2',
486 fixture: '60fps_720p_small.mp4' 474 fixture: '60fps_720p_small.mp4'
487 } 475 }
488 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 476 await servers[1].videos.upload({ attributes })
489 477
490 await waitJobs(servers) 478 await waitJobs(servers)
491 479
492 for (const server of servers) { 480 for (const server of servers) {
493 const res = await getVideosList(server.url) 481 const { data } = await server.videos.list()
494 482
495 const video = res.body.data.find(v => v.name === videoAttributes.name) 483 const video = data.find(v => v.name === attributes.name)
496 const res2 = await getVideo(server.url, video.id) 484 const videoDetails = await server.videos.get({ id: video.id })
497 const videoDetails: VideoDetails = res2.body
498 485
499 expect(videoDetails.files).to.have.lengthOf(4) 486 expect(videoDetails.files).to.have.lengthOf(4)
500 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62) 487 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62)
@@ -502,14 +489,16 @@ describe('Test video transcoding', function () {
502 expect(videoDetails.files[2].fps).to.be.below(31) 489 expect(videoDetails.files[2].fps).to.be.below(31)
503 expect(videoDetails.files[3].fps).to.be.below(31) 490 expect(videoDetails.files[3].fps).to.be.below(31)
504 491
505 for (const resolution of [ '240', '360', '480' ]) { 492 for (const resolution of [ 240, 360, 480 ]) {
506 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-' + resolution + '.mp4')) 493 const file = videoDetails.files.find(f => f.resolution.id === resolution)
494 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
507 const fps = await getVideoFileFPS(path) 495 const fps = await getVideoFileFPS(path)
508 496
509 expect(fps).to.be.below(31) 497 expect(fps).to.be.below(31)
510 } 498 }
511 499
512 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-720.mp4')) 500 const file = videoDetails.files.find(f => f.resolution.id === 720)
501 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
513 const fps = await getVideoFileFPS(path) 502 const fps = await getVideoFileFPS(path)
514 503
515 expect(fps).to.be.above(58).and.below(62) 504 expect(fps).to.be.above(58).and.below(62)
@@ -528,29 +517,32 @@ describe('Test video transcoding', function () {
528 expect(fps).to.be.equal(59) 517 expect(fps).to.be.equal(59)
529 } 518 }
530 519
531 const videoAttributes = { 520 const attributes = {
532 name: '59fps video', 521 name: '59fps video',
533 description: '59fps video', 522 description: '59fps video',
534 fixture: tempFixturePath 523 fixture: tempFixturePath
535 } 524 }
536 525
537 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 526 await servers[1].videos.upload({ attributes })
538 527
539 await waitJobs(servers) 528 await waitJobs(servers)
540 529
541 for (const server of servers) { 530 for (const server of servers) {
542 const res = await getVideosList(server.url) 531 const { data } = await server.videos.list()
543 532
544 const video = res.body.data.find(v => v.name === videoAttributes.name) 533 const { id } = data.find(v => v.name === attributes.name)
534 const video = await server.videos.get({ id })
545 535
546 { 536 {
547 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-240.mp4')) 537 const file = video.files.find(f => f.resolution.id === 240)
538 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
548 const fps = await getVideoFileFPS(path) 539 const fps = await getVideoFileFPS(path)
549 expect(fps).to.be.equal(25) 540 expect(fps).to.be.equal(25)
550 } 541 }
551 542
552 { 543 {
553 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-720.mp4')) 544 const file = video.files.find(f => f.resolution.id === 720)
545 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
554 const fps = await getVideoFileFPS(path) 546 const fps = await getVideoFileFPS(path)
555 expect(fps).to.be.equal(59) 547 expect(fps).to.be.equal(59)
556 } 548 }
@@ -559,6 +551,7 @@ describe('Test video transcoding', function () {
559 }) 551 })
560 552
561 describe('Bitrate control', function () { 553 describe('Bitrate control', function () {
554
562 it('Should respect maximum bitrate values', async function () { 555 it('Should respect maximum bitrate values', async function () {
563 this.timeout(160_000) 556 this.timeout(160_000)
564 557
@@ -571,30 +564,32 @@ describe('Test video transcoding', function () {
571 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 25, VIDEO_TRANSCODING_FPS)) 564 expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 25, VIDEO_TRANSCODING_FPS))
572 } 565 }
573 566
574 const videoAttributes = { 567 const attributes = {
575 name: 'high bitrate video', 568 name: 'high bitrate video',
576 description: 'high bitrate video', 569 description: 'high bitrate video',
577 fixture: tempFixturePath 570 fixture: tempFixturePath
578 } 571 }
579 572
580 await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 573 await servers[1].videos.upload({ attributes })
581 574
582 await waitJobs(servers) 575 await waitJobs(servers)
583 576
584 for (const server of servers) { 577 for (const server of servers) {
585 const res = await getVideosList(server.url) 578 const { data } = await server.videos.list()
586 579
587 const video = res.body.data.find(v => v.name === videoAttributes.name) 580 const { id } = data.find(v => v.name === attributes.name)
581 const video = await server.videos.get({ id })
588 582
589 for (const resolution of [ '240', '360', '480', '720', '1080' ]) { 583 for (const resolution of [ 240, 360, 480, 720, 1080 ]) {
590 const path = buildServerDirectory(servers[1], join('videos', video.uuid + '-' + resolution + '.mp4')) 584 const file = video.files.find(f => f.resolution.id === resolution)
585 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
591 586
592 const bitrate = await getVideoFileBitrate(path) 587 const bitrate = await getVideoFileBitrate(path)
593 const fps = await getVideoFileFPS(path) 588 const fps = await getVideoFileFPS(path)
594 const resolution2 = await getVideoFileResolution(path) 589 const { videoFileResolution } = await getVideoFileResolution(path)
595 590
596 expect(resolution2.videoFileResolution.toString()).to.equal(resolution) 591 expect(videoFileResolution).to.equal(resolution)
597 expect(bitrate).to.be.below(getMaxBitrate(resolution2.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) 592 expect(bitrate).to.be.below(getMaxBitrate(videoFileResolution, fps, VIDEO_TRANSCODING_FPS))
598 } 593 }
599 } 594 }
600 }) 595 })
@@ -602,7 +597,7 @@ describe('Test video transcoding', function () {
602 it('Should not transcode to an higher bitrate than the original file', async function () { 597 it('Should not transcode to an higher bitrate than the original file', async function () {
603 this.timeout(160_000) 598 this.timeout(160_000)
604 599
605 const config = { 600 const newConfig = {
606 transcoding: { 601 transcoding: {
607 enabled: true, 602 enabled: true,
608 resolutions: { 603 resolutions: {
@@ -618,22 +613,25 @@ describe('Test video transcoding', function () {
618 hls: { enabled: true } 613 hls: { enabled: true }
619 } 614 }
620 } 615 }
621 await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) 616 await servers[1].config.updateCustomSubConfig({ newConfig })
622 617
623 const videoAttributes = { 618 const attributes = {
624 name: 'low bitrate', 619 name: 'low bitrate',
625 fixture: 'low-bitrate.mp4' 620 fixture: 'low-bitrate.mp4'
626 } 621 }
627 622
628 const resUpload = await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) 623 const { id } = await servers[1].videos.upload({ attributes })
629 const videoUUID = resUpload.body.video.uuid
630 624
631 await waitJobs(servers) 625 await waitJobs(servers)
632 626
627 const video = await servers[1].videos.get({ id })
628
633 const resolutions = [ 240, 360, 480, 720, 1080 ] 629 const resolutions = [ 240, 360, 480, 720, 1080 ]
634 for (const r of resolutions) { 630 for (const r of resolutions) {
635 const path = `videos/${videoUUID}-${r}.mp4` 631 const file = video.files.find(f => f.resolution.id === r)
636 const size = await getServerFileSize(servers[1], path) 632
633 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
634 const size = await getFileSize(path)
637 expect(size, `${path} not below ${60_000}`).to.be.below(60_000) 635 expect(size, `${path} not below ${60_000}`).to.be.below(60_000)
638 } 636 }
639 }) 637 })
@@ -644,11 +642,13 @@ describe('Test video transcoding', function () {
644 it('Should provide valid ffprobe data', async function () { 642 it('Should provide valid ffprobe data', async function () {
645 this.timeout(160_000) 643 this.timeout(160_000)
646 644
647 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'ffprobe data' })).uuid 645 const videoUUID = (await servers[1].videos.quickUpload({ name: 'ffprobe data' })).uuid
648 await waitJobs(servers) 646 await waitJobs(servers)
649 647
650 { 648 {
651 const path = buildServerDirectory(servers[1], join('videos', videoUUID + '-240.mp4')) 649 const video = await servers[1].videos.get({ id: videoUUID })
650 const file = video.files.find(f => f.resolution.id === 240)
651 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
652 const metadata = await getMetadataFromFile(path) 652 const metadata = await getMetadataFromFile(path)
653 653
654 // expected format properties 654 // expected format properties
@@ -678,8 +678,7 @@ describe('Test video transcoding', function () {
678 } 678 }
679 679
680 for (const server of servers) { 680 for (const server of servers) {
681 const res2 = await getVideo(server.url, videoUUID) 681 const videoDetails = await server.videos.get({ id: videoUUID })
682 const videoDetails: VideoDetails = res2.body
683 682
684 const videoFiles = videoDetails.files 683 const videoFiles = videoDetails.files
685 .concat(videoDetails.streamingPlaylists[0].files) 684 .concat(videoDetails.streamingPlaylists[0].files)
@@ -691,8 +690,7 @@ describe('Test video transcoding', function () {
691 expect(file.metadataUrl).to.contain(servers[1].url) 690 expect(file.metadataUrl).to.contain(servers[1].url)
692 expect(file.metadataUrl).to.contain(videoUUID) 691 expect(file.metadataUrl).to.contain(videoUUID)
693 692
694 const res3 = await getVideoFileMetadataUrl(file.metadataUrl) 693 const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
695 const metadata: FfprobeData = res3.body
696 expect(metadata).to.have.nested.property('format.size') 694 expect(metadata).to.have.nested.property('format.size')
697 } 695 }
698 } 696 }
@@ -709,17 +707,14 @@ describe('Test video transcoding', function () {
709 describe('Transcoding job queue', function () { 707 describe('Transcoding job queue', function () {
710 708
711 it('Should have the appropriate priorities for transcoding jobs', async function () { 709 it('Should have the appropriate priorities for transcoding jobs', async function () {
712 const res = await getJobsListPaginationAndSort({ 710 const body = await servers[1].jobs.getJobsList({
713 url: servers[1].url,
714 accessToken: servers[1].accessToken,
715 start: 0, 711 start: 0,
716 count: 100, 712 count: 100,
717 sort: '-createdAt', 713 sort: '-createdAt',
718 jobType: 'video-transcoding' 714 jobType: 'video-transcoding'
719 }) 715 })
720 716
721 const jobs = res.body.data as Job[] 717 const jobs = body.data
722
723 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k) 718 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k)
724 719
725 expect(transcodingJobs).to.have.lengthOf(14) 720 expect(transcodingJobs).to.have.lengthOf(14)
diff --git a/server/tests/api/videos/videos-filter.ts b/server/tests/api/videos/videos-filter.ts
index 7428b82c5..2306807bf 100644
--- a/server/tests/api/videos/videos-filter.ts
+++ b/server/tests/api/videos/videos-filter.ts
@@ -1,25 +1,18 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createUser, 7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 flushAndRunMultipleServers,
10 makeGetRequest, 9 makeGetRequest,
11 ServerInfo, 10 PeerTubeServer,
12 setAccessTokensToServers, 11 setAccessTokensToServers
13 uploadVideo, 12} from '@shared/extra-utils'
14 userLogin 13import { HttpStatusCode, UserRole, Video, VideoPrivacy } from '@shared/models'
15} from '../../../../shared/extra-utils' 14
16import { Video, VideoPrivacy } from '../../../../shared/models/videos' 15async function getVideosNames (server: PeerTubeServer, token: string, filter: string, expectedStatus = HttpStatusCode.OK_200) {
17import { UserRole } from '../../../../shared/models/users'
18import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
19
20const expect = chai.expect
21
22async function getVideosNames (server: ServerInfo, token: string, filter: string, statusCodeExpected = HttpStatusCode.OK_200) {
23 const paths = [ 16 const paths = [
24 '/api/v1/video-channels/root_channel/videos', 17 '/api/v1/video-channels/root_channel/videos',
25 '/api/v1/accounts/root/videos', 18 '/api/v1/accounts/root/videos',
@@ -38,7 +31,7 @@ async function getVideosNames (server: ServerInfo, token: string, filter: string
38 sort: 'createdAt', 31 sort: 'createdAt',
39 filter 32 filter
40 }, 33 },
41 statusCodeExpected 34 expectedStatus
42 }) 35 })
43 36
44 videosResults.push(res.body.data.map(v => v.name)) 37 videosResults.push(res.body.data.map(v => v.name))
@@ -48,42 +41,32 @@ async function getVideosNames (server: ServerInfo, token: string, filter: string
48} 41}
49 42
50describe('Test videos filter', function () { 43describe('Test videos filter', function () {
51 let servers: ServerInfo[] 44 let servers: PeerTubeServer[]
52 45
53 // --------------------------------------------------------------- 46 // ---------------------------------------------------------------
54 47
55 before(async function () { 48 before(async function () {
56 this.timeout(160000) 49 this.timeout(160000)
57 50
58 servers = await flushAndRunMultipleServers(2) 51 servers = await createMultipleServers(2)
59 52
60 await setAccessTokensToServers(servers) 53 await setAccessTokensToServers(servers)
61 54
62 for (const server of servers) { 55 for (const server of servers) {
63 const moderator = { username: 'moderator', password: 'my super password' } 56 const moderator = { username: 'moderator', password: 'my super password' }
64 await createUser( 57 await server.users.create({ username: moderator.username, password: moderator.password, role: UserRole.MODERATOR })
65 { 58 server['moderatorAccessToken'] = await server.login.getAccessToken(moderator)
66 url: server.url,
67 accessToken: server.accessToken,
68 username: moderator.username,
69 password: moderator.password,
70 videoQuota: undefined,
71 videoQuotaDaily: undefined,
72 role: UserRole.MODERATOR
73 }
74 )
75 server['moderatorAccessToken'] = await userLogin(server, moderator)
76 59
77 await uploadVideo(server.url, server.accessToken, { name: 'public ' + server.serverNumber }) 60 await server.videos.upload({ attributes: { name: 'public ' + server.serverNumber } })
78 61
79 { 62 {
80 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED } 63 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED }
81 await uploadVideo(server.url, server.accessToken, attributes) 64 await server.videos.upload({ attributes })
82 } 65 }
83 66
84 { 67 {
85 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE } 68 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE }
86 await uploadVideo(server.url, server.accessToken, attributes) 69 await server.videos.upload({ attributes })
87 } 70 }
88 } 71 }
89 72
diff --git a/server/tests/api/videos/videos-history.ts b/server/tests/api/videos/videos-history.ts
index b25cff879..e4bc0bb3a 100644
--- a/server/tests/api/videos/videos-history.ts
+++ b/server/tests/api/videos/videos-history.ts
@@ -1,74 +1,66 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 createUser, 7 createSingleServer,
8 flushAndRunServer, 8 HistoryCommand,
9 getVideosListWithToken,
10 getVideoWithToken,
11 killallServers, 9 killallServers,
12 reRunServer, 10 PeerTubeServer,
13 searchVideoWithToken,
14 ServerInfo,
15 setAccessTokensToServers, 11 setAccessTokensToServers,
16 updateMyUser,
17 uploadVideo,
18 userLogin,
19 wait 12 wait
20} from '../../../../shared/extra-utils' 13} from '@shared/extra-utils'
21import { Video, VideoDetails } from '../../../../shared/models/videos' 14import { HttpStatusCode, Video } from '@shared/models'
22import { listMyVideosHistory, removeMyVideosHistory, userWatchVideo } from '../../../../shared/extra-utils/videos/video-history'
23import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
24 15
25const expect = chai.expect 16const expect = chai.expect
26 17
27describe('Test videos history', function () { 18describe('Test videos history', function () {
28 let server: ServerInfo = null 19 let server: PeerTubeServer = null
29 let video1UUID: string 20 let video1UUID: string
30 let video2UUID: string 21 let video2UUID: string
31 let video3UUID: string 22 let video3UUID: string
32 let video3WatchedDate: Date 23 let video3WatchedDate: Date
33 let userAccessToken: string 24 let userAccessToken: string
25 let command: HistoryCommand
34 26
35 before(async function () { 27 before(async function () {
36 this.timeout(30000) 28 this.timeout(30000)
37 29
38 server = await flushAndRunServer(1) 30 server = await createSingleServer(1)
39 31
40 await setAccessTokensToServers([ server ]) 32 await setAccessTokensToServers([ server ])
41 33
34 command = server.history
35
42 { 36 {
43 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 1' }) 37 const { uuid } = await server.videos.upload({ attributes: { name: 'video 1' } })
44 video1UUID = res.body.video.uuid 38 video1UUID = uuid
45 } 39 }
46 40
47 { 41 {
48 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 2' }) 42 const { uuid } = await server.videos.upload({ attributes: { name: 'video 2' } })
49 video2UUID = res.body.video.uuid 43 video2UUID = uuid
50 } 44 }
51 45
52 { 46 {
53 const res = await uploadVideo(server.url, server.accessToken, { name: 'video 3' }) 47 const { uuid } = await server.videos.upload({ attributes: { name: 'video 3' } })
54 video3UUID = res.body.video.uuid 48 video3UUID = uuid
55 } 49 }
56 50
57 const user = { 51 const user = {
58 username: 'user_1', 52 username: 'user_1',
59 password: 'super password' 53 password: 'super password'
60 } 54 }
61 await createUser({ url: server.url, accessToken: server.accessToken, username: user.username, password: user.password }) 55 await server.users.create({ username: user.username, password: user.password })
62 userAccessToken = await userLogin(server, user) 56 userAccessToken = await server.login.getAccessToken(user)
63 }) 57 })
64 58
65 it('Should get videos, without watching history', async function () { 59 it('Should get videos, without watching history', async function () {
66 const res = await getVideosListWithToken(server.url, server.accessToken) 60 const { data } = await server.videos.listWithToken()
67 const videos: Video[] = res.body.data
68 61
69 for (const video of videos) { 62 for (const video of data) {
70 const resDetail = await getVideoWithToken(server.url, server.accessToken, video.id) 63 const videoDetails = await server.videos.getWithToken({ id: video.id })
71 const videoDetails: VideoDetails = resDetail.body
72 64
73 expect(video.userHistory).to.be.undefined 65 expect(video.userHistory).to.be.undefined
74 expect(videoDetails.userHistory).to.be.undefined 66 expect(videoDetails.userHistory).to.be.undefined
@@ -76,21 +68,21 @@ describe('Test videos history', function () {
76 }) 68 })
77 69
78 it('Should watch the first and second video', async function () { 70 it('Should watch the first and second video', async function () {
79 await userWatchVideo(server.url, server.accessToken, video2UUID, 8) 71 await command.wathVideo({ videoId: video2UUID, currentTime: 8 })
80 await userWatchVideo(server.url, server.accessToken, video1UUID, 3) 72 await command.wathVideo({ videoId: video1UUID, currentTime: 3 })
81 }) 73 })
82 74
83 it('Should return the correct history when listing, searching and getting videos', async function () { 75 it('Should return the correct history when listing, searching and getting videos', async function () {
84 const videosOfVideos: Video[][] = [] 76 const videosOfVideos: Video[][] = []
85 77
86 { 78 {
87 const res = await getVideosListWithToken(server.url, server.accessToken) 79 const { data } = await server.videos.listWithToken()
88 videosOfVideos.push(res.body.data) 80 videosOfVideos.push(data)
89 } 81 }
90 82
91 { 83 {
92 const res = await searchVideoWithToken(server.url, 'video', server.accessToken) 84 const body = await server.search.searchVideos({ token: server.accessToken, search: 'video' })
93 videosOfVideos.push(res.body.data) 85 videosOfVideos.push(body.data)
94 } 86 }
95 87
96 for (const videos of videosOfVideos) { 88 for (const videos of videosOfVideos) {
@@ -108,24 +100,21 @@ describe('Test videos history', function () {
108 } 100 }
109 101
110 { 102 {
111 const resDetail = await getVideoWithToken(server.url, server.accessToken, video1UUID) 103 const videoDetails = await server.videos.getWithToken({ id: video1UUID })
112 const videoDetails: VideoDetails = resDetail.body
113 104
114 expect(videoDetails.userHistory).to.not.be.undefined 105 expect(videoDetails.userHistory).to.not.be.undefined
115 expect(videoDetails.userHistory.currentTime).to.equal(3) 106 expect(videoDetails.userHistory.currentTime).to.equal(3)
116 } 107 }
117 108
118 { 109 {
119 const resDetail = await getVideoWithToken(server.url, server.accessToken, video2UUID) 110 const videoDetails = await server.videos.getWithToken({ id: video2UUID })
120 const videoDetails: VideoDetails = resDetail.body
121 111
122 expect(videoDetails.userHistory).to.not.be.undefined 112 expect(videoDetails.userHistory).to.not.be.undefined
123 expect(videoDetails.userHistory.currentTime).to.equal(8) 113 expect(videoDetails.userHistory.currentTime).to.equal(8)
124 } 114 }
125 115
126 { 116 {
127 const resDetail = await getVideoWithToken(server.url, server.accessToken, video3UUID) 117 const videoDetails = await server.videos.getWithToken({ id: video3UUID })
128 const videoDetails: VideoDetails = resDetail.body
129 118
130 expect(videoDetails.userHistory).to.be.undefined 119 expect(videoDetails.userHistory).to.be.undefined
131 } 120 }
@@ -133,71 +122,64 @@ describe('Test videos history', function () {
133 122
134 it('Should have these videos when listing my history', async function () { 123 it('Should have these videos when listing my history', async function () {
135 video3WatchedDate = new Date() 124 video3WatchedDate = new Date()
136 await userWatchVideo(server.url, server.accessToken, video3UUID, 2) 125 await command.wathVideo({ videoId: video3UUID, currentTime: 2 })
137 126
138 const res = await listMyVideosHistory(server.url, server.accessToken) 127 const body = await command.list()
139 128
140 expect(res.body.total).to.equal(3) 129 expect(body.total).to.equal(3)
141 130
142 const videos: Video[] = res.body.data 131 const videos = body.data
143 expect(videos[0].name).to.equal('video 3') 132 expect(videos[0].name).to.equal('video 3')
144 expect(videos[1].name).to.equal('video 1') 133 expect(videos[1].name).to.equal('video 1')
145 expect(videos[2].name).to.equal('video 2') 134 expect(videos[2].name).to.equal('video 2')
146 }) 135 })
147 136
148 it('Should not have videos history on another user', async function () { 137 it('Should not have videos history on another user', async function () {
149 const res = await listMyVideosHistory(server.url, userAccessToken) 138 const body = await command.list({ token: userAccessToken })
150 139
151 expect(res.body.total).to.equal(0) 140 expect(body.total).to.equal(0)
152 expect(res.body.data).to.have.lengthOf(0) 141 expect(body.data).to.have.lengthOf(0)
153 }) 142 })
154 143
155 it('Should be able to search through videos in my history', async function () { 144 it('Should be able to search through videos in my history', async function () {
156 const res = await listMyVideosHistory(server.url, server.accessToken, '2') 145 const body = await command.list({ search: '2' })
157 146 expect(body.total).to.equal(1)
158 expect(res.body.total).to.equal(1)
159 147
160 const videos: Video[] = res.body.data 148 const videos = body.data
161 expect(videos[0].name).to.equal('video 2') 149 expect(videos[0].name).to.equal('video 2')
162 }) 150 })
163 151
164 it('Should clear my history', async function () { 152 it('Should clear my history', async function () {
165 await removeMyVideosHistory(server.url, server.accessToken, video3WatchedDate.toISOString()) 153 await command.remove({ beforeDate: video3WatchedDate.toISOString() })
166 }) 154 })
167 155
168 it('Should have my history cleared', async function () { 156 it('Should have my history cleared', async function () {
169 const res = await listMyVideosHistory(server.url, server.accessToken) 157 const body = await command.list()
158 expect(body.total).to.equal(1)
170 159
171 expect(res.body.total).to.equal(1) 160 const videos = body.data
172
173 const videos: Video[] = res.body.data
174 expect(videos[0].name).to.equal('video 3') 161 expect(videos[0].name).to.equal('video 3')
175 }) 162 })
176 163
177 it('Should disable videos history', async function () { 164 it('Should disable videos history', async function () {
178 await updateMyUser({ 165 await server.users.updateMe({
179 url: server.url,
180 accessToken: server.accessToken,
181 videosHistoryEnabled: false 166 videosHistoryEnabled: false
182 }) 167 })
183 168
184 await userWatchVideo(server.url, server.accessToken, video2UUID, 8, HttpStatusCode.CONFLICT_409) 169 await command.wathVideo({ videoId: video2UUID, currentTime: 8, expectedStatus: HttpStatusCode.CONFLICT_409 })
185 }) 170 })
186 171
187 it('Should re-enable videos history', async function () { 172 it('Should re-enable videos history', async function () {
188 await updateMyUser({ 173 await server.users.updateMe({
189 url: server.url,
190 accessToken: server.accessToken,
191 videosHistoryEnabled: true 174 videosHistoryEnabled: true
192 }) 175 })
193 176
194 await userWatchVideo(server.url, server.accessToken, video1UUID, 8) 177 await command.wathVideo({ videoId: video1UUID, currentTime: 8 })
195
196 const res = await listMyVideosHistory(server.url, server.accessToken)
197 178
198 expect(res.body.total).to.equal(2) 179 const body = await command.list()
180 expect(body.total).to.equal(2)
199 181
200 const videos: Video[] = res.body.data 182 const videos = body.data
201 expect(videos[0].name).to.equal('video 1') 183 expect(videos[0].name).to.equal('video 1')
202 expect(videos[1].name).to.equal('video 3') 184 expect(videos[1].name).to.equal('video 3')
203 }) 185 })
@@ -205,30 +187,29 @@ describe('Test videos history', function () {
205 it('Should not clean old history', async function () { 187 it('Should not clean old history', async function () {
206 this.timeout(50000) 188 this.timeout(50000)
207 189
208 killallServers([ server ]) 190 await killallServers([ server ])
209 191
210 await reRunServer(server, { history: { videos: { max_age: '10 days' } } }) 192 await server.run({ history: { videos: { max_age: '10 days' } } })
211 193
212 await wait(6000) 194 await wait(6000)
213 195
214 // Should still have history 196 // Should still have history
215 197
216 const res = await listMyVideosHistory(server.url, server.accessToken) 198 const body = await command.list()
217 199 expect(body.total).to.equal(2)
218 expect(res.body.total).to.equal(2)
219 }) 200 })
220 201
221 it('Should clean old history', async function () { 202 it('Should clean old history', async function () {
222 this.timeout(50000) 203 this.timeout(50000)
223 204
224 killallServers([ server ]) 205 await killallServers([ server ])
225 206
226 await reRunServer(server, { history: { videos: { max_age: '5 seconds' } } }) 207 await server.run({ history: { videos: { max_age: '5 seconds' } } })
227 208
228 await wait(6000) 209 await wait(6000)
229 210
230 const res = await listMyVideosHistory(server.url, server.accessToken) 211 const body = await command.list()
231 expect(res.body.total).to.equal(0) 212 expect(body.total).to.equal(0)
232 }) 213 })
233 214
234 after(async function () { 215 after(async function () {
diff --git a/server/tests/api/videos/videos-overview.ts b/server/tests/api/videos/videos-overview.ts
index c266a1dc5..70aa66549 100644
--- a/server/tests/api/videos/videos-overview.ts
+++ b/server/tests/api/videos/videos-overview.ts
@@ -2,29 +2,15 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5 5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, wait } from '@shared/extra-utils'
6import { 6import { VideosOverview } from '@shared/models'
7 cleanupTests,
8 flushAndRunServer,
9 generateUserAccessToken,
10 ServerInfo,
11 setAccessTokensToServers,
12 uploadVideo,
13 wait
14} from '../../../../shared/extra-utils'
15import { getVideosOverview, getVideosOverviewWithToken } from '../../../../shared/extra-utils/overviews/overviews'
16import { VideosOverview } from '../../../../shared/models/overviews'
17import { addAccountToAccountBlocklist } from '@shared/extra-utils/users/blocklist'
18import { Response } from 'superagent'
19 7
20const expect = chai.expect 8const expect = chai.expect
21 9
22describe('Test a videos overview', function () { 10describe('Test a videos overview', function () {
23 let server: ServerInfo = null 11 let server: PeerTubeServer = null
24
25 function testOverviewCount (res: Response, expected: number) {
26 const overview: VideosOverview = res.body
27 12
13 function testOverviewCount (overview: VideosOverview, expected: number) {
28 expect(overview.tags).to.have.lengthOf(expected) 14 expect(overview.tags).to.have.lengthOf(expected)
29 expect(overview.categories).to.have.lengthOf(expected) 15 expect(overview.categories).to.have.lengthOf(expected)
30 expect(overview.channels).to.have.lengthOf(expected) 16 expect(overview.channels).to.have.lengthOf(expected)
@@ -33,15 +19,15 @@ describe('Test a videos overview', function () {
33 before(async function () { 19 before(async function () {
34 this.timeout(30000) 20 this.timeout(30000)
35 21
36 server = await flushAndRunServer(1) 22 server = await createSingleServer(1)
37 23
38 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
39 }) 25 })
40 26
41 it('Should send empty overview', async function () { 27 it('Should send empty overview', async function () {
42 const res = await getVideosOverview(server.url, 1) 28 const body = await server.overviews.getVideos({ page: 1 })
43 29
44 testOverviewCount(res, 0) 30 testOverviewCount(body, 0)
45 }) 31 })
46 32
47 it('Should upload 5 videos in a specific category, tag and channel but not include them in overview', async function () { 33 it('Should upload 5 videos in a specific category, tag and channel but not include them in overview', async function () {
@@ -49,40 +35,45 @@ describe('Test a videos overview', function () {
49 35
50 await wait(3000) 36 await wait(3000)
51 37
52 await uploadVideo(server.url, server.accessToken, { 38 await server.videos.upload({
53 name: 'video 0', 39 attributes: {
54 category: 3, 40 name: 'video 0',
55 tags: [ 'coucou1', 'coucou2' ] 41 category: 3,
42 tags: [ 'coucou1', 'coucou2' ]
43 }
56 }) 44 })
57 45
58 const res = await getVideosOverview(server.url, 1) 46 const body = await server.overviews.getVideos({ page: 1 })
59 47
60 testOverviewCount(res, 0) 48 testOverviewCount(body, 0)
61 }) 49 })
62 50
63 it('Should upload another video and include all videos in the overview', async function () { 51 it('Should upload another video and include all videos in the overview', async function () {
64 this.timeout(30000) 52 this.timeout(30000)
65 53
66 for (let i = 1; i < 6; i++) { 54 {
67 await uploadVideo(server.url, server.accessToken, { 55 for (let i = 1; i < 6; i++) {
68 name: 'video ' + i, 56 await server.videos.upload({
69 category: 3, 57 attributes: {
70 tags: [ 'coucou1', 'coucou2' ] 58 name: 'video ' + i,
71 }) 59 category: 3,
60 tags: [ 'coucou1', 'coucou2' ]
61 }
62 })
63 }
64
65 await wait(3000)
72 } 66 }
73 67
74 await wait(3000)
75
76 { 68 {
77 const res = await getVideosOverview(server.url, 1) 69 const body = await server.overviews.getVideos({ page: 1 })
78 70
79 testOverviewCount(res, 1) 71 testOverviewCount(body, 1)
80 } 72 }
81 73
82 { 74 {
83 const res = await getVideosOverview(server.url, 2) 75 const overview = await server.overviews.getVideos({ page: 2 })
84 76
85 const overview: VideosOverview = res.body
86 expect(overview.tags).to.have.lengthOf(1) 77 expect(overview.tags).to.have.lengthOf(1)
87 expect(overview.categories).to.have.lengthOf(0) 78 expect(overview.categories).to.have.lengthOf(0)
88 expect(overview.channels).to.have.lengthOf(0) 79 expect(overview.channels).to.have.lengthOf(0)
@@ -90,20 +81,10 @@ describe('Test a videos overview', function () {
90 }) 81 })
91 82
92 it('Should have the correct overview', async function () { 83 it('Should have the correct overview', async function () {
93 const res1 = await getVideosOverview(server.url, 1) 84 const overview1 = await server.overviews.getVideos({ page: 1 })
94 const res2 = await getVideosOverview(server.url, 2) 85 const overview2 = await server.overviews.getVideos({ page: 2 })
95
96 const overview1: VideosOverview = res1.body
97 const overview2: VideosOverview = res2.body
98
99 const tmp = [
100 overview1.tags,
101 overview1.categories,
102 overview1.channels,
103 overview2.tags
104 ]
105 86
106 for (const arr of tmp) { 87 for (const arr of [ overview1.tags, overview1.categories, overview1.channels, overview2.tags ]) {
107 expect(arr).to.have.lengthOf(1) 88 expect(arr).to.have.lengthOf(1)
108 89
109 const obj = arr[0] 90 const obj = arr[0]
@@ -127,20 +108,20 @@ describe('Test a videos overview', function () {
127 }) 108 })
128 109
129 it('Should hide muted accounts', async function () { 110 it('Should hide muted accounts', async function () {
130 const token = await generateUserAccessToken(server, 'choco') 111 const token = await server.users.generateUserAndToken('choco')
131 112
132 await addAccountToAccountBlocklist(server.url, token, 'root@' + server.host) 113 await server.blocklist.addToMyBlocklist({ token, account: 'root@' + server.host })
133 114
134 { 115 {
135 const res = await getVideosOverview(server.url, 1) 116 const body = await server.overviews.getVideos({ page: 1 })
136 117
137 testOverviewCount(res, 1) 118 testOverviewCount(body, 1)
138 } 119 }
139 120
140 { 121 {
141 const res = await getVideosOverviewWithToken(server.url, 1, token) 122 const body = await server.overviews.getVideos({ page: 1, token })
142 123
143 testOverviewCount(res, 0) 124 testOverviewCount(body, 0)
144 } 125 }
145 }) 126 })
146 127
diff --git a/server/tests/api/videos/videos-views-cleaner.ts b/server/tests/api/videos/videos-views-cleaner.ts
index b89f33217..82268b1be 100644
--- a/server/tests/api/videos/videos-views-cleaner.ts
+++ b/server/tests/api/videos/videos-views-cleaner.ts
@@ -1,19 +1,14 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 closeAllSequelize, 7 createMultipleServers,
8 countVideoViewsOf,
9 doubleFollow, 8 doubleFollow,
10 flushAndRunMultipleServers,
11 killallServers, 9 killallServers,
12 reRunServer, 10 PeerTubeServer,
13 ServerInfo,
14 setAccessTokensToServers, 11 setAccessTokensToServers,
15 uploadVideoAndGetId,
16 viewVideo,
17 wait, 12 wait,
18 waitJobs 13 waitJobs
19} from '../../../../shared/extra-utils' 14} from '../../../../shared/extra-utils'
@@ -21,7 +16,7 @@ import {
21const expect = chai.expect 16const expect = chai.expect
22 17
23describe('Test video views cleaner', function () { 18describe('Test video views cleaner', function () {
24 let servers: ServerInfo[] 19 let servers: PeerTubeServer[]
25 20
26 let videoIdServer1: string 21 let videoIdServer1: string
27 let videoIdServer2: string 22 let videoIdServer2: string
@@ -29,20 +24,20 @@ describe('Test video views cleaner', function () {
29 before(async function () { 24 before(async function () {
30 this.timeout(120000) 25 this.timeout(120000)
31 26
32 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
33 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
34 29
35 await doubleFollow(servers[0], servers[1]) 30 await doubleFollow(servers[0], servers[1])
36 31
37 videoIdServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' })).uuid 32 videoIdServer1 = (await servers[0].videos.quickUpload({ name: 'video server 1' })).uuid
38 videoIdServer2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' })).uuid 33 videoIdServer2 = (await servers[1].videos.quickUpload({ name: 'video server 2' })).uuid
39 34
40 await waitJobs(servers) 35 await waitJobs(servers)
41 36
42 await viewVideo(servers[0].url, videoIdServer1) 37 await servers[0].videos.view({ id: videoIdServer1 })
43 await viewVideo(servers[1].url, videoIdServer1) 38 await servers[1].videos.view({ id: videoIdServer1 })
44 await viewVideo(servers[0].url, videoIdServer2) 39 await servers[0].videos.view({ id: videoIdServer2 })
45 await viewVideo(servers[1].url, videoIdServer2) 40 await servers[1].videos.view({ id: videoIdServer2 })
46 41
47 await waitJobs(servers) 42 await waitJobs(servers)
48 }) 43 })
@@ -50,9 +45,9 @@ describe('Test video views cleaner', function () {
50 it('Should not clean old video views', async function () { 45 it('Should not clean old video views', async function () {
51 this.timeout(50000) 46 this.timeout(50000)
52 47
53 killallServers([ servers[0] ]) 48 await killallServers([ servers[0] ])
54 49
55 await reRunServer(servers[0], { views: { videos: { remote: { max_age: '10 days' } } } }) 50 await servers[0].run({ views: { videos: { remote: { max_age: '10 days' } } } })
56 51
57 await wait(6000) 52 await wait(6000)
58 53
@@ -60,14 +55,14 @@ describe('Test video views cleaner', function () {
60 55
61 { 56 {
62 for (const server of servers) { 57 for (const server of servers) {
63 const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) 58 const total = await server.sql.countVideoViewsOf(videoIdServer1)
64 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views') 59 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views')
65 } 60 }
66 } 61 }
67 62
68 { 63 {
69 for (const server of servers) { 64 for (const server of servers) {
70 const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer2) 65 const total = await server.sql.countVideoViewsOf(videoIdServer2)
71 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views') 66 expect(total).to.equal(2, 'Server ' + server.serverNumber + ' does not have the correct amount of views')
72 } 67 }
73 } 68 }
@@ -76,9 +71,9 @@ describe('Test video views cleaner', function () {
76 it('Should clean old video views', async function () { 71 it('Should clean old video views', async function () {
77 this.timeout(50000) 72 this.timeout(50000)
78 73
79 killallServers([ servers[0] ]) 74 await killallServers([ servers[0] ])
80 75
81 await reRunServer(servers[0], { views: { videos: { remote: { max_age: '5 seconds' } } } }) 76 await servers[0].run({ views: { videos: { remote: { max_age: '5 seconds' } } } })
82 77
83 await wait(6000) 78 await wait(6000)
84 79
@@ -86,23 +81,21 @@ describe('Test video views cleaner', function () {
86 81
87 { 82 {
88 for (const server of servers) { 83 for (const server of servers) {
89 const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) 84 const total = await server.sql.countVideoViewsOf(videoIdServer1)
90 expect(total).to.equal(2) 85 expect(total).to.equal(2)
91 } 86 }
92 } 87 }
93 88
94 { 89 {
95 const totalServer1 = await countVideoViewsOf(servers[0].internalServerNumber, videoIdServer2) 90 const totalServer1 = await servers[0].sql.countVideoViewsOf(videoIdServer2)
96 expect(totalServer1).to.equal(0) 91 expect(totalServer1).to.equal(0)
97 92
98 const totalServer2 = await countVideoViewsOf(servers[1].internalServerNumber, videoIdServer2) 93 const totalServer2 = await servers[1].sql.countVideoViewsOf(videoIdServer2)
99 expect(totalServer2).to.equal(2) 94 expect(totalServer2).to.equal(2)
100 } 95 }
101 }) 96 })
102 97
103 after(async function () { 98 after(async function () {
104 await closeAllSequelize(servers)
105
106 await cleanupTests(servers) 99 await cleanupTests(servers)
107 }) 100 })
108}) 101})
diff --git a/server/tests/cli/create-import-video-file-job.ts b/server/tests/cli/create-import-video-file-job.ts
index 49758ff56..bddcff5e7 100644
--- a/server/tests/cli/create-import-video-file-job.ts
+++ b/server/tests/cli/create-import-video-file-job.ts
@@ -2,21 +2,8 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoFile } from '@shared/models/videos/video-file.model' 5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { 6import { VideoFile } from '@shared/models'
7 cleanupTests,
8 doubleFollow,
9 execCLI,
10 flushAndRunMultipleServers,
11 getEnvCli,
12 getVideo,
13 getVideosList,
14 ServerInfo,
15 setAccessTokensToServers,
16 uploadVideo
17} from '../../../shared/extra-utils'
18import { waitJobs } from '../../../shared/extra-utils/server/jobs'
19import { VideoDetails } from '../../../shared/models/videos'
20 7
21const expect = chai.expect 8const expect = chai.expect
22 9
@@ -33,7 +20,7 @@ function assertVideoProperties (video: VideoFile, resolution: number, extname: s
33describe('Test create import video jobs', function () { 20describe('Test create import video jobs', function () {
34 this.timeout(60000) 21 this.timeout(60000)
35 22
36 let servers: ServerInfo[] = [] 23 let servers: PeerTubeServer[] = []
37 let video1UUID: string 24 let video1UUID: string
38 let video2UUID: string 25 let video2UUID: string
39 26
@@ -41,56 +28,61 @@ describe('Test create import video jobs', function () {
41 this.timeout(90000) 28 this.timeout(90000)
42 29
43 // Run server 2 to have transcoding enabled 30 // Run server 2 to have transcoding enabled
44 servers = await flushAndRunMultipleServers(2) 31 servers = await createMultipleServers(2)
45 await setAccessTokensToServers(servers) 32 await setAccessTokensToServers(servers)
46 33
47 await doubleFollow(servers[0], servers[1]) 34 await doubleFollow(servers[0], servers[1])
48 35
49 // Upload two videos for our needs 36 // Upload two videos for our needs
50 const res1 = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video1' }) 37 {
51 video1UUID = res1.body.video.uuid 38 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video1' } })
52 const res2 = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' }) 39 video1UUID = uuid
53 video2UUID = res2.body.video.uuid 40 }
41
42 {
43 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video2' } })
44 video2UUID = uuid
45 }
54 46
55 // Transcoding 47 // Transcoding
56 await waitJobs(servers) 48 await waitJobs(servers)
57 }) 49 })
58 50
59 it('Should run a import job on video 1 with a lower resolution', async function () { 51 it('Should run a import job on video 1 with a lower resolution', async function () {
60 const env = getEnvCli(servers[0]) 52 const command = `npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short-480.webm`
61 await execCLI(`${env} npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short-480.webm`) 53 await servers[0].cli.execWithEnv(command)
62 54
63 await waitJobs(servers) 55 await waitJobs(servers)
64 56
65 for (const server of servers) { 57 for (const server of servers) {
66 const { data: videos } = (await getVideosList(server.url)).body 58 const { data: videos } = await server.videos.list()
67 expect(videos).to.have.lengthOf(2) 59 expect(videos).to.have.lengthOf(2)
68 60
69 const video = videos.find(({ uuid }) => uuid === video1UUID) 61 const video = videos.find(({ uuid }) => uuid === video1UUID)
70 const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body 62 const videoDetails = await server.videos.get({ id: video.uuid })
71 63
72 expect(videoDetail.files).to.have.lengthOf(2) 64 expect(videoDetails.files).to.have.lengthOf(2)
73 const [ originalVideo, transcodedVideo ] = videoDetail.files 65 const [ originalVideo, transcodedVideo ] = videoDetails.files
74 assertVideoProperties(originalVideo, 720, 'webm', 218910) 66 assertVideoProperties(originalVideo, 720, 'webm', 218910)
75 assertVideoProperties(transcodedVideo, 480, 'webm', 69217) 67 assertVideoProperties(transcodedVideo, 480, 'webm', 69217)
76 } 68 }
77 }) 69 })
78 70
79 it('Should run a import job on video 2 with the same resolution and a different extension', async function () { 71 it('Should run a import job on video 2 with the same resolution and a different extension', async function () {
80 const env = getEnvCli(servers[1]) 72 const command = `npm run create-import-video-file-job -- -v ${video2UUID} -i server/tests/fixtures/video_short.ogv`
81 await execCLI(`${env} npm run create-import-video-file-job -- -v ${video2UUID} -i server/tests/fixtures/video_short.ogv`) 73 await servers[1].cli.execWithEnv(command)
82 74
83 await waitJobs(servers) 75 await waitJobs(servers)
84 76
85 for (const server of servers) { 77 for (const server of servers) {
86 const { data: videos } = (await getVideosList(server.url)).body 78 const { data: videos } = await server.videos.list()
87 expect(videos).to.have.lengthOf(2) 79 expect(videos).to.have.lengthOf(2)
88 80
89 const video = videos.find(({ uuid }) => uuid === video2UUID) 81 const video = videos.find(({ uuid }) => uuid === video2UUID)
90 const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body 82 const videoDetails = await server.videos.get({ id: video.uuid })
91 83
92 expect(videoDetail.files).to.have.lengthOf(4) 84 expect(videoDetails.files).to.have.lengthOf(4)
93 const [ originalVideo, transcodedVideo420, transcodedVideo320, transcodedVideo240 ] = videoDetail.files 85 const [ originalVideo, transcodedVideo420, transcodedVideo320, transcodedVideo240 ] = videoDetails.files
94 assertVideoProperties(originalVideo, 720, 'ogv', 140849) 86 assertVideoProperties(originalVideo, 720, 'ogv', 140849)
95 assertVideoProperties(transcodedVideo420, 480, 'mp4') 87 assertVideoProperties(transcodedVideo420, 480, 'mp4')
96 assertVideoProperties(transcodedVideo320, 360, 'mp4') 88 assertVideoProperties(transcodedVideo320, 360, 'mp4')
@@ -99,20 +91,20 @@ describe('Test create import video jobs', function () {
99 }) 91 })
100 92
101 it('Should run a import job on video 2 with the same resolution and the same extension', async function () { 93 it('Should run a import job on video 2 with the same resolution and the same extension', async function () {
102 const env = getEnvCli(servers[0]) 94 const command = `npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short2.webm`
103 await execCLI(`${env} npm run create-import-video-file-job -- -v ${video1UUID} -i server/tests/fixtures/video_short2.webm`) 95 await servers[0].cli.execWithEnv(command)
104 96
105 await waitJobs(servers) 97 await waitJobs(servers)
106 98
107 for (const server of servers) { 99 for (const server of servers) {
108 const { data: videos } = (await getVideosList(server.url)).body 100 const { data: videos } = await server.videos.list()
109 expect(videos).to.have.lengthOf(2) 101 expect(videos).to.have.lengthOf(2)
110 102
111 const video = videos.find(({ uuid }) => uuid === video1UUID) 103 const video = videos.find(({ uuid }) => uuid === video1UUID)
112 const videoDetail: VideoDetails = (await getVideo(server.url, video.uuid)).body 104 const videoDetails = await server.videos.get({ id: video.uuid })
113 105
114 expect(videoDetail.files).to.have.lengthOf(2) 106 expect(videoDetails.files).to.have.lengthOf(2)
115 const [ video720, video480 ] = videoDetail.files 107 const [ video720, video480 ] = videoDetails.files
116 assertVideoProperties(video720, 720, 'webm', 942961) 108 assertVideoProperties(video720, 720, 'webm', 942961)
117 assertVideoProperties(video480, 480, 'webm', 69217) 109 assertVideoProperties(video480, 480, 'webm', 69217)
118 } 110 }
diff --git a/server/tests/cli/create-transcoding-job.ts b/server/tests/cli/create-transcoding-job.ts
index 5bc1687cd..df787ccdc 100644
--- a/server/tests/cli/create-transcoding-job.ts
+++ b/server/tests/cli/create-transcoding-job.ts
@@ -2,26 +2,19 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoDetails } from '../../../shared/models/videos'
6import { 5import {
7 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 execCLI, 9 PeerTubeServer,
10 flushAndRunMultipleServers,
11 getEnvCli,
12 getVideo,
13 getVideosList,
14 ServerInfo,
15 setAccessTokensToServers, 10 setAccessTokensToServers,
16 updateCustomSubConfig, 11 waitJobs
17 uploadVideo
18} from '../../../shared/extra-utils' 12} from '../../../shared/extra-utils'
19import { waitJobs } from '../../../shared/extra-utils/server/jobs'
20 13
21const expect = chai.expect 14const expect = chai.expect
22 15
23describe('Test create transcoding jobs', function () { 16describe('Test create transcoding jobs', function () {
24 let servers: ServerInfo[] = [] 17 let servers: PeerTubeServer[] = []
25 const videosUUID: string[] = [] 18 const videosUUID: string[] = []
26 19
27 const config = { 20 const config = {
@@ -46,16 +39,16 @@ describe('Test create transcoding jobs', function () {
46 this.timeout(60000) 39 this.timeout(60000)
47 40
48 // Run server 2 to have transcoding enabled 41 // Run server 2 to have transcoding enabled
49 servers = await flushAndRunMultipleServers(2) 42 servers = await createMultipleServers(2)
50 await setAccessTokensToServers(servers) 43 await setAccessTokensToServers(servers)
51 44
52 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 45 await servers[0].config.updateCustomSubConfig({ newConfig: config })
53 46
54 await doubleFollow(servers[0], servers[1]) 47 await doubleFollow(servers[0], servers[1])
55 48
56 for (let i = 1; i <= 5; i++) { 49 for (let i = 1; i <= 5; i++) {
57 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' + i }) 50 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' + i } })
58 videosUUID.push(res.body.video.uuid) 51 videosUUID.push(uuid)
59 } 52 }
60 53
61 await waitJobs(servers) 54 await waitJobs(servers)
@@ -65,13 +58,11 @@ describe('Test create transcoding jobs', function () {
65 this.timeout(30000) 58 this.timeout(30000)
66 59
67 for (const server of servers) { 60 for (const server of servers) {
68 const res = await getVideosList(server.url) 61 const { data } = await server.videos.list()
69 const videos = res.body.data 62 expect(data).to.have.lengthOf(videosUUID.length)
70 expect(videos).to.have.lengthOf(videosUUID.length)
71 63
72 for (const video of videos) { 64 for (const video of data) {
73 const res2 = await getVideo(server.url, video.uuid) 65 const videoDetail = await server.videos.get({ id: video.uuid })
74 const videoDetail: VideoDetails = res2.body
75 expect(videoDetail.files).to.have.lengthOf(1) 66 expect(videoDetail.files).to.have.lengthOf(1)
76 expect(videoDetail.streamingPlaylists).to.have.lengthOf(0) 67 expect(videoDetail.streamingPlaylists).to.have.lengthOf(0)
77 } 68 }
@@ -81,20 +72,16 @@ describe('Test create transcoding jobs', function () {
81 it('Should run a transcoding job on video 2', async function () { 72 it('Should run a transcoding job on video 2', async function () {
82 this.timeout(60000) 73 this.timeout(60000)
83 74
84 const env = getEnvCli(servers[0]) 75 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[1]}`)
85 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[1]}`)
86
87 await waitJobs(servers) 76 await waitJobs(servers)
88 77
89 for (const server of servers) { 78 for (const server of servers) {
90 const res = await getVideosList(server.url) 79 const { data } = await server.videos.list()
91 const videos = res.body.data
92 80
93 let infoHashes: { [id: number]: string } 81 let infoHashes: { [id: number]: string }
94 82
95 for (const video of videos) { 83 for (const video of data) {
96 const res2 = await getVideo(server.url, video.uuid) 84 const videoDetail = await server.videos.get({ id: video.uuid })
97 const videoDetail: VideoDetails = res2.body
98 85
99 if (video.uuid === videosUUID[1]) { 86 if (video.uuid === videosUUID[1]) {
100 expect(videoDetail.files).to.have.lengthOf(4) 87 expect(videoDetail.files).to.have.lengthOf(4)
@@ -123,43 +110,38 @@ describe('Test create transcoding jobs', function () {
123 it('Should run a transcoding job on video 1 with resolution', async function () { 110 it('Should run a transcoding job on video 1 with resolution', async function () {
124 this.timeout(60000) 111 this.timeout(60000)
125 112
126 const env = getEnvCli(servers[0]) 113 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[0]} -r 480`)
127 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[0]} -r 480`)
128 114
129 await waitJobs(servers) 115 await waitJobs(servers)
130 116
131 for (const server of servers) { 117 for (const server of servers) {
132 const res = await getVideosList(server.url) 118 const { data } = await server.videos.list()
133 const videos = res.body.data 119 expect(data).to.have.lengthOf(videosUUID.length)
134 expect(videos).to.have.lengthOf(videosUUID.length)
135 120
136 const res2 = await getVideo(server.url, videosUUID[0]) 121 const videoDetails = await server.videos.get({ id: videosUUID[0] })
137 const videoDetail: VideoDetails = res2.body
138 122
139 expect(videoDetail.files).to.have.lengthOf(2) 123 expect(videoDetails.files).to.have.lengthOf(2)
140 expect(videoDetail.files[0].resolution.id).to.equal(720) 124 expect(videoDetails.files[0].resolution.id).to.equal(720)
141 expect(videoDetail.files[1].resolution.id).to.equal(480) 125 expect(videoDetails.files[1].resolution.id).to.equal(480)
142 126
143 expect(videoDetail.streamingPlaylists).to.have.lengthOf(0) 127 expect(videoDetails.streamingPlaylists).to.have.lengthOf(0)
144 } 128 }
145 }) 129 })
146 130
147 it('Should generate an HLS resolution', async function () { 131 it('Should generate an HLS resolution', async function () {
148 this.timeout(120000) 132 this.timeout(120000)
149 133
150 const env = getEnvCli(servers[0]) 134 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
151 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
152 135
153 await waitJobs(servers) 136 await waitJobs(servers)
154 137
155 for (const server of servers) { 138 for (const server of servers) {
156 const res = await getVideo(server.url, videosUUID[2]) 139 const videoDetails = await server.videos.get({ id: videosUUID[2] })
157 const videoDetail: VideoDetails = res.body
158 140
159 expect(videoDetail.files).to.have.lengthOf(1) 141 expect(videoDetails.files).to.have.lengthOf(1)
160 expect(videoDetail.streamingPlaylists).to.have.lengthOf(1) 142 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
161 143
162 const files = videoDetail.streamingPlaylists[0].files 144 const files = videoDetails.streamingPlaylists[0].files
163 expect(files).to.have.lengthOf(1) 145 expect(files).to.have.lengthOf(1)
164 expect(files[0].resolution.id).to.equal(480) 146 expect(files[0].resolution.id).to.equal(480)
165 } 147 }
@@ -168,16 +150,14 @@ describe('Test create transcoding jobs', function () {
168 it('Should not duplicate an HLS resolution', async function () { 150 it('Should not duplicate an HLS resolution', async function () {
169 this.timeout(120000) 151 this.timeout(120000)
170 152
171 const env = getEnvCli(servers[0]) 153 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
172 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[2]} --generate-hls -r 480`)
173 154
174 await waitJobs(servers) 155 await waitJobs(servers)
175 156
176 for (const server of servers) { 157 for (const server of servers) {
177 const res = await getVideo(server.url, videosUUID[2]) 158 const videoDetails = await server.videos.get({ id: videosUUID[2] })
178 const videoDetail: VideoDetails = res.body
179 159
180 const files = videoDetail.streamingPlaylists[0].files 160 const files = videoDetails.streamingPlaylists[0].files
181 expect(files).to.have.lengthOf(1) 161 expect(files).to.have.lengthOf(1)
182 expect(files[0].resolution.id).to.equal(480) 162 expect(files[0].resolution.id).to.equal(480)
183 } 163 }
@@ -186,19 +166,17 @@ describe('Test create transcoding jobs', function () {
186 it('Should generate all HLS resolutions', async function () { 166 it('Should generate all HLS resolutions', async function () {
187 this.timeout(120000) 167 this.timeout(120000)
188 168
189 const env = getEnvCli(servers[0]) 169 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[3]} --generate-hls`)
190 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[3]} --generate-hls`)
191 170
192 await waitJobs(servers) 171 await waitJobs(servers)
193 172
194 for (const server of servers) { 173 for (const server of servers) {
195 const res = await getVideo(server.url, videosUUID[3]) 174 const videoDetails = await server.videos.get({ id: videosUUID[3] })
196 const videoDetail: VideoDetails = res.body
197 175
198 expect(videoDetail.files).to.have.lengthOf(1) 176 expect(videoDetails.files).to.have.lengthOf(1)
199 expect(videoDetail.streamingPlaylists).to.have.lengthOf(1) 177 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
200 178
201 const files = videoDetail.streamingPlaylists[0].files 179 const files = videoDetails.streamingPlaylists[0].files
202 expect(files).to.have.lengthOf(4) 180 expect(files).to.have.lengthOf(4)
203 } 181 }
204 }) 182 })
@@ -207,20 +185,18 @@ describe('Test create transcoding jobs', function () {
207 this.timeout(120000) 185 this.timeout(120000)
208 186
209 config.transcoding.hls.enabled = true 187 config.transcoding.hls.enabled = true
210 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) 188 await servers[0].config.updateCustomSubConfig({ newConfig: config })
211 189
212 const env = getEnvCli(servers[0]) 190 await servers[0].cli.execWithEnv(`npm run create-transcoding-job -- -v ${videosUUID[4]}`)
213 await execCLI(`${env} npm run create-transcoding-job -- -v ${videosUUID[4]}`)
214 191
215 await waitJobs(servers) 192 await waitJobs(servers)
216 193
217 for (const server of servers) { 194 for (const server of servers) {
218 const res = await getVideo(server.url, videosUUID[4]) 195 const videoDetails = await server.videos.get({ id: videosUUID[4] })
219 const videoDetail: VideoDetails = res.body
220 196
221 expect(videoDetail.files).to.have.lengthOf(4) 197 expect(videoDetails.files).to.have.lengthOf(4)
222 expect(videoDetail.streamingPlaylists).to.have.lengthOf(1) 198 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
223 expect(videoDetail.streamingPlaylists[0].files).to.have.lengthOf(4) 199 expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(4)
224 } 200 }
225 }) 201 })
226 202
diff --git a/server/tests/cli/optimize-old-videos.ts b/server/tests/cli/optimize-old-videos.ts
index 91a1c9cc4..579b2e7d8 100644
--- a/server/tests/cli/optimize-old-videos.ts
+++ b/server/tests/cli/optimize-old-videos.ts
@@ -2,38 +2,30 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { join } from 'path'
6import { 5import {
7 buildServerDirectory,
8 cleanupTests, 6 cleanupTests,
7 createMultipleServers,
9 doubleFollow, 8 doubleFollow,
10 execCLI,
11 flushAndRunMultipleServers,
12 generateHighBitrateVideo, 9 generateHighBitrateVideo,
13 getEnvCli, 10 PeerTubeServer,
14 getVideo,
15 getVideosList,
16 ServerInfo,
17 setAccessTokensToServers, 11 setAccessTokensToServers,
18 uploadVideo, 12 wait,
19 viewVideo, 13 waitJobs
20 wait 14} from '@shared/extra-utils'
21} from '../../../shared/extra-utils' 15import { getMaxBitrate, VideoResolution } from '@shared/models'
22import { waitJobs } from '../../../shared/extra-utils/server/jobs'
23import { getMaxBitrate, Video, VideoDetails, VideoResolution } from '../../../shared/models/videos'
24import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffprobe-utils' 16import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffprobe-utils'
25import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' 17import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants'
26 18
27const expect = chai.expect 19const expect = chai.expect
28 20
29describe('Test optimize old videos', function () { 21describe('Test optimize old videos', function () {
30 let servers: ServerInfo[] = [] 22 let servers: PeerTubeServer[] = []
31 23
32 before(async function () { 24 before(async function () {
33 this.timeout(200000) 25 this.timeout(200000)
34 26
35 // Run server 2 to have transcoding enabled 27 // Run server 2 to have transcoding enabled
36 servers = await flushAndRunMultipleServers(2) 28 servers = await createMultipleServers(2)
37 await setAccessTokensToServers(servers) 29 await setAccessTokensToServers(servers)
38 30
39 await doubleFollow(servers[0], servers[1]) 31 await doubleFollow(servers[0], servers[1])
@@ -48,8 +40,8 @@ describe('Test optimize old videos', function () {
48 } 40 }
49 41
50 // Upload two videos for our needs 42 // Upload two videos for our needs
51 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video1', fixture: tempFixturePath }) 43 await servers[0].videos.upload({ attributes: { name: 'video1', fixture: tempFixturePath } })
52 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video2', fixture: tempFixturePath }) 44 await servers[0].videos.upload({ attributes: { name: 'video2', fixture: tempFixturePath } })
53 45
54 await waitJobs(servers) 46 await waitJobs(servers)
55 }) 47 })
@@ -58,14 +50,12 @@ describe('Test optimize old videos', function () {
58 this.timeout(30000) 50 this.timeout(30000)
59 51
60 for (const server of servers) { 52 for (const server of servers) {
61 const res = await getVideosList(server.url) 53 const { data } = await server.videos.list()
62 const videos = res.body.data 54 expect(data).to.have.lengthOf(2)
63 expect(videos).to.have.lengthOf(2) 55
64 56 for (const video of data) {
65 for (const video of videos) { 57 const videoDetails = await server.videos.get({ id: video.uuid })
66 const res2 = await getVideo(server.url, video.uuid) 58 expect(videoDetails.files).to.have.lengthOf(1)
67 const videoDetail: VideoDetails = res2.body
68 expect(videoDetail.files).to.have.lengthOf(1)
69 } 59 }
70 } 60 }
71 }) 61 })
@@ -73,34 +63,29 @@ describe('Test optimize old videos', function () {
73 it('Should run optimize script', async function () { 63 it('Should run optimize script', async function () {
74 this.timeout(200000) 64 this.timeout(200000)
75 65
76 const env = getEnvCli(servers[0]) 66 await servers[0].cli.execWithEnv('npm run optimize-old-videos')
77 await execCLI(`${env} npm run optimize-old-videos`)
78
79 await waitJobs(servers) 67 await waitJobs(servers)
80 68
81 for (const server of servers) { 69 for (const server of servers) {
82 const res = await getVideosList(server.url) 70 const { data } = await server.videos.list()
83 const videos: Video[] = res.body.data 71 expect(data).to.have.lengthOf(2)
84
85 expect(videos).to.have.lengthOf(2)
86 72
87 for (const video of videos) { 73 for (const video of data) {
88 await viewVideo(server.url, video.uuid) 74 await server.videos.view({ id: video.uuid })
89 75
90 // Refresh video 76 // Refresh video
91 await waitJobs(servers) 77 await waitJobs(servers)
92 await wait(5000) 78 await wait(5000)
93 await waitJobs(servers) 79 await waitJobs(servers)
94 80
95 const res2 = await getVideo(server.url, video.uuid) 81 const videoDetails = await server.videos.get({ id: video.uuid })
96 const videosDetails: VideoDetails = res2.body
97 82
98 expect(videosDetails.files).to.have.lengthOf(1) 83 expect(videoDetails.files).to.have.lengthOf(1)
99 const file = videosDetails.files[0] 84 const file = videoDetails.files[0]
100 85
101 expect(file.size).to.be.below(8000000) 86 expect(file.size).to.be.below(8000000)
102 87
103 const path = buildServerDirectory(servers[0], join('videos', video.uuid + '-' + file.resolution.id + '.mp4')) 88 const path = servers[0].servers.buildWebTorrentFilePath(file.fileUrl)
104 const bitrate = await getVideoFileBitrate(path) 89 const bitrate = await getVideoFileBitrate(path)
105 const fps = await getVideoFileFPS(path) 90 const fps = await getVideoFileFPS(path)
106 const resolution = await getVideoFileResolution(path) 91 const resolution = await getVideoFileResolution(path)
diff --git a/server/tests/cli/peertube.ts b/server/tests/cli/peertube.ts
index fcf7e2e2e..f2a984962 100644
--- a/server/tests/cli/peertube.ts
+++ b/server/tests/cli/peertube.ts
@@ -2,97 +2,91 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { Video, VideoDetails } from '../../../shared'
6import { 5import {
7 addVideoChannel,
8 areHttpImportTestsDisabled, 6 areHttpImportTestsDisabled,
9 buildAbsoluteFixturePath, 7 buildAbsoluteFixturePath,
10 cleanupTests, 8 cleanupTests,
11 createUser, 9 CLICommand,
10 createSingleServer,
12 doubleFollow, 11 doubleFollow,
13 execCLI, 12 FIXTURE_URLS,
14 flushAndRunServer, 13 PeerTubeServer,
15 getEnvCli,
16 getLocalIdByUUID,
17 getVideo,
18 getVideosList,
19 removeVideo,
20 ServerInfo,
21 setAccessTokensToServers, 14 setAccessTokensToServers,
22 testHelloWorldRegisteredSettings, 15 testHelloWorldRegisteredSettings,
23 uploadVideoAndGetId,
24 userLogin,
25 waitJobs 16 waitJobs
26} from '../../../shared/extra-utils' 17} from '../../../shared/extra-utils'
27import { getYoutubeVideoUrl } from '../../../shared/extra-utils/videos/video-imports'
28 18
29describe('Test CLI wrapper', function () { 19describe('Test CLI wrapper', function () {
30 let server: ServerInfo 20 let server: PeerTubeServer
31 let userAccessToken: string 21 let userAccessToken: string
32 22
23 let cliCommand: CLICommand
24
33 const cmd = 'node ./dist/server/tools/peertube.js' 25 const cmd = 'node ./dist/server/tools/peertube.js'
34 26
35 before(async function () { 27 before(async function () {
36 this.timeout(30000) 28 this.timeout(30000)
37 29
38 server = await flushAndRunServer(1) 30 server = await createSingleServer(1)
39 await setAccessTokensToServers([ server ]) 31 await setAccessTokensToServers([ server ])
40 32
41 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user_1', password: 'super_password' }) 33 await server.users.create({ username: 'user_1', password: 'super_password' })
42 34
43 userAccessToken = await userLogin(server, { username: 'user_1', password: 'super_password' }) 35 userAccessToken = await server.login.getAccessToken({ username: 'user_1', password: 'super_password' })
44 36
45 { 37 {
46 const args = { name: 'user_channel', displayName: 'User channel', support: 'super support text' } 38 const attributes = { name: 'user_channel', displayName: 'User channel', support: 'super support text' }
47 await addVideoChannel(server.url, userAccessToken, args) 39 await server.channels.create({ token: userAccessToken, attributes })
48 } 40 }
41
42 cliCommand = server.cli
49 }) 43 })
50 44
51 describe('Authentication and instance selection', function () { 45 describe('Authentication and instance selection', function () {
52 46
47 it('Should get an access token', async function () {
48 const stdout = await cliCommand.execWithEnv(`${cmd} token --url ${server.url} --username user_1 --password super_password`)
49 const token = stdout.trim()
50
51 const body = await server.users.getMyInfo({ token })
52 expect(body.username).to.equal('user_1')
53 })
54
53 it('Should display no selected instance', async function () { 55 it('Should display no selected instance', async function () {
54 this.timeout(60000) 56 this.timeout(60000)
55 57
56 const env = getEnvCli(server) 58 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
57 const stdout = await execCLI(`${env} ${cmd} --help`)
58
59 expect(stdout).to.contain('no instance selected') 59 expect(stdout).to.contain('no instance selected')
60 }) 60 })
61 61
62 it('Should add a user', async function () { 62 it('Should add a user', async function () {
63 this.timeout(60000) 63 this.timeout(60000)
64 64
65 const env = getEnvCli(server) 65 await cliCommand.execWithEnv(`${cmd} auth add -u ${server.url} -U user_1 -p super_password`)
66 await execCLI(`${env} ${cmd} auth add -u ${server.url} -U user_1 -p super_password`)
67 }) 66 })
68 67
69 it('Should not fail to add a user if there is a slash at the end of the instance URL', async function () { 68 it('Should not fail to add a user if there is a slash at the end of the instance URL', async function () {
70 this.timeout(60000) 69 this.timeout(60000)
71 70
72 const env = getEnvCli(server) 71 let fullServerURL = server.url + '/'
73 let fullServerURL 72
74 fullServerURL = server.url + '/' 73 await cliCommand.execWithEnv(`${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
75 await execCLI(`${env} ${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
76 74
77 fullServerURL = server.url + '/asdfasdf' 75 fullServerURL = server.url + '/asdfasdf'
78 await execCLI(`${env} ${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`) 76 await cliCommand.execWithEnv(`${cmd} auth add -u ${fullServerURL} -U user_1 -p super_password`)
79 }) 77 })
80 78
81 it('Should default to this user', async function () { 79 it('Should default to this user', async function () {
82 this.timeout(60000) 80 this.timeout(60000)
83 81
84 const env = getEnvCli(server) 82 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
85 const stdout = await execCLI(`${env} ${cmd} --help`)
86
87 expect(stdout).to.contain(`instance ${server.url} selected`) 83 expect(stdout).to.contain(`instance ${server.url} selected`)
88 }) 84 })
89 85
90 it('Should remember the user', async function () { 86 it('Should remember the user', async function () {
91 this.timeout(60000) 87 this.timeout(60000)
92 88
93 const env = getEnvCli(server) 89 const stdout = await cliCommand.execWithEnv(`${cmd} auth list`)
94 const stdout = await execCLI(`${env} ${cmd} auth list`)
95
96 expect(stdout).to.contain(server.url) 90 expect(stdout).to.contain(server.url)
97 }) 91 })
98 }) 92 })
@@ -102,24 +96,17 @@ describe('Test CLI wrapper', function () {
102 it('Should upload a video', async function () { 96 it('Should upload a video', async function () {
103 this.timeout(60000) 97 this.timeout(60000)
104 98
105 const env = getEnvCli(server)
106
107 const fixture = buildAbsoluteFixturePath('60fps_720p_small.mp4') 99 const fixture = buildAbsoluteFixturePath('60fps_720p_small.mp4')
108
109 const params = `-f ${fixture} --video-name 'test upload' --channel-name user_channel --support 'support_text'` 100 const params = `-f ${fixture} --video-name 'test upload' --channel-name user_channel --support 'support_text'`
110 101
111 await execCLI(`${env} ${cmd} upload ${params}`) 102 await cliCommand.execWithEnv(`${cmd} upload ${params}`)
112 }) 103 })
113 104
114 it('Should have the video uploaded', async function () { 105 it('Should have the video uploaded', async function () {
115 const res = await getVideosList(server.url) 106 const { total, data } = await server.videos.list()
116 107 expect(total).to.equal(1)
117 expect(res.body.total).to.equal(1)
118
119 const videos: Video[] = res.body.data
120
121 const video: VideoDetails = (await getVideo(server.url, videos[0].uuid)).body
122 108
109 const video = await server.videos.get({ id: data[0].uuid })
123 expect(video.name).to.equal('test upload') 110 expect(video.name).to.equal('test upload')
124 expect(video.support).to.equal('support_text') 111 expect(video.support).to.equal('support_text')
125 expect(video.channel.name).to.equal('user_channel') 112 expect(video.channel.name).to.equal('user_channel')
@@ -130,11 +117,8 @@ describe('Test CLI wrapper', function () {
130 117
131 this.timeout(60000) 118 this.timeout(60000)
132 119
133 const env = getEnvCli(server) 120 const params = `--target-url ${FIXTURE_URLS.youtube} --channel-name user_channel`
134 121 await cliCommand.execWithEnv(`${cmd} import ${params}`)
135 const params = `--target-url ${getYoutubeVideoUrl()} --channel-name user_channel`
136
137 await execCLI(`${env} ${cmd} import ${params}`)
138 }) 122 })
139 123
140 it('Should have imported the video', async function () { 124 it('Should have imported the video', async function () {
@@ -144,21 +128,19 @@ describe('Test CLI wrapper', function () {
144 128
145 await waitJobs([ server ]) 129 await waitJobs([ server ])
146 130
147 const res = await getVideosList(server.url) 131 const { total, data } = await server.videos.list()
148 132 expect(total).to.equal(2)
149 expect(res.body.total).to.equal(2)
150 133
151 const videos: Video[] = res.body.data 134 const video = data.find(v => v.name === 'small video - youtube')
152 const video = videos.find(v => v.name === 'small video - youtube')
153 expect(video).to.not.be.undefined 135 expect(video).to.not.be.undefined
154 136
155 const videoDetails: VideoDetails = (await getVideo(server.url, video.id)).body 137 const videoDetails = await server.videos.get({ id: video.id })
156 expect(videoDetails.channel.name).to.equal('user_channel') 138 expect(videoDetails.channel.name).to.equal('user_channel')
157 expect(videoDetails.support).to.equal('super support text') 139 expect(videoDetails.support).to.equal('super support text')
158 expect(videoDetails.nsfw).to.be.false 140 expect(videoDetails.nsfw).to.be.false
159 141
160 // So we can reimport it 142 // So we can reimport it
161 await removeVideo(server.url, userAccessToken, video.id) 143 await server.videos.remove({ token: userAccessToken, id: video.id })
162 }) 144 })
163 145
164 it('Should import and override some imported attributes', async function () { 146 it('Should import and override some imported attributes', async function () {
@@ -166,23 +148,20 @@ describe('Test CLI wrapper', function () {
166 148
167 this.timeout(60000) 149 this.timeout(60000)
168 150
169 const env = getEnvCli(server) 151 const params = `--target-url ${FIXTURE_URLS.youtube} ` +
170 152 `--channel-name user_channel --video-name toto --nsfw --support support`
171 const params = `--target-url ${getYoutubeVideoUrl()} --channel-name user_channel --video-name toto --nsfw --support support` 153 await cliCommand.execWithEnv(`${cmd} import ${params}`)
172
173 await execCLI(`${env} ${cmd} import ${params}`)
174 154
175 await waitJobs([ server ]) 155 await waitJobs([ server ])
176 156
177 { 157 {
178 const res = await getVideosList(server.url) 158 const { total, data } = await server.videos.list()
179 expect(res.body.total).to.equal(2) 159 expect(total).to.equal(2)
180 160
181 const videos: Video[] = res.body.data 161 const video = data.find(v => v.name === 'toto')
182 const video = videos.find(v => v.name === 'toto')
183 expect(video).to.not.be.undefined 162 expect(video).to.not.be.undefined
184 163
185 const videoDetails: VideoDetails = (await getVideo(server.url, video.id)).body 164 const videoDetails = await server.videos.get({ id: video.id })
186 expect(videoDetails.channel.name).to.equal('user_channel') 165 expect(videoDetails.channel.name).to.equal('user_channel')
187 expect(videoDetails.support).to.equal('support') 166 expect(videoDetails.support).to.equal('support')
188 expect(videoDetails.nsfw).to.be.true 167 expect(videoDetails.nsfw).to.be.true
@@ -194,18 +173,14 @@ describe('Test CLI wrapper', function () {
194 describe('Admin auth', function () { 173 describe('Admin auth', function () {
195 174
196 it('Should remove the auth user', async function () { 175 it('Should remove the auth user', async function () {
197 const env = getEnvCli(server) 176 await cliCommand.execWithEnv(`${cmd} auth del ${server.url}`)
198
199 await execCLI(`${env} ${cmd} auth del ${server.url}`)
200
201 const stdout = await execCLI(`${env} ${cmd} --help`)
202 177
178 const stdout = await cliCommand.execWithEnv(`${cmd} --help`)
203 expect(stdout).to.contain('no instance selected') 179 expect(stdout).to.contain('no instance selected')
204 }) 180 })
205 181
206 it('Should add the admin user', async function () { 182 it('Should add the admin user', async function () {
207 const env = getEnvCli(server) 183 await cliCommand.execWithEnv(`${cmd} auth add -u ${server.url} -U root -p test${server.internalServerNumber}`)
208 await execCLI(`${env} ${cmd} auth add -u ${server.url} -U root -p test${server.internalServerNumber}`)
209 }) 184 })
210 }) 185 })
211 186
@@ -214,8 +189,7 @@ describe('Test CLI wrapper', function () {
214 it('Should install a plugin', async function () { 189 it('Should install a plugin', async function () {
215 this.timeout(60000) 190 this.timeout(60000)
216 191
217 const env = getEnvCli(server) 192 await cliCommand.execWithEnv(`${cmd} plugins install --npm-name peertube-plugin-hello-world`)
218 await execCLI(`${env} ${cmd} plugins install --npm-name peertube-plugin-hello-world`)
219 }) 193 })
220 194
221 it('Should have registered settings', async function () { 195 it('Should have registered settings', async function () {
@@ -223,29 +197,27 @@ describe('Test CLI wrapper', function () {
223 }) 197 })
224 198
225 it('Should list installed plugins', async function () { 199 it('Should list installed plugins', async function () {
226 const env = getEnvCli(server) 200 const res = await cliCommand.execWithEnv(`${cmd} plugins list`)
227 const res = await execCLI(`${env} ${cmd} plugins list`)
228 201
229 expect(res).to.contain('peertube-plugin-hello-world') 202 expect(res).to.contain('peertube-plugin-hello-world')
230 }) 203 })
231 204
232 it('Should uninstall the plugin', async function () { 205 it('Should uninstall the plugin', async function () {
233 const env = getEnvCli(server) 206 const res = await cliCommand.execWithEnv(`${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
234 const res = await execCLI(`${env} ${cmd} plugins uninstall --npm-name peertube-plugin-hello-world`)
235 207
236 expect(res).to.not.contain('peertube-plugin-hello-world') 208 expect(res).to.not.contain('peertube-plugin-hello-world')
237 }) 209 })
238 }) 210 })
239 211
240 describe('Manage video redundancies', function () { 212 describe('Manage video redundancies', function () {
241 let anotherServer: ServerInfo 213 let anotherServer: PeerTubeServer
242 let video1Server2: number 214 let video1Server2: number
243 let servers: ServerInfo[] 215 let servers: PeerTubeServer[]
244 216
245 before(async function () { 217 before(async function () {
246 this.timeout(120000) 218 this.timeout(120000)
247 219
248 anotherServer = await flushAndRunServer(2) 220 anotherServer = await createSingleServer(2)
249 await setAccessTokensToServers([ anotherServer ]) 221 await setAccessTokensToServers([ anotherServer ])
250 222
251 await doubleFollow(server, anotherServer) 223 await doubleFollow(server, anotherServer)
@@ -253,20 +225,17 @@ describe('Test CLI wrapper', function () {
253 servers = [ server, anotherServer ] 225 servers = [ server, anotherServer ]
254 await waitJobs(servers) 226 await waitJobs(servers)
255 227
256 const uuid = (await uploadVideoAndGetId({ server: anotherServer, videoName: 'super video' })).uuid 228 const { uuid } = await anotherServer.videos.quickUpload({ name: 'super video' })
257 await waitJobs(servers) 229 await waitJobs(servers)
258 230
259 video1Server2 = await getLocalIdByUUID(server.url, uuid) 231 video1Server2 = await server.videos.getId({ uuid })
260 }) 232 })
261 233
262 it('Should add a redundancy', async function () { 234 it('Should add a redundancy', async function () {
263 this.timeout(60000) 235 this.timeout(60000)
264 236
265 const env = getEnvCli(server)
266
267 const params = `add --video ${video1Server2}` 237 const params = `add --video ${video1Server2}`
268 238 await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
269 await execCLI(`${env} ${cmd} redundancy ${params}`)
270 239
271 await waitJobs(servers) 240 await waitJobs(servers)
272 }) 241 })
@@ -275,10 +244,8 @@ describe('Test CLI wrapper', function () {
275 this.timeout(60000) 244 this.timeout(60000)
276 245
277 { 246 {
278 const env = getEnvCli(server)
279
280 const params = 'list-my-redundancies' 247 const params = 'list-my-redundancies'
281 const stdout = await execCLI(`${env} ${cmd} redundancy ${params}`) 248 const stdout = await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
282 249
283 expect(stdout).to.contain('super video') 250 expect(stdout).to.contain('super video')
284 expect(stdout).to.contain(`localhost:${server.port}`) 251 expect(stdout).to.contain(`localhost:${server.port}`)
@@ -288,18 +255,14 @@ describe('Test CLI wrapper', function () {
288 it('Should remove a redundancy', async function () { 255 it('Should remove a redundancy', async function () {
289 this.timeout(60000) 256 this.timeout(60000)
290 257
291 const env = getEnvCli(server)
292
293 const params = `remove --video ${video1Server2}` 258 const params = `remove --video ${video1Server2}`
294 259 await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
295 await execCLI(`${env} ${cmd} redundancy ${params}`)
296 260
297 await waitJobs(servers) 261 await waitJobs(servers)
298 262
299 { 263 {
300 const env = getEnvCli(server)
301 const params = 'list-my-redundancies' 264 const params = 'list-my-redundancies'
302 const stdout = await execCLI(`${env} ${cmd} redundancy ${params}`) 265 const stdout = await cliCommand.execWithEnv(`${cmd} redundancy ${params}`)
303 266
304 expect(stdout).to.not.contain('super video') 267 expect(stdout).to.not.contain('super video')
305 } 268 }
diff --git a/server/tests/cli/plugins.ts b/server/tests/cli/plugins.ts
index 7f19f14b7..07c78cc89 100644
--- a/server/tests/cli/plugins.ts
+++ b/server/tests/cli/plugins.ts
@@ -1,55 +1,47 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { 5import {
5 cleanupTests, 6 cleanupTests,
6 execCLI, 7 createSingleServer,
7 flushAndRunServer,
8 getConfig,
9 getEnvCli,
10 getPluginTestPath,
11 killallServers, 8 killallServers,
12 reRunServer, 9 PeerTubeServer,
13 ServerInfo, 10 PluginsCommand,
14 setAccessTokensToServers 11 setAccessTokensToServers
15} from '../../../shared/extra-utils' 12} from '../../../shared/extra-utils'
16import { ServerConfig } from '../../../shared/models/server'
17import { expect } from 'chai'
18 13
19describe('Test plugin scripts', function () { 14describe('Test plugin scripts', function () {
20 let server: ServerInfo 15 let server: PeerTubeServer
21 16
22 before(async function () { 17 before(async function () {
23 this.timeout(30000) 18 this.timeout(30000)
24 19
25 server = await flushAndRunServer(1) 20 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 21 await setAccessTokensToServers([ server ])
27 }) 22 })
28 23
29 it('Should install a plugin from stateless CLI', async function () { 24 it('Should install a plugin from stateless CLI', async function () {
30 this.timeout(60000) 25 this.timeout(60000)
31 26
32 const packagePath = getPluginTestPath() 27 const packagePath = PluginsCommand.getPluginTestPath()
33 28
34 const env = getEnvCli(server) 29 await server.cli.execWithEnv(`npm run plugin:install -- --plugin-path ${packagePath}`)
35 await execCLI(`${env} npm run plugin:install -- --plugin-path ${packagePath}`)
36 }) 30 })
37 31
38 it('Should install a theme from stateless CLI', async function () { 32 it('Should install a theme from stateless CLI', async function () {
39 this.timeout(60000) 33 this.timeout(60000)
40 34
41 const env = getEnvCli(server) 35 await server.cli.execWithEnv(`npm run plugin:install -- --npm-name peertube-theme-background-red`)
42 await execCLI(`${env} npm run plugin:install -- --npm-name peertube-theme-background-red`)
43 }) 36 })
44 37
45 it('Should have the theme and the plugin registered when we restart peertube', async function () { 38 it('Should have the theme and the plugin registered when we restart peertube', async function () {
46 this.timeout(30000) 39 this.timeout(30000)
47 40
48 killallServers([ server ]) 41 await killallServers([ server ])
49 await reRunServer(server) 42 await server.run()
50 43
51 const res = await getConfig(server.url) 44 const config = await server.config.getConfig()
52 const config: ServerConfig = res.body
53 45
54 const plugin = config.plugin.registered 46 const plugin = config.plugin.registered
55 .find(p => p.name === 'test') 47 .find(p => p.name === 'test')
@@ -63,18 +55,16 @@ describe('Test plugin scripts', function () {
63 it('Should uninstall a plugin from stateless CLI', async function () { 55 it('Should uninstall a plugin from stateless CLI', async function () {
64 this.timeout(60000) 56 this.timeout(60000)
65 57
66 const env = getEnvCli(server) 58 await server.cli.execWithEnv(`npm run plugin:uninstall -- --npm-name peertube-plugin-test`)
67 await execCLI(`${env} npm run plugin:uninstall -- --npm-name peertube-plugin-test`)
68 }) 59 })
69 60
70 it('Should have removed the plugin on another peertube restart', async function () { 61 it('Should have removed the plugin on another peertube restart', async function () {
71 this.timeout(30000) 62 this.timeout(30000)
72 63
73 killallServers([ server ]) 64 await killallServers([ server ])
74 await reRunServer(server) 65 await server.run()
75 66
76 const res = await getConfig(server.url) 67 const config = await server.config.getConfig()
77 const config: ServerConfig = res.body
78 68
79 const plugin = config.plugin.registered 69 const plugin = config.plugin.registered
80 .find(p => p.name === 'test') 70 .find(p => p.name === 'test')
diff --git a/server/tests/cli/print-transcode-command.ts b/server/tests/cli/print-transcode-command.ts
index 2d7255db7..3a7969e68 100644
--- a/server/tests/cli/print-transcode-command.ts
+++ b/server/tests/cli/print-transcode-command.ts
@@ -2,14 +2,15 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { execCLI } from '../../../shared/extra-utils' 5import { getVideoFileBitrate, getVideoFileFPS } from '@server/helpers/ffprobe-utils'
6import { CLICommand } from '@shared/extra-utils'
6import { getTargetBitrate, VideoResolution } from '../../../shared/models/videos' 7import { getTargetBitrate, VideoResolution } from '../../../shared/models/videos'
7import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' 8import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants'
8import { getVideoFileBitrate, getVideoFileFPS } from '@server/helpers/ffprobe-utils'
9 9
10const expect = chai.expect 10const expect = chai.expect
11 11
12describe('Test create transcoding jobs', function () { 12describe('Test create transcoding jobs', function () {
13
13 it('Should print the correct command for each resolution', async function () { 14 it('Should print the correct command for each resolution', async function () {
14 const fixturePath = 'server/tests/fixtures/video_short.webm' 15 const fixturePath = 'server/tests/fixtures/video_short.webm'
15 const fps = await getVideoFileFPS(fixturePath) 16 const fps = await getVideoFileFPS(fixturePath)
@@ -19,7 +20,7 @@ describe('Test create transcoding jobs', function () {
19 VideoResolution.H_720P, 20 VideoResolution.H_720P,
20 VideoResolution.H_1080P 21 VideoResolution.H_1080P
21 ]) { 22 ]) {
22 const command = await execCLI(`npm run print-transcode-command -- ${fixturePath} -r ${resolution}`) 23 const command = await CLICommand.exec(`npm run print-transcode-command -- ${fixturePath} -r ${resolution}`)
23 const targetBitrate = Math.min(getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS), bitrate) 24 const targetBitrate = Math.min(getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS), bitrate)
24 25
25 expect(command).to.includes(`-vf scale=w=-2:h=${resolution}`) 26 expect(command).to.includes(`-vf scale=w=-2:h=${resolution}`)
diff --git a/server/tests/cli/prune-storage.ts b/server/tests/cli/prune-storage.ts
index a0af09de8..2d4c02da7 100644
--- a/server/tests/cli/prune-storage.ts
+++ b/server/tests/cli/prune-storage.ts
@@ -5,87 +5,89 @@ import * as chai from 'chai'
5import { createFile, readdir } from 'fs-extra' 5import { createFile, readdir } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { buildUUID } from '@server/helpers/uuid' 7import { buildUUID } from '@server/helpers/uuid'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
9import { 8import {
10 buildServerDirectory,
11 cleanupTests, 9 cleanupTests,
12 createVideoPlaylist, 10 CLICommand,
11 createMultipleServers,
13 doubleFollow, 12 doubleFollow,
14 execCLI,
15 flushAndRunMultipleServers,
16 getAccount,
17 getEnvCli,
18 killallServers, 13 killallServers,
19 makeGetRequest, 14 makeGetRequest,
20 ServerInfo, 15 PeerTubeServer,
21 setAccessTokensToServers, 16 setAccessTokensToServers,
22 setDefaultVideoChannel, 17 setDefaultVideoChannel,
23 updateMyAvatar, 18 wait,
24 uploadVideo, 19 waitJobs
25 wait 20} from '@shared/extra-utils'
26} from '../../../shared/extra-utils' 21import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
27import { waitJobs } from '../../../shared/extra-utils/server/jobs'
28import { Account, VideoPlaylistPrivacy } from '../../../shared/models'
29 22
30const expect = chai.expect 23const expect = chai.expect
31 24
32async function countFiles (internalServerNumber: number, directory: string) { 25async function countFiles (server: PeerTubeServer, directory: string) {
33 const files = await readdir(buildServerDirectory({ internalServerNumber }, directory)) 26 const files = await readdir(server.servers.buildDirectory(directory))
34 27
35 return files.length 28 return files.length
36} 29}
37 30
38async function assertNotExists (internalServerNumber: number, directory: string, substring: string) { 31async function assertNotExists (server: PeerTubeServer, directory: string, substring: string) {
39 const files = await readdir(buildServerDirectory({ internalServerNumber }, directory)) 32 const files = await readdir(server.servers.buildDirectory(directory))
40 33
41 for (const f of files) { 34 for (const f of files) {
42 expect(f).to.not.contain(substring) 35 expect(f).to.not.contain(substring)
43 } 36 }
44} 37}
45 38
46async function assertCountAreOkay (servers: ServerInfo[]) { 39async function assertCountAreOkay (servers: PeerTubeServer[], videoServer2UUID: string) {
47 for (const server of servers) { 40 for (const server of servers) {
48 const videosCount = await countFiles(server.internalServerNumber, 'videos') 41 const videosCount = await countFiles(server, 'videos')
49 expect(videosCount).to.equal(8) 42 expect(videosCount).to.equal(8)
50 43
51 const torrentsCount = await countFiles(server.internalServerNumber, 'torrents') 44 const torrentsCount = await countFiles(server, 'torrents')
52 expect(torrentsCount).to.equal(16) 45 expect(torrentsCount).to.equal(16)
53 46
54 const previewsCount = await countFiles(server.internalServerNumber, 'previews') 47 const previewsCount = await countFiles(server, 'previews')
55 expect(previewsCount).to.equal(2) 48 expect(previewsCount).to.equal(2)
56 49
57 const thumbnailsCount = await countFiles(server.internalServerNumber, 'thumbnails') 50 const thumbnailsCount = await countFiles(server, 'thumbnails')
58 expect(thumbnailsCount).to.equal(6) 51 expect(thumbnailsCount).to.equal(6)
59 52
60 const avatarsCount = await countFiles(server.internalServerNumber, 'avatars') 53 const avatarsCount = await countFiles(server, 'avatars')
61 expect(avatarsCount).to.equal(2) 54 expect(avatarsCount).to.equal(2)
62 } 55 }
56
57 // When we'll prune HLS directories too
58 // const hlsRootCount = await countFiles(servers[1], 'streaming-playlists/hls/')
59 // expect(hlsRootCount).to.equal(2)
60
61 // const hlsCount = await countFiles(servers[1], 'streaming-playlists/hls/' + videoServer2UUID)
62 // expect(hlsCount).to.equal(10)
63} 63}
64 64
65describe('Test prune storage scripts', function () { 65describe('Test prune storage scripts', function () {
66 let servers: ServerInfo[] 66 let servers: PeerTubeServer[]
67 const badNames: { [directory: string]: string[] } = {} 67 const badNames: { [directory: string]: string[] } = {}
68 68
69 let videoServer2UUID: string
70
69 before(async function () { 71 before(async function () {
70 this.timeout(120000) 72 this.timeout(120000)
71 73
72 servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: true } }) 74 servers = await createMultipleServers(2, { transcoding: { enabled: true } })
73 await setAccessTokensToServers(servers) 75 await setAccessTokensToServers(servers)
74 await setDefaultVideoChannel(servers) 76 await setDefaultVideoChannel(servers)
75 77
76 for (const server of servers) { 78 for (const server of servers) {
77 await uploadVideo(server.url, server.accessToken, { name: 'video 1' }) 79 await server.videos.upload({ attributes: { name: 'video 1' } })
78 await uploadVideo(server.url, server.accessToken, { name: 'video 2' })
79 80
80 await updateMyAvatar({ url: server.url, accessToken: server.accessToken, fixture: 'avatar.png' }) 81 const { uuid } = await server.videos.upload({ attributes: { name: 'video 2' } })
82 if (server.serverNumber === 2) videoServer2UUID = uuid
81 83
82 await createVideoPlaylist({ 84 await server.users.updateMyAvatar({ fixture: 'avatar.png' })
83 url: server.url, 85
84 token: server.accessToken, 86 await server.playlists.create({
85 playlistAttrs: { 87 attributes: {
86 displayName: 'playlist', 88 displayName: 'playlist',
87 privacy: VideoPlaylistPrivacy.PUBLIC, 89 privacy: VideoPlaylistPrivacy.PUBLIC,
88 videoChannelId: server.videoChannel.id, 90 videoChannelId: server.store.channel.id,
89 thumbnailfile: 'thumbnail.jpg' 91 thumbnailfile: 'thumbnail.jpg'
90 } 92 }
91 }) 93 })
@@ -95,41 +97,39 @@ describe('Test prune storage scripts', function () {
95 97
96 // Lazy load the remote avatar 98 // Lazy load the remote avatar
97 { 99 {
98 const res = await getAccount(servers[0].url, 'root@localhost:' + servers[1].port) 100 const account = await servers[0].accounts.get({ accountName: 'root@localhost:' + servers[1].port })
99 const account: Account = res.body
100 await makeGetRequest({ 101 await makeGetRequest({
101 url: servers[0].url, 102 url: servers[0].url,
102 path: account.avatar.path, 103 path: account.avatar.path,
103 statusCodeExpected: HttpStatusCode.OK_200 104 expectedStatus: HttpStatusCode.OK_200
104 }) 105 })
105 } 106 }
106 107
107 { 108 {
108 const res = await getAccount(servers[1].url, 'root@localhost:' + servers[0].port) 109 const account = await servers[1].accounts.get({ accountName: 'root@localhost:' + servers[0].port })
109 const account: Account = res.body
110 await makeGetRequest({ 110 await makeGetRequest({
111 url: servers[1].url, 111 url: servers[1].url,
112 path: account.avatar.path, 112 path: account.avatar.path,
113 statusCodeExpected: HttpStatusCode.OK_200 113 expectedStatus: HttpStatusCode.OK_200
114 }) 114 })
115 } 115 }
116 116
117 await wait(1000) 117 await wait(1000)
118 118
119 await waitJobs(servers) 119 await waitJobs(servers)
120 killallServers(servers) 120 await killallServers(servers)
121 121
122 await wait(1000) 122 await wait(1000)
123 }) 123 })
124 124
125 it('Should have the files on the disk', async function () { 125 it('Should have the files on the disk', async function () {
126 await assertCountAreOkay(servers) 126 await assertCountAreOkay(servers, videoServer2UUID)
127 }) 127 })
128 128
129 it('Should create some dirty files', async function () { 129 it('Should create some dirty files', async function () {
130 for (let i = 0; i < 2; i++) { 130 for (let i = 0; i < 2; i++) {
131 { 131 {
132 const base = buildServerDirectory(servers[0], 'videos') 132 const base = servers[0].servers.buildDirectory('videos')
133 133
134 const n1 = buildUUID() + '.mp4' 134 const n1 = buildUUID() + '.mp4'
135 const n2 = buildUUID() + '.webm' 135 const n2 = buildUUID() + '.webm'
@@ -141,7 +141,7 @@ describe('Test prune storage scripts', function () {
141 } 141 }
142 142
143 { 143 {
144 const base = buildServerDirectory(servers[0], 'torrents') 144 const base = servers[0].servers.buildDirectory('torrents')
145 145
146 const n1 = buildUUID() + '-240.torrent' 146 const n1 = buildUUID() + '-240.torrent'
147 const n2 = buildUUID() + '-480.torrent' 147 const n2 = buildUUID() + '-480.torrent'
@@ -153,7 +153,7 @@ describe('Test prune storage scripts', function () {
153 } 153 }
154 154
155 { 155 {
156 const base = buildServerDirectory(servers[0], 'thumbnails') 156 const base = servers[0].servers.buildDirectory('thumbnails')
157 157
158 const n1 = buildUUID() + '.jpg' 158 const n1 = buildUUID() + '.jpg'
159 const n2 = buildUUID() + '.jpg' 159 const n2 = buildUUID() + '.jpg'
@@ -165,7 +165,7 @@ describe('Test prune storage scripts', function () {
165 } 165 }
166 166
167 { 167 {
168 const base = buildServerDirectory(servers[0], 'previews') 168 const base = servers[0].servers.buildDirectory('previews')
169 169
170 const n1 = buildUUID() + '.jpg' 170 const n1 = buildUUID() + '.jpg'
171 const n2 = buildUUID() + '.jpg' 171 const n2 = buildUUID() + '.jpg'
@@ -177,7 +177,7 @@ describe('Test prune storage scripts', function () {
177 } 177 }
178 178
179 { 179 {
180 const base = buildServerDirectory(servers[0], 'avatars') 180 const base = servers[0].servers.buildDirectory('avatars')
181 181
182 const n1 = buildUUID() + '.png' 182 const n1 = buildUUID() + '.png'
183 const n2 = buildUUID() + '.jpg' 183 const n2 = buildUUID() + '.jpg'
@@ -187,22 +187,44 @@ describe('Test prune storage scripts', function () {
187 187
188 badNames['avatars'] = [ n1, n2 ] 188 badNames['avatars'] = [ n1, n2 ]
189 } 189 }
190
191 // When we'll prune HLS directories too
192 // {
193 // const directory = join('streaming-playlists', 'hls')
194 // const base = servers[1].servers.buildDirectory(directory)
195
196 // const n1 = buildUUID()
197 // await createFile(join(base, n1))
198 // badNames[directory] = [ n1 ]
199 // }
200
201 // {
202 // const directory = join('streaming-playlists', 'hls', videoServer2UUID)
203 // const base = servers[1].servers.buildDirectory(directory)
204 // const n1 = buildUUID() + '-240-fragmented-.mp4'
205 // const n2 = buildUUID() + '-master.m3u8'
206
207 // await createFile(join(base, n1))
208 // await createFile(join(base, n2))
209
210 // badNames[directory] = [ n1, n2 ]
211 // }
190 } 212 }
191 }) 213 })
192 214
193 it('Should run prune storage', async function () { 215 it('Should run prune storage', async function () {
194 this.timeout(30000) 216 this.timeout(30000)
195 217
196 const env = getEnvCli(servers[0]) 218 const env = servers[0].cli.getEnv()
197 await execCLI(`echo y | ${env} npm run prune-storage`) 219 await CLICommand.exec(`echo y | ${env} npm run prune-storage`)
198 }) 220 })
199 221
200 it('Should have removed files', async function () { 222 it('Should have removed files', async function () {
201 await assertCountAreOkay(servers) 223 await assertCountAreOkay(servers, videoServer2UUID)
202 224
203 for (const directory of Object.keys(badNames)) { 225 for (const directory of Object.keys(badNames)) {
204 for (const name of badNames[directory]) { 226 for (const name of badNames[directory]) {
205 await assertNotExists(servers[0].internalServerNumber, directory, name) 227 await assertNotExists(servers[0], directory, name)
206 } 228 }
207 } 229 }
208 }) 230 })
diff --git a/server/tests/cli/regenerate-thumbnails.ts b/server/tests/cli/regenerate-thumbnails.ts
index 8acb9f263..780c9b4bd 100644
--- a/server/tests/cli/regenerate-thumbnails.ts
+++ b/server/tests/cli/regenerate-thumbnails.ts
@@ -2,36 +2,33 @@ import 'mocha'
2import { expect } from 'chai' 2import { expect } from 'chai'
3import { writeFile } from 'fs-extra' 3import { writeFile } from 'fs-extra'
4import { basename, join } from 'path' 4import { basename, join } from 'path'
5import { Video, VideoDetails } from '@shared/models' 5import { HttpStatusCode, Video } from '@shared/models'
6import { 6import {
7 buildServerDirectory,
8 cleanupTests, 7 cleanupTests,
8 createMultipleServers,
9 doubleFollow, 9 doubleFollow,
10 execCLI,
11 flushAndRunMultipleServers,
12 getEnvCli,
13 getVideo,
14 makeRawRequest, 10 makeRawRequest,
15 ServerInfo, 11 PeerTubeServer,
16 setAccessTokensToServers, 12 setAccessTokensToServers,
17 uploadVideoAndGetId,
18 waitJobs 13 waitJobs
19} from '../../../shared/extra-utils' 14} from '../../../shared/extra-utils'
20import { HttpStatusCode } from '@shared/core-utils'
21 15
22async function testThumbnail (server: ServerInfo, videoId: number | string) { 16async function testThumbnail (server: PeerTubeServer, videoId: number | string) {
23 const res = await getVideo(server.url, videoId) 17 const video = await server.videos.get({ id: videoId })
24 const video: VideoDetails = res.body
25 18
26 const res1 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200) 19 const requests = [
27 expect(res1.body).to.not.have.lengthOf(0) 20 makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200),
21 makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
22 ]
28 23
29 const res2 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200) 24 for (const req of requests) {
30 expect(res2.body).to.not.have.lengthOf(0) 25 const res = await req
26 expect(res.body).to.not.have.lengthOf(0)
27 }
31} 28}
32 29
33describe('Test regenerate thumbnails script', function () { 30describe('Test regenerate thumbnails script', function () {
34 let servers: ServerInfo[] 31 let servers: PeerTubeServer[]
35 32
36 let video1: Video 33 let video1: Video
37 let video2: Video 34 let video2: Video
@@ -43,28 +40,28 @@ describe('Test regenerate thumbnails script', function () {
43 before(async function () { 40 before(async function () {
44 this.timeout(60000) 41 this.timeout(60000)
45 42
46 servers = await flushAndRunMultipleServers(2) 43 servers = await createMultipleServers(2)
47 await setAccessTokensToServers(servers) 44 await setAccessTokensToServers(servers)
48 45
49 await doubleFollow(servers[0], servers[1]) 46 await doubleFollow(servers[0], servers[1])
50 47
51 { 48 {
52 const videoUUID1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).uuid 49 const videoUUID1 = (await servers[0].videos.quickUpload({ name: 'video 1' })).uuid
53 video1 = await (getVideo(servers[0].url, videoUUID1).then(res => res.body)) 50 video1 = await servers[0].videos.get({ id: videoUUID1 })
54 51
55 thumbnail1Path = join(buildServerDirectory(servers[0], 'thumbnails'), basename(video1.thumbnailPath)) 52 thumbnail1Path = join(servers[0].servers.buildDirectory('thumbnails'), basename(video1.thumbnailPath))
56 53
57 const videoUUID2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).uuid 54 const videoUUID2 = (await servers[0].videos.quickUpload({ name: 'video 2' })).uuid
58 video2 = await (getVideo(servers[0].url, videoUUID2).then(res => res.body)) 55 video2 = await servers[0].videos.get({ id: videoUUID2 })
59 } 56 }
60 57
61 { 58 {
62 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3' })).uuid 59 const videoUUID = (await servers[1].videos.quickUpload({ name: 'video 3' })).uuid
63 await waitJobs(servers) 60 await waitJobs(servers)
64 61
65 remoteVideo = await (getVideo(servers[0].url, videoUUID).then(res => res.body)) 62 remoteVideo = await servers[0].videos.get({ id: videoUUID })
66 63
67 thumbnailRemotePath = join(buildServerDirectory(servers[0], 'thumbnails'), basename(remoteVideo.thumbnailPath)) 64 thumbnailRemotePath = join(servers[0].servers.buildDirectory('thumbnails'), basename(remoteVideo.thumbnailPath))
68 } 65 }
69 66
70 await writeFile(thumbnail1Path, '') 67 await writeFile(thumbnail1Path, '')
@@ -91,8 +88,7 @@ describe('Test regenerate thumbnails script', function () {
91 it('Should regenerate local thumbnails from the CLI', async function () { 88 it('Should regenerate local thumbnails from the CLI', async function () {
92 this.timeout(15000) 89 this.timeout(15000)
93 90
94 const env = getEnvCli(servers[0]) 91 await servers[0].cli.execWithEnv(`npm run regenerate-thumbnails`)
95 await execCLI(`${env} npm run regenerate-thumbnails`)
96 }) 92 })
97 93
98 it('Should have generated new thumbnail files', async function () { 94 it('Should have generated new thumbnail files', async function () {
diff --git a/server/tests/cli/reset-password.ts b/server/tests/cli/reset-password.ts
index a84463b33..4a02db35d 100644
--- a/server/tests/cli/reset-password.ts
+++ b/server/tests/cli/reset-password.ts
@@ -1,35 +1,24 @@
1import 'mocha' 1import 'mocha'
2 2import { cleanupTests, CLICommand, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '../../../shared/extra-utils'
3import {
4 cleanupTests,
5 createUser,
6 execCLI,
7 flushAndRunServer,
8 getEnvCli,
9 login,
10 ServerInfo,
11 setAccessTokensToServers
12} from '../../../shared/extra-utils'
13import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
14 3
15describe('Test reset password scripts', function () { 4describe('Test reset password scripts', function () {
16 let server: ServerInfo 5 let server: PeerTubeServer
17 6
18 before(async function () { 7 before(async function () {
19 this.timeout(30000) 8 this.timeout(30000)
20 server = await flushAndRunServer(1) 9 server = await createSingleServer(1)
21 await setAccessTokensToServers([ server ]) 10 await setAccessTokensToServers([ server ])
22 11
23 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user_1', password: 'super password' }) 12 await server.users.create({ username: 'user_1', password: 'super password' })
24 }) 13 })
25 14
26 it('Should change the user password from CLI', async function () { 15 it('Should change the user password from CLI', async function () {
27 this.timeout(60000) 16 this.timeout(60000)
28 17
29 const env = getEnvCli(server) 18 const env = server.cli.getEnv()
30 await execCLI(`echo coucou | ${env} npm run reset-password -- -u user_1`) 19 await CLICommand.exec(`echo coucou | ${env} npm run reset-password -- -u user_1`)
31 20
32 await login(server.url, server.client, { username: 'user_1', password: 'coucou' }, HttpStatusCode.OK_200) 21 await server.login.login({ user: { username: 'user_1', password: 'coucou' } })
33 }) 22 })
34 23
35 after(async function () { 24 after(async function () {
diff --git a/server/tests/cli/update-host.ts b/server/tests/cli/update-host.ts
index 2070f16f5..43fbaec30 100644
--- a/server/tests/cli/update-host.ts
+++ b/server/tests/cli/update-host.ts
@@ -1,33 +1,20 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import { expect } from 'chai'
5import { VideoDetails } from '../../../shared/models/videos'
6import { waitJobs } from '../../../shared/extra-utils/server/jobs'
7import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
8import { 5import {
9 addVideoChannel,
10 cleanupTests, 6 cleanupTests,
11 createUser, 7 createSingleServer,
12 execCLI,
13 flushAndRunServer,
14 getEnvCli,
15 getVideo,
16 getVideoChannelsList,
17 getVideosList,
18 killallServers, 8 killallServers,
19 makeActivityPubGetRequest, 9 makeActivityPubGetRequest,
20 parseTorrentVideo, reRunServer, 10 parseTorrentVideo,
21 ServerInfo, 11 PeerTubeServer,
22 setAccessTokensToServers, 12 setAccessTokensToServers,
23 uploadVideo 13 waitJobs
24} from '../../../shared/extra-utils' 14} from '@shared/extra-utils'
25import { getAccountsList } from '../../../shared/extra-utils/users/accounts'
26
27const expect = chai.expect
28 15
29describe('Test update host scripts', function () { 16describe('Test update host scripts', function () {
30 let server: ServerInfo 17 let server: PeerTubeServer
31 18
32 before(async function () { 19 before(async function () {
33 this.timeout(60000) 20 this.timeout(60000)
@@ -38,17 +25,15 @@ describe('Test update host scripts', function () {
38 } 25 }
39 } 26 }
40 // Run server 2 to have transcoding enabled 27 // Run server 2 to have transcoding enabled
41 server = await flushAndRunServer(2, overrideConfig) 28 server = await createSingleServer(2, overrideConfig)
42 await setAccessTokensToServers([ server ]) 29 await setAccessTokensToServers([ server ])
43 30
44 // Upload two videos for our needs 31 // Upload two videos for our needs
45 const videoAttributes = {} 32 const { uuid: video1UUID } = await server.videos.upload()
46 const resVideo1 = await uploadVideo(server.url, server.accessToken, videoAttributes) 33 await server.videos.upload()
47 const video1UUID = resVideo1.body.video.uuid
48 await uploadVideo(server.url, server.accessToken, videoAttributes)
49 34
50 // Create a user 35 // Create a user
51 await createUser({ url: server.url, accessToken: server.accessToken, username: 'toto', password: 'coucou' }) 36 await server.users.create({ username: 'toto', password: 'coucou' })
52 37
53 // Create channel 38 // Create channel
54 const videoChannel = { 39 const videoChannel = {
@@ -56,11 +41,11 @@ describe('Test update host scripts', function () {
56 displayName: 'second video channel', 41 displayName: 'second video channel',
57 description: 'super video channel description' 42 description: 'super video channel description'
58 } 43 }
59 await addVideoChannel(server.url, server.accessToken, videoChannel) 44 await server.channels.create({ attributes: videoChannel })
60 45
61 // Create comments 46 // Create comments
62 const text = 'my super first comment' 47 const text = 'my super first comment'
63 await addVideoCommentThread(server.url, server.accessToken, video1UUID, text) 48 await server.comments.createThread({ videoId: video1UUID, text })
64 49
65 await waitJobs(server) 50 await waitJobs(server)
66 }) 51 })
@@ -68,25 +53,23 @@ describe('Test update host scripts', function () {
68 it('Should run update host', async function () { 53 it('Should run update host', async function () {
69 this.timeout(30000) 54 this.timeout(30000)
70 55
71 killallServers([ server ]) 56 await killallServers([ server ])
72 // Run server with standard configuration 57 // Run server with standard configuration
73 await reRunServer(server) 58 await server.run()
74 59
75 const env = getEnvCli(server) 60 await server.cli.execWithEnv(`npm run update-host`)
76 await execCLI(`${env} npm run update-host`)
77 }) 61 })
78 62
79 it('Should have updated videos url', async function () { 63 it('Should have updated videos url', async function () {
80 const res = await getVideosList(server.url) 64 const { total, data } = await server.videos.list()
81 expect(res.body.total).to.equal(2) 65 expect(total).to.equal(2)
82 66
83 for (const video of res.body.data) { 67 for (const video of data) {
84 const { body } = await makeActivityPubGetRequest(server.url, '/videos/watch/' + video.uuid) 68 const { body } = await makeActivityPubGetRequest(server.url, '/videos/watch/' + video.uuid)
85 69
86 expect(body.id).to.equal('http://localhost:9002/videos/watch/' + video.uuid) 70 expect(body.id).to.equal('http://localhost:9002/videos/watch/' + video.uuid)
87 71
88 const res = await getVideo(server.url, video.uuid) 72 const videoDetails = await server.videos.get({ id: video.uuid })
89 const videoDetails: VideoDetails = res.body
90 73
91 expect(videoDetails.trackerUrls[0]).to.include(server.host) 74 expect(videoDetails.trackerUrls[0]).to.include(server.host)
92 expect(videoDetails.streamingPlaylists[0].playlistUrl).to.include(server.host) 75 expect(videoDetails.streamingPlaylists[0].playlistUrl).to.include(server.host)
@@ -95,10 +78,10 @@ describe('Test update host scripts', function () {
95 }) 78 })
96 79
97 it('Should have updated video channels url', async function () { 80 it('Should have updated video channels url', async function () {
98 const res = await getVideoChannelsList(server.url, 0, 5, '-name') 81 const { data, total } = await server.channels.list({ sort: '-name' })
99 expect(res.body.total).to.equal(3) 82 expect(total).to.equal(3)
100 83
101 for (const channel of res.body.data) { 84 for (const channel of data) {
102 const { body } = await makeActivityPubGetRequest(server.url, '/video-channels/' + channel.name) 85 const { body } = await makeActivityPubGetRequest(server.url, '/video-channels/' + channel.name)
103 86
104 expect(body.id).to.equal('http://localhost:9002/video-channels/' + channel.name) 87 expect(body.id).to.equal('http://localhost:9002/video-channels/' + channel.name)
@@ -106,10 +89,10 @@ describe('Test update host scripts', function () {
106 }) 89 })
107 90
108 it('Should have updated accounts url', async function () { 91 it('Should have updated accounts url', async function () {
109 const res = await getAccountsList(server.url) 92 const body = await server.accounts.list()
110 expect(res.body.total).to.equal(3) 93 expect(body.total).to.equal(3)
111 94
112 for (const account of res.body.data) { 95 for (const account of body.data) {
113 const usernameWithDomain = account.name 96 const usernameWithDomain = account.name
114 const { body } = await makeActivityPubGetRequest(server.url, '/accounts/' + usernameWithDomain) 97 const { body } = await makeActivityPubGetRequest(server.url, '/accounts/' + usernameWithDomain)
115 98
@@ -120,28 +103,27 @@ describe('Test update host scripts', function () {
120 it('Should have updated torrent hosts', async function () { 103 it('Should have updated torrent hosts', async function () {
121 this.timeout(30000) 104 this.timeout(30000)
122 105
123 const res = await getVideosList(server.url) 106 const { data } = await server.videos.list()
124 const videos = res.body.data 107 expect(data).to.have.lengthOf(2)
125 expect(videos).to.have.lengthOf(2)
126 108
127 for (const video of videos) { 109 for (const video of data) {
128 const res2 = await getVideo(server.url, video.id) 110 const videoDetails = await server.videos.get({ id: video.id })
129 const videoDetails: VideoDetails = res2.body 111 const files = videoDetails.files.concat(videoDetails.streamingPlaylists[0].files)
130 112
131 expect(videoDetails.files).to.have.lengthOf(4) 113 expect(files).to.have.lengthOf(8)
132 114
133 for (const file of videoDetails.files) { 115 for (const file of files) {
134 expect(file.magnetUri).to.contain('localhost%3A9002%2Ftracker%2Fsocket') 116 expect(file.magnetUri).to.contain('localhost%3A9002%2Ftracker%2Fsocket')
135 expect(file.magnetUri).to.contain('localhost%3A9002%2Fstatic%2Fwebseed%2F') 117 expect(file.magnetUri).to.contain('localhost%3A9002%2Fstatic%2F')
136 118
137 const torrent = await parseTorrentVideo(server, videoDetails.uuid, file.resolution.id) 119 const torrent = await parseTorrentVideo(server, file)
138 const announceWS = torrent.announce.find(a => a === 'ws://localhost:9002/tracker/socket') 120 const announceWS = torrent.announce.find(a => a === 'ws://localhost:9002/tracker/socket')
139 expect(announceWS).to.not.be.undefined 121 expect(announceWS).to.not.be.undefined
140 122
141 const announceHttp = torrent.announce.find(a => a === 'http://localhost:9002/tracker/announce') 123 const announceHttp = torrent.announce.find(a => a === 'http://localhost:9002/tracker/announce')
142 expect(announceHttp).to.not.be.undefined 124 expect(announceHttp).to.not.be.undefined
143 125
144 expect(torrent.urlList[0]).to.contain('http://localhost:9002/static/webseed') 126 expect(torrent.urlList[0]).to.contain('http://localhost:9002/static/')
145 } 127 }
146 } 128 }
147 }) 129 })
diff --git a/server/tests/client.ts b/server/tests/client.ts
index 7c4fb4e46..4f0d052c8 100644
--- a/server/tests/client.ts
+++ b/server/tests/client.ts
@@ -3,28 +3,16 @@
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { omit } from 'lodash' 5import { omit } from 'lodash'
6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 6import { Account, HTMLServerConfig, HttpStatusCode, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
7import { Account, CustomConfig, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
8import { 7import {
9 addVideoInPlaylist,
10 cleanupTests, 8 cleanupTests,
11 createVideoPlaylist, 9 createMultipleServers,
12 doubleFollow, 10 doubleFollow,
13 flushAndRunMultipleServers,
14 getAccount,
15 getConfig,
16 getCustomConfig,
17 getVideosList,
18 makeGetRequest, 11 makeGetRequest,
19 makeHTMLRequest, 12 makeHTMLRequest,
20 ServerInfo, 13 PeerTubeServer,
21 setAccessTokensToServers, 14 setAccessTokensToServers,
22 setDefaultVideoChannel, 15 setDefaultVideoChannel,
23 updateCustomConfig,
24 updateCustomSubConfig,
25 updateMyUser,
26 updateVideoChannel,
27 uploadVideo,
28 waitJobs 16 waitJobs
29} from '../../shared/extra-utils' 17} from '../../shared/extra-utils'
30 18
@@ -40,7 +28,7 @@ function checkIndexTags (html: string, title: string, description: string, css:
40} 28}
41 29
42describe('Test a client controllers', function () { 30describe('Test a client controllers', function () {
43 let servers: ServerInfo[] = [] 31 let servers: PeerTubeServer[] = []
44 let account: Account 32 let account: Account
45 33
46 const videoName = 'my super name for server 1' 34 const videoName = 'my super name for server 1'
@@ -62,7 +50,7 @@ describe('Test a client controllers', function () {
62 before(async function () { 50 before(async function () {
63 this.timeout(120000) 51 this.timeout(120000)
64 52
65 servers = await flushAndRunMultipleServers(2) 53 servers = await createMultipleServers(2)
66 54
67 await setAccessTokensToServers(servers) 55 await setAccessTokensToServers(servers)
68 56
@@ -70,47 +58,48 @@ describe('Test a client controllers', function () {
70 58
71 await setDefaultVideoChannel(servers) 59 await setDefaultVideoChannel(servers)
72 60
73 await updateVideoChannel(servers[0].url, servers[0].accessToken, servers[0].videoChannel.name, { description: channelDescription }) 61 await servers[0].channels.update({
62 channelName: servers[0].store.channel.name,
63 attributes: { description: channelDescription }
64 })
74 65
75 // Video 66 // Video
76 67
77 const videoAttributes = { name: videoName, description: videoDescription } 68 {
78 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 69 const attributes = { name: videoName, description: videoDescription }
70 await servers[0].videos.upload({ attributes })
79 71
80 const resVideosRequest = await getVideosList(servers[0].url) 72 const { data } = await servers[0].videos.list()
81 const videos = resVideosRequest.body.data 73 expect(data.length).to.equal(1)
82 expect(videos.length).to.equal(1)
83 74
84 const video = videos[0] 75 const video = data[0]
85 servers[0].video = video 76 servers[0].store.video = video
86 videoIds = [ video.id, video.uuid, video.shortUUID ] 77 videoIds = [ video.id, video.uuid, video.shortUUID ]
78 }
87 79
88 // Playlist 80 // Playlist
89 81
90 const playlistAttrs = { 82 {
91 displayName: playlistName, 83 const attributes = {
92 description: playlistDescription, 84 displayName: playlistName,
93 privacy: VideoPlaylistPrivacy.PUBLIC, 85 description: playlistDescription,
94 videoChannelId: servers[0].videoChannel.id 86 privacy: VideoPlaylistPrivacy.PUBLIC,
95 } 87 videoChannelId: servers[0].store.channel.id
88 }
96 89
97 const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) 90 playlist = await servers[0].playlists.create({ attributes })
98 playlist = resVideoPlaylistRequest.body.videoPlaylist 91 playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
99 playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
100 92
101 await addVideoInPlaylist({ 93 await servers[0].playlists.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: servers[0].store.video.id } })
102 url: servers[0].url, 94 }
103 token: servers[0].accessToken,
104 playlistId: playlist.shortUUID,
105 elementAttrs: { videoId: video.id }
106 })
107 95
108 // Account 96 // Account
109 97
110 await updateMyUser({ url: servers[0].url, accessToken: servers[0].accessToken, description: 'my account description' }) 98 {
99 await servers[0].users.updateMe({ description: 'my account description' })
111 100
112 const resAccountRequest = await getAccount(servers[0].url, `${servers[0].user.username}@${servers[0].host}`) 101 account = await servers[0].accounts.get({ accountName: `${servers[0].store.user.username}@${servers[0].host}` })
113 account = resAccountRequest.body 102 }
114 103
115 await waitJobs(servers) 104 await waitJobs(servers)
116 }) 105 })
@@ -124,14 +113,14 @@ describe('Test a client controllers', function () {
124 url: servers[0].url, 113 url: servers[0].url,
125 path: basePath + id, 114 path: basePath + id,
126 accept: 'text/html', 115 accept: 'text/html',
127 statusCodeExpected: HttpStatusCode.OK_200 116 expectedStatus: HttpStatusCode.OK_200
128 }) 117 })
129 118
130 const port = servers[0].port 119 const port = servers[0].port
131 120
132 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' + 121 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
133 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2F${servers[0].video.uuid}" ` + 122 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2F${servers[0].store.video.shortUUID}" ` +
134 `title="${servers[0].video.name}" />` 123 `title="${servers[0].store.video.name}" />`
135 124
136 expect(res.text).to.contain(expectedLink) 125 expect(res.text).to.contain(expectedLink)
137 } 126 }
@@ -145,13 +134,13 @@ describe('Test a client controllers', function () {
145 url: servers[0].url, 134 url: servers[0].url,
146 path: basePath + id, 135 path: basePath + id,
147 accept: 'text/html', 136 accept: 'text/html',
148 statusCodeExpected: HttpStatusCode.OK_200 137 expectedStatus: HttpStatusCode.OK_200
149 }) 138 })
150 139
151 const port = servers[0].port 140 const port = servers[0].port
152 141
153 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' + 142 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
154 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlist.uuid}" ` + 143 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlist.shortUUID}" ` +
155 `title="${playlistName}" />` 144 `title="${playlistName}" />`
156 145
157 expect(res.text).to.contain(expectedLink) 146 expect(res.text).to.contain(expectedLink)
@@ -163,55 +152,55 @@ describe('Test a client controllers', function () {
163 describe('Open Graph', function () { 152 describe('Open Graph', function () {
164 153
165 async function accountPageTest (path: string) { 154 async function accountPageTest (path: string) {
166 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 155 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
167 const text = res.text 156 const text = res.text
168 157
169 expect(text).to.contain(`<meta property="og:title" content="${account.displayName}" />`) 158 expect(text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
170 expect(text).to.contain(`<meta property="og:description" content="${account.description}" />`) 159 expect(text).to.contain(`<meta property="og:description" content="${account.description}" />`)
171 expect(text).to.contain('<meta property="og:type" content="website" />') 160 expect(text).to.contain('<meta property="og:type" content="website" />')
172 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`) 161 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].store.user.username}" />`)
173 } 162 }
174 163
175 async function channelPageTest (path: string) { 164 async function channelPageTest (path: string) {
176 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 165 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
177 const text = res.text 166 const text = res.text
178 167
179 expect(text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`) 168 expect(text).to.contain(`<meta property="og:title" content="${servers[0].store.channel.displayName}" />`)
180 expect(text).to.contain(`<meta property="og:description" content="${channelDescription}" />`) 169 expect(text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
181 expect(text).to.contain('<meta property="og:type" content="website" />') 170 expect(text).to.contain('<meta property="og:type" content="website" />')
182 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`) 171 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].store.channel.name}" />`)
183 } 172 }
184 173
185 async function watchVideoPageTest (path: string) { 174 async function watchVideoPageTest (path: string) {
186 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 175 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
187 const text = res.text 176 const text = res.text
188 177
189 expect(text).to.contain(`<meta property="og:title" content="${videoName}" />`) 178 expect(text).to.contain(`<meta property="og:title" content="${videoName}" />`)
190 expect(text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`) 179 expect(text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
191 expect(text).to.contain('<meta property="og:type" content="video" />') 180 expect(text).to.contain('<meta property="og:type" content="video" />')
192 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/${servers[0].video.uuid}" />`) 181 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/${servers[0].store.video.shortUUID}" />`)
193 } 182 }
194 183
195 async function watchPlaylistPageTest (path: string) { 184 async function watchPlaylistPageTest (path: string) {
196 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 185 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
197 const text = res.text 186 const text = res.text
198 187
199 expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`) 188 expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
200 expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`) 189 expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
201 expect(text).to.contain('<meta property="og:type" content="video" />') 190 expect(text).to.contain('<meta property="og:type" content="video" />')
202 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.uuid}" />`) 191 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.shortUUID}" />`)
203 } 192 }
204 193
205 it('Should have valid Open Graph tags on the account page', async function () { 194 it('Should have valid Open Graph tags on the account page', async function () {
206 await accountPageTest('/accounts/' + servers[0].user.username) 195 await accountPageTest('/accounts/' + servers[0].store.user.username)
207 await accountPageTest('/a/' + servers[0].user.username) 196 await accountPageTest('/a/' + servers[0].store.user.username)
208 await accountPageTest('/@' + servers[0].user.username) 197 await accountPageTest('/@' + servers[0].store.user.username)
209 }) 198 })
210 199
211 it('Should have valid Open Graph tags on the channel page', async function () { 200 it('Should have valid Open Graph tags on the channel page', async function () {
212 await channelPageTest('/video-channels/' + servers[0].videoChannel.name) 201 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
213 await channelPageTest('/c/' + servers[0].videoChannel.name) 202 await channelPageTest('/c/' + servers[0].store.channel.name)
214 await channelPageTest('/@' + servers[0].videoChannel.name) 203 await channelPageTest('/@' + servers[0].store.channel.name)
215 }) 204 })
216 205
217 it('Should have valid Open Graph tags on the watch page', async function () { 206 it('Should have valid Open Graph tags on the watch page', async function () {
@@ -236,7 +225,7 @@ describe('Test a client controllers', function () {
236 describe('Not whitelisted', function () { 225 describe('Not whitelisted', function () {
237 226
238 async function accountPageTest (path: string) { 227 async function accountPageTest (path: string) {
239 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 228 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
240 const text = res.text 229 const text = res.text
241 230
242 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 231 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -246,17 +235,17 @@ describe('Test a client controllers', function () {
246 } 235 }
247 236
248 async function channelPageTest (path: string) { 237 async function channelPageTest (path: string) {
249 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 238 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
250 const text = res.text 239 const text = res.text
251 240
252 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 241 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
253 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />') 242 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
254 expect(text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`) 243 expect(text).to.contain(`<meta property="twitter:title" content="${servers[0].store.channel.displayName}" />`)
255 expect(text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`) 244 expect(text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
256 } 245 }
257 246
258 async function watchVideoPageTest (path: string) { 247 async function watchVideoPageTest (path: string) {
259 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 248 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
260 const text = res.text 249 const text = res.text
261 250
262 expect(text).to.contain('<meta property="twitter:card" content="summary_large_image" />') 251 expect(text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
@@ -266,7 +255,7 @@ describe('Test a client controllers', function () {
266 } 255 }
267 256
268 async function watchPlaylistPageTest (path: string) { 257 async function watchPlaylistPageTest (path: string) {
269 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 258 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
270 const text = res.text 259 const text = res.text
271 260
272 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 261 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -298,27 +287,26 @@ describe('Test a client controllers', function () {
298 }) 287 })
299 288
300 it('Should have valid twitter card on the channel page', async function () { 289 it('Should have valid twitter card on the channel page', async function () {
301 await channelPageTest('/video-channels/' + servers[0].videoChannel.name) 290 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
302 await channelPageTest('/c/' + servers[0].videoChannel.name) 291 await channelPageTest('/c/' + servers[0].store.channel.name)
303 await channelPageTest('/@' + servers[0].videoChannel.name) 292 await channelPageTest('/@' + servers[0].store.channel.name)
304 }) 293 })
305 }) 294 })
306 295
307 describe('Whitelisted', function () { 296 describe('Whitelisted', function () {
308 297
309 before(async function () { 298 before(async function () {
310 const res = await getCustomConfig(servers[0].url, servers[0].accessToken) 299 const config = await servers[0].config.getCustomConfig()
311 const config = res.body as CustomConfig
312 config.services.twitter = { 300 config.services.twitter = {
313 username: '@Kuja', 301 username: '@Kuja',
314 whitelisted: true 302 whitelisted: true
315 } 303 }
316 304
317 await updateCustomConfig(servers[0].url, servers[0].accessToken, config) 305 await servers[0].config.updateCustomConfig({ newCustomConfig: config })
318 }) 306 })
319 307
320 async function accountPageTest (path: string) { 308 async function accountPageTest (path: string) {
321 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 309 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
322 const text = res.text 310 const text = res.text
323 311
324 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 312 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -326,7 +314,7 @@ describe('Test a client controllers', function () {
326 } 314 }
327 315
328 async function channelPageTest (path: string) { 316 async function channelPageTest (path: string) {
329 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 317 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
330 const text = res.text 318 const text = res.text
331 319
332 expect(text).to.contain('<meta property="twitter:card" content="summary" />') 320 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
@@ -334,7 +322,7 @@ describe('Test a client controllers', function () {
334 } 322 }
335 323
336 async function watchVideoPageTest (path: string) { 324 async function watchVideoPageTest (path: string) {
337 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 325 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
338 const text = res.text 326 const text = res.text
339 327
340 expect(text).to.contain('<meta property="twitter:card" content="player" />') 328 expect(text).to.contain('<meta property="twitter:card" content="player" />')
@@ -342,7 +330,7 @@ describe('Test a client controllers', function () {
342 } 330 }
343 331
344 async function watchPlaylistPageTest (path: string) { 332 async function watchPlaylistPageTest (path: string) {
345 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) 333 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
346 const text = res.text 334 const text = res.text
347 335
348 expect(text).to.contain('<meta property="twitter:card" content="player" />') 336 expect(text).to.contain('<meta property="twitter:card" content="player" />')
@@ -372,9 +360,9 @@ describe('Test a client controllers', function () {
372 }) 360 })
373 361
374 it('Should have valid twitter card on the channel page', async function () { 362 it('Should have valid twitter card on the channel page', async function () {
375 await channelPageTest('/video-channels/' + servers[0].videoChannel.name) 363 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
376 await channelPageTest('/c/' + servers[0].videoChannel.name) 364 await channelPageTest('/c/' + servers[0].store.channel.name)
377 await channelPageTest('/@' + servers[0].videoChannel.name) 365 await channelPageTest('/@' + servers[0].store.channel.name)
378 }) 366 })
379 }) 367 })
380 }) 368 })
@@ -382,53 +370,55 @@ describe('Test a client controllers', function () {
382 describe('Index HTML', function () { 370 describe('Index HTML', function () {
383 371
384 it('Should have valid index html tags (title, description...)', async function () { 372 it('Should have valid index html tags (title, description...)', async function () {
385 const resConfig = await getConfig(servers[0].url) 373 const config = await servers[0].config.getConfig()
386 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 374 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
387 375
388 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' 376 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
389 checkIndexTags(res.text, 'PeerTube', description, '', resConfig.body) 377 checkIndexTags(res.text, 'PeerTube', description, '', config)
390 }) 378 })
391 379
392 it('Should update the customized configuration and have the correct index html tags', async function () { 380 it('Should update the customized configuration and have the correct index html tags', async function () {
393 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 381 await servers[0].config.updateCustomSubConfig({
394 instance: { 382 newConfig: {
395 name: 'PeerTube updated', 383 instance: {
396 shortDescription: 'my short description', 384 name: 'PeerTube updated',
397 description: 'my super description', 385 shortDescription: 'my short description',
398 terms: 'my super terms', 386 description: 'my super description',
399 defaultNSFWPolicy: 'blur', 387 terms: 'my super terms',
400 defaultClientRoute: '/videos/recently-added', 388 defaultNSFWPolicy: 'blur',
401 customizations: { 389 defaultClientRoute: '/videos/recently-added',
402 javascript: 'alert("coucou")', 390 customizations: {
403 css: 'body { background-color: red; }' 391 javascript: 'alert("coucou")',
392 css: 'body { background-color: red; }'
393 }
404 } 394 }
405 } 395 }
406 }) 396 })
407 397
408 const resConfig = await getConfig(servers[0].url) 398 const config = await servers[0].config.getConfig()
409 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 399 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
410 400
411 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) 401 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
412 }) 402 })
413 403
414 it('Should have valid index html updated tags (title, description...)', async function () { 404 it('Should have valid index html updated tags (title, description...)', async function () {
415 const resConfig = await getConfig(servers[0].url) 405 const config = await servers[0].config.getConfig()
416 const res = await makeHTMLRequest(servers[0].url, '/videos/trending') 406 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
417 407
418 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) 408 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
419 }) 409 })
420 410
421 it('Should use the original video URL for the canonical tag', async function () { 411 it('Should use the original video URL for the canonical tag', async function () {
422 for (const basePath of watchVideoBasePaths) { 412 for (const basePath of watchVideoBasePaths) {
423 for (const id of videoIds) { 413 for (const id of videoIds) {
424 const res = await makeHTMLRequest(servers[1].url, basePath + id) 414 const res = await makeHTMLRequest(servers[1].url, basePath + id)
425 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`) 415 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].store.video.uuid}" />`)
426 } 416 }
427 } 417 }
428 }) 418 })
429 419
430 it('Should use the original account URL for the canonical tag', async function () { 420 it('Should use the original account URL for the canonical tag', async function () {
431 const accountURLtest = (res) => { 421 const accountURLtest = res => {
432 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`) 422 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
433 } 423 }
434 424
@@ -438,7 +428,7 @@ describe('Test a client controllers', function () {
438 }) 428 })
439 429
440 it('Should use the original channel URL for the canonical tag', async function () { 430 it('Should use the original channel URL for the canonical tag', async function () {
441 const channelURLtests = (res) => { 431 const channelURLtests = res => {
442 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`) 432 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
443 } 433 }
444 434
@@ -455,15 +445,49 @@ describe('Test a client controllers', function () {
455 } 445 }
456 } 446 }
457 }) 447 })
448
449 it('Should add noindex meta tag for remote accounts', async function () {
450 const handle = 'root@' + servers[0].host
451 const paths = [ '/accounts/', '/a/', '/@' ]
452
453 for (const path of paths) {
454 {
455 const { text } = await makeHTMLRequest(servers[1].url, path + handle)
456 expect(text).to.contain('<meta name="robots" content="noindex" />')
457 }
458
459 {
460 const { text } = await makeHTMLRequest(servers[0].url, path + handle)
461 expect(text).to.not.contain('<meta name="robots" content="noindex" />')
462 }
463 }
464 })
465
466 it('Should add noindex meta tag for remote accounts', async function () {
467 const handle = 'root_channel@' + servers[0].host
468 const paths = [ '/video-channels/', '/c/', '/@' ]
469
470 for (const path of paths) {
471 {
472 const { text } = await makeHTMLRequest(servers[1].url, path + handle)
473 expect(text).to.contain('<meta name="robots" content="noindex" />')
474 }
475
476 {
477 const { text } = await makeHTMLRequest(servers[0].url, path + handle)
478 expect(text).to.not.contain('<meta name="robots" content="noindex" />')
479 }
480 }
481 })
458 }) 482 })
459 483
460 describe('Embed HTML', function () { 484 describe('Embed HTML', function () {
461 485
462 it('Should have the correct embed html tags', async function () { 486 it('Should have the correct embed html tags', async function () {
463 const resConfig = await getConfig(servers[0].url) 487 const config = await servers[0].config.getConfig()
464 const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath) 488 const res = await makeHTMLRequest(servers[0].url, servers[0].store.video.embedPath)
465 489
466 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) 490 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
467 }) 491 })
468 }) 492 })
469 493
diff --git a/server/tests/external-plugins/auth-ldap.ts b/server/tests/external-plugins/auth-ldap.ts
index e4eae7e8c..acec69df5 100644
--- a/server/tests/external-plugins/auth-ldap.ts
+++ b/server/tests/external-plugins/auth-ldap.ts
@@ -2,46 +2,29 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { User } from '@shared/models/users/user.model' 5import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
6import { 6import { HttpStatusCode } from '@shared/models'
7 blockUser,
8 getMyUserInformation,
9 installPlugin,
10 setAccessTokensToServers,
11 unblockUser,
12 uninstallPlugin,
13 updatePluginSettings,
14 uploadVideo,
15 userLogin
16} from '../../../shared/extra-utils'
17import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
18 7
19describe('Official plugin auth-ldap', function () { 8describe('Official plugin auth-ldap', function () {
20 let server: ServerInfo 9 let server: PeerTubeServer
21 let accessToken: string 10 let accessToken: string
22 let userId: number 11 let userId: number
23 12
24 before(async function () { 13 before(async function () {
25 this.timeout(30000) 14 this.timeout(30000)
26 15
27 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
28 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
29 18
30 await installPlugin({ 19 await server.plugins.install({ npmName: 'peertube-plugin-auth-ldap' })
31 url: server.url,
32 accessToken: server.accessToken,
33 npmName: 'peertube-plugin-auth-ldap'
34 })
35 }) 20 })
36 21
37 it('Should not login with without LDAP settings', async function () { 22 it('Should not login with without LDAP settings', async function () {
38 await userLogin(server, { username: 'fry', password: 'fry' }, 400) 23 await server.login.login({ user: { username: 'fry', password: 'fry' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
39 }) 24 })
40 25
41 it('Should not login with bad LDAP settings', async function () { 26 it('Should not login with bad LDAP settings', async function () {
42 await updatePluginSettings({ 27 await server.plugins.updateSettings({
43 url: server.url,
44 accessToken: server.accessToken,
45 npmName: 'peertube-plugin-auth-ldap', 28 npmName: 'peertube-plugin-auth-ldap',
46 settings: { 29 settings: {
47 'bind-credentials': 'GoodNewsEveryone', 30 'bind-credentials': 'GoodNewsEveryone',
@@ -55,13 +38,11 @@ describe('Official plugin auth-ldap', function () {
55 } 38 }
56 }) 39 })
57 40
58 await userLogin(server, { username: 'fry', password: 'fry' }, 400) 41 await server.login.login({ user: { username: 'fry', password: 'fry' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
59 }) 42 })
60 43
61 it('Should not login with good LDAP settings but wrong username/password', async function () { 44 it('Should not login with good LDAP settings but wrong username/password', async function () {
62 await updatePluginSettings({ 45 await server.plugins.updateSettings({
63 url: server.url,
64 accessToken: server.accessToken,
65 npmName: 'peertube-plugin-auth-ldap', 46 npmName: 'peertube-plugin-auth-ldap',
66 settings: { 47 settings: {
67 'bind-credentials': 'GoodNewsEveryone', 48 'bind-credentials': 'GoodNewsEveryone',
@@ -75,22 +56,20 @@ describe('Official plugin auth-ldap', function () {
75 } 56 }
76 }) 57 })
77 58
78 await userLogin(server, { username: 'fry', password: 'bad password' }, 400) 59 await server.login.login({ user: { username: 'fry', password: 'bad password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
79 await userLogin(server, { username: 'fryr', password: 'fry' }, 400) 60 await server.login.login({ user: { username: 'fryr', password: 'fry' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
80 }) 61 })
81 62
82 it('Should login with the appropriate username/password', async function () { 63 it('Should login with the appropriate username/password', async function () {
83 accessToken = await userLogin(server, { username: 'fry', password: 'fry' }) 64 accessToken = await server.login.getAccessToken({ username: 'fry', password: 'fry' })
84 }) 65 })
85 66
86 it('Should login with the appropriate email/password', async function () { 67 it('Should login with the appropriate email/password', async function () {
87 accessToken = await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }) 68 accessToken = await server.login.getAccessToken({ username: 'fry@planetexpress.com', password: 'fry' })
88 }) 69 })
89 70
90 it('Should login get my profile', async function () { 71 it('Should login get my profile', async function () {
91 const res = await getMyUserInformation(server.url, accessToken) 72 const body = await server.users.getMyInfo({ token: accessToken })
92 const body: User = res.body
93
94 expect(body.username).to.equal('fry') 73 expect(body.username).to.equal('fry')
95 expect(body.email).to.equal('fry@planetexpress.com') 74 expect(body.email).to.equal('fry@planetexpress.com')
96 75
@@ -98,25 +77,31 @@ describe('Official plugin auth-ldap', function () {
98 }) 77 })
99 78
100 it('Should upload a video', async function () { 79 it('Should upload a video', async function () {
101 await uploadVideo(server.url, accessToken, { name: 'my super video' }) 80 await server.videos.upload({ token: accessToken, attributes: { name: 'my super video' } })
102 }) 81 })
103 82
104 it('Should not be able to login if the user is banned', async function () { 83 it('Should not be able to login if the user is banned', async function () {
105 await blockUser(server.url, userId, server.accessToken) 84 await server.users.banUser({ userId })
106 85
107 await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }, 400) 86 await server.login.login({
87 user: { username: 'fry@planetexpress.com', password: 'fry' },
88 expectedStatus: HttpStatusCode.BAD_REQUEST_400
89 })
108 }) 90 })
109 91
110 it('Should be able to login if the user is unbanned', async function () { 92 it('Should be able to login if the user is unbanned', async function () {
111 await unblockUser(server.url, userId, server.accessToken) 93 await server.users.unbanUser({ userId })
112 94
113 await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }) 95 await server.login.login({ user: { username: 'fry@planetexpress.com', password: 'fry' } })
114 }) 96 })
115 97
116 it('Should not login if the plugin is uninstalled', async function () { 98 it('Should not login if the plugin is uninstalled', async function () {
117 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-auth-ldap' }) 99 await server.plugins.uninstall({ npmName: 'peertube-plugin-auth-ldap' })
118 100
119 await userLogin(server, { username: 'fry@planetexpress.com', password: 'fry' }, 400) 101 await server.login.login({
102 user: { username: 'fry@planetexpress.com', password: 'fry' },
103 expectedStatus: HttpStatusCode.BAD_REQUEST_400
104 })
120 }) 105 })
121 106
122 after(async function () { 107 after(async function () {
diff --git a/server/tests/external-plugins/auto-block-videos.ts b/server/tests/external-plugins/auto-block-videos.ts
index 18ea17d78..0eb4bda9a 100644
--- a/server/tests/external-plugins/auto-block-videos.ts
+++ b/server/tests/external-plugins/auto-block-videos.ts
@@ -2,41 +2,29 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { Video, VideoBlacklist } from '@shared/models'
6import { 5import {
6 cleanupTests,
7 createMultipleServers,
7 doubleFollow, 8 doubleFollow,
8 getBlacklistedVideosList, 9 killallServers,
9 getVideosList,
10 installPlugin,
11 MockBlocklist, 10 MockBlocklist,
12 removeVideoFromBlacklist, 11 PeerTubeServer,
13 setAccessTokensToServers, 12 setAccessTokensToServers,
14 updatePluginSettings,
15 uploadVideoAndGetId,
16 wait 13 wait
17} from '../../../shared/extra-utils' 14} from '@shared/extra-utils'
18import { 15import { Video } from '@shared/models'
19 cleanupTests,
20 flushAndRunMultipleServers,
21 killallServers,
22 reRunServer,
23 ServerInfo
24} from '../../../shared/extra-utils/server/servers'
25 16
26async function check (server: ServerInfo, videoUUID: string, exists = true) { 17async function check (server: PeerTubeServer, videoUUID: string, exists = true) {
27 const res = await getVideosList(server.url) 18 const { data } = await server.videos.list()
28 19
29 const video = res.body.data.find(v => v.uuid === videoUUID) 20 const video = data.find(v => v.uuid === videoUUID)
30 21
31 if (exists) { 22 if (exists) expect(video).to.not.be.undefined
32 expect(video).to.not.be.undefined 23 else expect(video).to.be.undefined
33 } else {
34 expect(video).to.be.undefined
35 }
36} 24}
37 25
38describe('Official plugin auto-block videos', function () { 26describe('Official plugin auto-block videos', function () {
39 let servers: ServerInfo[] 27 let servers: PeerTubeServer[]
40 let blocklistServer: MockBlocklist 28 let blocklistServer: MockBlocklist
41 let server1Videos: Video[] = [] 29 let server1Videos: Video[] = []
42 let server2Videos: Video[] = [] 30 let server2Videos: Video[] = []
@@ -45,42 +33,36 @@ describe('Official plugin auto-block videos', function () {
45 before(async function () { 33 before(async function () {
46 this.timeout(60000) 34 this.timeout(60000)
47 35
48 servers = await flushAndRunMultipleServers(2) 36 servers = await createMultipleServers(2)
49 await setAccessTokensToServers(servers) 37 await setAccessTokensToServers(servers)
50 38
51 for (const server of servers) { 39 for (const server of servers) {
52 await installPlugin({ 40 await server.plugins.install({ npmName: 'peertube-plugin-auto-block-videos' })
53 url: server.url,
54 accessToken: server.accessToken,
55 npmName: 'peertube-plugin-auto-block-videos'
56 })
57 } 41 }
58 42
59 blocklistServer = new MockBlocklist() 43 blocklistServer = new MockBlocklist()
60 port = await blocklistServer.initialize() 44 port = await blocklistServer.initialize()
61 45
62 await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' }) 46 await servers[0].videos.quickUpload({ name: 'video server 1' })
63 await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' }) 47 await servers[1].videos.quickUpload({ name: 'video server 2' })
64 await uploadVideoAndGetId({ server: servers[1], videoName: 'video 2 server 2' }) 48 await servers[1].videos.quickUpload({ name: 'video 2 server 2' })
65 await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3 server 2' }) 49 await servers[1].videos.quickUpload({ name: 'video 3 server 2' })
66 50
67 { 51 {
68 const res = await getVideosList(servers[0].url) 52 const { data } = await servers[0].videos.list()
69 server1Videos = res.body.data.map(v => Object.assign(v, { url: servers[0].url + '/videos/watch/' + v.uuid })) 53 server1Videos = data.map(v => Object.assign(v, { url: servers[0].url + '/videos/watch/' + v.uuid }))
70 } 54 }
71 55
72 { 56 {
73 const res = await getVideosList(servers[1].url) 57 const { data } = await servers[1].videos.list()
74 server2Videos = res.body.data.map(v => Object.assign(v, { url: servers[1].url + '/videos/watch/' + v.uuid })) 58 server2Videos = data.map(v => Object.assign(v, { url: servers[1].url + '/videos/watch/' + v.uuid }))
75 } 59 }
76 60
77 await doubleFollow(servers[0], servers[1]) 61 await doubleFollow(servers[0], servers[1])
78 }) 62 })
79 63
80 it('Should update plugin settings', async function () { 64 it('Should update plugin settings', async function () {
81 await updatePluginSettings({ 65 await servers[0].plugins.updateSettings({
82 url: servers[0].url,
83 accessToken: servers[0].accessToken,
84 npmName: 'peertube-plugin-auto-block-videos', 66 npmName: 'peertube-plugin-auto-block-videos',
85 settings: { 67 settings: {
86 'blocklist-urls': `http://localhost:${port}/blocklist`, 68 'blocklist-urls': `http://localhost:${port}/blocklist`,
@@ -108,10 +90,9 @@ describe('Official plugin auto-block videos', function () {
108 }) 90 })
109 91
110 it('Should have video in blacklists', async function () { 92 it('Should have video in blacklists', async function () {
111 const res = await getBlacklistedVideosList({ url: servers[0].url, token: servers[0].accessToken }) 93 const body = await servers[0].blacklist.list()
112
113 const videoBlacklists = res.body.data as VideoBlacklist[]
114 94
95 const videoBlacklists = body.data
115 expect(videoBlacklists).to.have.lengthOf(1) 96 expect(videoBlacklists).to.have.lengthOf(1)
116 expect(videoBlacklists[0].reason).to.contains('Automatically blocked from auto block plugin') 97 expect(videoBlacklists[0].reason).to.contains('Automatically blocked from auto block plugin')
117 expect(videoBlacklists[0].video.name).to.equal(server2Videos[0].name) 98 expect(videoBlacklists[0].video.name).to.equal(server2Videos[0].name)
@@ -174,12 +155,12 @@ describe('Official plugin auto-block videos', function () {
174 155
175 await check(servers[0], video.uuid, false) 156 await check(servers[0], video.uuid, false)
176 157
177 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video.uuid) 158 await servers[0].blacklist.remove({ videoId: video.uuid })
178 159
179 await check(servers[0], video.uuid, true) 160 await check(servers[0], video.uuid, true)
180 161
181 killallServers([ servers[0] ]) 162 await killallServers([ servers[0] ])
182 await reRunServer(servers[0]) 163 await servers[0].run()
183 await wait(2000) 164 await wait(2000)
184 165
185 await check(servers[0], video.uuid, true) 166 await check(servers[0], video.uuid, true)
diff --git a/server/tests/external-plugins/auto-mute.ts b/server/tests/external-plugins/auto-mute.ts
index 09355d932..271779dd4 100644
--- a/server/tests/external-plugins/auto-mute.ts
+++ b/server/tests/external-plugins/auto-mute.ts
@@ -3,63 +3,45 @@
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { 5import {
6 addAccountToServerBlocklist, 6 cleanupTests,
7 addServerToAccountBlocklist, 7 createMultipleServers,
8 removeAccountFromServerBlocklist
9} from '@shared/extra-utils/users/blocklist'
10import {
11 doubleFollow, 8 doubleFollow,
12 getVideosList, 9 killallServers,
13 installPlugin,
14 makeGetRequest, 10 makeGetRequest,
15 MockBlocklist, 11 MockBlocklist,
12 PeerTubeServer,
16 setAccessTokensToServers, 13 setAccessTokensToServers,
17 updatePluginSettings,
18 uploadVideoAndGetId,
19 wait 14 wait
20} from '../../../shared/extra-utils' 15} from '@shared/extra-utils'
21import { 16import { HttpStatusCode } from '@shared/models'
22 cleanupTests,
23 flushAndRunMultipleServers,
24 killallServers,
25 reRunServer,
26 ServerInfo
27} from '../../../shared/extra-utils/server/servers'
28import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
29 17
30describe('Official plugin auto-mute', function () { 18describe('Official plugin auto-mute', function () {
31 const autoMuteListPath = '/plugins/auto-mute/router/api/v1/mute-list' 19 const autoMuteListPath = '/plugins/auto-mute/router/api/v1/mute-list'
32 let servers: ServerInfo[] 20 let servers: PeerTubeServer[]
33 let blocklistServer: MockBlocklist 21 let blocklistServer: MockBlocklist
34 let port: number 22 let port: number
35 23
36 before(async function () { 24 before(async function () {
37 this.timeout(30000) 25 this.timeout(30000)
38 26
39 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
40 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
41 29
42 for (const server of servers) { 30 for (const server of servers) {
43 await installPlugin({ 31 await server.plugins.install({ npmName: 'peertube-plugin-auto-mute' })
44 url: server.url,
45 accessToken: server.accessToken,
46 npmName: 'peertube-plugin-auto-mute'
47 })
48 } 32 }
49 33
50 blocklistServer = new MockBlocklist() 34 blocklistServer = new MockBlocklist()
51 port = await blocklistServer.initialize() 35 port = await blocklistServer.initialize()
52 36
53 await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' }) 37 await servers[0].videos.quickUpload({ name: 'video server 1' })
54 await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' }) 38 await servers[1].videos.quickUpload({ name: 'video server 2' })
55 39
56 await doubleFollow(servers[0], servers[1]) 40 await doubleFollow(servers[0], servers[1])
57 }) 41 })
58 42
59 it('Should update plugin settings', async function () { 43 it('Should update plugin settings', async function () {
60 await updatePluginSettings({ 44 await servers[0].plugins.updateSettings({
61 url: servers[0].url,
62 accessToken: servers[0].accessToken,
63 npmName: 'peertube-plugin-auto-mute', 45 npmName: 'peertube-plugin-auto-mute',
64 settings: { 46 settings: {
65 'blocklist-urls': `http://localhost:${port}/blocklist`, 47 'blocklist-urls': `http://localhost:${port}/blocklist`,
@@ -81,8 +63,8 @@ describe('Official plugin auto-mute', function () {
81 63
82 await wait(2000) 64 await wait(2000)
83 65
84 const res = await getVideosList(servers[0].url) 66 const { total } = await servers[0].videos.list()
85 expect(res.body.total).to.equal(1) 67 expect(total).to.equal(1)
86 }) 68 })
87 69
88 it('Should remove a server blocklist', async function () { 70 it('Should remove a server blocklist', async function () {
@@ -99,8 +81,8 @@ describe('Official plugin auto-mute', function () {
99 81
100 await wait(2000) 82 await wait(2000)
101 83
102 const res = await getVideosList(servers[0].url) 84 const { total } = await servers[0].videos.list()
103 expect(res.body.total).to.equal(2) 85 expect(total).to.equal(2)
104 }) 86 })
105 87
106 it('Should add an account blocklist', async function () { 88 it('Should add an account blocklist', async function () {
@@ -116,8 +98,8 @@ describe('Official plugin auto-mute', function () {
116 98
117 await wait(2000) 99 await wait(2000)
118 100
119 const res = await getVideosList(servers[0].url) 101 const { total } = await servers[0].videos.list()
120 expect(res.body.total).to.equal(1) 102 expect(total).to.equal(1)
121 }) 103 })
122 104
123 it('Should remove an account blocklist', async function () { 105 it('Should remove an account blocklist', async function () {
@@ -134,8 +116,8 @@ describe('Official plugin auto-mute', function () {
134 116
135 await wait(2000) 117 await wait(2000)
136 118
137 const res = await getVideosList(servers[0].url) 119 const { total } = await servers[0].videos.list()
138 expect(res.body.total).to.equal(2) 120 expect(total).to.equal(2)
139 }) 121 })
140 122
141 it('Should auto mute an account, manually unmute it and do not remute it automatically', async function () { 123 it('Should auto mute an account, manually unmute it and do not remute it automatically', async function () {
@@ -155,24 +137,24 @@ describe('Official plugin auto-mute', function () {
155 await wait(2000) 137 await wait(2000)
156 138
157 { 139 {
158 const res = await getVideosList(servers[0].url) 140 const { total } = await servers[0].videos.list()
159 expect(res.body.total).to.equal(1) 141 expect(total).to.equal(1)
160 } 142 }
161 143
162 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, account) 144 await servers[0].blocklist.removeFromServerBlocklist({ account })
163 145
164 { 146 {
165 const res = await getVideosList(servers[0].url) 147 const { total } = await servers[0].videos.list()
166 expect(res.body.total).to.equal(2) 148 expect(total).to.equal(2)
167 } 149 }
168 150
169 killallServers([ servers[0] ]) 151 await killallServers([ servers[0] ])
170 await reRunServer(servers[0]) 152 await servers[0].run()
171 await wait(2000) 153 await wait(2000)
172 154
173 { 155 {
174 const res = await getVideosList(servers[0].url) 156 const { total } = await servers[0].videos.list()
175 expect(res.body.total).to.equal(2) 157 expect(total).to.equal(2)
176 } 158 }
177 }) 159 })
178 160
@@ -180,14 +162,12 @@ describe('Official plugin auto-mute', function () {
180 await makeGetRequest({ 162 await makeGetRequest({
181 url: servers[0].url, 163 url: servers[0].url,
182 path: '/plugins/auto-mute/router/api/v1/mute-list', 164 path: '/plugins/auto-mute/router/api/v1/mute-list',
183 statusCodeExpected: HttpStatusCode.FORBIDDEN_403 165 expectedStatus: HttpStatusCode.FORBIDDEN_403
184 }) 166 })
185 }) 167 })
186 168
187 it('Should enable auto mute list', async function () { 169 it('Should enable auto mute list', async function () {
188 await updatePluginSettings({ 170 await servers[0].plugins.updateSettings({
189 url: servers[0].url,
190 accessToken: servers[0].accessToken,
191 npmName: 'peertube-plugin-auto-mute', 171 npmName: 'peertube-plugin-auto-mute',
192 settings: { 172 settings: {
193 'blocklist-urls': '', 173 'blocklist-urls': '',
@@ -199,16 +179,14 @@ describe('Official plugin auto-mute', function () {
199 await makeGetRequest({ 179 await makeGetRequest({
200 url: servers[0].url, 180 url: servers[0].url,
201 path: '/plugins/auto-mute/router/api/v1/mute-list', 181 path: '/plugins/auto-mute/router/api/v1/mute-list',
202 statusCodeExpected: HttpStatusCode.OK_200 182 expectedStatus: HttpStatusCode.OK_200
203 }) 183 })
204 }) 184 })
205 185
206 it('Should mute an account on server 1, and server 2 auto mutes it', async function () { 186 it('Should mute an account on server 1, and server 2 auto mutes it', async function () {
207 this.timeout(20000) 187 this.timeout(20000)
208 188
209 await updatePluginSettings({ 189 await servers[1].plugins.updateSettings({
210 url: servers[1].url,
211 accessToken: servers[1].accessToken,
212 npmName: 'peertube-plugin-auto-mute', 190 npmName: 'peertube-plugin-auto-mute',
213 settings: { 191 settings: {
214 'blocklist-urls': 'http://localhost:' + servers[0].port + autoMuteListPath, 192 'blocklist-urls': 'http://localhost:' + servers[0].port + autoMuteListPath,
@@ -217,13 +195,13 @@ describe('Official plugin auto-mute', function () {
217 } 195 }
218 }) 196 })
219 197
220 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port) 198 await servers[0].blocklist.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
221 await addServerToAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port) 199 await servers[0].blocklist.addToMyBlocklist({ server: 'localhost:' + servers[1].port })
222 200
223 const res = await makeGetRequest({ 201 const res = await makeGetRequest({
224 url: servers[0].url, 202 url: servers[0].url,
225 path: '/plugins/auto-mute/router/api/v1/mute-list', 203 path: '/plugins/auto-mute/router/api/v1/mute-list',
226 statusCodeExpected: HttpStatusCode.OK_200 204 expectedStatus: HttpStatusCode.OK_200
227 }) 205 })
228 206
229 const data = res.body.data 207 const data = res.body.data
@@ -234,8 +212,8 @@ describe('Official plugin auto-mute', function () {
234 await wait(2000) 212 await wait(2000)
235 213
236 for (const server of servers) { 214 for (const server of servers) {
237 const res = await getVideosList(server.url) 215 const { total } = await server.videos.list()
238 expect(res.body.total).to.equal(1) 216 expect(total).to.equal(1)
239 } 217 }
240 }) 218 })
241 219
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index 7bad81751..55b434846 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -4,34 +4,16 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import * as xmlParser from 'fast-xml-parser' 5import * as xmlParser from 'fast-xml-parser'
6import { 6import {
7 addAccountToAccountBlocklist,
8 addAccountToServerBlocklist,
9 removeAccountFromServerBlocklist
10} from '@shared/extra-utils/users/blocklist'
11import { addUserSubscription, listUserSubscriptionVideos } from '@shared/extra-utils/users/user-subscriptions'
12import { VideoPrivacy } from '@shared/models'
13import { ScopedToken } from '@shared/models/users/user-scoped-token'
14import {
15 cleanupTests, 7 cleanupTests,
16 createUser, 8 createMultipleServers,
9 createSingleServer,
17 doubleFollow, 10 doubleFollow,
18 flushAndRunMultipleServers, 11 makeGetRequest,
19 flushAndRunServer, 12 PeerTubeServer,
20 getJSONfeed,
21 getMyUserInformation,
22 getUserScopedTokens,
23 getXMLfeed,
24 renewUserScopedTokens,
25 ServerInfo,
26 setAccessTokensToServers, 13 setAccessTokensToServers,
27 uploadVideo, 14 waitJobs
28 uploadVideoAndGetId, 15} from '@shared/extra-utils'
29 userLogin 16import { HttpStatusCode, VideoPrivacy } from '@shared/models'
30} from '../../../shared/extra-utils'
31import { waitJobs } from '../../../shared/extra-utils/server/jobs'
32import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
33import { User } from '../../../shared/models/users'
34import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
35 17
36chai.use(require('chai-xml')) 18chai.use(require('chai-xml'))
37chai.use(require('chai-json-schema')) 19chai.use(require('chai-json-schema'))
@@ -39,8 +21,8 @@ chai.config.includeStack = true
39const expect = chai.expect 21const expect = chai.expect
40 22
41describe('Test syndication feeds', () => { 23describe('Test syndication feeds', () => {
42 let servers: ServerInfo[] = [] 24 let servers: PeerTubeServer[] = []
43 let serverHLSOnly: ServerInfo 25 let serverHLSOnly: PeerTubeServer
44 let userAccessToken: string 26 let userAccessToken: string
45 let rootAccountId: number 27 let rootAccountId: number
46 let rootChannelId: number 28 let rootChannelId: number
@@ -52,8 +34,8 @@ describe('Test syndication feeds', () => {
52 this.timeout(120000) 34 this.timeout(120000)
53 35
54 // Run servers 36 // Run servers
55 servers = await flushAndRunMultipleServers(2) 37 servers = await createMultipleServers(2)
56 serverHLSOnly = await flushAndRunServer(3, { 38 serverHLSOnly = await createSingleServer(3, {
57 transcoding: { 39 transcoding: {
58 enabled: true, 40 enabled: true,
59 webtorrent: { enabled: false }, 41 webtorrent: { enabled: false },
@@ -65,50 +47,43 @@ describe('Test syndication feeds', () => {
65 await doubleFollow(servers[0], servers[1]) 47 await doubleFollow(servers[0], servers[1])
66 48
67 { 49 {
68 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 50 const user = await servers[0].users.getMyInfo()
69 const user: User = res.body
70 rootAccountId = user.account.id 51 rootAccountId = user.account.id
71 rootChannelId = user.videoChannels[0].id 52 rootChannelId = user.videoChannels[0].id
72 } 53 }
73 54
74 { 55 {
75 const attr = { username: 'john', password: 'password' } 56 userAccessToken = await servers[0].users.generateUserAndToken('john')
76 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password })
77 userAccessToken = await userLogin(servers[0], attr)
78 57
79 const res = await getMyUserInformation(servers[0].url, userAccessToken) 58 const user = await servers[0].users.getMyInfo({ token: userAccessToken })
80 const user: User = res.body
81 userAccountId = user.account.id 59 userAccountId = user.account.id
82 userChannelId = user.videoChannels[0].id 60 userChannelId = user.videoChannels[0].id
83 61
84 const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) 62 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken })
85 const token: ScopedToken = res2.body
86 userFeedToken = token.feedToken 63 userFeedToken = token.feedToken
87 } 64 }
88 65
89 { 66 {
90 await uploadVideo(servers[0].url, userAccessToken, { name: 'user video' }) 67 await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'user video' } })
91 } 68 }
92 69
93 { 70 {
94 const videoAttributes = { 71 const attributes = {
95 name: 'my super name for server 1', 72 name: 'my super name for server 1',
96 description: 'my super description for server 1', 73 description: 'my super description for server 1',
97 fixture: 'video_short.webm' 74 fixture: 'video_short.webm'
98 } 75 }
99 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 76 const { id } = await servers[0].videos.upload({ attributes })
100 const videoId = res.body.video.id
101 77
102 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 1') 78 await servers[0].comments.createThread({ videoId: id, text: 'super comment 1' })
103 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'super comment 2') 79 await servers[0].comments.createThread({ videoId: id, text: 'super comment 2' })
104 } 80 }
105 81
106 { 82 {
107 const videoAttributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED } 83 const attributes = { name: 'unlisted video', privacy: VideoPrivacy.UNLISTED }
108 const res = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes) 84 const { id } = await servers[0].videos.upload({ attributes })
109 const videoId = res.body.video.id
110 85
111 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoId, 'comment on unlisted video') 86 await servers[0].comments.createThread({ videoId: id, text: 'comment on unlisted video' })
112 } 87 }
113 88
114 await waitJobs(servers) 89 await waitJobs(servers)
@@ -118,30 +93,65 @@ describe('Test syndication feeds', () => {
118 93
119 it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () { 94 it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () {
120 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { 95 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
121 const rss = await getXMLfeed(servers[0].url, feed) 96 const rss = await servers[0].feed.getXML({ feed })
122 expect(rss.text).xml.to.be.valid() 97 expect(rss).xml.to.be.valid()
123 98
124 const atom = await getXMLfeed(servers[0].url, feed, 'atom') 99 const atom = await servers[0].feed.getXML({ feed, format: 'atom' })
125 expect(atom.text).xml.to.be.valid() 100 expect(atom).xml.to.be.valid()
126 } 101 }
127 }) 102 })
128 103
129 it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () { 104 it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () {
130 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { 105 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
131 const json = await getJSONfeed(servers[0].url, feed) 106 const jsonText = await servers[0].feed.getJSON({ feed })
132 expect(JSON.parse(json.text)).to.be.jsonSchema({ type: 'object' }) 107 expect(JSON.parse(jsonText)).to.be.jsonSchema({ type: 'object' })
133 } 108 }
134 }) 109 })
110
111 it('Should serve the endpoint with a classic request', async function () {
112 await makeGetRequest({
113 url: servers[0].url,
114 path: '/feeds/videos.xml',
115 accept: 'application/xml',
116 expectedStatus: HttpStatusCode.OK_200
117 })
118 })
119
120 it('Should serve the endpoint as a cached request', async function () {
121 const res = await makeGetRequest({
122 url: servers[0].url,
123 path: '/feeds/videos.xml',
124 accept: 'application/xml',
125 expectedStatus: HttpStatusCode.OK_200
126 })
127
128 expect(res.headers['x-api-cache-cached']).to.equal('true')
129 })
130
131 it('Should not serve the endpoint as a cached request', async function () {
132 const res = await makeGetRequest({
133 url: servers[0].url,
134 path: '/feeds/videos.xml?v=186',
135 accept: 'application/xml',
136 expectedStatus: HttpStatusCode.OK_200
137 })
138
139 expect(res.headers['x-api-cache-cached']).to.not.exist
140 })
141
142 it('Should refuse to serve the endpoint without accept header', async function () {
143 await makeGetRequest({ url: servers[0].url, path: '/feeds/videos.xml', expectedStatus: HttpStatusCode.NOT_ACCEPTABLE_406 })
144 })
135 }) 145 })
136 146
137 describe('Videos feed', function () { 147 describe('Videos feed', function () {
138 148
139 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () { 149 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () {
140 for (const server of servers) { 150 for (const server of servers) {
141 const rss = await getXMLfeed(server.url, 'videos') 151 const rss = await server.feed.getXML({ feed: 'videos' })
142 expect(xmlParser.validate(rss.text)).to.be.true 152 expect(xmlParser.validate(rss)).to.be.true
143 153
144 const xmlDoc = xmlParser.parse(rss.text, { parseAttributeValue: true, ignoreAttributes: false }) 154 const xmlDoc = xmlParser.parse(rss, { parseAttributeValue: true, ignoreAttributes: false })
145 155
146 const enclosure = xmlDoc.rss.channel.item[0].enclosure 156 const enclosure = xmlDoc.rss.channel.item[0].enclosure
147 expect(enclosure).to.exist 157 expect(enclosure).to.exist
@@ -153,8 +163,8 @@ describe('Test syndication feeds', () => {
153 163
154 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () { 164 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () {
155 for (const server of servers) { 165 for (const server of servers) {
156 const json = await getJSONfeed(server.url, 'videos') 166 const json = await server.feed.getJSON({ feed: 'videos' })
157 const jsonObj = JSON.parse(json.text) 167 const jsonObj = JSON.parse(json)
158 expect(jsonObj.items.length).to.be.equal(2) 168 expect(jsonObj.items.length).to.be.equal(2)
159 expect(jsonObj.items[0].attachments).to.exist 169 expect(jsonObj.items[0].attachments).to.exist
160 expect(jsonObj.items[0].attachments.length).to.be.eq(1) 170 expect(jsonObj.items[0].attachments.length).to.be.eq(1)
@@ -166,16 +176,16 @@ describe('Test syndication feeds', () => {
166 176
167 it('Should filter by account', async function () { 177 it('Should filter by account', async function () {
168 { 178 {
169 const json = await getJSONfeed(servers[0].url, 'videos', { accountId: rootAccountId }) 179 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: rootAccountId } })
170 const jsonObj = JSON.parse(json.text) 180 const jsonObj = JSON.parse(json)
171 expect(jsonObj.items.length).to.be.equal(1) 181 expect(jsonObj.items.length).to.be.equal(1)
172 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 182 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
173 expect(jsonObj.items[0].author.name).to.equal('root') 183 expect(jsonObj.items[0].author.name).to.equal('root')
174 } 184 }
175 185
176 { 186 {
177 const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId }) 187 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: userAccountId } })
178 const jsonObj = JSON.parse(json.text) 188 const jsonObj = JSON.parse(json)
179 expect(jsonObj.items.length).to.be.equal(1) 189 expect(jsonObj.items.length).to.be.equal(1)
180 expect(jsonObj.items[0].title).to.equal('user video') 190 expect(jsonObj.items[0].title).to.equal('user video')
181 expect(jsonObj.items[0].author.name).to.equal('john') 191 expect(jsonObj.items[0].author.name).to.equal('john')
@@ -183,15 +193,15 @@ describe('Test syndication feeds', () => {
183 193
184 for (const server of servers) { 194 for (const server of servers) {
185 { 195 {
186 const json = await getJSONfeed(server.url, 'videos', { accountName: 'root@localhost:' + servers[0].port }) 196 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'root@localhost:' + servers[0].port } })
187 const jsonObj = JSON.parse(json.text) 197 const jsonObj = JSON.parse(json)
188 expect(jsonObj.items.length).to.be.equal(1) 198 expect(jsonObj.items.length).to.be.equal(1)
189 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 199 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
190 } 200 }
191 201
192 { 202 {
193 const json = await getJSONfeed(server.url, 'videos', { accountName: 'john@localhost:' + servers[0].port }) 203 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'john@localhost:' + servers[0].port } })
194 const jsonObj = JSON.parse(json.text) 204 const jsonObj = JSON.parse(json)
195 expect(jsonObj.items.length).to.be.equal(1) 205 expect(jsonObj.items.length).to.be.equal(1)
196 expect(jsonObj.items[0].title).to.equal('user video') 206 expect(jsonObj.items[0].title).to.equal('user video')
197 } 207 }
@@ -200,16 +210,16 @@ describe('Test syndication feeds', () => {
200 210
201 it('Should filter by video channel', async function () { 211 it('Should filter by video channel', async function () {
202 { 212 {
203 const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: rootChannelId }) 213 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: rootChannelId } })
204 const jsonObj = JSON.parse(json.text) 214 const jsonObj = JSON.parse(json)
205 expect(jsonObj.items.length).to.be.equal(1) 215 expect(jsonObj.items.length).to.be.equal(1)
206 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 216 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
207 expect(jsonObj.items[0].author.name).to.equal('root') 217 expect(jsonObj.items[0].author.name).to.equal('root')
208 } 218 }
209 219
210 { 220 {
211 const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: userChannelId }) 221 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: userChannelId } })
212 const jsonObj = JSON.parse(json.text) 222 const jsonObj = JSON.parse(json)
213 expect(jsonObj.items.length).to.be.equal(1) 223 expect(jsonObj.items.length).to.be.equal(1)
214 expect(jsonObj.items[0].title).to.equal('user video') 224 expect(jsonObj.items[0].title).to.equal('user video')
215 expect(jsonObj.items[0].author.name).to.equal('john') 225 expect(jsonObj.items[0].author.name).to.equal('john')
@@ -217,15 +227,17 @@ describe('Test syndication feeds', () => {
217 227
218 for (const server of servers) { 228 for (const server of servers) {
219 { 229 {
220 const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'root_channel@localhost:' + servers[0].port }) 230 const query = { videoChannelName: 'root_channel@localhost:' + servers[0].port }
221 const jsonObj = JSON.parse(json.text) 231 const json = await server.feed.getJSON({ feed: 'videos', query })
232 const jsonObj = JSON.parse(json)
222 expect(jsonObj.items.length).to.be.equal(1) 233 expect(jsonObj.items.length).to.be.equal(1)
223 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 234 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
224 } 235 }
225 236
226 { 237 {
227 const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'john_channel@localhost:' + servers[0].port }) 238 const query = { videoChannelName: 'john_channel@localhost:' + servers[0].port }
228 const jsonObj = JSON.parse(json.text) 239 const json = await server.feed.getJSON({ feed: 'videos', query })
240 const jsonObj = JSON.parse(json)
229 expect(jsonObj.items.length).to.be.equal(1) 241 expect(jsonObj.items.length).to.be.equal(1)
230 expect(jsonObj.items[0].title).to.equal('user video') 242 expect(jsonObj.items[0].title).to.equal('user video')
231 } 243 }
@@ -235,12 +247,12 @@ describe('Test syndication feeds', () => {
235 it('Should correctly have videos feed with HLS only', async function () { 247 it('Should correctly have videos feed with HLS only', async function () {
236 this.timeout(120000) 248 this.timeout(120000)
237 249
238 await uploadVideo(serverHLSOnly.url, serverHLSOnly.accessToken, { name: 'hls only video' }) 250 await serverHLSOnly.videos.upload({ attributes: { name: 'hls only video' } })
239 251
240 await waitJobs([ serverHLSOnly ]) 252 await waitJobs([ serverHLSOnly ])
241 253
242 const json = await getJSONfeed(serverHLSOnly.url, 'videos') 254 const json = await serverHLSOnly.feed.getJSON({ feed: 'videos' })
243 const jsonObj = JSON.parse(json.text) 255 const jsonObj = JSON.parse(json)
244 expect(jsonObj.items.length).to.be.equal(1) 256 expect(jsonObj.items.length).to.be.equal(1)
245 expect(jsonObj.items[0].attachments).to.exist 257 expect(jsonObj.items[0].attachments).to.exist
246 expect(jsonObj.items[0].attachments.length).to.be.eq(4) 258 expect(jsonObj.items[0].attachments.length).to.be.eq(4)
@@ -257,9 +269,9 @@ describe('Test syndication feeds', () => {
257 269
258 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () { 270 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () {
259 for (const server of servers) { 271 for (const server of servers) {
260 const json = await getJSONfeed(server.url, 'video-comments') 272 const json = await server.feed.getJSON({ feed: 'video-comments' })
261 273
262 const jsonObj = JSON.parse(json.text) 274 const jsonObj = JSON.parse(json)
263 expect(jsonObj.items.length).to.be.equal(2) 275 expect(jsonObj.items.length).to.be.equal(2)
264 expect(jsonObj.items[0].html_content).to.equal('super comment 2') 276 expect(jsonObj.items[0].html_content).to.equal('super comment 2')
265 expect(jsonObj.items[1].html_content).to.equal('super comment 1') 277 expect(jsonObj.items[1].html_content).to.equal('super comment 1')
@@ -271,32 +283,32 @@ describe('Test syndication feeds', () => {
271 283
272 const remoteHandle = 'root@localhost:' + servers[0].port 284 const remoteHandle = 'root@localhost:' + servers[0].port
273 285
274 await addAccountToServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 286 await servers[1].blocklist.addToServerBlocklist({ account: remoteHandle })
275 287
276 { 288 {
277 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 2 }) 289 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 2 } })
278 const jsonObj = JSON.parse(json.text) 290 const jsonObj = JSON.parse(json)
279 expect(jsonObj.items.length).to.be.equal(0) 291 expect(jsonObj.items.length).to.be.equal(0)
280 } 292 }
281 293
282 await removeAccountFromServerBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 294 await servers[1].blocklist.removeFromServerBlocklist({ account: remoteHandle })
283 295
284 { 296 {
285 const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'server 2' })).uuid 297 const videoUUID = (await servers[1].videos.quickUpload({ name: 'server 2' })).uuid
286 await waitJobs(servers) 298 await waitJobs(servers)
287 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'super comment') 299 await servers[0].comments.createThread({ videoId: videoUUID, text: 'super comment' })
288 await waitJobs(servers) 300 await waitJobs(servers)
289 301
290 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 3 }) 302 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 3 } })
291 const jsonObj = JSON.parse(json.text) 303 const jsonObj = JSON.parse(json)
292 expect(jsonObj.items.length).to.be.equal(3) 304 expect(jsonObj.items.length).to.be.equal(3)
293 } 305 }
294 306
295 await addAccountToAccountBlocklist(servers[1].url, servers[1].accessToken, remoteHandle) 307 await servers[1].blocklist.addToMyBlocklist({ account: remoteHandle })
296 308
297 { 309 {
298 const json = await getJSONfeed(servers[1].url, 'video-comments', { version: 4 }) 310 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 4 } })
299 const jsonObj = JSON.parse(json.text) 311 const jsonObj = JSON.parse(json)
300 expect(jsonObj.items.length).to.be.equal(2) 312 expect(jsonObj.items.length).to.be.equal(2)
301 } 313 }
302 }) 314 })
@@ -308,66 +320,64 @@ describe('Test syndication feeds', () => {
308 320
309 it('Should list no videos for a user with no videos and no subscriptions', async function () { 321 it('Should list no videos for a user with no videos and no subscriptions', async function () {
310 const attr = { username: 'feeduser', password: 'password' } 322 const attr = { username: 'feeduser', password: 'password' }
311 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: attr.username, password: attr.password }) 323 await servers[0].users.create({ username: attr.username, password: attr.password })
312 const feeduserAccessToken = await userLogin(servers[0], attr) 324 const feeduserAccessToken = await servers[0].login.getAccessToken(attr)
313 325
314 { 326 {
315 const res = await getMyUserInformation(servers[0].url, feeduserAccessToken) 327 const user = await servers[0].users.getMyInfo({ token: feeduserAccessToken })
316 const user: User = res.body
317 feeduserAccountId = user.account.id 328 feeduserAccountId = user.account.id
318 } 329 }
319 330
320 { 331 {
321 const res = await getUserScopedTokens(servers[0].url, feeduserAccessToken) 332 const token = await servers[0].users.getMyScopedTokens({ token: feeduserAccessToken })
322 const token: ScopedToken = res.body
323 feeduserFeedToken = token.feedToken 333 feeduserFeedToken = token.feedToken
324 } 334 }
325 335
326 { 336 {
327 const res = await listUserSubscriptionVideos(servers[0].url, feeduserAccessToken) 337 const body = await servers[0].subscriptions.listVideos({ token: feeduserAccessToken })
328 expect(res.body.total).to.equal(0) 338 expect(body.total).to.equal(0)
329 339
330 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: feeduserFeedToken }) 340 const query = { accountId: feeduserAccountId, token: feeduserFeedToken }
331 const jsonObj = JSON.parse(json.text) 341 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
342 const jsonObj = JSON.parse(json)
332 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos 343 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
333 } 344 }
334 }) 345 })
335 346
336 it('Should fail with an invalid token', async function () { 347 it('Should fail with an invalid token', async function () {
337 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: 'toto' }, HttpStatusCode.FORBIDDEN_403) 348 const query = { accountId: feeduserAccountId, token: 'toto' }
349 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
338 }) 350 })
339 351
340 it('Should fail with a token of another user', async function () { 352 it('Should fail with a token of another user', async function () {
341 await getJSONfeed( 353 const query = { accountId: feeduserAccountId, token: userFeedToken }
342 servers[0].url, 354 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
343 'subscriptions',
344 { accountId: feeduserAccountId, token: userFeedToken },
345 HttpStatusCode.FORBIDDEN_403
346 )
347 }) 355 })
348 356
349 it('Should list no videos for a user with videos but no subscriptions', async function () { 357 it('Should list no videos for a user with videos but no subscriptions', async function () {
350 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 358 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
351 expect(res.body.total).to.equal(0) 359 expect(body.total).to.equal(0)
352 360
353 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken }) 361 const query = { accountId: userAccountId, token: userFeedToken }
354 const jsonObj = JSON.parse(json.text) 362 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
363 const jsonObj = JSON.parse(json)
355 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos 364 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
356 }) 365 })
357 366
358 it('Should list self videos for a user with a subscription to themselves', async function () { 367 it('Should list self videos for a user with a subscription to themselves', async function () {
359 this.timeout(30000) 368 this.timeout(30000)
360 369
361 await addUserSubscription(servers[0].url, userAccessToken, 'john_channel@localhost:' + servers[0].port) 370 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'john_channel@localhost:' + servers[0].port })
362 await waitJobs(servers) 371 await waitJobs(servers)
363 372
364 { 373 {
365 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 374 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
366 expect(res.body.total).to.equal(1) 375 expect(body.total).to.equal(1)
367 expect(res.body.data[0].name).to.equal('user video') 376 expect(body.data[0].name).to.equal('user video')
368 377
369 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 1 }) 378 const query = { accountId: userAccountId, token: userFeedToken, version: 1 }
370 const jsonObj = JSON.parse(json.text) 379 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
380 const jsonObj = JSON.parse(json)
371 expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's 381 expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's
372 } 382 }
373 }) 383 })
@@ -375,36 +385,33 @@ describe('Test syndication feeds', () => {
375 it('Should list videos of a user\'s subscription', async function () { 385 it('Should list videos of a user\'s subscription', async function () {
376 this.timeout(30000) 386 this.timeout(30000)
377 387
378 await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) 388 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[0].port })
379 await waitJobs(servers) 389 await waitJobs(servers)
380 390
381 { 391 {
382 const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken) 392 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
383 expect(res.body.total).to.equal(2, "there should be 2 videos part of the subscription") 393 expect(body.total).to.equal(2, "there should be 2 videos part of the subscription")
384 394
385 const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 2 }) 395 const query = { accountId: userAccountId, token: userFeedToken, version: 2 }
386 const jsonObj = JSON.parse(json.text) 396 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query })
397 const jsonObj = JSON.parse(json)
387 expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's 398 expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's
388 } 399 }
389 }) 400 })
390 401
391 it('Should renew the token, and so have an invalid old token', async function () { 402 it('Should renew the token, and so have an invalid old token', async function () {
392 await renewUserScopedTokens(servers[0].url, userAccessToken) 403 await servers[0].users.renewMyScopedTokens({ token: userAccessToken })
393 404
394 await getJSONfeed( 405 const query = { accountId: userAccountId, token: userFeedToken, version: 3 }
395 servers[0].url, 406 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
396 'subscriptions',
397 { accountId: userAccountId, token: userFeedToken, version: 3 },
398 HttpStatusCode.FORBIDDEN_403
399 )
400 }) 407 })
401 408
402 it('Should succeed with the new token', async function () { 409 it('Should succeed with the new token', async function () {
403 const res2 = await getUserScopedTokens(servers[0].url, userAccessToken) 410 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken })
404 const token: ScopedToken = res2.body
405 userFeedToken = token.feedToken 411 userFeedToken = token.feedToken
406 412
407 await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 4 }) 413 const query = { accountId: userAccountId, token: userFeedToken, version: 4 }
414 await servers[0].feed.getJSON({ feed: 'subscriptions', query })
408 }) 415 })
409 416
410 }) 417 })
diff --git a/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
index 59b136947..c4ae777f5 100644
--- a/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-transcoding-one/main.js
@@ -18,12 +18,12 @@ async function register ({ transcodingManager }) {
18 const builder = (options) => { 18 const builder = (options) => {
19 return { 19 return {
20 outputOptions: [ 20 outputOptions: [
21 '-r:' + options.streamNum + ' 5' 21 '-r:' + options.streamNum + ' 50'
22 ] 22 ]
23 } 23 }
24 } 24 }
25 25
26 transcodingManager.addLiveProfile('libx264', 'low-live', builder) 26 transcodingManager.addLiveProfile('libx264', 'high-live', builder)
27 } 27 }
28 } 28 }
29 29
@@ -45,7 +45,7 @@ async function register ({ transcodingManager }) {
45 const builder = () => { 45 const builder = () => {
46 return { 46 return {
47 inputOptions: [ 47 inputOptions: [
48 '-r 5' 48 '-r 50'
49 ] 49 ]
50 } 50 }
51 } 51 }
@@ -82,7 +82,6 @@ async function register ({ transcodingManager }) {
82 } 82 }
83} 83}
84 84
85
86async function unregister () { 85async function unregister () {
87 return 86 return
88} 87}
diff --git a/server/tests/fixtures/peertube-plugin-test-video-constants/main.js b/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
index 3e650e0a1..06527bd35 100644
--- a/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
+++ b/server/tests/fixtures/peertube-plugin-test-video-constants/main.js
@@ -1,46 +1,46 @@
1async function register ({ 1async function register ({
2 registerHook,
3 registerSetting,
4 settingsManager,
5 storageManager,
6 videoCategoryManager, 2 videoCategoryManager,
7 videoLicenceManager, 3 videoLicenceManager,
8 videoLanguageManager, 4 videoLanguageManager,
9 videoPrivacyManager, 5 videoPrivacyManager,
10 playlistPrivacyManager 6 playlistPrivacyManager,
7 getRouter
11}) { 8}) {
12 videoLanguageManager.addLanguage('al_bhed', 'Al Bhed') 9 videoLanguageManager.addConstant('al_bhed', 'Al Bhed')
13 videoLanguageManager.addLanguage('al_bhed2', 'Al Bhed 2') 10 videoLanguageManager.addLanguage('al_bhed2', 'Al Bhed 2')
14 videoLanguageManager.addLanguage('al_bhed3', 'Al Bhed 3') 11 videoLanguageManager.addConstant('al_bhed3', 'Al Bhed 3')
15 videoLanguageManager.deleteLanguage('en') 12 videoLanguageManager.deleteConstant('en')
16 videoLanguageManager.deleteLanguage('fr') 13 videoLanguageManager.deleteLanguage('fr')
17 videoLanguageManager.deleteLanguage('al_bhed3') 14 videoLanguageManager.deleteConstant('al_bhed3')
18 15
19 videoCategoryManager.addCategory(42, 'Best category') 16 videoCategoryManager.addCategory(42, 'Best category')
20 videoCategoryManager.addCategory(43, 'High best category') 17 videoCategoryManager.addConstant(43, 'High best category')
21 videoCategoryManager.deleteCategory(1) // Music 18 videoCategoryManager.deleteConstant(1) // Music
22 videoCategoryManager.deleteCategory(2) // Films 19 videoCategoryManager.deleteCategory(2) // Films
23 20
24 videoLicenceManager.addLicence(42, 'Best licence') 21 videoLicenceManager.addLicence(42, 'Best licence')
25 videoLicenceManager.addLicence(43, 'High best licence') 22 videoLicenceManager.addConstant(43, 'High best licence')
26 videoLicenceManager.deleteLicence(1) // Attribution 23 videoLicenceManager.deleteConstant(1) // Attribution
27 videoLicenceManager.deleteLicence(7) // Public domain 24 videoLicenceManager.deleteConstant(7) // Public domain
28 25
26 videoPrivacyManager.deleteConstant(2)
29 videoPrivacyManager.deletePrivacy(2) 27 videoPrivacyManager.deletePrivacy(2)
28 playlistPrivacyManager.deleteConstant(3)
30 playlistPrivacyManager.deletePlaylistPrivacy(3) 29 playlistPrivacyManager.deletePlaylistPrivacy(3)
31}
32 30
33async function unregister () { 31 {
34 return 32 const router = getRouter()
33 router.get('/reset-categories', (req, res) => {
34 videoCategoryManager.resetConstants()
35
36 res.sendStatus(204)
37 })
38 }
35} 39}
36 40
41async function unregister () {}
42
37module.exports = { 43module.exports = {
38 register, 44 register,
39 unregister 45 unregister
40} 46}
41
42// ############################################################################
43
44function addToCount (obj) {
45 return Object.assign({}, obj, { count: obj.count + 1 })
46}
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index f8e6f0b98..db405ff31 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -234,7 +234,7 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
234 }) 234 })
235 235
236 { 236 {
237 const searchHooks = [ 237 const filterHooks = [
238 'filter:api.search.videos.local.list.params', 238 'filter:api.search.videos.local.list.params',
239 'filter:api.search.videos.local.list.result', 239 'filter:api.search.videos.local.list.result',
240 'filter:api.search.videos.index.list.params', 240 'filter:api.search.videos.index.list.params',
@@ -246,10 +246,13 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
246 'filter:api.search.video-playlists.local.list.params', 246 'filter:api.search.video-playlists.local.list.params',
247 'filter:api.search.video-playlists.local.list.result', 247 'filter:api.search.video-playlists.local.list.result',
248 'filter:api.search.video-playlists.index.list.params', 248 'filter:api.search.video-playlists.index.list.params',
249 'filter:api.search.video-playlists.index.list.result' 249 'filter:api.search.video-playlists.index.list.result',
250
251 'filter:api.overviews.videos.list.params',
252 'filter:api.overviews.videos.list.result'
250 ] 253 ]
251 254
252 for (const h of searchHooks) { 255 for (const h of filterHooks) {
253 registerHook({ 256 registerHook({
254 target: h, 257 target: h,
255 handler: (obj) => { 258 handler: (obj) => {
diff --git a/server/tests/fixtures/video_very_short_240p.mp4 b/server/tests/fixtures/video_very_short_240p.mp4
new file mode 100644
index 000000000..95b6be92a
--- /dev/null
+++ b/server/tests/fixtures/video_very_short_240p.mp4
Binary files differ
diff --git a/server/tests/helpers/comment-model.ts b/server/tests/helpers/comment-model.ts
index 4c51b7000..31dc6ec72 100644
--- a/server/tests/helpers/comment-model.ts
+++ b/server/tests/helpers/comment-model.ts
@@ -1,7 +1,7 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { VideoCommentModel } from '../../models/video/video-comment' 5import { VideoCommentModel } from '../../models/video/video-comment'
6 6
7const expect = chai.expect 7const expect = chai.expect
diff --git a/server/tests/helpers/core-utils.ts b/server/tests/helpers/core-utils.ts
index c028b316d..d5cac51a3 100644
--- a/server/tests/helpers/core-utils.ts
+++ b/server/tests/helpers/core-utils.ts
@@ -1,10 +1,10 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
4import * as chai from 'chai'
5import { snakeCase } from 'lodash' 5import { snakeCase } from 'lodash'
6import { objectConverter, parseBytes } from '../../helpers/core-utils'
7import validator from 'validator' 6import validator from 'validator'
7import { objectConverter, parseBytes } from '../../helpers/core-utils'
8 8
9const expect = chai.expect 9const expect = chai.expect
10 10
diff --git a/server/tests/helpers/image.ts b/server/tests/helpers/image.ts
index 54911697f..9fe9aa4cb 100644
--- a/server/tests/helpers/image.ts
+++ b/server/tests/helpers/image.ts
@@ -1,11 +1,11 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { readFile, remove } from 'fs-extra' 5import { readFile, remove } from 'fs-extra'
5import { join } from 'path' 6import { join } from 'path'
6import { processImage } from '../../../server/helpers/image-utils' 7import { processImage } from '../../../server/helpers/image-utils'
7import { buildAbsoluteFixturePath, root } from '../../../shared/extra-utils' 8import { buildAbsoluteFixturePath, root } from '../../../shared/extra-utils'
8import { expect } from 'chai'
9 9
10async function checkBuffers (path1: string, path2: string, equals: boolean) { 10async function checkBuffers (path1: string, path2: string, equals: boolean) {
11 const [ buf1, buf2 ] = await Promise.all([ 11 const [ buf1, buf2 ] = await Promise.all([
diff --git a/server/tests/helpers/request.ts b/server/tests/helpers/request.ts
index 5e77f129e..7f7873df3 100644
--- a/server/tests/helpers/request.ts
+++ b/server/tests/helpers/request.ts
@@ -4,7 +4,7 @@ import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { pathExists, remove } from 'fs-extra' 5import { pathExists, remove } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { get4KFileUrl, root, wait } from '../../../shared/extra-utils' 7import { FIXTURE_URLS, root, wait } from '../../../shared/extra-utils'
8import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests' 8import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
9 9
10describe('Request helpers', function () { 10describe('Request helpers', function () {
@@ -13,7 +13,7 @@ describe('Request helpers', function () {
13 13
14 it('Should throw an error when the bytes limit is exceeded for request', async function () { 14 it('Should throw an error when the bytes limit is exceeded for request', async function () {
15 try { 15 try {
16 await doRequest(get4KFileUrl(), { bodyKBLimit: 3 }) 16 await doRequest(FIXTURE_URLS.video4K, { bodyKBLimit: 3 })
17 } catch { 17 } catch {
18 return 18 return
19 } 19 }
@@ -23,7 +23,7 @@ describe('Request helpers', function () {
23 23
24 it('Should throw an error when the bytes limit is exceeded for request and save file', async function () { 24 it('Should throw an error when the bytes limit is exceeded for request and save file', async function () {
25 try { 25 try {
26 await doRequestAndSaveToFile(get4KFileUrl(), destPath1, { bodyKBLimit: 3 }) 26 await doRequestAndSaveToFile(FIXTURE_URLS.video4K, destPath1, { bodyKBLimit: 3 })
27 } catch { 27 } catch {
28 28
29 await wait(500) 29 await wait(500)
@@ -35,8 +35,8 @@ describe('Request helpers', function () {
35 }) 35 })
36 36
37 it('Should succeed if the file is below the limit', async function () { 37 it('Should succeed if the file is below the limit', async function () {
38 await doRequest(get4KFileUrl(), { bodyKBLimit: 5 }) 38 await doRequest(FIXTURE_URLS.video4K, { bodyKBLimit: 5 })
39 await doRequestAndSaveToFile(get4KFileUrl(), destPath2, { bodyKBLimit: 5 }) 39 await doRequestAndSaveToFile(FIXTURE_URLS.video4K, destPath2, { bodyKBLimit: 5 })
40 40
41 expect(await pathExists(destPath2)).to.be.true 41 expect(await pathExists(destPath2)).to.be.true
42 }) 42 })
diff --git a/server/tests/index.ts b/server/tests/index.ts
index 3fbd0ebbd..1718ac424 100644
--- a/server/tests/index.ts
+++ b/server/tests/index.ts
@@ -6,3 +6,4 @@ import './cli/'
6import './api/' 6import './api/'
7import './plugins/' 7import './plugins/'
8import './helpers/' 8import './helpers/'
9import './lib/'
diff --git a/server/tests/lib/index.ts b/server/tests/lib/index.ts
new file mode 100644
index 000000000..a40df35fd
--- /dev/null
+++ b/server/tests/lib/index.ts
@@ -0,0 +1 @@
export * from './video-constant-registry-factory'
diff --git a/server/tests/lib/video-constant-registry-factory.ts b/server/tests/lib/video-constant-registry-factory.ts
new file mode 100644
index 000000000..e26b286e1
--- /dev/null
+++ b/server/tests/lib/video-constant-registry-factory.ts
@@ -0,0 +1,155 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions */
2import 'mocha'
3import { expect } from 'chai'
4import { VideoConstantManagerFactory } from '@server/lib/plugins/video-constant-manager-factory'
5import {
6 VIDEO_CATEGORIES,
7 VIDEO_LANGUAGES,
8 VIDEO_LICENCES,
9 VIDEO_PLAYLIST_PRIVACIES,
10 VIDEO_PRIVACIES
11} from '@server/initializers/constants'
12import {
13 VideoPlaylistPrivacy,
14 VideoPrivacy
15} from '@shared/models'
16
17describe('VideoConstantManagerFactory', function () {
18 const factory = new VideoConstantManagerFactory('peertube-plugin-constants')
19
20 afterEach(() => {
21 factory.resetVideoConstants('peertube-plugin-constants')
22 })
23
24 describe('VideoCategoryManager', () => {
25 const videoCategoryManager = factory.createVideoConstantManager<number>('category')
26
27 it('Should be able to list all video category constants', () => {
28 const constants = videoCategoryManager.getConstants()
29 expect(constants).to.deep.equal(VIDEO_CATEGORIES)
30 })
31
32 it('Should be able to delete a video category constant', () => {
33 const successfullyDeleted = videoCategoryManager.deleteConstant(1)
34 expect(successfullyDeleted).to.be.true
35 expect(videoCategoryManager.getConstantValue(1)).to.be.undefined
36 })
37
38 it('Should be able to add a video category constant', () => {
39 const successfullyAdded = videoCategoryManager.addConstant(42, 'The meaning of life')
40 expect(successfullyAdded).to.be.true
41 expect(videoCategoryManager.getConstantValue(42)).to.equal('The meaning of life')
42 })
43
44 it('Should be able to reset video category constants', () => {
45 videoCategoryManager.deleteConstant(1)
46 videoCategoryManager.resetConstants()
47 expect(videoCategoryManager.getConstantValue(1)).not.be.undefined
48 })
49 })
50
51 describe('VideoLicenceManager', () => {
52 const videoLicenceManager = factory.createVideoConstantManager<number>('licence')
53 it('Should be able to list all video licence constants', () => {
54 const constants = videoLicenceManager.getConstants()
55 expect(constants).to.deep.equal(VIDEO_LICENCES)
56 })
57
58 it('Should be able to delete a video licence constant', () => {
59 const successfullyDeleted = videoLicenceManager.deleteConstant(1)
60 expect(successfullyDeleted).to.be.true
61 expect(videoLicenceManager.getConstantValue(1)).to.be.undefined
62 })
63
64 it('Should be able to add a video licence constant', () => {
65 const successfullyAdded = videoLicenceManager.addConstant(42, 'European Union Public Licence')
66 expect(successfullyAdded).to.be.true
67 expect(videoLicenceManager.getConstantValue(42)).to.equal('European Union Public Licence')
68 })
69
70 it('Should be able to reset video licence constants', () => {
71 videoLicenceManager.deleteConstant(1)
72 videoLicenceManager.resetConstants()
73 expect(videoLicenceManager.getConstantValue(1)).not.be.undefined
74 })
75 })
76
77 describe('PlaylistPrivacyManager', () => {
78 const playlistPrivacyManager = factory.createVideoConstantManager<VideoPlaylistPrivacy>('playlistPrivacy')
79 it('Should be able to list all video playlist privacy constants', () => {
80 const constants = playlistPrivacyManager.getConstants()
81 expect(constants).to.deep.equal(VIDEO_PLAYLIST_PRIVACIES)
82 })
83
84 it('Should be able to delete a video playlist privacy constant', () => {
85 const successfullyDeleted = playlistPrivacyManager.deleteConstant(1)
86 expect(successfullyDeleted).to.be.true
87 expect(playlistPrivacyManager.getConstantValue(1)).to.be.undefined
88 })
89
90 it('Should be able to add a video playlist privacy constant', () => {
91 const successfullyAdded = playlistPrivacyManager.addConstant(42, 'Friends only')
92 expect(successfullyAdded).to.be.true
93 expect(playlistPrivacyManager.getConstantValue(42)).to.equal('Friends only')
94 })
95
96 it('Should be able to reset video playlist privacy constants', () => {
97 playlistPrivacyManager.deleteConstant(1)
98 playlistPrivacyManager.resetConstants()
99 expect(playlistPrivacyManager.getConstantValue(1)).not.be.undefined
100 })
101 })
102
103 describe('VideoPrivacyManager', () => {
104 const videoPrivacyManager = factory.createVideoConstantManager<VideoPrivacy>('privacy')
105 it('Should be able to list all video privacy constants', () => {
106 const constants = videoPrivacyManager.getConstants()
107 expect(constants).to.deep.equal(VIDEO_PRIVACIES)
108 })
109
110 it('Should be able to delete a video privacy constant', () => {
111 const successfullyDeleted = videoPrivacyManager.deleteConstant(1)
112 expect(successfullyDeleted).to.be.true
113 expect(videoPrivacyManager.getConstantValue(1)).to.be.undefined
114 })
115
116 it('Should be able to add a video privacy constant', () => {
117 const successfullyAdded = videoPrivacyManager.addConstant(42, 'Friends only')
118 expect(successfullyAdded).to.be.true
119 expect(videoPrivacyManager.getConstantValue(42)).to.equal('Friends only')
120 })
121
122 it('Should be able to reset video privacy constants', () => {
123 videoPrivacyManager.deleteConstant(1)
124 videoPrivacyManager.resetConstants()
125 expect(videoPrivacyManager.getConstantValue(1)).not.be.undefined
126 })
127 })
128
129 describe('VideoLanguageManager', () => {
130 const videoLanguageManager = factory.createVideoConstantManager<string>('language')
131 it('Should be able to list all video language constants', () => {
132 const constants = videoLanguageManager.getConstants()
133 expect(constants).to.deep.equal(VIDEO_LANGUAGES)
134 })
135
136 it('Should be able to add a video language constant', () => {
137 const successfullyAdded = videoLanguageManager.addConstant('fr', 'Fr occitan')
138 expect(successfullyAdded).to.be.true
139 expect(videoLanguageManager.getConstantValue('fr')).to.equal('Fr occitan')
140 })
141
142 it('Should be able to delete a video language constant', () => {
143 videoLanguageManager.addConstant('fr', 'Fr occitan')
144 const successfullyDeleted = videoLanguageManager.deleteConstant('fr')
145 expect(successfullyDeleted).to.be.true
146 expect(videoLanguageManager.getConstantValue('fr')).to.be.undefined
147 })
148
149 it('Should be able to reset video language constants', () => {
150 videoLanguageManager.addConstant('fr', 'Fr occitan')
151 videoLanguageManager.resetConstants()
152 expect(videoLanguageManager.getConstantValue('fr')).to.be.undefined
153 })
154 })
155})
diff --git a/server/tests/misc-endpoints.ts b/server/tests/misc-endpoints.ts
index 09e5afcf9..4968eef08 100644
--- a/server/tests/misc-endpoints.ts
+++ b/server/tests/misc-endpoints.ts
@@ -2,28 +2,18 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import { cleanupTests, createSingleServer, makeGetRequest, PeerTubeServer, setAccessTokensToServers } from '@shared/extra-utils'
6 addVideoChannel, 6import { HttpStatusCode, VideoPrivacy } from '@shared/models'
7 cleanupTests,
8 createUser,
9 flushAndRunServer,
10 makeGetRequest,
11 ServerInfo,
12 setAccessTokensToServers,
13 uploadVideo
14} from '../../shared/extra-utils'
15import { VideoPrivacy } from '../../shared/models/videos'
16import { HttpStatusCode } from '@shared/core-utils'
17 7
18const expect = chai.expect 8const expect = chai.expect
19 9
20describe('Test misc endpoints', function () { 10describe('Test misc endpoints', function () {
21 let server: ServerInfo 11 let server: PeerTubeServer
22 12
23 before(async function () { 13 before(async function () {
24 this.timeout(120000) 14 this.timeout(120000)
25 15
26 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
27 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
28 }) 18 })
29 19
@@ -33,7 +23,7 @@ describe('Test misc endpoints', function () {
33 const res = await makeGetRequest({ 23 const res = await makeGetRequest({
34 url: server.url, 24 url: server.url,
35 path: '/.well-known/security.txt', 25 path: '/.well-known/security.txt',
36 statusCodeExpected: HttpStatusCode.OK_200 26 expectedStatus: HttpStatusCode.OK_200
37 }) 27 })
38 28
39 expect(res.text).to.contain('security issue') 29 expect(res.text).to.contain('security issue')
@@ -43,7 +33,7 @@ describe('Test misc endpoints', function () {
43 const res = await makeGetRequest({ 33 const res = await makeGetRequest({
44 url: server.url, 34 url: server.url,
45 path: '/.well-known/nodeinfo', 35 path: '/.well-known/nodeinfo',
46 statusCodeExpected: HttpStatusCode.OK_200 36 expectedStatus: HttpStatusCode.OK_200
47 }) 37 })
48 38
49 expect(res.body.links).to.be.an('array') 39 expect(res.body.links).to.be.an('array')
@@ -55,7 +45,7 @@ describe('Test misc endpoints', function () {
55 const res = await makeGetRequest({ 45 const res = await makeGetRequest({
56 url: server.url, 46 url: server.url,
57 path: '/.well-known/dnt-policy.txt', 47 path: '/.well-known/dnt-policy.txt',
58 statusCodeExpected: HttpStatusCode.OK_200 48 expectedStatus: HttpStatusCode.OK_200
59 }) 49 })
60 50
61 expect(res.text).to.contain('http://www.w3.org/TR/tracking-dnt') 51 expect(res.text).to.contain('http://www.w3.org/TR/tracking-dnt')
@@ -65,7 +55,7 @@ describe('Test misc endpoints', function () {
65 const res = await makeGetRequest({ 55 const res = await makeGetRequest({
66 url: server.url, 56 url: server.url,
67 path: '/.well-known/dnt', 57 path: '/.well-known/dnt',
68 statusCodeExpected: HttpStatusCode.OK_200 58 expectedStatus: HttpStatusCode.OK_200
69 }) 59 })
70 60
71 expect(res.body.tracking).to.equal('N') 61 expect(res.body.tracking).to.equal('N')
@@ -75,7 +65,7 @@ describe('Test misc endpoints', function () {
75 const res = await makeGetRequest({ 65 const res = await makeGetRequest({
76 url: server.url, 66 url: server.url,
77 path: '/.well-known/change-password', 67 path: '/.well-known/change-password',
78 statusCodeExpected: HttpStatusCode.FOUND_302 68 expectedStatus: HttpStatusCode.FOUND_302
79 }) 69 })
80 70
81 expect(res.header.location).to.equal('/my-account/settings') 71 expect(res.header.location).to.equal('/my-account/settings')
@@ -88,7 +78,7 @@ describe('Test misc endpoints', function () {
88 const res = await makeGetRequest({ 78 const res = await makeGetRequest({
89 url: server.url, 79 url: server.url,
90 path: '/.well-known/webfinger?resource=' + resource, 80 path: '/.well-known/webfinger?resource=' + resource,
91 statusCodeExpected: HttpStatusCode.OK_200 81 expectedStatus: HttpStatusCode.OK_200
92 }) 82 })
93 83
94 const data = res.body 84 const data = res.body
@@ -113,7 +103,7 @@ describe('Test misc endpoints', function () {
113 const res = await makeGetRequest({ 103 const res = await makeGetRequest({
114 url: server.url, 104 url: server.url,
115 path: '/robots.txt', 105 path: '/robots.txt',
116 statusCodeExpected: HttpStatusCode.OK_200 106 expectedStatus: HttpStatusCode.OK_200
117 }) 107 })
118 108
119 expect(res.text).to.contain('User-agent') 109 expect(res.text).to.contain('User-agent')
@@ -123,7 +113,7 @@ describe('Test misc endpoints', function () {
123 await makeGetRequest({ 113 await makeGetRequest({
124 url: server.url, 114 url: server.url,
125 path: '/security.txt', 115 path: '/security.txt',
126 statusCodeExpected: HttpStatusCode.MOVED_PERMANENTLY_301 116 expectedStatus: HttpStatusCode.MOVED_PERMANENTLY_301
127 }) 117 })
128 }) 118 })
129 119
@@ -131,7 +121,7 @@ describe('Test misc endpoints', function () {
131 const res = await makeGetRequest({ 121 const res = await makeGetRequest({
132 url: server.url, 122 url: server.url,
133 path: '/nodeinfo/2.0.json', 123 path: '/nodeinfo/2.0.json',
134 statusCodeExpected: HttpStatusCode.OK_200 124 expectedStatus: HttpStatusCode.OK_200
135 }) 125 })
136 126
137 expect(res.body.software.name).to.equal('peertube') 127 expect(res.body.software.name).to.equal('peertube')
@@ -146,7 +136,7 @@ describe('Test misc endpoints', function () {
146 const res = await makeGetRequest({ 136 const res = await makeGetRequest({
147 url: server.url, 137 url: server.url,
148 path: '/sitemap.xml', 138 path: '/sitemap.xml',
149 statusCodeExpected: HttpStatusCode.OK_200 139 expectedStatus: HttpStatusCode.OK_200
150 }) 140 })
151 141
152 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"') 142 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"')
@@ -157,7 +147,7 @@ describe('Test misc endpoints', function () {
157 const res = await makeGetRequest({ 147 const res = await makeGetRequest({
158 url: server.url, 148 url: server.url,
159 path: '/sitemap.xml', 149 path: '/sitemap.xml',
160 statusCodeExpected: HttpStatusCode.OK_200 150 expectedStatus: HttpStatusCode.OK_200
161 }) 151 })
162 152
163 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"') 153 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"')
@@ -167,20 +157,20 @@ describe('Test misc endpoints', function () {
167 it('Should add videos, channel and accounts and get sitemap', async function () { 157 it('Should add videos, channel and accounts and get sitemap', async function () {
168 this.timeout(35000) 158 this.timeout(35000)
169 159
170 await uploadVideo(server.url, server.accessToken, { name: 'video 1', nsfw: false }) 160 await server.videos.upload({ attributes: { name: 'video 1', nsfw: false } })
171 await uploadVideo(server.url, server.accessToken, { name: 'video 2', nsfw: false }) 161 await server.videos.upload({ attributes: { name: 'video 2', nsfw: false } })
172 await uploadVideo(server.url, server.accessToken, { name: 'video 3', privacy: VideoPrivacy.PRIVATE }) 162 await server.videos.upload({ attributes: { name: 'video 3', privacy: VideoPrivacy.PRIVATE } })
173 163
174 await addVideoChannel(server.url, server.accessToken, { name: 'channel1', displayName: 'channel 1' }) 164 await server.channels.create({ attributes: { name: 'channel1', displayName: 'channel 1' } })
175 await addVideoChannel(server.url, server.accessToken, { name: 'channel2', displayName: 'channel 2' }) 165 await server.channels.create({ attributes: { name: 'channel2', displayName: 'channel 2' } })
176 166
177 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user1', password: 'password' }) 167 await server.users.create({ username: 'user1', password: 'password' })
178 await createUser({ url: server.url, accessToken: server.accessToken, username: 'user2', password: 'password' }) 168 await server.users.create({ username: 'user2', password: 'password' })
179 169
180 const res = await makeGetRequest({ 170 const res = await makeGetRequest({
181 url: server.url, 171 url: server.url,
182 path: '/sitemap.xml?t=1', // avoid using cache 172 path: '/sitemap.xml?t=1', // avoid using cache
183 statusCodeExpected: HttpStatusCode.OK_200 173 expectedStatus: HttpStatusCode.OK_200
184 }) 174 })
185 175
186 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"') 176 expect(res.text).to.contain('xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"')
diff --git a/server/tests/plugins/action-hooks.ts b/server/tests/plugins/action-hooks.ts
index 0f57ef7fe..4c1bc7d06 100644
--- a/server/tests/plugins/action-hooks.ts
+++ b/server/tests/plugins/action-hooks.ts
@@ -1,63 +1,38 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { ServerHookName, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
5import {
6 addVideoCommentReply,
7 addVideoCommentThread,
8 addVideoInPlaylist,
9 blockUser,
10 createLive,
11 createUser,
12 createVideoPlaylist,
13 deleteVideoComment,
14 getPluginTestPath,
15 installPlugin,
16 registerUser,
17 removeUser,
18 setAccessTokensToServers,
19 setDefaultVideoChannel,
20 unblockUser,
21 updateUser,
22 updateVideo,
23 uploadVideo,
24 userLogin,
25 viewVideo
26} from '../../../shared/extra-utils'
27import { 4import {
28 cleanupTests, 5 cleanupTests,
29 flushAndRunMultipleServers, 6 createMultipleServers,
30 killallServers, 7 killallServers,
31 reRunServer, 8 PeerTubeServer,
32 ServerInfo, 9 PluginsCommand,
33 waitUntilLog 10 setAccessTokensToServers,
34} from '../../../shared/extra-utils/server/servers' 11 setDefaultVideoChannel
12} from '@shared/extra-utils'
13import { ServerHookName, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
35 14
36describe('Test plugin action hooks', function () { 15describe('Test plugin action hooks', function () {
37 let servers: ServerInfo[] 16 let servers: PeerTubeServer[]
38 let videoUUID: string 17 let videoUUID: string
39 let threadId: number 18 let threadId: number
40 19
41 function checkHook (hook: ServerHookName) { 20 function checkHook (hook: ServerHookName) {
42 return waitUntilLog(servers[0], 'Run hook ' + hook) 21 return servers[0].servers.waitUntilLog('Run hook ' + hook)
43 } 22 }
44 23
45 before(async function () { 24 before(async function () {
46 this.timeout(30000) 25 this.timeout(30000)
47 26
48 servers = await flushAndRunMultipleServers(2) 27 servers = await createMultipleServers(2)
49 await setAccessTokensToServers(servers) 28 await setAccessTokensToServers(servers)
50 await setDefaultVideoChannel(servers) 29 await setDefaultVideoChannel(servers)
51 30
52 await installPlugin({ 31 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath() })
53 url: servers[0].url,
54 accessToken: servers[0].accessToken,
55 path: getPluginTestPath()
56 })
57 32
58 killallServers([ servers[0] ]) 33 await killallServers([ servers[0] ])
59 34
60 await reRunServer(servers[0], { 35 await servers[0].run({
61 live: { 36 live: {
62 enabled: true 37 enabled: true
63 } 38 }
@@ -73,20 +48,20 @@ describe('Test plugin action hooks', function () {
73 describe('Videos hooks', function () { 48 describe('Videos hooks', function () {
74 49
75 it('Should run action:api.video.uploaded', async function () { 50 it('Should run action:api.video.uploaded', async function () {
76 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 51 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' } })
77 videoUUID = res.body.video.uuid 52 videoUUID = uuid
78 53
79 await checkHook('action:api.video.uploaded') 54 await checkHook('action:api.video.uploaded')
80 }) 55 })
81 56
82 it('Should run action:api.video.updated', async function () { 57 it('Should run action:api.video.updated', async function () {
83 await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video updated' }) 58 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video updated' } })
84 59
85 await checkHook('action:api.video.updated') 60 await checkHook('action:api.video.updated')
86 }) 61 })
87 62
88 it('Should run action:api.video.viewed', async function () { 63 it('Should run action:api.video.viewed', async function () {
89 await viewVideo(servers[0].url, videoUUID) 64 await servers[0].videos.view({ id: videoUUID })
90 65
91 await checkHook('action:api.video.viewed') 66 await checkHook('action:api.video.viewed')
92 }) 67 })
@@ -98,10 +73,10 @@ describe('Test plugin action hooks', function () {
98 const attributes = { 73 const attributes = {
99 name: 'live', 74 name: 'live',
100 privacy: VideoPrivacy.PUBLIC, 75 privacy: VideoPrivacy.PUBLIC,
101 channelId: servers[0].videoChannel.id 76 channelId: servers[0].store.channel.id
102 } 77 }
103 78
104 await createLive(servers[0].url, servers[0].accessToken, attributes) 79 await servers[0].live.create({ fields: attributes })
105 80
106 await checkHook('action:api.live-video.created') 81 await checkHook('action:api.live-video.created')
107 }) 82 })
@@ -109,20 +84,20 @@ describe('Test plugin action hooks', function () {
109 84
110 describe('Comments hooks', function () { 85 describe('Comments hooks', function () {
111 it('Should run action:api.video-thread.created', async function () { 86 it('Should run action:api.video-thread.created', async function () {
112 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread') 87 const created = await servers[0].comments.createThread({ videoId: videoUUID, text: 'thread' })
113 threadId = res.body.comment.id 88 threadId = created.id
114 89
115 await checkHook('action:api.video-thread.created') 90 await checkHook('action:api.video-thread.created')
116 }) 91 })
117 92
118 it('Should run action:api.video-comment-reply.created', async function () { 93 it('Should run action:api.video-comment-reply.created', async function () {
119 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'reply') 94 await servers[0].comments.addReply({ videoId: videoUUID, toCommentId: threadId, text: 'reply' })
120 95
121 await checkHook('action:api.video-comment-reply.created') 96 await checkHook('action:api.video-comment-reply.created')
122 }) 97 })
123 98
124 it('Should run action:api.video-comment.deleted', async function () { 99 it('Should run action:api.video-comment.deleted', async function () {
125 await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId) 100 await servers[0].comments.delete({ videoId: videoUUID, commentId: threadId })
126 101
127 await checkHook('action:api.video-comment.deleted') 102 await checkHook('action:api.video-comment.deleted')
128 }) 103 })
@@ -132,49 +107,44 @@ describe('Test plugin action hooks', function () {
132 let userId: number 107 let userId: number
133 108
134 it('Should run action:api.user.registered', async function () { 109 it('Should run action:api.user.registered', async function () {
135 await registerUser(servers[0].url, 'registered_user', 'super_password') 110 await servers[0].users.register({ username: 'registered_user' })
136 111
137 await checkHook('action:api.user.registered') 112 await checkHook('action:api.user.registered')
138 }) 113 })
139 114
140 it('Should run action:api.user.created', async function () { 115 it('Should run action:api.user.created', async function () {
141 const res = await createUser({ 116 const user = await servers[0].users.create({ username: 'created_user' })
142 url: servers[0].url, 117 userId = user.id
143 accessToken: servers[0].accessToken,
144 username: 'created_user',
145 password: 'super_password'
146 })
147 userId = res.body.user.id
148 118
149 await checkHook('action:api.user.created') 119 await checkHook('action:api.user.created')
150 }) 120 })
151 121
152 it('Should run action:api.user.oauth2-got-token', async function () { 122 it('Should run action:api.user.oauth2-got-token', async function () {
153 await userLogin(servers[0], { username: 'created_user', password: 'super_password' }) 123 await servers[0].login.login({ user: { username: 'created_user' } })
154 124
155 await checkHook('action:api.user.oauth2-got-token') 125 await checkHook('action:api.user.oauth2-got-token')
156 }) 126 })
157 127
158 it('Should run action:api.user.blocked', async function () { 128 it('Should run action:api.user.blocked', async function () {
159 await blockUser(servers[0].url, userId, servers[0].accessToken) 129 await servers[0].users.banUser({ userId })
160 130
161 await checkHook('action:api.user.blocked') 131 await checkHook('action:api.user.blocked')
162 }) 132 })
163 133
164 it('Should run action:api.user.unblocked', async function () { 134 it('Should run action:api.user.unblocked', async function () {
165 await unblockUser(servers[0].url, userId, servers[0].accessToken) 135 await servers[0].users.unbanUser({ userId })
166 136
167 await checkHook('action:api.user.unblocked') 137 await checkHook('action:api.user.unblocked')
168 }) 138 })
169 139
170 it('Should run action:api.user.updated', async function () { 140 it('Should run action:api.user.updated', async function () {
171 await updateUser({ url: servers[0].url, accessToken: servers[0].accessToken, userId, videoQuota: 50 }) 141 await servers[0].users.update({ userId, videoQuota: 50 })
172 142
173 await checkHook('action:api.user.updated') 143 await checkHook('action:api.user.updated')
174 }) 144 })
175 145
176 it('Should run action:api.user.deleted', async function () { 146 it('Should run action:api.user.deleted', async function () {
177 await removeUser(servers[0].url, userId, servers[0].accessToken) 147 await servers[0].users.remove({ userId })
178 148
179 await checkHook('action:api.user.deleted') 149 await checkHook('action:api.user.deleted')
180 }) 150 })
@@ -186,30 +156,23 @@ describe('Test plugin action hooks', function () {
186 156
187 before(async function () { 157 before(async function () {
188 { 158 {
189 const res = await createVideoPlaylist({ 159 const { id } = await servers[0].playlists.create({
190 url: servers[0].url, 160 attributes: {
191 token: servers[0].accessToken,
192 playlistAttrs: {
193 displayName: 'My playlist', 161 displayName: 'My playlist',
194 privacy: VideoPlaylistPrivacy.PRIVATE 162 privacy: VideoPlaylistPrivacy.PRIVATE
195 } 163 }
196 }) 164 })
197 playlistId = res.body.videoPlaylist.id 165 playlistId = id
198 } 166 }
199 167
200 { 168 {
201 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'my super name' }) 169 const { id } = await servers[0].videos.upload({ attributes: { name: 'my super name' } })
202 videoId = res.body.video.id 170 videoId = id
203 } 171 }
204 }) 172 })
205 173
206 it('Should run action:api.video-playlist-element.created', async function () { 174 it('Should run action:api.video-playlist-element.created', async function () {
207 await addVideoInPlaylist({ 175 await servers[0].playlists.addElement({ playlistId, attributes: { videoId } })
208 url: servers[0].url,
209 token: servers[0].accessToken,
210 playlistId,
211 elementAttrs: { videoId }
212 })
213 176
214 await checkHook('action:api.video-playlist-element.created') 177 await checkHook('action:api.video-playlist-element.created')
215 }) 178 })
diff --git a/server/tests/plugins/external-auth.ts b/server/tests/plugins/external-auth.ts
index 5addb45c7..f3e018d43 100644
--- a/server/tests/plugins/external-auth.ts
+++ b/server/tests/plugins/external-auth.ts
@@ -2,44 +2,32 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { ServerConfig, User, UserRole } from '@shared/models'
6import { 5import {
6 cleanupTests,
7 createSingleServer,
7 decodeQueryString, 8 decodeQueryString,
8 getConfig, 9 PeerTubeServer,
9 getExternalAuth, 10 PluginsCommand,
10 getMyUserInformation,
11 getPluginTestPath,
12 installPlugin,
13 loginUsingExternalToken,
14 logout,
15 refreshToken,
16 setAccessTokensToServers, 11 setAccessTokensToServers,
17 uninstallPlugin, 12 wait
18 updateMyUser, 13} from '@shared/extra-utils'
19 wait, 14import { HttpStatusCode, UserRole } from '@shared/models'
20 userLogin,
21 updatePluginSettings,
22 createUser
23} from '../../../shared/extra-utils'
24import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
25import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
26 15
27async function loginExternal (options: { 16async function loginExternal (options: {
28 server: ServerInfo 17 server: PeerTubeServer
29 npmName: string 18 npmName: string
30 authName: string 19 authName: string
31 username: string 20 username: string
32 query?: any 21 query?: any
33 statusCodeExpected?: HttpStatusCode 22 expectedStatus?: HttpStatusCode
34 statusCodeExpectedStep2?: HttpStatusCode 23 expectedStatusStep2?: HttpStatusCode
35}) { 24}) {
36 const res = await getExternalAuth({ 25 const res = await options.server.plugins.getExternalAuth({
37 url: options.server.url,
38 npmName: options.npmName, 26 npmName: options.npmName,
39 npmVersion: '0.0.1', 27 npmVersion: '0.0.1',
40 authName: options.authName, 28 authName: options.authName,
41 query: options.query, 29 query: options.query,
42 statusCodeExpected: options.statusCodeExpected || HttpStatusCode.FOUND_302 30 expectedStatus: options.expectedStatus || HttpStatusCode.FOUND_302
43 }) 31 })
44 32
45 if (res.status !== HttpStatusCode.FOUND_302) return 33 if (res.status !== HttpStatusCode.FOUND_302) return
@@ -47,18 +35,17 @@ async function loginExternal (options: {
47 const location = res.header.location 35 const location = res.header.location
48 const { externalAuthToken } = decodeQueryString(location) 36 const { externalAuthToken } = decodeQueryString(location)
49 37
50 const resLogin = await loginUsingExternalToken( 38 const resLogin = await options.server.login.loginUsingExternalToken({
51 options.server, 39 username: options.username,
52 options.username, 40 externalAuthToken: externalAuthToken as string,
53 externalAuthToken as string, 41 expectedStatus: options.expectedStatusStep2
54 options.statusCodeExpectedStep2 42 })
55 )
56 43
57 return resLogin.body 44 return resLogin.body
58} 45}
59 46
60describe('Test external auth plugins', function () { 47describe('Test external auth plugins', function () {
61 let server: ServerInfo 48 let server: PeerTubeServer
62 49
63 let cyanAccessToken: string 50 let cyanAccessToken: string
64 let cyanRefreshToken: string 51 let cyanRefreshToken: string
@@ -71,22 +58,16 @@ describe('Test external auth plugins', function () {
71 before(async function () { 58 before(async function () {
72 this.timeout(30000) 59 this.timeout(30000)
73 60
74 server = await flushAndRunServer(1) 61 server = await createSingleServer(1)
75 await setAccessTokensToServers([ server ]) 62 await setAccessTokensToServers([ server ])
76 63
77 for (const suffix of [ 'one', 'two', 'three' ]) { 64 for (const suffix of [ 'one', 'two', 'three' ]) {
78 await installPlugin({ 65 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-external-auth-' + suffix) })
79 url: server.url,
80 accessToken: server.accessToken,
81 path: getPluginTestPath('-external-auth-' + suffix)
82 })
83 } 66 }
84 }) 67 })
85 68
86 it('Should display the correct configuration', async function () { 69 it('Should display the correct configuration', async function () {
87 const res = await getConfig(server.url) 70 const config = await server.config.getConfig()
88
89 const config: ServerConfig = res.body
90 71
91 const auths = config.plugin.registeredExternalAuths 72 const auths = config.plugin.registeredExternalAuths
92 expect(auths).to.have.lengthOf(8) 73 expect(auths).to.have.lengthOf(8)
@@ -98,15 +79,14 @@ describe('Test external auth plugins', function () {
98 }) 79 })
99 80
100 it('Should redirect for a Cyan login', async function () { 81 it('Should redirect for a Cyan login', async function () {
101 const res = await getExternalAuth({ 82 const res = await server.plugins.getExternalAuth({
102 url: server.url,
103 npmName: 'test-external-auth-one', 83 npmName: 'test-external-auth-one',
104 npmVersion: '0.0.1', 84 npmVersion: '0.0.1',
105 authName: 'external-auth-1', 85 authName: 'external-auth-1',
106 query: { 86 query: {
107 username: 'cyan' 87 username: 'cyan'
108 }, 88 },
109 statusCodeExpected: HttpStatusCode.FOUND_302 89 expectedStatus: HttpStatusCode.FOUND_302
110 }) 90 })
111 91
112 const location = res.header.location 92 const location = res.header.location
@@ -121,13 +101,17 @@ describe('Test external auth plugins', function () {
121 }) 101 })
122 102
123 it('Should reject auto external login with a missing or invalid token', async function () { 103 it('Should reject auto external login with a missing or invalid token', async function () {
124 await loginUsingExternalToken(server, 'cyan', '', HttpStatusCode.BAD_REQUEST_400) 104 const command = server.login
125 await loginUsingExternalToken(server, 'cyan', 'blabla', HttpStatusCode.BAD_REQUEST_400) 105
106 await command.loginUsingExternalToken({ username: 'cyan', externalAuthToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
107 await command.loginUsingExternalToken({ username: 'cyan', externalAuthToken: 'blabla', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
126 }) 108 })
127 109
128 it('Should reject auto external login with a missing or invalid username', async function () { 110 it('Should reject auto external login with a missing or invalid username', async function () {
129 await loginUsingExternalToken(server, '', externalAuthToken, HttpStatusCode.BAD_REQUEST_400) 111 const command = server.login
130 await loginUsingExternalToken(server, '', externalAuthToken, HttpStatusCode.BAD_REQUEST_400) 112
113 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
114 await command.loginUsingExternalToken({ username: '', externalAuthToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
131 }) 115 })
132 116
133 it('Should reject auto external login with an expired token', async function () { 117 it('Should reject auto external login with an expired token', async function () {
@@ -135,9 +119,13 @@ describe('Test external auth plugins', function () {
135 119
136 await wait(5000) 120 await wait(5000)
137 121
138 await loginUsingExternalToken(server, 'cyan', externalAuthToken, HttpStatusCode.BAD_REQUEST_400) 122 await server.login.loginUsingExternalToken({
123 username: 'cyan',
124 externalAuthToken,
125 expectedStatus: HttpStatusCode.BAD_REQUEST_400
126 })
139 127
140 await waitUntilLog(server, 'expired external auth token', 2) 128 await server.servers.waitUntilLog('expired external auth token', 2)
141 }) 129 })
142 130
143 it('Should auto login Cyan, create the user and use the token', async function () { 131 it('Should auto login Cyan, create the user and use the token', async function () {
@@ -157,9 +145,7 @@ describe('Test external auth plugins', function () {
157 } 145 }
158 146
159 { 147 {
160 const res = await getMyUserInformation(server.url, cyanAccessToken) 148 const body = await server.users.getMyInfo({ token: cyanAccessToken })
161
162 const body: User = res.body
163 expect(body.username).to.equal('cyan') 149 expect(body.username).to.equal('cyan')
164 expect(body.account.displayName).to.equal('cyan') 150 expect(body.account.displayName).to.equal('cyan')
165 expect(body.email).to.equal('cyan@example.com') 151 expect(body.email).to.equal('cyan@example.com')
@@ -181,9 +167,7 @@ describe('Test external auth plugins', function () {
181 } 167 }
182 168
183 { 169 {
184 const res = await getMyUserInformation(server.url, kefkaAccessToken) 170 const body = await server.users.getMyInfo({ token: kefkaAccessToken })
185
186 const body: User = res.body
187 expect(body.username).to.equal('kefka') 171 expect(body.username).to.equal('kefka')
188 expect(body.account.displayName).to.equal('Kefka Palazzo') 172 expect(body.account.displayName).to.equal('Kefka Palazzo')
189 expect(body.email).to.equal('kefka@example.com') 173 expect(body.email).to.equal('kefka@example.com')
@@ -193,43 +177,39 @@ describe('Test external auth plugins', function () {
193 177
194 it('Should refresh Cyan token, but not Kefka token', async function () { 178 it('Should refresh Cyan token, but not Kefka token', async function () {
195 { 179 {
196 const resRefresh = await refreshToken(server, cyanRefreshToken) 180 const resRefresh = await server.login.refreshToken({ refreshToken: cyanRefreshToken })
197 cyanAccessToken = resRefresh.body.access_token 181 cyanAccessToken = resRefresh.body.access_token
198 cyanRefreshToken = resRefresh.body.refresh_token 182 cyanRefreshToken = resRefresh.body.refresh_token
199 183
200 const res = await getMyUserInformation(server.url, cyanAccessToken) 184 const body = await server.users.getMyInfo({ token: cyanAccessToken })
201 const user: User = res.body 185 expect(body.username).to.equal('cyan')
202 expect(user.username).to.equal('cyan')
203 } 186 }
204 187
205 { 188 {
206 await refreshToken(server, kefkaRefreshToken, HttpStatusCode.BAD_REQUEST_400) 189 await server.login.refreshToken({ refreshToken: kefkaRefreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
207 } 190 }
208 }) 191 })
209 192
210 it('Should update Cyan profile', async function () { 193 it('Should update Cyan profile', async function () {
211 await updateMyUser({ 194 await server.users.updateMe({
212 url: server.url, 195 token: cyanAccessToken,
213 accessToken: cyanAccessToken,
214 displayName: 'Cyan Garamonde', 196 displayName: 'Cyan Garamonde',
215 description: 'Retainer to the king of Doma' 197 description: 'Retainer to the king of Doma'
216 }) 198 })
217 199
218 const res = await getMyUserInformation(server.url, cyanAccessToken) 200 const body = await server.users.getMyInfo({ token: cyanAccessToken })
219
220 const body: User = res.body
221 expect(body.account.displayName).to.equal('Cyan Garamonde') 201 expect(body.account.displayName).to.equal('Cyan Garamonde')
222 expect(body.account.description).to.equal('Retainer to the king of Doma') 202 expect(body.account.description).to.equal('Retainer to the king of Doma')
223 }) 203 })
224 204
225 it('Should logout Cyan', async function () { 205 it('Should logout Cyan', async function () {
226 await logout(server.url, cyanAccessToken) 206 await server.login.logout({ token: cyanAccessToken })
227 }) 207 })
228 208
229 it('Should have logged out Cyan', async function () { 209 it('Should have logged out Cyan', async function () {
230 await waitUntilLog(server, 'On logout cyan') 210 await server.servers.waitUntilLog('On logout cyan')
231 211
232 await getMyUserInformation(server.url, cyanAccessToken, HttpStatusCode.UNAUTHORIZED_401) 212 await server.users.getMyInfo({ token: cyanAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
233 }) 213 })
234 214
235 it('Should login Cyan and keep the old existing profile', async function () { 215 it('Should login Cyan and keep the old existing profile', async function () {
@@ -247,9 +227,7 @@ describe('Test external auth plugins', function () {
247 cyanAccessToken = res.access_token 227 cyanAccessToken = res.access_token
248 } 228 }
249 229
250 const res = await getMyUserInformation(server.url, cyanAccessToken) 230 const body = await server.users.getMyInfo({ token: cyanAccessToken })
251
252 const body: User = res.body
253 expect(body.username).to.equal('cyan') 231 expect(body.username).to.equal('cyan')
254 expect(body.account.displayName).to.equal('Cyan Garamonde') 232 expect(body.account.displayName).to.equal('Cyan Garamonde')
255 expect(body.account.description).to.equal('Retainer to the king of Doma') 233 expect(body.account.description).to.equal('Retainer to the king of Doma')
@@ -257,12 +235,11 @@ describe('Test external auth plugins', function () {
257 }) 235 })
258 236
259 it('Should not update an external auth email', async function () { 237 it('Should not update an external auth email', async function () {
260 await updateMyUser({ 238 await server.users.updateMe({
261 url: server.url, 239 token: cyanAccessToken,
262 accessToken: cyanAccessToken,
263 email: 'toto@example.com', 240 email: 'toto@example.com',
264 currentPassword: 'toto', 241 currentPassword: 'toto',
265 statusCodeExpected: HttpStatusCode.BAD_REQUEST_400 242 expectedStatus: HttpStatusCode.BAD_REQUEST_400
266 }) 243 })
267 }) 244 })
268 245
@@ -271,18 +248,16 @@ describe('Test external auth plugins', function () {
271 248
272 await wait(5000) 249 await wait(5000)
273 250
274 await getMyUserInformation(server.url, kefkaAccessToken, HttpStatusCode.UNAUTHORIZED_401) 251 await server.users.getMyInfo({ token: kefkaAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
275 }) 252 })
276 253
277 it('Should unregister external-auth-2 and do not login existing Kefka', async function () { 254 it('Should unregister external-auth-2 and do not login existing Kefka', async function () {
278 await updatePluginSettings({ 255 await server.plugins.updateSettings({
279 url: server.url,
280 accessToken: server.accessToken,
281 npmName: 'peertube-plugin-test-external-auth-one', 256 npmName: 'peertube-plugin-test-external-auth-one',
282 settings: { disableKefka: true } 257 settings: { disableKefka: true }
283 }) 258 })
284 259
285 await userLogin(server, { username: 'kefka', password: 'fake' }, HttpStatusCode.BAD_REQUEST_400) 260 await server.login.login({ user: { username: 'kefka', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
286 261
287 await loginExternal({ 262 await loginExternal({
288 server, 263 server,
@@ -292,14 +267,12 @@ describe('Test external auth plugins', function () {
292 username: 'kefka' 267 username: 'kefka'
293 }, 268 },
294 username: 'kefka', 269 username: 'kefka',
295 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 270 expectedStatus: HttpStatusCode.NOT_FOUND_404
296 }) 271 })
297 }) 272 })
298 273
299 it('Should have disabled this auth', async function () { 274 it('Should have disabled this auth', async function () {
300 const res = await getConfig(server.url) 275 const config = await server.config.getConfig()
301
302 const config: ServerConfig = res.body
303 276
304 const auths = config.plugin.registeredExternalAuths 277 const auths = config.plugin.registeredExternalAuths
305 expect(auths).to.have.lengthOf(7) 278 expect(auths).to.have.lengthOf(7)
@@ -309,11 +282,7 @@ describe('Test external auth plugins', function () {
309 }) 282 })
310 283
311 it('Should uninstall the plugin one and do not login Cyan', async function () { 284 it('Should uninstall the plugin one and do not login Cyan', async function () {
312 await uninstallPlugin({ 285 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-external-auth-one' })
313 url: server.url,
314 accessToken: server.accessToken,
315 npmName: 'peertube-plugin-test-external-auth-one'
316 })
317 286
318 await loginExternal({ 287 await loginExternal({
319 server, 288 server,
@@ -323,12 +292,12 @@ describe('Test external auth plugins', function () {
323 username: 'cyan' 292 username: 'cyan'
324 }, 293 },
325 username: 'cyan', 294 username: 'cyan',
326 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 295 expectedStatus: HttpStatusCode.NOT_FOUND_404
327 }) 296 })
328 297
329 await userLogin(server, { username: 'cyan', password: null }, HttpStatusCode.BAD_REQUEST_400) 298 await server.login.login({ user: { username: 'cyan', password: null }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
330 await userLogin(server, { username: 'cyan', password: '' }, HttpStatusCode.BAD_REQUEST_400) 299 await server.login.login({ user: { username: 'cyan', password: '' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
331 await userLogin(server, { username: 'cyan', password: 'fake' }, HttpStatusCode.BAD_REQUEST_400) 300 await server.login.login({ user: { username: 'cyan', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
332 }) 301 })
333 302
334 it('Should not login kefka with another plugin', async function () { 303 it('Should not login kefka with another plugin', async function () {
@@ -337,7 +306,7 @@ describe('Test external auth plugins', function () {
337 npmName: 'test-external-auth-two', 306 npmName: 'test-external-auth-two',
338 authName: 'external-auth-4', 307 authName: 'external-auth-4',
339 username: 'kefka2', 308 username: 'kefka2',
340 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400 309 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
341 }) 310 })
342 311
343 await loginExternal({ 312 await loginExternal({
@@ -345,31 +314,24 @@ describe('Test external auth plugins', function () {
345 npmName: 'test-external-auth-two', 314 npmName: 'test-external-auth-two',
346 authName: 'external-auth-4', 315 authName: 'external-auth-4',
347 username: 'kefka', 316 username: 'kefka',
348 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400 317 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
349 }) 318 })
350 }) 319 })
351 320
352 it('Should not login an existing user', async function () { 321 it('Should not login an existing user', async function () {
353 await createUser({ 322 await server.users.create({ username: 'existing_user', password: 'super_password' })
354 url: server.url,
355 accessToken: server.accessToken,
356 username: 'existing_user',
357 password: 'super_password'
358 })
359 323
360 await loginExternal({ 324 await loginExternal({
361 server, 325 server,
362 npmName: 'test-external-auth-two', 326 npmName: 'test-external-auth-two',
363 authName: 'external-auth-6', 327 authName: 'external-auth-6',
364 username: 'existing_user', 328 username: 'existing_user',
365 statusCodeExpectedStep2: HttpStatusCode.BAD_REQUEST_400 329 expectedStatusStep2: HttpStatusCode.BAD_REQUEST_400
366 }) 330 })
367 }) 331 })
368 332
369 it('Should display the correct configuration', async function () { 333 it('Should display the correct configuration', async function () {
370 const res = await getConfig(server.url) 334 const config = await server.config.getConfig()
371
372 const config: ServerConfig = res.body
373 335
374 const auths = config.plugin.registeredExternalAuths 336 const auths = config.plugin.registeredExternalAuths
375 expect(auths).to.have.lengthOf(6) 337 expect(auths).to.have.lengthOf(6)
@@ -390,9 +352,8 @@ describe('Test external auth plugins', function () {
390 username: 'cid' 352 username: 'cid'
391 }) 353 })
392 354
393 const resLogout = await logout(server.url, resLogin.access_token) 355 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
394 356 expect(redirectUrl).to.equal('https://example.com/redirectUrl')
395 expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl')
396 }) 357 })
397 358
398 it('Should call the plugin\'s onLogout method with the request', async function () { 359 it('Should call the plugin\'s onLogout method with the request', async function () {
@@ -403,8 +364,7 @@ describe('Test external auth plugins', function () {
403 username: 'cid' 364 username: 'cid'
404 }) 365 })
405 366
406 const resLogout = await logout(server.url, resLogin.access_token) 367 const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
407 368 expect(redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
408 expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
409 }) 369 })
410}) 370})
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index 644b41dea..02915f08c 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -2,192 +2,152 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { advancedVideoChannelSearch } from '@shared/extra-utils/search/video-channels'
6import { ServerConfig } from '@shared/models'
7import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
8import { 5import {
9 addVideoCommentReply, 6 cleanupTests,
10 addVideoCommentThread, 7 createMultipleServers,
11 advancedVideoPlaylistSearch,
12 advancedVideosSearch,
13 createLive,
14 createVideoPlaylist,
15 doubleFollow, 8 doubleFollow,
16 getAccountVideos, 9 FIXTURE_URLS,
17 getConfig,
18 getMyVideos,
19 getPluginTestPath,
20 getVideo,
21 getVideoChannelVideos,
22 getVideoCommentThreads,
23 getVideoPlaylist,
24 getVideosList,
25 getVideosListPagination,
26 getVideoThreadComments,
27 getVideoWithToken,
28 installPlugin,
29 makeRawRequest, 10 makeRawRequest,
30 registerUser, 11 PeerTubeServer,
12 PluginsCommand,
31 setAccessTokensToServers, 13 setAccessTokensToServers,
32 setDefaultVideoChannel, 14 setDefaultVideoChannel,
33 updateCustomSubConfig,
34 updateVideo,
35 uploadVideo,
36 uploadVideoAndGetId,
37 waitJobs 15 waitJobs
38} from '../../../shared/extra-utils' 16} from '@shared/extra-utils'
39import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' 17import { HttpStatusCode, VideoDetails, VideoImportState, VideoPlaylist, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
40import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports'
41import {
42 VideoCommentThreadTree,
43 VideoDetails,
44 VideoImport,
45 VideoImportState,
46 VideoPlaylist,
47 VideoPlaylistPrivacy,
48 VideoPrivacy
49} from '../../../shared/models/videos'
50 18
51const expect = chai.expect 19const expect = chai.expect
52 20
53describe('Test plugin filter hooks', function () { 21describe('Test plugin filter hooks', function () {
54 let servers: ServerInfo[] 22 let servers: PeerTubeServer[]
55 let videoUUID: string 23 let videoUUID: string
56 let threadId: number 24 let threadId: number
57 25
58 before(async function () { 26 before(async function () {
59 this.timeout(60000) 27 this.timeout(60000)
60 28
61 servers = await flushAndRunMultipleServers(2) 29 servers = await createMultipleServers(2)
62 await setAccessTokensToServers(servers) 30 await setAccessTokensToServers(servers)
63 await setDefaultVideoChannel(servers) 31 await setDefaultVideoChannel(servers)
64 await doubleFollow(servers[0], servers[1]) 32 await doubleFollow(servers[0], servers[1])
65 33
66 await installPlugin({ 34 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath() })
67 url: servers[0].url, 35 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath('-filter-translations') })
68 accessToken: servers[0].accessToken,
69 path: getPluginTestPath()
70 })
71
72 await installPlugin({
73 url: servers[0].url,
74 accessToken: servers[0].accessToken,
75 path: getPluginTestPath('-filter-translations')
76 })
77 36
78 for (let i = 0; i < 10; i++) { 37 for (let i = 0; i < 10; i++) {
79 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'default video ' + i }) 38 await servers[0].videos.upload({ attributes: { name: 'default video ' + i } })
80 } 39 }
81 40
82 const res = await getVideosList(servers[0].url) 41 const { data } = await servers[0].videos.list()
83 videoUUID = res.body.data[0].uuid 42 videoUUID = data[0].uuid
84 43
85 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 44 await servers[0].config.updateCustomSubConfig({
86 live: { enabled: true }, 45 newConfig: {
87 signup: { enabled: true }, 46 live: { enabled: true },
88 import: { 47 signup: { enabled: true },
89 videos: { 48 import: {
90 http: { enabled: true }, 49 videos: {
91 torrent: { enabled: true } 50 http: { enabled: true },
51 torrent: { enabled: true }
52 }
92 } 53 }
93 } 54 }
94 }) 55 })
95 }) 56 })
96 57
97 it('Should run filter:api.videos.list.params', async function () { 58 it('Should run filter:api.videos.list.params', async function () {
98 const res = await getVideosListPagination(servers[0].url, 0, 2) 59 const { data } = await servers[0].videos.list({ start: 0, count: 2 })
99 60
100 // 2 plugins do +1 to the count parameter 61 // 2 plugins do +1 to the count parameter
101 expect(res.body.data).to.have.lengthOf(4) 62 expect(data).to.have.lengthOf(4)
102 }) 63 })
103 64
104 it('Should run filter:api.videos.list.result', async function () { 65 it('Should run filter:api.videos.list.result', async function () {
105 const res = await getVideosListPagination(servers[0].url, 0, 0) 66 const { total } = await servers[0].videos.list({ start: 0, count: 0 })
106 67
107 // Plugin do +1 to the total result 68 // Plugin do +1 to the total result
108 expect(res.body.total).to.equal(11) 69 expect(total).to.equal(11)
109 }) 70 })
110 71
111 it('Should run filter:api.accounts.videos.list.params', async function () { 72 it('Should run filter:api.accounts.videos.list.params', async function () {
112 const res = await getAccountVideos(servers[0].url, servers[0].accessToken, 'root', 0, 2) 73 const { data } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
113 74
114 // 1 plugin do +1 to the count parameter 75 // 1 plugin do +1 to the count parameter
115 expect(res.body.data).to.have.lengthOf(3) 76 expect(data).to.have.lengthOf(3)
116 }) 77 })
117 78
118 it('Should run filter:api.accounts.videos.list.result', async function () { 79 it('Should run filter:api.accounts.videos.list.result', async function () {
119 const res = await getAccountVideos(servers[0].url, servers[0].accessToken, 'root', 0, 2) 80 const { total } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
120 81
121 // Plugin do +2 to the total result 82 // Plugin do +2 to the total result
122 expect(res.body.total).to.equal(12) 83 expect(total).to.equal(12)
123 }) 84 })
124 85
125 it('Should run filter:api.video-channels.videos.list.params', async function () { 86 it('Should run filter:api.video-channels.videos.list.params', async function () {
126 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'root_channel', 0, 2) 87 const { data } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
127 88
128 // 1 plugin do +3 to the count parameter 89 // 1 plugin do +3 to the count parameter
129 expect(res.body.data).to.have.lengthOf(5) 90 expect(data).to.have.lengthOf(5)
130 }) 91 })
131 92
132 it('Should run filter:api.video-channels.videos.list.result', async function () { 93 it('Should run filter:api.video-channels.videos.list.result', async function () {
133 const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'root_channel', 0, 2) 94 const { total } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
134 95
135 // Plugin do +3 to the total result 96 // Plugin do +3 to the total result
136 expect(res.body.total).to.equal(13) 97 expect(total).to.equal(13)
137 }) 98 })
138 99
139 it('Should run filter:api.user.me.videos.list.params', async function () { 100 it('Should run filter:api.user.me.videos.list.params', async function () {
140 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 2) 101 const { data } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
141 102
142 // 1 plugin do +4 to the count parameter 103 // 1 plugin do +4 to the count parameter
143 expect(res.body.data).to.have.lengthOf(6) 104 expect(data).to.have.lengthOf(6)
144 }) 105 })
145 106
146 it('Should run filter:api.user.me.videos.list.result', async function () { 107 it('Should run filter:api.user.me.videos.list.result', async function () {
147 const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 2) 108 const { total } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
148 109
149 // Plugin do +4 to the total result 110 // Plugin do +4 to the total result
150 expect(res.body.total).to.equal(14) 111 expect(total).to.equal(14)
151 }) 112 })
152 113
153 it('Should run filter:api.video.get.result', async function () { 114 it('Should run filter:api.video.get.result', async function () {
154 const res = await getVideo(servers[0].url, videoUUID) 115 const video = await servers[0].videos.get({ id: videoUUID })
155 116 expect(video.name).to.contain('<3')
156 expect(res.body.name).to.contain('<3')
157 }) 117 })
158 118
159 it('Should run filter:api.video.upload.accept.result', async function () { 119 it('Should run filter:api.video.upload.accept.result', async function () {
160 await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video with bad word' }, HttpStatusCode.FORBIDDEN_403) 120 await servers[0].videos.upload({ attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
161 }) 121 })
162 122
163 it('Should run filter:api.live-video.create.accept.result', async function () { 123 it('Should run filter:api.live-video.create.accept.result', async function () {
164 const attributes = { 124 const attributes = {
165 name: 'video with bad word', 125 name: 'video with bad word',
166 privacy: VideoPrivacy.PUBLIC, 126 privacy: VideoPrivacy.PUBLIC,
167 channelId: servers[0].videoChannel.id 127 channelId: servers[0].store.channel.id
168 } 128 }
169 129
170 await createLive(servers[0].url, servers[0].accessToken, attributes, HttpStatusCode.FORBIDDEN_403) 130 await servers[0].live.create({ fields: attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
171 }) 131 })
172 132
173 it('Should run filter:api.video.pre-import-url.accept.result', async function () { 133 it('Should run filter:api.video.pre-import-url.accept.result', async function () {
174 const baseAttributes = { 134 const attributes = {
175 name: 'normal title', 135 name: 'normal title',
176 privacy: VideoPrivacy.PUBLIC, 136 privacy: VideoPrivacy.PUBLIC,
177 channelId: servers[0].videoChannel.id, 137 channelId: servers[0].store.channel.id,
178 targetUrl: getGoodVideoUrl() + 'bad' 138 targetUrl: FIXTURE_URLS.goodVideo + 'bad'
179 } 139 }
180 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, HttpStatusCode.FORBIDDEN_403) 140 await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
181 }) 141 })
182 142
183 it('Should run filter:api.video.pre-import-torrent.accept.result', async function () { 143 it('Should run filter:api.video.pre-import-torrent.accept.result', async function () {
184 const baseAttributes = { 144 const attributes = {
185 name: 'bad torrent', 145 name: 'bad torrent',
186 privacy: VideoPrivacy.PUBLIC, 146 privacy: VideoPrivacy.PUBLIC,
187 channelId: servers[0].videoChannel.id, 147 channelId: servers[0].store.channel.id,
188 torrentfile: 'video-720p.torrent' as any 148 torrentfile: 'video-720p.torrent' as any
189 } 149 }
190 await importVideo(servers[0].url, servers[0].accessToken, baseAttributes, HttpStatusCode.FORBIDDEN_403) 150 await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
191 }) 151 })
192 152
193 it('Should run filter:api.video.post-import-url.accept.result', async function () { 153 it('Should run filter:api.video.post-import-url.accept.result', async function () {
@@ -196,21 +156,21 @@ describe('Test plugin filter hooks', function () {
196 let videoImportId: number 156 let videoImportId: number
197 157
198 { 158 {
199 const baseAttributes = { 159 const attributes = {
200 name: 'title with bad word', 160 name: 'title with bad word',
201 privacy: VideoPrivacy.PUBLIC, 161 privacy: VideoPrivacy.PUBLIC,
202 channelId: servers[0].videoChannel.id, 162 channelId: servers[0].store.channel.id,
203 targetUrl: getGoodVideoUrl() 163 targetUrl: FIXTURE_URLS.goodVideo
204 } 164 }
205 const res = await importVideo(servers[0].url, servers[0].accessToken, baseAttributes) 165 const body = await servers[0].imports.importVideo({ attributes })
206 videoImportId = res.body.id 166 videoImportId = body.id
207 } 167 }
208 168
209 await waitJobs(servers) 169 await waitJobs(servers)
210 170
211 { 171 {
212 const res = await getMyVideoImports(servers[0].url, servers[0].accessToken) 172 const body = await servers[0].imports.getMyVideoImports()
213 const videoImports = res.body.data as VideoImport[] 173 const videoImports = body.data
214 174
215 const videoImport = videoImports.find(i => i.id === videoImportId) 175 const videoImport = videoImports.find(i => i.id === videoImportId)
216 176
@@ -225,21 +185,20 @@ describe('Test plugin filter hooks', function () {
225 let videoImportId: number 185 let videoImportId: number
226 186
227 { 187 {
228 const baseAttributes = { 188 const attributes = {
229 name: 'title with bad word', 189 name: 'title with bad word',
230 privacy: VideoPrivacy.PUBLIC, 190 privacy: VideoPrivacy.PUBLIC,
231 channelId: servers[0].videoChannel.id, 191 channelId: servers[0].store.channel.id,
232 torrentfile: 'video-720p.torrent' as any 192 torrentfile: 'video-720p.torrent' as any
233 } 193 }
234 const res = await importVideo(servers[0].url, servers[0].accessToken, baseAttributes) 194 const body = await servers[0].imports.importVideo({ attributes })
235 videoImportId = res.body.id 195 videoImportId = body.id
236 } 196 }
237 197
238 await waitJobs(servers) 198 await waitJobs(servers)
239 199
240 { 200 {
241 const res = await getMyVideoImports(servers[0].url, servers[0].accessToken) 201 const { data: videoImports } = await servers[0].imports.getMyVideoImports()
242 const videoImports = res.body.data as VideoImport[]
243 202
244 const videoImport = videoImports.find(i => i.id === videoImportId) 203 const videoImport = videoImports.find(i => i.id === videoImportId)
245 204
@@ -249,60 +208,71 @@ describe('Test plugin filter hooks', function () {
249 }) 208 })
250 209
251 it('Should run filter:api.video-thread.create.accept.result', async function () { 210 it('Should run filter:api.video-thread.create.accept.result', async function () {
252 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'comment with bad word', HttpStatusCode.FORBIDDEN_403) 211 await servers[0].comments.createThread({
212 videoId: videoUUID,
213 text: 'comment with bad word',
214 expectedStatus: HttpStatusCode.FORBIDDEN_403
215 })
253 }) 216 })
254 217
255 it('Should run filter:api.video-comment-reply.create.accept.result', async function () { 218 it('Should run filter:api.video-comment-reply.create.accept.result', async function () {
256 const res = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID, 'thread') 219 const created = await servers[0].comments.createThread({ videoId: videoUUID, text: 'thread' })
257 threadId = res.body.comment.id 220 threadId = created.id
258 221
259 await addVideoCommentReply( 222 await servers[0].comments.addReply({
260 servers[0].url, 223 videoId: videoUUID,
261 servers[0].accessToken, 224 toCommentId: threadId,
262 videoUUID, 225 text: 'comment with bad word',
263 threadId, 226 expectedStatus: HttpStatusCode.FORBIDDEN_403
264 'comment with bad word', 227 })
265 HttpStatusCode.FORBIDDEN_403 228 await servers[0].comments.addReply({
266 ) 229 videoId: videoUUID,
267 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID, threadId, 'comment with good word', HttpStatusCode.OK_200) 230 toCommentId: threadId,
231 text: 'comment with good word',
232 expectedStatus: HttpStatusCode.OK_200
233 })
268 }) 234 })
269 235
270 it('Should run filter:api.video-threads.list.params', async function () { 236 it('Should run filter:api.video-threads.list.params', async function () {
271 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 0) 237 const { data } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
272 238
273 // our plugin do +1 to the count parameter 239 // our plugin do +1 to the count parameter
274 expect(res.body.data).to.have.lengthOf(1) 240 expect(data).to.have.lengthOf(1)
275 }) 241 })
276 242
277 it('Should run filter:api.video-threads.list.result', async function () { 243 it('Should run filter:api.video-threads.list.result', async function () {
278 const res = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 0) 244 const { total } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
279 245
280 // Plugin do +1 to the total result 246 // Plugin do +1 to the total result
281 expect(res.body.total).to.equal(2) 247 expect(total).to.equal(2)
282 }) 248 })
283 249
284 it('Should run filter:api.video-thread-comments.list.params') 250 it('Should run filter:api.video-thread-comments.list.params')
285 251
286 it('Should run filter:api.video-thread-comments.list.result', async function () { 252 it('Should run filter:api.video-thread-comments.list.result', async function () {
287 const res = await getVideoThreadComments(servers[0].url, videoUUID, threadId) 253 const thread = await servers[0].comments.getThread({ videoId: videoUUID, threadId })
288 254
289 const thread = res.body as VideoCommentThreadTree
290 expect(thread.comment.text.endsWith(' <3')).to.be.true 255 expect(thread.comment.text.endsWith(' <3')).to.be.true
291 }) 256 })
292 257
293 describe('Should run filter:video.auto-blacklist.result', function () { 258 it('Should run filter:api.overviews.videos.list.{params,result}', async function () {
259 await servers[0].overviews.getVideos({ page: 1 })
294 260
295 async function checkIsBlacklisted (oldRes: any, value: boolean) { 261 // 3 because we get 3 samples per page
296 const videoId = oldRes.body.video.uuid 262 await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.params', 3)
263 await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.result', 3)
264 })
297 265
298 const res = await getVideoWithToken(servers[0].url, servers[0].accessToken, videoId) 266 describe('Should run filter:video.auto-blacklist.result', function () {
299 const video: VideoDetails = res.body 267
268 async function checkIsBlacklisted (id: number | string, value: boolean) {
269 const video = await servers[0].videos.getWithToken({ id })
300 expect(video.blacklisted).to.equal(value) 270 expect(video.blacklisted).to.equal(value)
301 } 271 }
302 272
303 it('Should blacklist on upload', async function () { 273 it('Should blacklist on upload', async function () {
304 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video please blacklist me' }) 274 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video please blacklist me' } })
305 await checkIsBlacklisted(res, true) 275 await checkIsBlacklisted(uuid, true)
306 }) 276 })
307 277
308 it('Should blacklist on import', async function () { 278 it('Should blacklist on import', async function () {
@@ -310,60 +280,62 @@ describe('Test plugin filter hooks', function () {
310 280
311 const attributes = { 281 const attributes = {
312 name: 'video please blacklist me', 282 name: 'video please blacklist me',
313 targetUrl: getGoodVideoUrl(), 283 targetUrl: FIXTURE_URLS.goodVideo,
314 channelId: servers[0].videoChannel.id 284 channelId: servers[0].store.channel.id
315 } 285 }
316 const res = await importVideo(servers[0].url, servers[0].accessToken, attributes) 286 const body = await servers[0].imports.importVideo({ attributes })
317 await checkIsBlacklisted(res, true) 287 await checkIsBlacklisted(body.video.uuid, true)
318 }) 288 })
319 289
320 it('Should blacklist on update', async function () { 290 it('Should blacklist on update', async function () {
321 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video' }) 291 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' } })
322 const videoId = res.body.video.uuid 292 await checkIsBlacklisted(uuid, false)
323 await checkIsBlacklisted(res, false)
324 293
325 await updateVideo(servers[0].url, servers[0].accessToken, videoId, { name: 'please blacklist me' }) 294 await servers[0].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
326 await checkIsBlacklisted(res, true) 295 await checkIsBlacklisted(uuid, true)
327 }) 296 })
328 297
329 it('Should blacklist on remote upload', async function () { 298 it('Should blacklist on remote upload', async function () {
330 this.timeout(120000) 299 this.timeout(120000)
331 300
332 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'remote please blacklist me' }) 301 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'remote please blacklist me' } })
333 await waitJobs(servers) 302 await waitJobs(servers)
334 303
335 await checkIsBlacklisted(res, true) 304 await checkIsBlacklisted(uuid, true)
336 }) 305 })
337 306
338 it('Should blacklist on remote update', async function () { 307 it('Should blacklist on remote update', async function () {
339 this.timeout(120000) 308 this.timeout(120000)
340 309
341 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video' }) 310 const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video' } })
342 await waitJobs(servers) 311 await waitJobs(servers)
343 312
344 const videoId = res.body.video.uuid 313 await checkIsBlacklisted(uuid, false)
345 await checkIsBlacklisted(res, false)
346 314
347 await updateVideo(servers[1].url, servers[1].accessToken, videoId, { name: 'please blacklist me' }) 315 await servers[1].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
348 await waitJobs(servers) 316 await waitJobs(servers)
349 317
350 await checkIsBlacklisted(res, true) 318 await checkIsBlacklisted(uuid, true)
351 }) 319 })
352 }) 320 })
353 321
354 describe('Should run filter:api.user.signup.allowed.result', function () { 322 describe('Should run filter:api.user.signup.allowed.result', function () {
355 323
356 it('Should run on config endpoint', async function () { 324 it('Should run on config endpoint', async function () {
357 const res = await getConfig(servers[0].url) 325 const body = await servers[0].config.getConfig()
358 expect((res.body as ServerConfig).signup.allowed).to.be.true 326 expect(body.signup.allowed).to.be.true
359 }) 327 })
360 328
361 it('Should allow a signup', async function () { 329 it('Should allow a signup', async function () {
362 await registerUser(servers[0].url, 'john', 'password') 330 await servers[0].users.register({ username: 'john', password: 'password' })
363 }) 331 })
364 332
365 it('Should not allow a signup', async function () { 333 it('Should not allow a signup', async function () {
366 const res = await registerUser(servers[0].url, 'jma', 'password', HttpStatusCode.FORBIDDEN_403) 334 const res = await servers[0].users.register({
335 username: 'jma',
336 password: 'password',
337 expectedStatus: HttpStatusCode.FORBIDDEN_403
338 })
367 339
368 expect(res.body.error).to.equal('No jma') 340 expect(res.body.error).to.equal('No jma')
369 }) 341 })
@@ -375,13 +347,15 @@ describe('Test plugin filter hooks', function () {
375 before(async function () { 347 before(async function () {
376 this.timeout(120000) 348 this.timeout(120000)
377 349
378 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 350 await servers[0].config.updateCustomSubConfig({
379 transcoding: { 351 newConfig: {
380 webtorrent: { 352 transcoding: {
381 enabled: true 353 webtorrent: {
382 }, 354 enabled: true
383 hls: { 355 },
384 enabled: true 356 hls: {
357 enabled: true
358 }
385 } 359 }
386 } 360 }
387 }) 361 })
@@ -389,15 +363,14 @@ describe('Test plugin filter hooks', function () {
389 const uuids: string[] = [] 363 const uuids: string[] = []
390 364
391 for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) { 365 for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) {
392 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid 366 const uuid = (await servers[0].videos.quickUpload({ name: name })).uuid
393 uuids.push(uuid) 367 uuids.push(uuid)
394 } 368 }
395 369
396 await waitJobs(servers) 370 await waitJobs(servers)
397 371
398 for (const uuid of uuids) { 372 for (const uuid of uuids) {
399 const res = await getVideo(servers[0].url, uuid) 373 downloadVideos.push(await servers[0].videos.get({ id: uuid }))
400 downloadVideos.push(res.body)
401 } 374 }
402 }) 375 })
403 376
@@ -437,25 +410,26 @@ describe('Test plugin filter hooks', function () {
437 before(async function () { 410 before(async function () {
438 this.timeout(60000) 411 this.timeout(60000)
439 412
440 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 413 await servers[0].config.updateCustomSubConfig({
441 transcoding: { 414 newConfig: {
442 enabled: false 415 transcoding: {
416 enabled: false
417 }
443 } 418 }
444 }) 419 })
445 420
446 for (const name of [ 'bad embed', 'good embed' ]) { 421 for (const name of [ 'bad embed', 'good embed' ]) {
447 { 422 {
448 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: name })).uuid 423 const uuid = (await servers[0].videos.quickUpload({ name: name })).uuid
449 const res = await getVideo(servers[0].url, uuid) 424 embedVideos.push(await servers[0].videos.get({ id: uuid }))
450 embedVideos.push(res.body)
451 } 425 }
452 426
453 { 427 {
454 const playlistAttrs = { displayName: name, videoChannelId: servers[0].videoChannel.id, privacy: VideoPlaylistPrivacy.PUBLIC } 428 const attributes = { displayName: name, videoChannelId: servers[0].store.channel.id, privacy: VideoPlaylistPrivacy.PUBLIC }
455 const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) 429 const { id } = await servers[0].playlists.create({ attributes })
456 430
457 const resPlaylist = await getVideoPlaylist(servers[0].url, res.body.videoPlaylist.id) 431 const playlist = await servers[0].playlists.get({ playlistId: id })
458 embedPlaylists.push(resPlaylist.body) 432 embedPlaylists.push(playlist)
459 } 433 }
460 } 434 }
461 }) 435 })
@@ -474,78 +448,92 @@ describe('Test plugin filter hooks', function () {
474 describe('Search filters', function () { 448 describe('Search filters', function () {
475 449
476 before(async function () { 450 before(async function () {
477 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, { 451 await servers[0].config.updateCustomSubConfig({
478 search: { 452 newConfig: {
479 searchIndex: { 453 search: {
480 enabled: true, 454 searchIndex: {
481 isDefaultSearch: false, 455 enabled: true,
482 disableLocalSearch: false 456 isDefaultSearch: false,
457 disableLocalSearch: false
458 }
483 } 459 }
484 } 460 }
485 }) 461 })
486 }) 462 })
487 463
488 it('Should run filter:api.search.videos.local.list.{params,result}', async function () { 464 it('Should run filter:api.search.videos.local.list.{params,result}', async function () {
489 await advancedVideosSearch(servers[0].url, { 465 await servers[0].search.advancedVideoSearch({
490 search: 'Sun Quan' 466 search: {
467 search: 'Sun Quan'
468 }
491 }) 469 })
492 470
493 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) 471 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.params', 1)
494 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1) 472 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.result', 1)
495 }) 473 })
496 474
497 it('Should run filter:api.search.videos.index.list.{params,result}', async function () { 475 it('Should run filter:api.search.videos.index.list.{params,result}', async function () {
498 await advancedVideosSearch(servers[0].url, { 476 await servers[0].search.advancedVideoSearch({
499 search: 'Sun Quan', 477 search: {
500 searchTarget: 'search-index' 478 search: 'Sun Quan',
479 searchTarget: 'search-index'
480 }
501 }) 481 })
502 482
503 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.params', 1) 483 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.params', 1)
504 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.local.list.result', 1) 484 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.result', 1)
505 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.params', 1) 485 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.index.list.params', 1)
506 await waitUntilLog(servers[0], 'Run hook filter:api.search.videos.index.list.result', 1) 486 await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.index.list.result', 1)
507 }) 487 })
508 488
509 it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () { 489 it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () {
510 await advancedVideoChannelSearch(servers[0].url, { 490 await servers[0].search.advancedChannelSearch({
511 search: 'Sun Ce' 491 search: {
492 search: 'Sun Ce'
493 }
512 }) 494 })
513 495
514 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) 496 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.params', 1)
515 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1) 497 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.result', 1)
516 }) 498 })
517 499
518 it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () { 500 it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () {
519 await advancedVideoChannelSearch(servers[0].url, { 501 await servers[0].search.advancedChannelSearch({
520 search: 'Sun Ce', 502 search: {
521 searchTarget: 'search-index' 503 search: 'Sun Ce',
504 searchTarget: 'search-index'
505 }
522 }) 506 })
523 507
524 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.params', 1) 508 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.params', 1)
525 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.local.list.result', 1) 509 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.result', 1)
526 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.params', 1) 510 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.index.list.params', 1)
527 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-channels.index.list.result', 1) 511 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.index.list.result', 1)
528 }) 512 })
529 513
530 it('Should run filter:api.search.video-playlists.local.list.{params,result}', async function () { 514 it('Should run filter:api.search.video-playlists.local.list.{params,result}', async function () {
531 await advancedVideoPlaylistSearch(servers[0].url, { 515 await servers[0].search.advancedPlaylistSearch({
532 search: 'Sun Jian' 516 search: {
517 search: 'Sun Jian'
518 }
533 }) 519 })
534 520
535 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.params', 1) 521 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.params', 1)
536 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.result', 1) 522 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.result', 1)
537 }) 523 })
538 524
539 it('Should run filter:api.search.video-playlists.index.list.{params,result}', async function () { 525 it('Should run filter:api.search.video-playlists.index.list.{params,result}', async function () {
540 await advancedVideoPlaylistSearch(servers[0].url, { 526 await servers[0].search.advancedPlaylistSearch({
541 search: 'Sun Jian', 527 search: {
542 searchTarget: 'search-index' 528 search: 'Sun Jian',
529 searchTarget: 'search-index'
530 }
543 }) 531 })
544 532
545 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.params', 1) 533 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.params', 1)
546 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.local.list.result', 1) 534 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.result', 1)
547 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.index.list.params', 1) 535 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.index.list.params', 1)
548 await waitUntilLog(servers[0], 'Run hook filter:api.search.video-playlists.index.list.result', 1) 536 await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.index.list.result', 1)
549 }) 537 })
550 }) 538 })
551 539
diff --git a/server/tests/plugins/html-injection.ts b/server/tests/plugins/html-injection.ts
index 4fa8caa3a..95c0cd687 100644
--- a/server/tests/plugins/html-injection.ts
+++ b/server/tests/plugins/html-injection.ts
@@ -4,31 +4,32 @@ import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { 5import {
6 cleanupTests, 6 cleanupTests,
7 flushAndRunServer, 7 createSingleServer,
8 getPluginsCSS,
9 installPlugin,
10 makeHTMLRequest, 8 makeHTMLRequest,
11 ServerInfo, 9 PeerTubeServer,
12 setAccessTokensToServers, 10 PluginsCommand,
13 uninstallPlugin 11 setAccessTokensToServers
14} from '../../../shared/extra-utils' 12} from '../../../shared/extra-utils'
15 13
16const expect = chai.expect 14const expect = chai.expect
17 15
18describe('Test plugins HTML injection', function () { 16describe('Test plugins HTML injection', function () {
19 let server: ServerInfo = null 17 let server: PeerTubeServer = null
18 let command: PluginsCommand
20 19
21 before(async function () { 20 before(async function () {
22 this.timeout(30000) 21 this.timeout(30000)
23 22
24 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
25 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
25
26 command = server.plugins
26 }) 27 })
27 28
28 it('Should not inject global css file in HTML', async function () { 29 it('Should not inject global css file in HTML', async function () {
29 { 30 {
30 const res = await getPluginsCSS(server.url) 31 const text = await command.getCSS()
31 expect(res.text).to.be.empty 32 expect(text).to.be.empty
32 } 33 }
33 34
34 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) { 35 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) {
@@ -40,17 +41,13 @@ describe('Test plugins HTML injection', function () {
40 it('Should install a plugin and a theme', async function () { 41 it('Should install a plugin and a theme', async function () {
41 this.timeout(30000) 42 this.timeout(30000)
42 43
43 await installPlugin({ 44 await command.install({ npmName: 'peertube-plugin-hello-world' })
44 url: server.url,
45 accessToken: server.accessToken,
46 npmName: 'peertube-plugin-hello-world'
47 })
48 }) 45 })
49 46
50 it('Should have the correct global css', async function () { 47 it('Should have the correct global css', async function () {
51 { 48 {
52 const res = await getPluginsCSS(server.url) 49 const text = await command.getCSS()
53 expect(res.text).to.contain('background-color: red') 50 expect(text).to.contain('background-color: red')
54 } 51 }
55 52
56 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) { 53 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) {
@@ -60,15 +57,11 @@ describe('Test plugins HTML injection', function () {
60 }) 57 })
61 58
62 it('Should have an empty global css on uninstall', async function () { 59 it('Should have an empty global css on uninstall', async function () {
63 await uninstallPlugin({ 60 await command.uninstall({ npmName: 'peertube-plugin-hello-world' })
64 url: server.url,
65 accessToken: server.accessToken,
66 npmName: 'peertube-plugin-hello-world'
67 })
68 61
69 { 62 {
70 const res = await getPluginsCSS(server.url) 63 const text = await command.getCSS()
71 expect(res.text).to.be.empty 64 expect(text).to.be.empty
72 } 65 }
73 66
74 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) { 67 for (const path of [ '/', '/videos/embed/1', '/video-playlists/embed/1' ]) {
diff --git a/server/tests/plugins/id-and-pass-auth.ts b/server/tests/plugins/id-and-pass-auth.ts
index cbba638c2..fde0166f9 100644
--- a/server/tests/plugins/id-and-pass-auth.ts
+++ b/server/tests/plugins/id-and-pass-auth.ts
@@ -1,24 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
5import {
6 getMyUserInformation,
7 getPluginTestPath,
8 installPlugin,
9 logout,
10 setAccessTokensToServers,
11 uninstallPlugin,
12 updateMyUser,
13 userLogin,
14 wait,
15 login, refreshToken, getConfig, updatePluginSettings, getUsersList
16} from '../../../shared/extra-utils'
17import { User, UserRole, ServerConfig } from '@shared/models'
18import { expect } from 'chai' 4import { expect } from 'chai'
5import { cleanupTests, createSingleServer, PeerTubeServer, PluginsCommand, setAccessTokensToServers, wait } from '@shared/extra-utils'
6import { HttpStatusCode, UserRole } from '@shared/models'
19 7
20describe('Test id and pass auth plugins', function () { 8describe('Test id and pass auth plugins', function () {
21 let server: ServerInfo 9 let server: PeerTubeServer
22 10
23 let crashAccessToken: string 11 let crashAccessToken: string
24 let crashRefreshToken: string 12 let crashRefreshToken: string
@@ -29,22 +17,16 @@ describe('Test id and pass auth plugins', function () {
29 before(async function () { 17 before(async function () {
30 this.timeout(30000) 18 this.timeout(30000)
31 19
32 server = await flushAndRunServer(1) 20 server = await createSingleServer(1)
33 await setAccessTokensToServers([ server ]) 21 await setAccessTokensToServers([ server ])
34 22
35 for (const suffix of [ 'one', 'two', 'three' ]) { 23 for (const suffix of [ 'one', 'two', 'three' ]) {
36 await installPlugin({ 24 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-id-pass-auth-' + suffix) })
37 url: server.url,
38 accessToken: server.accessToken,
39 path: getPluginTestPath('-id-pass-auth-' + suffix)
40 })
41 } 25 }
42 }) 26 })
43 27
44 it('Should display the correct configuration', async function () { 28 it('Should display the correct configuration', async function () {
45 const res = await getConfig(server.url) 29 const config = await server.config.getConfig()
46
47 const config: ServerConfig = res.body
48 30
49 const auths = config.plugin.registeredIdAndPassAuths 31 const auths = config.plugin.registeredIdAndPassAuths
50 expect(auths).to.have.lengthOf(8) 32 expect(auths).to.have.lengthOf(8)
@@ -56,15 +38,14 @@ describe('Test id and pass auth plugins', function () {
56 }) 38 })
57 39
58 it('Should not login', async function () { 40 it('Should not login', async function () {
59 await userLogin(server, { username: 'toto', password: 'password' }, 400) 41 await server.login.login({ user: { username: 'toto', password: 'password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
60 }) 42 })
61 43
62 it('Should login Spyro, create the user and use the token', async function () { 44 it('Should login Spyro, create the user and use the token', async function () {
63 const accessToken = await userLogin(server, { username: 'spyro', password: 'spyro password' }) 45 const accessToken = await server.login.getAccessToken({ username: 'spyro', password: 'spyro password' })
64 46
65 const res = await getMyUserInformation(server.url, accessToken) 47 const body = await server.users.getMyInfo({ token: accessToken })
66 48
67 const body: User = res.body
68 expect(body.username).to.equal('spyro') 49 expect(body.username).to.equal('spyro')
69 expect(body.account.displayName).to.equal('Spyro the Dragon') 50 expect(body.account.displayName).to.equal('Spyro the Dragon')
70 expect(body.role).to.equal(UserRole.USER) 51 expect(body.role).to.equal(UserRole.USER)
@@ -72,15 +53,14 @@ describe('Test id and pass auth plugins', function () {
72 53
73 it('Should login Crash, create the user and use the token', async function () { 54 it('Should login Crash, create the user and use the token', async function () {
74 { 55 {
75 const res = await login(server.url, server.client, { username: 'crash', password: 'crash password' }) 56 const body = await server.login.login({ user: { username: 'crash', password: 'crash password' } })
76 crashAccessToken = res.body.access_token 57 crashAccessToken = body.access_token
77 crashRefreshToken = res.body.refresh_token 58 crashRefreshToken = body.refresh_token
78 } 59 }
79 60
80 { 61 {
81 const res = await getMyUserInformation(server.url, crashAccessToken) 62 const body = await server.users.getMyInfo({ token: crashAccessToken })
82 63
83 const body: User = res.body
84 expect(body.username).to.equal('crash') 64 expect(body.username).to.equal('crash')
85 expect(body.account.displayName).to.equal('Crash Bandicoot') 65 expect(body.account.displayName).to.equal('Crash Bandicoot')
86 expect(body.role).to.equal(UserRole.MODERATOR) 66 expect(body.role).to.equal(UserRole.MODERATOR)
@@ -89,15 +69,14 @@ describe('Test id and pass auth plugins', function () {
89 69
90 it('Should login the first Laguna, create the user and use the token', async function () { 70 it('Should login the first Laguna, create the user and use the token', async function () {
91 { 71 {
92 const res = await login(server.url, server.client, { username: 'laguna', password: 'laguna password' }) 72 const body = await server.login.login({ user: { username: 'laguna', password: 'laguna password' } })
93 lagunaAccessToken = res.body.access_token 73 lagunaAccessToken = body.access_token
94 lagunaRefreshToken = res.body.refresh_token 74 lagunaRefreshToken = body.refresh_token
95 } 75 }
96 76
97 { 77 {
98 const res = await getMyUserInformation(server.url, lagunaAccessToken) 78 const body = await server.users.getMyInfo({ token: lagunaAccessToken })
99 79
100 const body: User = res.body
101 expect(body.username).to.equal('laguna') 80 expect(body.username).to.equal('laguna')
102 expect(body.account.displayName).to.equal('laguna') 81 expect(body.account.displayName).to.equal('laguna')
103 expect(body.role).to.equal(UserRole.USER) 82 expect(body.role).to.equal(UserRole.USER)
@@ -106,51 +85,47 @@ describe('Test id and pass auth plugins', function () {
106 85
107 it('Should refresh crash token, but not laguna token', async function () { 86 it('Should refresh crash token, but not laguna token', async function () {
108 { 87 {
109 const resRefresh = await refreshToken(server, crashRefreshToken) 88 const resRefresh = await server.login.refreshToken({ refreshToken: crashRefreshToken })
110 crashAccessToken = resRefresh.body.access_token 89 crashAccessToken = resRefresh.body.access_token
111 crashRefreshToken = resRefresh.body.refresh_token 90 crashRefreshToken = resRefresh.body.refresh_token
112 91
113 const res = await getMyUserInformation(server.url, crashAccessToken) 92 const body = await server.users.getMyInfo({ token: crashAccessToken })
114 const user: User = res.body 93 expect(body.username).to.equal('crash')
115 expect(user.username).to.equal('crash')
116 } 94 }
117 95
118 { 96 {
119 await refreshToken(server, lagunaRefreshToken, 400) 97 await server.login.refreshToken({ refreshToken: lagunaRefreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
120 } 98 }
121 }) 99 })
122 100
123 it('Should update Crash profile', async function () { 101 it('Should update Crash profile', async function () {
124 await updateMyUser({ 102 await server.users.updateMe({
125 url: server.url, 103 token: crashAccessToken,
126 accessToken: crashAccessToken,
127 displayName: 'Beautiful Crash', 104 displayName: 'Beautiful Crash',
128 description: 'Mutant eastern barred bandicoot' 105 description: 'Mutant eastern barred bandicoot'
129 }) 106 })
130 107
131 const res = await getMyUserInformation(server.url, crashAccessToken) 108 const body = await server.users.getMyInfo({ token: crashAccessToken })
132 109
133 const body: User = res.body
134 expect(body.account.displayName).to.equal('Beautiful Crash') 110 expect(body.account.displayName).to.equal('Beautiful Crash')
135 expect(body.account.description).to.equal('Mutant eastern barred bandicoot') 111 expect(body.account.description).to.equal('Mutant eastern barred bandicoot')
136 }) 112 })
137 113
138 it('Should logout Crash', async function () { 114 it('Should logout Crash', async function () {
139 await logout(server.url, crashAccessToken) 115 await server.login.logout({ token: crashAccessToken })
140 }) 116 })
141 117
142 it('Should have logged out Crash', async function () { 118 it('Should have logged out Crash', async function () {
143 await waitUntilLog(server, 'On logout for auth 1 - 2') 119 await server.servers.waitUntilLog('On logout for auth 1 - 2')
144 120
145 await getMyUserInformation(server.url, crashAccessToken, 401) 121 await server.users.getMyInfo({ token: crashAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
146 }) 122 })
147 123
148 it('Should login Crash and keep the old existing profile', async function () { 124 it('Should login Crash and keep the old existing profile', async function () {
149 crashAccessToken = await userLogin(server, { username: 'crash', password: 'crash password' }) 125 crashAccessToken = await server.login.getAccessToken({ username: 'crash', password: 'crash password' })
150 126
151 const res = await getMyUserInformation(server.url, crashAccessToken) 127 const body = await server.users.getMyInfo({ token: crashAccessToken })
152 128
153 const body: User = res.body
154 expect(body.username).to.equal('crash') 129 expect(body.username).to.equal('crash')
155 expect(body.account.displayName).to.equal('Beautiful Crash') 130 expect(body.account.displayName).to.equal('Beautiful Crash')
156 expect(body.account.description).to.equal('Mutant eastern barred bandicoot') 131 expect(body.account.description).to.equal('Mutant eastern barred bandicoot')
@@ -162,39 +137,38 @@ describe('Test id and pass auth plugins', function () {
162 137
163 await wait(5000) 138 await wait(5000)
164 139
165 await getMyUserInformation(server.url, lagunaAccessToken, 401) 140 await server.users.getMyInfo({ token: lagunaAccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
166 }) 141 })
167 142
168 it('Should reject an invalid username, email, role or display name', async function () { 143 it('Should reject an invalid username, email, role or display name', async function () {
169 await userLogin(server, { username: 'ward', password: 'ward password' }, 400) 144 const command = server.login
170 await waitUntilLog(server, 'valid username')
171 145
172 await userLogin(server, { username: 'kiros', password: 'kiros password' }, 400) 146 await command.login({ user: { username: 'ward', password: 'ward password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
173 await waitUntilLog(server, 'valid display name') 147 await server.servers.waitUntilLog('valid username')
174 148
175 await userLogin(server, { username: 'raine', password: 'raine password' }, 400) 149 await command.login({ user: { username: 'kiros', password: 'kiros password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
176 await waitUntilLog(server, 'valid role') 150 await server.servers.waitUntilLog('valid display name')
177 151
178 await userLogin(server, { username: 'ellone', password: 'elonne password' }, 400) 152 await command.login({ user: { username: 'raine', password: 'raine password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
179 await waitUntilLog(server, 'valid email') 153 await server.servers.waitUntilLog('valid role')
154
155 await command.login({ user: { username: 'ellone', password: 'elonne password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
156 await server.servers.waitUntilLog('valid email')
180 }) 157 })
181 158
182 it('Should unregister spyro-auth and do not login existing Spyro', async function () { 159 it('Should unregister spyro-auth and do not login existing Spyro', async function () {
183 await updatePluginSettings({ 160 await server.plugins.updateSettings({
184 url: server.url,
185 accessToken: server.accessToken,
186 npmName: 'peertube-plugin-test-id-pass-auth-one', 161 npmName: 'peertube-plugin-test-id-pass-auth-one',
187 settings: { disableSpyro: true } 162 settings: { disableSpyro: true }
188 }) 163 })
189 164
190 await userLogin(server, { username: 'spyro', password: 'spyro password' }, 400) 165 const command = server.login
191 await userLogin(server, { username: 'spyro', password: 'fake' }, 400) 166 await command.login({ user: { username: 'spyro', password: 'spyro password' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
167 await command.login({ user: { username: 'spyro', password: 'fake' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
192 }) 168 })
193 169
194 it('Should have disabled this auth', async function () { 170 it('Should have disabled this auth', async function () {
195 const res = await getConfig(server.url) 171 const config = await server.config.getConfig()
196
197 const config: ServerConfig = res.body
198 172
199 const auths = config.plugin.registeredIdAndPassAuths 173 const auths = config.plugin.registeredIdAndPassAuths
200 expect(auths).to.have.lengthOf(7) 174 expect(auths).to.have.lengthOf(7)
@@ -204,19 +178,16 @@ describe('Test id and pass auth plugins', function () {
204 }) 178 })
205 179
206 it('Should uninstall the plugin one and do not login existing Crash', async function () { 180 it('Should uninstall the plugin one and do not login existing Crash', async function () {
207 await uninstallPlugin({ 181 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-id-pass-auth-one' })
208 url: server.url,
209 accessToken: server.accessToken,
210 npmName: 'peertube-plugin-test-id-pass-auth-one'
211 })
212 182
213 await userLogin(server, { username: 'crash', password: 'crash password' }, 400) 183 await server.login.login({
184 user: { username: 'crash', password: 'crash password' },
185 expectedStatus: HttpStatusCode.BAD_REQUEST_400
186 })
214 }) 187 })
215 188
216 it('Should display the correct configuration', async function () { 189 it('Should display the correct configuration', async function () {
217 const res = await getConfig(server.url) 190 const config = await server.config.getConfig()
218
219 const config: ServerConfig = res.body
220 191
221 const auths = config.plugin.registeredIdAndPassAuths 192 const auths = config.plugin.registeredIdAndPassAuths
222 expect(auths).to.have.lengthOf(6) 193 expect(auths).to.have.lengthOf(6)
@@ -226,13 +197,11 @@ describe('Test id and pass auth plugins', function () {
226 }) 197 })
227 198
228 it('Should display plugin auth information in users list', async function () { 199 it('Should display plugin auth information in users list', async function () {
229 const res = await getUsersList(server.url, server.accessToken) 200 const { data } = await server.users.list()
230
231 const users: User[] = res.body.data
232 201
233 const root = users.find(u => u.username === 'root') 202 const root = data.find(u => u.username === 'root')
234 const crash = users.find(u => u.username === 'crash') 203 const crash = data.find(u => u.username === 'crash')
235 const laguna = users.find(u => u.username === 'laguna') 204 const laguna = data.find(u => u.username === 'laguna')
236 205
237 expect(root.pluginAuth).to.be.null 206 expect(root.pluginAuth).to.be.null
238 expect(crash.pluginAuth).to.equal('peertube-plugin-test-id-pass-auth-one') 207 expect(crash.pluginAuth).to.equal('peertube-plugin-test-id-pass-auth-one')
diff --git a/server/tests/plugins/plugin-helpers.ts b/server/tests/plugins/plugin-helpers.ts
index 0296d6eb7..5d16b28a4 100644
--- a/server/tests/plugins/plugin-helpers.ts
+++ b/server/tests/plugins/plugin-helpers.ts
@@ -1,25 +1,22 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { 5import {
5 checkVideoFilesWereRemoved, 6 checkVideoFilesWereRemoved,
7 cleanupTests,
8 createMultipleServers,
6 doubleFollow, 9 doubleFollow,
7 getPluginTestPath, 10 makeGetRequest,
8 getVideo,
9 installPlugin,
10 makePostBodyRequest, 11 makePostBodyRequest,
12 PeerTubeServer,
13 PluginsCommand,
11 setAccessTokensToServers, 14 setAccessTokensToServers,
12 uploadVideoAndGetId, 15 waitJobs
13 viewVideo, 16} from '@shared/extra-utils'
14 getVideosList, 17import { HttpStatusCode } from '@shared/models'
15 waitJobs,
16 makeGetRequest
17} from '../../../shared/extra-utils'
18import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
19import { expect } from 'chai'
20import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
21 18
22function postCommand (server: ServerInfo, command: string, bodyArg?: object) { 19function postCommand (server: PeerTubeServer, command: string, bodyArg?: object) {
23 const body = { command } 20 const body = { command }
24 if (bodyArg) Object.assign(body, bodyArg) 21 if (bodyArg) Object.assign(body, bodyArg)
25 22
@@ -27,54 +24,50 @@ function postCommand (server: ServerInfo, command: string, bodyArg?: object) {
27 url: server.url, 24 url: server.url,
28 path: '/plugins/test-four/router/commander', 25 path: '/plugins/test-four/router/commander',
29 fields: body, 26 fields: body,
30 statusCodeExpected: HttpStatusCode.NO_CONTENT_204 27 expectedStatus: HttpStatusCode.NO_CONTENT_204
31 }) 28 })
32} 29}
33 30
34describe('Test plugin helpers', function () { 31describe('Test plugin helpers', function () {
35 let servers: ServerInfo[] 32 let servers: PeerTubeServer[]
36 33
37 before(async function () { 34 before(async function () {
38 this.timeout(60000) 35 this.timeout(60000)
39 36
40 servers = await flushAndRunMultipleServers(2) 37 servers = await createMultipleServers(2)
41 await setAccessTokensToServers(servers) 38 await setAccessTokensToServers(servers)
42 39
43 await doubleFollow(servers[0], servers[1]) 40 await doubleFollow(servers[0], servers[1])
44 41
45 await installPlugin({ 42 await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath('-four') })
46 url: servers[0].url,
47 accessToken: servers[0].accessToken,
48 path: getPluginTestPath('-four')
49 })
50 }) 43 })
51 44
52 describe('Logger', function () { 45 describe('Logger', function () {
53 46
54 it('Should have logged things', async function () { 47 it('Should have logged things', async function () {
55 await waitUntilLog(servers[0], 'localhost:' + servers[0].port + ' peertube-plugin-test-four', 1, false) 48 await servers[0].servers.waitUntilLog('localhost:' + servers[0].port + ' peertube-plugin-test-four', 1, false)
56 await waitUntilLog(servers[0], 'Hello world from plugin four', 1) 49 await servers[0].servers.waitUntilLog('Hello world from plugin four', 1)
57 }) 50 })
58 }) 51 })
59 52
60 describe('Database', function () { 53 describe('Database', function () {
61 54
62 it('Should have made a query', async function () { 55 it('Should have made a query', async function () {
63 await waitUntilLog(servers[0], `root email is admin${servers[0].internalServerNumber}@example.com`) 56 await servers[0].servers.waitUntilLog(`root email is admin${servers[0].internalServerNumber}@example.com`)
64 }) 57 })
65 }) 58 })
66 59
67 describe('Config', function () { 60 describe('Config', function () {
68 61
69 it('Should have the correct webserver url', async function () { 62 it('Should have the correct webserver url', async function () {
70 await waitUntilLog(servers[0], `server url is http://localhost:${servers[0].port}`) 63 await servers[0].servers.waitUntilLog(`server url is http://localhost:${servers[0].port}`)
71 }) 64 })
72 65
73 it('Should have the correct config', async function () { 66 it('Should have the correct config', async function () {
74 const res = await makeGetRequest({ 67 const res = await makeGetRequest({
75 url: servers[0].url, 68 url: servers[0].url,
76 path: '/plugins/test-four/router/server-config', 69 path: '/plugins/test-four/router/server-config',
77 statusCodeExpected: HttpStatusCode.OK_200 70 expectedStatus: HttpStatusCode.OK_200
78 }) 71 })
79 72
80 expect(res.body.serverConfig).to.exist 73 expect(res.body.serverConfig).to.exist
@@ -85,7 +78,7 @@ describe('Test plugin helpers', function () {
85 describe('Server', function () { 78 describe('Server', function () {
86 79
87 it('Should get the server actor', async function () { 80 it('Should get the server actor', async function () {
88 await waitUntilLog(servers[0], 'server actor name is peertube') 81 await servers[0].servers.waitUntilLog('server actor name is peertube')
89 }) 82 })
90 }) 83 })
91 84
@@ -95,7 +88,7 @@ describe('Test plugin helpers', function () {
95 const res = await makeGetRequest({ 88 const res = await makeGetRequest({
96 url: servers[0].url, 89 url: servers[0].url,
97 path: '/plugins/test-four/router/static-route', 90 path: '/plugins/test-four/router/static-route',
98 statusCodeExpected: HttpStatusCode.OK_200 91 expectedStatus: HttpStatusCode.OK_200
99 }) 92 })
100 93
101 expect(res.body.staticRoute).to.equal('/plugins/test-four/0.0.1/static/') 94 expect(res.body.staticRoute).to.equal('/plugins/test-four/0.0.1/static/')
@@ -107,7 +100,7 @@ describe('Test plugin helpers', function () {
107 const res = await makeGetRequest({ 100 const res = await makeGetRequest({
108 url: servers[0].url, 101 url: servers[0].url,
109 path: baseRouter + 'router-route', 102 path: baseRouter + 'router-route',
110 statusCodeExpected: HttpStatusCode.OK_200 103 expectedStatus: HttpStatusCode.OK_200
111 }) 104 })
112 105
113 expect(res.body.routerRoute).to.equal(baseRouter) 106 expect(res.body.routerRoute).to.equal(baseRouter)
@@ -120,7 +113,7 @@ describe('Test plugin helpers', function () {
120 await makeGetRequest({ 113 await makeGetRequest({
121 url: servers[0].url, 114 url: servers[0].url,
122 path: '/plugins/test-four/router/user', 115 path: '/plugins/test-four/router/user',
123 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 116 expectedStatus: HttpStatusCode.NOT_FOUND_404
124 }) 117 })
125 }) 118 })
126 119
@@ -129,7 +122,7 @@ describe('Test plugin helpers', function () {
129 url: servers[0].url, 122 url: servers[0].url,
130 token: servers[0].accessToken, 123 token: servers[0].accessToken,
131 path: '/plugins/test-four/router/user', 124 path: '/plugins/test-four/router/user',
132 statusCodeExpected: HttpStatusCode.OK_200 125 expectedStatus: HttpStatusCode.OK_200
133 }) 126 })
134 127
135 expect(res.body.username).to.equal('root') 128 expect(res.body.username).to.equal('root')
@@ -147,59 +140,54 @@ describe('Test plugin helpers', function () {
147 this.timeout(60000) 140 this.timeout(60000)
148 141
149 { 142 {
150 const res = await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' }) 143 const res = await servers[0].videos.quickUpload({ name: 'video server 1' })
151 videoUUIDServer1 = res.uuid 144 videoUUIDServer1 = res.uuid
152 } 145 }
153 146
154 { 147 {
155 await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' }) 148 await servers[1].videos.quickUpload({ name: 'video server 2' })
156 } 149 }
157 150
158 await waitJobs(servers) 151 await waitJobs(servers)
159 152
160 const res = await getVideosList(servers[0].url) 153 const { data } = await servers[0].videos.list()
161 const videos = res.body.data
162 154
163 expect(videos).to.have.lengthOf(2) 155 expect(data).to.have.lengthOf(2)
164 }) 156 })
165 157
166 it('Should mute server 2', async function () { 158 it('Should mute server 2', async function () {
167 this.timeout(10000) 159 this.timeout(10000)
168 await postCommand(servers[0], 'blockServer', { hostToBlock: `localhost:${servers[1].port}` }) 160 await postCommand(servers[0], 'blockServer', { hostToBlock: `localhost:${servers[1].port}` })
169 161
170 const res = await getVideosList(servers[0].url) 162 const { data } = await servers[0].videos.list()
171 const videos = res.body.data
172 163
173 expect(videos).to.have.lengthOf(1) 164 expect(data).to.have.lengthOf(1)
174 expect(videos[0].name).to.equal('video server 1') 165 expect(data[0].name).to.equal('video server 1')
175 }) 166 })
176 167
177 it('Should unmute server 2', async function () { 168 it('Should unmute server 2', async function () {
178 await postCommand(servers[0], 'unblockServer', { hostToUnblock: `localhost:${servers[1].port}` }) 169 await postCommand(servers[0], 'unblockServer', { hostToUnblock: `localhost:${servers[1].port}` })
179 170
180 const res = await getVideosList(servers[0].url) 171 const { data } = await servers[0].videos.list()
181 const videos = res.body.data
182 172
183 expect(videos).to.have.lengthOf(2) 173 expect(data).to.have.lengthOf(2)
184 }) 174 })
185 175
186 it('Should mute account of server 2', async function () { 176 it('Should mute account of server 2', async function () {
187 await postCommand(servers[0], 'blockAccount', { handleToBlock: `root@localhost:${servers[1].port}` }) 177 await postCommand(servers[0], 'blockAccount', { handleToBlock: `root@localhost:${servers[1].port}` })
188 178
189 const res = await getVideosList(servers[0].url) 179 const { data } = await servers[0].videos.list()
190 const videos = res.body.data
191 180
192 expect(videos).to.have.lengthOf(1) 181 expect(data).to.have.lengthOf(1)
193 expect(videos[0].name).to.equal('video server 1') 182 expect(data[0].name).to.equal('video server 1')
194 }) 183 })
195 184
196 it('Should unmute account of server 2', async function () { 185 it('Should unmute account of server 2', async function () {
197 await postCommand(servers[0], 'unblockAccount', { handleToUnblock: `root@localhost:${servers[1].port}` }) 186 await postCommand(servers[0], 'unblockAccount', { handleToUnblock: `root@localhost:${servers[1].port}` })
198 187
199 const res = await getVideosList(servers[0].url) 188 const { data } = await servers[0].videos.list()
200 const videos = res.body.data
201 189
202 expect(videos).to.have.lengthOf(2) 190 expect(data).to.have.lengthOf(2)
203 }) 191 })
204 192
205 it('Should blacklist video', async function () { 193 it('Should blacklist video', async function () {
@@ -210,11 +198,10 @@ describe('Test plugin helpers', function () {
210 await waitJobs(servers) 198 await waitJobs(servers)
211 199
212 for (const server of servers) { 200 for (const server of servers) {
213 const res = await getVideosList(server.url) 201 const { data } = await server.videos.list()
214 const videos = res.body.data
215 202
216 expect(videos).to.have.lengthOf(1) 203 expect(data).to.have.lengthOf(1)
217 expect(videos[0].name).to.equal('video server 2') 204 expect(data[0].name).to.equal('video server 2')
218 } 205 }
219 }) 206 })
220 207
@@ -226,10 +213,9 @@ describe('Test plugin helpers', function () {
226 await waitJobs(servers) 213 await waitJobs(servers)
227 214
228 for (const server of servers) { 215 for (const server of servers) {
229 const res = await getVideosList(server.url) 216 const { data } = await server.videos.list()
230 const videos = res.body.data
231 217
232 expect(videos).to.have.lengthOf(2) 218 expect(data).to.have.lengthOf(2)
233 } 219 }
234 }) 220 })
235 }) 221 })
@@ -238,7 +224,7 @@ describe('Test plugin helpers', function () {
238 let videoUUID: string 224 let videoUUID: string
239 225
240 before(async () => { 226 before(async () => {
241 const res = await uploadVideoAndGetId({ server: servers[0], videoName: 'video1' }) 227 const res = await servers[0].videos.quickUpload({ name: 'video1' })
242 videoUUID = res.uuid 228 videoUUID = res.uuid
243 }) 229 })
244 230
@@ -246,25 +232,25 @@ describe('Test plugin helpers', function () {
246 this.timeout(40000) 232 this.timeout(40000)
247 233
248 // Should not throw -> video exists 234 // Should not throw -> video exists
249 await getVideo(servers[0].url, videoUUID) 235 const video = await servers[0].videos.get({ id: videoUUID })
250 // Should delete the video 236 // Should delete the video
251 await viewVideo(servers[0].url, videoUUID) 237 await servers[0].videos.view({ id: videoUUID })
252 238
253 await waitUntilLog(servers[0], 'Video deleted by plugin four.') 239 await servers[0].servers.waitUntilLog('Video deleted by plugin four.')
254 240
255 try { 241 try {
256 // Should throw because the video should have been deleted 242 // Should throw because the video should have been deleted
257 await getVideo(servers[0].url, videoUUID) 243 await servers[0].videos.get({ id: videoUUID })
258 throw new Error('Video exists') 244 throw new Error('Video exists')
259 } catch (err) { 245 } catch (err) {
260 if (err.message.includes('exists')) throw err 246 if (err.message.includes('exists')) throw err
261 } 247 }
262 248
263 await checkVideoFilesWereRemoved(videoUUID, servers[0].internalServerNumber) 249 await checkVideoFilesWereRemoved({ server: servers[0], video })
264 }) 250 })
265 251
266 it('Should have fetched the video by URL', async function () { 252 it('Should have fetched the video by URL', async function () {
267 await waitUntilLog(servers[0], `video from DB uuid is ${videoUUID}`) 253 await servers[0].servers.waitUntilLog(`video from DB uuid is ${videoUUID}`)
268 }) 254 })
269 }) 255 })
270 256
diff --git a/server/tests/plugins/plugin-router.ts b/server/tests/plugins/plugin-router.ts
index 24e6a1e83..b1ac9e2fe 100644
--- a/server/tests/plugins/plugin-router.ts
+++ b/server/tests/plugins/plugin-router.ts
@@ -1,19 +1,20 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 4import { expect } from 'chai'
5import { 5import {
6 getPluginTestPath, 6 cleanupTests,
7 installPlugin, 7 createSingleServer,
8 makeGetRequest, 8 makeGetRequest,
9 makePostBodyRequest, 9 makePostBodyRequest,
10 setAccessTokensToServers, uninstallPlugin 10 PeerTubeServer,
11} from '../../../shared/extra-utils' 11 PluginsCommand,
12import { expect } from 'chai' 12 setAccessTokensToServers
13import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 13} from '@shared/extra-utils'
14import { HttpStatusCode } from '@shared/models'
14 15
15describe('Test plugin helpers', function () { 16describe('Test plugin helpers', function () {
16 let server: ServerInfo 17 let server: PeerTubeServer
17 const basePaths = [ 18 const basePaths = [
18 '/plugins/test-five/router/', 19 '/plugins/test-five/router/',
19 '/plugins/test-five/0.0.1/router/' 20 '/plugins/test-five/0.0.1/router/'
@@ -22,14 +23,10 @@ describe('Test plugin helpers', function () {
22 before(async function () { 23 before(async function () {
23 this.timeout(30000) 24 this.timeout(30000)
24 25
25 server = await flushAndRunServer(1) 26 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 27 await setAccessTokensToServers([ server ])
27 28
28 await installPlugin({ 29 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-five') })
29 url: server.url,
30 accessToken: server.accessToken,
31 path: getPluginTestPath('-five')
32 })
33 }) 30 })
34 31
35 it('Should answer "pong"', async function () { 32 it('Should answer "pong"', async function () {
@@ -37,7 +34,7 @@ describe('Test plugin helpers', function () {
37 const res = await makeGetRequest({ 34 const res = await makeGetRequest({
38 url: server.url, 35 url: server.url,
39 path: path + 'ping', 36 path: path + 'ping',
40 statusCodeExpected: HttpStatusCode.OK_200 37 expectedStatus: HttpStatusCode.OK_200
41 }) 38 })
42 39
43 expect(res.body.message).to.equal('pong') 40 expect(res.body.message).to.equal('pong')
@@ -50,7 +47,7 @@ describe('Test plugin helpers', function () {
50 url: server.url, 47 url: server.url,
51 path: path + 'is-authenticated', 48 path: path + 'is-authenticated',
52 token: server.accessToken, 49 token: server.accessToken,
53 statusCodeExpected: 200 50 expectedStatus: 200
54 }) 51 })
55 52
56 expect(res.body.isAuthenticated).to.equal(true) 53 expect(res.body.isAuthenticated).to.equal(true)
@@ -58,7 +55,7 @@ describe('Test plugin helpers', function () {
58 const secRes = await makeGetRequest({ 55 const secRes = await makeGetRequest({
59 url: server.url, 56 url: server.url,
60 path: path + 'is-authenticated', 57 path: path + 'is-authenticated',
61 statusCodeExpected: 200 58 expectedStatus: 200
62 }) 59 })
63 60
64 expect(secRes.body.isAuthenticated).to.equal(false) 61 expect(secRes.body.isAuthenticated).to.equal(false)
@@ -77,7 +74,7 @@ describe('Test plugin helpers', function () {
77 url: server.url, 74 url: server.url,
78 path: path + 'form/post/mirror', 75 path: path + 'form/post/mirror',
79 fields: body, 76 fields: body,
80 statusCodeExpected: HttpStatusCode.OK_200 77 expectedStatus: HttpStatusCode.OK_200
81 }) 78 })
82 79
83 expect(res.body).to.deep.equal(body) 80 expect(res.body).to.deep.equal(body)
@@ -85,24 +82,20 @@ describe('Test plugin helpers', function () {
85 }) 82 })
86 83
87 it('Should remove the plugin and remove the routes', async function () { 84 it('Should remove the plugin and remove the routes', async function () {
88 await uninstallPlugin({ 85 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-five' })
89 url: server.url,
90 accessToken: server.accessToken,
91 npmName: 'peertube-plugin-test-five'
92 })
93 86
94 for (const path of basePaths) { 87 for (const path of basePaths) {
95 await makeGetRequest({ 88 await makeGetRequest({
96 url: server.url, 89 url: server.url,
97 path: path + 'ping', 90 path: path + 'ping',
98 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 91 expectedStatus: HttpStatusCode.NOT_FOUND_404
99 }) 92 })
100 93
101 await makePostBodyRequest({ 94 await makePostBodyRequest({
102 url: server.url, 95 url: server.url,
103 path: path + 'ping', 96 path: path + 'ping',
104 fields: {}, 97 fields: {},
105 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 98 expectedStatus: HttpStatusCode.NOT_FOUND_404
106 }) 99 })
107 } 100 }
108 }) 101 })
diff --git a/server/tests/plugins/plugin-storage.ts b/server/tests/plugins/plugin-storage.ts
index 3c46b2585..e20c36dba 100644
--- a/server/tests/plugins/plugin-storage.ts
+++ b/server/tests/plugins/plugin-storage.ts
@@ -4,37 +4,32 @@ import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { pathExists, readdir, readFile } from 'fs-extra' 5import { pathExists, readdir, readFile } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { HttpStatusCode } from '@shared/core-utils'
8import { 7import {
9 buildServerDirectory, 8 cleanupTests,
10 getPluginTestPath, 9 createSingleServer,
11 installPlugin,
12 makeGetRequest, 10 makeGetRequest,
13 setAccessTokensToServers, 11 PeerTubeServer,
14 uninstallPlugin 12 PluginsCommand,
15} from '../../../shared/extra-utils' 13 setAccessTokensToServers
16import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' 14} from '@shared/extra-utils'
15import { HttpStatusCode } from '@shared/models'
17 16
18describe('Test plugin storage', function () { 17describe('Test plugin storage', function () {
19 let server: ServerInfo 18 let server: PeerTubeServer
20 19
21 before(async function () { 20 before(async function () {
22 this.timeout(30000) 21 this.timeout(30000)
23 22
24 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
25 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
26 25
27 await installPlugin({ 26 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-six') })
28 url: server.url,
29 accessToken: server.accessToken,
30 path: getPluginTestPath('-six')
31 })
32 }) 27 })
33 28
34 describe('DB storage', function () { 29 describe('DB storage', function () {
35 30
36 it('Should correctly store a subkey', async function () { 31 it('Should correctly store a subkey', async function () {
37 await waitUntilLog(server, 'superkey stored value is toto') 32 await server.servers.waitUntilLog('superkey stored value is toto')
38 }) 33 })
39 }) 34 })
40 35
@@ -50,12 +45,12 @@ describe('Test plugin storage', function () {
50 } 45 }
51 46
52 before(function () { 47 before(function () {
53 dataPath = buildServerDirectory(server, 'plugins/data') 48 dataPath = server.servers.buildDirectory('plugins/data')
54 pluginDataPath = join(dataPath, 'peertube-plugin-test-six') 49 pluginDataPath = join(dataPath, 'peertube-plugin-test-six')
55 }) 50 })
56 51
57 it('Should have created the directory on install', async function () { 52 it('Should have created the directory on install', async function () {
58 const dataPath = buildServerDirectory(server, 'plugins/data') 53 const dataPath = server.servers.buildDirectory('plugins/data')
59 const pluginDataPath = join(dataPath, 'peertube-plugin-test-six') 54 const pluginDataPath = join(dataPath, 'peertube-plugin-test-six')
60 55
61 expect(await pathExists(dataPath)).to.be.true 56 expect(await pathExists(dataPath)).to.be.true
@@ -68,7 +63,7 @@ describe('Test plugin storage', function () {
68 url: server.url, 63 url: server.url,
69 token: server.accessToken, 64 token: server.accessToken,
70 path: '/plugins/test-six/router/create-file', 65 path: '/plugins/test-six/router/create-file',
71 statusCodeExpected: HttpStatusCode.OK_200 66 expectedStatus: HttpStatusCode.OK_200
72 }) 67 })
73 68
74 const content = await getFileContent() 69 const content = await getFileContent()
@@ -76,22 +71,14 @@ describe('Test plugin storage', function () {
76 }) 71 })
77 72
78 it('Should still have the file after an uninstallation', async function () { 73 it('Should still have the file after an uninstallation', async function () {
79 await uninstallPlugin({ 74 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-six' })
80 url: server.url,
81 accessToken: server.accessToken,
82 npmName: 'peertube-plugin-test-six'
83 })
84 75
85 const content = await getFileContent() 76 const content = await getFileContent()
86 expect(content).to.equal('Prince Ali') 77 expect(content).to.equal('Prince Ali')
87 }) 78 })
88 79
89 it('Should still have the file after the reinstallation', async function () { 80 it('Should still have the file after the reinstallation', async function () {
90 await installPlugin({ 81 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-six') })
91 url: server.url,
92 accessToken: server.accessToken,
93 path: getPluginTestPath('-six')
94 })
95 82
96 const content = await getFileContent() 83 const content = await getFileContent()
97 expect(content).to.equal('Prince Ali') 84 expect(content).to.equal('Prince Ali')
diff --git a/server/tests/plugins/plugin-transcoding.ts b/server/tests/plugins/plugin-transcoding.ts
index eefb2294d..93637e3ce 100644
--- a/server/tests/plugins/plugin-transcoding.ts
+++ b/server/tests/plugins/plugin-transcoding.ts
@@ -2,79 +2,73 @@
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { join } from 'path'
6import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils' 5import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server/helpers/ffprobe-utils'
7import { ServerConfig, VideoDetails, VideoPrivacy } from '@shared/models'
8import { 6import {
9 buildServerDirectory, 7 cleanupTests,
10 createLive, 8 createSingleServer,
11 getConfig, 9 PeerTubeServer,
12 getPluginTestPath, 10 PluginsCommand,
13 getVideo,
14 installPlugin,
15 sendRTMPStreamInVideo,
16 setAccessTokensToServers, 11 setAccessTokensToServers,
17 setDefaultVideoChannel, 12 setDefaultVideoChannel,
18 testFfmpegStreamError, 13 testFfmpegStreamError,
19 uninstallPlugin, 14 waitJobs
20 updateCustomSubConfig, 15} from '@shared/extra-utils'
21 uploadVideoAndGetId, 16import { VideoPrivacy } from '@shared/models'
22 waitJobs, 17
23 waitUntilLivePublished 18async function createLiveWrapper (server: PeerTubeServer) {
24} from '../../../shared/extra-utils'
25import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers'
26
27async function createLiveWrapper (server: ServerInfo) {
28 const liveAttributes = { 19 const liveAttributes = {
29 name: 'live video', 20 name: 'live video',
30 channelId: server.videoChannel.id, 21 channelId: server.store.channel.id,
31 privacy: VideoPrivacy.PUBLIC 22 privacy: VideoPrivacy.PUBLIC
32 } 23 }
33 24
34 const res = await createLive(server.url, server.accessToken, liveAttributes) 25 const { uuid } = await server.live.create({ fields: liveAttributes })
35 return res.body.video.uuid 26
27 return uuid
36} 28}
37 29
38function updateConf (server: ServerInfo, vodProfile: string, liveProfile: string) { 30function updateConf (server: PeerTubeServer, vodProfile: string, liveProfile: string) {
39 return updateCustomSubConfig(server.url, server.accessToken, { 31 return server.config.updateCustomSubConfig({
40 transcoding: { 32 newConfig: {
41 enabled: true,
42 profile: vodProfile,
43 hls: {
44 enabled: true
45 },
46 webtorrent: {
47 enabled: true
48 },
49 resolutions: {
50 '240p': true,
51 '360p': false,
52 '480p': false,
53 '720p': true
54 }
55 },
56 live: {
57 transcoding: { 33 transcoding: {
58 profile: liveProfile,
59 enabled: true, 34 enabled: true,
35 profile: vodProfile,
36 hls: {
37 enabled: true
38 },
39 webtorrent: {
40 enabled: true
41 },
60 resolutions: { 42 resolutions: {
61 '240p': true, 43 '240p': true,
62 '360p': false, 44 '360p': false,
63 '480p': false, 45 '480p': false,
64 '720p': true 46 '720p': true
65 } 47 }
48 },
49 live: {
50 transcoding: {
51 profile: liveProfile,
52 enabled: true,
53 resolutions: {
54 '240p': true,
55 '360p': false,
56 '480p': false,
57 '720p': true
58 }
59 }
66 } 60 }
67 } 61 }
68 }) 62 })
69} 63}
70 64
71describe('Test transcoding plugins', function () { 65describe('Test transcoding plugins', function () {
72 let server: ServerInfo 66 let server: PeerTubeServer
73 67
74 before(async function () { 68 before(async function () {
75 this.timeout(60000) 69 this.timeout(60000)
76 70
77 server = await flushAndRunServer(1) 71 server = await createSingleServer(1)
78 await setAccessTokensToServers([ server ]) 72 await setAccessTokensToServers([ server ])
79 await setDefaultVideoChannel([ server ]) 73 await setDefaultVideoChannel([ server ])
80 74
@@ -84,8 +78,7 @@ describe('Test transcoding plugins', function () {
84 describe('When using a plugin adding profiles to existing encoders', function () { 78 describe('When using a plugin adding profiles to existing encoders', function () {
85 79
86 async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) { 80 async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) {
87 const res = await getVideo(server.url, uuid) 81 const video = await server.videos.get({ id: uuid })
88 const video = res.body as VideoDetails
89 const files = video.files.concat(...video.streamingPlaylists.map(p => p.files)) 82 const files = video.files.concat(...video.streamingPlaylists.map(p => p.files))
90 83
91 for (const file of files) { 84 for (const file of files) {
@@ -109,134 +102,132 @@ describe('Test transcoding plugins', function () {
109 } 102 }
110 103
111 before(async function () { 104 before(async function () {
112 await installPlugin({ 105 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-one') })
113 url: server.url,
114 accessToken: server.accessToken,
115 path: getPluginTestPath('-transcoding-one')
116 })
117 }) 106 })
118 107
119 it('Should have the appropriate available profiles', async function () { 108 it('Should have the appropriate available profiles', async function () {
120 const res = await getConfig(server.url) 109 const config = await server.config.getConfig()
121 const config = res.body as ServerConfig
122 110
123 expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ]) 111 expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ])
124 expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'low-live', 'input-options-live', 'bad-scale-live' ]) 112 expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'high-live', 'input-options-live', 'bad-scale-live' ])
125 }) 113 })
126 114
127 it('Should not use the plugin profile if not chosen by the admin', async function () { 115 describe('VOD', function () {
128 this.timeout(240000)
129 116
130 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 117 it('Should not use the plugin profile if not chosen by the admin', async function () {
131 await waitJobs([ server ]) 118 this.timeout(240000)
132 119
133 await checkVideoFPS(videoUUID, 'above', 20) 120 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
134 }) 121 await waitJobs([ server ])
135 122
136 it('Should use the vod profile', async function () { 123 await checkVideoFPS(videoUUID, 'above', 20)
137 this.timeout(240000) 124 })
138 125
139 await updateConf(server, 'low-vod', 'default') 126 it('Should use the vod profile', async function () {
127 this.timeout(240000)
140 128
141 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 129 await updateConf(server, 'low-vod', 'default')
142 await waitJobs([ server ])
143 130
144 await checkVideoFPS(videoUUID, 'below', 12) 131 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
145 }) 132 await waitJobs([ server ])
146 133
147 it('Should apply input options in vod profile', async function () { 134 await checkVideoFPS(videoUUID, 'below', 12)
148 this.timeout(240000) 135 })
149 136
150 await updateConf(server, 'input-options-vod', 'default') 137 it('Should apply input options in vod profile', async function () {
138 this.timeout(240000)
151 139
152 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 140 await updateConf(server, 'input-options-vod', 'default')
153 await waitJobs([ server ])
154 141
155 await checkVideoFPS(videoUUID, 'below', 6) 142 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
156 }) 143 await waitJobs([ server ])
157 144
158 it('Should apply the scale filter in vod profile', async function () { 145 await checkVideoFPS(videoUUID, 'below', 6)
159 this.timeout(240000) 146 })
160 147
161 await updateConf(server, 'bad-scale-vod', 'default') 148 it('Should apply the scale filter in vod profile', async function () {
149 this.timeout(240000)
162 150
163 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 151 await updateConf(server, 'bad-scale-vod', 'default')
164 await waitJobs([ server ])
165 152
166 // Transcoding failed 153 const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
167 const res = await getVideo(server.url, videoUUID) 154 await waitJobs([ server ])
168 const video: VideoDetails = res.body
169 155
170 expect(video.files).to.have.lengthOf(1) 156 // Transcoding failed
171 expect(video.streamingPlaylists).to.have.lengthOf(0) 157 const video = await server.videos.get({ id: videoUUID })
158 expect(video.files).to.have.lengthOf(1)
159 expect(video.streamingPlaylists).to.have.lengthOf(0)
160 })
172 }) 161 })
173 162
174 it('Should not use the plugin profile if not chosen by the admin', async function () { 163 describe('Live', function () {
175 this.timeout(240000)
176 164
177 const liveVideoId = await createLiveWrapper(server) 165 it('Should not use the plugin profile if not chosen by the admin', async function () {
166 this.timeout(240000)
178 167
179 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 168 const liveVideoId = await createLiveWrapper(server)
180 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
181 await waitJobs([ server ])
182 169
183 await checkLiveFPS(liveVideoId, 'above', 20) 170 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
184 }) 171 await server.live.waitUntilPublished({ videoId: liveVideoId })
172 await waitJobs([ server ])
185 173
186 it('Should use the live profile', async function () { 174 await checkLiveFPS(liveVideoId, 'above', 20)
187 this.timeout(240000) 175 })
188 176
189 await updateConf(server, 'low-vod', 'low-live') 177 it('Should use the live profile', async function () {
178 this.timeout(240000)
190 179
191 const liveVideoId = await createLiveWrapper(server) 180 await updateConf(server, 'low-vod', 'high-live')
192 181
193 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 182 const liveVideoId = await createLiveWrapper(server)
194 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
195 await waitJobs([ server ])
196 183
197 await checkLiveFPS(liveVideoId, 'below', 12) 184 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
198 }) 185 await server.live.waitUntilPublished({ videoId: liveVideoId })
186 await waitJobs([ server ])
199 187
200 it('Should apply the input options on live profile', async function () { 188 await checkLiveFPS(liveVideoId, 'above', 45)
201 this.timeout(240000) 189 })
202 190
203 await updateConf(server, 'low-vod', 'input-options-live') 191 it('Should apply the input options on live profile', async function () {
192 this.timeout(240000)
204 193
205 const liveVideoId = await createLiveWrapper(server) 194 await updateConf(server, 'low-vod', 'input-options-live')
206 195
207 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 196 const liveVideoId = await createLiveWrapper(server)
208 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId)
209 await waitJobs([ server ])
210 197
211 await checkLiveFPS(liveVideoId, 'below', 6) 198 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
212 }) 199 await server.live.waitUntilPublished({ videoId: liveVideoId })
200 await waitJobs([ server ])
213 201
214 it('Should apply the scale filter name on live profile', async function () { 202 await checkLiveFPS(liveVideoId, 'above', 45)
215 this.timeout(240000) 203 })
216 204
217 await updateConf(server, 'low-vod', 'bad-scale-live') 205 it('Should apply the scale filter name on live profile', async function () {
206 this.timeout(240000)
218 207
219 const liveVideoId = await createLiveWrapper(server) 208 await updateConf(server, 'low-vod', 'bad-scale-live')
220 209
221 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 210 const liveVideoId = await createLiveWrapper(server)
222 await testFfmpegStreamError(command, true)
223 })
224 211
225 it('Should default to the default profile if the specified profile does not exist', async function () { 212 const command = await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
226 this.timeout(240000) 213 await testFfmpegStreamError(command, true)
214 })
227 215
228 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-transcoding-one' }) 216 it('Should default to the default profile if the specified profile does not exist', async function () {
217 this.timeout(240000)
229 218
230 const res = await getConfig(server.url) 219 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-transcoding-one' })
231 const config = res.body as ServerConfig
232 220
233 expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ]) 221 const config = await server.config.getConfig()
234 expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ])
235 222
236 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video' })).uuid 223 expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ])
237 await waitJobs([ server ]) 224 expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ])
225
226 const videoUUID = (await server.videos.quickUpload({ name: 'video', fixture: 'video_very_short_240p.mp4' })).uuid
227 await waitJobs([ server ])
238 228
239 await checkVideoFPS(videoUUID, 'above', 20) 229 await checkVideoFPS(videoUUID, 'above', 20)
230 })
240 }) 231 })
241 232
242 }) 233 })
@@ -244,11 +235,7 @@ describe('Test transcoding plugins', function () {
244 describe('When using a plugin adding new encoders', function () { 235 describe('When using a plugin adding new encoders', function () {
245 236
246 before(async function () { 237 before(async function () {
247 await installPlugin({ 238 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-two') })
248 url: server.url,
249 accessToken: server.accessToken,
250 path: getPluginTestPath('-transcoding-two')
251 })
252 239
253 await updateConf(server, 'test-vod-profile', 'test-live-profile') 240 await updateConf(server, 'test-vod-profile', 'test-live-profile')
254 }) 241 })
@@ -256,10 +243,12 @@ describe('Test transcoding plugins', function () {
256 it('Should use the new vod encoders', async function () { 243 it('Should use the new vod encoders', async function () {
257 this.timeout(240000) 244 this.timeout(240000)
258 245
259 const videoUUID = (await uploadVideoAndGetId({ server, videoName: 'video', fixture: 'video_short_240p.mp4' })).uuid 246 const videoUUID = (await server.videos.quickUpload({ name: 'video', fixture: 'video_very_short_240p.mp4' })).uuid
260 await waitJobs([ server ]) 247 await waitJobs([ server ])
261 248
262 const path = buildServerDirectory(server, join('videos', videoUUID + '-240.mp4')) 249 const video = await server.videos.get({ id: videoUUID })
250
251 const path = server.servers.buildWebTorrentFilePath(video.files[0].fileUrl)
263 const audioProbe = await getAudioStream(path) 252 const audioProbe = await getAudioStream(path)
264 expect(audioProbe.audioStream.codec_name).to.equal('opus') 253 expect(audioProbe.audioStream.codec_name).to.equal('opus')
265 254
@@ -272,8 +261,8 @@ describe('Test transcoding plugins', function () {
272 261
273 const liveVideoId = await createLiveWrapper(server) 262 const liveVideoId = await createLiveWrapper(server)
274 263
275 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 264 await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
276 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 265 await server.live.waitUntilPublished({ videoId: liveVideoId })
277 await waitJobs([ server ]) 266 await waitJobs([ server ])
278 267
279 const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8` 268 const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8`
diff --git a/server/tests/plugins/plugin-unloading.ts b/server/tests/plugins/plugin-unloading.ts
index 74ca82e2f..6bf2fda9b 100644
--- a/server/tests/plugins/plugin-unloading.ts
+++ b/server/tests/plugins/plugin-unloading.ts
@@ -1,42 +1,36 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { 5import {
5 cleanupTests, 6 cleanupTests,
6 flushAndRunServer, 7 createSingleServer,
7 getPluginTestPath,
8 makeGetRequest, 8 makeGetRequest,
9 installPlugin, 9 PeerTubeServer,
10 uninstallPlugin, 10 PluginsCommand,
11 ServerInfo,
12 setAccessTokensToServers 11 setAccessTokensToServers
13} from '../../../shared/extra-utils' 12} from '@shared/extra-utils'
14import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 13import { HttpStatusCode } from '@shared/models'
15import { expect } from 'chai'
16 14
17describe('Test plugins module unloading', function () { 15describe('Test plugins module unloading', function () {
18 let server: ServerInfo = null 16 let server: PeerTubeServer = null
19 const requestPath = '/plugins/test-unloading/router/get' 17 const requestPath = '/plugins/test-unloading/router/get'
20 let value: string = null 18 let value: string = null
21 19
22 before(async function () { 20 before(async function () {
23 this.timeout(30000) 21 this.timeout(30000)
24 22
25 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
26 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
27 25
28 await installPlugin({ 26 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-unloading') })
29 url: server.url,
30 accessToken: server.accessToken,
31 path: getPluginTestPath('-unloading')
32 })
33 }) 27 })
34 28
35 it('Should return a numeric value', async function () { 29 it('Should return a numeric value', async function () {
36 const res = await makeGetRequest({ 30 const res = await makeGetRequest({
37 url: server.url, 31 url: server.url,
38 path: requestPath, 32 path: requestPath,
39 statusCodeExpected: HttpStatusCode.OK_200 33 expectedStatus: HttpStatusCode.OK_200
40 }) 34 })
41 35
42 expect(res.body.message).to.match(/^\d+$/) 36 expect(res.body.message).to.match(/^\d+$/)
@@ -47,36 +41,29 @@ describe('Test plugins module unloading', function () {
47 const res = await makeGetRequest({ 41 const res = await makeGetRequest({
48 url: server.url, 42 url: server.url,
49 path: requestPath, 43 path: requestPath,
50 statusCodeExpected: HttpStatusCode.OK_200 44 expectedStatus: HttpStatusCode.OK_200
51 }) 45 })
52 46
53 expect(res.body.message).to.be.equal(value) 47 expect(res.body.message).to.be.equal(value)
54 }) 48 })
55 49
56 it('Should uninstall the plugin and free the route', async function () { 50 it('Should uninstall the plugin and free the route', async function () {
57 await uninstallPlugin({ 51 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-unloading' })
58 url: server.url,
59 accessToken: server.accessToken,
60 npmName: 'peertube-plugin-test-unloading'
61 })
62 52
63 await makeGetRequest({ 53 await makeGetRequest({
64 url: server.url, 54 url: server.url,
65 path: requestPath, 55 path: requestPath,
66 statusCodeExpected: HttpStatusCode.NOT_FOUND_404 56 expectedStatus: HttpStatusCode.NOT_FOUND_404
67 }) 57 })
68 }) 58 })
69 59
70 it('Should return a different numeric value', async function () { 60 it('Should return a different numeric value', async function () {
71 await installPlugin({ 61 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-unloading') })
72 url: server.url, 62
73 accessToken: server.accessToken,
74 path: getPluginTestPath('-unloading')
75 })
76 const res = await makeGetRequest({ 63 const res = await makeGetRequest({
77 url: server.url, 64 url: server.url,
78 path: requestPath, 65 path: requestPath,
79 statusCodeExpected: HttpStatusCode.OK_200 66 expectedStatus: HttpStatusCode.OK_200
80 }) 67 })
81 68
82 expect(res.body.message).to.match(/^\d+$/) 69 expect(res.body.message).to.match(/^\d+$/)
diff --git a/server/tests/plugins/translations.ts b/server/tests/plugins/translations.ts
index 9fd2ba1c5..8b25c6b75 100644
--- a/server/tests/plugins/translations.ts
+++ b/server/tests/plugins/translations.ts
@@ -1,50 +1,37 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 4import * as chai from 'chai'
6import { 5import { cleanupTests, createSingleServer, PeerTubeServer, PluginsCommand, setAccessTokensToServers } from '@shared/extra-utils'
7 getPluginTestPath,
8 getPluginTranslations,
9 installPlugin,
10 setAccessTokensToServers,
11 uninstallPlugin
12} from '../../../shared/extra-utils'
13 6
14const expect = chai.expect 7const expect = chai.expect
15 8
16describe('Test plugin translations', function () { 9describe('Test plugin translations', function () {
17 let server: ServerInfo 10 let server: PeerTubeServer
11 let command: PluginsCommand
18 12
19 before(async function () { 13 before(async function () {
20 this.timeout(30000) 14 this.timeout(30000)
21 15
22 server = await flushAndRunServer(1) 16 server = await createSingleServer(1)
23 await setAccessTokensToServers([ server ]) 17 await setAccessTokensToServers([ server ])
24 18
25 await installPlugin({ 19 command = server.plugins
26 url: server.url,
27 accessToken: server.accessToken,
28 path: getPluginTestPath()
29 })
30 20
31 await installPlugin({ 21 await command.install({ path: PluginsCommand.getPluginTestPath() })
32 url: server.url, 22 await command.install({ path: PluginsCommand.getPluginTestPath('-filter-translations') })
33 accessToken: server.accessToken,
34 path: getPluginTestPath('-filter-translations')
35 })
36 }) 23 })
37 24
38 it('Should not have translations for locale pt', async function () { 25 it('Should not have translations for locale pt', async function () {
39 const res = await getPluginTranslations({ url: server.url, locale: 'pt' }) 26 const body = await command.getTranslations({ locale: 'pt' })
40 27
41 expect(res.body).to.deep.equal({}) 28 expect(body).to.deep.equal({})
42 }) 29 })
43 30
44 it('Should have translations for locale fr', async function () { 31 it('Should have translations for locale fr', async function () {
45 const res = await getPluginTranslations({ url: server.url, locale: 'fr-FR' }) 32 const body = await command.getTranslations({ locale: 'fr-FR' })
46 33
47 expect(res.body).to.deep.equal({ 34 expect(body).to.deep.equal({
48 'peertube-plugin-test': { 35 'peertube-plugin-test': {
49 Hi: 'Coucou' 36 Hi: 'Coucou'
50 }, 37 },
@@ -55,9 +42,9 @@ describe('Test plugin translations', function () {
55 }) 42 })
56 43
57 it('Should have translations of locale it', async function () { 44 it('Should have translations of locale it', async function () {
58 const res = await getPluginTranslations({ url: server.url, locale: 'it-IT' }) 45 const body = await command.getTranslations({ locale: 'it-IT' })
59 46
60 expect(res.body).to.deep.equal({ 47 expect(body).to.deep.equal({
61 'peertube-plugin-test-filter-translations': { 48 'peertube-plugin-test-filter-translations': {
62 'Hello world': 'Ciao, mondo!' 49 'Hello world': 'Ciao, mondo!'
63 } 50 }
@@ -65,12 +52,12 @@ describe('Test plugin translations', function () {
65 }) 52 })
66 53
67 it('Should remove the plugin and remove the locales', async function () { 54 it('Should remove the plugin and remove the locales', async function () {
68 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-filter-translations' }) 55 await command.uninstall({ npmName: 'peertube-plugin-test-filter-translations' })
69 56
70 { 57 {
71 const res = await getPluginTranslations({ url: server.url, locale: 'fr-FR' }) 58 const body = await command.getTranslations({ locale: 'fr-FR' })
72 59
73 expect(res.body).to.deep.equal({ 60 expect(body).to.deep.equal({
74 'peertube-plugin-test': { 61 'peertube-plugin-test': {
75 Hi: 'Coucou' 62 Hi: 'Coucou'
76 } 63 }
@@ -78,9 +65,9 @@ describe('Test plugin translations', function () {
78 } 65 }
79 66
80 { 67 {
81 const res = await getPluginTranslations({ url: server.url, locale: 'it-IT' }) 68 const body = await command.getTranslations({ locale: 'it-IT' })
82 69
83 expect(res.body).to.deep.equal({}) 70 expect(body).to.deep.equal({})
84 } 71 }
85 }) 72 })
86 73
diff --git a/server/tests/plugins/video-constants.ts b/server/tests/plugins/video-constants.ts
index eb014c596..19cba6c2c 100644
--- a/server/tests/plugins/video-constants.ts
+++ b/server/tests/plugins/video-constants.ts
@@ -1,44 +1,33 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import * as chai from 'chai'
4import 'mocha' 3import 'mocha'
5import { cleanupTests, flushAndRunServer, ServerInfo } from '../../../shared/extra-utils/server/servers' 4import * as chai from 'chai'
6import { 5import {
7 createVideoPlaylist, 6 cleanupTests,
8 getPluginTestPath, 7 createSingleServer,
9 getVideo, 8 makeGetRequest,
10 getVideoCategories, 9 PeerTubeServer,
11 getVideoLanguages, 10 PluginsCommand,
12 getVideoLicences, getVideoPlaylistPrivacies, getVideoPrivacies, 11 setAccessTokensToServers
13 installPlugin, 12} from '@shared/extra-utils'
14 setAccessTokensToServers, 13import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models'
15 uninstallPlugin,
16 uploadVideo
17} from '../../../shared/extra-utils'
18import { VideoDetails, VideoPlaylistPrivacy } from '../../../shared/models/videos'
19import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
20 14
21const expect = chai.expect 15const expect = chai.expect
22 16
23describe('Test plugin altering video constants', function () { 17describe('Test plugin altering video constants', function () {
24 let server: ServerInfo 18 let server: PeerTubeServer
25 19
26 before(async function () { 20 before(async function () {
27 this.timeout(30000) 21 this.timeout(30000)
28 22
29 server = await flushAndRunServer(1) 23 server = await createSingleServer(1)
30 await setAccessTokensToServers([ server ]) 24 await setAccessTokensToServers([ server ])
31 25
32 await installPlugin({ 26 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-video-constants') })
33 url: server.url,
34 accessToken: server.accessToken,
35 path: getPluginTestPath('-video-constants')
36 })
37 }) 27 })
38 28
39 it('Should have updated languages', async function () { 29 it('Should have updated languages', async function () {
40 const res = await getVideoLanguages(server.url) 30 const languages = await server.videos.getLanguages()
41 const languages = res.body
42 31
43 expect(languages['en']).to.not.exist 32 expect(languages['en']).to.not.exist
44 expect(languages['fr']).to.not.exist 33 expect(languages['fr']).to.not.exist
@@ -49,8 +38,7 @@ describe('Test plugin altering video constants', function () {
49 }) 38 })
50 39
51 it('Should have updated categories', async function () { 40 it('Should have updated categories', async function () {
52 const res = await getVideoCategories(server.url) 41 const categories = await server.videos.getCategories()
53 const categories = res.body
54 42
55 expect(categories[1]).to.not.exist 43 expect(categories[1]).to.not.exist
56 expect(categories[2]).to.not.exist 44 expect(categories[2]).to.not.exist
@@ -60,8 +48,7 @@ describe('Test plugin altering video constants', function () {
60 }) 48 })
61 49
62 it('Should have updated licences', async function () { 50 it('Should have updated licences', async function () {
63 const res = await getVideoLicences(server.url) 51 const licences = await server.videos.getLicences()
64 const licences = res.body
65 52
66 expect(licences[1]).to.not.exist 53 expect(licences[1]).to.not.exist
67 expect(licences[7]).to.not.exist 54 expect(licences[7]).to.not.exist
@@ -71,8 +58,7 @@ describe('Test plugin altering video constants', function () {
71 }) 58 })
72 59
73 it('Should have updated video privacies', async function () { 60 it('Should have updated video privacies', async function () {
74 const res = await getVideoPrivacies(server.url) 61 const privacies = await server.videos.getPrivacies()
75 const privacies = res.body
76 62
77 expect(privacies[1]).to.exist 63 expect(privacies[1]).to.exist
78 expect(privacies[2]).to.not.exist 64 expect(privacies[2]).to.not.exist
@@ -81,8 +67,7 @@ describe('Test plugin altering video constants', function () {
81 }) 67 })
82 68
83 it('Should have updated playlist privacies', async function () { 69 it('Should have updated playlist privacies', async function () {
84 const res = await getVideoPlaylistPrivacies(server.url) 70 const playlistPrivacies = await server.playlists.getPrivacies()
85 const playlistPrivacies = res.body
86 71
87 expect(playlistPrivacies[1]).to.exist 72 expect(playlistPrivacies[1]).to.exist
88 expect(playlistPrivacies[2]).to.exist 73 expect(playlistPrivacies[2]).to.exist
@@ -90,38 +75,30 @@ describe('Test plugin altering video constants', function () {
90 }) 75 })
91 76
92 it('Should not be able to create a video with this privacy', async function () { 77 it('Should not be able to create a video with this privacy', async function () {
93 const attrs = { name: 'video', privacy: 2 } 78 const attributes = { name: 'video', privacy: 2 }
94 await uploadVideo(server.url, server.accessToken, attrs, HttpStatusCode.BAD_REQUEST_400) 79 await server.videos.upload({ attributes, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
95 }) 80 })
96 81
97 it('Should not be able to create a video with this privacy', async function () { 82 it('Should not be able to create a video with this privacy', async function () {
98 const attrs = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE } 83 const attributes = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE }
99 await createVideoPlaylist({ 84 await server.playlists.create({ attributes, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
100 url: server.url,
101 token: server.accessToken,
102 playlistAttrs: attrs,
103 expectedStatus: HttpStatusCode.BAD_REQUEST_400
104 })
105 }) 85 })
106 86
107 it('Should be able to upload a video with these values', async function () { 87 it('Should be able to upload a video with these values', async function () {
108 const attrs = { name: 'video', category: 42, licence: 42, language: 'al_bhed2' } 88 const attributes = { name: 'video', category: 42, licence: 42, language: 'al_bhed2' }
109 const resUpload = await uploadVideo(server.url, server.accessToken, attrs) 89 const { uuid } = await server.videos.upload({ attributes })
110 90
111 const res = await getVideo(server.url, resUpload.body.video.uuid) 91 const video = await server.videos.get({ id: uuid })
112
113 const video: VideoDetails = res.body
114 expect(video.language.label).to.equal('Al Bhed 2') 92 expect(video.language.label).to.equal('Al Bhed 2')
115 expect(video.licence.label).to.equal('Best licence') 93 expect(video.licence.label).to.equal('Best licence')
116 expect(video.category.label).to.equal('Best category') 94 expect(video.category.label).to.equal('Best category')
117 }) 95 })
118 96
119 it('Should uninstall the plugin and reset languages, categories, licences and privacies', async function () { 97 it('Should uninstall the plugin and reset languages, categories, licences and privacies', async function () {
120 await uninstallPlugin({ url: server.url, accessToken: server.accessToken, npmName: 'peertube-plugin-test-video-constants' }) 98 await server.plugins.uninstall({ npmName: 'peertube-plugin-test-video-constants' })
121 99
122 { 100 {
123 const res = await getVideoLanguages(server.url) 101 const languages = await server.videos.getLanguages()
124 const languages = res.body
125 102
126 expect(languages['en']).to.equal('English') 103 expect(languages['en']).to.equal('English')
127 expect(languages['fr']).to.equal('French') 104 expect(languages['fr']).to.equal('French')
@@ -132,8 +109,7 @@ describe('Test plugin altering video constants', function () {
132 } 109 }
133 110
134 { 111 {
135 const res = await getVideoCategories(server.url) 112 const categories = await server.videos.getCategories()
136 const categories = res.body
137 113
138 expect(categories[1]).to.equal('Music') 114 expect(categories[1]).to.equal('Music')
139 expect(categories[2]).to.equal('Films') 115 expect(categories[2]).to.equal('Films')
@@ -143,8 +119,7 @@ describe('Test plugin altering video constants', function () {
143 } 119 }
144 120
145 { 121 {
146 const res = await getVideoLicences(server.url) 122 const licences = await server.videos.getLicences()
147 const licences = res.body
148 123
149 expect(licences[1]).to.equal('Attribution') 124 expect(licences[1]).to.equal('Attribution')
150 expect(licences[7]).to.equal('Public Domain Dedication') 125 expect(licences[7]).to.equal('Public Domain Dedication')
@@ -154,8 +129,7 @@ describe('Test plugin altering video constants', function () {
154 } 129 }
155 130
156 { 131 {
157 const res = await getVideoPrivacies(server.url) 132 const privacies = await server.videos.getPrivacies()
158 const privacies = res.body
159 133
160 expect(privacies[1]).to.exist 134 expect(privacies[1]).to.exist
161 expect(privacies[2]).to.exist 135 expect(privacies[2]).to.exist
@@ -164,8 +138,7 @@ describe('Test plugin altering video constants', function () {
164 } 138 }
165 139
166 { 140 {
167 const res = await getVideoPlaylistPrivacies(server.url) 141 const playlistPrivacies = await server.playlists.getPrivacies()
168 const playlistPrivacies = res.body
169 142
170 expect(playlistPrivacies[1]).to.exist 143 expect(playlistPrivacies[1]).to.exist
171 expect(playlistPrivacies[2]).to.exist 144 expect(playlistPrivacies[2]).to.exist
@@ -173,6 +146,37 @@ describe('Test plugin altering video constants', function () {
173 } 146 }
174 }) 147 })
175 148
149 it('Should be able to reset categories', async function () {
150 await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-video-constants') })
151
152 {
153 const categories = await server.videos.getCategories()
154
155 expect(categories[1]).to.not.exist
156 expect(categories[2]).to.not.exist
157
158 expect(categories[42]).to.exist
159 expect(categories[43]).to.exist
160 }
161
162 await makeGetRequest({
163 url: server.url,
164 token: server.accessToken,
165 path: '/plugins/test-video-constants/router/reset-categories',
166 expectedStatus: HttpStatusCode.NO_CONTENT_204
167 })
168
169 {
170 const categories = await server.videos.getCategories()
171
172 expect(categories[1]).to.exist
173 expect(categories[2]).to.exist
174
175 expect(categories[42]).to.not.exist
176 expect(categories[43]).to.not.exist
177 }
178 })
179
176 after(async function () { 180 after(async function () {
177 await cleanupTests([ server ]) 181 await cleanupTests([ server ])
178 }) 182 })
diff --git a/server/tools/cli.ts b/server/tools/cli.ts
index 7b94306cd..52e6ea593 100644
--- a/server/tools/cli.ts
+++ b/server/tools/cli.ts
@@ -1,14 +1,11 @@
1import { Command } from 'commander'
1import { Netrc } from 'netrc-parser' 2import { Netrc } from 'netrc-parser'
2import { getAppNumber, isTestInstance } from '../helpers/core-utils'
3import { join } from 'path' 3import { join } from 'path'
4import { root } from '../../shared/extra-utils/miscs/miscs'
5import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels'
6import { VideoChannel, VideoPrivacy } from '../../shared/models/videos'
7import { createLogger, format, transports } from 'winston' 4import { createLogger, format, transports } from 'winston'
8import { getMyUserInformation } from '@shared/extra-utils/users/users' 5import { PeerTubeServer } from '@shared/extra-utils'
9import { User, UserRole } from '@shared/models' 6import { UserRole } from '@shared/models'
10import { getAccessToken } from '@shared/extra-utils/users/login' 7import { VideoPrivacy } from '../../shared/models/videos'
11import { Command } from 'commander' 8import { getAppNumber, isTestInstance, root } from '../helpers/core-utils'
12 9
13let configName = 'PeerTube/CLI' 10let configName = 'PeerTube/CLI'
14if (isTestInstance()) configName += `-${getAppNumber()}` 11if (isTestInstance()) configName += `-${getAppNumber()}`
@@ -17,17 +14,16 @@ const config = require('application-config')(configName)
17 14
18const version = require('../../../package.json').version 15const version = require('../../../package.json').version
19 16
20async function getAdminTokenOrDie (url: string, username: string, password: string) { 17async function getAdminTokenOrDie (server: PeerTubeServer, username: string, password: string) {
21 const accessToken = await getAccessToken(url, username, password) 18 const token = await server.login.getAccessToken(username, password)
22 const resMe = await getMyUserInformation(url, accessToken) 19 const me = await server.users.getMyInfo({ token })
23 const me: User = resMe.body
24 20
25 if (me.role !== UserRole.ADMINISTRATOR) { 21 if (me.role !== UserRole.ADMINISTRATOR) {
26 console.error('You must be an administrator.') 22 console.error('You must be an administrator.')
27 process.exit(-1) 23 process.exit(-1)
28 } 24 }
29 25
30 return accessToken 26 return token
31} 27}
32 28
33interface Settings { 29interface Settings {
@@ -128,7 +124,7 @@ function buildCommonVideoOptions (command: Command) {
128 .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info') 124 .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info')
129} 125}
130 126
131async function buildVideoAttributesFromCommander (url: string, command: Command, defaultAttributes: any = {}) { 127async function buildVideoAttributesFromCommander (server: PeerTubeServer, command: Command, defaultAttributes: any = {}) {
132 const options = command.opts() 128 const options = command.opts()
133 129
134 const defaultBooleanAttributes = { 130 const defaultBooleanAttributes = {
@@ -164,8 +160,7 @@ async function buildVideoAttributesFromCommander (url: string, command: Command,
164 Object.assign(videoAttributes, booleanAttributes) 160 Object.assign(videoAttributes, booleanAttributes)
165 161
166 if (options.channelName) { 162 if (options.channelName) {
167 const res = await getVideoChannel(url, options.channelName) 163 const videoChannel = await server.channels.get({ channelName: options.channelName })
168 const videoChannel: VideoChannel = res.body
169 164
170 Object.assign(videoAttributes, { channelId: videoChannel.id }) 165 Object.assign(videoAttributes, { channelId: videoChannel.id })
171 166
@@ -184,6 +179,19 @@ function getServerCredentials (program: Command) {
184 }) 179 })
185} 180}
186 181
182function buildServer (url: string) {
183 return new PeerTubeServer({ url })
184}
185
186async function assignToken (server: PeerTubeServer, username: string, password: string) {
187 const bodyClient = await server.login.getClient()
188 const client = { id: bodyClient.client_id, secret: bodyClient.client_secret }
189
190 const body = await server.login.login({ client, user: { username, password } })
191
192 server.accessToken = body.access_token
193}
194
187function getLogger (logLevel = 'info') { 195function getLogger (logLevel = 'info') {
188 const logLevels = { 196 const logLevels = {
189 0: 0, 197 0: 0,
@@ -230,5 +238,7 @@ export {
230 buildCommonVideoOptions, 238 buildCommonVideoOptions,
231 buildVideoAttributesFromCommander, 239 buildVideoAttributesFromCommander,
232 240
233 getAdminTokenOrDie 241 getAdminTokenOrDie,
242 buildServer,
243 assignToken
234} 244}
diff --git a/server/tools/peertube-auth.ts b/server/tools/peertube-auth.ts
index 1934e7986..b9f4ef4f8 100644
--- a/server/tools/peertube-auth.ts
+++ b/server/tools/peertube-auth.ts
@@ -5,9 +5,8 @@ registerTSPaths()
5 5
6import { OptionValues, program } from 'commander' 6import { OptionValues, program } from 'commander'
7import * as prompt from 'prompt' 7import * as prompt from 'prompt'
8import { getNetrc, getSettings, writeSettings } from './cli' 8import { assignToken, buildServer, getNetrc, getSettings, writeSettings } from './cli'
9import { isUserUsernameValid } from '../helpers/custom-validators/users' 9import { isUserUsernameValid } from '../helpers/custom-validators/users'
10import { getAccessToken } from '../../shared/extra-utils'
11import * as CliTable3 from 'cli-table3' 10import * as CliTable3 from 'cli-table3'
12 11
13async function delInstance (url: string) { 12async function delInstance (url: string) {
@@ -97,7 +96,8 @@ program
97 // @see https://github.com/Chocobozzz/PeerTube/issues/3520 96 // @see https://github.com/Chocobozzz/PeerTube/issues/3520
98 result.url = stripExtraneousFromPeerTubeUrl(result.url) 97 result.url = stripExtraneousFromPeerTubeUrl(result.url)
99 98
100 await getAccessToken(result.url, result.username, result.password) 99 const server = buildServer(result.url)
100 await assignToken(server, result.username, result.password)
101 } catch (err) { 101 } catch (err) {
102 console.error(err.message) 102 console.error(err.message)
103 process.exit(-1) 103 process.exit(-1)
diff --git a/server/tools/peertube-get-access-token.ts b/server/tools/peertube-get-access-token.ts
index 9488eba0e..a67de9180 100644
--- a/server/tools/peertube-get-access-token.ts
+++ b/server/tools/peertube-get-access-token.ts
@@ -2,7 +2,7 @@ import { registerTSPaths } from '../helpers/register-ts-paths'
2registerTSPaths() 2registerTSPaths()
3 3
4import { program } from 'commander' 4import { program } from 'commander'
5import { getClient, Server, serverLogin } from '../../shared/extra-utils' 5import { assignToken, buildServer } from './cli'
6 6
7program 7program
8 .option('-u, --url <url>', 'Server url') 8 .option('-u, --url <url>', 'Server url')
@@ -24,24 +24,11 @@ if (
24 process.exit(-1) 24 process.exit(-1)
25} 25}
26 26
27getClient(options.url) 27const server = buildServer(options.url)
28 .then(res => {
29 const server = {
30 url: options.url,
31 user: {
32 username: options.username,
33 password: options.password
34 },
35 client: {
36 id: res.body.client_id,
37 secret: res.body.client_secret
38 }
39 } as Server
40 28
41 return serverLogin(server) 29assignToken(server, options.username, options.password)
42 }) 30 .then(() => {
43 .then(accessToken => { 31 console.log(server.accessToken)
44 console.log(accessToken)
45 process.exit(0) 32 process.exit(0)
46 }) 33 })
47 .catch(err => { 34 .catch(err => {
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts
index 101a95b2a..52aae3d2c 100644
--- a/server/tools/peertube-import-videos.ts
+++ b/server/tools/peertube-import-videos.ts
@@ -8,17 +8,19 @@ import { truncate } from 'lodash'
8import { join } from 'path' 8import { join } from 'path'
9import * as prompt from 'prompt' 9import * as prompt from 'prompt'
10import { promisify } from 'util' 10import { promisify } from 'util'
11import { advancedVideosSearch, getClient, getVideoCategories, login, uploadVideo } from '../../shared/extra-utils/index' 11import { YoutubeDL } from '@server/helpers/youtube-dl'
12import { sha256 } from '../helpers/core-utils' 12import { sha256 } from '../helpers/core-utils'
13import { doRequestAndSaveToFile } from '../helpers/requests' 13import { doRequestAndSaveToFile } from '../helpers/requests'
14import { CONSTRAINTS_FIELDS } from '../initializers/constants' 14import { CONSTRAINTS_FIELDS } from '../initializers/constants'
15import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' 15import {
16import { YoutubeDL } from '@server/helpers/youtube-dl' 16 assignToken,
17 17 buildCommonVideoOptions,
18type UserInfo = { 18 buildServer,
19 username: string 19 buildVideoAttributesFromCommander,
20 password: string 20 getLogger,
21} 21 getServerCredentials
22} from './cli'
23import { PeerTubeServer } from '@shared/extra-utils'
22 24
23const processOptions = { 25const processOptions = {
24 maxBuffer: Infinity 26 maxBuffer: Infinity
@@ -62,17 +64,13 @@ getServerCredentials(command)
62 url = normalizeTargetUrl(url) 64 url = normalizeTargetUrl(url)
63 options.targetUrl = normalizeTargetUrl(options.targetUrl) 65 options.targetUrl = normalizeTargetUrl(options.targetUrl)
64 66
65 const user = { username, password } 67 run(url, username, password)
66
67 run(url, user)
68 .catch(err => exitError(err)) 68 .catch(err => exitError(err))
69 }) 69 })
70 .catch(err => console.error(err)) 70 .catch(err => console.error(err))
71 71
72async function run (url: string, user: UserInfo) { 72async function run (url: string, username: string, password: string) {
73 if (!user.password) { 73 if (!password) password = await promptPassword()
74 user.password = await promptPassword()
75 }
76 74
77 const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL() 75 const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL()
78 76
@@ -111,7 +109,8 @@ async function run (url: string, user: UserInfo) {
111 await processVideo({ 109 await processVideo({
112 cwd: options.tmpdir, 110 cwd: options.tmpdir,
113 url, 111 url,
114 user, 112 username,
113 password,
115 youtubeInfo: info 114 youtubeInfo: info
116 }) 115 })
117 } catch (err) { 116 } catch (err) {
@@ -119,17 +118,18 @@ async function run (url: string, user: UserInfo) {
119 } 118 }
120 } 119 }
121 120
122 log.info('Video/s for user %s imported: %s', user.username, options.targetUrl) 121 log.info('Video/s for user %s imported: %s', username, options.targetUrl)
123 process.exit(0) 122 process.exit(0)
124} 123}
125 124
126async function processVideo (parameters: { 125async function processVideo (parameters: {
127 cwd: string 126 cwd: string
128 url: string 127 url: string
129 user: { username: string, password: string } 128 username: string
129 password: string
130 youtubeInfo: any 130 youtubeInfo: any
131}) { 131}) {
132 const { youtubeInfo, cwd, url, user } = parameters 132 const { youtubeInfo, cwd, url, username, password } = parameters
133 const youtubeDL = new YoutubeDL('', []) 133 const youtubeDL = new YoutubeDL('', [])
134 134
135 log.debug('Fetching object.', youtubeInfo) 135 log.debug('Fetching object.', youtubeInfo)
@@ -138,22 +138,29 @@ async function processVideo (parameters: {
138 log.debug('Fetched object.', videoInfo) 138 log.debug('Fetched object.', videoInfo)
139 139
140 const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo) 140 const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo)
141
141 if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) { 142 if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) {
142 log.info('Video "%s" has been published before "%s", don\'t upload it.\n', 143 log.info('Video "%s" has been published before "%s", don\'t upload it.\n', videoInfo.title, formatDate(options.since))
143 videoInfo.title, formatDate(options.since))
144 return 144 return
145 } 145 }
146
146 if (options.until && originallyPublishedAt && originallyPublishedAt.getTime() > options.until.getTime()) { 147 if (options.until && originallyPublishedAt && originallyPublishedAt.getTime() > options.until.getTime()) {
147 log.info('Video "%s" has been published after "%s", don\'t upload it.\n', 148 log.info('Video "%s" has been published after "%s", don\'t upload it.\n', videoInfo.title, formatDate(options.until))
148 videoInfo.title, formatDate(options.until))
149 return 149 return
150 } 150 }
151 151
152 const result = await advancedVideosSearch(url, { search: videoInfo.title, sort: '-match', searchTarget: 'local' }) 152 const server = buildServer(url)
153 const { data } = await server.search.advancedVideoSearch({
154 search: {
155 search: videoInfo.title,
156 sort: '-match',
157 searchTarget: 'local'
158 }
159 })
153 160
154 log.info('############################################################\n') 161 log.info('############################################################\n')
155 162
156 if (result.body.data.find(v => v.name === videoInfo.title)) { 163 if (data.find(v => v.name === videoInfo.title)) {
157 log.info('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title) 164 log.info('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title)
158 return 165 return
159 } 166 }
@@ -172,7 +179,8 @@ async function processVideo (parameters: {
172 youtubeDL, 179 youtubeDL,
173 cwd, 180 cwd,
174 url, 181 url,
175 user, 182 username,
183 password,
176 videoInfo: normalizeObject(videoInfo), 184 videoInfo: normalizeObject(videoInfo),
177 videoPath: path 185 videoPath: path
178 }) 186 })
@@ -187,11 +195,15 @@ async function uploadVideoOnPeerTube (parameters: {
187 videoPath: string 195 videoPath: string
188 cwd: string 196 cwd: string
189 url: string 197 url: string
190 user: { username: string, password: string } 198 username: string
199 password: string
191}) { 200}) {
192 const { youtubeDL, videoInfo, videoPath, cwd, url, user } = parameters 201 const { youtubeDL, videoInfo, videoPath, cwd, url, username, password } = parameters
193 202
194 const category = await getCategory(videoInfo.categories, url) 203 const server = buildServer(url)
204 await assignToken(server, username, password)
205
206 const category = await getCategory(server, videoInfo.categories)
195 const licence = getLicence(videoInfo.license) 207 const licence = getLicence(videoInfo.license)
196 let tags = [] 208 let tags = []
197 if (Array.isArray(videoInfo.tags)) { 209 if (Array.isArray(videoInfo.tags)) {
@@ -223,28 +235,28 @@ async function uploadVideoOnPeerTube (parameters: {
223 tags 235 tags
224 } 236 }
225 237
226 const videoAttributes = await buildVideoAttributesFromCommander(url, program, defaultAttributes) 238 const baseAttributes = await buildVideoAttributesFromCommander(server, program, defaultAttributes)
239
240 const attributes = {
241 ...baseAttributes,
227 242
228 Object.assign(videoAttributes, {
229 originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null, 243 originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null,
230 thumbnailfile, 244 thumbnailfile,
231 previewfile: thumbnailfile, 245 previewfile: thumbnailfile,
232 fixture: videoPath 246 fixture: videoPath
233 }) 247 }
234
235 log.info('\nUploading on PeerTube video "%s".', videoAttributes.name)
236 248
237 let accessToken = await getAccessTokenOrDie(url, user) 249 log.info('\nUploading on PeerTube video "%s".', attributes.name)
238 250
239 try { 251 try {
240 await uploadVideo(url, accessToken, videoAttributes) 252 await server.videos.upload({ attributes })
241 } catch (err) { 253 } catch (err) {
242 if (err.message.indexOf('401') !== -1) { 254 if (err.message.indexOf('401') !== -1) {
243 log.info('Got 401 Unauthorized, token may have expired, renewing token and retry.') 255 log.info('Got 401 Unauthorized, token may have expired, renewing token and retry.')
244 256
245 accessToken = await getAccessTokenOrDie(url, user) 257 server.accessToken = await server.login.getAccessToken(username, password)
246 258
247 await uploadVideo(url, accessToken, videoAttributes) 259 await server.videos.upload({ attributes })
248 } else { 260 } else {
249 exitError(err.message) 261 exitError(err.message)
250 } 262 }
@@ -253,20 +265,19 @@ async function uploadVideoOnPeerTube (parameters: {
253 await remove(videoPath) 265 await remove(videoPath)
254 if (thumbnailfile) await remove(thumbnailfile) 266 if (thumbnailfile) await remove(thumbnailfile)
255 267
256 log.warn('Uploaded video "%s"!\n', videoAttributes.name) 268 log.warn('Uploaded video "%s"!\n', attributes.name)
257} 269}
258 270
259/* ---------------------------------------------------------- */ 271/* ---------------------------------------------------------- */
260 272
261async function getCategory (categories: string[], url: string) { 273async function getCategory (server: PeerTubeServer, categories: string[]) {
262 if (!categories) return undefined 274 if (!categories) return undefined
263 275
264 const categoryString = categories[0] 276 const categoryString = categories[0]
265 277
266 if (categoryString === 'News & Politics') return 11 278 if (categoryString === 'News & Politics') return 11
267 279
268 const res = await getVideoCategories(url) 280 const categoriesServer = await server.videos.getCategories()
269 const categoriesServer = res.body
270 281
271 for (const key of Object.keys(categoriesServer)) { 282 for (const key of Object.keys(categoriesServer)) {
272 const categoryServer = categoriesServer[key] 283 const categoryServer = categoriesServer[key]
@@ -362,21 +373,6 @@ async function promptPassword () {
362 }) 373 })
363} 374}
364 375
365async function getAccessTokenOrDie (url: string, user: UserInfo) {
366 const resClient = await getClient(url)
367 const client = {
368 id: resClient.body.client_id,
369 secret: resClient.body.client_secret
370 }
371
372 try {
373 const res = await login(url, client, user)
374 return res.body.access_token
375 } catch (err) {
376 exitError('Cannot authenticate. Please check your username/password.')
377 }
378}
379
380function parseDate (dateAsStr: string): Date { 376function parseDate (dateAsStr: string): Date {
381 if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) { 377 if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) {
382 exitError(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`) 378 exitError(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`)
diff --git a/server/tools/peertube-plugins.ts b/server/tools/peertube-plugins.ts
index 54ea1264d..d9c285115 100644
--- a/server/tools/peertube-plugins.ts
+++ b/server/tools/peertube-plugins.ts
@@ -4,9 +4,8 @@ import { registerTSPaths } from '../helpers/register-ts-paths'
4registerTSPaths() 4registerTSPaths()
5 5
6import { program, Command, OptionValues } from 'commander' 6import { program, Command, OptionValues } from 'commander'
7import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' 7import { assignToken, buildServer, getServerCredentials } from './cli'
8import { getAdminTokenOrDie, getServerCredentials } from './cli' 8import { PluginType } from '../../shared/models'
9import { PeerTubePlugin, PluginType } from '../../shared/models'
10import { isAbsolute } from 'path' 9import { isAbsolute } from 'path'
11import * as CliTable3 from 'cli-table3' 10import * as CliTable3 from 'cli-table3'
12 11
@@ -63,28 +62,21 @@ program.parse(process.argv)
63 62
64async function pluginsListCLI (command: Command, options: OptionValues) { 63async function pluginsListCLI (command: Command, options: OptionValues) {
65 const { url, username, password } = await getServerCredentials(command) 64 const { url, username, password } = await getServerCredentials(command)
66 const accessToken = await getAdminTokenOrDie(url, username, password) 65 const server = buildServer(url)
66 await assignToken(server, username, password)
67 67
68 let pluginType: PluginType 68 let pluginType: PluginType
69 if (options.onlyThemes) pluginType = PluginType.THEME 69 if (options.onlyThemes) pluginType = PluginType.THEME
70 if (options.onlyPlugins) pluginType = PluginType.PLUGIN 70 if (options.onlyPlugins) pluginType = PluginType.PLUGIN
71 71
72 const res = await listPlugins({ 72 const { data } = await server.plugins.list({ start: 0, count: 100, sort: 'name', pluginType })
73 url,
74 accessToken,
75 start: 0,
76 count: 100,
77 sort: 'name',
78 pluginType
79 })
80 const plugins: PeerTubePlugin[] = res.body.data
81 73
82 const table = new CliTable3({ 74 const table = new CliTable3({
83 head: [ 'name', 'version', 'homepage' ], 75 head: [ 'name', 'version', 'homepage' ],
84 colWidths: [ 50, 10, 50 ] 76 colWidths: [ 50, 10, 50 ]
85 }) as any 77 }) as any
86 78
87 for (const plugin of plugins) { 79 for (const plugin of data) {
88 const npmName = plugin.type === PluginType.PLUGIN 80 const npmName = plugin.type === PluginType.PLUGIN
89 ? 'peertube-plugin-' + plugin.name 81 ? 'peertube-plugin-' + plugin.name
90 : 'peertube-theme-' + plugin.name 82 : 'peertube-theme-' + plugin.name
@@ -113,15 +105,11 @@ async function installPluginCLI (command: Command, options: OptionValues) {
113 } 105 }
114 106
115 const { url, username, password } = await getServerCredentials(command) 107 const { url, username, password } = await getServerCredentials(command)
116 const accessToken = await getAdminTokenOrDie(url, username, password) 108 const server = buildServer(url)
109 await assignToken(server, username, password)
117 110
118 try { 111 try {
119 await installPlugin({ 112 await server.plugins.install({ npmName: options.npmName, path: options.path })
120 url,
121 accessToken,
122 npmName: options.npmName,
123 path: options.path
124 })
125 } catch (err) { 113 } catch (err) {
126 console.error('Cannot install plugin.', err) 114 console.error('Cannot install plugin.', err)
127 process.exit(-1) 115 process.exit(-1)
@@ -144,15 +132,11 @@ async function updatePluginCLI (command: Command, options: OptionValues) {
144 } 132 }
145 133
146 const { url, username, password } = await getServerCredentials(command) 134 const { url, username, password } = await getServerCredentials(command)
147 const accessToken = await getAdminTokenOrDie(url, username, password) 135 const server = buildServer(url)
136 await assignToken(server, username, password)
148 137
149 try { 138 try {
150 await updatePlugin({ 139 await server.plugins.update({ npmName: options.npmName, path: options.path })
151 url,
152 accessToken,
153 npmName: options.npmName,
154 path: options.path
155 })
156 } catch (err) { 140 } catch (err) {
157 console.error('Cannot update plugin.', err) 141 console.error('Cannot update plugin.', err)
158 process.exit(-1) 142 process.exit(-1)
@@ -170,14 +154,11 @@ async function uninstallPluginCLI (command: Command, options: OptionValues) {
170 } 154 }
171 155
172 const { url, username, password } = await getServerCredentials(command) 156 const { url, username, password } = await getServerCredentials(command)
173 const accessToken = await getAdminTokenOrDie(url, username, password) 157 const server = buildServer(url)
158 await assignToken(server, username, password)
174 159
175 try { 160 try {
176 await uninstallPlugin({ 161 await server.plugins.uninstall({ npmName: options.npmName })
177 url,
178 accessToken,
179 npmName: options.npmName
180 })
181 } catch (err) { 162 } catch (err) {
182 console.error('Cannot uninstall plugin.', err) 163 console.error('Cannot uninstall plugin.', err)
183 process.exit(-1) 164 process.exit(-1)
diff --git a/server/tools/peertube-redundancy.ts b/server/tools/peertube-redundancy.ts
index 4810deee0..73b026ac8 100644
--- a/server/tools/peertube-redundancy.ts
+++ b/server/tools/peertube-redundancy.ts
@@ -1,17 +1,13 @@
1// eslint-disable @typescript-eslint/no-unnecessary-type-assertion
2
3import { registerTSPaths } from '../helpers/register-ts-paths' 1import { registerTSPaths } from '../helpers/register-ts-paths'
4registerTSPaths() 2registerTSPaths()
5 3
6import { program, Command } from 'commander'
7import { getAdminTokenOrDie, getServerCredentials } from './cli'
8import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
9import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
11import validator from 'validator'
12import * as CliTable3 from 'cli-table3' 4import * as CliTable3 from 'cli-table3'
13import { URL } from 'url' 5import { Command, program } from 'commander'
14import { uniq } from 'lodash' 6import { uniq } from 'lodash'
7import { URL } from 'url'
8import validator from 'validator'
9import { HttpStatusCode, VideoRedundanciesTarget } from '@shared/models'
10import { assignToken, buildServer, getServerCredentials } from './cli'
15 11
16import bytes = require('bytes') 12import bytes = require('bytes')
17 13
@@ -63,15 +59,16 @@ program.parse(process.argv)
63 59
64async function listRedundanciesCLI (target: VideoRedundanciesTarget) { 60async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
65 const { url, username, password } = await getServerCredentials(program) 61 const { url, username, password } = await getServerCredentials(program)
66 const accessToken = await getAdminTokenOrDie(url, username, password) 62 const server = buildServer(url)
63 await assignToken(server, username, password)
67 64
68 const redundancies = await listVideoRedundanciesData(url, accessToken, target) 65 const { data } = await server.redundancy.listVideos({ start: 0, count: 100, sort: 'name', target })
69 66
70 const table = new CliTable3({ 67 const table = new CliTable3({
71 head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ] 68 head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ]
72 }) as any 69 }) as any
73 70
74 for (const redundancy of redundancies) { 71 for (const redundancy of data) {
75 const webtorrentFiles = redundancy.redundancies.files 72 const webtorrentFiles = redundancy.redundancies.files
76 const streamingPlaylists = redundancy.redundancies.streamingPlaylists 73 const streamingPlaylists = redundancy.redundancies.streamingPlaylists
77 74
@@ -106,7 +103,8 @@ async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
106 103
107async function addRedundancyCLI (options: { video: number }, command: Command) { 104async function addRedundancyCLI (options: { video: number }, command: Command) {
108 const { url, username, password } = await getServerCredentials(command) 105 const { url, username, password } = await getServerCredentials(command)
109 const accessToken = await getAdminTokenOrDie(url, username, password) 106 const server = buildServer(url)
107 await assignToken(server, username, password)
110 108
111 if (!options.video || validator.isInt('' + options.video) === false) { 109 if (!options.video || validator.isInt('' + options.video) === false) {
112 console.error('You need to specify the video id to duplicate and it should be a number.\n') 110 console.error('You need to specify the video id to duplicate and it should be a number.\n')
@@ -115,11 +113,7 @@ async function addRedundancyCLI (options: { video: number }, command: Command) {
115 } 113 }
116 114
117 try { 115 try {
118 await addVideoRedundancy({ 116 await server.redundancy.addVideo({ videoId: options.video })
119 url,
120 accessToken,
121 videoId: options.video
122 })
123 117
124 console.log('Video will be duplicated by your instance!') 118 console.log('Video will be duplicated by your instance!')
125 119
@@ -139,7 +133,8 @@ async function addRedundancyCLI (options: { video: number }, command: Command) {
139 133
140async function removeRedundancyCLI (options: { video: number }, command: Command) { 134async function removeRedundancyCLI (options: { video: number }, command: Command) {
141 const { url, username, password } = await getServerCredentials(command) 135 const { url, username, password } = await getServerCredentials(command)
142 const accessToken = await getAdminTokenOrDie(url, username, password) 136 const server = buildServer(url)
137 await assignToken(server, username, password)
143 138
144 if (!options.video || validator.isInt('' + options.video) === false) { 139 if (!options.video || validator.isInt('' + options.video) === false) {
145 console.error('You need to specify the video id to remove from your redundancies.\n') 140 console.error('You need to specify the video id to remove from your redundancies.\n')
@@ -149,12 +144,12 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
149 144
150 const videoId = parseInt(options.video + '', 10) 145 const videoId = parseInt(options.video + '', 10)
151 146
152 let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos') 147 const myVideoRedundancies = await server.redundancy.listVideos({ target: 'my-videos' })
153 let videoRedundancy = redundancies.find(r => videoId === r.id) 148 let videoRedundancy = myVideoRedundancies.data.find(r => videoId === r.id)
154 149
155 if (!videoRedundancy) { 150 if (!videoRedundancy) {
156 redundancies = await listVideoRedundanciesData(url, accessToken, 'remote-videos') 151 const remoteVideoRedundancies = await server.redundancy.listVideos({ target: 'remote-videos' })
157 videoRedundancy = redundancies.find(r => videoId === r.id) 152 videoRedundancy = remoteVideoRedundancies.data.find(r => videoId === r.id)
158 } 153 }
159 154
160 if (!videoRedundancy) { 155 if (!videoRedundancy) {
@@ -168,11 +163,7 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
168 .map(r => r.id) 163 .map(r => r.id)
169 164
170 for (const id of ids) { 165 for (const id of ids) {
171 await removeVideoRedundancy({ 166 await server.redundancy.removeVideo({ redundancyId: id })
172 url,
173 accessToken,
174 redundancyId: id
175 })
176 } 167 }
177 168
178 console.log('Video redundancy removed!') 169 console.log('Video redundancy removed!')
@@ -183,16 +174,3 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
183 process.exit(-1) 174 process.exit(-1)
184 } 175 }
185} 176}
186
187async function listVideoRedundanciesData (url: string, accessToken: string, target: VideoRedundanciesTarget) {
188 const res = await listVideoRedundancies({
189 url,
190 accessToken,
191 start: 0,
192 count: 100,
193 sort: 'name',
194 target
195 })
196
197 return res.body.data as VideoRedundancy[]
198}
diff --git a/server/tools/peertube-upload.ts b/server/tools/peertube-upload.ts
index 02edbd809..01fb1fe8d 100644
--- a/server/tools/peertube-upload.ts
+++ b/server/tools/peertube-upload.ts
@@ -4,9 +4,7 @@ registerTSPaths()
4import { program } from 'commander' 4import { program } from 'commander'
5import { access, constants } from 'fs-extra' 5import { access, constants } from 'fs-extra'
6import { isAbsolute } from 'path' 6import { isAbsolute } from 'path'
7import { getAccessToken } from '../../shared/extra-utils' 7import { assignToken, buildCommonVideoOptions, buildServer, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
8import { uploadVideo } from '../../shared/extra-utils/'
9import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
10 8
11let command = program 9let command = program
12 .name('upload') 10 .name('upload')
@@ -46,22 +44,25 @@ getServerCredentials(command)
46 .catch(err => console.error(err)) 44 .catch(err => console.error(err))
47 45
48async function run (url: string, username: string, password: string) { 46async function run (url: string, username: string, password: string) {
49 const accessToken = await getAccessToken(url, username, password) 47 const server = buildServer(url)
48 await assignToken(server, username, password)
50 49
51 await access(options.file, constants.F_OK) 50 await access(options.file, constants.F_OK)
52 51
53 console.log('Uploading %s video...', options.videoName) 52 console.log('Uploading %s video...', options.videoName)
54 53
55 const videoAttributes = await buildVideoAttributesFromCommander(url, program) 54 const baseAttributes = await buildVideoAttributesFromCommander(server, program)
55
56 const attributes = {
57 ...baseAttributes,
56 58
57 Object.assign(videoAttributes, {
58 fixture: options.file, 59 fixture: options.file,
59 thumbnailfile: options.thumbnail, 60 thumbnailfile: options.thumbnail,
60 previewfile: options.preview 61 previewfile: options.preview
61 }) 62 }
62 63
63 try { 64 try {
64 await uploadVideo(url, accessToken, videoAttributes) 65 await server.videos.upload({ attributes })
65 console.log(`Video ${options.videoName} uploaded.`) 66 console.log(`Video ${options.videoName} uploaded.`)
66 process.exit(0) 67 process.exit(0)
67 } catch (err) { 68 } catch (err) {
diff --git a/server/tools/test.ts b/server/tools/test-live.ts
index fbdbae0b0..50dc04438 100644
--- a/server/tools/test.ts
+++ b/server/tools/test-live.ts
@@ -1,26 +1,23 @@
1import { registerTSPaths } from '../helpers/register-ts-paths'
2registerTSPaths()
3
4import { LiveVideo, LiveVideoCreate, VideoPrivacy } from '@shared/models'
5import { program } from 'commander' 1import { program } from 'commander'
2import { LiveVideoCreate, VideoPrivacy } from '@shared/models'
6import { 3import {
7 createLive, 4 createSingleServer,
8 flushAndRunServer,
9 getLive,
10 killallServers, 5 killallServers,
11 sendRTMPStream, 6 sendRTMPStream,
12 ServerInfo, 7 PeerTubeServer,
13 setAccessTokensToServers, 8 setAccessTokensToServers,
14 setDefaultVideoChannel, 9 setDefaultVideoChannel
15 updateCustomSubConfig
16} from '../../shared/extra-utils' 10} from '../../shared/extra-utils'
11import { registerTSPaths } from '../helpers/register-ts-paths'
12
13registerTSPaths()
17 14
18type CommandType = 'live-mux' | 'live-transcoding' 15type CommandType = 'live-mux' | 'live-transcoding'
19 16
20registerTSPaths() 17registerTSPaths()
21 18
22const command = program 19const command = program
23 .name('test') 20 .name('test-live')
24 .option('-t, --type <type>', 'live-muxing|live-transcoding') 21 .option('-t, --type <type>', 'live-muxing|live-transcoding')
25 .parse(process.argv) 22 .parse(process.argv)
26 23
@@ -39,11 +36,11 @@ async function run () {
39 36
40 console.log('Starting server.') 37 console.log('Starting server.')
41 38
42 const server = await flushAndRunServer(1, {}, [], { hideLogs: false, execArgv: [ '--inspect' ] }) 39 const server = await createSingleServer(1, {}, { hideLogs: false, nodeArgs: [ '--inspect' ] })
43 40
44 const cleanup = () => { 41 const cleanup = async () => {
45 console.log('Killing server') 42 console.log('Killing server')
46 killallServers([ server ]) 43 await killallServers([ server ])
47 } 44 }
48 45
49 process.on('exit', cleanup) 46 process.on('exit', cleanup)
@@ -57,17 +54,15 @@ async function run () {
57 const attributes: LiveVideoCreate = { 54 const attributes: LiveVideoCreate = {
58 name: 'live', 55 name: 'live',
59 saveReplay: true, 56 saveReplay: true,
60 channelId: server.videoChannel.id, 57 channelId: server.store.channel.id,
61 privacy: VideoPrivacy.PUBLIC 58 privacy: VideoPrivacy.PUBLIC
62 } 59 }
63 60
64 console.log('Creating live.') 61 console.log('Creating live.')
65 62
66 const res = await createLive(server.url, server.accessToken, attributes) 63 const { uuid: liveVideoUUID } = await server.live.create({ fields: attributes })
67 const liveVideoUUID = res.body.video.uuid
68 64
69 const resLive = await getLive(server.url, server.accessToken, liveVideoUUID) 65 const live = await server.live.get({ videoId: liveVideoUUID })
70 const live: LiveVideo = resLive.body
71 66
72 console.log('Sending RTMP stream.') 67 console.log('Sending RTMP stream.')
73 68
@@ -86,19 +81,21 @@ async function run () {
86 81
87// ---------------------------------------------------------------------------- 82// ----------------------------------------------------------------------------
88 83
89async function buildConfig (server: ServerInfo, commandType: CommandType) { 84async function buildConfig (server: PeerTubeServer, commandType: CommandType) {
90 await updateCustomSubConfig(server.url, server.accessToken, { 85 await server.config.updateCustomSubConfig({
91 instance: { 86 newConfig: {
92 customizations: { 87 instance: {
93 javascript: '', 88 customizations: {
94 css: '' 89 javascript: '',
95 } 90 css: ''
96 }, 91 }
97 live: { 92 },
98 enabled: true, 93 live: {
99 allowReplay: true, 94 enabled: true,
100 transcoding: { 95 allowReplay: true,
101 enabled: commandType === 'live-transcoding' 96 transcoding: {
97 enabled: commandType === 'live-transcoding'
98 }
102 } 99 }
103 } 100 }
104 }) 101 })
diff --git a/server/types/models/video/video-streaming-playlist.ts b/server/types/models/video/video-streaming-playlist.ts
index 8b3ef51fc..1e4dccb8e 100644
--- a/server/types/models/video/video-streaming-playlist.ts
+++ b/server/types/models/video/video-streaming-playlist.ts
@@ -39,5 +39,5 @@ export type MStreamingPlaylistRedundanciesOpt =
39 PickWithOpt<VideoStreamingPlaylistModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]> 39 PickWithOpt<VideoStreamingPlaylistModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]>
40 40
41export function isStreamingPlaylist (value: MVideo | MStreamingPlaylistVideo): value is MStreamingPlaylistVideo { 41export function isStreamingPlaylist (value: MVideo | MStreamingPlaylistVideo): value is MStreamingPlaylistVideo {
42 return !!(value as MStreamingPlaylist).playlistUrl 42 return !!(value as MStreamingPlaylist).videoId
43} 43}
diff --git a/server/typings/express/index.d.ts b/server/typings/express/index.d.ts
index 1a8dc3430..1a99b598a 100644
--- a/server/typings/express/index.d.ts
+++ b/server/typings/express/index.d.ts
@@ -1,4 +1,5 @@
1 1
2import { OutgoingHttpHeaders } from 'http'
2import { RegisterServerAuthExternalOptions } from '@server/types' 3import { RegisterServerAuthExternalOptions } from '@server/types'
3import { 4import {
4 MAbuseMessage, 5 MAbuseMessage,
@@ -22,8 +23,7 @@ import { MPlugin, MServer, MServerBlocklist } from '@server/types/models/server'
22import { MVideoImportDefault } from '@server/types/models/video/video-import' 23import { MVideoImportDefault } from '@server/types/models/video/video-import'
23import { MVideoPlaylistElement, MVideoPlaylistElementVideoUrlPlaylistPrivacy } from '@server/types/models/video/video-playlist-element' 24import { MVideoPlaylistElement, MVideoPlaylistElementVideoUrlPlaylistPrivacy } from '@server/types/models/video/video-playlist-element'
24import { MAccountVideoRateAccountVideo } from '@server/types/models/video/video-rate' 25import { MAccountVideoRateAccountVideo } from '@server/types/models/video/video-rate'
25import { HttpMethod } from '@shared/core-utils/miscs/http-methods' 26import { HttpMethod, PeerTubeProblemDocumentData, ServerErrorCode, VideoCreate } from '@shared/models'
26import { PeerTubeProblemDocumentData, ServerErrorCode, VideoCreate } from '@shared/models'
27import { File as UploadXFile, Metadata } from '@uploadx/core' 27import { File as UploadXFile, Metadata } from '@uploadx/core'
28import { RegisteredPlugin } from '../../lib/plugins/plugin-manager' 28import { RegisteredPlugin } from '../../lib/plugins/plugin-manager'
29import { 29import {
@@ -41,6 +41,7 @@ import {
41 MVideoShareActor, 41 MVideoShareActor,
42 MVideoThumbnail 42 MVideoThumbnail
43} from '../../types/models' 43} from '../../types/models'
44import { Writable } from 'stream'
44 45
45declare module 'express' { 46declare module 'express' {
46 export interface Request { 47 export interface Request {
@@ -99,6 +100,15 @@ declare module 'express' {
99 }) => void 100 }) => void
100 101
101 locals: { 102 locals: {
103 apicache: {
104 content: string | Buffer
105 write: Writable['write']
106 writeHead: Response['writeHead']
107 end: Response['end']
108 cacheable: boolean
109 headers: OutgoingHttpHeaders
110 }
111
102 docUrl?: string 112 docUrl?: string
103 113
104 videoAPI?: MVideoFormattableDetails 114 videoAPI?: MVideoFormattableDetails